Add named callgroups/pickupgroups
authorMatthew Jordan <mjordan@digium.com>
Tue, 7 Aug 2012 12:46:36 +0000 (12:46 +0000)
committerMatthew Jordan <mjordan@digium.com>
Tue, 7 Aug 2012 12:46:36 +0000 (12:46 +0000)
This patch adds named calledgroups/pickupgroups to Asterisk.  Named groups are
implemented in parallel to the existing numbered callgroup/pickupgroup
implementation.  However, unlike the existing implementation, which is limited
to a maximum of 64 defined groups, the number of defined groups allowed for
named callgroups/pickupgroups is effectively unlimited.

Named groups are configured with the keywords "namedcallgroup" and
"namedpickupgroup".  This corresponds to the numbered group definitions of
"callgroup" and "pickupgroup".  Note that as the implementation of named groups
coexists with the existing numbered implementation, a defined named group of
"4" does not equate to numbered group 4.

Support for the named groups has been added to the SIP, DAHDI, and mISDN channel
drivers.

Review: https://reviewboard.asterisk.org/r/2043

Uploaded by:
Guenther Kelleter(license #6372)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@370831 65c4cc65-6c06-0410-ace0-fbb531ad65f3

14 files changed:
CHANGES
channels/chan_dahdi.c
channels/chan_misdn.c
channels/chan_sip.c
channels/misdn/chan_misdn_config.h
channels/misdn_config.c
channels/sip/include/sip.h
configs/chan_dahdi.conf.sample
configs/misdn.conf.sample
configs/sip.conf.sample
include/asterisk/channel.h
main/channel.c
main/channel_internal_api.c
main/features.c

diff --git a/CHANGES b/CHANGES
index 1119b3f..4842370 100644 (file)
--- a/CHANGES
+++ b/CHANGES
@@ -109,12 +109,14 @@ MixMonitor
  * Added 'm' option, which stores a copy of the recording as a voicemail in the
    indicated mailboxes.
 
+
 MySQL
 -------------------
  * The connect action in app_mysql now allows you to specify a port number to
    connect to.  This is useful if you run a MySQL server on a non-standard
    port number.
 
+
 OSP Applications
 -------------------
  * Increased the default number of allowed destinations from 5 to 12.
@@ -242,6 +244,9 @@ chan_dahdi
    send connected line information on initial connect; and update, to send
    information on any update during a call.  Default is update.
 
+ * Add options namedcallgroup and namedpickupgroup to support installations
+   where a higher number of groups (>64) is required.
+
 
 chan_motif
 ------------------
@@ -315,15 +320,20 @@ chan_sip
 
  * Add support for WebSocket transport. This can be configured using 'ws' or 'wss'
    as the transport.
+
  * Add options subminexpiry and submaxexpiry to set limits of subscription
    timer independently from registration timer settings. The setting of the
    registration timer limits still is done by options minexpiry, maxexpiry
    and defaultexpiry. For backwards compatibility the setting of minexpiry
    and maxexpiry also is used to configure the subscription timer limits if
    subminexpiry and submaxexpiry are not set in sip.conf.
+
  * Set registration timer limits to default values when reloading sip
    configuration and values are not set by configuration.
 
+ * Add options namedcallgroup and namedpickupgroup to support installations
+   where a higher number of groups (>64) is required.
+
  * When a MESSAGE request is received, the address the request was received from
    is now saved in the SIP_RECVADDR variable.
 
@@ -371,6 +381,12 @@ chan_unistim
    on-screen key
 
 
+chan_mISDN:
+------------------
+ * Add options namedcallgroup and namedpickupgroup to support installations
+   where a higher number of groups (>64) is required.
+
+
 Core
 ------------------
  * The minimum DTMF duration can now be configured in asterisk.conf
index c0afc57..b608383 100644 (file)
@@ -1092,6 +1092,16 @@ struct dahdi_pvt {
         */
        ast_group_t pickupgroup;
        /*!
+        * \brief Named call groups this belongs to.
+        * \note The "namedcallgroup" string read in from chan_dahdi.conf
+        */
+       struct ast_namedgroups *named_callgroups;
+       /*!
+        * \brief Named pickup groups this belongs to.
+        * \note The "namedpickupgroup" string read in from chan_dahdi.conf
+        */
+       struct ast_namedgroups *named_pickupgroups;
+       /*!
         * \brief Channel variable list with associated values to set when a channel is created.
         * \note The "setvar" strings read in from chan_dahdi.conf
         */
@@ -5917,6 +5927,10 @@ static void destroy_dahdi_pvt(struct dahdi_pvt *pvt)
        if (p->cc_params) {
                ast_cc_config_params_destroy(p->cc_params);
        }
+
+       p->named_callgroups = ast_unref_namedgroups(p->named_callgroups);
+       p->named_pickupgroups = ast_unref_namedgroups(p->named_pickupgroups);
+
        ast_mutex_destroy(&p->lock);
        dahdi_close_sub(p, SUB_REAL);
        if (p->owner)
@@ -9904,6 +9918,8 @@ static struct ast_channel *dahdi_new(struct dahdi_pvt *i, int state, int startpb
                /* Only FXO signalled stuff can be picked up */
                ast_channel_callgroup_set(tmp, i->callgroup);
                ast_channel_pickupgroup_set(tmp, i->pickupgroup);
+               ast_channel_named_callgroups_set(tmp, i->named_callgroups);
+               ast_channel_named_pickupgroups_set(tmp, i->named_pickupgroups);
        }
        if (!ast_strlen_zero(i->parkinglot))
                ast_channel_parkinglot_set(tmp, i->parkinglot);
@@ -13159,6 +13175,10 @@ static struct dahdi_pvt *mkintf(int channel, const struct dahdi_chan_conf *conf,
                tmp->group = conf->chan.group;
                tmp->callgroup = conf->chan.callgroup;
                tmp->pickupgroup= conf->chan.pickupgroup;
+               ast_unref_namedgroups(tmp->named_callgroups);
+               tmp->named_callgroups = ast_ref_namedgroups(conf->chan.named_callgroups);
+               ast_unref_namedgroups(tmp->named_pickupgroups);
+               tmp->named_pickupgroups = ast_ref_namedgroups(conf->chan.named_pickupgroups);
                if (conf->chan.vars) {
                        struct ast_variable *v, *tmpvar;
                        for (v = conf->chan.vars ; v ; v = v->next) {
@@ -17557,6 +17577,10 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                                confp->chan.pickupgroup = 0;
                        else
                                confp->chan.pickupgroup = ast_get_group(v->value);
+               } else if (!strcasecmp(v->name, "namedcallgroup")) {
+                       confp->chan.named_callgroups = ast_get_namedgroups(v->value);
+               } else if (!strcasecmp(v->name, "namedpickupgroup")) {
+                       confp->chan.named_pickupgroups = ast_get_namedgroups(v->value);
                } else if (!strcasecmp(v->name, "setvar")) {
                        char *varname = ast_strdupa(v->value), *varval = NULL;
                        struct ast_variable *tmpvar;
@@ -18541,6 +18565,11 @@ static int process_dahdi(struct dahdi_chan_conf *confp, const char *cat, struct
                }
                ast_cc_config_params_destroy(conf.chan.cc_params);
        }
+
+       /* Since named callgroup and named pickup group are ref'd to dahdi_pvt at this point, unref container in confp's pvt. */
+       confp->chan.named_callgroups = ast_unref_namedgroups(confp->chan.named_callgroups);
+       confp->chan.named_pickupgroups = ast_unref_namedgroups(confp->chan.named_pickupgroups);
+
        return 0;
 }
 
index 8293961..ce654ee 100644 (file)
@@ -5904,6 +5904,9 @@ static int read_config(struct chan_list *ch)
        char buf2[256];
        ast_group_t pg;
        ast_group_t cg;
+       struct ast_namedgroups *npg;
+       struct ast_namedgroups *ncg;
+       struct ast_str *tmp_str;
 
        if (!ch) {
                ast_log(LOG_WARNING, "Cannot configure without chanlist\n");
@@ -5987,6 +5990,20 @@ static int read_config(struct chan_list *ch)
        ast_channel_pickupgroup_set(ast, pg);
        ast_channel_callgroup_set(ast, cg);
 
+       misdn_cfg_get(port, MISDN_CFG_NAMEDPICKUPGROUP, &npg, sizeof(npg));
+       misdn_cfg_get(port, MISDN_CFG_NAMEDCALLGROUP, &ncg, sizeof(ncg));
+
+       tmp_str = ast_str_create(1024);
+       if (tmp_str) {
+               chan_misdn_log(5, port, " --> * NamedCallGrp:%s\n", ast_print_namedgroups(&tmp_str, ncg));
+               ast_str_reset(tmp_str);
+               chan_misdn_log(5, port, " --> * NamedPickupGrp:%s\n", ast_print_namedgroups(&tmp_str, npg));
+               ast_free(tmp_str);
+       }
+
+       ast_channel_named_pickupgroups_set(ast, npg);
+       ast_channel_named_callgroups_set(ast, ncg);
+
        if (ch->originator == ORG_AST) {
                char callerid[BUFFERSIZE + 1];
 
index 7afe46f..b002254 100644 (file)
@@ -410,6 +410,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
                                        <enum name="pickupgroup">
                                                <para>The configured Pickupgroup.</para>
                                        </enum>
+                                       <enum name="namedcallgroup">
+                                               <para>The configured Named Callgroup.</para>
+                                       </enum>
+                                       <enum name="namedpickupgroup">
+                                               <para>The configured Named Pickupgroup.</para>
+                                       </enum>
                                        <enum name="codecs">
                                                <para>The configured codecs.</para>
                                        </enum>
@@ -1453,6 +1459,7 @@ static char * _sip_show_peers(int fd, int *total, struct mansession *s, const st
 static char *sip_show_peers(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 static char *sip_show_objects(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
 static void  print_group(int fd, ast_group_t group, int crlf);
+static void  print_named_groups(int fd, struct ast_namedgroups *groups, int crlf);
 static const char *dtmfmode2str(int mode) attribute_const;
 static int str2dtmfmode(const char *str) attribute_unused;
 static const char *insecure2str(int mode) attribute_const;
@@ -4907,6 +4914,9 @@ static void sip_destroy_peer(struct sip_peer *peer)
                peer->socket.ws_session = NULL;
        }
 
+       peer->named_callgroups = ast_unref_namedgroups(peer->named_callgroups);
+       peer->named_pickupgroups = ast_unref_namedgroups(peer->named_pickupgroups);
+
        ast_cc_config_params_destroy(peer->cc_params);
 
        ast_string_field_free_memory(peer);
@@ -5628,6 +5638,10 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
        ref_proxy(dialog, obproxy_get(dialog, peer));
        dialog->callgroup = peer->callgroup;
        dialog->pickupgroup = peer->pickupgroup;
+       ast_unref_namedgroups(dialog->named_callgroups);
+       dialog->named_callgroups = ast_ref_namedgroups(peer->named_callgroups);
+       ast_unref_namedgroups(dialog->named_pickupgroups);
+       dialog->named_pickupgroups = ast_ref_namedgroups(peer->named_pickupgroups);
        ast_copy_string(dialog->zone, peer->zone, sizeof(dialog->zone));
        dialog->allowtransfer = peer->allowtransfer;
        dialog->jointnoncodeccapability = dialog->noncodeccapability;
@@ -6219,6 +6233,9 @@ void __sip_destroy(struct sip_pvt *p, int lockowner, int lockdialoglist)
                p->peerauth = NULL;
        }
 
+       p->named_callgroups = ast_unref_namedgroups(p->named_callgroups);
+       p->named_pickupgroups = ast_unref_namedgroups(p->named_pickupgroups);
+
        p->caps = ast_format_cap_destroy(p->caps);
        p->jointcaps = ast_format_cap_destroy(p->jointcaps);
        p->peercaps = ast_format_cap_destroy(p->peercaps);
@@ -7570,6 +7587,10 @@ static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *tit
 
        ast_channel_callgroup_set(tmp, i->callgroup);
        ast_channel_pickupgroup_set(tmp, i->pickupgroup);
+
+       ast_channel_named_callgroups_set(tmp, i->named_callgroups);
+       ast_channel_named_pickupgroups_set(tmp, i->named_pickupgroups);
+
        ast_channel_caller(tmp)->id.name.presentation = i->callingpres;
        ast_channel_caller(tmp)->id.number.presentation = i->callingpres;
        if (!ast_strlen_zero(i->parkinglot)) {
@@ -17344,6 +17365,10 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
                p->amaflags = peer->amaflags;
                p->callgroup = peer->callgroup;
                p->pickupgroup = peer->pickupgroup;
+               ast_unref_namedgroups(p->named_callgroups);
+               p->named_callgroups = ast_ref_namedgroups(peer->named_callgroups);
+               ast_unref_namedgroups(p->named_pickupgroups);
+               p->named_pickupgroups = ast_ref_namedgroups(peer->named_pickupgroups);
                ast_format_cap_copy(p->caps, peer->caps);
                ast_format_cap_copy(p->jointcaps, peer->caps);
                p->prefs = peer->prefs;
@@ -18347,6 +18372,16 @@ static void print_group(int fd, ast_group_t group, int crlf)
        ast_cli(fd, crlf ? "%s\r\n" : "%s\n", ast_print_group(buf, sizeof(buf), group) );
 }
 
+/*! \brief Print named call groups and pickup groups */
+static void print_named_groups(int fd, struct ast_namedgroups *group, int crlf)
+{
+       struct ast_str *buf = ast_str_create(1024);
+       if (buf) {
+               ast_cli(fd, crlf ? "%s\r\n" : "%s\n", ast_print_namedgroups(&buf, group) );
+               ast_free(buf);
+       }
+}
+
 /*! \brief mapping between dtmf flags and strings */
 static const struct _map_x_s dtmfstr[] = {
        { SIP_DTMF_RFC2833,     "rfc2833" },
@@ -19002,6 +19037,10 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
                print_group(fd, peer->callgroup, 0);
                ast_cli(fd, "  Pickupgroup  : ");
                print_group(fd, peer->pickupgroup, 0);
+               ast_cli(fd, "  Named Callgr : ");
+               print_named_groups(fd, peer->named_callgroups, 0);
+               ast_cli(fd, "  Nam. Pickupgr: ");
+               print_named_groups(fd, peer->named_pickupgroups, 0);
                peer_mailboxes_to_str(&mailbox_str, peer);
                ast_cli(fd, "  MOH Suggest  : %s\n", peer->mohsuggest);
                ast_cli(fd, "  Mailbox      : %s\n", mailbox_str->str);
@@ -19096,7 +19135,7 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
                peer = sip_unref_peer(peer, "sip_show_peer: sip_unref_peer: done with peer ptr");
        } else  if (peer && type == 1) { /* manager listing */
                char buffer[256];
-               struct ast_str *mailbox_str = ast_str_alloca(512);
+               struct ast_str *tmp_str = ast_str_alloca(512);
                astman_append(s, "Channeltype: SIP\r\n");
                astman_append(s, "ObjectName: %s\r\n", peer->name);
                astman_append(s, "ChanObjectType: peer\r\n");
@@ -19118,9 +19157,15 @@ static char *_sip_show_peer(int type, int fd, struct mansession *s, const struct
                astman_append(s, "%s\r\n", ast_print_group(buffer, sizeof(buffer), peer->callgroup));
                astman_append(s, "Pickupgroup: ");
                astman_append(s, "%s\r\n", ast_print_group(buffer, sizeof(buffer), peer->pickupgroup));
+               astman_append(s, "Named Callgroup: ");
+               astman_append(s, "%s\r\n", ast_print_namedgroups(&tmp_str, peer->named_callgroups));
+               ast_str_reset(tmp_str);
+               astman_append(s, "Named Pickupgroup: ");
+               astman_append(s, "%s\r\n", ast_print_namedgroups(&tmp_str, peer->named_pickupgroups));
+               ast_str_reset(tmp_str);
                astman_append(s, "MOHSuggest: %s\r\n", peer->mohsuggest);
-               peer_mailboxes_to_str(&mailbox_str, peer);
-               astman_append(s, "VoiceMailbox: %s\r\n", mailbox_str->str);
+               peer_mailboxes_to_str(&tmp_str, peer);
+               astman_append(s, "VoiceMailbox: %s\r\n", tmp_str->str);
                astman_append(s, "TransferMode: %s\r\n", transfermode2str(peer->allowtransfer));
                astman_append(s, "LastMsgsSent: %d\r\n", peer->lastmsgssent);
                astman_append(s, "Maxforwards: %d\r\n", peer->maxforwards);
@@ -19287,6 +19332,10 @@ static char *sip_show_user(struct ast_cli_entry *e, int cmd, struct ast_cli_args
                print_group(a->fd, user->callgroup, 0);
                ast_cli(a->fd, "  Pickupgroup  : ");
                print_group(a->fd, user->pickupgroup, 0);
+               ast_cli(a->fd, "  Named Callgr : ");
+               print_named_groups(a->fd, user->named_callgroups, 0);
+               ast_cli(a->fd, "  Nam. Pickupgr: ");
+               print_named_groups(a->fd, user->named_pickupgroups, 0);
                ast_cli(a->fd, "  Callerid     : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), user->cid_name, user->cid_num, "<unspecified>"));
                ast_cli(a->fd, "  ACL          : %s\n", AST_CLI_YESNO(ast_acl_list_is_empty(user->acl) == 0));
                ast_cli(a->fd, "  Sess-Timers  : %s\n", stmode2str(user->stimer.st_mode_oper));
@@ -21039,6 +21088,18 @@ static int function_sippeer(struct ast_channel *chan, const char *cmd, char *dat
                ast_print_group(buf, len, peer->callgroup);
        } else  if (!strcasecmp(colname, "pickupgroup")) {
                ast_print_group(buf, len, peer->pickupgroup);
+       } else  if (!strcasecmp(colname, "namedcallgroup")) {
+               struct ast_str *tmp_str = ast_str_create(1024);
+               if (tmp_str) {
+                       ast_copy_string(buf, ast_print_namedgroups(&tmp_str, peer->named_callgroups), len);
+                       ast_free(tmp_str);
+               }
+       } else  if (!strcasecmp(colname, "namedpickupgroup")) {
+               struct ast_str *tmp_str = ast_str_create(1024);
+               if (tmp_str) {
+                       ast_copy_string(buf, ast_print_namedgroups(&tmp_str, peer->named_pickupgroups), len);
+                       ast_free(tmp_str);
+               }
        } else  if (!strcasecmp(colname, "useragent")) {
                ast_copy_string(buf, peer->useragent, len);
        } else  if (!strcasecmp(colname, "mailbox")) {
@@ -29431,6 +29492,10 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                }
        }
 
+       /* clear named callgroup and named pickup group container */
+       peer->named_callgroups = ast_unref_namedgroups(peer->named_callgroups);
+       peer->named_pickupgroups = ast_unref_namedgroups(peer->named_pickupgroups);
+
        for (; v || ((v = alt) && !(alt=NULL)); v = v->next) {
                if (!devstate_only) {
                        if (handle_common_options(&peerflags[0], &mask[0], v)) {
@@ -29668,6 +29733,10 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                                peer->allowtransfer = ast_true(v->value) ? TRANSFER_OPENFORALL : TRANSFER_CLOSED;
                        } else if (!strcasecmp(v->name, "pickupgroup")) {
                                peer->pickupgroup = ast_get_group(v->value);
+                       } else if (!strcasecmp(v->name, "namedcallgroup")) {
+                               peer->named_callgroups = ast_get_namedgroups(v->value);
+                       } else if (!strcasecmp(v->name, "namedpickupgroup")) {
+                               peer->named_pickupgroups = ast_get_namedgroups(v->value);
                        } else if (!strcasecmp(v->name, "allow")) {
                                int error = ast_parse_allow_disallow(&peer->prefs, peer->caps, v->value, TRUE);
                                if (error) {
index 270b505..c4054a8 100644 (file)
@@ -89,6 +89,8 @@ enum misdn_cfg_elements {
        MISDN_CFG_JITTERBUFFER_UPPER_THRESHOLD,              /* int */
        MISDN_CFG_CALLGROUP,           /* ast_group_t */
        MISDN_CFG_PICKUPGROUP,         /* ast_group_t */
+       MISDN_CFG_NAMEDCALLGROUP,      /* ast_namedgroups * */
+       MISDN_CFG_NAMEDPICKUPGROUP,    /* ast_namedgroups * */
        MISDN_CFG_MAX_IN,              /* int */
        MISDN_CFG_MAX_OUT,              /* int */
        MISDN_CFG_L1_TIMEOUT,          /* int */
index 30e96ec..32f92c3 100644 (file)
@@ -70,7 +70,8 @@ enum misdn_cfg_type {
        MISDN_CTYPE_BOOL,
        MISDN_CTYPE_BOOLINT,
        MISDN_CTYPE_MSNLIST,
-       MISDN_CTYPE_ASTGROUP
+       MISDN_CTYPE_ASTGROUP,
+       MISDN_CTYPE_ASTNAMEDGROUP
 };
 
 struct msn_list {
@@ -83,6 +84,7 @@ union misdn_cfg_pt {
        int *num;
        struct msn_list *ml;
        ast_group_t *grp;
+       struct ast_namedgroups *namgrp;
        void *any;
 };
 
@@ -330,6 +332,10 @@ static const struct misdn_cfg_spec port_spec[] = {
                "Callgroup." },
        { "pickupgroup", MISDN_CFG_PICKUPGROUP, MISDN_CTYPE_ASTGROUP, NO_DEFAULT, NONE,
                "Pickupgroup." },
+       { "namedcallgroup", MISDN_CFG_NAMEDCALLGROUP, MISDN_CTYPE_ASTNAMEDGROUP, NO_DEFAULT, NONE,
+               "Named callgroup." },
+       { "namedpickupgroup", MISDN_CFG_NAMEDPICKUPGROUP, MISDN_CTYPE_ASTNAMEDGROUP, NO_DEFAULT, NONE,
+               "Named pickupgroup." },
        { "max_incoming", MISDN_CFG_MAX_IN, MISDN_CTYPE_INT, "-1", NONE,
                "Defines the maximum amount of incoming calls per port for this group.\n"
                "\tCalls which exceed the maximum will be marked with the channel variable\n"
@@ -541,10 +547,13 @@ static void _free_port_cfg (void)
        for (j = 0; free_list[j]; ++j) {
                for (i = 0; i < NUM_PORT_ELEMENTS; ++i) {
                        if (free_list[j][i].any) {
-                               if (port_spec[i].type == MISDN_CTYPE_MSNLIST)
+                               if (port_spec[i].type == MISDN_CTYPE_MSNLIST) {
                                        _free_msn_list(free_list[j][i].ml);
-                               else
+                               } else if (port_spec[i].type == MISDN_CTYPE_ASTNAMEDGROUP) {
+                                       ast_unref_namedgroups(free_list[j][i].namgrp);
+                               } else {
                                        ast_free(free_list[j][i].any);
+                               }
                        }
                }
        }
@@ -588,6 +597,17 @@ void misdn_cfg_get(int port, enum misdn_cfg_elements elem, void *buf, int bufsiz
                                        } else
                                                memset(buf, 0, bufsize);
                                        break;
+                               case MISDN_CTYPE_ASTNAMEDGROUP:
+                                       if (bufsize >= sizeof(struct ast_namedgroups *)) {
+                                               if (port_cfg[port][place].namgrp) {
+                                                       *(struct ast_namedgroups **)buf = port_cfg[port][place].namgrp;
+                                               } else if (port_cfg[0][place].namgrp) {
+                                                       *(struct ast_namedgroups **)buf = port_cfg[0][place].namgrp;
+                                               } else {
+                                                       *(struct ast_namedgroups **)buf = NULL;
+                                               }
+                                       }
+                                       break;
                                default:
                                        if (port_cfg[port][place].any)
                                                memcpy(buf, port_cfg[port][place].any, bufsize);
@@ -831,6 +851,25 @@ void misdn_cfg_get_config_string (int port, enum misdn_cfg_elements elem, char*
                        else
                                snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
                        break;
+               case MISDN_CTYPE_ASTNAMEDGROUP:
+                       if (port_cfg[port][place].namgrp) {
+                               struct ast_str *tmp_str = ast_str_create(1024);
+                               if (tmp_str) {
+                                       snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
+                                                       ast_print_namedgroups(&tmp_str, port_cfg[port][place].namgrp));
+                                       ast_free(tmp_str);
+                               }
+                       } else if (port_cfg[0][place].namgrp) {
+                               struct ast_str *tmp_str = ast_str_create(1024);
+                               if (tmp_str) {
+                                       snprintf(buf, bufsize, " -> %s: %s", port_spec[place].name,
+                                                       ast_print_namedgroups(&tmp_str, port_cfg[0][place].namgrp));
+                                       ast_free(tmp_str);
+                               }
+                       } else {
+                               snprintf(buf, bufsize, " -> %s:", port_spec[place].name);
+                       }
+                       break;
                case MISDN_CTYPE_MSNLIST:
                        if (port_cfg[port][place].ml)
                                iter = port_cfg[port][place].ml;
@@ -984,6 +1023,9 @@ static int _parse (union misdn_cfg_pt *dest, const char *value, enum misdn_cfg_t
                }
                *(dest->grp) = ast_get_group(value);
                break;
+       case MISDN_CTYPE_ASTNAMEDGROUP:
+               dest->namgrp = ast_get_namedgroups(value);
+               break;
        }
 
        return re;
index a10d405..a821435 100644 (file)
@@ -1062,6 +1062,8 @@ struct sip_pvt {
        uint32_t init_icseq;                    /*!< Initial incoming seqno from first request */
        ast_group_t callgroup;                  /*!< Call group */
        ast_group_t pickupgroup;                /*!< Pickup group */
+       struct ast_namedgroups *named_callgroups;   /*!< Named call group */
+       struct ast_namedgroups *named_pickupgroups; /*!< Named pickup group */
        uint32_t lastinvite;                    /*!< Last seqno of invite */
        struct ast_flags flags[3];              /*!< SIP_ flags */
 
@@ -1328,6 +1330,8 @@ struct sip_peer {
        int rtpkeepalive;               /*!<  Send RTP packets for keepalive */
        ast_group_t callgroup;          /*!<  Call group */
        ast_group_t pickupgroup;        /*!<  Pickup group */
+       struct ast_namedgroups *named_callgroups;   /*!< Named call group */
+       struct ast_namedgroups *named_pickupgroups; /*!< Named pickup group */
        struct sip_proxy *outboundproxy;/*!< Outbound proxy for this peer */
        struct ast_dnsmgr_entry *dnsmgr;/*!<  DNS refresh manager for peer */
        struct ast_sockaddr addr;        /*!<  IP address of peer */
index b747a1e..460cd0b 100644 (file)
@@ -841,6 +841,15 @@ group=1
 ;
 callgroup=1
 pickupgroup=1
+;
+; Named ring groups (a.k.a. named call groups) and named pickup groups.
+; If a phone is ringing and it is a member of a group which is one of your
+; named pickup groups, then you can answer it by picking up and dialing *8#.
+; For simple offices, just make these both the same.
+; The number of named groups is not limited.
+;
+;namedcallgroup=engineering,sales,netgroup,protgroup
+;namedpickupgroup=sales
 
 ; Channel variable to be set for all calls from this channel
 ;setvar=CHANNEL=42
index bd51686..ac54dbc 100644 (file)
@@ -390,6 +390,13 @@ nodialtone=no
 ;callgroup=1
 ;pickupgroup=1
 
+; Named pickup groups and named call groups
+;
+; give a name to groups and configure any number of groups
+;
+;namedcallgroup=engineering,sales,netgroup,protgroup
+;namedpickupgroup=sales
+
 ; Set the outgoing caller id to the value.
 ;callerid="name" <number>
 
index d9ffb85..81ca998 100644 (file)
@@ -1432,6 +1432,8 @@ srvlookup=yes                   ; Enable DNS SRV lookups on outbound calls
 ;
 ;callgroup=1,3-4                 ; We are in caller groups 1,3,4
 ;pickupgroup=1,3-5               ; We can do call pick-p for call group 1,3,4,5
+;namedcallgroup=engineering,sales,netgroup,protgroup ; We are in named call groups engineering,sales,netgroup,protgroup
+;namedpickupgroup=sales          ; We can do call pick-p for named call group sales
 ;defaultip=192.168.0.60          ; IP address to use if peer has not registered
 ;deny=0.0.0.0/0.0.0.0            ; ACL: Control access to this account based on IP address
 ;permit=192.168.0.60/255.255.255.0
index 13d3f1f..e8785cf 100644 (file)
@@ -973,6 +973,17 @@ enum channelreloadreason {
        CHANNEL_ACL_RELOAD,
 };
 
+
+/*! \brief Structure to handle ao2-container for named groups */
+struct namedgroup_entry {
+       /*! string representation of group */
+       char *name;
+
+       /*! pre-built hash of groupname string */
+       unsigned int hash;
+};
+
+
 /*!
  * \note None of the datastore API calls lock the ast_channel they are using.
  *       So, the channel should be locked before calling the functions that
@@ -2417,9 +2428,22 @@ static inline enum ast_t38_state ast_channel_get_t38_state(struct ast_channel *c
 
 ast_group_t ast_get_group(const char *s);
 
-/*! \brief print call- and pickup groups into buffer */
+/*! \brief Print call- and pickup groups into buffer */
 char *ast_print_group(char *buf, int buflen, ast_group_t group);
 
+/*! \brief Opaque struct holding a namedgroups set, i.e. a set of group names */
+struct ast_namedgroups;
+
+/*! \brief Create an ast_namedgroups set with group name from comma separated string s */
+struct ast_namedgroups *ast_get_namedgroups(const char *s);
+struct ast_namedgroups *ast_unref_namedgroups(struct ast_namedgroups *groups);
+struct ast_namedgroups *ast_ref_namedgroups(struct ast_namedgroups *groups);
+/*! \brief Return TRUE if group a and b contain at least one common groupname */
+int ast_namedgroups_intersect(struct ast_namedgroups *a, struct ast_namedgroups *b);
+
+/*! \brief Print named call groups and named pickup groups ---*/
+char *ast_print_namedgroups(struct ast_str **buf, struct ast_namedgroups *groups);
+
 /*!
  * \brief Convert enum channelreloadreason to text string for manager event
  * \param reason The reason for reload (manager, cli, start etc)
@@ -3768,6 +3792,8 @@ void ast_channel_redirecting_set(struct ast_channel *chan, struct ast_party_redi
 void ast_channel_dtmf_tv_set(struct ast_channel *chan, struct timeval *value);
 void ast_channel_whentohangup_set(struct ast_channel *chan, struct timeval *value);
 void ast_channel_varshead_set(struct ast_channel *chan, struct varshead *value);
+struct timeval ast_channel_creationtime(struct ast_channel *chan);
+void ast_channel_creationtime_set(struct ast_channel *chan, struct timeval *value);
 
 /* List getters */
 struct ast_hangup_handler_list *ast_channel_hangup_handlers(struct ast_channel *chan);
@@ -3780,6 +3806,10 @@ ast_group_t ast_channel_callgroup(const struct ast_channel *chan);
 void ast_channel_callgroup_set(struct ast_channel *chan, ast_group_t value);
 ast_group_t ast_channel_pickupgroup(const struct ast_channel *chan);
 void ast_channel_pickupgroup_set(struct ast_channel *chan, ast_group_t value);
+struct ast_namedgroups *ast_channel_named_callgroups(const struct ast_channel *chan);
+void ast_channel_named_callgroups_set(struct ast_channel *chan, struct ast_namedgroups *value);
+struct ast_namedgroups *ast_channel_named_pickupgroups(const struct ast_channel *chan);
+void ast_channel_named_pickupgroups_set(struct ast_channel *chan, struct ast_namedgroups *value);
 
 /* Alertpipe accessors--the "internal" functions for channel.c use only */
 typedef enum {
index fdb4f00..4870902 100644 (file)
@@ -953,6 +953,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
        struct ast_format_cap *nativeformats;
        struct ast_sched_context *schedctx;
        struct ast_timer *timer;
+       struct timeval now;
 
        /* If shutting down, don't allocate any new channels */
        if (ast_shutting_down()) {
@@ -1042,6 +1043,9 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
        ast_channel_fin_set(tmp, global_fin);
        ast_channel_fout_set(tmp, global_fout);
 
+       now = ast_tvnow();
+       ast_channel_creationtime_set(tmp, &now);
+
        if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
                ast_channel_uniqueid_build(tmp, "%li.%d", (long) time(NULL),
                        ast_atomic_fetchadd_int(&uniqueint, 1));
@@ -2339,6 +2343,10 @@ static void ast_channel_destructor(void *obj)
        if (callid) {
                ast_callid_unref(callid);
        }
+
+       ast_channel_named_callgroups_set(chan, NULL);
+       ast_channel_named_pickupgroups_set(chan, NULL);
+
        ast_atomic_fetchadd_int(&chancount, -1);
 }
 
@@ -8074,6 +8082,113 @@ ast_group_t ast_get_group(const char *s)
        return group;
 }
 
+/*! \brief Comparison function used for named group container */
+static int namedgroup_cmp_cb(void *obj, void *arg, int flags)
+{
+       const struct namedgroup_entry *an = obj;
+       const struct namedgroup_entry *bn = arg;
+       return strcmp(an->name, bn->name) ? 0 : CMP_MATCH | CMP_STOP;
+}
+
+/*! \brief Hashing function used for named group container */
+static int namedgroup_hash_cb(const void *obj, const int flags)
+{
+       const struct namedgroup_entry *entry = obj;
+       return entry->hash;
+}
+
+static void namedgroup_dtor(void *obj)
+{
+       ast_free(((struct namedgroup_entry*)obj)->name);
+}
+
+/*! \internal \brief Actually a refcounted ao2_object. An indirect ao2_container to hide the implementation of namedgroups. */
+struct ast_namedgroups {
+       struct ao2_container *container;
+};
+
+static void ast_namedgroups_dtor(void *obj)
+{
+       ao2_cleanup(((struct ast_namedgroups *)obj)->container);
+}
+
+struct ast_namedgroups *ast_get_namedgroups(const char *s)
+{
+       struct ast_namedgroups *namedgroups;
+       char *piece;
+       char *c;
+
+       if (ast_strlen_zero(s)) {
+               return NULL;
+       }
+       c = ast_strdupa(s);
+       if (!c) {
+               return NULL;
+       }
+
+       namedgroups = ao2_alloc_options(sizeof(*namedgroups), ast_namedgroups_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
+       if (!namedgroups) {
+               return NULL;
+       }
+       namedgroups->container = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 19, namedgroup_hash_cb, namedgroup_cmp_cb);
+       if (!namedgroups->container) {
+               ao2_ref(namedgroups, -1);
+               return NULL;
+       }
+
+       /*! \brief Remove leading and trailing whitespace */
+       c = ast_strip(c);
+
+       while ((piece = strsep(&c, ","))) {
+               struct namedgroup_entry *entry;
+
+               /* remove leading/trailing whitespace */
+               piece = ast_strip(piece);
+               if (strlen(piece) == 0) {
+                       ast_log(LOG_ERROR, "Syntax error parsing named group configuration '%s' at '%s'. Ignoring.\n", s, piece);
+                       continue;
+               }
+
+               entry = ao2_alloc_options(sizeof(*entry), namedgroup_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
+               if (!entry) {
+                       ao2_ref(namedgroups, -1);
+                       return NULL;
+               }
+               entry->name = ast_strdup(piece);
+               if (!entry->name) {
+                       ao2_ref(entry, -1);
+                       ao2_ref(namedgroups, -1);
+                       return NULL;
+               }
+               entry->hash = ast_str_hash(entry->name);
+               /* every group name may exist only once, delete duplicates */
+               ao2_find(namedgroups->container, entry, OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
+               ao2_link(namedgroups->container, entry);
+               ao2_ref(entry, -1);
+       }
+
+       if (ao2_container_count(namedgroups->container) == 0) {
+               ao2_ref(namedgroups, -1);
+               namedgroups = NULL;
+       }
+
+       return namedgroups;
+}
+
+struct ast_namedgroups *ast_unref_namedgroups(struct ast_namedgroups *groups)
+{
+       ao2_cleanup(groups);
+       return NULL;
+}
+
+struct ast_namedgroups *ast_ref_namedgroups(struct ast_namedgroups *groups)
+{
+       if (groups) {
+               ao2_ref(groups, 1);
+       }
+       return groups;
+}
+
 static int (*ast_moh_start_ptr)(struct ast_channel *, const char *, const char *) = NULL;
 static void (*ast_moh_stop_ptr)(struct ast_channel *) = NULL;
 static void (*ast_moh_cleanup_ptr)(struct ast_channel *) = NULL;
@@ -8304,6 +8419,59 @@ char *ast_print_group(char *buf, int buflen, ast_group_t group)
        return buf;
 }
 
+/*! \brief Print named call group and named pickup group ---*/
+char *ast_print_namedgroups(struct ast_str **buf, struct ast_namedgroups *group)
+{
+       struct namedgroup_entry *ng;
+       int first = 1;
+       struct ao2_iterator it;
+
+       if (group == NULL) {
+               return ast_str_buffer(*buf);
+       }
+
+       it = ao2_iterator_init(group->container, 0);
+       while ((ng = ao2_iterator_next(&it))) {
+               if (!first) {
+                       ast_str_append(buf, 0, ", ");
+               } else {
+                       first = 0;
+               }
+               ast_str_append(buf, 0, "%s", ng->name);
+               ao2_ref(ng, -1);
+       }
+       ao2_iterator_destroy(&it);
+
+       return ast_str_buffer(*buf);
+}
+
+static int namedgroup_match(void *obj, void *arg, int flags)
+{
+       void *match;
+
+       match = ao2_find(arg, obj, OBJ_POINTER);
+       ao2_cleanup(match);
+
+       return match ? CMP_MATCH | CMP_STOP : 0;
+}
+
+int ast_namedgroups_intersect(struct ast_namedgroups *a, struct ast_namedgroups *b)
+{
+       /*
+        * Do a and b intersect? Since b is hash table average time complexity is O(|a|)
+        */
+       void *match;
+
+       if (!a || !b) {
+               return 0;
+       }
+
+       match = ao2_callback(a->container, 0, namedgroup_match, b->container);
+       ao2_cleanup(match);
+
+       return match != NULL;
+}
+
 void ast_set_variables(struct ast_channel *chan, struct ast_variable *vars)
 {
        struct ast_variable *cur;
index b659fb9..368940f 100644 (file)
@@ -136,6 +136,9 @@ struct ast_channel {
        struct varshead varshead;                       /*!< A linked list for channel variables. See \ref AstChanVar */
        ast_group_t callgroup;                          /*!< Call group for call pickups */
        ast_group_t pickupgroup;                        /*!< Pickup group - which calls groups can be picked up? */
+       struct ast_namedgroups *named_callgroups;       /*!< Named call group for call pickups */
+       struct ast_namedgroups *named_pickupgroups;     /*!< Named pickup group - which call groups can be picked up? */
+       struct timeval creationtime;                    /*!< The time of channel creation */
        struct ast_readq_list readq;
        struct ast_jb jb;                               /*!< The jitterbuffer state */
        struct timeval dtmf_tv;                         /*!< The time that an in process digit began, or the last digit ended */
@@ -983,6 +986,14 @@ void ast_channel_varshead_set(struct ast_channel *chan, struct varshead *value)
 {
        chan->varshead = *value;
 }
+struct timeval ast_channel_creationtime(struct ast_channel *chan)
+{
+       return chan->creationtime;
+}
+void ast_channel_creationtime_set(struct ast_channel *chan, struct timeval *value)
+{
+       chan->creationtime = *value;
+}
 
 /* Evil softhangup accessors */
 int ast_channel_softhangup_internal_flag(struct ast_channel *chan)
@@ -1026,6 +1037,24 @@ void ast_channel_pickupgroup_set(struct ast_channel *chan, ast_group_t value)
 {
        chan->pickupgroup = value;
 }
+struct ast_namedgroups *ast_channel_named_callgroups(const struct ast_channel *chan)
+{
+       return chan->named_callgroups;
+}
+void ast_channel_named_callgroups_set(struct ast_channel *chan, struct ast_namedgroups *value)
+{
+       ast_unref_namedgroups(chan->named_callgroups);
+       chan->named_callgroups = ast_ref_namedgroups(value);
+}
+struct ast_namedgroups *ast_channel_named_pickupgroups(const struct ast_channel *chan)
+{
+       return chan->named_pickupgroups;
+}
+void ast_channel_named_pickupgroups_set(struct ast_channel *chan, struct ast_namedgroups *value)
+{
+       ast_unref_namedgroups(chan->named_pickupgroups);
+       chan->named_pickupgroups = ast_ref_namedgroups(value);
+}
 
 /* Alertpipe functions */
 int ast_channel_alert_write(struct ast_channel *chan)
index 5d0f8ec..0794a2f 100644 (file)
@@ -7679,10 +7679,19 @@ static int find_channel_by_group(void *obj, void *arg, void *data, int flags)
        struct ast_channel *chan = data;/*!< Channel wanting to pickup call */
 
        ast_channel_lock(target);
-       if (chan != target && (ast_channel_pickupgroup(chan) & ast_channel_callgroup(target))
-               && ast_can_pickup(target)) {
-               /* Return with the channel still locked on purpose */
-               return CMP_MATCH | CMP_STOP;
+
+       /*
+        * Both, callgroup and namedcallgroup pickup variants are matched independently.
+        * Checking for named group match is done last since it's a rather expensive operation.
+        */
+       if (chan != target && ast_can_pickup(target)
+               && ((ast_channel_pickupgroup(chan) & ast_channel_callgroup(target))
+                       || (ast_namedgroups_intersect(ast_channel_named_pickupgroups(chan), ast_channel_named_callgroups(target))))) {
+               /*
+                * Return with the channel unlocked on purpose, else we would lock many channels with the chance for deadlock
+                */
+               ast_channel_unlock(target);
+               return CMP_MATCH;
        }
        ast_channel_unlock(target);
 
@@ -7700,11 +7709,36 @@ static int find_channel_by_group(void *obj, void *arg, void *data, int flags)
 int ast_pickup_call(struct ast_channel *chan)
 {
        struct ast_channel *target;/*!< Potential pickup target */
+       struct ao2_iterator *targets_it;/*!< Potential pickup targets, must find the oldest of them */
+       struct ast_channel *candidate;/*!< Potential new older target */
        int res = -1;
        ast_debug(1, "pickup attempt by %s\n", ast_channel_name(chan));
 
-       /* The found channel is already locked. */
-       target = ast_channel_callback(find_channel_by_group, NULL, chan, 0);
+       /*
+        * Transfer all pickup-able channels to another container-iterator.
+        * Iterate it to find the oldest channel.
+        */
+       targets_it = (struct ao2_iterator *)ast_channel_callback(find_channel_by_group, NULL, chan, OBJ_MULTIPLE);
+
+       target = NULL;
+       while ((candidate = ao2_iterator_next(targets_it))) {
+               if (!target || ast_tvcmp(ast_channel_creationtime(candidate), ast_channel_creationtime(target)) < 0) {
+                       target = candidate;
+               }
+               ast_channel_unref(candidate);
+       }
+       if (target) {
+               /* The found channel must be locked and ref'd. */
+               ast_channel_lock(ast_channel_ref(target));
+               /* Recheck pickup ability */
+               if (!ast_can_pickup(target)) {
+                       ast_channel_unlock(target);
+                       target = ast_channel_unref(target);/* Bad luck */
+               }
+       }
+
+       ao2_iterator_destroy(targets_it);
+
        if (target) {
                ast_log(LOG_NOTICE, "pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan));