(closes issue #6113)
authorJeff Peeler <jpeeler@digium.com>
Mon, 21 Apr 2008 23:42:45 +0000 (23:42 +0000)
committerJeff Peeler <jpeeler@digium.com>
Mon, 21 Apr 2008 23:42:45 +0000 (23:42 +0000)
Reported by: oej
Tested by: jpeeler

This patch implements multiple parking lots for parked calls. The default parkinglot is used by default, however setting the channel variable PARKINGLOT in the dialplan will allow use of any other configured parkinglot. See configs/features.conf.sample for more details on setting up another non-default parkinglot. Also, one can (currently) set the default parkinglot to use in the driver configuration file via the parkinglot option.

Patch initially written by oej, brought up to date and finalized by mvanbaak, and then stabilized and converted to astobj2 by me.

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

16 files changed:
apps/app_dumpchan.c
channels/chan_console.c
channels/chan_gtalk.c
channels/chan_iax2.c
channels/chan_jingle.c
channels/chan_mgcp.c
channels/chan_sip.c
channels/chan_skinny.c
channels/chan_unistim.c
channels/chan_zap.c
configs/features.conf.sample
configs/iax.conf.sample
configs/sip.conf.sample
funcs/func_channel.c
include/asterisk/channel.h
main/features.c

index 37e040e..d01ea7a 100644 (file)
@@ -75,6 +75,7 @@ static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
                        "CallerIDName=       %s\n"
                        "DNIDDigits=         %s\n"
                        "RDNIS=              %s\n"
+                       "Parkinglot=         %s\n"
                        "Language=           %s\n"
                        "State=              %s (%d)\n"
                        "Rings=              %d\n"
@@ -103,6 +104,7 @@ static int serialize_showchan(struct ast_channel *c, char *buf, size_t size)
                        S_OR(c->cid.cid_name, "(N/A)"),
                        S_OR(c->cid.cid_dnid, "(N/A)"),
                        S_OR(c->cid.cid_rdnis, "(N/A)"),
+                       c->parkinglot,
                        c->language,
                        ast_state2str(c->_state),
                        c->_state,
index 7d62e62..0fe3cbe 100644 (file)
@@ -141,6 +141,8 @@ static struct console_pvt {
                AST_STRING_FIELD(mohinterpret);
                /*! Default language */
                AST_STRING_FIELD(language);
+               /*! Default parkinglot */
+               AST_STRING_FIELD(parkinglot);
        );
        /*! Current channel for this device */
        struct ast_channel *owner;
@@ -1004,6 +1006,7 @@ static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_a
                               "=== ---> CallerID Name:    %s\n"
                               "=== ---> MOH Interpret:    %s\n"
                               "=== ---> Language:         %s\n"
+                              "=== ---> Parkinglot:       %s\n"
                               "=== ---> Muted:            %s\n"
                               "=== ---> Auto-Answer:      %s\n"
                               "=== ---> Override Context: %s\n"
@@ -1011,7 +1014,7 @@ static char *cli_list_devices(struct ast_cli_entry *e, int cmd, struct ast_cli_a
                        pvt->name, (pvt == active_pvt) ? "Yes" : "No",
                        pvt->input_device, pvt->output_device, pvt->context,
                        pvt->exten, pvt->cid_num, pvt->cid_name, pvt->mohinterpret,
-                       pvt->language, pvt->muted ? "Yes" : "No", pvt->autoanswer ? "Yes" : "No",
+                       pvt->language, pvt->parkinglot, pvt->muted ? "Yes" : "No", pvt->autoanswer ? "Yes" : "No",
                        pvt->overridecontext ? "Yes" : "No");
 
                console_pvt_unlock(pvt);
@@ -1236,6 +1239,7 @@ static void set_pvt_defaults(struct console_pvt *pvt)
                ast_string_field_set(pvt, language, "");
                ast_string_field_set(pvt, cid_num, "");
                ast_string_field_set(pvt, cid_name, "");
+               ast_string_field_set(pvt, parkinglot, "");
        
                pvt->overridecontext = 0;
                pvt->autoanswer = 0;
@@ -1248,6 +1252,7 @@ static void set_pvt_defaults(struct console_pvt *pvt)
                ast_string_field_set(pvt, language, globals.language);
                ast_string_field_set(pvt, cid_num, globals.cid_num);
                ast_string_field_set(pvt, cid_name, globals.cid_name);
+               ast_string_field_set(pvt, parkinglot, globals.parkinglot);
 
                pvt->overridecontext = globals.overridecontext;
                pvt->autoanswer = globals.autoanswer;
@@ -1287,6 +1292,7 @@ static void store_config_core(struct console_pvt *pvt, const char *var, const ch
        CV_F("callerid", store_callerid(pvt, value));
        CV_BOOL("overridecontext", pvt->overridecontext);
        CV_BOOL("autoanswer", pvt->autoanswer);
+       CV_STRFIELD("parkinglot", pvt, parkinglot);
 
        if (pvt != &globals) {
                CV_F("active", set_active(pvt, value))
index fb58aa4..04ea608 100644 (file)
@@ -142,6 +142,7 @@ struct gtalk {
        int amaflags;                   /*!< AMA Flags */
        char user[AJI_MAX_JIDLEN];
        char context[AST_MAX_CONTEXT];
+       char parkinglot[AST_MAX_CONTEXT];       /*!<  Parkinglot */
        char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Account code */
        int capability;
        ast_group_t callgroup;  /*!< Call group */
@@ -1024,6 +1025,8 @@ static struct ast_channel *gtalk_new(struct gtalk *client, struct gtalk_pvt *i,
                ast_string_field_set(tmp, language, client->language);
        if (!ast_strlen_zero(client->musicclass))
                ast_string_field_set(tmp, musicclass, client->musicclass);
+       if (!ast_strlen_zero(client->parkinglot))
+               ast_string_field_set(tmp, parkinglot, client->parkinglot);
        i->owner = tmp;
        ast_module_ref(ast_module_info->self);
        ast_copy_string(tmp->context, client->context, sizeof(tmp->context));
@@ -1794,6 +1797,8 @@ static int gtalk_create_member(char *label, struct ast_variable *var, int allowg
                        ast_parse_allow_disallow(&member->prefs, &member->capability, var->value, 1);
                else if (!strcasecmp(var->name, "context"))
                        ast_copy_string(member->context, var->value, sizeof(member->context));
+               else if (!strcasecmp(var->name, "parkinglot"))
+                       ast_copy_string(member->parkinglot, var->value, sizeof(member->parkinglot));
 #if 0
                else if (!strcasecmp(var->name, "candidate")) {
                        candidate = gtalk_create_candidate(var->value);
@@ -1832,6 +1837,7 @@ static int gtalk_load_config(void)
        char *cat = NULL;
        struct ast_config *cfg = NULL;
        char context[AST_MAX_CONTEXT];
+       char parkinglot[AST_MAX_CONTEXT];
        int allowguest = 1;
        struct ast_variable *var;
        struct gtalk *member;
@@ -1864,6 +1870,8 @@ static int gtalk_load_config(void)
                        ast_parse_allow_disallow(&prefs, &global_capability, var->value, 1);
                else if (!strcasecmp(var->name, "context"))
                        ast_copy_string(context, var->value, sizeof(context));
+               else if (!strcasecmp(var->name, "parkinglot"))
+                       ast_copy_string(parkinglot, var->value, sizeof(parkinglot));
                else if (!strcasecmp(var->name, "bindaddr")) {
                        if (!(hp = ast_gethostbyname(var->value, &ahp))) {
                                ast_log(LOG_WARNING, "Invalid address: %s\n", var->value);
@@ -1892,6 +1900,7 @@ static int gtalk_load_config(void)
                                ast_copy_string(member->name, "guest", sizeof(member->name));
                                ast_copy_string(member->user, "guest", sizeof(member->user));
                                ast_copy_string(member->context, context, sizeof(member->context));
+                               ast_copy_string(member->parkinglot, parkinglot, sizeof(member->parkinglot));
                                member->allowguest = allowguest;
                                member->prefs = prefs;
                                while (var) {
@@ -1904,6 +1913,9 @@ static int gtalk_load_config(void)
                                        else if (!strcasecmp(var->name, "context"))
                                                ast_copy_string(member->context, var->value,
                                                                                sizeof(member->context));
+                                       else if (!strcasecmp(var->name, "parkinglot"))
+                                               ast_copy_string(member->parkinglot, var->value,
+                                                                               sizeof(member->parkinglot));
 /*  Idea to allow for custom candidates  */
 /*
                                        else if (!strcasecmp(var->name, "candidate")) {
index d930340..c11fa1c 100644 (file)
@@ -26,6 +26,8 @@
  * \arg \ref Config_iax
  *
  * \ingroup channel_drivers
+ * 
+ * \todo Implement musicclass settings for IAX2 devices
  */
 
 /*** MODULEINFO
@@ -140,6 +142,7 @@ static int trunk_timed, trunk_untimed, trunk_maxmtu, trunk_nmaxmtu ;        /*!< Trunk
 
 
 static char context[80] = "default";
+static char default_parkinglot[AST_MAX_CONTEXT];
 
 static char language[MAX_LANGUAGE] = "";
 static char regcontext[AST_MAX_CONTEXT] = "";
@@ -298,6 +301,7 @@ struct iax2_user {
                AST_STRING_FIELD(language);
                AST_STRING_FIELD(cid_num);
                AST_STRING_FIELD(cid_name);
+               AST_STRING_FIELD(parkinglot);           /*!< Default parkinglot for device */
        );
        
        int authmethods;
@@ -333,6 +337,7 @@ struct iax2_peer {
                AST_STRING_FIELD(cid_num);              /*!< Default context (for transfer really) */
                AST_STRING_FIELD(cid_name);             /*!< Default context (for transfer really) */
                AST_STRING_FIELD(zonetag);              /*!< Time Zone */
+               AST_STRING_FIELD(parkinglot);   /*!< Default parkinglot for device */
        );
        struct ast_codec_pref prefs;
        struct ast_dnsmgr_entry *dnsmgr;                /*!< DNS refresh manager */
@@ -580,6 +585,8 @@ struct chan_iax2_pvt {
                AST_STRING_FIELD(mohsuggest);
                /*! received OSP token */
                AST_STRING_FIELD(osptoken);
+               /*! Default parkinglot */
+               AST_STRING_FIELD(parkinglot);
        );
        
        /*! permitted authentication methods */
@@ -1555,6 +1562,7 @@ static int __find_callno(unsigned short callno, unsigned short dcallno, struct s
                        ast_string_field_set(iaxs[x], accountcode, accountcode);
                        ast_string_field_set(iaxs[x], mohinterpret, mohinterpret);
                        ast_string_field_set(iaxs[x], mohsuggest, mohsuggest);
+                       ast_string_field_set(iaxs[x], parkinglot, default_parkinglot);
                } else {
                        ast_log(LOG_WARNING, "Out of resources\n");
                        ast_mutex_unlock(&iaxsl[x]);
@@ -2437,6 +2445,7 @@ static char *handle_cli_iax2_show_peer(struct ast_cli_entry *e, int cmd, struct
                ast_cli(a->fd, "  * Name       : %s\n", peer->name);
                ast_cli(a->fd, "  Secret       : %s\n", ast_strlen_zero(peer->secret) ? "<Not set>" : "<Set>");
                ast_cli(a->fd, "  Context      : %s\n", peer->context);
+               ast_cli(a->fd, "  Parking lot  : %s\n", peer->parkinglot);
                ast_cli(a->fd, "  Mailbox      : %s\n", peer->mailbox);
                ast_cli(a->fd, "  Dynamic      : %s\n", ast_test_flag(peer, IAX_DYNAMIC) ? "Yes" : "No");
                ast_cli(a->fd, "  Callerid     : %s\n", ast_callerid_merge(cbuf, sizeof(cbuf), peer->cid_name, peer->cid_num, "<unspecified>"));
@@ -3916,6 +3925,8 @@ static struct ast_channel *ast_iax2_new(int callno, int state, int capability)
        tmp->writeformat = ast_best_codec(capability);
        tmp->tech_pvt = CALLNO_TO_PTR(i->callno);
 
+       if (!ast_strlen_zero(i->parkinglot))
+               ast_string_field_set(tmp, parkinglot, i->parkinglot);
        /* Don't use ast_set_callerid() here because it will
         * generate a NewCallerID event before the NewChannel event */
        if (!ast_strlen_zero(i->ani))
@@ -5739,6 +5750,8 @@ static int check_access(int callno, struct sockaddr_in *sin, struct iax_ies *ies
                        ast_string_field_set(iaxs[callno], mohinterpret, user->mohinterpret);
                if (!ast_strlen_zero(user->mohsuggest))
                        ast_string_field_set(iaxs[callno], mohsuggest, user->mohsuggest);
+               if (!ast_strlen_zero(user->parkinglot))
+                       ast_string_field_set(iaxs[callno], parkinglot, user->parkinglot);
                if (user->amaflags)
                        iaxs[callno]->amaflags = user->amaflags;
                if (!ast_strlen_zero(user->language))
@@ -10421,6 +10434,8 @@ static struct iax2_user *build_user(const char *name, struct ast_variable *v, st
                                ast_string_field_set(user, mohinterpret, v->value);
                        } else if (!strcasecmp(v->name, "mohsuggest")) {
                                ast_string_field_set(user, mohsuggest, v->value);
+                       } else if (!strcasecmp(v->name, "parkinglot")) {
+                               ast_string_field_set(user, parkinglot, v->value);
                        } else if (!strcasecmp(v->name, "language")) {
                                ast_string_field_set(user, language, v->value);
                        } else if (!strcasecmp(v->name, "amaflags")) {
@@ -10622,6 +10637,8 @@ static int set_config(char *config_file, int reload)
 #ifdef SO_NO_CHECK
        nochecksums = 0;
 #endif
+       /* Reset default parking lot */
+       default_parkinglot[0] = '\0';
 
        min_reg_expire = IAX_DEFAULT_REG_EXPIRE;
        max_reg_expire = IAX_DEFAULT_REG_EXPIRE;
@@ -10818,6 +10835,8 @@ static int set_config(char *config_file, int reload)
                } else if (!strcasecmp(v->name, "cos")) {
                        if (ast_str2cos(v->value, &cos))
                                ast_log(LOG_WARNING, "Invalid cos value at line %d, refer to QoS documentation\n", v->lineno);
+               } else if (!strcasecmp(v->name, "parkinglot")) {
+                       ast_copy_string(default_parkinglot, v->value, sizeof(default_parkinglot));
                } else if (!strcasecmp(v->name, "accountcode")) {
                        ast_copy_string(accountcode, v->value, sizeof(accountcode));
                } else if (!strcasecmp(v->name, "mohinterpret")) {
index 52a4693..7285549 100644 (file)
@@ -151,6 +151,7 @@ struct jingle {
        int allowguest;
        char language[MAX_LANGUAGE];    /*!<  Default language for prompts */
        char musicclass[MAX_MUSICCLASS];        /*!<  Music on Hold class */
+       char parkinglot[AST_MAX_CONTEXT];   /*!< Parkinglot */
 };
 
 struct jingle_container {
@@ -1741,6 +1742,9 @@ static int jingle_load_config(void)
                                        else if (!strcasecmp(var->name, "context"))
                                                ast_copy_string(member->context, var->value,
                                                                                sizeof(member->context));
+                                       else if (!strcasecmp(var->name, "parkinglot"))
+                                               ast_copy_string(member->parkinglot, var->value,
+                                                                               sizeof(member->parkinglot));
 /*  Idea to allow for custom candidates  */
 /*
                                        else if (!strcasecmp(var->name, "candidate")) {
index f18dac3..021999a 100644 (file)
@@ -143,6 +143,7 @@ static char context[AST_MAX_EXTENSION] = "default";
 
 static char language[MAX_LANGUAGE] = "";
 static char musicclass[MAX_MUSICCLASS] = "";
+static char parkinglot[AST_MAX_CONTEXT];
 static char cid_num[AST_MAX_EXTENSION] = "";
 static char cid_name[AST_MAX_EXTENSION] = "";
 
@@ -317,6 +318,7 @@ struct mgcp_endpoint {
        char musicclass[MAX_MUSICCLASS];
        char curtone[80];                       /*!< Current tone */
        char mailbox[AST_MAX_EXTENSION];
+       char parkinglot[AST_MAX_CONTEXT];   /*!< Parkinglot */
        struct ast_event_sub *mwi_event_sub;
        ast_group_t callgroup;
        ast_group_t pickupgroup;
@@ -3685,6 +3687,8 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
                                }
                        } else if (!strcasecmp(v->name, "musiconhold")) {
                                ast_copy_string(musicclass, v->value, sizeof(musicclass));
+                       } else if (!strcasecmp(v->name, "parkinglot")) {
+                               ast_copy_string(parkinglot, v->value, sizeof(parkinglot));
                        } else if (!strcasecmp(v->name, "callgroup")) {
                                cur_callergroup = ast_get_group(v->value);
                        } else if (!strcasecmp(v->name, "pickupgroup")) {
@@ -3748,6 +3752,7 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
                                        ast_copy_string(e->language, language, sizeof(e->language));
                                        ast_copy_string(e->musicclass, musicclass, sizeof(e->musicclass));
                                        ast_copy_string(e->mailbox, mailbox, sizeof(e->mailbox));
+                                       ast_copy_string(e->parkinglot, parkinglot, sizeof(e->parkinglot));
                                        if (!ast_strlen_zero(e->mailbox)) {
                                                char *mailbox, *context;
                                                context = mailbox = ast_strdupa(e->mailbox);
@@ -3856,6 +3861,7 @@ static struct mgcp_gateway *build_gateway(char *cat, struct ast_variable *v)
                                        ast_copy_string(e->language, language, sizeof(e->language));
                                        ast_copy_string(e->musicclass, musicclass, sizeof(e->musicclass));
                                        ast_copy_string(e->mailbox, mailbox, sizeof(e->mailbox));
+                                       ast_copy_string(e->parkinglot, parkinglot, sizeof(e->parkinglot));
                                        if (!ast_strlen_zero(mailbox)) {
                                                ast_verb(3, "Setting mailbox '%s' on %s@%s\n", mailbox, gw->name, e->name);
                                        }
index 79fc483..31bd576 100644 (file)
@@ -663,6 +663,7 @@ static char default_vmexten[AST_MAX_EXTENSION];
 static char default_mohinterpret[MAX_MUSICCLASS];  /*!< Global setting for moh class to use when put on hold */
 static char default_mohsuggest[MAX_MUSICCLASS];           /*!< Global setting for moh class to suggest when putting 
                                                     *   a bridged channel on hold */
+static char default_parkinglot[AST_MAX_CONTEXT]; /*!< Parkinglot */
 static int default_maxcallbitrate;     /*!< Maximum bitrate for call */
 static struct ast_codec_pref default_prefs;            /*!< Default codec prefs */
 
@@ -1219,6 +1220,7 @@ struct sip_pvt {
                AST_STRING_FIELD(rpid);         /*!< Our RPID header */
                AST_STRING_FIELD(rpid_from);    /*!< Our RPID From header */
                AST_STRING_FIELD(url);          /*!< URL to be sent with next message to peer */
+               AST_STRING_FIELD(parkinglot);           /*!< Parkinglot */
        );
        struct sip_socket socket;               /*!< The socket used for this dialog */
        unsigned int ocseq;                     /*!< Current outgoing seqno */
@@ -1405,6 +1407,7 @@ struct sip_user {
        char language[MAX_LANGUAGE];    /*!< Default language for this user */
        char mohinterpret[MAX_MUSICCLASS];/*!< Music on Hold class */
        char mohsuggest[MAX_MUSICCLASS];/*!< Music on Hold class */
+       char parkinglot[AST_MAX_CONTEXT];/*!< Parkinglot */
        char useragent[256];            /*!< User agent in SIP request */
        struct ast_codec_pref prefs;    /*!< codec prefs */
        ast_group_t callgroup;          /*!< Call group */
@@ -1474,6 +1477,7 @@ struct sip_peer {
        char language[MAX_LANGUAGE];    /*!<  Default language for prompts */
        char mohinterpret[MAX_MUSICCLASS];/*!<  Music on Hold class */
        char mohsuggest[MAX_MUSICCLASS];/*!<  Music on Hold class */
+       char parkinglot[AST_MAX_CONTEXT];/*!<  Parkinglot */
        char useragent[256];            /*!<  User agent in SIP request (saved from registration) */
        struct ast_codec_pref prefs;    /*!<  codec prefs */
        int lastmsgssent;
@@ -4246,6 +4250,7 @@ static int create_addr_from_peer(struct sip_pvt *dialog, struct sip_peer *peer)
        ast_string_field_set(dialog, tohost, peer->tohost);
        ast_string_field_set(dialog, fullcontact, peer->fullcontact);
        ast_string_field_set(dialog, context, peer->context);
+       ast_string_field_set(dialog, parkinglot, peer->parkinglot);
        dialog->outboundproxy = obproxy_get(dialog, peer);
        dialog->callgroup = peer->callgroup;
        dialog->pickupgroup = peer->pickupgroup;
@@ -6076,6 +6081,7 @@ static struct sip_pvt *sip_alloc(ast_string_field callid, struct sockaddr_in *si
                p->t38.jointcapability = p->t38.capability;
        }
        ast_string_field_set(p, context, default_context);
+       ast_string_field_set(p, parkinglot, default_parkinglot);
 
 
        /* Add to active dialog list */
@@ -11578,6 +11584,7 @@ static enum check_auth_result check_user_ok(struct sip_pvt *p, char *of,
                ast_string_field_set(p, language, user->language);
                ast_string_field_set(p, mohsuggest, user->mohsuggest);
                ast_string_field_set(p, mohinterpret, user->mohinterpret);
+               ast_string_field_set(p, parkinglot, user->parkinglot);
                p->allowtransfer = user->allowtransfer;
                p->amaflags = user->amaflags;
                p->callgroup = user->callgroup;
@@ -11665,6 +11672,7 @@ static enum check_auth_result check_peer_ok(struct sip_pvt *p, char *of,
        ast_string_field_set(p, subscribecontext, peer->subscribecontext);
        ast_string_field_set(p, mohinterpret, peer->mohinterpret);
        ast_string_field_set(p, mohsuggest, peer->mohsuggest);
+       ast_string_field_set(p, parkinglot, peer->parkinglot);
        if (peer->callingpres)  /* Peer calling pres setting will override RPID */
                p->callingpres = peer->callingpres;
        if (peer->maxms && peer->lastms)
@@ -20484,6 +20492,8 @@ static struct sip_user *build_user(const char *name, struct ast_variable *v, str
                        user->callgroup = ast_get_group(v->value);
                } else if (!strcasecmp(v->name, "pickupgroup")) {
                        user->pickupgroup = ast_get_group(v->value);
+               } else if (!strcasecmp(v->name, "parkinglot")) {
+                       ast_copy_string(user->parkinglot, v->value, sizeof(user->parkinglot));
                } else if (!strcasecmp(v->name, "language")) {
                        ast_copy_string(user->language, v->value, sizeof(user->language));
                } else if (!strcasecmp(v->name, "mohinterpret")) {
@@ -20861,6 +20871,8 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, str
                        ast_copy_string(peer->mohinterpret, v->value, sizeof(peer->mohinterpret));
                } else if (!strcasecmp(v->name, "mohsuggest")) {
                        ast_copy_string(peer->mohsuggest, v->value, sizeof(peer->mohsuggest));
+               } else if (!strcasecmp(v->name, "parkinglot")) {
+                       ast_copy_string(peer->parkinglot, v->value, sizeof(peer->parkinglot));
                } else if (!strcasecmp(v->name, "mailbox")) {
                        add_peer_mailboxes(peer, v->value);
                } else if (!strcasecmp(v->name, "subscribemwi")) {
index 198b464..c4458ba 100644 (file)
@@ -968,6 +968,7 @@ static char mohsuggest[MAX_MUSICCLASS] = "";
 static char cid_num[AST_MAX_EXTENSION] = "";
 static char cid_name[AST_MAX_EXTENSION] = "";
 static char linelabel[AST_MAX_EXTENSION] ="";
+static char parkinglot[AST_MAX_CONTEXT] ="";
 static int nat = 0;
 static ast_group_t cur_callergroup = 0;
 static ast_group_t cur_pickupgroup = 0;
@@ -1162,6 +1163,7 @@ struct skinny_line {
        char vmexten[AST_MAX_EXTENSION];
        char regexten[AST_MAX_EXTENSION];               /* Extension for auto-extensions */
        char regcontext[AST_MAX_CONTEXT];               /* Context for auto-extensions */
+       char parkinglot[AST_MAX_CONTEXT];               /* Parkinglot for parkedcalls */
        char mohinterpret[MAX_MUSICCLASS];
        char mohsuggest[MAX_MUSICCLASS];
        char lastnumberdialed[AST_MAX_EXTENSION];       /* Last number that was dialed - used for redial */
@@ -3024,6 +3026,8 @@ static struct skinny_device *build_device(const char *cat, struct ast_variable *
                                ast_copy_string(linelabel, v->value, sizeof(linelabel));
                        } else if (!strcasecmp(v->name, "setvar")) {
                                chanvars = add_var(v->value, chanvars);
+                       } else if ( !strcasecmp(v->name, "parkinglot")) {
+                               ast_copy_string(parkinglot, v->value, sizeof(parkinglot));
                        } else if (!strcasecmp(v->name, "speeddial")) {
                                if (!(sd = ast_calloc(1, sizeof(*sd)))) {
                                        return NULL;
@@ -3077,6 +3081,7 @@ static struct skinny_device *build_device(const char *cat, struct ast_variable *
                                        ast_copy_string(l->cid_num, cid_num, sizeof(l->cid_num));
                                        ast_copy_string(l->cid_name, cid_name, sizeof(l->cid_name));
                                        ast_copy_string(l->label, linelabel, sizeof(l->label));
+                                       ast_copy_string(l->parkinglot, parkinglot, sizeof(l->parkinglot));
                                        ast_copy_string(l->language, language, sizeof(l->language));
                                        ast_copy_string(l->mohinterpret, mohinterpret, sizeof(l->mohinterpret));
                                        ast_copy_string(l->mohsuggest, mohsuggest, sizeof(l->mohsuggest));
index 88f37fb..7318e73 100644 (file)
@@ -404,6 +404,8 @@ struct unistim_line {
        int amaflags;
        /*! Codec supported */
        int capability;
+       /*! Parkinglot */
+       char parkinglot[AST_MAX_CONTEXT];
        struct unistim_line *next;
        struct unistim_device *parent;
 };
@@ -5111,6 +5113,8 @@ static struct unistim_device *build_device(const char *cat, const struct ast_var
                        l->pickupgroup = ast_get_group(v->value);
                else if (!strcasecmp(v->name, "mailbox"))
                        ast_copy_string(l->mailbox, v->value, sizeof(l->mailbox));
+               else if (!strcasecmp(v->name, "parkinglot"))
+                       ast_copy_string(l->parkinglot, v->value, sizeof(l->parkinglot));
                else if (!strcasecmp(v->name, "linelabel"))
                        unquote(linelabel, v->value, sizeof(linelabel) - 1);
                else if (!strcasecmp(v->name, "extension")) {
index def1015..927275d 100644 (file)
@@ -237,6 +237,8 @@ static const char config[] = "zapata.conf";
 static char defaultcic[64] = "";
 static char defaultozz[64] = "";
 
+static char parkinglot[AST_MAX_EXTENSION] = "";                /*!< Default parking lot for this channel */
+
 /*! Run this script when the MWI state changes on an FXO line, if mwimonitor is enabled */
 static char mwimonitornotify[PATH_MAX] = "";
 
@@ -595,6 +597,7 @@ static struct zt_pvt {
        char language[MAX_LANGUAGE];
        char mohinterpret[MAX_MUSICCLASS];
        char mohsuggest[MAX_MUSICCLASS];
+       char parkinglot[AST_MAX_EXTENSION]; /*!< Parking lot for this channel */
 #if defined(PRI_ANI) || defined(HAVE_SS7)
        char cid_ani[AST_MAX_EXTENSION];
 #endif
@@ -777,6 +780,7 @@ static struct zt_chan_conf zt_chan_conf_default(void) {
                        .cid_name = "",
                        .mohinterpret = "default",
                        .mohsuggest = "",
+                       .parkinglot = "",
                        .transfertobusy = 1,
 
                        .cid_signalling = CID_SIG_BELL,
@@ -5963,6 +5967,8 @@ static struct ast_channel *zt_new(struct zt_pvt *i, int state, int startpbx, int
                tmp->callgroup = i->callgroup;
                tmp->pickupgroup = i->pickupgroup;
        }
+       if (!ast_strlen_zero(i->parkinglot))
+               ast_string_field_set(tmp, parkinglot, i->parkinglot);
        if (!ast_strlen_zero(i->language))
                ast_string_field_set(tmp, language, i->language);
        if (!i->owner)
@@ -8077,6 +8083,7 @@ static struct zt_pvt *mkintf(int channel, struct zt_chan_conf conf, struct zt_pr
        struct zt_bufferinfo bi;
 #endif
        struct zt_spaninfo si;
+
        int res;
        int span=0;
        int here = 0;
@@ -8493,6 +8500,7 @@ static struct zt_pvt *mkintf(int channel, struct zt_chan_conf conf, struct zt_pr
                ast_copy_string(tmp->mohsuggest, conf.chan.mohsuggest, sizeof(tmp->mohsuggest));
                ast_copy_string(tmp->context, conf.chan.context, sizeof(tmp->context));
                ast_copy_string(tmp->cid_num, conf.chan.cid_num, sizeof(tmp->cid_num));
+               ast_copy_string(tmp->parkinglot, conf.chan.parkinglot, sizeof(tmp->parkinglot));
                tmp->cid_ton = 0;
                ast_copy_string(tmp->cid_name, conf.chan.cid_name, sizeof(tmp->cid_name));
                ast_copy_string(tmp->mailbox, conf.chan.mailbox, sizeof(tmp->mailbox));
@@ -13258,12 +13266,17 @@ static int process_zap(struct zt_chan_conf *confp, struct ast_variable *v, int r
        const char *ringc; /* temporary string for parsing the dring number. */
        int y;
        int found_pseudo = 0;
-        char zapchan[MAX_CHANLIST_LEN] = {};
+       char zapchan[MAX_CHANLIST_LEN] = {};
 
        for (; v; v = v->next) {
                if (!ast_jb_read_conf(&global_jbconf, v->name, v->value))
                        continue;
 
+               /* must have parkinglot in confp before build_channels is called */
+               if (!strcasecmp(v->name, "parkinglot")) {
+                       ast_copy_string(confp->chan.parkinglot, v->value, sizeof(confp->chan.parkinglot));
+               }
+
                /* Create the interface list */
                if (!strcasecmp(v->name, "channel")
 #ifdef HAVE_PRI
@@ -13424,6 +13437,8 @@ static int process_zap(struct zt_chan_conf *confp, struct ast_variable *v, int r
                        ast_copy_string(confp->chan.mohinterpret, v->value, sizeof(confp->chan.mohinterpret));
                } else if (!strcasecmp(v->name, "mohsuggest")) {
                        ast_copy_string(confp->chan.mohsuggest, v->value, sizeof(confp->chan.mohsuggest));
+               } else if (!strcasecmp(v->name, "parkinglot")) {
+                       ast_copy_string(parkinglot, v->value, sizeof(parkinglot));
                } else if (!strcasecmp(v->name, "stripmsd")) {
                        ast_log(LOG_NOTICE, "Configuration option \"%s\" has been deprecated. Please use dialplan instead\n", v->name);
                        confp->chan.stripmsd = atoi(v->value);
index 3666e80..6e83389 100644 (file)
@@ -3,10 +3,11 @@
 ;
 
 [general]
-parkext => 700                 ; What extension to dial to park
-parkpos => 701-720             ; What extensions to park calls on. These needs to be
-                               ; numeric, as Asterisk starts from the start position
+parkext => 700                 ; What extension to dial to park        (all parking lots)
+parkpos => 701-720             ; What extensions to park calls on. (defafult parking lot)
+                               ; These needs to be numeric, as Asterisk starts from the start position
                                ; and increments with one for the next parked call.
+context => parkedcalls         ; Which context parked calls are in (default parking lot)
 context => parkedcalls         ; Which context parked calls are in
 ;parkingtime => 45             ; Number of seconds a call can be parked for 
                                ; (default is 45 seconds)
@@ -46,6 +47,7 @@ context => parkedcalls                ; Which context parked calls are in
 ; They can not be used while the remote party is ringing or in progress. If you require this feature you can use
 ; chan_local in combination with Answer to accomplish it.
 
+
 [featuremap]
 ;blindxfer => #1               ; Blind transfer  (default is #)
 ;disconnect => *0              ; Disconnect  (default is *)
@@ -87,6 +89,7 @@ context => parkedcalls                ; Which context parked calls are in
 ;                   channel waits for the feature to complete. If left blank,
 ;                   no music will be played.
 ;
+
 ;
 ; IMPORTANT NOTE: The applicationmap is not intended to be used for all Asterisk
 ;   applications. When applications are used in extensions.conf, they are executed
@@ -108,20 +111,21 @@ context => parkedcalls            ; Which context parked calls are in
 ;unpauseMonitor => #3,self/callee,UnPauseMonitor   ;Allow the callee to unpause monitoring
 ;                                                  ;on their channel
 ;
+;*** Define another parking lot
+;
+; You can set parkinglot with the CHANNEL dialplan function
+; or configure it in the device configuration in the channel
+;
+;[parkinglot_edvina]
+;context => edvinapark
+;parkpos => 800-850
+;findslot => next
+
 ; GROUPS
 ;   Groups are groupings of features defined in [applicationmap]
 ;   that can have their own key mappings.
 ;
-;   Groups are defined as a configuration section,
-;   and can be set as part of DYNAMIC_FEATURES in
-;   the same way that a normal feature can... 
-;      etc:    
-;
-;        Set(DYNAMIC_FEATURES=myGroupName);
-;
 ; example:
 ; [myGroupName]        ; defines the group named myGroupName
 ; testfeature => #9    ; associates testfeature with the group and the keycode #9
 ; pauseMonitor         ; associates pauseMonitor with the group and the keycode
-;                      ; defined in [applicationmap]
-
index 2441f2c..1059400 100644 (file)
@@ -286,6 +286,10 @@ autokill=yes
                        ; has expired based on its registration interval, used the stored
                        ; address information regardless. (yes|no)
 
+;parkinglot=edvina             ; Default parkinglot for IAX peers and users
+                               ; This can also be configured per device
+                               ; Parkinglots are defined in features.conf
+
 ; Guest sections for unauthenticated connection attempts.  Just specify an
 ; empty secret, or provide no secret section.
 ;
index 10293a7..7a3d114 100644 (file)
@@ -195,6 +195,9 @@ srvlookup=yes                       ; Enable DNS SRV lookups on outbound calls
 ;
 ;mohsuggest=default
 ;
+;parkinglot=plaza              ; Sets the default parking lot for call parking
+                               ; This may also be set for individual users/peers
+                               ; Parkinglots are configured in features.conf
 ;language=en                   ; Default language setting for all users/peers
                                ; This may also be set for individual users/peers
 ;relaxdtmf=yes                 ; Relax dtmf handling
index 42b3c68..8388d7f 100644 (file)
@@ -84,6 +84,8 @@ static int func_channel_read(struct ast_channel *chan, const char *function,
                locked_copy_string(chan, buf, chan->language, len);
        else if (!strcasecmp(data, "musicclass"))
                locked_copy_string(chan, buf, chan->musicclass, len);
+       else if (!strcasecmp(data, "parkinglot"))
+               locked_copy_string(chan, buf, chan->parkinglot, len);
        else if (!strcasecmp(data, "state"))
                locked_copy_string(chan, buf, ast_state2str(chan->_state), len);
        else if (!strcasecmp(data, "channeltype"))
@@ -110,6 +112,8 @@ static int func_channel_write(struct ast_channel *chan, const char *function,
 
        if (!strcasecmp(data, "language"))
                locked_string_field_set(chan, language, value);
+       else if (!strcasecmp(data, "parkinglot"))
+               locked_string_field_set(chan, parkinglot, value);
        else if (!strcasecmp(data, "musicclass"))
                locked_string_field_set(chan, musicclass, value);
 #ifdef CHANNEL_TRACE
@@ -172,6 +176,7 @@ static struct ast_custom_function channel_function = {
                "R/O    channeltype        technology used for channel\n"
                "R/W    language           language for sounds played\n"
                "R/W    musicclass         class (from musiconhold.conf) for hold music\n"
+               "R/W    parkinglot         parkinglot for parking\n"
                "R/W    rxgain             set rxgain level on channel drivers that support it\n"
                "R/O    state              state for channel\n"
                "R/W    tonezone           zone for indications played\n"
index ddba96f..e0b1f79 100644 (file)
@@ -434,6 +434,7 @@ struct ast_channel {
                AST_STRING_FIELD(accountcode);          /*!< Account code for billing */
                AST_STRING_FIELD(call_forward);         /*!< Where to forward to if asked to dial on this interface */
                AST_STRING_FIELD(uniqueid);             /*!< Unique Channel Identifier */
+               AST_STRING_FIELD(parkinglot);           /*! Default parking lot, if empty, default parking lot  */
        );
        
        int fds[AST_MAX_FDS];                           /*!< File descriptors for channel -- Drivers will poll on
index 089f27a..59e2d9a 100644 (file)
@@ -54,11 +54,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/monitor.h"
 #include "asterisk/audiohook.h"
 #include "asterisk/global_datastores.h"
+#include "asterisk/astobj2.h"
 
 #define DEFAULT_PARK_TIME 45000
 #define DEFAULT_TRANSFER_DIGIT_TIMEOUT 3000
 #define DEFAULT_FEATURE_DIGIT_TIMEOUT 500
 #define DEFAULT_NOANSWER_TIMEOUT_ATTENDED_TRANSFER 15000
+#define DEFAULT_PARKINGLOT "default"                   /*!< Default parking lot */
 #define DEFAULT_ATXFER_DROP_CALL 0
 #define DEFAULT_ATXFER_LOOP_DELAY 10000
 #define DEFAULT_ATXFER_CALLBACK_RETRIES 2
@@ -85,26 +87,55 @@ static AST_RWLIST_HEAD_STATIC(feature_groups, feature_group);
 
 static char *parkedcall = "ParkedCall";
 
-static int parkaddhints = 0;                               /*!< Add parking hints automatically */
-static int parkedcalltransfers = 0;                        /*!< Enable DTMF based transfers on bridge when picking up parked calls */
-static int parkedcallreparking = 0;                        /*!< Enable DTMF based parking on bridge when picking up parked calls */
-static int parkingtime = DEFAULT_PARK_TIME;                /*!< No more than 45 seconds parked before you do something with them */
-static char parking_con[AST_MAX_EXTENSION];                /*!< Context for which parking is made accessible */
-static char parking_con_dial[AST_MAX_EXTENSION];           /*!< Context for dialback for parking (KLUDGE) */
-static char parking_ext[AST_MAX_EXTENSION];                /*!< Extension you type to park the call */
 static char pickup_ext[AST_MAX_EXTENSION];                 /*!< Call pickup extension */
-static char parkmohclass[MAX_MUSICCLASS];                  /*!< Music class used for parking */
-static int parking_start;                                  /*!< First available extension for parking */
-static int parking_stop;                                   /*!< Last available extension for parking */
+
+/*! \brief Description of one parked call, added to a list while active, then removed.
+       The list belongs to a parkinglot 
+*/
+struct parkeduser {
+       struct ast_channel *chan;                   /*!< Parking channel */
+       struct timeval start;                       /*!< Time the parking started */
+       int parkingnum;                             /*!< Parking lot */
+       char parkingexten[AST_MAX_EXTENSION];       /*!< If set beforehand, parking extension used for this call */
+       char context[AST_MAX_CONTEXT];              /*!< Where to go if our parking time expires */
+       char exten[AST_MAX_EXTENSION];
+       int priority;
+       int parkingtime;                            /*!< Maximum length in parking lot before return */
+       int notquiteyet;
+       char peername[1024];
+       unsigned char moh_trys;
+       struct ast_parkinglot *parkinglot;
+       AST_LIST_ENTRY(parkeduser) list;
+};
+
+/*! \brief Structure for parking lots which are put in a container. */
+struct ast_parkinglot {
+       char name[AST_MAX_CONTEXT];
+       char parking_con[AST_MAX_EXTENSION];            /*!< Context for which parking is made accessible */
+       char parking_con_dial[AST_MAX_EXTENSION];       /*!< Context for dialback for parking (KLUDGE) */
+       int parking_start;                              /*!< First available extension for parking */
+       int parking_stop;                               /*!< Last available extension for parking */
+       int parking_offset;
+       int parkfindnext;
+       int parkingtime;                                /*!< Default parking time */
+       char mohclass[MAX_MUSICCLASS];                  /*!< Music class used for parking */
+       int parkaddhints;                               /*!< Add parking hints automatically */
+       int parkedcalltransfers;                        /*!< Enable DTMF based transfers on bridge when picking up parked calls */
+       int parkedcallreparking;                        /*!< Enable DTMF based parking on bridge when picking up parked calls */
+       AST_LIST_HEAD(parkinglot_parklist, parkeduser) parkings; /*!< List of active parkings in this parkinglot */
+};
+
+/*! \brief The list of parking lots configured. Always at least one  - the default parking lot */
+static struct ao2_container *parkinglots;
+struct ast_parkinglot *default_parkinglot;
+char parking_ext[AST_MAX_EXTENSION];            /*!< Extension you type to park the call */
 
 static char courtesytone[256];                             /*!< Courtesy tone */
 static int parkedplay = 0;                                 /*!< Who to play the courtesy tone to */
 static char xfersound[256];                                /*!< Call transfer sound */
 static char xferfailsound[256];                            /*!< Call transfer failure sound */
 
-static int parking_offset;
-static int parkfindnext;
-
 static int adsipark;
 
 static int transferdigittimeout;
@@ -152,24 +183,15 @@ static int mixmonitor_ok = 1;
 static struct ast_app *stopmixmonitor_app = NULL;
 static int stopmixmonitor_ok = 1;
 
-struct parkeduser {
-       struct ast_channel *chan;                   /*!< Parking channel */
-       struct timeval start;                       /*!< Time the parking started */
-       int parkingnum;                             /*!< Parking lot */
-       char parkingexten[AST_MAX_EXTENSION];       /*!< If set beforehand, parking extension used for this call */
-       char context[AST_MAX_CONTEXT];              /*!< Where to go if our parking time expires */
-       char exten[AST_MAX_EXTENSION];
-       int priority;
-       int parkingtime;                            /*!< Maximum length in parking lot before return */
-       int notquiteyet;
-       char peername[1024];
-       unsigned char moh_trys;
-       AST_LIST_ENTRY(parkeduser) list;
-};
+static pthread_t parking_thread;
 
-static AST_LIST_HEAD_STATIC(parkinglot, parkeduser);
+/* Forward declarations */
+static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot);
+static void parkinglot_unref(struct ast_parkinglot *parkinglot);
+static void parkinglot_destroy(void *obj);
+int manage_parkinglot(struct ast_parkinglot *curlot, fd_set *rfds, fd_set *efds, fd_set *nrfds, fd_set *nefds, int *fs, int *max);
+struct ast_parkinglot *find_parkinglot(const char *name);
 
-static pthread_t parking_thread;
 
 const char *ast_parking_ext(void)
 {
@@ -189,7 +211,17 @@ struct ast_bridge_thread_obj
        unsigned int return_to_pbx:1;
 };
 
+static int parkinglot_hash_cb(const void *obj, const int flags)
+{
+       const struct ast_parkinglot *parkinglot = obj;
+       return ast_str_hash(parkinglot->name);
+}
 
+static int parkinglot_cmp_cb(void *obj, void *arg, int flags)
+{
+       struct ast_parkinglot *parkinglot = obj, *parkinglot2 = arg;
+       return !strcasecmp(parkinglot->name, parkinglot2->name) ? CMP_MATCH : 0;
+}
 
 /*!
  * \brief store context, extension and priority 
@@ -345,6 +377,23 @@ static int adsi_announce_park(struct ast_channel *chan, char *parkingexten)
        return ast_adsi_print(chan, message, justify, 1);
 }
 
+/*! \brief Find parking lot name from channel */
+static const char *findparkinglotname(struct ast_channel *chan)
+{
+       const char *temp, *parkinglot = NULL;
+
+       /* Check if the channel has a parking lot */
+       if (!ast_strlen_zero(chan->parkinglot))
+               parkinglot = chan->parkinglot;
+
+       /* Channel variables override everything */
+
+       if ((temp  = pbx_builtin_getvar_helper(chan, "PARKINGLOT")))
+               return temp;
+
+       return parkinglot;
+}
+
 /*! \brief Notify metermaids that we've changed an extension */
 static void notify_metermaids(const char *exten, char *context, enum ast_device_state state)
 {
@@ -375,36 +424,54 @@ static enum ast_device_state metermaidstate(const char *data)
 }
 
 /* Park a call */
-static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout, char *orig_chan_name)
+static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout, char *orig_chan_name, struct ast_parkinglot *parkinglot)
 {
        struct parkeduser *pu, *cur;
        int i, x = -1, parking_range;
        struct ast_context *con;
+       const char *parkinglotname;
        const char *parkingexten;
        
+       parkinglotname = findparkinglotname(peer);
+
+       if (parkinglotname) {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Found chanvar Parkinglot: %s\n", parkinglotname);
+               parkinglot = find_parkinglot(parkinglotname);   
+       }
+       if (!parkinglot)
+               parkinglot = default_parkinglot;
+
+       parkinglot_addref(parkinglot);
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Parkinglot: %s\n", parkinglot->name);
+
        /* Allocate memory for parking data */
-       if (!(pu = ast_calloc(1, sizeof(*pu)))) 
+       if (!(pu = ast_calloc(1, sizeof(*pu)))) {
+               parkinglot_unref(parkinglot);
                return -1;
+       }
 
-       /* Lock parking lot */
-       AST_LIST_LOCK(&parkinglot);
+       /* Lock parking list */
+       AST_LIST_LOCK(&parkinglot->parkings);
        /* Check for channel variable PARKINGEXTEN */
        parkingexten = pbx_builtin_getvar_helper(chan, "PARKINGEXTEN");
        if (!ast_strlen_zero(parkingexten)) {
-               if (ast_exists_extension(NULL, parking_con, parkingexten, 1, NULL)) {
-                       AST_LIST_UNLOCK(&parkinglot);
+               if (ast_exists_extension(NULL, parkinglot->parking_con, parkingexten, 1, NULL)) {
+                       AST_LIST_UNLOCK(&parkinglot->parkings);
+                       parkinglot_unref(parkinglot);
                        ast_free(pu);
-                       ast_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parking_con);
+                       ast_log(LOG_WARNING, "Requested parking extension already exists: %s@%s\n", parkingexten, parkinglot->parking_con);
                        return 1;       /* Continue execution if possible */
                }
                ast_copy_string(pu->parkingexten, parkingexten, sizeof(pu->parkingexten));
                x = atoi(parkingexten);
        } else {
                /* Select parking space within range */
-               parking_range = parking_stop - parking_start+1;
+               parking_range = parkinglot->parking_stop - parkinglot->parking_start+1;
                for (i = 0; i < parking_range; i++) {
-                       x = (i + parking_offset) % parking_range + parking_start;
-                       AST_LIST_TRAVERSE(&parkinglot, cur, list) {
+                       x = (i + parkinglot->parking_offset) % parking_range + parkinglot->parking_start;
+                       AST_LIST_TRAVERSE(&parkinglot->parkings, cur, list) {
                                if (cur->parkingnum == x)
                                        break;
                        }
@@ -415,12 +482,13 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, in
                if (!(i < parking_range)) {
                        ast_log(LOG_WARNING, "No more parking spaces\n");
                        ast_free(pu);
-                       AST_LIST_UNLOCK(&parkinglot);
+                       AST_LIST_UNLOCK(&parkinglot->parkings);
+                       parkinglot_unref(parkinglot);
                        return -1;
                }
                /* Set pointer for next parking */
-               if (parkfindnext) 
-                       parking_offset = x - parking_start + 1;
+               if (parkinglot->parkfindnext) 
+                       parkinglot->parking_offset = x - parkinglot->parking_start + 1;
        }
        
        chan->appl = "Parked Call";
@@ -431,13 +499,14 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, in
        /* Put the parked channel on hold if we have two different channels */
        if (chan != peer) {
                ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
-                       S_OR(parkmohclass, NULL),
-                       !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
+                       S_OR(parkinglot->mohclass, NULL),
+                       !ast_strlen_zero(parkinglot->mohclass) ? strlen(parkinglot->mohclass) + 1 : 0);
        }
        
        pu->start = ast_tvnow();
        pu->parkingnum = x;
-       pu->parkingtime = (timeout > 0) ? timeout : parkingtime;
+       pu->parkinglot = parkinglot;
+       pu->parkingtime = (timeout > 0) ? timeout : parkinglot->parkingtime;
        if (extout)
                *extout = x;
 
@@ -449,26 +518,28 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, in
        ast_copy_string(pu->context, S_OR(chan->macrocontext, chan->context), sizeof(pu->context));
        ast_copy_string(pu->exten, S_OR(chan->macroexten, chan->exten), sizeof(pu->exten));
        pu->priority = chan->macropriority ? chan->macropriority : chan->priority;
-       AST_LIST_INSERT_TAIL(&parkinglot, pu, list);
+       AST_LIST_INSERT_TAIL(&parkinglot->parkings, pu, list);
 
        /* If parking a channel directly, don't quiet yet get parking running on it */
        if (peer == chan) 
                pu->notquiteyet = 1;
-       AST_LIST_UNLOCK(&parkinglot);
+       AST_LIST_UNLOCK(&parkinglot->parkings);
+
        /* Wake up the (presumably select()ing) thread */
        pthread_kill(parking_thread, SIGURG);
-       ast_verb(2, "Parked %s on %d@%s. Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, parking_con, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000));
+       ast_verb(2, "Parked %s on %d (lot %s). Will timeout back to extension [%s] %s, %d in %d seconds\n", pu->chan->name, pu->parkingnum, parkinglot->name, pu->context, pu->exten, pu->priority, (pu->parkingtime/1000));
 
        if (pu->parkingnum != -1)
                snprintf(pu->parkingexten, sizeof(pu->parkingexten), "%d", x);
        manager_event(EVENT_FLAG_CALL, "ParkedCall",
                "Exten: %s\r\n"
                "Channel: %s\r\n"
+               "Parkinglot: %s\r\n"
                "From: %s\r\n"
                "Timeout: %ld\r\n"
                "CallerIDNum: %s\r\n"
                "CallerIDName: %s\r\n",
-               pu->parkingexten, pu->chan->name, peer ? peer->name : "",
+               pu->parkingexten, pu->chan->name, pu->parkinglot->name, peer ? peer->name : "",
                (long)pu->start.tv_sec + (long)(pu->parkingtime/1000) - (long)time(NULL),
                S_OR(pu->chan->cid.cid_num, "<unknown>"),
                S_OR(pu->chan->cid.cid_name, "<unknown>")
@@ -479,9 +550,9 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, in
                ast_adsi_unload_session(peer);
        }
 
-       con = ast_context_find_or_create(NULL, NULL, parking_con, registrar);
+       con = ast_context_find_or_create(NULL, NULL, parkinglot->parking_con, registrar);
        if (!con)       /* Still no context? Bad */
-               ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
+               ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parkinglot->parking_con);
        /* Tell the peer channel the number of the parking space */
        if (peer && ((pu->parkingnum != -1 && ast_strlen_zero(orig_chan_name)) || !strcasecmp(peer->name, orig_chan_name))) { /* Only say number if it's a number and the channel hasn't been masqueraded away */
                /* If a channel is masqueraded into peer while playing back the parking slot number do not continue playing it back. This is the case if an attended transfer occurs. */
@@ -491,13 +562,13 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, in
        }
        if (con) {
                if (!ast_add_extension2(con, 1, pu->parkingexten, 1, NULL, NULL, parkedcall, ast_strdup(pu->parkingexten), ast_free_ptr, registrar))
-                       notify_metermaids(pu->parkingexten, parking_con, AST_DEVICE_INUSE);
+                       notify_metermaids(pu->parkingexten, parkinglot->parking_con, AST_DEVICE_INUSE);
        }
        if (pu->notquiteyet) {
                /* Wake up parking thread if we're really done */
                ast_indicate_data(pu->chan, AST_CONTROL_HOLD, 
-                       S_OR(parkmohclass, NULL),
-                       !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
+                       S_OR(parkinglot->mohclass, NULL),
+                       !ast_strlen_zero(parkinglot->mohclass) ? strlen(parkinglot->mohclass) + 1 : 0);
                pu->notquiteyet = 0;
                pthread_kill(parking_thread, SIGURG);
        }
@@ -507,7 +578,7 @@ static int park_call_full(struct ast_channel *chan, struct ast_channel *peer, in
 /*! \brief Park a call */
 int ast_park_call(struct ast_channel *chan, struct ast_channel *peer, int timeout, int *extout)
 {
-       return park_call_full(chan, peer, timeout, extout, NULL);
+       return park_call_full(chan, peer, timeout, extout, NULL, NULL);
 }
 
 /* Park call via masquraded channel */
@@ -537,7 +608,7 @@ int ast_masq_park_call(struct ast_channel *rchan, struct ast_channel *peer, int
 
        orig_chan_name = ast_strdupa(chan->name);
 
-       park_call_full(chan, peer, timeout, extout, orig_chan_name);
+       park_call_full(chan, peer, timeout, extout, orig_chan_name, NULL);
 
        return 0;
 }
@@ -742,12 +813,12 @@ static int builtin_automixmonitor(struct ast_channel *chan, struct ast_channel *
        count = ast_channel_audiohook_count_by_source(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
        ast_channel_unlock(callee_chan);
 
-       // This means a mixmonitor is attached to the channel, running or not is unknown.
+       /* This means a mixmonitor is attached to the channel, running or not is unknown. */
        if (count > 0) {
                
                ast_verb(3, "User hit '%s' to stop recording call.\n", code);
 
-               //Make sure they are running
+               /* Make sure they are running */
                ast_channel_lock(callee_chan);
                count = ast_channel_audiohook_count_by_source_running(callee_chan, mixmonitor_spy_type, AST_AUDIOHOOK_TYPE_SPY);
                ast_channel_unlock(callee_chan);
@@ -2165,186 +2236,208 @@ static void post_manager_event(const char *s, struct parkeduser *pu)
        manager_event(EVENT_FLAG_CALL, s,
                "Exten: %s\r\n"
                "Channel: %s\r\n"
+               "Parkinglot: %s\r\n"
                "CallerIDNum: %s\r\n"
                "CallerIDName: %s\r\n\r\n",
                pu->parkingexten, 
                pu->chan->name,
+               pu->parkinglot->name,
                S_OR(pu->chan->cid.cid_num, "<unknown>"),
                S_OR(pu->chan->cid.cid_name, "<unknown>")
                );
 }
 
-/*! 
- * \brief Take care of parked calls and unpark them if needed 
- * \param ignore unused var.
- * 
- * Start inf loop, lock parking lot, check if any parked channels have gone above timeout
- * if so, remove channel from parking lot and return it to the extension that parked it.
- * Check if parked channel decided to hangup, wait until next FD via select().
-*/
-static void *do_parking_thread(void *ignore)
+/*! \brief Run management on parkinglots, collad once per parkinglot */
+int manage_parkinglot(struct ast_parkinglot *curlot, fd_set *rfds, fd_set *efds, fd_set *nrfds, fd_set *nefds, int *ms, int *max)
 {
-       char parkingslot[AST_MAX_EXTENSION];
-       fd_set rfds, efds;      /* results from previous select, to be preserved across loops. */
 
-       FD_ZERO(&rfds);
-       FD_ZERO(&efds);
+       struct parkeduser *pu;
+       int res = 0;
+       char parkingslot[AST_MAX_EXTENSION];
 
-       for (;;) {
-               struct parkeduser *pu;
-               int ms = -1;    /* select timeout, uninitialized */
-               int max = -1;   /* max fd, none there yet */
-               fd_set nrfds, nefds;    /* args for the next select */
-               FD_ZERO(&nrfds);
-               FD_ZERO(&nefds);
-
-               AST_LIST_LOCK(&parkinglot);
-               AST_LIST_TRAVERSE_SAFE_BEGIN(&parkinglot, pu, list) {
-                       struct ast_channel *chan = pu->chan;    /* shorthand */
-                       int tms;        /* timeout for this item */
-                       int x;          /* fd index in channel */
-                       struct ast_context *con;
-
-                       if (pu->notquiteyet) /* Pretend this one isn't here yet */
-                               continue;
-                       tms = ast_tvdiff_ms(ast_tvnow(), pu->start);
-                       if (tms > pu->parkingtime) {
-                               ast_indicate(chan, AST_CONTROL_UNHOLD);
-                               /* Get chan, exten from derived kludge */
-                               if (pu->peername[0]) {
-                                       char *peername = ast_strdupa(pu->peername);
-                                       char *cp = strrchr(peername, '-');
-                                       char peername_flat[AST_MAX_EXTENSION]; /* using something like Zap/52 for an extension name is NOT a good idea */
-                                       int i;
-
-                                       if (cp) 
-                                               *cp = 0;
-                                       ast_copy_string(peername_flat,peername,sizeof(peername_flat));
-                                       for(i=0; peername_flat[i] && i < AST_MAX_EXTENSION; i++) {
-                                               if (peername_flat[i] == '/') 
-                                                       peername_flat[i]= '0';
-                                       }
-                                       con = ast_context_find_or_create(NULL, NULL, parking_con_dial, registrar);
-                                       if (!con)
-                                               ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", parking_con_dial);
-                                       if (con) {
-                                               char returnexten[AST_MAX_EXTENSION];
-                                               struct ast_datastore *features_datastore;
-                                               struct ast_dial_features *dialfeatures = NULL;
+       /* TODO: I believe this reference increase is not necessary since the iterator in the calling function already did so */
+       //parkinglot_addref(curlot);
+       /* Lock parking list */
+       AST_LIST_LOCK(&curlot->parkings);
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&curlot->parkings, pu, list) {
+               struct ast_channel *chan = pu->chan;    /* shorthand */
+               int tms;        /* timeout for this item */
+               int x;          /* fd index in channel */
+               struct ast_context *con;
+
+               if (pu->notquiteyet) { /* Pretend this one isn't here yet */
+                       continue;
+               }
+               tms = ast_tvdiff_ms(ast_tvnow(), pu->start);
+               if (tms > pu->parkingtime) {
+                       /* Stop music on hold */
+                       ast_indicate(pu->chan, AST_CONTROL_UNHOLD);
+                       /* Get chan, exten from derived kludge */
+                       if (pu->peername[0]) {
+                               char *peername = ast_strdupa(pu->peername);
+                               char *cp = strrchr(peername, '-');
+                               char peername_flat[AST_MAX_EXTENSION]; /* using something like Zap/52 for an extension name is NOT a good idea */
+                               int i;
+
+                               if (cp) 
+                                       *cp = 0;
+                               ast_copy_string(peername_flat,peername,sizeof(peername_flat));
+                               for(i=0; peername_flat[i] && i < AST_MAX_EXTENSION; i++) {
+                                       if (peername_flat[i] == '/') 
+                                               peername_flat[i]= '0';
+                               }
+                               con = ast_context_find_or_create(NULL, NULL, pu->parkinglot->parking_con_dial, registrar);
+                               if (!con) {
+                                       ast_log(LOG_ERROR, "Parking dial context '%s' does not exist and unable to create\n", pu->parkinglot->parking_con_dial);
+                               }
+                               if (con) {
+                                       char returnexten[AST_MAX_EXTENSION];
+                                       struct ast_datastore *features_datastore;
+                                       struct ast_dial_features *dialfeatures = NULL;
 
-                                               ast_channel_lock(chan);
+                                       ast_channel_lock(chan);
 
-                                               if ((features_datastore = ast_channel_datastore_find(chan, &dial_features_info, NULL)))
-                                                       dialfeatures = features_datastore->data;
+                                       if ((features_datastore = ast_channel_datastore_find(chan, &dial_features_info, NULL)))
+                                               dialfeatures = features_datastore->data;
 
-                                               ast_channel_unlock(chan);
+                                       ast_channel_unlock(chan);
 
-                                               if (dialfeatures)
-                                                       snprintf(returnexten, sizeof(returnexten), "%s,,%s", peername, dialfeatures->options);
-                                               else /* Existing default */
-                                                       snprintf(returnexten, sizeof(returnexten), "%s,,t", peername);
+                                       if (dialfeatures)
+                                               snprintf(returnexten, sizeof(returnexten), "%s,,%s", peername, dialfeatures->options);
+                                       else /* Existing default */
+                                               snprintf(returnexten, sizeof(returnexten), "%s,,t", peername);
 
-                                               ast_add_extension2(con, 1, peername_flat, 1, NULL, NULL, "Dial", ast_strdup(returnexten), ast_free_ptr, registrar);
-                                       }
-                                       if (comebacktoorigin) {
-                                               set_c_e_p(chan, parking_con_dial, peername_flat, 1);
-                                       } else {
-                                               ast_log(LOG_WARNING, "now going to parkedcallstimeout,s,1 | ps is %d\n",pu->parkingnum);
-                                               snprintf(parkingslot, sizeof(parkingslot), "%d", pu->parkingnum);
-                                               pbx_builtin_setvar_helper(pu->chan, "PARKINGSLOT", parkingslot);
-                                               set_c_e_p(chan, "parkedcallstimeout", peername_flat, 1);
-                                       }
+                                       ast_add_extension2(con, 1, peername_flat, 1, NULL, NULL, "Dial", ast_strdup(returnexten), ast_free_ptr, registrar);
+                               }
+                               if (comebacktoorigin) {
+                                       set_c_e_p(chan, pu->parkinglot->parking_con_dial, peername_flat, 1);
                                } else {
-                                       /* They've been waiting too long, send them back to where they came.  Theoretically they
-                                          should have their original extensions and such, but we copy to be on the safe side */
-                                       set_c_e_p(chan, pu->context, pu->exten, pu->priority);
+                                       ast_log(LOG_WARNING, "now going to parkedcallstimeout,s,1 | ps is %d\n",pu->parkingnum);
+                                       snprintf(parkingslot, sizeof(parkingslot), "%d", pu->parkingnum);
+                                       pbx_builtin_setvar_helper(chan, "PARKINGSLOT", parkingslot);
+                                       set_c_e_p(chan, "parkedcallstimeout", peername_flat, 1);
                                }
+                       } else {
+                               /* They've been waiting too long, send them back to where they came.  Theoretically they
+                                  should have their original extensions and such, but we copy to be on the safe side */
+                               set_c_e_p(chan, pu->context, pu->exten, pu->priority);
+                       }
+                       post_manager_event("ParkedCallTimeOut", pu);
+
+                       ast_verb(2, "Timeout for %s parked on %d (%s). Returning to %s,%s,%d\n", pu->chan->name, pu->parkingnum, pu->parkinglot->name, pu->chan->context, pu->chan->exten, pu->chan->priority);
+                       /* Start up the PBX, or hang them up */
+                       if (ast_pbx_start(chan))  {
+                               ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", pu->chan->name);
+                               ast_hangup(chan);
+                       }
+                       /* And take them out of the parking lot */
+                       con = ast_context_find(pu->parkinglot->parking_con);
+                       if (con) {
+                               if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL))
+                                       ast_log(LOG_WARNING, "Whoa, failed to remove the parking extension!\n");
+                               else
+                                       notify_metermaids(pu->parkingexten, curlot->parking_con, AST_DEVICE_NOT_INUSE);
+                       } else
+                               ast_log(LOG_WARNING, "Whoa, no parking context?\n");
+                       AST_LIST_REMOVE_CURRENT(list);
+                       parkinglot_unref(curlot);
+               } else {        /* still within parking time, process descriptors */
+                       for (x = 0; x < AST_MAX_FDS; x++) {
+                               struct ast_frame *f;
 
-                               post_manager_event("ParkedCallTimeOut", pu);
+                               if ((chan->fds[x] == -1) && (!FD_ISSET(chan->fds[x], rfds) && !FD_ISSET(pu->chan->fds[x], efds))) 
+                                       continue;
+                               
+                               if (FD_ISSET(chan->fds[x], efds))
+                                       ast_set_flag(chan, AST_FLAG_EXCEPTION);
+                               else
+                                       ast_clear_flag(chan, AST_FLAG_EXCEPTION);
+                               chan->fdno = x;
+
+                               /* See if they need servicing */
+                               f = ast_read(pu->chan);
+                               /* Hangup? */
+                               if (!f || ((f->frametype == AST_FRAME_CONTROL) && (f->subclass ==  AST_CONTROL_HANGUP))) {
+                                       if (f)
+                                               ast_frfree(f);
+                                       post_manager_event("ParkedCallGiveUp", pu);
 
-                               ast_verb(2, "Timeout for %s parked on %d. Returning to %s,%s,%d\n", chan->name, pu->parkingnum, chan->context, chan->exten, chan->priority);
-                               /* Start up the PBX, or hang them up */
-                               if (ast_pbx_start(chan))  {
-                                       ast_log(LOG_WARNING, "Unable to restart the PBX for user on '%s', hanging them up...\n", chan->name);
+                                       /* There's a problem, hang them up*/
+                                       ast_verb(2, "%s got tired of being parked\n", chan->name);
                                        ast_hangup(chan);
-                               }
-                               /* And take them out of the parking lot */
-                               AST_LIST_REMOVE_CURRENT(list);
-                               con = ast_context_find(parking_con);
-                               if (con) {
-                                       if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL))
-                                               ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
-                                       else
-                                               notify_metermaids(pu->parkingexten, parking_con, AST_DEVICE_NOT_INUSE);
-                               } else
-                                       ast_log(LOG_WARNING, "Whoa, no parking context?\n");
-                               ast_free(pu);
-                       } else {        /* still within parking time, process descriptors */
-                               for (x = 0; x < AST_MAX_FDS; x++) {
-                                       struct ast_frame *f;
-
-                                       if (chan->fds[x] == -1 || (!FD_ISSET(chan->fds[x], &rfds) && !FD_ISSET(chan->fds[x], &efds)))
-                                               continue;       /* nothing on this descriptor */
-
-                                       if (FD_ISSET(chan->fds[x], &efds))
-                                               ast_set_flag(chan, AST_FLAG_EXCEPTION);
-                                       else
-                                               ast_clear_flag(chan, AST_FLAG_EXCEPTION);
-                                       chan->fdno = x;
-
-                                       /* See if they need servicing */
-                                       f = ast_read(chan);
-                                       if (!f || (f->frametype == AST_FRAME_CONTROL && f->subclass ==  AST_CONTROL_HANGUP)) {
-                                               if (f)
-                                                       ast_frfree(f);
-                                               post_manager_event("ParkedCallGiveUp", pu);
-
-                                               /* There's a problem, hang them up*/
-                                               ast_verb(2, "%s got tired of being parked\n", chan->name);
-                                               ast_hangup(chan);
-                                               /* And take them out of the parking lot */
-                                               AST_LIST_REMOVE_CURRENT(list);
-                                               con = ast_context_find(parking_con);
-                                               if (con) {
-                                                       if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL))
-                                                               ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
-                                                       else
-                                                               notify_metermaids(pu->parkingexten, parking_con, AST_DEVICE_NOT_INUSE);
-                                               } else
-                                                       ast_log(LOG_WARNING, "Whoa, no parking context?\n");
-                                               ast_free(pu);
-                                               break;
-                                       } else {
-                                               /*! \todo XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
-                                               ast_frfree(f);
-                                               if (pu->moh_trys < 3 && !chan->generatordata) {
-                                                       ast_debug(1, "MOH on parked call stopped by outside source.  Restarting.\n");
-                                                       ast_indicate_data(chan, AST_CONTROL_HOLD, 
-                                                               S_OR(parkmohclass, NULL),
-                                                               !ast_strlen_zero(parkmohclass) ? strlen(parkmohclass) + 1 : 0);
-                                                       pu->moh_trys++;
-                                               }
-                                               goto std;       /*! \todo XXX Ick: jumping into an else statement??? XXX */
+                                       /* And take them out of the parking lot */
+                                       con = ast_context_find(curlot->parking_con);
+                                       if (con) {
+                                               if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL))
+                                                       ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
+                                               else
+                                                       notify_metermaids(pu->parkingexten, curlot->parking_con, AST_DEVICE_NOT_INUSE);
+                                       } else
+                                               ast_log(LOG_WARNING, "Whoa, no parking context for parking lot %s?\n", curlot->name);
+                                       AST_LIST_REMOVE_CURRENT(list);
+                                       parkinglot_unref(curlot);
+                                       break;
+                               } else {
+                                       /* XXX Maybe we could do something with packets, like dial "0" for operator or something XXX */
+                                       ast_frfree(f);
+                                       if (pu->moh_trys < 3 && !chan->generatordata) {
+                                               ast_debug(1, "MOH on parked call stopped by outside source.  Restarting on channel %s.\n", chan->name);
+                                               ast_indicate_data(chan, AST_CONTROL_HOLD, 
+                                                       S_OR(curlot->mohclass, NULL),
+                                                       (!ast_strlen_zero(curlot->mohclass) ? strlen(curlot->mohclass) + 1 : 0));
+                                               pu->moh_trys++;
                                        }
-
-                               } /* end for */
-                               if (x >= AST_MAX_FDS) {
-std:                                   for (x=0; x<AST_MAX_FDS; x++) { /* mark fds for next round */
-                                               if (chan->fds[x] > -1) {
-                                                       FD_SET(chan->fds[x], &nrfds);
-                                                       FD_SET(chan->fds[x], &nefds);
-                                                       if (chan->fds[x] > max)
-                                                               max = chan->fds[x];
-                                               }
+                                       goto std;       /* XXX Ick: jumping into an else statement??? XXX */
+                               }
+                       } /* End for */
+                       if (x >= AST_MAX_FDS) {
+std:                           for (x=0; x<AST_MAX_FDS; x++) { /* mark fds for next round */
+                                       if (chan->fds[x] > -1) {
+                                               FD_SET(chan->fds[x], nrfds);
+                                               FD_SET(chan->fds[x], nefds);
+                                               if (chan->fds[x] > *max)
+                                                       *max = chan->fds[x];
                                        }
-                                       /* Keep track of our shortest wait */
-                                       if (tms < ms || ms < 0)
-                                               ms = tms;
                                }
+                               /* Keep track of our shortest wait */
+                               if (tms < *ms || *ms < 0)
+                                       *ms = tms;
                        }
-               } /* end while */
-               AST_LIST_TRAVERSE_SAFE_END
-               AST_LIST_UNLOCK(&parkinglot);
+               }
+       }
+       AST_LIST_TRAVERSE_SAFE_END;
+       AST_LIST_UNLOCK(&curlot->parkings);
+       return res;
+}
+
+/*! 
+ * \brief Take care of parked calls and unpark them if needed 
+ * \param ignore unused var.
+ * 
+ * Start inf loop, lock parking lot, check if any parked channels have gone above timeout
+ * if so, remove channel from parking lot and return it to the extension that parked it.
+ * Check if parked channel decided to hangup, wait until next FD via select().
+*/
+static void *do_parking_thread(void *ignore)
+{
+       fd_set rfds, efds;      /* results from previous select, to be preserved across loops. */
+       fd_set nrfds, nefds;    /* args for the next select */
+       FD_ZERO(&nrfds);
+       FD_ZERO(&nefds);
+
+       for (;;) {
+               int res = 0;
+               int ms = -1;    /* select timeout, uninitialized */
+               int max = -1;   /* max fd, none there yet */
+               struct ao2_iterator iter;
+               struct ast_parkinglot *curlot;
+               iter = ao2_iterator_init(parkinglots, 0);
+
+               while ((curlot = ao2_iterator_next(&iter))) {
+                       res = manage_parkinglot(curlot, &rfds, &efds, &nrfds, &nefds, &ms, &max);
+                       ao2_ref(curlot, -1);
+               }
+
                rfds = nrfds;
                efds = nefds;
                {
@@ -2357,6 +2450,25 @@ std:                                     for (x=0; x<AST_MAX_FDS; x++) { /* mark fds for next round */
        return NULL;    /* Never reached */
 }
 
+/*! \brief Find parkinglot by name */
+struct ast_parkinglot *find_parkinglot(const char *name)
+{
+       struct ast_parkinglot *parkinglot = NULL;
+       struct ast_parkinglot tmp_parkinglot;
+       
+       if (ast_strlen_zero(name))
+               return NULL;
+
+       ast_copy_string(tmp_parkinglot.name, name, sizeof(tmp_parkinglot.name));
+
+       parkinglot = ao2_find(parkinglots, &tmp_parkinglot, OBJ_POINTER);
+
+       if (parkinglot && option_debug)
+               ast_log(LOG_DEBUG, "Found Parkinglot: %s\n", parkinglot->name);
+
+       return parkinglot;
+}
+
 /*! \brief Park a call */
 static int park_call_exec(struct ast_channel *chan, void *data)
 {
@@ -2367,12 +2479,25 @@ static int park_call_exec(struct ast_channel *chan, void *data)
        char orig_exten[AST_MAX_EXTENSION];
        int orig_priority = chan->priority;
 
+       const char *parkinglotname = DEFAULT_PARKINGLOT;
+       struct ast_parkinglot *parkinglot = NULL;
        /* Data is unused at the moment but could contain a parking
           lot context eventually */
        int res = 0;
 
        ast_copy_string(orig_exten, chan->exten, sizeof(orig_exten));
 
+       /* Check if the channel has a parking lot */
+       parkinglotname = findparkinglotname(chan);
+
+       if (parkinglotname) {
+               if (option_debug > 2)
+                       ast_log(LOG_DEBUG, "Found chanvar Parkinglot: %s\n", parkinglotname);
+               parkinglot = find_parkinglot(parkinglotname);   
+       }
+
+
        /* Setup the exten/priority to be s/1 since we don't know
           where this call should return */
        strcpy(chan->exten, "s");
@@ -2385,7 +2510,7 @@ static int park_call_exec(struct ast_channel *chan, void *data)
                res = ast_safe_sleep(chan, 1000);
        /* Park the call */
        if (!res) {
-               res = park_call_full(chan, chan, 0, NULL, orig_chan_name);
+               res = park_call_full(chan, chan, 0, NULL, orig_chan_name, parkinglot);
                /* Continue on in the dialplan */
                if (res == 1) {
                        ast_copy_string(chan->exten, orig_exten, sizeof(chan->exten));
@@ -2399,7 +2524,7 @@ static int park_call_exec(struct ast_channel *chan, void *data)
 }
 
 /*! \brief Pickup parked call */
-static int park_exec(struct ast_channel *chan, void *data)
+static int park_exec_full(struct ast_channel *chan, void *data, struct ast_parkinglot *parkinglot)
 {
        int res = 0;
        struct ast_channel *peer=NULL;
@@ -2411,24 +2536,28 @@ static int park_exec(struct ast_channel *chan, void *data)
        if (data)
                park = atoi((char *)data);
 
-       AST_LIST_LOCK(&parkinglot);
-       AST_LIST_TRAVERSE_SAFE_BEGIN(&parkinglot, pu, list) {
+       parkinglot = find_parkinglot(findparkinglotname(chan));         
+       if (!parkinglot)
+               parkinglot = default_parkinglot;
+
+       AST_LIST_LOCK(&parkinglot->parkings);
+       AST_LIST_TRAVERSE_SAFE_BEGIN(&parkinglot->parkings, pu, list) {
                if (!data || pu->parkingnum == park) {
                        AST_LIST_REMOVE_CURRENT(list);
                        break;
                }
        }
        AST_LIST_TRAVERSE_SAFE_END
-       AST_LIST_UNLOCK(&parkinglot);
+       AST_LIST_UNLOCK(&parkinglot->parkings);
 
        if (pu) {
                peer = pu->chan;
-               con = ast_context_find(parking_con);
+               con = ast_context_find(parkinglot->parking_con);
                if (con) {
                        if (ast_context_remove_extension2(con, pu->parkingexten, 1, NULL))
                                ast_log(LOG_WARNING, "Whoa, failed to remove the extension!\n");
                        else
-                               notify_metermaids(pu->parkingexten, parking_con, AST_DEVICE_NOT_INUSE);
+                               notify_metermaids(pu->parkingexten, parkinglot->parking_con, AST_DEVICE_NOT_INUSE);
                } else
                        ast_log(LOG_WARNING, "Whoa, no parking context?\n");
 
@@ -2449,6 +2578,10 @@ static int park_exec(struct ast_channel *chan, void *data)
        if (chan->_state != AST_STATE_UP)
                ast_answer(chan);
 
+       //XXX Why do we unlock here ?
+       // uncomment it for now, till my setup with debug_threads and detect_deadlocks starts to complain
+       //ASTOBJ_UNLOCK(parkinglot);
+
        if (peer) {
                /* Play a courtesy to the source(s) configured to prefix the bridge connecting */
                
@@ -2491,13 +2624,13 @@ static int park_exec(struct ast_channel *chan, void *data)
                pbx_builtin_setvar_helper(chan, "PARKEDCHANNEL", peer->name);
                ast_cdr_setdestchan(chan->cdr, peer->name);
                memset(&config, 0, sizeof(struct ast_bridge_config));
-               if ((parkedcalltransfers == AST_FEATURE_FLAG_BYCALLEE) || (parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH))
+               if ((parkinglot->parkedcalltransfers == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH))
                        ast_set_flag(&(config.features_callee), AST_FEATURE_REDIRECT);
-               if ((parkedcalltransfers == AST_FEATURE_FLAG_BYCALLER) || (parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH))
+               if ((parkinglot->parkedcalltransfers == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->parkedcalltransfers == AST_FEATURE_FLAG_BYBOTH))
                        ast_set_flag(&(config.features_caller), AST_FEATURE_REDIRECT);
-               if ((parkedcallreparking == AST_FEATURE_FLAG_BYCALLEE) || (parkedcallreparking == AST_FEATURE_FLAG_BYBOTH))
+               if ((parkinglot->parkedcallreparking == AST_FEATURE_FLAG_BYCALLEE) || (parkinglot->parkedcallreparking == AST_FEATURE_FLAG_BYBOTH))
                        ast_set_flag(&(config.features_callee), AST_FEATURE_PARKCALL);
-               if ((parkedcallreparking == AST_FEATURE_FLAG_BYCALLER) || (parkedcallreparking == AST_FEATURE_FLAG_BYBOTH))
+               if ((parkinglot->parkedcallreparking == AST_FEATURE_FLAG_BYCALLER) || (parkinglot->parkedcallreparking == AST_FEATURE_FLAG_BYBOTH))
                        ast_set_flag(&(config.features_caller), AST_FEATURE_PARKCALL);
                res = ast_bridge_call(chan, peer, &config);
 
@@ -2519,6 +2652,154 @@ static int park_exec(struct ast_channel *chan, void *data)
        return res;
 }
 
+static int park_exec(struct ast_channel *chan, void *data) 
+{
+       return park_exec_full(chan, data, default_parkinglot);
+}
+
+/*! \brief Unreference parkinglot object. If no more references,
+       then go ahead and delete it */
+static void parkinglot_unref(struct ast_parkinglot *parkinglot) 
+{
+       int refcount = ao2_ref(parkinglot, -1);
+       if (option_debug > 2)
+               ast_log(LOG_DEBUG, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount - 1);
+}
+
+static struct ast_parkinglot *parkinglot_addref(struct ast_parkinglot *parkinglot)
+{
+       int refcount = ao2_ref(parkinglot, +1);
+       if (option_debug > 2)
+               ast_log(LOG_DEBUG, "Multiparking: %s refcount now %d\n", parkinglot->name, refcount + 1);
+       return parkinglot;
+}
+
+/*! \brief Allocate parking lot structure */
+static struct ast_parkinglot *create_parkinglot(char *name)
+{
+       struct ast_parkinglot *newlot = (struct ast_parkinglot *) NULL;
+
+       if (!name)
+               return NULL;
+
+       newlot = ao2_alloc(sizeof(*newlot), parkinglot_destroy);
+       if (!newlot)
+               return NULL;
+       
+       ast_copy_string(newlot->name, name, sizeof(newlot->name));
+
+       return newlot;
+}
+
+/*! \brief Destroy a parking lot */
+static void parkinglot_destroy(void *obj)
+{
+       struct ast_parkinglot *ruin = obj;
+       struct ast_context *con;
+       con = ast_context_find(ruin->parking_con);
+       if (con)
+               ast_context_destroy(con, registrar);
+       ao2_unlink(parkinglots, ruin);
+}
+
+/*! \brief Build parkinglot from configuration and chain it in */
+static struct ast_parkinglot *build_parkinglot(char *name, struct ast_variable *var)
+{
+       struct ast_parkinglot *parkinglot;
+       struct ast_context *con = NULL;
+
+       struct ast_variable *confvar = var;
+       int error = 0;
+       int start = 0, end = 0;
+       int oldparkinglot = 0;
+
+       parkinglot = find_parkinglot(name);
+       if (parkinglot)
+               oldparkinglot = 1;
+       else
+               parkinglot = create_parkinglot(name);
+
+       if (!parkinglot)
+               return NULL;
+
+       ao2_lock(parkinglot);
+
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Building parking lot %s\n", name);
+       
+       /* Do some config stuff */
+       while(confvar) {
+               if (!strcasecmp(confvar->name, "context")) {
+                       ast_copy_string(parkinglot->parking_con, confvar->value, sizeof(parkinglot->parking_con));
+               } else if (!strcasecmp(confvar->name, "parkingtime")) {
+                       if ((sscanf(confvar->value, "%d", &parkinglot->parkingtime) != 1) || (parkinglot->parkingtime < 1)) {
+                               ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", confvar->value);
+                               parkinglot->parkingtime = DEFAULT_PARK_TIME;
+                       } else
+                               parkinglot->parkingtime = parkinglot->parkingtime * 1000;
+               } else if (!strcasecmp(confvar->name, "parkpos")) {
+                       if (sscanf(confvar->value, "%d-%d", &start, &end) != 2) {
+                               ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of parking.conf\n", confvar->lineno);
+                               error = 1;
+                       } else {
+                               parkinglot->parking_start = start;
+                               parkinglot->parking_stop = end;
+                       }
+               } else if (!strcasecmp(confvar->name, "findslot")) {
+                       parkinglot->parkfindnext = (!strcasecmp(confvar->value, "next"));
+               }
+               confvar = confvar->next;
+       }
+       /* make sure parkingtime is set if not specified */
+       if (parkinglot->parkingtime == 0) {
+               parkinglot->parkingtime = DEFAULT_PARK_TIME;
+       }
+
+       if (!var) {     /* Default parking lot */
+               ast_copy_string(parkinglot->parking_con, "parkedcalls", sizeof(parkinglot->parking_con));
+               ast_copy_string(parkinglot->parking_con_dial, "park-dial", sizeof(parkinglot->parking_con_dial));
+               ast_copy_string(parkinglot->mohclass, "default", sizeof(parkinglot->mohclass));
+       }
+
+       /* Check for errors */
+       if (ast_strlen_zero(parkinglot->parking_con)) {
+               ast_log(LOG_WARNING, "Parking lot %s lacks context\n", name);
+               error = 1;
+       }
+
+       /* Create context */
+       if (!error && !(con = ast_context_find_or_create(NULL, NULL, parkinglot->parking_con, registrar))) {
+               ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parkinglot->parking_con);
+               error = 1;
+       }
+
+       /* Add a parking extension into the context */
+       if (!oldparkinglot) {
+               if (ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, strdup(""), ast_free, registrar) == -1)
+                       error = 1;
+       }
+
+       ao2_unlock(parkinglot);
+
+       if (error) {
+               ast_log(LOG_WARNING, "Parking %s not open for business. Configuration error.\n", name);
+               parkinglot_destroy(parkinglot);
+               return NULL;
+       }
+       if (option_debug)
+               ast_log(LOG_DEBUG, "Parking %s now open for business. (start exten %d end %d)\n", name, start, end);
+
+
+       /* Move it into the list, if it wasn't already there */
+       if (!oldparkinglot) {
+               ao2_link(parkinglots, parkinglot);
+       }
+       parkinglot_unref(parkinglot);
+
+       return parkinglot;
+}
+
+
 /*! 
  * \brief Add parking hints for all defined parking lots 
  * \param context
@@ -2560,28 +2841,42 @@ static int load_config(void)
                "applicationmap"
        };
 
-       if (!ast_strlen_zero(parking_con)) {
+       if (default_parkinglot) {
+               strcpy(old_parking_con, default_parkinglot->parking_con);
                strcpy(old_parking_ext, parking_ext);
-               strcpy(old_parking_con, parking_con);
-       } 
+       } else {
+               default_parkinglot = build_parkinglot(DEFAULT_PARKINGLOT, NULL);
+               if (default_parkinglot) {
+                       ao2_lock(default_parkinglot);
+                       default_parkinglot->parking_start = 701;
+                       default_parkinglot->parking_stop = 750;
+                       default_parkinglot->parking_offset = 0;
+                       default_parkinglot->parkfindnext = 0;
+                       default_parkinglot->parkingtime = 0;
+                       ao2_unlock(default_parkinglot);
+               }
+       }
+       if (default_parkinglot) {
+               if (option_debug)
+                       ast_log(LOG_DEBUG, "Configuration of default parkinglot done.\n");
+       } else {
+               ast_log(LOG_ERROR, "Configuration of default parkinglot failed.\n");
+               return -1;
+       }
+       
 
        /* Reset to defaults */
-       strcpy(parking_con, "parkedcalls");
-       strcpy(parking_con_dial, "park-dial");
        strcpy(parking_ext, "700");
        strcpy(pickup_ext, "*8");
-       strcpy(parkmohclass, "default");
        courtesytone[0] = '\0';
        strcpy(xfersound, "beep");
        strcpy(xferfailsound, "pbx-invalid");
-       parking_start = 701;
-       parking_stop = 750;
-       parkfindnext = 0;
        adsipark = 0;
        comebacktoorigin = 1;
-       parkaddhints = 0;
-       parkedcalltransfers = 0;
-       parkedcallreparking = 0;
+
+       default_parkinglot->parkaddhints = 0;
+       default_parkinglot->parkedcalltransfers = 0;
+       default_parkinglot->parkedcallreparking = 0;
 
        transferdigittimeout = DEFAULT_TRANSFER_DIGIT_TIMEOUT;
        featuredigittimeout = DEFAULT_FEATURE_DIGIT_TIMEOUT;
@@ -2599,38 +2894,40 @@ static int load_config(void)
                if (!strcasecmp(var->name, "parkext")) {
                        ast_copy_string(parking_ext, var->value, sizeof(parking_ext));
                } else if (!strcasecmp(var->name, "context")) {
-                       ast_copy_string(parking_con, var->value, sizeof(parking_con));
+                       ast_copy_string(default_parkinglot->parking_con, var->value, sizeof(default_parkinglot->parking_con));
                } else if (!strcasecmp(var->name, "parkingtime")) {
-                       if ((sscanf(var->value, "%d", &parkingtime) != 1) || (parkingtime < 1)) {
+                       if ((sscanf(var->value, "%d", &default_parkinglot->parkingtime) != 1) || (default_parkinglot->parkingtime < 1)) {
                                ast_log(LOG_WARNING, "%s is not a valid parkingtime\n", var->value);
-                               parkingtime = DEFAULT_PARK_TIME;
+                               default_parkinglot->parkingtime = DEFAULT_PARK_TIME;
                        } else
-                               parkingtime = parkingtime * 1000;
+                               default_parkinglot->parkingtime = default_parkinglot->parkingtime * 1000;
                } else if (!strcasecmp(var->name, "parkpos")) {
                        if (sscanf(var->value, "%d-%d", &start, &end) != 2) {
                                ast_log(LOG_WARNING, "Format for parking positions is a-b, where a and b are numbers at line %d of features.conf\n", var->lineno);
+                       } else if (default_parkinglot) {
+                               default_parkinglot->parking_start = start;
+                               default_parkinglot->parking_stop = end;
                        } else {
-                               parking_start = start;
-                               parking_stop = end;
+                               ast_log(LOG_WARNING, "No default parking lot!\n");
                        }
                } else if (!strcasecmp(var->name, "findslot")) {
-                       parkfindnext = (!strcasecmp(var->value, "next"));
+                       default_parkinglot->parkfindnext = (!strcasecmp(var->value, "next"));
                } else if (!strcasecmp(var->name, "parkinghints")) {
-                       parkaddhints = ast_true(var->value);
+                       default_parkinglot->parkaddhints = ast_true(var->value);
                } else if (!strcasecmp(var->name, "parkedcalltransfers")) {
                        if (!strcasecmp(var->value, "both"))
-                               parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
+                               default_parkinglot->parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
                        else if (!strcasecmp(var->value, "caller"))
-                               parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
+                               default_parkinglot->parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
                        else if (!strcasecmp(var->value, "callee"))
-                               parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
+                               default_parkinglot->parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
                } else if (!strcasecmp(var->name, "parkedcallreparking")) {
                        if (!strcasecmp(var->value, "both"))
-                               parkedcalltransfers = AST_FEATURE_FLAG_BYBOTH;
+                               default_parkinglot->parkedcallreparking = AST_FEATURE_FLAG_BYBOTH;
                        else if (!strcasecmp(var->value, "caller"))
-                               parkedcalltransfers = AST_FEATURE_FLAG_BYCALLER;
+                               default_parkinglot->parkedcallreparking = AST_FEATURE_FLAG_BYCALLER;
                        else if (!strcasecmp(var->value, "callee"))
-                               parkedcalltransfers = AST_FEATURE_FLAG_BYCALLEE;
+                               default_parkinglot->parkedcallreparking = AST_FEATURE_FLAG_BYCALLEE;
                } else if (!strcasecmp(var->name, "adsipark")) {
                        adsipark = ast_true(var->value);
                } else if (!strcasecmp(var->name, "transferdigittimeout")) {
@@ -2681,7 +2978,7 @@ static int load_config(void)
                } else if (!strcasecmp(var->name, "comebacktoorigin")) {
                        comebacktoorigin = ast_true(var->value);
                } else if (!strcasecmp(var->name, "parkedmusicclass")) {
-                       ast_copy_string(parkmohclass, var->value, sizeof(parkmohclass));
+                       ast_copy_string(default_parkinglot->mohclass, var->value, sizeof(default_parkinglot->mohclass));
                }
        }
 
@@ -2777,6 +3074,16 @@ static int load_config(void)
 
        ctg = NULL;
        while ((ctg = ast_category_browse(cfg, ctg))) {
+               /* Is this a parkinglot definition ? */
+               if (!strncasecmp(ctg, "parkinglot_", strlen("parkinglot_"))) {
+                       ast_debug(2, "Found configuration section %s, assume parking context\n", ctg);
+                       if(!build_parkinglot(ctg, ast_variable_browse(cfg, ctg)))
+                               ast_log(LOG_ERROR, "Could not build parking lot %s. Configuration error.\n", ctg);
+                       else
+                               ast_debug(1, "Configured parking context %s\n", ctg);
+                       continue;       
+               }
+               /* No, check if it's a group */
                for (i = 0; i < ARRAY_LEN(categories); i++) {
                        if (!strcasecmp(categories[i], ctg))
                                break;
@@ -2815,15 +3122,15 @@ static int load_config(void)
                ast_debug(1, "Removed old parking extension %s@%s\n", old_parking_ext, old_parking_con);
        }
        
-       if (!(con = ast_context_find_or_create(NULL, NULL, parking_con, registrar))) {
-               ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", parking_con);
+       if (!(con = ast_context_find_or_create(NULL, NULL, default_parkinglot->parking_con, registrar))) {
+               ast_log(LOG_ERROR, "Parking context '%s' does not exist and unable to create\n", default_parkinglot->parking_con);
                return -1;
        }
        res = ast_add_extension2(con, 1, ast_parking_ext(), 1, NULL, NULL, parkcall, NULL, NULL, registrar);
-       if (parkaddhints)
-               park_add_hints(parking_con, parking_start, parking_stop);
+       if (default_parkinglot->parkaddhints)
+               park_add_hints(default_parkinglot->parking_con, default_parkinglot->parking_start, default_parkinglot->parking_stop);
        if (!res)
-               notify_metermaids(ast_parking_ext(), parking_con, AST_DEVICE_INUSE);
+               notify_metermaids(ast_parking_ext(), default_parkinglot->parking_con, AST_DEVICE_INUSE);
        return res;
 
 }
@@ -2841,6 +3148,8 @@ static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cl
 {
        int i;
        struct ast_call_feature *feature;
+       struct ao2_iterator iter;
+       struct ast_parkinglot *curlot;
 #define HFS_FORMAT "%-25s %-7s %-7s\n"
 
        switch (cmd) {
@@ -2876,21 +3185,36 @@ static char *handle_feature_show(struct ast_cli_entry *e, int cmd, struct ast_cl
                        ast_cli(a->fd, HFS_FORMAT, feature->sname, "no def", feature->exten);
                AST_LIST_UNLOCK(&feature_list);
        }
-       ast_cli(a->fd, "\nCall parking\n");
-       ast_cli(a->fd, "------------\n");
-       ast_cli(a->fd,"%-20s:      %s\n", "Parking extension", parking_ext);
-       ast_cli(a->fd,"%-20s:      %s\n", "Parking context", parking_con);
-       ast_cli(a->fd,"%-20s:      %d-%d\n", "Parked call extensions", parking_start, parking_stop);
-       ast_cli(a->fd,"\n");
+
+       // loop through all the parking lots
+       iter = ao2_iterator_init(parkinglots, 0);
+
+       while ((curlot = ao2_iterator_next(&iter))) {
+               ast_cli(a->fd, "\nCall parking (Parking lot: %s)\n", curlot->name);
+               ast_cli(a->fd, "------------\n");
+               ast_cli(a->fd,"%-22s:      %s\n", "Parking extension", parking_ext);
+               ast_cli(a->fd,"%-22s:      %s\n", "Parking context", curlot->parking_con);
+               ast_cli(a->fd,"%-22s:      %d-%d\n", "Parked call extensions", curlot->parking_start, curlot->parking_stop);
+               ast_cli(a->fd,"\n");
+               ao2_ref(curlot, -1);
+       }
+
 
        return CLI_SUCCESS;
 }
 
 int ast_features_reload(void)
 {
-       load_config();
+       int res;
+       /* Release parking lot list */
+       //ASTOBJ_CONTAINER_MARKALL(&parkinglots);
+       // TODO: I don't think any marking is necessary
 
-       return RESULT_SUCCESS;
+       /* Reload configuration */
+       res = load_config();
+       
+       //ASTOBJ_CONTAINER_PRUNE_MARKED(&parkinglots, parkinglot_destroy);
+       return res;
 }
 
 static char *handle_features_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
@@ -2905,7 +3229,7 @@ static char *handle_features_reload(struct ast_cli_entry *e, int cmd, struct ast
        case CLI_GENERATE:
                return NULL;
        }
-       load_config();
+       ast_features_reload();
 
        return CLI_SUCCESS;
 }
@@ -3066,6 +3390,8 @@ static char *handle_parkedcalls(struct ast_cli_entry *e, int cmd, struct ast_cli
 {
        struct parkeduser *cur;
        int numparked = 0;
+       struct ao2_iterator iter;
+       struct ast_parkinglot *curlot;
 
        switch (cmd) {
        case CLI_INIT:
@@ -3084,17 +3410,27 @@ static char *handle_parkedcalls(struct ast_cli_entry *e, int cmd, struct ast_cli
        ast_cli(a->fd, "%4s %25s (%-15s %-12s %-4s) %-6s \n", "Num", "Channel"
                , "Context", "Extension", "Pri", "Timeout");
 
-       AST_LIST_LOCK(&parkinglot);
-       AST_LIST_TRAVERSE(&parkinglot, cur, list) {
-               ast_cli(a->fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n"
-                       ,cur->parkingexten, cur->chan->name, cur->context, cur->exten
-                       ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
+       iter = ao2_iterator_init(parkinglots, 0);
+       while ((curlot = ao2_iterator_next(&iter))) {
+               int lotparked = 0;
+               ast_cli(a->fd, "*** Parking lot: %s\n", curlot->name);
+
+               AST_LIST_LOCK(&curlot->parkings);
+               AST_LIST_TRAVERSE(&curlot->parkings, cur, list) {
+                       ast_cli(a->fd, "%-10.10s %25s (%-15s %-12s %-4d) %6lds\n"
+                               ,cur->parkingexten, cur->chan->name, cur->context, cur->exten
+                               ,cur->priority, cur->start.tv_sec + (cur->parkingtime/1000) - time(NULL));
+                       numparked++;
+                       numparked += lotparked;
+               }
+               AST_LIST_UNLOCK(&curlot->parkings);
+               if (lotparked)
+                       ast_cli(a->fd, "   %d parked call%s in parking lot %s\n", lotparked, ESS(lotparked), curlot->name);
 
-               numparked++;
+               ao2_ref(curlot, -1);
        }
-       AST_LIST_UNLOCK(&parkinglot);
-       ast_cli(a->fd, "%d parked call%s.\n", numparked, ESS(numparked));
 
+       ast_cli(a->fd, "---\n%d parked call%s in total.\n", numparked, ESS(numparked));
 
        return CLI_SUCCESS;
 }
@@ -3128,29 +3464,36 @@ static int manager_parking_status(struct mansession *s, const struct message *m)
        struct parkeduser *cur;
        const char *id = astman_get_header(m, "ActionID");
        char idText[256] = "";
+       struct ao2_iterator iter;
+       struct ast_parkinglot *curlot;
 
        if (!ast_strlen_zero(id))
                snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
 
        astman_send_ack(s, m, "Parked calls will follow");
 
-       AST_LIST_LOCK(&parkinglot);
-
-       AST_LIST_TRAVERSE(&parkinglot, cur, list) {
-               astman_append(s, "Event: ParkedCall\r\n"
-                       "Exten: %d\r\n"
-                       "Channel: %s\r\n"
-                       "From: %s\r\n"
-                       "Timeout: %ld\r\n"
-                       "CallerIDNum: %s\r\n"
-                       "CallerIDName: %s\r\n"
-                       "%s"
-                       "\r\n",
-                       cur->parkingnum, cur->chan->name, cur->peername,
-                       (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
-                       S_OR(cur->chan->cid.cid_num, ""),       /* XXX in other places it is <unknown> */
-                       S_OR(cur->chan->cid.cid_name, ""),
-                       idText);
+       iter = ao2_iterator_init(parkinglots, 0);
+       while ((curlot = ao2_iterator_next(&iter))) {
+
+               AST_LIST_LOCK(&curlot->parkings);
+               AST_LIST_TRAVERSE(&curlot->parkings, cur, list) {
+                       astman_append(s, "Event: ParkedCall\r\n"
+                               "Exten: %d\r\n"
+                               "Channel: %s\r\n"
+                               "From: %s\r\n"
+                               "Timeout: %ld\r\n"
+                               "CallerIDNum: %s\r\n"
+                               "CallerIDName: %s\r\n"
+                               "%s"
+                               "\r\n",
+                               cur->parkingnum, cur->chan->name, cur->peername,
+                               (long) cur->start.tv_sec + (long) (cur->parkingtime / 1000) - (long) time(NULL),
+                               S_OR(cur->chan->cid.cid_num, ""),       /* XXX in other places it is <unknown> */
+                               S_OR(cur->chan->cid.cid_name, ""),
+                               idText);
+               }
+               AST_LIST_UNLOCK(&curlot->parkings);
+               ao2_ref(curlot, -1);
        }
 
        astman_append(s,
@@ -3158,7 +3501,6 @@ static int manager_parking_status(struct mansession *s, const struct message *m)
                "%s"
                "\r\n",idText);
 
-       AST_LIST_UNLOCK(&parkinglot);
 
        return RESULT_SUCCESS;
 }
@@ -3427,8 +3769,7 @@ int ast_features_init(void)
 
        ast_register_application2(app_bridge, bridge_exec, bridge_synopsis, bridge_descrip, NULL);
 
-       memset(parking_ext, 0, sizeof(parking_ext));
-       memset(parking_con, 0, sizeof(parking_con));
+       parkinglots = ao2_container_alloc(7, parkinglot_hash_cb, parkinglot_cmp_cb);
 
        if ((res = load_config()))
                return res;
@@ -3439,8 +3780,7 @@ int ast_features_init(void)
                res = ast_register_application2(parkcall, park_call_exec, synopsis2, descrip2, NULL);
        if (!res) {
                ast_manager_register("ParkedCalls", 0, manager_parking_status, "List parked calls");
-               ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park,
-                       "Park a channel", mandescr_park); 
+               ast_manager_register2("Park", EVENT_FLAG_CALL, manager_park, "Park a channel", mandescr_park); 
                ast_manager_register2("Bridge", EVENT_FLAG_CALL, action_bridge, "Bridge two channels already in the PBX", mandescr_bridge);
        }