diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index e42c7a7c06..3ec5564783 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -402,6 +402,11 @@ struct soft_key_event_message { #define HEADSET_STATUS_MESSAGE 0x002B #define REGISTER_AVAILABLE_LINES_MESSAGE 0x002D +#define SERVICEURL_STATREQ_MESSAGE 0x0033 +struct serviceurl_statreq_message { + uint32_t instance; +}; + #define REGISTER_ACK_MESSAGE 0x0081 struct register_ack_message { uint32_t keepAlive; @@ -576,6 +581,7 @@ struct button_definition_template { #define STIMULUS_LINE 0x09 #define STIMULUS_VOICEMAIL 0x0F #define STIMULUS_AUTOANSWER 0x11 +#define STIMULUS_SERVICEURL 0x14 #define STIMULUS_DND 0x3F #define STIMULUS_CONFERENCE 0x7D #define STIMULUS_CALLPARK 0x7E @@ -594,6 +600,7 @@ struct button_definition_template { #define BT_LINE STIMULUS_LINE #define BT_VOICEMAIL STIMULUS_VOICEMAIL #define BT_AUTOANSWER STIMULUS_AUTOANSWER +#define BT_SERVICEURL STIMULUS_SERVICEURL #define BT_DND STIMULUS_DND #define BT_CONFERENCE STIMULUS_CONFERENCE #define BT_CALLPARK STIMULUS_CALLPARK @@ -1080,6 +1087,14 @@ struct dialed_number_message { uint32_t callReference; }; +#define MAX_SERVICEURL 256 +#define SERVICEURL_STAT_MESSAGE 0x012F +struct serviceurl_stat_message { + uint32_t instance; + char url[MAX_SERVICEURL]; + char displayName[40]; +}; + #define MAXCALLINFOSTR 256 #define CALL_INFO_MESSAGE_VARIABLE 0x014A struct call_info_message_variable { @@ -1152,6 +1167,7 @@ union skinny_data { struct bksp_req_message bkspmessage; struct call_info_message_variable callinfomessagevariable; struct display_prompt_status_message_variable displaypromptstatusvar; + struct serviceurl_stat_message serviceurlmessage; }; /* packet composition */ @@ -1467,6 +1483,14 @@ struct skinny_speeddial { struct skinny_device *parent; }; +struct skinny_serviceurl { + int instance; + char url[MAX_SERVICEURL]; + char displayName[40]; + AST_LIST_ENTRY(skinny_serviceurl) list; + struct skinny_device *device; +}; + #define SKINNY_DEVICECONTAINER 1 #define SKINNY_LINECONTAINER 2 #define SKINNY_SUBLINECONTAINER 3 @@ -1516,6 +1540,7 @@ struct skinny_device { struct ast_format_cap *confcap; AST_LIST_HEAD(, skinny_line) lines; AST_LIST_HEAD(, skinny_speeddial) speeddials; + AST_LIST_HEAD(, skinny_serviceurl) serviceurls; AST_LIST_HEAD(, skinny_addon) addons; AST_LIST_ENTRY(skinny_device) list; }; @@ -3199,6 +3224,31 @@ static void transmit_backspace(struct skinny_device *d, int instance, unsigned c transmit_response(d, req); } +static void transmit_serviceurlstat(struct skinny_device *d, int instance) +{ + struct skinny_req *req; + struct skinny_serviceurl *surl; + + if (!(req = req_alloc(sizeof(struct serviceurl_stat_message), SERVICEURL_STAT_MESSAGE))) + return; + + AST_LIST_TRAVERSE(&d->serviceurls, surl, list) { + if (surl->instance == instance) { + break; + } + } + + if (surl) { + memcpy(req->data.serviceurlmessage.displayName, surl->displayName, sizeof(req->data.serviceurlmessage.displayName)); + memcpy(req->data.serviceurlmessage.url, surl->url, sizeof(req->data.serviceurlmessage.url)); + } + req->data.serviceurlmessage.instance = htolel(instance); + + SKINNY_DEBUG(DEBUG_PACKET, 3, "Transmitting SERVICEURL_STAT_MESSAGE to %s, inst %d\n", + d->name, instance); + transmit_response(d, req); +} + static int skinny_extensionstate_cb(char *context, char *exten, struct ast_state_cb_info *info, void *data) { struct skinny_container *container = data; @@ -3975,6 +4025,7 @@ static char *_skinny_show_device(int type, int fd, struct mansession *s, const s struct skinny_line *l; struct skinny_speeddial *sd; struct skinny_addon *sa; + struct skinny_serviceurl *surl; char codec_buf[512]; if (argc < 4) { @@ -3984,7 +4035,7 @@ static char *_skinny_show_device(int type, int fd, struct mansession *s, const s AST_LIST_LOCK(&devices); AST_LIST_TRAVERSE(&devices, d, list) { if (!strcasecmp(argv[3], d->id) || !strcasecmp(argv[3], d->name)) { - int numlines = 0, numaddons = 0, numspeeddials = 0; + int numlines = 0, numaddons = 0, numspeeddials = 0, numserviceurls = 0; AST_LIST_TRAVERSE(&d->lines, l, list){ numlines++; @@ -3998,6 +4049,10 @@ static char *_skinny_show_device(int type, int fd, struct mansession *s, const s numspeeddials++; } + AST_LIST_TRAVERSE(&d->serviceurls, surl, list) { + numserviceurls++; + } + if (type == 0) { /* CLI */ ast_cli(fd, "Name: %s\n", d->name); ast_cli(fd, "Id: %s\n", d->id); @@ -4016,20 +4071,18 @@ static char *_skinny_show_device(int type, int fd, struct mansession *s, const s AST_LIST_TRAVERSE(&d->lines, l, list) { ast_cli(fd, " %s (%s)\n", l->name, l->label); } - AST_LIST_TRAVERSE(&d->addons, sa, list) { - numaddons++; - } ast_cli(fd, "Addons: %d\n", numaddons); AST_LIST_TRAVERSE(&d->addons, sa, list) { ast_cli(fd, " %s\n", sa->type); } - AST_LIST_TRAVERSE(&d->speeddials, sd, list) { - numspeeddials++; - } ast_cli(fd, "Speeddials: %d\n", numspeeddials); AST_LIST_TRAVERSE(&d->speeddials, sd, list) { ast_cli(fd, " %s (%s) ishint: %d\n", sd->exten, sd->label, sd->isHint); } + ast_cli(fd, "ServiceURLs: %d\n", numserviceurls); + AST_LIST_TRAVERSE(&d->serviceurls, surl, list) { + ast_cli(fd, " %s (%s)\n", surl->displayName, surl->url); + } } else { /* manager */ astman_append(s, "Channeltype: SKINNY\r\n"); astman_append(s, "ObjectName: %s\r\n", d->name); @@ -4058,6 +4111,10 @@ static char *_skinny_show_device(int type, int fd, struct mansession *s, const s AST_LIST_TRAVERSE(&d->speeddials, sd, list) { astman_append(s, "Speeddial: %s (%s) ishint: %d\r\n", sd->exten, sd->label, sd->isHint); } + astman_append(s, "ServiceURLs: %d\r\n", numserviceurls); + AST_LIST_TRAVERSE(&d->serviceurls, surl, list) { + astman_append(s, " %s (%s)\r\n", surl->displayName, surl->url); + } } } } @@ -6378,11 +6435,12 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski struct skinny_device *d = s->device; struct skinny_line *l; int i; - struct skinny_speeddial *sd; + struct skinny_serviceurl *surl; struct button_definition_template btn[42]; int lineInstance = 1; int speeddialInstance = 1; + int serviceurlInstance = 1; int buttonCount = 0; if (!(req = req_alloc(sizeof(struct button_template_res_message), BUTTON_TEMPLATE_RES_MESSAGE))) @@ -6465,6 +6523,20 @@ static int handle_button_template_req_message(struct skinny_req *req, struct ski } } } + + if (!btnSet) { + AST_LIST_TRAVERSE(&d->serviceurls, surl, list) { + if (surl->instance == serviceurlInstance) { + SKINNY_DEBUG(DEBUG_TEMPLATE, 4, "Adding button: %d, %d\n", BT_SERVICEURL, serviceurlInstance); + req->data.buttontemplate.definition[i].buttonDefinition = BT_SERVICEURL; + req->data.buttontemplate.definition[i].instanceNumber = serviceurlInstance; + serviceurlInstance++; + buttonCount++; + btnSet = 1; + break; + } + } + } break; case BT_LINE: req->data.buttontemplate.definition[i].buttonDefinition = htolel(BT_NONE); @@ -7094,6 +7166,10 @@ static int handle_message(struct skinny_req *req, struct skinnysession *s) /* XXX I have no clue what this is for, but my phone was sending it, so... */ SKINNY_DEBUG(DEBUG_PACKET, 3, "Received REGISTER_AVAILABLE_LINES_MESSAGE from %s\n", d->name); break; + case SERVICEURL_STATREQ_MESSAGE: + SKINNY_DEBUG(DEBUG_PACKET, 3, "SERVICEURL_STATREQ_MESSAGE from %s\n", d->name); + transmit_serviceurlstat(d, letohl(req->data.serviceurlmessage.instance)); + break; default: SKINNY_DEBUG(DEBUG_PACKET, 3, "Received UNKNOWN_MESSAGE(%x) from %s\n", letohl(req->e), d->name); break; @@ -7417,6 +7493,7 @@ static void config_parse_variables(int type, void *item, struct ast_variable *vp struct ast_variable *v; int lineInstance = 1; int speeddialInstance = 1; + int serviceUrlInstance = 1; while(vptr) { v = vptr; @@ -7859,6 +7936,30 @@ static void config_parse_variables(int type, void *item, struct ast_variable *vp AST_LIST_INSERT_HEAD(&CDEV->speeddials, sd, list); continue; } + } else if (!strcasecmp(v->name, "serviceurl")) { + if (type & (TYPE_DEVICE)) { + struct skinny_serviceurl *surl; + char buf[256]; + char *stringp = buf, *serviceUrl, *displayName; + if (!(surl = ast_calloc(1, sizeof(*surl)))) { + ast_log(LOG_WARNING, "Unable to allocate memory for serviceurl %s. Ignoring service URL.\n", v->name); + continue; + } + ast_copy_string(buf, v->value, sizeof(buf)); + displayName = strsep(&stringp, ","); + if (stringp) { + serviceUrl = stringp; + ast_copy_string(surl->url, ast_strip(serviceUrl), sizeof(surl->url)); + ast_copy_string(surl->displayName, displayName, sizeof(surl->displayName)); + surl->instance = serviceUrlInstance++; + surl->device = CDEV; + AST_LIST_INSERT_HEAD(&CDEV->serviceurls, surl, list); + } else { + ast_free(surl); + ast_log(LOG_WARNING, "Badly formed option for service URL in %s. Ignoring service URL.\n", v->name); + } + continue; + } } else if (!strcasecmp(v->name, "addon")) { if (type & (TYPE_DEVICE)) { struct skinny_addon *a; @@ -8170,6 +8271,7 @@ static void delete_devices(void) struct skinny_line *l; struct skinny_speeddial *sd; struct skinny_addon *a; + struct skinny_serviceurl *surl; AST_LIST_LOCK(&devices); AST_LIST_LOCK(&lines); @@ -8187,6 +8289,10 @@ static void delete_devices(void) free(sd->container); free(sd); } + /* Delete all serviceurls for this device */ + while ((surl = AST_LIST_REMOVE_HEAD(&d->serviceurls, list))) { + free(surl); + } /* Delete all addons for this device */ while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) { free(a); diff --git a/configs/skinny.conf.sample b/configs/skinny.conf.sample index 783f65da68..9509099a8e 100644 --- a/configs/skinny.conf.sample +++ b/configs/skinny.conf.sample @@ -178,6 +178,12 @@ keepalive=120 ;directmedia=yes ; Allow media to go directly between two RTP endpoints. ;line=120 ; Dial(Skinny/120@florian) +; Service URLs attached to line buttons (eg phone directory) +; See http://www.voip-info.org/wiki/view/Asterisk+Cisco+79XX+XML+Services +; for intro to xml structure. +;serviceurl=Directory,http://host/file.xml + + ; Typical config for a 7910 ;[duba] ; Device name ;device=SEP0007EB463101 ; Official identifier