diff options
author | Alfred E. Heggestad <aeh@db.org> | 2014-02-09 11:50:07 +0100 |
---|---|---|
committer | Alfred E. Heggestad <aeh@db.org> | 2014-02-09 11:50:07 +0100 |
commit | 98bf08bdcf2edd9d397f32650a8bfe62186fbecf (patch) | |
tree | ebc6ec71f44bff8c42e4eefced61948623df02fc /src/account.c | |
parent | e6ad5cf4401b860ba402d4b7b3c7c254bc87a019 (diff) |
baresip 0.4.10
Diffstat (limited to 'src/account.c')
-rw-r--r-- | src/account.c | 574 |
1 files changed, 574 insertions, 0 deletions
diff --git a/src/account.c b/src/account.c new file mode 100644 index 0000000..95ceeba --- /dev/null +++ b/src/account.c @@ -0,0 +1,574 @@ +/** + * @file src/account.c User-Agent account + * + * Copyright (C) 2010 Creytiv.com + */ +#include <string.h> +#include <re.h> +#include <baresip.h> +#include "core.h" + + +enum { + REG_INTERVAL = 3600, +}; + + +static void destructor(void *arg) +{ + struct account *acc = arg; + size_t i; + + list_clear(&acc->aucodecl); + list_clear(&acc->vidcodecl); + mem_deref(acc->auth_user); + mem_deref(acc->auth_pass); + for (i=0; i<ARRAY_SIZE(acc->outbound); i++) + mem_deref(acc->outbound[i]); + mem_deref(acc->regq); + mem_deref(acc->rtpkeep); + mem_deref(acc->sipnat); + mem_deref(acc->stun_user); + mem_deref(acc->stun_pass); + mem_deref(acc->stun_host); + mem_deref(acc->mnatid); + mem_deref(acc->mencid); + mem_deref(acc->aor); + mem_deref(acc->dispname); + mem_deref(acc->buf); +} + + +static int param_dstr(char **dstr, const struct pl *params, const char *name) +{ + struct pl pl; + + if (sip_param_decode(params, name, &pl)) + return 0; + + return pl_strdup(dstr, &pl); +} + + +static int param_u32(uint32_t *v, const struct pl *params, const char *name) +{ + struct pl pl; + + if (sip_param_decode(params, name, &pl)) + return 0; + + *v = pl_u32(&pl); + + return 0; +} + + +static int stunsrv_decode(struct account *acc, const struct sip_addr *aor) +{ + struct pl srv; + struct uri uri; + int err; + + if (!acc || !aor) + return EINVAL; + + memset(&uri, 0, sizeof(uri)); + + if (0 == sip_param_decode(&aor->params, "stunserver", &srv)) { + + info("using stunserver: '%r'\n", &srv); + + err = uri_decode(&uri, &srv); + if (err) { + warning("account: %r: decode failed: %m\n", &srv, err); + memset(&uri, 0, sizeof(uri)); + } + + if (0 != pl_strcasecmp(&uri.scheme, "stun")) { + warning("account: unknown scheme: %r\n", &uri.scheme); + return EINVAL; + } + } + + err = 0; + if (pl_isset(&uri.user)) + err |= pl_strdup(&acc->stun_user, &uri.user); + else + err |= pl_strdup(&acc->stun_user, &aor->uri.user); + + if (pl_isset(&uri.password)) + err |= pl_strdup(&acc->stun_pass, &uri.password); + else + err |= pl_strdup(&acc->stun_pass, &aor->uri.password); + + if (pl_isset(&uri.host)) + err |= pl_strdup(&acc->stun_host, &uri.host); + else + err |= pl_strdup(&acc->stun_host, &aor->uri.host); + + acc->stun_port = uri.port; + + return err; +} + + +/** Decode media parameters */ +static int media_decode(struct account *acc, const struct pl *prm) +{ + int err = 0; + + if (!acc || !prm) + return EINVAL; + + err |= param_dstr(&acc->mencid, prm, "mediaenc"); + err |= param_dstr(&acc->mnatid, prm, "medianat"); + err |= param_dstr(&acc->rtpkeep, prm, "rtpkeep" ); + err |= param_u32(&acc->ptime, prm, "ptime" ); + + return err; +} + + +/* Decode answermode parameter */ +static void answermode_decode(struct account *prm, const struct pl *pl) +{ + struct pl amode; + + if (0 == sip_param_decode(pl, "answermode", &amode)) { + + if (0 == pl_strcasecmp(&amode, "manual")) { + prm->answermode = ANSWERMODE_MANUAL; + } + else if (0 == pl_strcasecmp(&amode, "early")) { + prm->answermode = ANSWERMODE_EARLY; + } + else if (0 == pl_strcasecmp(&amode, "auto")) { + prm->answermode = ANSWERMODE_AUTO; + } + else { + warning("account: answermode unknown (%r)\n", &amode); + prm->answermode = ANSWERMODE_MANUAL; + } + } +} + + +static int csl_parse(struct pl *pl, char *str, size_t sz) +{ + struct pl ws = PL_INIT, val, ws2 = PL_INIT, cma = PL_INIT; + int err; + + err = re_regex(pl->p, pl->l, "[ \t]*[^, \t]+[ \t]*[,]*", + &ws, &val, &ws2, &cma); + if (err) + return err; + + pl_advance(pl, ws.l + val.l + ws2.l + cma.l); + + (void)pl_strcpy(&val, str, sz); + + return 0; +} + + +static int audio_codecs_decode(struct account *acc, const struct pl *prm) +{ + struct pl tmp; + + if (!acc || !prm) + return EINVAL; + + list_init(&acc->aucodecl); + + if (0 == sip_param_exists(prm, "audio_codecs", &tmp)) { + struct pl acs; + char cname[64]; + unsigned i = 0; + + if (sip_param_decode(prm, "audio_codecs", &acs)) + return 0; + + while (0 == csl_parse(&acs, cname, sizeof(cname))) { + struct aucodec *ac; + struct pl pl_cname, pl_srate, pl_ch = PL_INIT; + uint32_t srate = 8000; + uint8_t ch = 1; + + /* Format: "codec/srate/ch" */ + if (0 == re_regex(cname, str_len(cname), + "[^/]+/[0-9]+[/]*[0-9]*", + &pl_cname, &pl_srate, + NULL, &pl_ch)) { + (void)pl_strcpy(&pl_cname, cname, + sizeof(cname)); + srate = pl_u32(&pl_srate); + if (pl_isset(&pl_ch)) + ch = pl_u32(&pl_ch); + } + + ac = (struct aucodec *)aucodec_find(cname, srate, ch); + if (!ac) { + warning("account: audio codec not found:" + " %s/%u/%d\n", + cname, srate, ch); + continue; + } + + /* NOTE: static list with references to aucodec */ + list_append(&acc->aucodecl, &acc->acv[i++], ac); + + if (i >= ARRAY_SIZE(acc->acv)) + break; + } + } + + return 0; +} + + +#ifdef USE_VIDEO +static int video_codecs_decode(struct account *acc, const struct pl *prm) +{ + struct pl tmp; + + if (!acc || !prm) + return EINVAL; + + list_init(&acc->vidcodecl); + + if (0 == sip_param_exists(prm, "video_codecs", &tmp)) { + struct pl vcs; + char cname[64]; + unsigned i = 0; + + if (sip_param_decode(prm, "video_codecs", &vcs)) + return 0; + + while (0 == csl_parse(&vcs, cname, sizeof(cname))) { + struct vidcodec *vc; + + vc = (struct vidcodec *)vidcodec_find(cname, NULL); + if (!vc) { + warning("account: video codec not found: %s\n", + cname); + continue; + } + + /* NOTE: static list with references to vidcodec */ + list_append(&acc->vidcodecl, &acc->vcv[i++], vc); + + if (i >= ARRAY_SIZE(acc->vcv)) + break; + } + } + + return 0; +} +#endif + + +static int sip_params_decode(struct account *acc, const struct sip_addr *aor) +{ + struct pl auth_user; + size_t i; + int err = 0; + + if (!acc || !aor) + return EINVAL; + + acc->regint = REG_INTERVAL + (rand_u32()&0xff); + err |= param_u32(&acc->regint, &aor->params, "regint"); + + err |= param_dstr(&acc->regq, &aor->params, "regq"); + + for (i=0; i<ARRAY_SIZE(acc->outbound); i++) { + + char expr[16] = "outbound"; + + expr[8] = i + 1 + 0x30; + expr[9] = '\0'; + + err |= param_dstr(&acc->outbound[i], &aor->params, expr); + } + + /* backwards compat */ + if (!acc->outbound[0]) { + err |= param_dstr(&acc->outbound[0], &aor->params, "outbound"); + } + + err |= param_dstr(&acc->sipnat, &aor->params, "sipnat"); + + if (0 == sip_param_decode(&aor->params, "auth_user", &auth_user)) + err |= pl_strdup(&acc->auth_user, &auth_user); + else + err |= pl_strdup(&acc->auth_user, &aor->uri.user); + + if (pl_isset(&aor->dname)) + err |= pl_strdup(&acc->dispname, &aor->dname); + + return err; +} + + +static int encode_uri_user(struct re_printf *pf, const struct uri *uri) +{ + struct uri uuri = *uri; + + uuri.password = uuri.params = uuri.headers = pl_null; + + return uri_encode(pf, &uuri); +} + + +static int password_prompt(struct account *acc) +{ + char pwd[64]; + char *nl; + int err; + + (void)re_printf("Please enter password for %r@%r: ", + &acc->luri.user, &acc->luri.host); + + /* note: blocking UI call */ + fgets(pwd, sizeof(pwd), stdin); + pwd[sizeof(pwd) - 1] = '\0'; + + nl = strchr(pwd, '\n'); + if (nl == NULL) { + (void)re_printf("Invalid password (0 - 63 characters" + " followed by newline)\n"); + return EINVAL; + } + + *nl = '\0'; + + err = str_dup(&acc->auth_pass, pwd); + if (err) + return err; + + return 0; +} + + +int account_alloc(struct account **accp, const char *sipaddr) +{ + struct account *acc; + struct pl pl; + int err = 0; + + if (!accp || !sipaddr) + return EINVAL; + + acc = mem_zalloc(sizeof(*acc), destructor); + if (!acc) + return ENOMEM; + + err = str_dup(&acc->buf, sipaddr); + if (err) + goto out; + + pl_set_str(&pl, acc->buf); + err = sip_addr_decode(&acc->laddr, &pl); + if (err) { + warning("account: invalid SIP address: `%r'\n", &pl); + goto out; + } + + acc->luri = acc->laddr.uri; + acc->luri.password = pl_null; + + err = re_sdprintf(&acc->aor, "%H", encode_uri_user, &acc->luri); + if (err) + goto out; + + /* Decode parameters */ + acc->ptime = 20; + err |= sip_params_decode(acc, &acc->laddr); + answermode_decode(acc, &acc->laddr.params); + err |= audio_codecs_decode(acc, &acc->laddr.params); +#ifdef USE_VIDEO + err |= video_codecs_decode(acc, &acc->laddr.params); +#endif + err |= media_decode(acc, &acc->laddr.params); + if (err) + goto out; + + /* optional password prompt */ + if (!pl_isset(&acc->laddr.uri.password)) { + err = password_prompt(acc); + if (err) + goto out; + } + else { + err = pl_strdup(&acc->auth_pass, &acc->laddr.uri.password); + if (err) + goto out; + } + + if (acc->mnatid) { + err = stunsrv_decode(acc, &acc->laddr); + if (err) + goto out; + + acc->mnat = mnat_find(acc->mnatid); + if (!acc->mnat) { + warning("account: medianat not found: %s\n", + acc->mnatid); + } + } + + if (acc->mencid) { + acc->menc = menc_find(acc->mencid); + if (!acc->menc) { + warning("account: mediaenc not found: %s\n", + acc->mencid); + } + } + + out: + if (err) + mem_deref(acc); + else + *accp = acc; + + return err; +} + + +/** + * Sets the displayed name. Pass null in dname to disable display name + * + * @param acc User-Agent account + * @param dname Display name (NULL to disable) + * + * @return 0 if success, otherwise errorcode + */ +int account_set_display_name(struct account *acc, const char *dname) +{ + if (!acc) + return EINVAL; + + acc->dispname = mem_deref(acc->dispname); + + if (dname) + return str_dup(&acc->dispname, dname); + + return 0; +} + + +/** + * Authenticate a User-Agent (UA) + * + * @param acc User-Agent account + * @param username Pointer to allocated username string + * @param password Pointer to allocated password string + * @param realm Realm string + * + * @return 0 if success, otherwise errorcode + */ +int account_auth(const struct account *acc, char **username, char **password, + const char *realm) +{ + if (!acc) + return EINVAL; + + (void)realm; + + *username = mem_ref(acc->auth_user); + *password = mem_ref(acc->auth_pass); + + return 0; +} + + +struct list *account_aucodecl(const struct account *acc) +{ + return (acc && !list_isempty(&acc->aucodecl)) + ? (struct list *)&acc->aucodecl : aucodec_list(); +} + + +#ifdef USE_VIDEO +struct list *account_vidcodecl(const struct account *acc) +{ + return (acc && !list_isempty(&acc->vidcodecl)) + ? (struct list *)&acc->vidcodecl : vidcodec_list(); +} +#endif + + +struct sip_addr *account_laddr(const struct account *acc) +{ + return acc ? (struct sip_addr *)&acc->laddr : NULL; +} + + +static const char *answermode_str(enum answermode mode) +{ + switch (mode) { + + case ANSWERMODE_MANUAL: return "manual"; + case ANSWERMODE_EARLY: return "early"; + case ANSWERMODE_AUTO: return "auto"; + default: return "???"; + } +} + + +int account_debug(struct re_printf *pf, const struct account *acc) +{ + struct le *le; + size_t i; + int err = 0; + + if (!acc) + return 0; + + err |= re_hprintf(pf, "\nAccount:\n"); + + err |= re_hprintf(pf, " address: %s\n", acc->buf); + err |= re_hprintf(pf, " luri: %H\n", + uri_encode, &acc->luri); + err |= re_hprintf(pf, " aor: %s\n", acc->aor); + err |= re_hprintf(pf, " dispname: %s\n", acc->dispname); + err |= re_hprintf(pf, " answermode: %s\n", + answermode_str(acc->answermode)); + if (!list_isempty(&acc->aucodecl)) { + err |= re_hprintf(pf, " audio_codecs:"); + for (le = list_head(&acc->aucodecl); le; le = le->next) { + const struct aucodec *ac = le->data; + err |= re_hprintf(pf, " %s/%u/%u", + ac->name, ac->srate, ac->ch); + } + err |= re_hprintf(pf, "\n"); + } + err |= re_hprintf(pf, " auth_user: %s\n", acc->auth_user); + err |= re_hprintf(pf, " mediaenc: %s\n", + acc->mencid ? acc->mencid : "none"); + err |= re_hprintf(pf, " medianat: %s\n", + acc->mnatid ? acc->mnatid : "none"); + for (i=0; i<ARRAY_SIZE(acc->outbound); i++) { + if (acc->outbound[i]) { + err |= re_hprintf(pf, " outbound%d: %s\n", + i+1, acc->outbound[i]); + } + } + err |= re_hprintf(pf, " ptime: %u\n", acc->ptime); + err |= re_hprintf(pf, " regint: %u\n", acc->regint); + err |= re_hprintf(pf, " regq: %s\n", acc->regq); + err |= re_hprintf(pf, " rtpkeep: %s\n", acc->rtpkeep); + err |= re_hprintf(pf, " sipnat: %s\n", acc->sipnat); + err |= re_hprintf(pf, " stunserver: stun:%s@%s:%u\n", + acc->stun_user, acc->stun_host, acc->stun_port); + if (!list_isempty(&acc->vidcodecl)) { + err |= re_hprintf(pf, " video_codecs:"); + for (le = list_head(&acc->vidcodecl); le; le = le->next) { + const struct vidcodec *vc = le->data; + err |= re_hprintf(pf, " %s", vc->name); + } + err |= re_hprintf(pf, "\n"); + } + + return err; +} |