diff --git a/apps/app_dumpchan.c b/apps/app_dumpchan.c index 37e040e9b32d0966cc1d335bd2d27bce1c0b6c75..d01ea7aedfd1123482ad7c2397e277da77aaf173 100644 --- a/apps/app_dumpchan.c +++ b/apps/app_dumpchan.c @@ -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, diff --git a/channels/chan_console.c b/channels/chan_console.c index 7d62e6235c0e6aa8176193f23a903df11c7593d8..0fe3cbef4f2fade9eee0747e2e76162b30a943da 100644 --- a/channels/chan_console.c +++ b/channels/chan_console.c @@ -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)) diff --git a/channels/chan_gtalk.c b/channels/chan_gtalk.c index fb58aa490eab659c015afac70f3125cac7961284..04ea60865e331bc676c3a341c8e3ca21c37a782f 100644 --- a/channels/chan_gtalk.c +++ b/channels/chan_gtalk.c @@ -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")) { diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c index d9303405498ca0fda681020cb7d9e02f6e5b8aa4..c11fa1cd7ef4a7ecc1593f51034b220f78d01a1e 100644 --- a/channels/chan_iax2.c +++ b/channels/chan_iax2.c @@ -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")) { diff --git a/channels/chan_jingle.c b/channels/chan_jingle.c index 52a4693e072b36bb2ba54bfa9b8e145cfe0c2b98..7285549162361ec3a7828eb8b9bf3e749ddbbae8 100644 --- a/channels/chan_jingle.c +++ b/channels/chan_jingle.c @@ -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")) { diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c index f18dac361de11630b84713ded30355e31e93dc80..021999abdf44e37738e15162c05946b24341d690 100644 --- a/channels/chan_mgcp.c +++ b/channels/chan_mgcp.c @@ -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); } diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 79fc483990b98cc616bd1f87362a3ab08a43a8fa..31bd5763d3167a08259e66bc74b0cb55a0a31582 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -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")) { diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index 198b464068ea22fc0fd8251ee0f5ab62ca5fd463..c4458ba04e879f4a4e9dd694f4b1ac2c62ac6df2 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -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)); diff --git a/channels/chan_unistim.c b/channels/chan_unistim.c index 88f37fbec175f8e14f9db98d67d33c9f349a9689..7318e7353d34cda22c0e69261ff763f86524aed7 100644 --- a/channels/chan_unistim.c +++ b/channels/chan_unistim.c @@ -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")) { diff --git a/channels/chan_zap.c b/channels/chan_zap.c index def1015f87fa5a612853b4b3790651883d93ecbf..927275d8b77cd4b92a1865747691aa798d664886 100644 --- a/channels/chan_zap.c +++ b/channels/chan_zap.c @@ -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); diff --git a/configs/features.conf.sample b/configs/features.conf.sample index 3666e804b2988d9c83bc56a77fe199be22b71ecc..6e833891fe9f37cbfea2afd9a56aeee9707a53ce 100644 --- a/configs/features.conf.sample +++ b/configs/features.conf.sample @@ -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] - diff --git a/configs/iax.conf.sample b/configs/iax.conf.sample index 2441f2cf4288904278eb51dcbc6fa722893c6664..105940060d6b0bdf17069e713dc3a4fa9040eae3 100644 --- a/configs/iax.conf.sample +++ b/configs/iax.conf.sample @@ -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. ; diff --git a/configs/sip.conf.sample b/configs/sip.conf.sample index 10293a77ca1e70a11ee51e3b88ea55b0585a5be7..7a3d114605c2ccea13233b9709751c3ddb9986ab 100644 --- a/configs/sip.conf.sample +++ b/configs/sip.conf.sample @@ -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 diff --git a/funcs/func_channel.c b/funcs/func_channel.c index 42b3c68c9f7e64b3b5f6755f9c57e774a9e2a2ba..8388d7fa6d2e644996e364cd819d9906fd5a892c 100644 --- a/funcs/func_channel.c +++ b/funcs/func_channel.c @@ -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" diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index ddba96f8942b0e05de1ca33a9331a059e25adf15..e0b1f79bb58eb0d56fc45cbb87454c5f8a77bf59 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -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 diff --git a/main/features.c b/main/features.c index 089f27a0fe75a8e2e5e877320d50d2102d94e8df..59e2d9a58bd4229df32519c9d75e50c4db6bfc06 100644 --- a/main/features.c +++ b/main/features.c @@ -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); }