diff --git a/channels/chan_dahdi.c b/channels/chan_dahdi.c index 1e075224fb..81d8b6d19a 100644 --- a/channels/chan_dahdi.c +++ b/channels/chan_dahdi.c @@ -1436,6 +1436,7 @@ static int my_get_callerid(void *pvt, char *namebuf, char *numbuf, enum analog_e int res; unsigned char buf[256]; int flags; + int redirecting; poller.fd = p->subs[SUB_REAL].dfd; poller.events = POLLPRI | POLLIN; @@ -1482,7 +1483,8 @@ static int my_get_callerid(void *pvt, char *namebuf, char *numbuf, enum analog_e } if (res == 1) { - callerid_get(p->cs, &name, &num, &flags); + struct ast_channel *chan = analog_p->ss_astchan; + callerid_get_with_redirecting(p->cs, &name, &num, &flags, &redirecting); if (name) ast_copy_string(namebuf, name, ANALOG_MAX_CID); if (num) @@ -1490,7 +1492,6 @@ static int my_get_callerid(void *pvt, char *namebuf, char *numbuf, enum analog_e if (flags & (CID_PRIVATE_NUMBER | CID_UNKNOWN_NUMBER)) { /* If we got a presentation, we must set it on the channel */ - struct ast_channel *chan = analog_p->ss_astchan; struct ast_party_caller caller; ast_party_caller_set_init(&caller, ast_channel_caller(chan)); @@ -1499,8 +1500,19 @@ static int my_get_callerid(void *pvt, char *namebuf, char *numbuf, enum analog_e ast_party_caller_set(ast_channel_caller(chan), &caller, NULL); ast_party_caller_free(&caller); } + if (redirecting) { + /* There is a redirecting reason available in the Caller*ID received. + * No idea what the redirecting number is, since the Caller*ID protocol + * has no parameter for that, but at least we know WHY it was redirected. */ + ast_channel_redirecting(chan)->reason.code = redirecting; + } + + if (flags & CID_QUALIFIER) { + /* This is the inverse of how the qualifier is set in sig_analog */ + pbx_builtin_setvar_helper(chan, "CALL_QUALIFIER", "1"); + } - ast_debug(1, "CallerID number: %s, name: %s, flags=%d\n", num, name, flags); + ast_debug(1, "CallerID number: %s, name: %s, flags=%d, redirecting=%s\n", num, name, flags, ast_redirecting_reason_name(&ast_channel_redirecting(chan)->reason)); return 0; } } diff --git a/funcs/func_callerid.c b/funcs/func_callerid.c index 850db5c906..1f962aacc9 100644 --- a/funcs/func_callerid.c +++ b/funcs/func_callerid.c @@ -192,6 +192,8 @@ You are responsible for setting it if/when needed. Supporting Caller ID units will display the LDC (Long Distance Call) indicator when they receive this parameter. + For incoming calls on FXO ports, if the Call Qualifier parameter is received, + this variable will also be set to 1. This option must be used with a channel driver that allows Asterisk to generate the Caller ID spill, which currently only includes chan_dahdi. diff --git a/include/asterisk/callerid.h b/include/asterisk/callerid.h index 77b5822ba1..3c6d9c3b7c 100644 --- a/include/asterisk/callerid.h +++ b/include/asterisk/callerid.h @@ -190,15 +190,29 @@ int callerid_feed_jp(struct callerid_state *cid, unsigned char *ubuf, int sample * \param cid Callerid state machine to act upon * \param number Pass the address of a pointer-to-char (will contain the phone number) * \param name Pass the address of a pointer-to-char (will contain the name) - * \param flags Pass the address of an int variable(will contain the various callerid flags) + * \param flags Pass the address of an int variable (will contain the various callerid flags - presentation flags and call qualifier) * * \details * This function extracts a callerid string out of a callerid_state state machine. * If no number is found, *number will be set to NULL. Likewise for the name. - * Flags can contain any of the following: + * Flags can contain any of the following: CID_PRIVATE_NAME, CID_PRIVATE_NUMBER, CID_UNKNOWN_NAME, CID_UNKNOWN_NUMBER, CID_MSGWAITING, CID_NOMSGWAITING, CID_QUALIFIER */ void callerid_get(struct callerid_state *cid, char **number, char **name, int *flags); +/*! \brief Extract info out of callerID state machine. Flags are listed above + * \param cid Callerid state machine to act upon + * \param[out] number Pass the address of a pointer-to-char (will contain the phone number) + * \param[out] name Pass the address of a pointer-to-char (will contain the name) + * \param[out] flags Pass the address of an int variable (will contain the various callerid flags) + * \param[out] redirecting Pass the address of an int variable (will contain the redirecting reason, if received - presentation flags and call qualifier) + * + * \details + * This function extracts a callerid string out of a callerid_state state machine. + * If no number is found, *number will be set to NULL. Likewise for the name. + * Flags can contain any of the following: CID_PRIVATE_NAME, CID_PRIVATE_NUMBER, CID_UNKNOWN_NAME, CID_UNKNOWN_NUMBER, CID_MSGWAITING, CID_NOMSGWAITING, CID_QUALIFIER + */ +void callerid_get_with_redirecting(struct callerid_state *cid, char **name, char **number, int *flags, int *redirecting); + /*! * \brief Get and parse DTMF-based callerid * \param cidstring The actual transmitted string. diff --git a/main/callerid.c b/main/callerid.c index 9755c456a7..da0e7fc68c 100644 --- a/main/callerid.c +++ b/main/callerid.c @@ -53,6 +53,7 @@ struct callerid_state { char name[64]; char number[64]; int flags; + int redirecting; int sawflag; int len; @@ -185,17 +186,26 @@ struct callerid_state *callerid_new(int cid_signalling) return cid; } -void callerid_get(struct callerid_state *cid, char **name, char **number, int *flags) +void callerid_get_with_redirecting(struct callerid_state *cid, char **name, char **number, int *flags, int *redirecting) { *flags = cid->flags; - if (cid->flags & (CID_UNKNOWN_NAME | CID_PRIVATE_NAME)) + if (cid->flags & (CID_UNKNOWN_NAME | CID_PRIVATE_NAME)) { *name = NULL; - else + } else { *name = cid->name; - if (cid->flags & (CID_UNKNOWN_NUMBER | CID_PRIVATE_NUMBER)) + } + if (cid->flags & (CID_UNKNOWN_NUMBER | CID_PRIVATE_NUMBER)) { *number = NULL; - else + } else { *number = cid->number; + } + *redirecting = cid->redirecting; +} + +void callerid_get(struct callerid_state *cid, char **name, char **number, int *flags) +{ + int redirecting; + return callerid_get_with_redirecting(cid, name, number, flags, &redirecting); } void callerid_get_dtmf(char *cidstring, char *number, int *flags) @@ -541,6 +551,21 @@ int callerid_feed_jp(struct callerid_state *cid, unsigned char *ubuf, int len, s return 0; } +static const char *mdmf_param_name(int param) +{ + switch (param) { + case 0x1: return "Date/Time"; + case 0x2: return "Caller Number"; + case 0x3: return "DNIS"; + case 0x4: return "Reason For Absence of Number"; + case 0x5: return "Reason For Redirection"; + case 0x6: return "Call Qualifier"; + case 0x7: return "Name"; + case 0x8: return "Reason For Absence of Name"; + case 0xB: return "Message Waiting"; + default: return "Unknown"; + } +} int callerid_feed(struct callerid_state *cid, unsigned char *ubuf, int len, struct ast_format *codec) { @@ -631,18 +656,41 @@ int callerid_feed(struct callerid_state *cid, unsigned char *ubuf, int len, stru cid->name[0] = '\0'; /* Update flags */ cid->flags = 0; + cid->redirecting = 0; /* If we get this far we're fine. */ if ((cid->type == 0x80) || (cid->type == 0x82)) { /* MDMF */ + ast_debug(6, "%s Caller*ID spill received\n", cid->type == 0x80 ? "MDMF" : "MDMF Message Waiting"); /* Go through each element and process */ for (x = 0; x < cid->pos;) { - switch (cid->rawdata[x++]) { + int param = cid->rawdata[x++]; + ast_debug(7, "Caller*ID parameter %d (%s), length %d\n", param, mdmf_param_name(param), cid->rawdata[x]); + switch (param) { case 1: - /* Date */ + /* Date/Time... in theory we could synchronize our time according to the Caller*ID, + * but it would be silly for a telephone switch to do that. */ break; + /* For MDMF spills, we would expect to get an "O" or a "P" + * for paramter 4 (or 8) as opposed to 2 (or 7) to indicate + * a blocked or out of area presentation. + * However, for SDMF, which doesn't have parameters, + * there is no differentiation, which is why the logic below + * just checks the number and name field and here, we use the same + * parsing logic for both parameters. Technically, it would be wrong + * to receive an 'O' or 'P' for parameters 2 or 7 and treat it as + * the reason for absence fields, but that is not likely to happen, + * and if it did, could possibly be buggy Caller ID generation we would + * want to treat the same way, anyways. + * + * The "Dialable Directory Number" is how the number would be called back. + * Asterisk doesn't really have a corresponding thing for this, + * so log it if we get it for debugging, but treat the same otherwise. + */ + case 3: /* Dialable Directory Number / Number (for Zebble) */ + ast_debug(3, "Caller*ID Dialable Directory Number: '%.*s'\n", cid->rawdata[x], cid->rawdata + x + 1); + /* Fall through */ case 2: /* Number */ - case 3: /* Number (for Zebble) */ - case 4: /* Number */ + case 4: /* Reason for Absence of Number. */ res = cid->rawdata[x]; if (res > 32) { ast_log(LOG_NOTICE, "Truncating long caller ID number from %d bytes to 32\n", cid->rawdata[x]); @@ -654,10 +702,43 @@ int callerid_feed(struct callerid_state *cid, unsigned char *ubuf, int len, stru cid->number[res] = '\0'; } break; + case 5: /* Reason for Redirection */ + res = cid->rawdata[x]; + if (res != 1) { + ast_log(LOG_WARNING, "Redirecting parameter length is %d?\n", res); + break; + } + switch (*(cid->rawdata + x + 1)) { + case 0x1: + cid->redirecting = AST_REDIRECTING_REASON_USER_BUSY; + break; + case 0x2: + cid->redirecting = AST_REDIRECTING_REASON_NO_ANSWER; + break; + case 0x3: + cid->redirecting = AST_REDIRECTING_REASON_UNCONDITIONAL; + break; + case 0x4: + cid->redirecting = AST_REDIRECTING_REASON_CALL_FWD_DTE; + break; + case 0x5: + cid->redirecting = AST_REDIRECTING_REASON_DEFLECTION; + break; + default: + ast_log(LOG_WARNING, "Redirecting reason is %02x?\n", *(cid->rawdata + x + 1)); + break; + } + break; case 6: /* Stentor Call Qualifier (ie. Long Distance call) */ + res = cid->rawdata[x]; + if (res == 1 && *(cid->rawdata + x + 1) == 'L') { + cid->flags |= CID_QUALIFIER; + } else if (res >= 1) { + ast_debug(2, "Invalid value (len %d) received for Call Qualifier: '%c'\n", res, *(cid->rawdata + x + 1)); + } break; case 7: /* Name */ - case 8: /* Name */ + case 8: /* Reason for Absence of Name */ res = cid->rawdata[x]; if (res > 32) { ast_log(LOG_NOTICE, "Truncating long caller ID name from %d bytes to 32\n", cid->rawdata[x]); @@ -692,6 +773,7 @@ int callerid_feed(struct callerid_state *cid, unsigned char *ubuf, int len, stru } } else if (cid->type == 0x6) { /* VMWI SDMF */ + ast_debug(6, "VMWI SDMF Caller*ID spill received\n"); if (cid->rawdata[2] == 0x42) { cid->flags |= CID_MSGWAITING; } else if (cid->rawdata[2] == 0x6f) { @@ -699,21 +781,38 @@ int callerid_feed(struct callerid_state *cid, unsigned char *ubuf, int len, stru } } else { /* SDMF */ + ast_debug(6, "SDMF Caller*ID spill received\n"); ast_copy_string(cid->number, cid->rawdata + 8, sizeof(cid->number)); } if (!strcmp(cid->number, "P")) { + ast_debug(6, "Caller*ID number is private\n"); strcpy(cid->number, ""); cid->flags |= CID_PRIVATE_NUMBER; - } else if (!strcmp(cid->number, "O") || ast_strlen_zero(cid->number)) { + } else if (!strcmp(cid->number, "O")) { + ast_debug(6, "Caller*ID number is out of area\n"); strcpy(cid->number, ""); cid->flags |= CID_UNKNOWN_NUMBER; + } else if (ast_strlen_zero(cid->number)) { + ast_debug(6, "No Caller*ID number provided, and no reason provided for its absence\n"); + strcpy(cid->number, ""); + cid->flags |= CID_UNKNOWN_NUMBER; + } else { + ast_debug(6, "Caller*ID number is '%s'\n", cid->number); } if (!strcmp(cid->name, "P")) { + ast_debug(6, "Caller*ID name is private\n"); strcpy(cid->name, ""); cid->flags |= CID_PRIVATE_NAME; - } else if (!strcmp(cid->name, "O") || ast_strlen_zero(cid->name)) { + } else if (!strcmp(cid->name, "O")) { + ast_debug(6, "Caller*ID name is out of area\n"); strcpy(cid->name, ""); cid->flags |= CID_UNKNOWN_NAME; + } else if (ast_strlen_zero(cid->name)) { + ast_debug(6, "No Caller*ID name provided, and no reason provided for its absence\n"); + strcpy(cid->name, ""); + cid->flags |= CID_UNKNOWN_NAME; + } else { + ast_debug(6, "Caller*ID name is '%s'\n", cid->name); } return 1; break;