Index: channels/chan_sip.c =================================================================== --- channels/chan_sip.c (revision 191488) +++ channels/chan_sip.c (working copy) @@ -7072,10 +7072,10 @@ static int sip_register(const char *value, int lineno) { struct sip_registry *reg; - int portnum = 0; + unsigned short portnum = 0; enum sip_transport transport = SIP_TRANSPORT_UDP; - char buf[256] = ""; - char *username = NULL; + char buf[256] = "", *ptrbuf = buf, **next; + char *username = NULL, *port = NULL; char *hostname=NULL, *secret=NULL, *authuser=NULL, *expire=NULL; char *callback=NULL; @@ -7084,44 +7084,71 @@ ast_copy_string(buf, value, sizeof(buf)); - /* split [/contact][~expiry] */ - expire = strchr(buf, '~'); - if (expire) - *expire++ = '\0'; - callback = strrchr(buf, '/'); - if (callback) - *callback++ = '\0'; + if (strncmp(buf + 3, "://", 3) == 0) { + next = &ptrbuf + 6; + if (strncasecmp(buf, "udp", 3) == 0) { + transport = SIP_TRANSPORT_UDP; + } else if (strncasecmp(buf, "tcp", 3) == 0) { + transport = SIP_TRANSPORT_TCP; + } else if (strncasecmp(buf, "tls", 3) == 0) { + transport = SIP_TRANSPORT_TLS; + } else { + ast_log(LOG_NOTICE, "'%.3s' is not a valid transport type on line %d of sip.conf. defaulting to udp.\n", buf, lineno); + } + } else { + next = &ptrbuf; + } + + next = (ast_app_sep(*next, &username, &secret, ":")) ? &username : &secret; + ast_debug(1, "Username=%s, secret=%s, next=%s\n", username, secret, *next); + + if (!ast_app_sep(*next, next, &authuser, ":")) { + next = &authuser; + } + ast_debug(1, "Username=%s, secret=%s, authuser=%s, next=%s\n", username, secret, authuser, *next); + + if (!ast_app_sep(*next, next, &hostname, "@")) { + next = &hostname; + } /* else no hostname found?!! */ + ast_debug(1, "Username=%s, secret=%s, authuser=%s, hostname=%s, next=%s\n", username, secret, authuser, hostname, *next); + + if (!ast_app_sep(*next, next, &port, ":")) { + next = &port; + } + ast_debug(1, "Username=%s, secret=%s, authuser=%s, hostname=%s, port=%s, next=%s\n", username, secret, authuser, hostname, port, *next); + + if (!ast_app_sep(*next, next, &callback, "/")) { + next = &callback; + } + ast_debug(1, "Username=%s, secret=%s, authuser=%s, hostname=%s, port=%s, callback=%s, next=%s\n", username, secret, authuser, hostname, port, callback, *next); + + if (port && !sscanf(port, "%hu", &portnum)) { + ast_log(LOG_NOTICE, "'%s' is not a valid port number on line %d of sip.conf. using default.\n", port, lineno); + port = NULL; + } + + if (!port) { + if (transport & SIP_TRANSPORT_TLS) { + portnum = STANDARD_TLS_PORT; + } else { + portnum = STANDARD_SIP_PORT; + } + } + + if (!ast_app_sep(*next, next, &expire, "~")) { + next = &expire; + } + ast_debug(1, "Username=%s, secret=%s, authuser=%s, hostname=%s, port=%s, callback=%s, expire=%s, next=%s\n", username, secret, authuser, hostname, port, callback, expire, *next); + if (ast_strlen_zero(callback)) callback = "s"; - sip_parse_host(buf, lineno, &username, &portnum, &transport); - - /* First split around the last '@' then parse the two components. */ - hostname = strrchr(username, '@'); /* allow @ in the first part */ - if (hostname) - *hostname++ = '\0'; if (ast_strlen_zero(username) || ast_strlen_zero(hostname)) { + ast_debug(1, "username=%s, hostname=%s\n", username, hostname); ast_log(LOG_WARNING, "Format for registration is [transport://]user[:secret[:authuser]]@domain[:port][/extension][~expiry] at line %d\n", lineno); return -1; } - /* split user[:secret[:authuser]] from the end to allow : character in user portion*/ - authuser = strrchr(username, ':'); - if (authuser) { - *authuser++ = '\0'; - secret = strrchr(username, ':'); - if (secret) - *secret++ = '\0'; - else { - secret = authuser; - authuser = NULL; - } - } - if ((authuser) && (ast_strlen_zero(authuser))) - authuser = NULL; - if ((secret) && (ast_strlen_zero(secret))) - secret = NULL; - if (!(reg = ast_calloc(1, sizeof(*reg)))) { ast_log(LOG_ERROR, "Out of memory. Can't allocate SIP registry entry\n"); return -1; Index: include/asterisk/app.h =================================================================== --- include/asterisk/app.h (revision 191488) +++ include/asterisk/app.h (working copy) @@ -554,6 +554,20 @@ */ void ast_safe_fork_cleanup(void); +/*! + * \brief Common routine to separate arguments into 2 sections + * The first part is permitted to include the delimiter as a character + * either by quoting the entire part or by escaping it with a backslash. + * The primary purpose of this routine is to allow characters that are + * normally used for field separation to be a part of passwords. + * \param original The original string, which may be modified by the routine. + * \param string1 A string pointer for the first part + * \param string2 A string pointer for the second part + * \param delimiter A string containing each character permitted to be the delimiter. + * \return 0 on success or -1 if a delimiter was not found. + */ +int ast_app_sep(char *original, char **string1, char **string2, const char *separator); + #if defined(__cplusplus) || defined(c_plusplus) } #endif Index: main/app.c =================================================================== --- main/app.c (revision 191488) +++ main/app.c (working copy) @@ -2040,3 +2040,41 @@ ast_unreplace_sigchld(); } +AST_THREADSTORAGE(app_sep); + +int ast_app_sep(char *original, char **string1, char **string2, const char *separator) +{ + char *ptr; + int inquote = 0, res = -1; + struct ast_str *buf = ast_str_thread_get(&app_sep, 16); + + if (!buf) { + return -1; + } + ast_str_set(&buf, 0, "%s", original); + + *string1 = original; + *string2 = NULL; + + if (strchr(original, *separator) == NULL) { + return -1; + } + + for (ptr = *string1; *ptr; ptr++) { + if (*ptr == '\\' && (strchr("\"\\", *(ptr + 1)) || strchr(separator, *(ptr + 1)))) { + /* Backslash is our escape only for quotes, backslashes, and separator characters */ + strcpy(ptr, ptr + 1); + } else if (*ptr == '"') { + strcpy(ptr, ptr + 1); + ptr--; + inquote = inquote ? 0 : 1; + } else if (!inquote && strchr(separator, *ptr)) { + *ptr = '\0'; + *string2 = ptr + 1; + res = 0; + break; + } + } + return res; +} +