Author: mmichelson
Date: Wed Mar  4 12:50:24 2015
New Revision: 432452

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=432452
Log:
Commit progress on some unit tests.

I decided to make this commit now to save some progress made on
unit tests. In addition, this makes a few DNS API changes:

* Structures are defined in dns_internal.h. This is useful for
unit tests that wish to create DNS queries without having to
perform a resolution.
* Some parameters have been constified.


Added:
    team/group/dns/include/asterisk/dns_internal.h   (with props)
    team/group/dns/tests/test_dns.c   (with props)
Modified:
    team/group/dns/include/asterisk/dns_resolver.h
    team/group/dns/main/dns_core.c

Added: team/group/dns/include/asterisk/dns_internal.h
URL: 
http://svnview.digium.com/svn/asterisk/team/group/dns/include/asterisk/dns_internal.h?view=auto&rev=432452
==============================================================================
--- team/group/dns/include/asterisk/dns_internal.h (added)
+++ team/group/dns/include/asterisk/dns_internal.h Wed Mar  4 12:50:24 2015
@@ -1,0 +1,109 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Joshua Colp <jc...@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Internal DNS structure definitions
+ *
+ * \author Joshua Colp <jc...@digium.com>
+ */
+
+/*! \brief Generic DNS record information */
+struct ast_dns_record {
+       /*! \brief Resource record type */
+       int rr_type;
+       /*! \brief Resource record class */
+       int rr_class;
+       /*! \brief Time-to-live of the record */
+       int ttl;
+       /*! \brief The raw DNS record */
+       char *data;
+       /*! \brief The size of the raw DNS record */
+       size_t data_len;
+       /*! \brief Linked list information */
+       AST_LIST_ENTRY(ast_dns_record) list;
+};
+
+/*! \brief An SRV record */
+struct ast_dns_srv_record {
+       /*! \brief Generic DNS record information */
+       struct ast_dns_record generic;
+       /*! \brief The hostname in the SRV record */
+       const char *host;
+       /*! \brief The priority of the SRV record */
+       unsigned short priority;
+       /*! \brief The weight of the SRV record */
+       unsigned short weight;
+       /*! \brief The port in the SRV record */
+       unsigned short port;
+};
+
+/*! \brief A NAPTR record */
+struct ast_dns_naptr_record {
+       /*! \brief Generic DNS record information */
+       struct ast_dns_record generic;
+       /*! \brief The flags from the NAPTR record */
+       const char *flags;
+       /*! \brief The service from the NAPTR record */
+       const char *service;
+       /*! \brief The regular expression from the NAPTR record */
+       const char *regexp;
+       /*! \brief The replacement from the NAPTR record */
+       const char *replacement;
+       /*! \brief The order for the NAPTR record */
+       unsigned short order;
+       /*! \brief The preference of the NAPTR record */
+       unsigned short preference;
+};
+
+/*! \brief The result of a DNS query */
+struct ast_dns_result {
+       /*! \brief Whether the domain was not found */
+       unsigned int nxdomain;
+       /*! \brief Whether the result is secure */
+       unsigned int secure;
+       /*! \brief Whether the result is bogus */
+       unsigned int bogus;
+       /*! \brief The canonical name */
+       const char *canonical;
+       /*! \brief Records returned */
+       AST_LIST_HEAD_NOLOCK(, ast_dns_record) records;
+};
+
+/*! \brief A DNS query */
+struct ast_dns_query {
+       /*! \brief Callback to invoke upon completion */
+       ast_dns_resolve_callback callback;
+       /*! \brief User-specific data */
+       void *user_data;
+       /*! \brief The resolver in use for this query */
+       struct ast_dns_resolver *resolver;
+       /*! \brief Resolver-specific data */
+       void *resolver_data;
+       /*! \brief Result of the DNS query */
+       struct ast_dns_result *result;
+       /*! \brief Timer for recurring resolution */
+       int timer;
+       /*! \brief Resource record type */
+       int rr_type;
+       /*! \brief Resource record class */
+       int rr_class;
+       /*! \brief The name of what is being resolved */
+       char name[0];
+};
+

Propchange: team/group/dns/include/asterisk/dns_internal.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/dns/include/asterisk/dns_internal.h
------------------------------------------------------------------------------
    svn:keywords = 'Author Date Id Revision'

Propchange: team/group/dns/include/asterisk/dns_internal.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/group/dns/include/asterisk/dns_resolver.h
URL: 
http://svnview.digium.com/svn/asterisk/team/group/dns/include/asterisk/dns_resolver.h?view=diff&rev=432452&r1=432451&r2=432452
==============================================================================
--- team/group/dns/include/asterisk/dns_resolver.h (original)
+++ team/group/dns/include/asterisk/dns_resolver.h Wed Mar  4 12:50:24 2015
@@ -86,7 +86,7 @@
  * \retval 0 success
  * \retval -1 failure
  */
-int ast_dns_resolver_add_record(struct ast_dns_query *query, int rr_type, int 
rr_class, int ttl, char *data, size_t size);
+int ast_dns_resolver_add_record(struct ast_dns_query *query, int rr_type, int 
rr_class, int ttl, const char *data, const size_t size);
 
 /*!
  * \brief Mark a DNS query as having been completed

Modified: team/group/dns/main/dns_core.c
URL: 
http://svnview.digium.com/svn/asterisk/team/group/dns/main/dns_core.c?view=diff&rev=432452&r1=432451&r2=432452
==============================================================================
--- team/group/dns/main/dns_core.c (original)
+++ team/group/dns/main/dns_core.c Wed Mar  4 12:50:24 2015
@@ -40,92 +40,9 @@
 #include "asterisk/dns_srv.h"
 #include "asterisk/dns_tlsa.h"
 #include "asterisk/dns_resolver.h"
+#include "asterisk/dns_internal.h"
 
 AST_RWLIST_HEAD_STATIC(resolvers, ast_dns_resolver);
-
-/*! \brief Generic DNS record information */
-struct ast_dns_record {
-       /*! \brief Resource record type */
-       int rr_type;
-       /*! \brief Resource record class */
-       int rr_class;
-       /*! \brief Time-to-live of the record */
-       int ttl;
-       /*! \brief The raw DNS record */
-       char *data;
-       /*! \brief The size of the raw DNS record */
-       size_t data_len;
-       /*! \brief Linked list information */
-       AST_LIST_ENTRY(ast_dns_record) list;
-};
-
-/*! \brief An SRV record */
-struct ast_dns_srv_record {
-       /*! \brief Generic DNS record information */
-       struct ast_dns_record generic;
-       /*! \brief The hostname in the SRV record */
-       const char *host;
-       /*! \brief The priority of the SRV record */
-       unsigned short priority;
-       /*! \brief The weight of the SRV record */
-       unsigned short weight;
-       /*! \brief The port in the SRV record */
-       unsigned short port;
-};
-
-/*! \brief A NAPTR record */
-struct ast_dns_naptr_record {
-       /*! \brief Generic DNS record information */
-       struct ast_dns_record generic;
-       /*! \brief The flags from the NAPTR record */
-       const char *flags;
-       /*! \brief The service from the NAPTR record */
-       const char *service;
-       /*! \brief The regular expression from the NAPTR record */
-       const char *regexp;
-       /*! \brief The replacement from the NAPTR record */
-       const char *replacement;
-       /*! \brief The order for the NAPTR record */
-       unsigned short order;
-       /*! \brief The preference of the NAPTR record */
-       unsigned short preference;
-};
-
-/*! \brief The result of a DNS query */
-struct ast_dns_result {
-       /*! \brief Whether the domain was not found */
-       unsigned int nxdomain;
-       /*! \brief Whether the result is secure */
-       unsigned int secure;
-       /*! \brief Whether the result is bogus */
-       unsigned int bogus;
-       /*! \brief The canonical name */
-       const char *canonical;
-       /*! \brief Records returned */
-       AST_LIST_HEAD_NOLOCK(, ast_dns_record) records;
-};
-
-/*! \brief A DNS query */
-struct ast_dns_query {
-       /*! \brief Callback to invoke upon completion */
-       ast_dns_resolve_callback callback;
-       /*! \brief User-specific data */
-       void *user_data;
-       /*! \brief The resolver in use for this query */
-       struct ast_dns_resolver *resolver;
-       /*! \brief Resolver-specific data */
-       void *resolver_data;
-       /*! \brief Result of the DNS query */
-       struct ast_dns_result *result;
-       /*! \brief Timer for recurring resolution */
-       int timer;
-       /*! \brief Resource record type */
-       int rr_type;
-       /*! \brief Resource record class */
-       int rr_class;
-       /*! \brief The name of what is being resolved */
-       char name[0];
-};
 
 const char *ast_dns_query_get_name(const struct ast_dns_query *query)
 {
@@ -426,7 +343,7 @@
        query->result = NULL;
 }
 
-int ast_dns_resolver_add_record(struct ast_dns_query *query, int rr_type, int 
rr_class, int ttl, char *data, size_t size)
+int ast_dns_resolver_add_record(struct ast_dns_query *query, int rr_type, int 
rr_class, int ttl, const char *data, const size_t size)
 {
        if (!query->result) {
                return -1;
@@ -509,4 +426,4 @@
        AST_RWLIST_UNLOCK(&resolvers);
 
        ast_verb(2, "Unregistered DNS resolver '%s'\n", resolver->name);
-}
+}

Added: team/group/dns/tests/test_dns.c
URL: 
http://svnview.digium.com/svn/asterisk/team/group/dns/tests/test_dns.c?view=auto&rev=432452
==============================================================================
--- team/group/dns/tests/test_dns.c (added)
+++ team/group/dns/tests/test_dns.c Wed Mar  4 12:50:24 2015
@@ -1,0 +1,384 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Mark Michelson
+ *
+ * Mark Michelson <mmichel...@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*** MODULEINFO
+       <depend>TEST_FRAMEWORK</depend>
+       <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+#include <arpa/nameser.h>
+
+#include "asterisk/test.h"
+#include "asterisk/module.h"
+#include "asterisk/dns_core.h"
+#include "asterisk/dns_resolver.h"
+#include "asterisk/dns_internal.h"
+
+/* Used when a stub is needed for certain tests */
+static int stub_resolve(struct ast_dns_query *query)
+{
+       return 0;
+}
+
+/* Used when a stub is needed for certain tests */
+static int stub_cancel(struct ast_dns_query *query)
+{
+       return 0;
+}
+
+AST_TEST_DEFINE(resolver_register_unregister)
+{
+       struct ast_dns_resolver cool_guy_resolver = {
+               .name = "A snake that swallowed a deer",
+               .priority = 19890504,
+               .resolve = stub_resolve,
+               .cancel = stub_cancel,
+       };
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "resolver_register_unregister";
+               info->category = "/main/dns/";
+               info->summary = "Test nominal resolver registration and 
unregistration";
+               info->description =
+                       "The test performs the following steps:\n"
+                       "\t* Register a valid resolver.\n"
+                       "\t* Unregister the resolver.\n"
+                       "If either step fails, the test fails\n";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       if (ast_dns_resolver_register(&cool_guy_resolver)) {
+               ast_test_status_update(test, "Unable to register a perfectly 
good resolver\n");
+               return AST_TEST_FAIL;
+       }
+
+       ast_dns_resolver_unregister(&cool_guy_resolver);
+
+       return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(resolver_register_off_nominal)
+{
+       struct ast_dns_resolver valid = {
+               .name = "valid",
+               .resolve = stub_resolve,
+               .cancel = stub_cancel,
+       };
+
+       struct ast_dns_resolver incomplete1 = {
+               .name = NULL,
+               .resolve = stub_resolve,
+               .cancel = stub_cancel,
+       };
+
+       struct ast_dns_resolver incomplete2 = {
+               .name = "incomplete2",
+               .resolve = NULL,
+               .cancel = stub_cancel,
+       };
+
+       struct ast_dns_resolver incomplete3 = {
+               .name = "incomplete3",
+               .resolve = stub_resolve,
+               .cancel = NULL,
+       };
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "resolver_register_off_nominal";
+               info->category = "/main/dns/";
+               info->summary = "Test off-nominal resolver registration";
+               info->description =
+                       "Test off-nominal resolver registration:\n"
+                       "\t* Register a duplicate resolver\n"
+                       "\t* Register a resolver without a name\n"
+                       "\t* Register a resolver without a resolve() method\n"
+                       "\t* Register a resolver without a cancel() method\n";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       if (ast_dns_resolver_register(&valid)) {
+               ast_test_status_update(test, "Failed to register valid 
resolver\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (!ast_dns_resolver_register(&valid)) {
+               ast_test_status_update(test, "Successfully registered the same 
resolver multiple times\n");
+               return AST_TEST_FAIL;
+       }
+
+       ast_dns_resolver_unregister(&valid);
+
+       if (!ast_dns_resolver_register(NULL)) {
+               ast_test_status_update(test, "Successfully registered a NULL 
resolver\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (!ast_dns_resolver_register(&incomplete1)) {
+               ast_test_status_update(test, "Successfully registered a DNS 
resolver with no name\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (!ast_dns_resolver_register(&incomplete2)) {
+               ast_test_status_update(test, "Successfully registered a DNS 
resolver with no resolve() method\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (!ast_dns_resolver_register(&incomplete3)) {
+               ast_test_status_update(test, "Successfully registered a DNS 
resolver with no cancel() method\n");
+               return AST_TEST_FAIL;
+       }
+
+       return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(resolver_unregister_off_nominal)
+{
+       struct ast_dns_resolver non_existent = {
+               .name = "I do not exist",
+               .priority = 20141004,
+               .resolve = stub_resolve,
+               .cancel = stub_cancel,
+       };
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "resolver_unregister_off_nominal";
+               info->category = "/main/dns/";
+               info->summary = "Test off-nominal DNS resolver unregister";
+               info->description =
+                       "The test attempts the following:\n"
+                       "\t* Unregister a resolver that is not registered.\n"
+                       "\t* Unregister a NULL pointer.\n"
+                       "Because unregistering a resolver does not return an 
indicator of success, the best\n"
+                       "this test can do is verify that nothing blows up when 
this is attempted.\n";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       ast_dns_resolver_unregister(&non_existent);
+       ast_dns_resolver_unregister(NULL);
+
+       return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(resolver_data)
+{
+       struct ast_dns_query some_query;
+
+       struct digits {
+               int fingers;
+               int toes;
+       };
+       
+       struct digits average = {
+               .fingers = 10,
+               .toes = 10,
+       };
+
+       struct digits polydactyl = {
+               .fingers = 12,
+               .toes = 10,
+       };
+
+       struct digits *data_ptr;
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "resolver_data";
+               info->category = "/main/dns/";
+               info->summary = "Test getting and setting data on a DNS 
resolver";
+               /* XXX Better description required */
+               info->description = "Sup dawg";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       memset(&some_query, 0, sizeof(some_query));
+
+       /* Ensure that NULL is retrieved if we haven't set anything on the 
query */
+       data_ptr = ast_dns_resolver_get_data(&some_query);
+       if (data_ptr) {
+               ast_test_status_update(test, "Retrieved non-NULL resolver data 
from query unexpectedly\n");
+               return AST_TEST_FAIL;
+       }
+
+       ast_dns_resolver_set_data(&some_query, &average);
+
+       /* Ensure that data can be set and retrieved */
+       data_ptr = ast_dns_resolver_get_data(&some_query);
+       if (!data_ptr) {
+               ast_test_status_update(test, "Unable to retrieve resolver data 
from DNS query\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (data_ptr->fingers != average.fingers || data_ptr->toes != 
average.toes) {
+               ast_test_status_update(test, "Unexpected resolver data 
retrieved from DNS query\n");
+               return AST_TEST_FAIL;
+       }
+
+       /* Ensure that we can set new resolver data even if there already is 
resolver data on the query */
+       ast_dns_resolver_set_data(&some_query, &polydactyl);
+
+       data_ptr = ast_dns_resolver_get_data(&some_query);
+       if (!data_ptr) {
+               ast_test_status_update(test, "Unable to retrieve resolver data 
from DNS query\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (data_ptr->fingers != polydactyl.fingers || data_ptr->toes != 
polydactyl.toes) {
+               ast_test_status_update(test, "Unexpected resolver data 
retrieved from DNS query\n");
+               return AST_TEST_FAIL;
+       }
+
+       /* Ensure that ast_dns_resolver_completed() removes resolver data from 
the query */
+       ast_dns_resolver_completed(&some_query);
+
+       data_ptr = ast_dns_resolver_get_data(&some_query);
+       if (data_ptr) {
+               ast_test_status_update(test, "Query still has resolver data 
after query completed\n");
+               return AST_TEST_FAIL;
+       }
+
+       return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(resolver_add_record)
+{
+       struct ast_dns_query some_query;
+       /* XXX I know this isn't what an A record looks like, but just trying 
to get something compiling right now */
+       static const char *CLEAN_ADDR = "127.0.0.1";
+
+       switch (cmd) {
+       case TEST_INIT:
+               /* XXX Add details */
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       memset(&some_query, 0, sizeof(some_query));
+
+       /* Nominal Record */
+       if (ast_dns_resolver_add_record(&some_query, ns_t_a, ns_c_in, 12345, 
CLEAN_ADDR, strlen(CLEAN_ADDR))) {
+               ast_test_status_update(test, "Unable to add nominal record to 
query\n");
+               return AST_TEST_FAIL;
+       }
+
+       /* Invalid RR types */
+       if (!ast_dns_resolver_add_record(&some_query, -1, ns_c_in, 12345, 
CLEAN_ADDR, strlen(CLEAN_ADDR))) {
+               ast_test_status_update(test, "Successfully added DNS record 
with negative RR type\n");
+               return AST_TEST_FAIL;
+       }
+       
+       if (!ast_dns_resolver_add_record(&some_query, ns_t_max + 1, ns_c_in, 
12345, CLEAN_ADDR, strlen(CLEAN_ADDR))) {
+               ast_test_status_update(test, "Successfully added DNS record 
with too large RR type\n");
+               return AST_TEST_FAIL;
+       }
+
+       /* Invalid RR classes */
+       if (!ast_dns_resolver_add_record(&some_query, ns_t_a, -1, 12345, 
CLEAN_ADDR, strlen(CLEAN_ADDR))) {
+               ast_test_status_update(test, "Successfully added DNS record 
with negative RR class\n");
+               return AST_TEST_FAIL;
+       }
+
+       if (!ast_dns_resolver_add_record(&some_query, ns_t_a, ns_c_max + 1, 
12345, CLEAN_ADDR, strlen(CLEAN_ADDR))) {
+               ast_test_status_update(test, "Successfully added DNS record 
with too large RR class\n");
+               return AST_TEST_FAIL;
+       }
+
+       /* Invalid TTL */
+       if (!ast_dns_resolver_add_record(&some_query, ns_t_a, ns_c_in, -1, 
CLEAN_ADDR, strlen(CLEAN_ADDR))) {
+               ast_test_status_update(test, "Successfully added DNS record 
with negative TTL\n");
+               return AST_TEST_FAIL;
+       }
+
+       /* No data */
+       if (!ast_dns_resolver_add_record(&some_query, ns_t_a, ns_c_in, 12345, 
NULL, 0)) {
+               ast_test_status_update(test, "Successfully added a DNS record 
with no data\n");
+               return AST_TEST_FAIL;
+       }
+
+       /* Lie about the length */
+       /* XXX I don't know how valid these tests actually are. */
+       if (!ast_dns_resolver_add_record(&some_query, ns_t_a, ns_c_in, 12345, 
CLEAN_ADDR, 0)) {
+               ast_test_status_update(test, "Successfully added a DNS record 
with length zero\n");
+               return AST_TEST_FAIL;
+       }
+       
+       if (!ast_dns_resolver_add_record(&some_query, ns_t_a, ns_c_in, 12345, 
CLEAN_ADDR, strlen(CLEAN_ADDR) * 3)) {
+               ast_test_status_update(test, "Successfully added a DNS record 
with overly-large length\n");
+               return AST_TEST_FAIL;
+       }
+
+       return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(resolver_set_result)
+{
+       struct ast_dns_query some_query;
+
+       switch (cmd) {
+       case TEST_INIT:
+               /* XXX Add details */
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       memset(&some_query, 0, sizeof(some_query));
+
+       /* XXX Not sure what to set for canonical on results */
+
+       return AST_TEST_PASS;
+}
+
+static int unload_module(void)
+{
+       AST_TEST_UNREGISTER(resolver_register_unregister);
+       AST_TEST_UNREGISTER(resolver_register_off_nominal);
+       AST_TEST_UNREGISTER(resolver_unregister_off_nominal);
+       AST_TEST_UNREGISTER(resolver_data);
+       AST_TEST_UNREGISTER(resolver_add_record);
+       AST_TEST_UNREGISTER(resolver_set_result);
+
+       return 0;
+}
+
+static int load_module(void)
+{
+       AST_TEST_REGISTER(resolver_register_unregister);
+       AST_TEST_REGISTER(resolver_register_off_nominal);
+       AST_TEST_REGISTER(resolver_unregister_off_nominal);
+       AST_TEST_REGISTER(resolver_data);
+       AST_TEST_REGISTER(resolver_add_record);
+       AST_TEST_REGISTER(resolver_set_result);
+
+       return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "DNS API Tests");

Propchange: team/group/dns/tests/test_dns.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/dns/tests/test_dns.c
------------------------------------------------------------------------------
    svn:keywords = 'Author Date Id Revision'

Propchange: team/group/dns/tests/test_dns.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain


-- 
_____________________________________________________________________
-- Bandwidth and Colocation Provided by http://www.api-digital.com --

svn-commits mailing list
To UNSUBSCRIBE or update options visit:
   http://lists.digium.com/mailman/listinfo/svn-commits

Reply via email to