Sun May 20 06:33:57 2012

Asterisk developer's documentation


reqresp_parser.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2010, Digium, Inc.
00005  *
00006  * See http://www.asterisk.org for more information about
00007  * the Asterisk project. Please do not directly contact
00008  * any of the maintainers of this project for assistance;
00009  * the project provides a web site, mailing lists and IRC
00010  * channels for your use.
00011  *
00012  * This program is free software, distributed under the terms of
00013  * the GNU General Public License Version 2. See the LICENSE file
00014  * at the top of the source tree.
00015  */
00016 
00017 /*!
00018  * \file
00019  * \brief sip request parsing functions and unit tests
00020  */
00021 
00022 #include "asterisk.h"
00023 
00024 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 366169 $")
00025 
00026 #include "include/sip.h"
00027 #include "include/sip_utils.h"
00028 #include "include/reqresp_parser.h"
00029 
00030 #ifdef HAVE_XLOCALE_H
00031 locale_t c_locale;
00032 #endif
00033 
00034 /*! \brief * parses a URI in its components.*/
00035 int parse_uri_full(char *uri, const char *scheme, char **user, char **pass,
00036          char **hostport, struct uriparams *params, char **headers,
00037          char **residue)
00038 {
00039    char *userinfo = NULL;
00040    char *parameters = NULL;
00041    char *endparams = NULL;
00042    char *c = NULL;
00043    int error = 0;
00044 
00045    /*
00046     * Initialize requested strings - some functions don't care if parse_uri fails
00047     * and will attempt to use string pointers passed into parse_uri even after a
00048     * parse_uri failure
00049     */
00050    if (user) {
00051       *user = "";
00052    }
00053    if (pass) {
00054       *pass = "";
00055    }
00056    if (hostport) {
00057       *hostport = "";
00058    }
00059    if (headers) {
00060       *headers = "";
00061    }
00062    if (residue) {
00063       *residue = "";
00064    }
00065 
00066    /* check for valid input */
00067    if (ast_strlen_zero(uri)) {
00068       return -1;
00069    }
00070 
00071    if (scheme) {
00072       int l;
00073       char *scheme2 = ast_strdupa(scheme);
00074       char *cur = strsep(&scheme2, ",");
00075       for (; !ast_strlen_zero(cur); cur = strsep(&scheme2, ",")) {
00076          l = strlen(cur);
00077          if (!strncasecmp(uri, cur, l)) {
00078             uri += l;
00079             break;
00080          }
00081       }
00082       if (ast_strlen_zero(cur)) {
00083          ast_debug(1, "No supported scheme found in '%s' using the scheme[s] %s\n", uri, scheme);
00084          error = -1;
00085       }
00086    }
00087 
00088    if (!hostport) {
00089       /* if we don't want to split around hostport, keep everything as a
00090        * userinfo - cos thats how old parse_uri operated*/
00091       userinfo = uri;
00092    } else {
00093       char *dom = "";
00094       if ((c = strchr(uri, '@'))) {
00095          *c++ = '\0';
00096          dom = c;
00097          userinfo = uri;
00098          uri = c; /* userinfo can contain ? and ; chars so step forward before looking for params and headers */
00099       } else {
00100          /* domain-only URI, according to the SIP RFC. */
00101          dom = uri;
00102          userinfo = "";
00103       }
00104 
00105       *hostport = dom;
00106    }
00107 
00108    if (pass && (c = strchr(userinfo, ':'))) {     /* user:password */
00109       *c++ = '\0';
00110       *pass = c;
00111    } else if (pass) {
00112       *pass = "";
00113    }
00114 
00115    if (user) {
00116       *user = userinfo;
00117    }
00118 
00119    parameters = uri;
00120    /* strip [?headers] from end of uri  - even if no header pointer exists*/
00121    if ((c = strrchr(uri, '?'))) {
00122       *c++ = '\0';
00123       uri = c;
00124       if (headers) {
00125          *headers = c;
00126       }
00127       if ((c = strrchr(uri, ';'))) {
00128          *c++ = '\0';
00129       } else {
00130          c = strrchr(uri, '\0');
00131       }
00132       uri = c; /* residue */
00133 
00134 
00135    } else if (headers) {
00136       *headers = "";
00137    }
00138 
00139    /* parse parameters */
00140    endparams = strchr(parameters,'\0');
00141    if ((c = strchr(parameters, ';'))) {
00142       *c++ = '\0';
00143       parameters = c;
00144    } else {
00145       parameters = endparams;
00146    }
00147 
00148    if (params) {
00149       char *rem = parameters; /* unparsed or unrecognised remainder */
00150       char *label;
00151       char *value;
00152       int lr = 0;
00153 
00154       params->transport = "";
00155       params->user = "";
00156       params->method = "";
00157       params->ttl = "";
00158       params->maddr = "";
00159       params->lr = 0;
00160 
00161       rem = parameters;
00162 
00163       while ((value = strchr(parameters, '=')) || (lr = !strncmp(parameters, "lr", 2))) {
00164          /* The while condition will not continue evaluation to set lr if it matches "lr=" */
00165          if (lr) {
00166             value = parameters;
00167          } else {
00168             *value++ = '\0';
00169          }
00170          label = parameters;
00171          if ((c = strchr(value, ';'))) {
00172             *c++ = '\0';
00173             parameters = c;
00174          } else {
00175             parameters = endparams;
00176          }
00177 
00178          if (!strcmp(label, "transport")) {
00179             params->transport = value;
00180             rem = parameters;
00181          } else if (!strcmp(label, "user")) {
00182             params->user = value;
00183             rem = parameters;
00184          } else if (!strcmp(label, "method")) {
00185             params->method = value;
00186             rem = parameters;
00187          } else if (!strcmp(label, "ttl")) {
00188             params->ttl = value;
00189             rem = parameters;
00190          } else if (!strcmp(label, "maddr")) {
00191             params->maddr = value;
00192             rem = parameters;
00193          /* Treat "lr", "lr=yes", "lr=on", "lr=1", "lr=almostanything" as lr enabled and "", "lr=no", "lr=off", "lr=0", "lr=" and "lranything" as lr disabled */
00194          } else if ((!strcmp(label, "lr") && strcmp(value, "no") && strcmp(value, "off") && strcmp(value, "0") && strcmp(value, "")) || ((lr) && strcmp(value, "lr"))) {
00195             params->lr = 1;
00196             rem = parameters;
00197          } else {
00198             value--;
00199             *value = '=';
00200             if (c) {
00201                c--;
00202                *c = ';';
00203             }
00204          }
00205       }
00206       if (rem > uri) { /* no headers */
00207          uri = rem;
00208       }
00209 
00210    }
00211 
00212    if (residue) {
00213       *residue = uri;
00214    }
00215 
00216    return error;
00217 }
00218 
00219 
00220 AST_TEST_DEFINE(sip_parse_uri_full_test)
00221 {
00222    int res = AST_TEST_PASS;
00223    char uri[1024];
00224    char *user, *pass, *hostport, *headers, *residue;
00225    struct uriparams params;
00226 
00227    struct testdata {
00228       char *desc;
00229       char *uri;
00230       char *user;
00231       char *pass;
00232       char *hostport;
00233       char *headers;
00234       char *residue;
00235       struct uriparams params;
00236       AST_LIST_ENTRY(testdata) list;
00237    };
00238 
00239 
00240    struct testdata *testdataptr;
00241 
00242    static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
00243 
00244    struct testdata td1 = {
00245       .desc = "no headers",
00246       .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=residue",
00247       .user = "user",
00248       .pass = "secret",
00249       .hostport = "host:5060",
00250       .headers = "",
00251       .residue = "param2=residue",
00252       .params.transport = "tcp",
00253       .params.lr = 0,
00254       .params.user = ""
00255    };
00256 
00257    struct testdata td2 = {
00258       .desc = "with headers",
00259       .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;param2=discard2?header=blah&header2=blah2;param3=residue",
00260       .user = "user",
00261       .pass = "secret",
00262       .hostport = "host:5060",
00263       .headers = "header=blah&header2=blah2",
00264       .residue = "param3=residue",
00265       .params.transport = "tcp",
00266       .params.lr = 0,
00267       .params.user = ""
00268    };
00269 
00270    struct testdata td3 = {
00271       .desc = "difficult user",
00272       .uri = "sip:-_.!~*'()&=+$,;?/:secret@host:5060;transport=tcp",
00273       .user = "-_.!~*'()&=+$,;?/",
00274       .pass = "secret",
00275       .hostport = "host:5060",
00276       .headers = "",
00277       .residue = "",
00278       .params.transport = "tcp",
00279       .params.lr = 0,
00280       .params.user = ""
00281    };
00282 
00283    struct testdata td4 = {
00284       .desc = "difficult pass",
00285       .uri = "sip:user:-_.!~*'()&=+$,@host:5060;transport=tcp",
00286       .user = "user",
00287       .pass = "-_.!~*'()&=+$,",
00288       .hostport = "host:5060",
00289       .headers = "",
00290       .residue = "",
00291       .params.transport = "tcp",
00292       .params.lr = 0,
00293       .params.user = ""
00294    };
00295 
00296    struct testdata td5 = {
00297       .desc = "difficult host",
00298       .uri = "sip:user:secret@1-1.a-1.:5060;transport=tcp",
00299       .user = "user",
00300       .pass = "secret",
00301       .hostport = "1-1.a-1.:5060",
00302       .headers = "",
00303       .residue = "",
00304       .params.transport = "tcp",
00305       .params.lr = 0,
00306       .params.user = ""
00307    };
00308 
00309    struct testdata td6 = {
00310       .desc = "difficult params near transport",
00311       .uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$;transport=tcp",
00312       .user = "user",
00313       .pass = "secret",
00314       .hostport = "host:5060",
00315       .headers = "",
00316       .residue = "",
00317       .params.transport = "tcp",
00318       .params.lr = 0,
00319       .params.user = ""
00320    };
00321 
00322    struct testdata td7 = {
00323       .desc = "difficult params near headers",
00324       .uri = "sip:user:secret@host:5060;-_.!~*'()[]/:&+$=-_.!~*'()[]/:&+$?header=blah&header2=blah2;-_.!~*'()[]/:&+$=residue",
00325       .user = "user",
00326       .pass = "secret",
00327       .hostport = "host:5060",
00328       .headers = "header=blah&header2=blah2",
00329       .residue = "-_.!~*'()[]/:&+$=residue",
00330       .params.transport = "",
00331       .params.lr = 0,
00332       .params.user = ""
00333    };
00334 
00335    struct testdata td8 = {
00336       .desc = "lr parameter",
00337       .uri = "sip:user:secret@host:5060;param=discard;lr?header=blah",
00338       .user = "user",
00339       .pass = "secret",
00340       .hostport = "host:5060",
00341       .headers = "header=blah",
00342       .residue = "",
00343       .params.transport = "",
00344       .params.lr = 1,
00345       .params.user = ""
00346    };
00347 
00348    struct testdata td9 = {
00349       .desc = "alternative lr parameter",
00350       .uri = "sip:user:secret@host:5060;param=discard;lr=yes?header=blah",
00351       .user = "user",
00352       .pass = "secret",
00353       .hostport = "host:5060",
00354       .headers = "header=blah",
00355       .residue = "",
00356       .params.transport = "",
00357       .params.lr = 1,
00358       .params.user = ""
00359    };
00360 
00361    struct testdata td10 = {
00362       .desc = "no lr parameter",
00363       .uri = "sip:user:secret@host:5060;paramlr=lr;lr=no;lr=off;lr=0;lr=;=lr;lrextra;lrparam2=lr?header=blah",
00364       .user = "user",
00365       .pass = "secret",
00366       .hostport = "host:5060",
00367       .headers = "header=blah",
00368       .residue = "",
00369       .params.transport = "",
00370       .params.lr = 0,
00371       .params.user = ""
00372    };
00373 
00374 
00375    AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
00376    AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
00377    AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
00378    AST_LIST_INSERT_TAIL(&testdatalist, &td4, list);
00379    AST_LIST_INSERT_TAIL(&testdatalist, &td5, list);
00380    AST_LIST_INSERT_TAIL(&testdatalist, &td6, list);
00381    AST_LIST_INSERT_TAIL(&testdatalist, &td7, list);
00382    AST_LIST_INSERT_TAIL(&testdatalist, &td8, list);
00383    AST_LIST_INSERT_TAIL(&testdatalist, &td9, list);
00384    AST_LIST_INSERT_TAIL(&testdatalist, &td10, list);
00385 
00386 
00387    switch (cmd) {
00388    case TEST_INIT:
00389       info->name = "sip_uri_full_parse_test";
00390       info->category = "/channels/chan_sip/";
00391       info->summary = "tests sip full uri parsing";
00392       info->description =
00393          "Tests full parsing of various URIs "
00394          "Verifies output matches expected behavior.";
00395       return AST_TEST_NOT_RUN;
00396    case TEST_EXECUTE:
00397       break;
00398    }
00399 
00400    AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
00401       user = pass = hostport = headers = residue = NULL;
00402       params.transport = params.user = params.method = params.ttl = params.maddr = NULL;
00403       params.lr = 0;
00404 
00405       ast_copy_string(uri,testdataptr->uri,sizeof(uri));
00406       if (parse_uri_full(uri, "sip:,sips:", &user,
00407                &pass, &hostport,
00408                &params,
00409                &headers,
00410                &residue) ||
00411          (user && strcmp(testdataptr->user, user)) ||
00412          (pass && strcmp(testdataptr->pass, pass)) ||
00413          (hostport && strcmp(testdataptr->hostport, hostport)) ||
00414          (headers && strcmp(testdataptr->headers, headers)) ||
00415          (residue && strcmp(testdataptr->residue, residue)) ||
00416          (strcmp(testdataptr->params.transport,params.transport)) ||
00417          (testdataptr->params.lr != params.lr) ||
00418          (strcmp(testdataptr->params.user,params.user))
00419       ) {
00420             ast_test_status_update(test, "Sub-Test: %s, failed.\n", testdataptr->desc);
00421             res = AST_TEST_FAIL;
00422       }
00423    }
00424 
00425 
00426    return res;
00427 }
00428 
00429 
00430 int parse_uri(char *uri, const char *scheme, char **user, char **pass,
00431          char **hostport, char **transport) {
00432    int ret;
00433    char *headers;
00434    struct uriparams params;
00435 
00436    headers = NULL;
00437    ret = parse_uri_full(uri, scheme, user, pass, hostport, &params, &headers, NULL);
00438    if (transport) {
00439       *transport=params.transport;
00440    }
00441    return ret;
00442 }
00443 
00444 AST_TEST_DEFINE(sip_parse_uri_test)
00445 {
00446    int res = AST_TEST_PASS;
00447    char *name, *pass, *hostport, *transport;
00448    char uri1[] = "sip:name@host";
00449    char uri2[] = "sip:name@host;transport=tcp";
00450    char uri3[] = "sip:name:secret@host;transport=tcp";
00451    char uri4[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
00452    /* test 5 is for NULL input */
00453    char uri6[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
00454    char uri7[] = "sip:name:secret@host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
00455    char uri8[] = "sip:host";
00456    char uri9[] = "sip:host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
00457    char uri10[] = "host:port;transport=tcp?headers=%40%40testblah&headers2=blah%20blah";
00458    char uri11[] = "host";
00459 
00460    switch (cmd) {
00461    case TEST_INIT:
00462       info->name = "sip_uri_parse_test";
00463       info->category = "/channels/chan_sip/";
00464       info->summary = "tests sip uri parsing";
00465       info->description =
00466                      "Tests parsing of various URIs "
00467                      "Verifies output matches expected behavior.";
00468       return AST_TEST_NOT_RUN;
00469    case TEST_EXECUTE:
00470       break;
00471    }
00472 
00473    /* Test 1, simple URI */
00474    name = pass = hostport = transport = NULL;
00475    if (parse_uri(uri1, "sip:,sips:", &name, &pass, &hostport, &transport) ||
00476          strcmp(name, "name")        ||
00477          !ast_strlen_zero(pass)      ||
00478          strcmp(hostport, "host")      ||
00479          !ast_strlen_zero(transport)) {
00480       ast_test_status_update(test, "Test 1: simple uri failed. \n");
00481       res = AST_TEST_FAIL;
00482    }
00483 
00484    /* Test 2, add tcp transport */
00485    name = pass = hostport = transport = NULL;
00486    if (parse_uri(uri2, "sip:,sips:", &name, &pass, &hostport, &transport) ||
00487          strcmp(name, "name")        ||
00488          !ast_strlen_zero(pass)      ||
00489          strcmp(hostport, "host")    ||
00490          strcmp(transport, "tcp")) {
00491       ast_test_status_update(test, "Test 2: uri with addtion of tcp transport failed. \n");
00492       res = AST_TEST_FAIL;
00493    }
00494 
00495    /* Test 3, add secret */
00496    name = pass = hostport = transport = NULL;
00497    if (parse_uri(uri3, "sip:,sips:", &name, &pass, &hostport, &transport) ||
00498          strcmp(name, "name")        ||
00499          strcmp(pass, "secret")      ||
00500          strcmp(hostport, "host")    ||
00501          strcmp(transport, "tcp")) {
00502       ast_test_status_update(test, "Test 3: uri with addition of secret failed.\n");
00503       res = AST_TEST_FAIL;
00504    }
00505 
00506    /* Test 4, add port and unparsed header field*/
00507    name = pass = hostport = transport = NULL;
00508    if (parse_uri(uri4, "sip:,sips:", &name, &pass, &hostport, &transport) ||
00509          strcmp(name, "name")        ||
00510          strcmp(pass, "secret")      ||
00511          strcmp(hostport, "host:port") ||
00512          strcmp(transport, "tcp")) {
00513       ast_test_status_update(test, "Test 4: add port and unparsed header field failed.\n");
00514       res = AST_TEST_FAIL;
00515    }
00516 
00517    /* Test 5, verify parse_uri does not crash when given a NULL uri */
00518    name = pass = hostport = transport = NULL;
00519    if (!parse_uri(NULL, "sip:,sips:", &name, &pass, &hostport, &transport)) {
00520       ast_test_status_update(test, "Test 5: passing a NULL uri failed.\n");
00521       res = AST_TEST_FAIL;
00522    }
00523 
00524    /* Test 6, verify parse_uri does not crash when given a NULL output parameters */
00525    name = pass = hostport = transport = NULL;
00526    if (parse_uri(uri6, "sip:,sips:", NULL, NULL, NULL, NULL)) {
00527       ast_test_status_update(test, "Test 6: passing NULL output parameters failed.\n");
00528       res = AST_TEST_FAIL;
00529    }
00530 
00531    /* Test 7, verify parse_uri returns user:secret and hostport when no port or secret output parameters are supplied. */
00532    name = pass = hostport = transport = NULL;
00533    if (parse_uri(uri7, "sip:,sips:", &name, NULL, &hostport, NULL) ||
00534          strcmp(name, "name:secret")        ||
00535          strcmp(hostport, "host:port")) {
00536 
00537       ast_test_status_update(test, "Test 7: providing no port and secret output parameters failed.\n");
00538       res = AST_TEST_FAIL;
00539    }
00540 
00541    /* Test 8, verify parse_uri can handle a hostport only uri */
00542    name = pass = hostport = transport = NULL;
00543    if (parse_uri(uri8, "sip:,sips:", &name, &pass, &hostport, &transport) ||
00544          strcmp(hostport, "host") ||
00545          !ast_strlen_zero(name)) {
00546       ast_test_status_update(test, "Test 8: add port and unparsed header field failed.\n");
00547       res = AST_TEST_FAIL;
00548    }
00549 
00550    /* Test 9, add port and unparsed header field with hostport only uri*/
00551    name = pass = hostport = transport = NULL;
00552    if (parse_uri(uri9, "sip:,sips:", &name, &pass, &hostport, &transport) ||
00553          !ast_strlen_zero(name)        ||
00554          !ast_strlen_zero(pass)      ||
00555          strcmp(hostport, "host:port")    ||
00556          strcmp(transport, "tcp")) {
00557       ast_test_status_update(test, "Test 9: hostport only uri failed \n");
00558       res = AST_TEST_FAIL;
00559    }
00560 
00561    /* Test 10, handle invalid/missing "sip:,sips:" scheme
00562     * we expect parse_uri to return an error, but still parse
00563     * the results correctly here */
00564    name = pass = hostport = transport = NULL;
00565    if (!parse_uri(uri10, "sip:,sips:", &name, &pass, &hostport, &transport) ||
00566          !ast_strlen_zero(name)        ||
00567          !ast_strlen_zero(pass)      ||
00568          strcmp(hostport, "host:port")    ||
00569          strcmp(transport, "tcp")) {
00570       ast_test_status_update(test, "Test 10: missing \"sip:sips:\" scheme failed\n");
00571       res = AST_TEST_FAIL;
00572    }
00573 
00574    /* Test 11, simple hostport only URI with missing scheme
00575     * we expect parse_uri to return an error, but still parse
00576     * the results correctly here */
00577    name = pass = hostport = transport = NULL;
00578    if (!parse_uri(uri11, "sip:,sips:", &name, &pass, &hostport, &transport) ||
00579          !ast_strlen_zero(name)      ||
00580          !ast_strlen_zero(pass)      ||
00581          strcmp(hostport, "host")      ||
00582          !ast_strlen_zero(transport)) {
00583       ast_test_status_update(test, "Test 11: simple uri with missing scheme failed. \n");
00584       res = AST_TEST_FAIL;
00585    }
00586 
00587    return res;
00588 }
00589 
00590 /*! \brief  Get caller id name from SIP headers, copy into output buffer
00591  *
00592  *  \retval input string pointer placed after display-name field if possible
00593  */
00594 const char *get_calleridname(const char *input, char *output, size_t outputsize)
00595 {
00596    /* From RFC3261:
00597     *
00598     * From           =  ( "From" / "f" ) HCOLON from-spec
00599     * from-spec      =  ( name-addr / addr-spec ) *( SEMI from-param )
00600     * name-addr      =  [ display-name ] LAQUOT addr-spec RAQUOT
00601     * display-name   =  *(token LWS)/ quoted-string
00602     * token          =  1*(alphanum / "-" / "." / "!" / "%" / "*"
00603     *                     / "_" / "+" / "`" / "'" / "~" )
00604     * quoted-string  =  SWS DQUOTE *(qdtext / quoted-pair ) DQUOTE
00605     * qdtext         =  LWS / %x21 / %x23-5B / %x5D-7E
00606     *                     / UTF8-NONASCII
00607     * quoted-pair    =  "\" (%x00-09 / %x0B-0C / %x0E-7F)
00608     *
00609     * HCOLON         = *WSP ":" SWS
00610     * SWS            = [LWS]
00611     * LWS            = *[*WSP CRLF] 1*WSP
00612     * WSP            = (SP / HTAB)
00613     *
00614     * Deviations from it:
00615     * - following CRLF's in LWS is not done (here at least)
00616     * - ascii NUL is never legal as it terminates the C-string
00617     * - utf8-nonascii is not checked for validity
00618     */
00619    char *orig_output = output;
00620    const char *orig_input = input;
00621 
00622    if (!output || !outputsize) {
00623       /* Bad output parameters.  Should never happen. */
00624       return input;
00625    }
00626 
00627    /* clear any empty characters in the beginning */
00628    input = ast_skip_blanks(input);
00629 
00630    /* make sure the output buffer is initilized */
00631    *orig_output = '\0';
00632 
00633    /* make room for '\0' at the end of the output buffer */
00634    --outputsize;
00635 
00636    /* no data at all or no display name? */
00637    if (!input || *input == '<') {
00638       return input;
00639    }
00640 
00641    /* quoted-string rules */
00642    if (input[0] == '"') {
00643       input++; /* skip the first " */
00644 
00645       for (; *input; ++input) {
00646          if (*input == '"') {  /* end of quoted-string */
00647             break;
00648          } else if (*input == 0x5c) { /* quoted-pair = "\" (%x00-09 / %x0B-0C / %x0E-7F) */
00649             ++input;
00650             if (!*input) {
00651                break;
00652             }
00653             if ((unsigned char) *input > 0x7f || *input == 0xa || *input == 0xd) {
00654                continue;  /* not a valid quoted-pair, so skip it */
00655             }
00656          } else if ((*input != 0x9 && (unsigned char) *input < 0x20)
00657             || *input == 0x7f) {
00658             continue; /* skip this invalid character. */
00659          }
00660 
00661          if (0 < outputsize) {
00662             /* We still have room for the output display-name. */
00663             *output++ = *input;
00664             --outputsize;
00665          }
00666       }
00667 
00668       /* if this is successful, input should be at the ending quote */
00669       if (*input != '"') {
00670          ast_log(LOG_WARNING, "No ending quote for display-name was found\n");
00671          *orig_output = '\0';
00672          return orig_input;
00673       }
00674 
00675       /* make sure input is past the last quote */
00676       ++input;
00677 
00678       /* terminate output */
00679       *output = '\0';
00680    } else {  /* either an addr-spec or tokenLWS-combo */
00681       for (; *input; ++input) {
00682          /* token or WSP (without LWS) */
00683          if ((*input >= '0' && *input <= '9') || (*input >= 'A' && *input <= 'Z')
00684             || (*input >= 'a' && *input <= 'z') || *input == '-' || *input == '.'
00685             || *input == '!' || *input == '%' || *input == '*' || *input == '_'
00686             || *input == '+' || *input == '`' || *input == '\'' || *input == '~'
00687             || *input == 0x9 || *input == ' ') {
00688             if (0 < outputsize) {
00689                /* We still have room for the output display-name. */
00690                *output++ = *input;
00691                --outputsize;
00692             }
00693          } else if (*input == '<') {   /* end of tokenLWS-combo */
00694             /* we could assert that the previous char is LWS, but we don't care */
00695             break;
00696          } else if (*input == ':') {
00697             /* This invalid character which indicates this is addr-spec rather than display-name. */
00698             *orig_output = '\0';
00699             return orig_input;
00700          } else {         /* else, invalid character we can skip. */
00701             continue;    /* skip this character */
00702          }
00703       }
00704 
00705       if (*input != '<') {   /* if we never found the start of addr-spec then this is invalid */
00706          *orig_output = '\0';
00707          return orig_input;
00708       }
00709 
00710       /* terminate output while trimming any trailing whitespace */
00711       do {
00712          *output-- = '\0';
00713       } while (orig_output <= output && (*output == 0x9 || *output == ' '));
00714    }
00715 
00716    return input;
00717 }
00718 
00719 AST_TEST_DEFINE(get_calleridname_test)
00720 {
00721    int res = AST_TEST_PASS;
00722    const char *in1 = " \" quoted-text internal \\\" quote \"<stuff>";
00723    const char *in2 = " token text with no quotes <stuff>";
00724    const char *overflow1 = " \"quoted-text overflow 1234567890123456789012345678901234567890\" <stuff>";
00725    const char *overflow2 = " non-quoted text overflow 1234567890123456789012345678901234567890 <stuff>";
00726    const char *noendquote = " \"quoted-text no end <stuff>";
00727    const char *addrspec = " sip:blah@blah";
00728    const char *no_quotes_no_brackets = "blah@blah";
00729    const char *after_dname;
00730    char dname[40];
00731 
00732    switch (cmd) {
00733    case TEST_INIT:
00734       info->name = "sip_get_calleridname_test";
00735       info->category = "/channels/chan_sip/";
00736       info->summary = "decodes callerid name from sip header";
00737       info->description = "Decodes display-name field of sip header.  Checks for valid output and expected failure cases.";
00738       return AST_TEST_NOT_RUN;
00739    case TEST_EXECUTE:
00740       break;
00741    }
00742 
00743    /* quoted-text with backslash escaped quote */
00744    after_dname = get_calleridname(in1, dname, sizeof(dname));
00745    ast_test_status_update(test, "display-name1: %s\nafter: %s\n", dname, after_dname);
00746    if (strcmp(dname, " quoted-text internal \" quote ")) {
00747       ast_test_status_update(test, "display-name1 test failed\n");
00748       res = AST_TEST_FAIL;
00749    }
00750 
00751    /* token text */
00752    after_dname = get_calleridname(in2, dname, sizeof(dname));
00753    ast_test_status_update(test, "display-name2: %s\nafter: %s\n", dname, after_dname);
00754    if (strcmp(dname, "token text with no quotes")) {
00755       ast_test_status_update(test, "display-name2 test failed\n");
00756       res = AST_TEST_FAIL;
00757    }
00758 
00759    /* quoted-text buffer overflow */
00760    after_dname = get_calleridname(overflow1, dname, sizeof(dname));
00761    ast_test_status_update(test, "overflow display-name1: %s\nafter: %s\n", dname, after_dname);
00762    if (strcmp(dname, "quoted-text overflow 123456789012345678")) {
00763       ast_test_status_update(test, "overflow display-name1 test failed\n");
00764       res = AST_TEST_FAIL;
00765    }
00766 
00767    /* non-quoted-text buffer overflow */
00768    after_dname = get_calleridname(overflow2, dname, sizeof(dname));
00769    ast_test_status_update(test, "overflow display-name2: %s\nafter: %s\n", dname, after_dname);
00770    if (strcmp(dname, "non-quoted text overflow 12345678901234")) {
00771       ast_test_status_update(test, "overflow display-name2 test failed\n");
00772       res = AST_TEST_FAIL;
00773    }
00774 
00775    /* quoted-text buffer with no terminating end quote */
00776    after_dname = get_calleridname(noendquote, dname, sizeof(dname));
00777    ast_test_status_update(test, "noendquote display-name1: %s\nafter: %s\n", dname, after_dname);
00778    if (*dname != '\0' && after_dname != noendquote) {
00779       ast_test_status_update(test, "no end quote for quoted-text display-name failed\n");
00780       res = AST_TEST_FAIL;
00781    }
00782 
00783    /* addr-spec rather than display-name. */
00784    after_dname = get_calleridname(addrspec, dname, sizeof(dname));
00785    ast_test_status_update(test, "addr-spec display-name1: %s\nafter: %s\n", dname, after_dname);
00786    if (*dname != '\0' && after_dname != addrspec) {
00787       ast_test_status_update(test, "detection of addr-spec failed\n");
00788       res = AST_TEST_FAIL;
00789    }
00790 
00791    /* no quotes, no brackets */
00792    after_dname = get_calleridname(no_quotes_no_brackets, dname, sizeof(dname));
00793    ast_test_status_update(test, "no_quotes_no_brackets display-name1: %s\nafter: %s\n", dname, after_dname);
00794    if (*dname != '\0' && after_dname != no_quotes_no_brackets) {
00795       ast_test_status_update(test, "detection of addr-spec failed\n");
00796       res = AST_TEST_FAIL;
00797    }
00798 
00799    return res;
00800 }
00801 
00802 int get_name_and_number(const char *hdr, char **name, char **number)
00803 {
00804    char header[256];
00805    char tmp_name[50];
00806    char *tmp_number = NULL;
00807    char *hostport = NULL;
00808    char *dummy = NULL;
00809 
00810    if (!name || !number || ast_strlen_zero(hdr)) {
00811       return -1;
00812    }
00813 
00814    *number = NULL;
00815    *name = NULL;
00816    ast_copy_string(header, hdr, sizeof(header));
00817 
00818    /* strip the display-name portion off the beginning of the header. */
00819    get_calleridname(header, tmp_name, sizeof(tmp_name));
00820 
00821    /* get uri within < > brackets */
00822    tmp_number = get_in_brackets(header);
00823 
00824    /* parse out the number here */
00825    if (parse_uri(tmp_number, "sip:,sips:", &tmp_number, &dummy, &hostport, NULL) || ast_strlen_zero(tmp_number)) {
00826       ast_log(LOG_ERROR, "can not parse name and number from sip header.\n");
00827       return -1;
00828    }
00829 
00830    /* number is not option, and must be present at this point */
00831    *number = ast_strdup(tmp_number);
00832    ast_uri_decode(*number, ast_uri_sip_user);
00833 
00834    /* name is optional and may not be present at this point */
00835    if (!ast_strlen_zero(tmp_name)) {
00836       *name = ast_strdup(tmp_name);
00837    }
00838 
00839    return 0;
00840 }
00841 
00842 AST_TEST_DEFINE(get_name_and_number_test)
00843 {
00844    int res = AST_TEST_PASS;
00845    char *name = NULL;
00846    char *number = NULL;
00847    const char *in1 = "NAME <sip:NUMBER@place>";
00848    const char *in2 = "\"NA><ME\" <sip:NUMBER@place>";
00849    const char *in3 = "NAME";
00850    const char *in4 = "<sip:NUMBER@place>";
00851    const char *in5 = "This is a screwed up string <sip:LOLCLOWNS<sip:>@place>";
00852 
00853    switch (cmd) {
00854    case TEST_INIT:
00855       info->name = "sip_get_name_and_number_test";
00856       info->category = "/channels/chan_sip/";
00857       info->summary = "Tests getting name and number from sip header";
00858       info->description =
00859             "Runs through various test situations in which a name and "
00860             "and number can be retrieved from a sip header.";
00861       return AST_TEST_NOT_RUN;
00862    case TEST_EXECUTE:
00863       break;
00864    }
00865 
00866    /* Test 1. get name and number */
00867    number = name = NULL;
00868    if ((get_name_and_number(in1, &name, &number)) ||
00869       strcmp(name, "NAME") ||
00870       strcmp(number, "NUMBER")) {
00871 
00872       ast_test_status_update(test, "Test 1, simple get name and number failed.\n");
00873       res = AST_TEST_FAIL;
00874    }
00875    ast_free(name);
00876    ast_free(number);
00877 
00878    /* Test 2. get quoted name and number */
00879    number = name = NULL;
00880    if ((get_name_and_number(in2, &name, &number)) ||
00881       strcmp(name, "NA><ME") ||
00882       strcmp(number, "NUMBER")) {
00883 
00884       ast_test_status_update(test, "Test 2, get quoted name and number failed.\n");
00885       res = AST_TEST_FAIL;
00886    }
00887    ast_free(name);
00888    ast_free(number);
00889 
00890    /* Test 3. name only */
00891    number = name = NULL;
00892    if (!(get_name_and_number(in3, &name, &number))) {
00893 
00894       ast_test_status_update(test, "Test 3, get name only was expected to fail but did not.\n");
00895       res = AST_TEST_FAIL;
00896    }
00897    ast_free(name);
00898    ast_free(number);
00899 
00900    /* Test 4. number only */
00901    number = name = NULL;
00902    if ((get_name_and_number(in4, &name, &number)) ||
00903       !ast_strlen_zero(name) ||
00904       strcmp(number, "NUMBER")) {
00905 
00906       ast_test_status_update(test, "Test 4, get number with no name present failed.\n");
00907       res = AST_TEST_FAIL;
00908    }
00909    ast_free(name);
00910    ast_free(number);
00911 
00912    /* Test 5. malformed string, since number can not be parsed, this should return an error.  */
00913    number = name = NULL;
00914    if (!(get_name_and_number(in5, &name, &number)) ||
00915       !ast_strlen_zero(name) ||
00916       !ast_strlen_zero(number)) {
00917 
00918       ast_test_status_update(test, "Test 5, processing malformed string failed.\n");
00919       res = AST_TEST_FAIL;
00920    }
00921    ast_free(name);
00922    ast_free(number);
00923 
00924    /* Test 6. NULL output parameters */
00925    number = name = NULL;
00926    if (!(get_name_and_number(in5, NULL, NULL))) {
00927 
00928       ast_test_status_update(test, "Test 6, NULL output parameters failed.\n");
00929       res = AST_TEST_FAIL;
00930    }
00931 
00932    /* Test 7. NULL input parameter */
00933    number = name = NULL;
00934    if (!(get_name_and_number(NULL, &name, &number)) ||
00935       !ast_strlen_zero(name) ||
00936       !ast_strlen_zero(number)) {
00937 
00938       ast_test_status_update(test, "Test 7, NULL input parameter failed.\n");
00939       res = AST_TEST_FAIL;
00940    }
00941    ast_free(name);
00942    ast_free(number);
00943 
00944    return res;
00945 }
00946 
00947 int get_in_brackets_full(char *tmp,char **out,char **residue)
00948 {
00949    const char *parse = tmp;
00950    char *first_bracket;
00951    char *second_bracket;
00952 
00953    if (out) {
00954       *out = "";
00955    }
00956    if (residue) {
00957       *residue = "";
00958    }
00959 
00960    if (ast_strlen_zero(tmp)) {
00961       return 1;
00962    }
00963 
00964    /*
00965     * Skip any quoted text until we find the part in brackets.
00966    * On any error give up and return -1
00967    */
00968    while ( (first_bracket = strchr(parse, '<')) ) {
00969       char *first_quote = strchr(parse, '"');
00970       first_bracket++;
00971       if (!first_quote || first_quote >= first_bracket) {
00972          break; /* no need to look at quoted part */
00973       }
00974       /* the bracket is within quotes, so ignore it */
00975       parse = find_closing_quote(first_quote + 1, NULL);
00976       if (!*parse) {
00977          ast_log(LOG_WARNING, "No closing quote found in '%s'\n", tmp);
00978          return  -1;
00979       }
00980       parse++;
00981    }
00982 
00983    /* If no first bracket then still look for a second bracket as some other parsing functions
00984    may overwrite first bracket with NULL when terminating a token based display-name. As this
00985    only affects token based display-names there is no danger of brackets being in quotes */
00986    if (first_bracket) {
00987       parse = first_bracket;
00988    } else {
00989       parse = tmp;
00990    }
00991 
00992    if ((second_bracket = strchr(parse, '>'))) {
00993       *second_bracket++ = '\0';
00994       if (out) {
00995          *out = (char *) parse;
00996       }
00997       if (residue) {
00998          *residue = second_bracket;
00999       }
01000       return 0;
01001    }
01002 
01003    if ((first_bracket)) {
01004       ast_log(LOG_WARNING, "No closing bracket found in '%s'\n", tmp);
01005       return -1;
01006    }
01007 
01008    if (out) {
01009       *out = tmp;
01010    }
01011 
01012    return 1;
01013 }
01014 
01015 char *get_in_brackets(char *tmp)
01016 {
01017    char *out;
01018 
01019    if ((get_in_brackets_full(tmp, &out, NULL))) {
01020       return tmp;
01021    }
01022    return out;
01023 }
01024 
01025 AST_TEST_DEFINE(get_in_brackets_test)
01026 {
01027    int res = AST_TEST_PASS;
01028    char in_brackets[] = "sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah";
01029    char no_name[] = "<sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
01030    char quoted_string[] = "\"I'm a quote stri><ng\" <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
01031    char missing_end_quote[] = "\"I'm a quote string <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
01032    char name_no_quotes[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
01033    char no_end_bracket[] = "name not in quotes <sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah";
01034    char no_name_no_brackets[] = "sip:name@host";
01035    char missing_start_bracket[] = "sip:name:secret@host:port;transport=tcp?headers=testblah&headers2=blahblah>";
01036    char *uri = NULL;
01037 
01038    switch (cmd) {
01039    case TEST_INIT:
01040       info->name = "sip_get_in_brackets_test";
01041       info->category = "/channels/chan_sip/";
01042       info->summary = "Tests getting a sip uri in <> brackets within a sip header.";
01043       info->description =
01044             "Runs through various test situations in which a sip uri "
01045             "in angle brackets needs to be retrieved";
01046       return AST_TEST_NOT_RUN;
01047    case TEST_EXECUTE:
01048       break;
01049    }
01050 
01051    /* Test 1, simple get in brackets */
01052    if (!(uri = get_in_brackets(no_name)) || strcmp(uri, in_brackets)) {
01053       ast_test_status_update(test, "Test 1, simple get in brackets failed. %s\n", uri);
01054       res = AST_TEST_FAIL;
01055    }
01056 
01057    /* Test 2, starts with quoted string */
01058    if (!(uri = get_in_brackets(quoted_string)) || strcmp(uri, in_brackets)) {
01059       ast_test_status_update(test, "Test 2, get in brackets with quoted string in front failed. %s\n", uri);
01060       res = AST_TEST_FAIL;
01061    }
01062 
01063    /* Test 3, missing end quote */
01064    if (!(uri = get_in_brackets(missing_end_quote)) || !strcmp(uri, in_brackets)) {
01065       ast_test_status_update(test, "Test 3, missing end quote failed. %s\n", uri);
01066       res = AST_TEST_FAIL;
01067    }
01068 
01069    /* Test 4, starts with a name not in quotes */
01070    if (!(uri = get_in_brackets(name_no_quotes)) || strcmp(uri, in_brackets)) {
01071       ast_test_status_update(test, "Test 4, passing name not in quotes failed. %s\n", uri);
01072       res = AST_TEST_FAIL;
01073    }
01074 
01075    /* Test 5, no end bracket, should just return everything after the first '<'  */
01076    if (!(uri = get_in_brackets(no_end_bracket)) || !strcmp(uri, in_brackets)) {
01077       ast_test_status_update(test, "Test 5, no end bracket failed. %s\n", uri);
01078       res = AST_TEST_FAIL;
01079    }
01080 
01081    /* Test 6, NULL input  */
01082    if ((uri = get_in_brackets(NULL))) {
01083       ast_test_status_update(test, "Test 6, NULL input failed.\n");
01084       res = AST_TEST_FAIL;
01085    }
01086 
01087    /* Test 7, no name, and no brackets. */
01088    if (!(uri = get_in_brackets(no_name_no_brackets)) || strcmp(uri, "sip:name@host")) {
01089       ast_test_status_update(test, "Test 7 failed. %s\n", uri);
01090       res = AST_TEST_FAIL;
01091    }
01092 
01093    /* Test 8, no start bracket, but with ending bracket. */
01094    if (!(uri = get_in_brackets(missing_start_bracket)) || strcmp(uri, in_brackets)) {
01095       ast_test_status_update(test, "Test 8 failed. %s\n", uri);
01096       res = AST_TEST_FAIL;
01097    }
01098 
01099    return res;
01100 }
01101 
01102 
01103 int parse_name_andor_addr(char *uri, const char *scheme, char **name,
01104            char **user, char **pass, char **hostport,
01105            struct uriparams *params, char **headers,
01106            char **residue)
01107 {
01108    char buf[1024];
01109    char **residue2 = residue;
01110    char *orig_uri = uri;
01111    int ret;
01112 
01113    buf[0] = '\0';
01114    if (name) {
01115       uri = (char *) get_calleridname(uri, buf, sizeof(buf));
01116    }
01117    ret = get_in_brackets_full(uri, &uri, residue);
01118    if (ret == 0) {
01119       /*
01120        * The uri is in brackets so do not treat unknown trailing uri
01121        * parameters as potential message header parameters.
01122        */
01123       if (residue && **residue) {
01124          /* step over the first semicolon as per parse_uri_full residue */
01125          *residue = *residue + 1;
01126       }
01127       residue2 = NULL;
01128    }
01129 
01130    if (name) {
01131       if (buf[0]) {
01132          /*
01133           * There is always room at orig_uri for the display-name because
01134           * at least one character has always been removed.  A '"' or '<'
01135           * has been removed.
01136           */
01137          strcpy(orig_uri, buf);
01138          *name = orig_uri;
01139       } else {
01140          *name = "";
01141       }
01142    }
01143 
01144    return parse_uri_full(uri, scheme, user, pass, hostport, params, headers, residue2);
01145 }
01146 
01147 AST_TEST_DEFINE(parse_name_andor_addr_test)
01148 {
01149    int res = AST_TEST_PASS;
01150    char uri[1024];
01151    char *name, *user, *pass, *hostport, *headers, *residue;
01152    struct uriparams params;
01153 
01154    struct testdata {
01155       char *desc;
01156       char *uri;
01157       char *name;
01158       char *user;
01159       char *pass;
01160       char *hostport;
01161       char *headers;
01162       char *residue;
01163       struct uriparams params;
01164       AST_LIST_ENTRY(testdata) list;
01165    };
01166 
01167    struct testdata *testdataptr;
01168 
01169    static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
01170 
01171    struct testdata td1 = {
01172       .desc = "quotes and brackets",
01173       .uri = "\"name :@ \" <sip:user:secret@host:5060;param=discard;transport=tcp>;tag=tag",
01174       .name =  "name :@ ",
01175       .user = "user",
01176       .pass = "secret",
01177       .hostport = "host:5060",
01178       .headers = "",
01179       .residue = "tag=tag",
01180       .params.transport = "tcp",
01181       .params.lr = 0,
01182       .params.user = ""
01183    };
01184 
01185    struct testdata td2 = {
01186       .desc = "no quotes",
01187       .uri = "givenname familyname <sip:user:secret@host:5060;param=discard;transport=tcp>;expires=3600",
01188       .name = "givenname familyname",
01189       .user = "user",
01190       .pass = "secret",
01191       .hostport = "host:5060",
01192       .headers = "",
01193       .residue = "expires=3600",
01194       .params.transport = "tcp",
01195       .params.lr = 0,
01196       .params.user = ""
01197    };
01198 
01199    struct testdata td3 = {
01200       .desc = "no brackets",
01201       .uri = "sip:user:secret@host:5060;param=discard;transport=tcp;q=1",
01202       .name = "",
01203       .user = "user",
01204       .pass = "secret",
01205       .hostport = "host:5060",
01206       .headers = "",
01207       .residue = "q=1",
01208       .params.transport = "tcp",
01209       .params.lr = 0,
01210       .params.user = ""
01211    };
01212 
01213    struct testdata td4 = {
01214       .desc = "just host",
01215       .uri = "sips:host",
01216       .name = "",
01217       .user = "",
01218       .pass = "",
01219       .hostport = "host",
01220       .headers = "",
01221       .residue = "",
01222       .params.transport = "",
01223       .params.lr = 0,
01224       .params.user = ""
01225    };
01226 
01227 
01228    AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
01229    AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
01230    AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
01231    AST_LIST_INSERT_TAIL(&testdatalist, &td4, list);
01232 
01233 
01234    switch (cmd) {
01235    case TEST_INIT:
01236       info->name = "parse_name_andor_addr_test";
01237       info->category = "/channels/chan_sip/";
01238       info->summary = "tests parsing of name_andor_addr abnf structure";
01239       info->description =
01240          "Tests parsing of abnf name-andor-addr = name-addr / addr-spec "
01241          "Verifies output matches expected behavior.";
01242       return AST_TEST_NOT_RUN;
01243    case TEST_EXECUTE:
01244       break;
01245    }
01246 
01247    AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
01248       name = user = pass = hostport = headers = residue = NULL;
01249       params.transport = params.user = params.method = params.ttl = params.maddr = NULL;
01250       params.lr = 0;
01251       ast_copy_string(uri,testdataptr->uri,sizeof(uri));
01252       if (parse_name_andor_addr(uri, "sip:,sips:",
01253                  &name,
01254                  &user,
01255                  &pass,
01256                  &hostport,
01257                  &params,
01258                  &headers,
01259                  &residue) ||
01260          (name && strcmp(testdataptr->name, name)) ||
01261          (user && strcmp(testdataptr->user, user)) ||
01262          (pass && strcmp(testdataptr->pass, pass)) ||
01263          (hostport && strcmp(testdataptr->hostport, hostport)) ||
01264          (headers && strcmp(testdataptr->headers, headers)) ||
01265          (residue && strcmp(testdataptr->residue, residue)) ||
01266          (strcmp(testdataptr->params.transport,params.transport)) ||
01267          (strcmp(testdataptr->params.user,params.user))
01268          ) {
01269          ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
01270          res = AST_TEST_FAIL;
01271       }
01272    }
01273 
01274    return res;
01275 }
01276 
01277 int get_comma(char *in, char **out)
01278 {
01279    char *c;
01280    char *parse = in;
01281    if (out) {
01282       *out = in;
01283    }
01284 
01285    /* Skip any quoted text */
01286    while (*parse) {
01287       if ((c = strchr(parse, '"'))) {
01288          in = (char *)find_closing_quote((const char *)c + 1, NULL);
01289          if (!*in) {
01290             ast_log(LOG_WARNING, "No closing quote found in '%s'\n", c);
01291             return -1;
01292          } else {
01293             break;
01294          }
01295       } else {
01296          break;
01297       }
01298       parse++;
01299    }
01300    parse = in;
01301 
01302    /* Skip any userinfo components of a uri as they may contain commas */
01303    if ((c = strchr(parse,'@'))) {
01304       parse = c+1;
01305    }
01306    if ((out) && (c = strchr(parse,','))) {
01307       *c++ = '\0';
01308       *out = c;
01309       return 0;
01310    }
01311    return 1;
01312 }
01313 
01314 int parse_contact_header(char *contactheader, struct contactliststruct *contactlist)
01315 {
01316    int res;
01317    int last;
01318    char *comma;
01319    char *residue;
01320    char *param;
01321    char *value;
01322    struct contact *split_contact = NULL;
01323 
01324    if (*contactheader == '*') {
01325       return 1;
01326    }
01327 
01328    split_contact = ast_calloc(1, sizeof(*split_contact));
01329 
01330    AST_LIST_HEAD_SET_NOLOCK(contactlist, split_contact);
01331    while ((last = get_comma(contactheader, &comma)) != -1) {
01332       res = parse_name_andor_addr(contactheader, "sip:,sips:",
01333          &split_contact->name, &split_contact->user,
01334          &split_contact->pass, &split_contact->hostport,
01335          &split_contact->params, &split_contact->headers,
01336          &residue);
01337       if (res == -1) {
01338          return res;
01339       }
01340 
01341       /* parse contact params */
01342       split_contact->expires = split_contact->q = "";
01343 
01344       while ((value = strchr(residue,'='))) {
01345          *value++ = '\0';
01346 
01347          param = residue;
01348          if ((residue = strchr(value,';'))) {
01349             *residue++ = '\0';
01350          } else {
01351             residue = "";
01352          }
01353 
01354          if (!strcmp(param,"expires")) {
01355             split_contact->expires = value;
01356          } else if (!strcmp(param,"q")) {
01357             split_contact->q = value;
01358          }
01359       }
01360 
01361       if (last) {
01362          return 0;
01363       }
01364       contactheader = comma;
01365 
01366       split_contact = ast_calloc(1, sizeof(*split_contact));
01367       AST_LIST_INSERT_TAIL(contactlist, split_contact, list);
01368    }
01369    return last;
01370 }
01371 
01372 AST_TEST_DEFINE(parse_contact_header_test)
01373 {
01374    int res = AST_TEST_PASS;
01375    char contactheader[1024];
01376    int star;
01377    struct contactliststruct contactlist;
01378    struct contactliststruct *contactlistptr=&contactlist;
01379 
01380    struct testdata {
01381       char *desc;
01382       char *contactheader;
01383       int star;
01384       struct contactliststruct *contactlist;
01385 
01386       AST_LIST_ENTRY(testdata) list;
01387    };
01388 
01389    struct testdata *testdataptr;
01390    struct contact *tdcontactptr;
01391    struct contact *contactptr;
01392 
01393    static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
01394    struct contactliststruct contactlist1, contactlist2;
01395 
01396    struct testdata td1 = {
01397       .desc = "single contact",
01398       .contactheader = "\"name :@;?&,\" <sip:user:secret@host:5082;param=discard;transport=tcp>;expires=3600",
01399       .contactlist = &contactlist1,
01400       .star = 0
01401    };
01402    struct contact contact11 = {
01403       .name = "name :@;?&,",
01404       .user = "user",
01405       .pass = "secret",
01406       .hostport = "host:5082",
01407       .params.transport = "tcp",
01408       .params.ttl = "",
01409       .params.lr = 0,
01410       .headers = "",
01411       .expires = "3600",
01412       .q = ""
01413    };
01414 
01415    struct testdata td2 = {
01416       .desc = "multiple contacts",
01417       .contactheader = "sip:,user1,:,secret1,@host1;ttl=7;q=1;expires=3600,sips:host2",
01418       .contactlist = &contactlist2,
01419       .star = 0,
01420    };
01421    struct contact contact21 = {
01422       .name = "",
01423       .user = ",user1,",
01424       .pass = ",secret1,",
01425       .hostport = "host1",
01426       .params.transport = "",
01427       .params.ttl = "7",
01428       .params.lr = 0,
01429       .headers = "",
01430       .expires = "3600",
01431       .q = "1"
01432    };
01433    struct contact contact22 = {
01434       .name = "",
01435       .user = "",
01436       .pass = "",
01437       .hostport = "host2",
01438       .params.transport = "",
01439       .params.ttl = "",
01440       .params.lr = 0,
01441       .headers = "",
01442       .expires = "",
01443       .q = ""
01444    };
01445 
01446    struct testdata td3 = {
01447       .desc = "star - all contacts",
01448       .contactheader = "*",
01449       .star = 1,
01450       .contactlist = NULL
01451    };
01452 
01453    AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &td1);
01454    AST_LIST_INSERT_TAIL(&testdatalist, &td2, list);
01455    AST_LIST_INSERT_TAIL(&testdatalist, &td3, list);
01456 
01457    AST_LIST_HEAD_SET_NOLOCK(&contactlist1, &contact11);
01458 
01459    AST_LIST_HEAD_SET_NOLOCK(&contactlist2, &contact21);
01460    AST_LIST_INSERT_TAIL(&contactlist2, &contact22, list);
01461 
01462 
01463    switch (cmd) {
01464    case TEST_INIT:
01465       info->name = "parse_contact_header_test";
01466       info->category = "/channels/chan_sip/";
01467       info->summary = "tests parsing of sip contact header";
01468       info->description =
01469          "Tests parsing of a contact header including those with multiple contacts "
01470          "Verifies output matches expected behavior.";
01471       return AST_TEST_NOT_RUN;
01472    case TEST_EXECUTE:
01473       break;
01474    }
01475 
01476    AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
01477       ast_copy_string(contactheader,testdataptr->contactheader,sizeof(contactheader));
01478       star = parse_contact_header(contactheader,contactlistptr);
01479       if (testdataptr->star) {
01480          /* expecting star rather than list of contacts */
01481          if (!star) {
01482             ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
01483             res = AST_TEST_FAIL;
01484             break;
01485          }
01486       } else {
01487          contactptr = AST_LIST_FIRST(contactlistptr);
01488          AST_LIST_TRAVERSE(testdataptr->contactlist, tdcontactptr, list) {
01489             if (!contactptr ||
01490                strcmp(tdcontactptr->name, contactptr->name) ||
01491                strcmp(tdcontactptr->user, contactptr->user) ||
01492                strcmp(tdcontactptr->pass, contactptr->pass) ||
01493                strcmp(tdcontactptr->hostport, contactptr->hostport) ||
01494                strcmp(tdcontactptr->headers, contactptr->headers) ||
01495                strcmp(tdcontactptr->expires, contactptr->expires) ||
01496                strcmp(tdcontactptr->q, contactptr->q) ||
01497                strcmp(tdcontactptr->params.transport, contactptr->params.transport) ||
01498                strcmp(tdcontactptr->params.ttl, contactptr->params.ttl) ||
01499                (tdcontactptr->params.lr != contactptr->params.lr)
01500                ) {
01501                ast_test_status_update(test, "Sub-Test: %s,failed.\n", testdataptr->desc);
01502                res = AST_TEST_FAIL;
01503                break;
01504             }
01505 
01506             contactptr = AST_LIST_NEXT(contactptr,list);
01507          }
01508       }
01509    }
01510 
01511    return res;
01512 }
01513 
01514 /*!
01515  * \brief Parse supported header in incoming packet
01516  *
01517  * \details This function parses through the options parameters and
01518  * builds a bit field representing all the SIP options in that field. When an
01519  * item is found that is not supported, it is copied to the unsupported
01520  * out buffer.
01521  *
01522  * \param option list
01523  * \param unsupported out buffer (optional)
01524  * \param unsupported out buffer length (optional)
01525  */
01526 unsigned int parse_sip_options(const char *options, char *unsupported, size_t unsupported_len)
01527 {
01528    char *next, *sep;
01529    char *temp;
01530    int i, found, supported;
01531    unsigned int profile = 0;
01532 
01533    char *out = unsupported;
01534    size_t outlen = unsupported_len;
01535    char *cur_out = out;
01536 
01537    if (out && (outlen > 0)) {
01538       memset(out, 0, outlen);
01539    }
01540 
01541    if (ast_strlen_zero(options) )
01542       return 0;
01543 
01544    temp = ast_strdupa(options);
01545 
01546    ast_debug(3, "Begin: parsing SIP \"Supported: %s\"\n", options);
01547 
01548    for (next = temp; next; next = sep) {
01549       found = FALSE;
01550       supported = FALSE;
01551       if ((sep = strchr(next, ',')) != NULL) {
01552          *sep++ = '\0';
01553       }
01554 
01555       /* trim leading and trailing whitespace */
01556       next = ast_strip(next);
01557 
01558       if (ast_strlen_zero(next)) {
01559          continue; /* if there is a blank argument in there just skip it */
01560       }
01561 
01562       ast_debug(3, "Found SIP option: -%s-\n", next);
01563       for (i = 0; i < ARRAY_LEN(sip_options); i++) {
01564          if (!strcasecmp(next, sip_options[i].text)) {
01565             profile |= sip_options[i].id;
01566             if (sip_options[i].supported == SUPPORTED) {
01567                supported = TRUE;
01568             }
01569             found = TRUE;
01570             ast_debug(3, "Matched SIP option: %s\n", next);
01571             break;
01572          }
01573       }
01574 
01575       /* If option is not supported, add to unsupported out buffer */
01576       if (!supported && out && outlen) {
01577          size_t copylen = strlen(next);
01578          size_t cur_outlen = strlen(out);
01579          /* Check to see if there is enough room to store this option.
01580           * Copy length is string length plus 2 for the ',' and '\0' */
01581          if ((cur_outlen + copylen + 2) < outlen) {
01582             /* if this isn't the first item, add the ',' */
01583             if (cur_outlen) {
01584                *cur_out = ',';
01585                cur_out++;
01586                cur_outlen++;
01587             }
01588             ast_copy_string(cur_out, next, (outlen - cur_outlen));
01589             cur_out += copylen;
01590          }
01591       }
01592 
01593       if (!found) {
01594          profile |= SIP_OPT_UNKNOWN;
01595          if (!strncasecmp(next, "x-", 2))
01596             ast_debug(3, "Found private SIP option, not supported: %s\n", next);
01597          else
01598             ast_debug(3, "Found no match for SIP option: %s (Please file bug report!)\n", next);
01599       }
01600    }
01601 
01602    return profile;
01603 }
01604 
01605 AST_TEST_DEFINE(sip_parse_options_test)
01606 {
01607    int res = AST_TEST_PASS;
01608    char unsupported[64];
01609    unsigned int option_profile = 0;
01610    struct testdata {
01611       char *name;
01612       char *input_options;
01613       char *expected_unsupported;
01614       unsigned int expected_profile;
01615       AST_LIST_ENTRY(testdata) list;
01616    };
01617 
01618    struct testdata *testdataptr;
01619    static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
01620 
01621    struct testdata test1 = {
01622       .name = "test_all_unsupported",
01623       .input_options = "unsupported1,,, ,unsupported2,unsupported3,unsupported4",
01624       .expected_unsupported = "unsupported1,unsupported2,unsupported3,unsupported4",
01625       .expected_profile = SIP_OPT_UNKNOWN,
01626    };
01627    struct testdata test2 = {
01628       .name = "test_all_unsupported_one_supported",
01629       .input_options = "  unsupported1, replaces,   unsupported3  , , , ,unsupported4",
01630       .expected_unsupported = "unsupported1,unsupported3,unsupported4",
01631       .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES
01632    };
01633    struct testdata test3 = {
01634       .name = "test_two_supported_two_unsupported",
01635       .input_options = ",,  timer  ,replaces     ,unsupported3,unsupported4",
01636       .expected_unsupported = "unsupported3,unsupported4",
01637       .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES | SIP_OPT_TIMER,
01638    };
01639 
01640    struct testdata test4 = {
01641       .name = "test_all_supported",
01642       .input_options = "timer,replaces",
01643       .expected_unsupported = "",
01644       .expected_profile = SIP_OPT_REPLACES | SIP_OPT_TIMER,
01645    };
01646 
01647    struct testdata test5 = {
01648       .name = "test_all_supported_redundant",
01649       .input_options = "timer,replaces,timer,replace,timer,replaces",
01650       .expected_unsupported = "",
01651       .expected_profile = SIP_OPT_REPLACES | SIP_OPT_TIMER,
01652    };
01653    struct testdata test6 = {
01654       .name = "test_buffer_overflow",
01655       .input_options = "unsupported1,replaces,timer,unsupported4,unsupported_huge____"
01656       "____________________________________,__________________________________________"
01657       "________________________________________________",
01658       .expected_unsupported = "unsupported1,unsupported4",
01659       .expected_profile = SIP_OPT_UNKNOWN | SIP_OPT_REPLACES | SIP_OPT_TIMER,
01660    };
01661    struct testdata test7 = {
01662       .name = "test_null_input",
01663       .input_options = NULL,
01664       .expected_unsupported = "",
01665       .expected_profile = 0,
01666    };
01667    struct testdata test8 = {
01668       .name = "test_whitespace_input",
01669       .input_options = "         ",
01670       .expected_unsupported = "",
01671       .expected_profile = 0,
01672    };
01673    struct testdata test9 = {
01674       .name = "test_whitespace_plus_option_input",
01675       .input_options = " , , ,timer , ,  , ,        ,    ",
01676       .expected_unsupported = "",
01677       .expected_profile = SIP_OPT_TIMER,
01678    };
01679 
01680    switch (cmd) {
01681    case TEST_INIT:
01682       info->name = "sip_parse_options_test";
01683       info->category = "/channels/chan_sip/";
01684       info->summary = "Tests parsing of sip options";
01685       info->description =
01686                      "Tests parsing of SIP options from supported and required "
01687                      "header fields.  Verifies when unsupported options are encountered "
01688                      "that they are appended to the unsupported out buffer and that the "
01689                      "correct bit field representnig the option profile is returned.";
01690       return AST_TEST_NOT_RUN;
01691    case TEST_EXECUTE:
01692       break;
01693    }
01694 
01695    AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &test1);
01696    AST_LIST_INSERT_TAIL(&testdatalist, &test2, list);
01697    AST_LIST_INSERT_TAIL(&testdatalist, &test3, list);
01698    AST_LIST_INSERT_TAIL(&testdatalist, &test4, list);
01699    AST_LIST_INSERT_TAIL(&testdatalist, &test5, list);
01700    AST_LIST_INSERT_TAIL(&testdatalist, &test6, list);
01701    AST_LIST_INSERT_TAIL(&testdatalist, &test7, list);
01702    AST_LIST_INSERT_TAIL(&testdatalist, &test8, list);
01703    AST_LIST_INSERT_TAIL(&testdatalist, &test9, list);
01704 
01705    /* Test with unsupported char buffer */
01706    AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
01707       option_profile = parse_sip_options(testdataptr->input_options, unsupported, ARRAY_LEN(unsupported));
01708       if (option_profile != testdataptr->expected_profile ||
01709          strcmp(unsupported, testdataptr->expected_unsupported)) {
01710          ast_test_status_update(test, "Test with output buffer \"%s\", expected unsupported: %s actual unsupported:"
01711             "%s expected bit profile: %x actual bit profile: %x\n",
01712             testdataptr->name,
01713             testdataptr->expected_unsupported,
01714             unsupported,
01715             testdataptr->expected_profile,
01716             option_profile);
01717          res = AST_TEST_FAIL;
01718       } else {
01719          ast_test_status_update(test, "\"%s\" passed got expected unsupported: %s and bit profile: %x\n",
01720             testdataptr->name,
01721             unsupported,
01722             option_profile);
01723       }
01724 
01725       option_profile = parse_sip_options(testdataptr->input_options, NULL, 0);
01726       if (option_profile != testdataptr->expected_profile) {
01727          ast_test_status_update(test, "NULL output test \"%s\", expected bit profile: %x actual bit profile: %x\n",
01728             testdataptr->name,
01729             testdataptr->expected_profile,
01730             option_profile);
01731          res = AST_TEST_FAIL;
01732       } else {
01733          ast_test_status_update(test, "\"%s\" with NULL output buf passed, bit profile: %x\n",
01734             testdataptr->name,
01735             option_profile);
01736       }
01737    }
01738 
01739    return res;
01740 }
01741 
01742 /*! \brief helper routine for sip_uri_cmp to compare URI parameters
01743  *
01744  * This takes the parameters from two SIP URIs and determines
01745  * if the URIs match. The rules for parameters *suck*. Here's a breakdown
01746  * 1. If a parameter appears in both URIs, then they must have the same value
01747  *    in order for the URIs to match
01748  * 2. If one URI has a user, maddr, ttl, or method parameter, then the other
01749  *    URI must also have that parameter and must have the same value
01750  *    in order for the URIs to match
01751  * 3. All other headers appearing in only one URI are not considered when
01752  *    determining if URIs match
01753  *
01754  * \param input1 Parameters from URI 1
01755  * \param input2 Parameters from URI 2
01756  * \retval 0 URIs' parameters match
01757  * \retval nonzero URIs' parameters do not match
01758  */
01759 static int sip_uri_params_cmp(const char *input1, const char *input2)
01760 {
01761    char *params1 = NULL;
01762    char *params2 = NULL;
01763    char *pos1;
01764    char *pos2;
01765    int zerolength1 = 0;
01766    int zerolength2 = 0;
01767    int maddrmatch = 0;
01768    int ttlmatch = 0;
01769    int usermatch = 0;
01770    int methodmatch = 0;
01771 
01772    if (ast_strlen_zero(input1)) {
01773       zerolength1 = 1;
01774    } else {
01775       params1 = ast_strdupa(input1);
01776    }
01777    if (ast_strlen_zero(input2)) {
01778       zerolength2 = 1;
01779    } else {
01780       params2 = ast_strdupa(input2);
01781    }
01782 
01783    /* Quick optimization. If both params are zero-length, then
01784     * they match
01785     */
01786    if (zerolength1 && zerolength2) {
01787       return 0;
01788    }
01789 
01790    for (pos1 = strsep(&params1, ";"); pos1; pos1 = strsep(&params1, ";")) {
01791       char *value1 = pos1;
01792       char *name1 = strsep(&value1, "=");
01793       char *params2dup = NULL;
01794       int matched = 0;
01795       if (!value1) {
01796          value1 = "";
01797       }
01798       /* Checkpoint reached. We have the name and value parsed for param1
01799        * We have to duplicate params2 each time through this loop
01800        * or else the inner loop below will not work properly.
01801        */
01802       if (!zerolength2) {
01803          params2dup = ast_strdupa(params2);
01804       }
01805       for (pos2 = strsep(&params2dup, ";"); pos2; pos2 = strsep(&params2dup, ";")) {
01806          char *name2 = pos2;
01807          char *value2 = strchr(pos2, '=');
01808          if (!value2) {
01809             value2 = "";
01810          } else {
01811             *value2++ = '\0';
01812          }
01813          if (!strcasecmp(name1, name2)) {
01814             if (strcasecmp(value1, value2)) {
01815                goto fail;
01816             } else {
01817                matched = 1;
01818                break;
01819             }
01820          }
01821       }
01822       /* Check to see if the parameter is one of the 'must-match' parameters */
01823       if (!strcasecmp(name1, "maddr")) {
01824          if (matched) {
01825             maddrmatch = 1;
01826          } else {
01827             goto fail;
01828          }
01829       } else if (!strcasecmp(name1, "ttl")) {
01830          if (matched) {
01831             ttlmatch = 1;
01832          } else {
01833             goto fail;
01834          }
01835       } else if (!strcasecmp(name1, "user")) {
01836          if (matched) {
01837             usermatch = 1;
01838          } else {
01839             goto fail;
01840          }
01841       } else if (!strcasecmp(name1, "method")) {
01842          if (matched) {
01843             methodmatch = 1;
01844          } else {
01845             goto fail;
01846          }
01847       }
01848    }
01849 
01850    /* We've made it out of that horrible O(m*n) construct and there are no
01851     * failures yet. We're not done yet, though, because params2 could have
01852     * an maddr, ttl, user, or method header and params1 did not.
01853     */
01854    for (pos2 = strsep(&params2, ";"); pos2; pos2 = strsep(&params2, ";")) {
01855       char *value2 = pos2;
01856       char *name2 = strsep(&value2, "=");
01857       if (!value2) {
01858          value2 = "";
01859       }
01860       if ((!strcasecmp(name2, "maddr") && !maddrmatch) ||
01861             (!strcasecmp(name2, "ttl") && !ttlmatch) ||
01862             (!strcasecmp(name2, "user") && !usermatch) ||
01863             (!strcasecmp(name2, "method") && !methodmatch)) {
01864          goto fail;
01865       }
01866    }
01867    return 0;
01868 
01869 fail:
01870    return 1;
01871 }
01872 
01873 /*! \brief helper routine for sip_uri_cmp to compare URI headers
01874  *
01875  * This takes the headers from two SIP URIs and determines
01876  * if the URIs match. The rules for headers is simple. If a header
01877  * appears in one URI, then it must also appear in the other URI. The
01878  * order in which the headers appear does not matter.
01879  *
01880  * \param input1 Headers from URI 1
01881  * \param input2 Headers from URI 2
01882  * \retval 0 URI headers match
01883  * \retval nonzero URI headers do not match
01884  */
01885 static int sip_uri_headers_cmp(const char *input1, const char *input2)
01886 {
01887    char *headers1 = NULL;
01888    char *headers2 = NULL;
01889    int zerolength1 = 0;
01890    int zerolength2 = 0;
01891    int different = 0;
01892    char *header1;
01893 
01894    if (ast_strlen_zero(input1)) {
01895       zerolength1 = 1;
01896    } else {
01897       headers1 = ast_strdupa(input1);
01898    }
01899 
01900    if (ast_strlen_zero(input2)) {
01901       zerolength2 = 1;
01902    } else {
01903       headers2 = ast_strdupa(input2);
01904    }
01905 
01906    /* If one URI contains no headers and the other
01907     * does, then they cannot possibly match
01908     */
01909    if (zerolength1 != zerolength2) {
01910       return 1;
01911    }
01912 
01913    if (zerolength1 && zerolength2)
01914       return 0;
01915 
01916    /* At this point, we can definitively state that both inputs are
01917     * not zero-length. First, one more optimization. If the length
01918     * of the headers is not equal, then we definitely have no match
01919     */
01920    if (strlen(headers1) != strlen(headers2)) {
01921       return 1;
01922    }
01923 
01924    for (header1 = strsep(&headers1, "&"); header1; header1 = strsep(&headers1, "&")) {
01925       if (!strcasestr(headers2, header1)) {
01926          different = 1;
01927          break;
01928       }
01929    }
01930 
01931    return different;
01932 }
01933 
01934 /*!
01935  * \brief Compare domain sections of SIP URIs
01936  *
01937  * For hostnames, a case insensitive string comparison is
01938  * used. For IP addresses, a binary comparison is used. This
01939  * is mainly because IPv6 addresses have many ways of writing
01940  * the same address.
01941  *
01942  * For specifics about IP address comparison, see the following
01943  * document: http://tools.ietf.org/html/draft-ietf-sip-ipv6-abnf-fix-05
01944  *
01945  * \param host1 The domain from the first URI
01946  * \param host2 THe domain from the second URI
01947  * \retval 0 The domains match
01948  * \retval nonzero The domains do not match
01949  */
01950 static int sip_uri_domain_cmp(const char *host1, const char *host2)
01951 {
01952    struct ast_sockaddr addr1;
01953    struct ast_sockaddr addr2;
01954    int addr1_parsed;
01955    int addr2_parsed;
01956 
01957    addr1_parsed = ast_sockaddr_parse(&addr1, host1, 0);
01958    addr2_parsed = ast_sockaddr_parse(&addr2, host2, 0);
01959 
01960    if (addr1_parsed != addr2_parsed) {
01961       /* One domain was an IP address and the other had
01962        * a host name. FAIL!
01963        */
01964       return 1;
01965    }
01966 
01967    /* Both are host names. A string comparison will work
01968     * perfectly here. Specifying the "C" locale ensures that
01969     * The LC_CTYPE conventions use those defined in ANSI C,
01970     * i.e. ASCII.
01971     */
01972    if (!addr1_parsed) {
01973 #ifdef HAVE_XLOCALE_H
01974       if(!c_locale) {
01975          return strcasecmp(host1, host2);
01976       } else {
01977          return strcasecmp_l(host1, host2, c_locale);
01978       }
01979 #else
01980       return strcasecmp(host1, host2);
01981 #endif
01982    }
01983 
01984    /* Both contain IP addresses */
01985    return ast_sockaddr_cmp(&addr1, &addr2);
01986 }
01987 
01988 int sip_uri_cmp(const char *input1, const char *input2)
01989 {
01990    char *uri1;
01991    char *uri2;
01992    char *uri_scheme1;
01993    char *uri_scheme2;
01994    char *host1;
01995    char *host2;
01996    char *params1;
01997    char *params2;
01998    char *headers1;
01999    char *headers2;
02000 
02001    /* XXX It would be really nice if we could just use parse_uri_full() here
02002     * to separate the components of the URI, but unfortunately it is written
02003     * in a way that can cause URI parameters to be discarded.
02004     */
02005 
02006    if (!input1 || !input2) {
02007       return 1;
02008    }
02009 
02010    uri1 = ast_strdupa(input1);
02011    uri2 = ast_strdupa(input2);
02012 
02013    ast_uri_decode(uri1, ast_uri_sip_user);
02014    ast_uri_decode(uri2, ast_uri_sip_user);
02015 
02016    uri_scheme1 = strsep(&uri1, ":");
02017    uri_scheme2 = strsep(&uri2, ":");
02018 
02019    if (strcmp(uri_scheme1, uri_scheme2)) {
02020       return 1;
02021    }
02022 
02023    /* This function is tailored for SIP and SIPS URIs. There's no
02024     * need to check uri_scheme2 since we have determined uri_scheme1
02025     * and uri_scheme2 are equivalent already.
02026     */
02027    if (strcmp(uri_scheme1, "sip") && strcmp(uri_scheme1, "sips")) {
02028       return 1;
02029    }
02030 
02031    if (ast_strlen_zero(uri1) || ast_strlen_zero(uri2)) {
02032       return 1;
02033    }
02034 
02035    if ((host1 = strchr(uri1, '@'))) {
02036       *host1++ = '\0';
02037    }
02038    if ((host2 = strchr(uri2, '@'))) {
02039       *host2++ = '\0';
02040    }
02041 
02042    /* Check for mismatched username and passwords. This is the
02043     * only case-sensitive comparison of a SIP URI
02044     */
02045    if ((host1 && !host2) ||
02046          (host2 && !host1) ||
02047          (host1 && host2 && strcmp(uri1, uri2))) {
02048       return 1;
02049    }
02050 
02051    if (!host1) {
02052       host1 = uri1;
02053    }
02054    if (!host2) {
02055       host2 = uri2;
02056    }
02057 
02058    /* Strip off the parameters and headers so we can compare
02059     * host and port
02060     */
02061 
02062    if ((params1 = strchr(host1, ';'))) {
02063       *params1++ = '\0';
02064    }
02065    if ((params2 = strchr(host2, ';'))) {
02066       *params2++ = '\0';
02067    }
02068 
02069    /* Headers come after parameters, but there may be headers without
02070     * parameters, thus the S_OR
02071     */
02072    if ((headers1 = strchr(S_OR(params1, host1), '?'))) {
02073       *headers1++ = '\0';
02074    }
02075    if ((headers2 = strchr(S_OR(params2, host2), '?'))) {
02076       *headers2++ = '\0';
02077    }
02078 
02079    if (sip_uri_domain_cmp(host1, host2)) {
02080       return 1;
02081    }
02082 
02083    /* Headers have easier rules to follow, so do those first */
02084    if (sip_uri_headers_cmp(headers1, headers2)) {
02085       return 1;
02086    }
02087 
02088    /* And now the parameters. Ugh */
02089    return sip_uri_params_cmp(params1, params2);
02090 }
02091 
02092 #define URI_CMP_MATCH 0
02093 #define URI_CMP_NOMATCH 1
02094 
02095 AST_TEST_DEFINE(sip_uri_cmp_test)
02096 {
02097    static const struct {
02098       const char *uri1;
02099       const char *uri2;
02100       int expected_result;
02101    } uri_cmp_tests [] = {
02102       /* These are identical, so they match */
02103       { "sip:bob@example.com", "sip:bob@example.com", URI_CMP_MATCH },
02104       /* Different usernames. No match */
02105       { "sip:alice@example.com", "sip:bob@example.com", URI_CMP_NOMATCH },
02106       /* Different hosts. No match */
02107       { "sip:bob@example.com", "sip:bob@examplez.com", URI_CMP_NOMATCH },
02108       /* Now start using IP addresses. Identical, so they match */
02109       { "sip:bob@1.2.3.4", "sip:bob@1.2.3.4", URI_CMP_MATCH },
02110       /* Two identical IPv4 addresses represented differently. Match */
02111       { "sip:bob@1.2.3.4", "sip:bob@001.002.003.004", URI_CMP_MATCH },
02112       /* Logically equivalent IPv4 Address and hostname. No Match */
02113       { "sip:bob@127.0.0.1", "sip:bob@localhost", URI_CMP_NOMATCH },
02114       /* Logically equivalent IPv6 address and hostname. No Match */
02115       { "sip:bob@[::1]", "sip:bob@localhost", URI_CMP_NOMATCH },
02116       /* Try an IPv6 one as well */
02117       { "sip:bob@[2001:db8::1234]", "sip:bob@[2001:db8::1234]", URI_CMP_MATCH },
02118       /* Two identical IPv6 addresses represented differently. Match */
02119       { "sip:bob@[2001:db8::1234]", "sip:bob@[2001:0db8::1234]", URI_CMP_MATCH },
02120       /* Different ports. No match */
02121       { "sip:bob@1.2.3.4:5060", "sip:bob@1.2.3.4:5061", URI_CMP_NOMATCH },
02122       /* Same port logically, but only one address specifies it. No match */
02123       { "sip:bob@1.2.3.4:5060", "sip:bob@1.2.3.4", URI_CMP_NOMATCH },
02124       /* And for safety, try with IPv6 */
02125       { "sip:bob@[2001:db8:1234]:5060", "sip:bob@[2001:db8:1234]", URI_CMP_NOMATCH },
02126       /* User comparison is case sensitive. No match */
02127       { "sip:bob@example.com", "sip:BOB@example.com", URI_CMP_NOMATCH },
02128       /* Host comparison is case insensitive. Match */
02129       { "sip:bob@example.com", "sip:bob@EXAMPLE.COM", URI_CMP_MATCH },
02130       /* Add headers to the URI. Identical, so they match */
02131       { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com?header1=value1&header2=value2", URI_CMP_MATCH },
02132       /* Headers in URI 1 are not in URI 2. No Match */
02133       { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com", URI_CMP_NOMATCH },
02134       /* Header present in both URIs does not have matching values. No match */
02135       { "sip:bob@example.com?header1=value1&header2=value2", "sip:bob@example.com?header1=value1&header2=value3", URI_CMP_NOMATCH },
02136       /* Add parameters to the URI. Identical so they match */
02137       { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com;param1=value1;param2=value2", URI_CMP_MATCH },
02138       /* Same parameters in both URIs but appear in different order. Match */
02139       { "sip:bob@example.com;param2=value2;param1=value1", "sip:bob@example.com;param1=value1;param2=value2", URI_CMP_MATCH },
02140       /* params in URI 1 are not in URI 2. Match */
02141       { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com", URI_CMP_MATCH },
02142       /* param present in both URIs does not have matching values. No match */
02143       { "sip:bob@example.com;param1=value1;param2=value2", "sip:bob@example.com;param1=value1;param2=value3", URI_CMP_NOMATCH },
02144       /* URI 1 has a maddr param but URI 2 does not. No match */
02145       { "sip:bob@example.com;param1=value1;maddr=192.168.0.1", "sip:bob@example.com;param1=value1", URI_CMP_NOMATCH },
02146       /* URI 1 and URI 2 both have identical maddr params. Match */
02147       { "sip:bob@example.com;param1=value1;maddr=192.168.0.1", "sip:bob@example.com;param1=value1;maddr=192.168.0.1", URI_CMP_MATCH },
02148       /* URI 1 is a SIPS URI and URI 2 is a SIP URI. No Match */
02149       { "sips:bob@example.com", "sip:bob@example.com", URI_CMP_NOMATCH },
02150       /* No URI schemes. No match */
02151       { "bob@example.com", "bob@example.com", URI_CMP_NOMATCH },
02152       /* Crashiness tests. Just an address scheme. No match */
02153       { "sip", "sips", URI_CMP_NOMATCH },
02154       /* Still just an address scheme. Even though they're the same, No match */
02155       { "sip", "sip", URI_CMP_NOMATCH },
02156       /* Empty strings. No match */
02157       { "", "", URI_CMP_NOMATCH },
02158       /* An empty string and a NULL. No match */
02159       { "", NULL, URI_CMP_NOMATCH },
02160    };
02161    int i;
02162    int test_res = AST_TEST_PASS;
02163    switch (cmd) {
02164    case TEST_INIT:
02165       info->name = "sip_uri_cmp_test";
02166       info->category = "/channels/chan_sip/";
02167       info->summary = "Tests comparison of SIP URIs";
02168       info->description = "Several would-be tricky URI comparisons are performed";
02169       return AST_TEST_NOT_RUN;
02170    case TEST_EXECUTE:
02171       break;
02172    }
02173 
02174    for (i = 0; i < ARRAY_LEN(uri_cmp_tests); ++i) {
02175       int cmp_res1;
02176       int cmp_res2;
02177       if ((cmp_res1 = sip_uri_cmp(uri_cmp_tests[i].uri1, uri_cmp_tests[i].uri2))) {
02178          /* URI comparison may return -1 or +1 depending on the failure. Standardize
02179           * the return value to be URI_CMP_NOMATCH on any failure
02180           */
02181          cmp_res1 = URI_CMP_NOMATCH;
02182       }
02183       if (cmp_res1 != uri_cmp_tests[i].expected_result) {
02184          ast_test_status_update(test, "Unexpected comparison result for URIs %s and %s. "
02185                "Expected %s but got %s\n", uri_cmp_tests[i].uri1, uri_cmp_tests[i].uri2,
02186                uri_cmp_tests[i].expected_result == URI_CMP_MATCH ? "Match" : "No Match",
02187                cmp_res1 == URI_CMP_MATCH ? "Match" : "No Match");
02188          test_res = AST_TEST_FAIL;
02189       }
02190 
02191       /* All URI comparisons are commutative, so for the sake of being thorough, we'll
02192        * rerun the comparison with the parameters reversed
02193        */
02194       if ((cmp_res2 = sip_uri_cmp(uri_cmp_tests[i].uri2, uri_cmp_tests[i].uri1))) {
02195          /* URI comparison may return -1 or +1 depending on the failure. Standardize
02196           * the return value to be URI_CMP_NOMATCH on any failure
02197           */
02198          cmp_res2 = URI_CMP_NOMATCH;
02199       }
02200       if (cmp_res2 != uri_cmp_tests[i].expected_result) {
02201          ast_test_status_update(test, "Unexpected comparison result for URIs %s and %s. "
02202                "Expected %s but got %s\n", uri_cmp_tests[i].uri2, uri_cmp_tests[i].uri1,
02203                uri_cmp_tests[i].expected_result == URI_CMP_MATCH ? "Match" : "No Match",
02204                cmp_res2 == URI_CMP_MATCH ? "Match" : "No Match");
02205          test_res = AST_TEST_FAIL;
02206       }
02207    }
02208 
02209    return test_res;
02210 }
02211 
02212 void free_via(struct sip_via *v)
02213 {
02214    if (!v) {
02215       return;
02216    }
02217 
02218    ast_free(v->via);
02219    ast_free(v);
02220 }
02221 
02222 struct sip_via *parse_via(const char *header)
02223 {
02224    struct sip_via *v = ast_calloc(1, sizeof(*v));
02225    char *via, *parm;
02226 
02227    if (!v) {
02228       return NULL;
02229    }
02230 
02231    v->via = ast_strdup(header);
02232    v->ttl = 1;
02233 
02234    via = v->via;
02235 
02236    if (ast_strlen_zero(via)) {
02237       ast_log(LOG_ERROR, "received request without a Via header\n");
02238       free_via(v);
02239       return NULL;
02240    }
02241 
02242    /* seperate the first via-parm */
02243    via = strsep(&via, ",");
02244 
02245    /* chop off sent-protocol */
02246    v->protocol = strsep(&via, " \t\r\n");
02247    if (ast_strlen_zero(v->protocol)) {
02248       ast_log(LOG_ERROR, "missing sent-protocol in Via header\n");
02249       free_via(v);
02250       return NULL;
02251    }
02252    v->protocol = ast_skip_blanks(v->protocol);
02253 
02254    if (via) {
02255       via = ast_skip_blanks(via);
02256    }
02257 
02258    /* chop off sent-by */
02259    v->sent_by = strsep(&via, "; \t\r\n");
02260    if (ast_strlen_zero(v->sent_by)) {
02261       ast_log(LOG_ERROR, "missing sent-by in Via header\n");
02262       free_via(v);
02263       return NULL;
02264    }
02265    v->sent_by = ast_skip_blanks(v->sent_by);
02266 
02267    /* store the port, we have to handle ipv6 addresses containing ':'
02268     * characters gracefully */
02269    if (((parm = strchr(v->sent_by, ']')) && *(++parm) == ':') || (parm = strchr(v->sent_by, ':'))) {
02270       char *endptr;
02271 
02272       v->port = strtol(++parm, &endptr, 10);
02273    }
02274 
02275    /* evaluate any via-parms */
02276    while ((parm = strsep(&via, "; \t\r\n"))) {
02277       char *c;
02278       if ((c = strstr(parm, "maddr="))) {
02279          v->maddr = ast_skip_blanks(c + sizeof("maddr=") - 1);
02280       } else if ((c = strstr(parm, "branch="))) {
02281          v->branch = ast_skip_blanks(c + sizeof("branch=") - 1);
02282       } else if ((c = strstr(parm, "ttl="))) {
02283          char *endptr;
02284          c = ast_skip_blanks(c + sizeof("ttl=") - 1);
02285          v->ttl = strtol(c, &endptr, 10);
02286 
02287          /* make sure we got a valid ttl value */
02288          if (c == endptr) {
02289             v->ttl = 1;
02290          }
02291       }
02292    }
02293 
02294    return v;
02295 }
02296 
02297 AST_TEST_DEFINE(parse_via_test)
02298 {
02299    int res = AST_TEST_PASS;
02300    int i = 1;
02301    struct sip_via *via;
02302    struct testdata {
02303       char *in;
02304       char *expected_protocol;
02305       char *expected_branch;
02306       char *expected_sent_by;
02307       char *expected_maddr;
02308       unsigned int expected_port;
02309       unsigned char expected_ttl;
02310       int expected_null;
02311       AST_LIST_ENTRY(testdata) list;
02312    };
02313    struct testdata *testdataptr;
02314    static AST_LIST_HEAD_NOLOCK(testdataliststruct, testdata) testdatalist;
02315    struct testdata t1 = {
02316       .in = "SIP/2.0/UDP host:port;branch=thebranch",
02317       .expected_protocol = "SIP/2.0/UDP",
02318       .expected_sent_by = "host:port",
02319       .expected_branch = "thebranch",
02320    };
02321    struct testdata t2 = {
02322       .in = "SIP/2.0/UDP host:port",
02323       .expected_protocol = "SIP/2.0/UDP",
02324       .expected_sent_by = "host:port",
02325       .expected_branch = "",
02326    };
02327    struct testdata t3 = {
02328       .in = "SIP/2.0/UDP",
02329       .expected_null = 1,
02330    };
02331    struct testdata t4 = {
02332       .in = "BLAH/BLAH/BLAH host:port;branch=",
02333       .expected_protocol = "BLAH/BLAH/BLAH",
02334       .expected_sent_by = "host:port",
02335       .expected_branch = "",
02336    };
02337    struct testdata t5 = {
02338       .in = "SIP/2.0/UDP host:5060;branch=thebranch;maddr=224.0.0.1;ttl=1",
02339       .expected_protocol = "SIP/2.0/UDP",
02340       .expected_sent_by = "host:5060",
02341       .expected_port = 5060,
02342       .expected_branch = "thebranch",
02343       .expected_maddr = "224.0.0.1",
02344       .expected_ttl = 1,
02345    };
02346    struct testdata t6 = {
02347       .in = "SIP/2.0/UDP      host:5060;\n   branch=thebranch;\r\n  maddr=224.0.0.1;   ttl=1",
02348       .expected_protocol = "SIP/2.0/UDP",
02349       .expected_sent_by = "host:5060",
02350       .expected_port = 5060,
02351       .expected_branch = "thebranch",
02352       .expected_maddr = "224.0.0.1",
02353       .expected_ttl = 1,
02354    };
02355    struct testdata t7 = {
02356       .in = "SIP/2.0/UDP [::1]:5060",
02357       .expected_protocol = "SIP/2.0/UDP",
02358       .expected_sent_by = "[::1]:5060",
02359       .expected_port = 5060,
02360       .expected_branch = "",
02361    };
02362    switch (cmd) {
02363    case TEST_INIT:
02364       info->name = "parse_via_test";
02365       info->category = "/channels/chan_sip/";
02366       info->summary = "Tests parsing the Via header";
02367       info->description =
02368             "Runs through various test situations in which various "
02369             " parameters parameter must be extracted from a VIA header";
02370       return AST_TEST_NOT_RUN;
02371    case TEST_EXECUTE:
02372       break;
02373    }
02374 
02375    AST_LIST_HEAD_SET_NOLOCK(&testdatalist, &t1);
02376    AST_LIST_INSERT_TAIL(&testdatalist, &t2, list);
02377    AST_LIST_INSERT_TAIL(&testdatalist, &t3, list);
02378    AST_LIST_INSERT_TAIL(&testdatalist, &t4, list);
02379    AST_LIST_INSERT_TAIL(&testdatalist, &t5, list);
02380    AST_LIST_INSERT_TAIL(&testdatalist, &t6, list);
02381    AST_LIST_INSERT_TAIL(&testdatalist, &t7, list);
02382 
02383 
02384    AST_LIST_TRAVERSE(&testdatalist, testdataptr, list) {
02385       via = parse_via(testdataptr->in);
02386       if (!via) {
02387               if (!testdataptr->expected_null) {
02388             ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
02389                "failed to parse header\n",
02390             i, testdataptr->in);
02391             res = AST_TEST_FAIL;
02392          }
02393          i++;
02394          continue;
02395       }
02396 
02397       if (testdataptr->expected_null) {
02398          ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
02399             "successfully parased invalid via header\n",
02400          i, testdataptr->in);
02401          res = AST_TEST_FAIL;
02402          free_via(via);
02403          i++;
02404          continue;
02405       }
02406 
02407       if ((ast_strlen_zero(via->protocol) && !ast_strlen_zero(testdataptr->expected_protocol))
02408          || (!ast_strlen_zero(via->protocol) && strcmp(via->protocol, testdataptr->expected_protocol))) {
02409 
02410          ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
02411             "parsed protocol = \"%s\"\n"
02412             "expected = \"%s\"\n"
02413             "failed to parse protocol\n",
02414          i, testdataptr->in, via->protocol, testdataptr->expected_protocol);
02415          res = AST_TEST_FAIL;
02416       }
02417 
02418       if ((ast_strlen_zero(via->sent_by) && !ast_strlen_zero(testdataptr->expected_sent_by))
02419          || (!ast_strlen_zero(via->sent_by) && strcmp(via->sent_by, testdataptr->expected_sent_by))) {
02420 
02421          ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
02422             "parsed sent_by = \"%s\"\n"
02423             "expected = \"%s\"\n"
02424             "failed to parse sent-by\n",
02425          i, testdataptr->in, via->sent_by, testdataptr->expected_sent_by);
02426          res = AST_TEST_FAIL;
02427       }
02428 
02429       if (testdataptr->expected_port && testdataptr->expected_port != via->port) {
02430          ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
02431             "parsed port = \"%d\"\n"
02432             "expected = \"%d\"\n"
02433             "failed to parse port\n",
02434          i, testdataptr->in, via->port, testdataptr->expected_port);
02435          res = AST_TEST_FAIL;
02436       }
02437 
02438       if ((ast_strlen_zero(via->branch) && !ast_strlen_zero(testdataptr->expected_branch))
02439          || (!ast_strlen_zero(via->branch) && strcmp(via->branch, testdataptr->expected_branch))) {
02440 
02441          ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
02442             "parsed branch = \"%s\"\n"
02443             "expected = \"%s\"\n"
02444             "failed to parse branch\n",
02445          i, testdataptr->in, via->branch, testdataptr->expected_branch);
02446          res = AST_TEST_FAIL;
02447       }
02448 
02449       if ((ast_strlen_zero(via->maddr) && !ast_strlen_zero(testdataptr->expected_maddr))
02450          || (!ast_strlen_zero(via->maddr) && strcmp(via->maddr, testdataptr->expected_maddr))) {
02451 
02452          ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
02453             "parsed maddr = \"%s\"\n"
02454             "expected = \"%s\"\n"
02455             "failed to parse maddr\n",
02456          i, testdataptr->in, via->maddr, testdataptr->expected_maddr);
02457          res = AST_TEST_FAIL;
02458       }
02459 
02460       if (testdataptr->expected_ttl && testdataptr->expected_ttl != via->ttl) {
02461          ast_test_status_update(test, "TEST#%d FAILED: VIA = \"%s\"\n"
02462             "parsed ttl = \"%d\"\n"
02463             "expected = \"%d\"\n"
02464             "failed to parse ttl\n",
02465          i, testdataptr->in, via->ttl, testdataptr->expected_ttl);
02466          res = AST_TEST_FAIL;
02467       }
02468 
02469       free_via(via);
02470       i++;
02471    }
02472    return res;
02473 }
02474 
02475 void sip_request_parser_register_tests(void)
02476 {
02477    AST_TEST_REGISTER(get_calleridname_test);
02478    AST_TEST_REGISTER(sip_parse_uri_test);
02479    AST_TEST_REGISTER(get_in_brackets_test);
02480    AST_TEST_REGISTER(get_name_and_number_test);
02481    AST_TEST_REGISTER(sip_parse_uri_full_test);
02482    AST_TEST_REGISTER(parse_name_andor_addr_test);
02483    AST_TEST_REGISTER(parse_contact_header_test);
02484    AST_TEST_REGISTER(sip_parse_options_test);
02485    AST_TEST_REGISTER(sip_uri_cmp_test);
02486    AST_TEST_REGISTER(parse_via_test);
02487 }
02488 void sip_request_parser_unregister_tests(void)
02489 {
02490    AST_TEST_UNREGISTER(sip_parse_uri_test);
02491    AST_TEST_UNREGISTER(get_calleridname_test);
02492    AST_TEST_UNREGISTER(get_in_brackets_test);
02493    AST_TEST_UNREGISTER(get_name_and_number_test);
02494    AST_TEST_UNREGISTER(sip_parse_uri_full_test);
02495    AST_TEST_UNREGISTER(parse_name_andor_addr_test);
02496    AST_TEST_UNREGISTER(parse_contact_header_test);
02497    AST_TEST_UNREGISTER(sip_parse_options_test);
02498    AST_TEST_UNREGISTER(sip_uri_cmp_test);
02499    AST_TEST_UNREGISTER(parse_via_test);
02500 }
02501 
02502 int sip_reqresp_parser_init(void)
02503 {
02504 #ifdef HAVE_XLOCALE_H
02505    c_locale = newlocale(LC_CTYPE_MASK, "C", NULL);
02506    if (!c_locale) {
02507       return -1;
02508    }
02509 #endif
02510    return 0;
02511 }
02512 
02513 void sip_reqresp_parser_exit(void)
02514 {
02515 #ifdef HAVE_XLOCALE_H
02516    if (c_locale) {
02517       freelocale(c_locale);
02518       c_locale = NULL;
02519    }
02520 #endif
02521 }

Generated on Sun May 20 06:33:57 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.5.6