Merge "res_pjsip: Refactor endpt_send_transaction (qualify_timeout)"
authorJoshua Colp <jcolp@digium.com>
Fri, 22 May 2015 15:40:54 +0000 (10:40 -0500)
committerGerrit Code Review <gerrit2@gerrit.digium.api>
Fri, 22 May 2015 15:40:54 +0000 (10:40 -0500)
19 files changed:
cdr/cdr_adaptive_odbc.c
cdr/cdr_pgsql.c
cel/cel_odbc.c
include/asterisk/sorcery.h
main/sorcery.c
res/res_mwi_external_ami.c
res/res_pjsip_outbound_registration.c
res/res_sorcery_memory_cache.c [new file with mode: 0644]
rest-api/api-docs/applications.json
rest-api/api-docs/asterisk.json
rest-api/api-docs/bridges.json
rest-api/api-docs/channels.json
rest-api/api-docs/deviceStates.json
rest-api/api-docs/endpoints.json
rest-api/api-docs/events.json
rest-api/api-docs/mailboxes.json
rest-api/api-docs/playbacks.json
rest-api/api-docs/recordings.json
rest-api/api-docs/sounds.json

index e6b60dc..f276959 100644 (file)
@@ -395,6 +395,8 @@ static int odbc_log(struct ast_cdr *cdr)
        char colbuf[1024], *colptr;
        SQLHSTMT stmt = NULL;
        SQLLEN rows = 0;
+       char *separator;
+       int quoted = 0;
 
        if (!sql || !sql2) {
                if (sql)
@@ -412,8 +414,7 @@ static int odbc_log(struct ast_cdr *cdr)
        }
 
        AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
-               int first = 1;
-               int quoted = 0;
+               separator = "";
 
                if (tableptr->quoted_identifiers != '\0'){
                        quoted = 1;
@@ -517,7 +518,7 @@ static int odbc_log(struct ast_cdr *cdr)
                                        LENGTHEN_BUF2(strlen(colptr));
 
                                        /* Encode value, with escaping */
-                                       ast_str_append(&sql2, 0, "%s'", first ? "" : ",");
+                                       ast_str_append(&sql2, 0, "%s'", separator);
                                        for (tmp = colptr; *tmp; tmp++) {
                                                if (*tmp == '\'') {
                                                        ast_str_append(&sql2, 0, "''");
@@ -550,7 +551,7 @@ static int odbc_log(struct ast_cdr *cdr)
                                                }
 
                                                LENGTHEN_BUF2(17);
-                                               ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", first ? "" : ",", year, month, day);
+                                               ast_str_append(&sql2, 0, "%s{ d '%04d-%02d-%02d' }", separator, year, month, day);
                                        }
                                        break;
                                case SQL_TYPE_TIME:
@@ -566,7 +567,7 @@ static int odbc_log(struct ast_cdr *cdr)
                                                }
 
                                                LENGTHEN_BUF2(15);
-                                               ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", first ? "" : ",", hour, minute, second);
+                                               ast_str_append(&sql2, 0, "%s{ t '%02d:%02d:%02d' }", separator, hour, minute, second);
                                        }
                                        break;
                                case SQL_TYPE_TIMESTAMP:
@@ -594,7 +595,7 @@ static int odbc_log(struct ast_cdr *cdr)
                                                }
 
                                                LENGTHEN_BUF2(26);
-                                               ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", first ? "" : ",", year, month, day, hour, minute, second);
+                                               ast_str_append(&sql2, 0, "%s{ ts '%04d-%02d-%02d %02d:%02d:%02d' }", separator, year, month, day, hour, minute, second);
                                        }
                                        break;
                                case SQL_INTEGER:
@@ -608,7 +609,7 @@ static int odbc_log(struct ast_cdr *cdr)
                                                }
 
                                                LENGTHEN_BUF2(12);
-                                               ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+                                               ast_str_append(&sql2, 0, "%s%d", separator, integer);
                                        }
                                        break;
                                case SQL_BIGINT:
@@ -622,7 +623,7 @@ static int odbc_log(struct ast_cdr *cdr)
                                                }
 
                                                LENGTHEN_BUF2(24);
-                                               ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer);
+                                               ast_str_append(&sql2, 0, "%s%lld", separator, integer);
                                        }
                                        break;
                                case SQL_SMALLINT:
@@ -636,7 +637,7 @@ static int odbc_log(struct ast_cdr *cdr)
                                                }
 
                                                LENGTHEN_BUF2(6);
-                                               ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+                                               ast_str_append(&sql2, 0, "%s%d", separator, integer);
                                        }
                                        break;
                                case SQL_TINYINT:
@@ -650,7 +651,7 @@ static int odbc_log(struct ast_cdr *cdr)
                                                }
 
                                                LENGTHEN_BUF2(4);
-                                               ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+                                               ast_str_append(&sql2, 0, "%s%d", separator, integer);
                                        }
                                        break;
                                case SQL_BIT:
@@ -666,7 +667,7 @@ static int odbc_log(struct ast_cdr *cdr)
                                                        integer = 1;
 
                                                LENGTHEN_BUF2(2);
-                                               ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+                                               ast_str_append(&sql2, 0, "%s%d", separator, integer);
                                        }
                                        break;
                                case SQL_NUMERIC:
@@ -698,7 +699,7 @@ static int odbc_log(struct ast_cdr *cdr)
                                                }
 
                                                LENGTHEN_BUF2(entry->decimals);
-                                               ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number);
+                                               ast_str_append(&sql2, 0, "%s%*.*lf", separator, entry->decimals, entry->radix, number);
                                        }
                                        break;
                                case SQL_FLOAT:
@@ -731,7 +732,7 @@ static int odbc_log(struct ast_cdr *cdr)
                                                }
 
                                                LENGTHEN_BUF2(entry->decimals);
-                                               ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number);
+                                               ast_str_append(&sql2, 0, "%s%lf", separator, number);
                                        }
                                        break;
                                default:
@@ -739,11 +740,11 @@ static int odbc_log(struct ast_cdr *cdr)
                                        continue;
                                }
                                if (quoted) {
-                                       ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+                                       ast_str_append(&sql, 0, "%s%s", separator, entry->name);
                                } else {
-                                       ast_str_append(&sql, 0, "%s%c%s%c", first ? "" : ",", tableptr->quoted_identifiers, entry->name, tableptr->quoted_identifiers);
+                                       ast_str_append(&sql, 0, "%s%c%s%c", separator, tableptr->quoted_identifiers, entry->name, tableptr->quoted_identifiers);
                                }
-                               first = 0;
+                               separator = ", ";
                        } else if (entry->filtervalue
                                && ((!entry->negatefiltervalue && entry->filtervalue[0] != '\0')
                                        || (entry->negatefiltervalue && entry->filtervalue[0] == '\0'))) {
index 37bc084..8dc49e1 100644 (file)
@@ -248,7 +248,7 @@ static int pgsql_log(struct ast_cdr *cdr)
                struct columns *cur;
                struct ast_str *sql = ast_str_create(maxsize), *sql2 = ast_str_create(maxsize2);
                char buf[257], escapebuf[513], *value;
-               int first = 1;
+               char *separator = "";
 
                if (!sql || !sql2) {
                        ast_free(sql);
@@ -270,86 +270,86 @@ static int pgsql_log(struct ast_cdr *cdr)
                                if (cur->notnull && !cur->hasdefault) {
                                        /* Field is NOT NULL (but no default), must include it anyway */
                                        LENGTHEN_BUF1(strlen(cur->name) + 2);
-                                       ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
+                                       ast_str_append(&sql, 0, "%s\"%s\"", separator, cur->name);
                                        LENGTHEN_BUF2(3);
-                                       ast_str_append(&sql2, 0, "%s''", first ? "" : ",");
-                                       first = 0;
+                                       ast_str_append(&sql2, 0, "%s''", separator);
+                                       separator = ", ";
                                }
                                continue;
                        }
 
                        LENGTHEN_BUF1(strlen(cur->name) + 2);
-                       ast_str_append(&sql, 0, "%s\"%s\"", first ? "" : ",", cur->name);
+                       ast_str_append(&sql, 0, "%s\"%s\"", separator, cur->name);
 
                        if (strcmp(cur->name, "start") == 0 || strcmp(cur->name, "calldate") == 0) {
                                if (strncmp(cur->type, "int", 3) == 0) {
                                        LENGTHEN_BUF2(13);
-                                       ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->start.tv_sec);
+                                       ast_str_append(&sql2, 0, "%s%ld", separator, (long) cdr->start.tv_sec);
                                } else if (strncmp(cur->type, "float", 5) == 0) {
                                        LENGTHEN_BUF2(31);
-                                       ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
+                                       ast_str_append(&sql2, 0, "%s%f", separator, (double)cdr->start.tv_sec + (double)cdr->start.tv_usec / 1000000.0);
                                } else {
                                        /* char, hopefully */
                                        LENGTHEN_BUF2(31);
                                        ast_localtime(&cdr->start, &tm, tz);
                                        ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
-                                       ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
+                                       ast_str_append(&sql2, 0, "%s%s", separator, buf);
                                }
                        } else if (strcmp(cur->name, "answer") == 0) {
                                if (strncmp(cur->type, "int", 3) == 0) {
                                        LENGTHEN_BUF2(13);
-                                       ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->answer.tv_sec);
+                                       ast_str_append(&sql2, 0, "%s%ld", separator, (long) cdr->answer.tv_sec);
                                } else if (strncmp(cur->type, "float", 5) == 0) {
                                        LENGTHEN_BUF2(31);
-                                       ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
+                                       ast_str_append(&sql2, 0, "%s%f", separator, (double)cdr->answer.tv_sec + (double)cdr->answer.tv_usec / 1000000.0);
                                } else {
                                        /* char, hopefully */
                                        LENGTHEN_BUF2(31);
                                        ast_localtime(&cdr->answer, &tm, tz);
                                        ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
-                                       ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
+                                       ast_str_append(&sql2, 0, "%s%s", separator, buf);
                                }
                        } else if (strcmp(cur->name, "end") == 0) {
                                if (strncmp(cur->type, "int", 3) == 0) {
                                        LENGTHEN_BUF2(13);
-                                       ast_str_append(&sql2, 0, "%s%ld", first ? "" : ",", (long) cdr->end.tv_sec);
+                                       ast_str_append(&sql2, 0, "%s%ld", separator, (long) cdr->end.tv_sec);
                                } else if (strncmp(cur->type, "float", 5) == 0) {
                                        LENGTHEN_BUF2(31);
-                                       ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
+                                       ast_str_append(&sql2, 0, "%s%f", separator, (double)cdr->end.tv_sec + (double)cdr->end.tv_usec / 1000000.0);
                                } else {
                                        /* char, hopefully */
                                        LENGTHEN_BUF2(31);
                                        ast_localtime(&cdr->end, &tm, tz);
                                        ast_strftime(buf, sizeof(buf), DATE_FORMAT, &tm);
-                                       ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", buf);
+                                       ast_str_append(&sql2, 0, "%s%s", separator, buf);
                                }
                        } else if (strcmp(cur->name, "duration") == 0 || strcmp(cur->name, "billsec") == 0) {
                                if (cur->type[0] == 'i') {
                                        /* Get integer, no need to escape anything */
                                        ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
                                        LENGTHEN_BUF2(13);
-                                       ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
+                                       ast_str_append(&sql2, 0, "%s%s", separator, value);
                                } else if (strncmp(cur->type, "float", 5) == 0) {
                                        struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
                                        LENGTHEN_BUF2(31);
-                                       ast_str_append(&sql2, 0, "%s%f", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
+                                       ast_str_append(&sql2, 0, "%s%f", separator, (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
                                } else {
                                        /* Char field, probably */
                                        struct timeval *when = cur->name[0] == 'd' ? &cdr->start : ast_tvzero(cdr->answer) ? &cdr->end : &cdr->answer;
                                        LENGTHEN_BUF2(31);
-                                       ast_str_append(&sql2, 0, "%s'%f'", first ? "" : ",", (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
+                                       ast_str_append(&sql2, 0, "%s'%f'", separator, (double) (ast_tvdiff_us(cdr->end, *when) / 1000000.0));
                                }
                        } else if (strcmp(cur->name, "disposition") == 0 || strcmp(cur->name, "amaflags") == 0) {
                                if (strncmp(cur->type, "int", 3) == 0) {
                                        /* Integer, no need to escape anything */
                                        ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 1);
                                        LENGTHEN_BUF2(13);
-                                       ast_str_append(&sql2, 0, "%s%s", first ? "" : ",", value);
+                                       ast_str_append(&sql2, 0, "%s%s", separator, value);
                                } else {
                                        /* Although this is a char field, there are no special characters in the values for these fields */
                                        ast_cdr_format_var(cdr, cur->name, &value, buf, sizeof(buf), 0);
                                        LENGTHEN_BUF2(31);
-                                       ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", value);
+                                       ast_str_append(&sql2, 0, "%s'%s'", separator, value);
                                }
                        } else {
                                /* Arbitrary field, could be anything */
@@ -358,19 +358,19 @@ static int pgsql_log(struct ast_cdr *cdr)
                                        long long whatever;
                                        if (value && sscanf(value, "%30lld", &whatever) == 1) {
                                                LENGTHEN_BUF2(26);
-                                               ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", whatever);
+                                               ast_str_append(&sql2, 0, "%s%lld", separator, whatever);
                                        } else {
                                                LENGTHEN_BUF2(2);
-                                               ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
+                                               ast_str_append(&sql2, 0, "%s0", separator);
                                        }
                                } else if (strncmp(cur->type, "float", 5) == 0) {
                                        long double whatever;
                                        if (value && sscanf(value, "%30Lf", &whatever) == 1) {
                                                LENGTHEN_BUF2(51);
-                                               ast_str_append(&sql2, 0, "%s%30Lf", first ? "" : ",", whatever);
+                                               ast_str_append(&sql2, 0, "%s%30Lf", separator, whatever);
                                        } else {
                                                LENGTHEN_BUF2(2);
-                                               ast_str_append(&sql2, 0, "%s0", first ? "" : ",");
+                                               ast_str_append(&sql2, 0, "%s0", separator);
                                        }
                                /* XXX Might want to handle dates, times, and other misc fields here XXX */
                                } else {
@@ -379,10 +379,10 @@ static int pgsql_log(struct ast_cdr *cdr)
                                        else
                                                escapebuf[0] = '\0';
                                        LENGTHEN_BUF2(strlen(escapebuf) + 3);
-                                       ast_str_append(&sql2, 0, "%s'%s'", first ? "" : ",", escapebuf);
+                                       ast_str_append(&sql2, 0, "%s'%s'", separator, escapebuf);
                                }
                        }
-                       first = 0;
+                       separator = ", ";
                }
 
                LENGTHEN_BUF1(ast_str_strlen(sql2) + 2);
index 4803444..2d8408b 100644 (file)
@@ -402,7 +402,7 @@ static void odbc_log(struct ast_event *event)
        }
 
        AST_LIST_TRAVERSE(&odbc_tables, tableptr, list) {
-               int first = 1;
+               char *separator = "";
                ast_str_set(&sql, 0, "INSERT INTO %s (", tableptr->table);
                ast_str_set(&sql2, 0, " VALUES (");
 
@@ -536,11 +536,11 @@ static void odbc_log(struct ast_event *event)
                                                }
                                        }
 
-                                       ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+                                       ast_str_append(&sql, 0, "%s%s", separator, entry->name);
                                        LENGTHEN_BUF2(strlen(colptr));
 
                                        /* Encode value, with escaping */
-                                       ast_str_append(&sql2, 0, "%s'", first ? "" : ",");
+                                       ast_str_append(&sql2, 0, "%s'", separator);
                                        for (tmp = colptr; *tmp; tmp++) {
                                                if (*tmp == '\'') {
                                                        ast_str_append(&sql2, 0, "''");
@@ -580,9 +580,9 @@ static void odbc_log(struct ast_event *event)
                                                        }
                                                }
 
-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+                                               ast_str_append(&sql, 0, "%s%s", separator, entry->name);
                                                LENGTHEN_BUF2(17);
-                                               ast_str_append(&sql2, 0, "%s{d '%04d-%02d-%02d'}", first ? "" : ",", year, month, day);
+                                               ast_str_append(&sql2, 0, "%s{d '%04d-%02d-%02d'}", separator, year, month, day);
                                        }
                                        break;
                                case SQL_TYPE_TIME:
@@ -605,9 +605,9 @@ static void odbc_log(struct ast_event *event)
                                                        }
                                                }
 
-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+                                               ast_str_append(&sql, 0, "%s%s", separator, entry->name);
                                                LENGTHEN_BUF2(15);
-                                               ast_str_append(&sql2, 0, "%s{t '%02d:%02d:%02d'}", first ? "" : ",", hour, minute, second);
+                                               ast_str_append(&sql2, 0, "%s{t '%02d:%02d:%02d'}", separator, hour, minute, second);
                                        }
                                        break;
                                case SQL_TYPE_TIMESTAMP:
@@ -646,9 +646,9 @@ static void odbc_log(struct ast_event *event)
                                                        }
                                                }
 
-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+                                               ast_str_append(&sql, 0, "%s%s", separator, entry->name);
                                                LENGTHEN_BUF2(27);
-                                               ast_str_append(&sql2, 0, "%s{ts '%04d-%02d-%02d %02d:%02d:%02d.%d'}", first ? "" : ",", year, month, day, hour, minute, second, fraction);
+                                               ast_str_append(&sql2, 0, "%s{ts '%04d-%02d-%02d %02d:%02d:%02d.%d'}", separator, year, month, day, hour, minute, second, fraction);
                                        }
                                        break;
                                case SQL_INTEGER:
@@ -659,9 +659,9 @@ static void odbc_log(struct ast_event *event)
                                                        continue;
                                                }
 
-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+                                               ast_str_append(&sql, 0, "%s%s", separator, entry->name);
                                                LENGTHEN_BUF2(12);
-                                               ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+                                               ast_str_append(&sql2, 0, "%s%d", separator, integer);
                                        }
                                        break;
                                case SQL_BIGINT:
@@ -673,9 +673,9 @@ static void odbc_log(struct ast_event *event)
                                                        continue;
                                                }
 
-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+                                               ast_str_append(&sql, 0, "%s%s", separator, entry->name);
                                                LENGTHEN_BUF2(24);
-                                               ast_str_append(&sql2, 0, "%s%lld", first ? "" : ",", integer);
+                                               ast_str_append(&sql2, 0, "%s%lld", separator, integer);
                                        }
                                        break;
                                case SQL_SMALLINT:
@@ -686,9 +686,9 @@ static void odbc_log(struct ast_event *event)
                                                        continue;
                                                }
 
-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+                                               ast_str_append(&sql, 0, "%s%s", separator, entry->name);
                                                LENGTHEN_BUF2(7);
-                                               ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+                                               ast_str_append(&sql2, 0, "%s%d", separator, integer);
                                        }
                                        break;
                                case SQL_TINYINT:
@@ -699,9 +699,9 @@ static void odbc_log(struct ast_event *event)
                                                        continue;
                                                }
 
-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+                                               ast_str_append(&sql, 0, "%s%s", separator, entry->name);
                                                LENGTHEN_BUF2(4);
-                                               ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+                                               ast_str_append(&sql2, 0, "%s%d", separator, integer);
                                        }
                                        break;
                                case SQL_BIT:
@@ -714,9 +714,9 @@ static void odbc_log(struct ast_event *event)
                                                if (integer != 0)
                                                        integer = 1;
 
-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+                                               ast_str_append(&sql, 0, "%s%s", separator, entry->name);
                                                LENGTHEN_BUF2(2);
-                                               ast_str_append(&sql2, 0, "%s%d", first ? "" : ",", integer);
+                                               ast_str_append(&sql2, 0, "%s%d", separator, integer);
                                        }
                                        break;
                                case SQL_NUMERIC:
@@ -728,9 +728,9 @@ static void odbc_log(struct ast_event *event)
                                                        continue;
                                                }
 
-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+                                               ast_str_append(&sql, 0, "%s%s", separator, entry->name);
                                                LENGTHEN_BUF2(entry->decimals + 2);
-                                               ast_str_append(&sql2, 0, "%s%*.*lf", first ? "" : ",", entry->decimals, entry->radix, number);
+                                               ast_str_append(&sql2, 0, "%s%*.*lf", separator, entry->decimals, entry->radix, number);
                                        }
                                        break;
                                case SQL_FLOAT:
@@ -743,16 +743,16 @@ static void odbc_log(struct ast_event *event)
                                                        continue;
                                                }
 
-                                               ast_str_append(&sql, 0, "%s%s", first ? "" : ",", entry->name);
+                                               ast_str_append(&sql, 0, "%s%s", separator, entry->name);
                                                LENGTHEN_BUF2(entry->decimals);
-                                               ast_str_append(&sql2, 0, "%s%lf", first ? "" : ",", number);
+                                               ast_str_append(&sql2, 0, "%s%lf", separator, number);
                                        }
                                        break;
                                default:
                                        ast_log(LOG_WARNING, "Column type %d (field '%s:%s:%s') is unsupported at this time.\n", entry->type, tableptr->connection, tableptr->table, entry->name);
                                        continue;
                                }
-                               first = 0;
+                               separator = ", ";
                        }
                }
 
index 9d39ce4..d2dc701 100644 (file)
@@ -1279,6 +1279,15 @@ struct ast_sorcery_object_type *ast_sorcery_get_object_type(const struct ast_sor
 int ast_sorcery_is_object_field_registered(const struct ast_sorcery_object_type *object_type,
                const char *field_name);
 
+/*!
+ * \brief Get the module that has opened the provided sorcery instance.
+ *
+ * \param sorcery The sorcery instance
+ *
+ * \return The module
+ */
+const char *ast_sorcery_get_module(const struct ast_sorcery *sorcery);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
index c779548..732fb62 100644 (file)
@@ -2328,3 +2328,8 @@ int ast_sorcery_is_object_field_registered(const struct ast_sorcery_object_type
        ao2_cleanup(object_field);
        return res;
 }
+
+const char *ast_sorcery_get_module(const struct ast_sorcery *sorcery)
+{
+       return sorcery->module_name;
+}
\ No newline at end of file
index 24562e1..7777214 100644 (file)
@@ -357,9 +357,9 @@ static int load_module(void)
        ast_mwi_external_ref();
 
        res = 0;
-       res |= ast_manager_register_xml_core("MWIGet", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, mwi_mailbox_get);
-       res |= ast_manager_register_xml_core("MWIDelete", EVENT_FLAG_CALL, mwi_mailbox_delete);
-       res |= ast_manager_register_xml_core("MWIUpdate", EVENT_FLAG_CALL, mwi_mailbox_update);
+       res |= ast_manager_register_xml("MWIGet", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, mwi_mailbox_get);
+       res |= ast_manager_register_xml("MWIDelete", EVENT_FLAG_CALL, mwi_mailbox_delete);
+       res |= ast_manager_register_xml("MWIUpdate", EVENT_FLAG_CALL, mwi_mailbox_update);
        if (res) {
                unload_module();
                return AST_MODULE_LOAD_DECLINE;
index a43fca2..7f60acd 100644 (file)
@@ -383,22 +383,27 @@ static int line_identify_relationship(void *obj, void *arg, int flags)
        return !pj_strcmp2(&line->value, state->client_state->line) ? CMP_MATCH | CMP_STOP : 0;
 }
 
+static struct pjsip_param *get_uri_option_line(const void *uri)
+{
+       pjsip_sip_uri *pjuri;
+       static const pj_str_t LINE_STR = { "line", 4 };
+
+       if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) {
+               return NULL;
+       }
+       pjuri = pjsip_uri_get_uri(uri);
+       return pjsip_param_find(&pjuri->other_param, &LINE_STR);
+}
+
 /*! \brief Endpoint identifier which uses the 'line' parameter to establish a relationship to an outgoing registration */
 static struct ast_sip_endpoint *line_identify(pjsip_rx_data *rdata)
 {
-       pjsip_sip_uri *uri;
-       static const pj_str_t LINE_STR = { "line", 4 };
        pjsip_param *line;
        RAII_VAR(struct ao2_container *, states, NULL, ao2_cleanup);
        RAII_VAR(struct sip_outbound_registration_state *, state, NULL, ao2_cleanup);
 
-       if (!PJSIP_URI_SCHEME_IS_SIP(rdata->msg_info.to->uri) && !PJSIP_URI_SCHEME_IS_SIPS(rdata->msg_info.to->uri)) {
-               return NULL;
-       }
-       uri = pjsip_uri_get_uri(rdata->msg_info.to->uri);
-
-       line = pjsip_param_find(&uri->other_param, &LINE_STR);
-       if (!line) {
+       if (!(line = get_uri_option_line(rdata->msg_info.to->uri))
+           && !(line = get_uri_option_line(rdata->msg_info.msg->line.req.uri))) {
                return NULL;
        }
 
diff --git a/res/res_sorcery_memory_cache.c b/res/res_sorcery_memory_cache.c
new file mode 100644 (file)
index 0000000..1290bd9
--- /dev/null
@@ -0,0 +1,1016 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@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 Sorcery Memory Cache Object Wizard
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+/*** MODULEINFO
+       <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_REGISTER_FILE()
+
+#include "asterisk/module.h"
+#include "asterisk/sorcery.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/sched.h"
+#include "asterisk/test.h"
+
+/*! \brief Structure for storing a memory cache */
+struct sorcery_memory_cache {
+       /*! \brief The name of the memory cache */
+       char *name;
+       /*! \brief Objects in the cache */
+       struct ao2_container *objects;
+       /*! \brief The maximum number of objects permitted in the cache, 0 if no limit */
+       unsigned int maximum_objects;
+       /*! \brief The maximum time (in seconds) an object will stay in the cache, 0 if no limit */
+       unsigned int object_lifetime_maximum;
+       /*! \brief The amount of time (in seconds) before an object is marked as stale, 0 if disabled */
+       unsigned int object_lifetime_stale;
+       /*! \brief Whether objects are prefetched from normal storage at load time, 0 if disabled */
+       unsigned int prefetch;
+       /** \brief Whether all objects are expired when the object type is reloaded, 0 if disabled */
+       unsigned int expire_on_reload;
+};
+
+/*! \brief Structure for stored a cached object */
+struct sorcery_memory_cached_object {
+       /*! \brief The cached object */
+       void *object;
+       /*! \brief The time at which the object was created */
+       struct timeval created;
+};
+
+static void *sorcery_memory_cache_open(const char *data);
+static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *data, void *object);
+static void sorcery_memory_cache_load(void *data, const struct ast_sorcery *sorcery, const char *type);
+static void sorcery_memory_cache_reload(void *data, const struct ast_sorcery *sorcery, const char *type);
+static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type,
+       const char *id);
+static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object);
+static void sorcery_memory_cache_close(void *data);
+
+static struct ast_sorcery_wizard memory_cache_object_wizard = {
+       .name = "memory_cache",
+       .open = sorcery_memory_cache_open,
+       .create = sorcery_memory_cache_create,
+       .update = sorcery_memory_cache_create,
+       .delete = sorcery_memory_cache_delete,
+       .load = sorcery_memory_cache_load,
+       .reload = sorcery_memory_cache_reload,
+       .retrieve_id = sorcery_memory_cache_retrieve_id,
+       .close = sorcery_memory_cache_close,
+};
+
+/*! \brief The bucket size for the container of caches */
+#define CACHES_CONTAINER_BUCKET_SIZE 53
+
+/*! \brief The default bucket size for the container of objects in the cache */
+#define CACHE_CONTAINER_BUCKET_SIZE 53
+
+/*! \brief Container of created caches */
+static struct ao2_container *caches;
+
+/*! \brief Scheduler for cache management */
+static struct ast_sched_context *sched;
+
+/*!
+ * \internal
+ * \brief Hashing function for the container holding caches
+ *
+ * \param obj A sorcery memory cache or name of one
+ * \param flags Hashing flags
+ *
+ * \return The hash of the memory cache name
+ */
+static int sorcery_memory_cache_hash(const void *obj, int flags)
+{
+       const struct sorcery_memory_cache *cache = obj;
+       const char *name = obj;
+       int hash;
+
+       switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
+       default:
+       case OBJ_SEARCH_OBJECT:
+               name = cache->name;
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               hash = ast_str_hash(name);
+               break;
+       case OBJ_SEARCH_PARTIAL_KEY:
+               /* Should never happen in hash callback. */
+               ast_assert(0);
+               hash = 0;
+               break;
+       }
+       return hash;
+}
+
+/*!
+ * \internal
+ * \brief Comparison function for the container holding caches
+ *
+ * \param obj A sorcery memory cache
+ * \param arg A sorcery memory cache, or name of one
+ * \param flags Comparison flags
+ *
+ * \retval CMP_MATCH if the name is the same
+ * \retval 0 if the name does not match
+ */
+static int sorcery_memory_cache_cmp(void *obj, void *arg, int flags)
+{
+       const struct sorcery_memory_cache *left = obj;
+       const struct sorcery_memory_cache *right = arg;
+       const char *right_name = arg;
+       int cmp;
+
+       switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
+       default:
+       case OBJ_SEARCH_OBJECT:
+               right_name = right->name;
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               cmp = strcmp(left->name, right_name);
+               break;
+       case OBJ_SEARCH_PARTIAL_KEY:
+               cmp = strncmp(left->name, right_name, strlen(right_name));
+               break;
+       }
+       return cmp ? 0 : CMP_MATCH;
+}
+
+/*!
+ * \internal
+ * \brief Hashing function for the container holding cached objects
+ *
+ * \param obj A cached object or id of one
+ * \param flags Hashing flags
+ *
+ * \return The hash of the cached object id
+ */
+static int sorcery_memory_cached_object_hash(const void *obj, int flags)
+{
+       const struct sorcery_memory_cached_object *cached = obj;
+       const char *name = obj;
+       int hash;
+
+       switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
+       default:
+       case OBJ_SEARCH_OBJECT:
+               name = ast_sorcery_object_get_id(cached->object);
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               hash = ast_str_hash(name);
+               break;
+       case OBJ_SEARCH_PARTIAL_KEY:
+               /* Should never happen in hash callback. */
+               ast_assert(0);
+               hash = 0;
+               break;
+       }
+       return hash;
+}
+
+/*!
+ * \internal
+ * \brief Comparison function for the container holding cached objects
+ *
+ * \param obj A cached object
+ * \param arg A cached object, or id of one
+ * \param flags Comparison flags
+ *
+ * \retval CMP_MATCH if the id is the same
+ * \retval 0 if the id does not match
+ */
+static int sorcery_memory_cached_object_cmp(void *obj, void *arg, int flags)
+{
+       struct sorcery_memory_cached_object *left = obj;
+       struct sorcery_memory_cached_object *right = arg;
+       const char *right_name = arg;
+       int cmp;
+
+       switch (flags & (OBJ_SEARCH_OBJECT | OBJ_SEARCH_KEY | OBJ_SEARCH_PARTIAL_KEY)) {
+       default:
+       case OBJ_SEARCH_OBJECT:
+               right_name = ast_sorcery_object_get_id(right->object);
+               /* Fall through */
+       case OBJ_SEARCH_KEY:
+               cmp = strcmp(ast_sorcery_object_get_id(left->object), right_name);
+               break;
+       case OBJ_SEARCH_PARTIAL_KEY:
+               cmp = strncmp(ast_sorcery_object_get_id(left->object), right_name, strlen(right_name));
+               break;
+       }
+       return cmp ? 0 : CMP_MATCH;
+}
+
+/*!
+ * \internal
+ * \brief Destructor function for a sorcery memory cache
+ *
+ * \param obj A sorcery memory cache
+ */
+static void sorcery_memory_cache_destructor(void *obj)
+{
+       struct sorcery_memory_cache *cache = obj;
+
+       ast_free(cache->name);
+       ao2_cleanup(cache->objects);
+}
+
+/*!
+ * \internal
+ * \brief Destructor function for sorcery memory cached objects
+ *
+ * \param obj A sorcery memory cached object
+ */
+static void sorcery_memory_cached_object_destructor(void *obj)
+{
+       struct sorcery_memory_cached_object *cached = obj;
+
+       ao2_cleanup(cached->object);
+}
+
+/*!
+ * \internal
+ * \brief Callback function to cache an object in a memory cache
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param object The object to cache
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int sorcery_memory_cache_create(const struct ast_sorcery *sorcery, void *data, void *object)
+{
+       struct sorcery_memory_cache *cache = data;
+       struct sorcery_memory_cached_object *cached;
+
+       cached = ao2_alloc_options(sizeof(*cached), sorcery_memory_cached_object_destructor,
+               AO2_ALLOC_OPT_LOCK_NOLOCK);
+       if (!cached) {
+               return -1;
+       }
+       cached->object = ao2_bump(object);
+       cached->created = ast_tvnow();
+
+       /* As there is no guarantee that this won't be called by multiple threads wanting to cache
+        * the same object we remove any old ones, which turns this into a create/update function
+        * in reality. As well since there's no guarantee that the object in the cache is the same
+        * one here we remove any old objects using the object identifier.
+        */
+
+       ao2_wrlock(cache->objects);
+       ao2_find(cache->objects, ast_sorcery_object_get_id(object), OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK | OBJ_NOLOCK);
+       ao2_link_flags(cache->objects, cached, OBJ_NOLOCK);
+       ao2_unlock(cache->objects);
+
+       ao2_ref(cached, -1);
+
+       return 0;
+}
+
+/*!
+ * \internal
+ * \brief Callback function to retrieve an object from a memory cache
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param type The type of the object to retrieve
+ * \param id The id of the object to retrieve
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static void *sorcery_memory_cache_retrieve_id(const struct ast_sorcery *sorcery, void *data, const char *type, const char *id)
+{
+       struct sorcery_memory_cache *cache = data;
+       struct sorcery_memory_cached_object *cached;
+       void *object;
+
+       cached = ao2_find(cache->objects, id, OBJ_SEARCH_KEY);
+       if (!cached) {
+               return NULL;
+       }
+
+       object = ao2_bump(cached->object);
+       ao2_ref(cached, -1);
+
+       return object;
+}
+
+/*!
+ * \internal
+ * \brief Callback function to finish configuring the memory cache and to prefetch objects
+ *
+ * \param data The sorcery memory cache
+ * \param sorcery The sorcery instance
+ * \param type The type of object being loaded
+ */
+static void sorcery_memory_cache_load(void *data, const struct ast_sorcery *sorcery, const char *type)
+{
+       struct sorcery_memory_cache *cache = data;
+
+       /* If no name was explicitly specified generate one given the sorcery instance and object type */
+       if (ast_strlen_zero(cache->name)) {
+               ast_asprintf(&cache->name, "%s/%s", ast_sorcery_get_module(sorcery), type);
+       }
+
+       ao2_link(caches, cache);
+       ast_debug(1, "Memory cache '%s' associated with sorcery instance '%p' of module '%s' with object type '%s'\n",
+               cache->name, sorcery, ast_sorcery_get_module(sorcery), type);
+}
+
+/*!
+ * \internal
+ * \brief Callback function to expire objects from the memory cache on reload (if configured)
+ *
+ * \param data The sorcery memory cache
+ * \param sorcery The sorcery instance
+ * \param type The type of object being reloaded
+ */
+static void sorcery_memory_cache_reload(void *data, const struct ast_sorcery *sorcery, const char *type)
+{
+}
+
+/*!
+ * \internal
+ * \brief Function used to take an unsigned integer based configuration option and parse it
+ *
+ * \param value The string value of the configuration option
+ * \param result The unsigned integer to place the result in
+ *
+ * \retval 0 failure
+ * \retval 1 success
+ */
+static int configuration_parse_unsigned_integer(const char *value, unsigned int *result)
+{
+       if (ast_strlen_zero(value) || !strncmp(value, "-", 1)) {
+               return 0;
+       }
+
+       return sscanf(value, "%30u", result);
+}
+
+/*!
+ * \internal
+ * \brief Callback function to create a new sorcery memory cache using provided configuration
+ *
+ * \param data A stringified configuration for the memory cache
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static void *sorcery_memory_cache_open(const char *data)
+{
+       char *options = ast_strdup(data), *option;
+       RAII_VAR(struct sorcery_memory_cache *, cache, NULL, ao2_cleanup);
+
+       cache = ao2_alloc_options(sizeof(*cache), sorcery_memory_cache_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
+       if (!cache) {
+               return NULL;
+       }
+
+       /* If no configuration options have been provided this memory cache will operate in a default
+        * configuration.
+        */
+       while (!ast_strlen_zero(options) && (option = strsep(&options, ","))) {
+               char *name = strsep(&option, "="), *value = option;
+
+               if (!strcasecmp(name, "name")) {
+                       if (ast_strlen_zero(value)) {
+                               ast_log(LOG_ERROR, "A name must be specified for the memory cache\n");
+                               return NULL;
+                       }
+                       ast_free(cache->name);
+                       cache->name = ast_strdup(value);
+               } else if (!strcasecmp(name, "maximum_objects")) {
+                       if (configuration_parse_unsigned_integer(value, &cache->maximum_objects) != 1) {
+                               ast_log(LOG_ERROR, "Unsupported maximum objects value of '%s' used for memory cache\n",
+                                       value);
+                               return NULL;
+                       }
+               } else if (!strcasecmp(name, "object_lifetime_maximum")) {
+                       if (configuration_parse_unsigned_integer(value, &cache->object_lifetime_maximum) != 1) {
+                               ast_log(LOG_ERROR, "Unsupported object maximum lifetime value of '%s' used for memory cache\n",
+                                       value);
+                               return NULL;
+                       }
+               } else if (!strcasecmp(name, "object_lifetime_stale")) {
+                       if (configuration_parse_unsigned_integer(value, &cache->object_lifetime_stale) != 1) {
+                               ast_log(LOG_ERROR, "Unsupported object stale lifetime value of '%s' used for memory cache\n",
+                                       value);
+                               return NULL;
+                       }
+               } else if (!strcasecmp(name, "prefetch")) {
+                       cache->prefetch = ast_true(value);
+               } else if (!strcasecmp(name, "expire_on_reload")) {
+                       cache->expire_on_reload = ast_true(value);
+               } else {
+                       ast_log(LOG_ERROR, "Unsupported option '%s' used for memory cache\n", name);
+                       return NULL;
+               }
+       }
+
+       cache->objects = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK,
+               cache->maximum_objects ? cache->maximum_objects : CACHE_CONTAINER_BUCKET_SIZE,
+               sorcery_memory_cached_object_hash, sorcery_memory_cached_object_cmp);
+       if (!cache->objects) {
+               ast_log(LOG_ERROR, "Could not create a container to hold cached objects for memory cache\n");
+               return NULL;
+       }
+
+       /* The memory cache is not linked to the caches container until the load callback is invoked.
+        * Linking occurs there so an intelligent cache name can be constructed using the module of
+        * the sorcery instance and the specific object type if no cache name was specified as part
+        * of the configuration.
+        */
+
+       /* This is done as RAII_VAR will drop the reference */
+       return ao2_bump(cache);
+}
+
+/*!
+ * \internal
+ * \brief Callback function to delete an object from a memory cache
+ *
+ * \param sorcery The sorcery instance
+ * \param data The sorcery memory cache
+ * \param object The object to cache
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int sorcery_memory_cache_delete(const struct ast_sorcery *sorcery, void *data, void *object)
+{
+       struct sorcery_memory_cache *cache = data;
+
+       /* There is no guarantee that the object we have cached is the one we will be provided
+        * with in this callback function. As a result of this we remove the cached object based on
+        * the identifier and not the object itself.
+        */
+       ao2_find(cache->objects, ast_sorcery_object_get_id(object), OBJ_SEARCH_KEY | OBJ_NODATA | OBJ_UNLINK);
+
+       return 0;
+}
+
+/*!
+ * \internal
+ * \brief Callback function to terminate a memory cache
+ *
+ * \param data The sorcery memory cache
+ */
+static void sorcery_memory_cache_close(void *data)
+{
+       struct sorcery_memory_cache *cache = data;
+
+       /* This can occur if a cache is created but never loaded */
+       if (!ast_strlen_zero(cache->name)) {
+               ao2_unlink(caches, cache);
+       }
+
+       ao2_ref(cache, -1);
+}
+
+#ifdef TEST_FRAMEWORK
+
+/*! \brief Dummy sorcery object */
+struct test_sorcery_object {
+       SORCERY_OBJECT(details);
+};
+
+/*!
+ * \internal
+ * \brief Allocator for test object
+ *
+ * \param id The identifier for the object
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static void *test_sorcery_object_alloc(const char *id)
+{
+       return ast_sorcery_generic_alloc(sizeof(struct test_sorcery_object), NULL);
+}
+
+/*!
+ * \internal
+ * \brief Allocator for test sorcery instance
+ *
+ * \retval non-NULL success
+ * \retval NULL failure
+ */
+static struct ast_sorcery *alloc_and_initialize_sorcery(void)
+{
+       struct ast_sorcery *sorcery;
+
+       if (!(sorcery = ast_sorcery_open())) {
+               return NULL;
+       }
+
+       if ((ast_sorcery_apply_default(sorcery, "test", "memory", NULL) != AST_SORCERY_APPLY_SUCCESS) ||
+               ast_sorcery_internal_object_register(sorcery, "test", test_sorcery_object_alloc, NULL, NULL)) {
+               ast_sorcery_unref(sorcery);
+               return NULL;
+       }
+
+       return sorcery;
+}
+
+AST_TEST_DEFINE(open_with_valid_options)
+{
+       int res = AST_TEST_PASS;
+       struct sorcery_memory_cache *cache;
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "open_with_valid_options";
+               info->category = "/res/res_sorcery_memory_cache/";
+               info->summary = "Attempt to create sorcery memory caches using valid options";
+               info->description = "This test performs the following:\n"
+                       "\t* Creates a memory cache with default configuration\n"
+                       "\t* Creates a memory cache with a maximum object count of 10 and verifies it\n"
+                       "\t* Creates a memory cache with a maximum object lifetime of 60 and verifies it\n"
+                       "\t* Creates a memory cache with a stale object lifetime of 90 and verifies it\n";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       cache = sorcery_memory_cache_open("");
+       if (!cache) {
+               ast_test_status_update(test, "Failed to create a sorcery memory cache using default configuration\n");
+               res = AST_TEST_FAIL;
+       } else {
+               sorcery_memory_cache_close(cache);
+       }
+
+       cache = sorcery_memory_cache_open("maximum_objects=10");
+       if (!cache) {
+               ast_test_status_update(test, "Failed to create a sorcery memory cache with a maximum object count of 10\n");
+               res = AST_TEST_FAIL;
+       } else {
+               if (cache->maximum_objects != 10) {
+                       ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of 10 but it has '%u'\n",
+                               cache->maximum_objects);
+               }
+               sorcery_memory_cache_close(cache);
+       }
+
+       cache = sorcery_memory_cache_open("object_lifetime_maximum=60");
+       if (!cache) {
+               ast_test_status_update(test, "Failed to create a sorcery memory cache with a maximum object lifetime of 60\n");
+               res = AST_TEST_FAIL;
+       } else {
+               if (cache->object_lifetime_maximum != 60) {
+                       ast_test_status_update(test, "Created a sorcery memory cache with a maximum object lifetime of 60 but it has '%u'\n",
+                               cache->object_lifetime_maximum);
+               }
+               sorcery_memory_cache_close(cache);
+       }
+
+       cache = sorcery_memory_cache_open("object_lifetime_stale=90");
+       if (!cache) {
+               ast_test_status_update(test, "Failed to create a sorcery memory cache with a stale object lifetime of 90\n");
+               res = AST_TEST_FAIL;
+       } else {
+               if (cache->object_lifetime_stale != 90) {
+                       ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of 90 but it has '%u'\n",
+                               cache->object_lifetime_stale);
+               }
+               sorcery_memory_cache_close(cache);
+       }
+
+
+       return res;
+}
+
+AST_TEST_DEFINE(open_with_invalid_options)
+{
+       int res = AST_TEST_PASS;
+       struct sorcery_memory_cache *cache;
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "open_with_invalid_options";
+               info->category = "/res/res_sorcery_memory_cache/";
+               info->summary = "Attempt to create sorcery memory caches using invalid options";
+               info->description = "This test attempts to perform the following:\n"
+                       "\t* Create a memory cache with an empty name\n"
+                       "\t* Create a memory cache with a maximum object count of -1\n"
+                       "\t* Create a memory cache with a maximum object count of toast\n"
+                       "\t* Create a memory cache with a maximum object lifetime of -1\n"
+                       "\t* Create a memory cache with a maximum object lifetime of toast\n"
+                       "\t* Create a memory cache with a stale object lifetime of -1\n"
+                       "\t* Create a memory cache with a stale object lifetime of toast\n";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       cache = sorcery_memory_cache_open("name=");
+       if (cache) {
+               ast_test_status_update(test, "Created a sorcery memory cache with an empty name\n");
+               sorcery_memory_cache_close(cache);
+               res = AST_TEST_FAIL;
+       }
+
+       cache = sorcery_memory_cache_open("maximum_objects=-1");
+       if (cache) {
+               ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of -1\n");
+               sorcery_memory_cache_close(cache);
+               res = AST_TEST_FAIL;
+       }
+
+       cache = sorcery_memory_cache_open("maximum_objects=toast");
+       if (cache) {
+               ast_test_status_update(test, "Created a sorcery memory cache with a maximum object count of toast\n");
+               sorcery_memory_cache_close(cache);
+               res = AST_TEST_FAIL;
+       }
+
+       cache = sorcery_memory_cache_open("object_lifetime_maximum=-1");
+       if (cache) {
+               ast_test_status_update(test, "Created a sorcery memory cache with an object lifetime maximum of -1\n");
+               sorcery_memory_cache_close(cache);
+               res = AST_TEST_FAIL;
+       }
+
+       cache = sorcery_memory_cache_open("object_lifetime_maximum=toast");
+       if (cache) {
+               ast_test_status_update(test, "Created a sorcery memory cache with an object lifetime maximum of toast\n");
+               sorcery_memory_cache_close(cache);
+               res = AST_TEST_FAIL;
+       }
+
+       cache = sorcery_memory_cache_open("object_lifetime_stale=-1");
+       if (cache) {
+               ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of -1\n");
+               sorcery_memory_cache_close(cache);
+               res = AST_TEST_FAIL;
+       }
+
+       cache = sorcery_memory_cache_open("object_lifetime_stale=toast");
+       if (cache) {
+               ast_test_status_update(test, "Created a sorcery memory cache with a stale object lifetime of toast\n");
+               sorcery_memory_cache_close(cache);
+               res = AST_TEST_FAIL;
+       }
+
+       cache = sorcery_memory_cache_open("tacos");
+       if (cache) {
+               ast_test_status_update(test, "Created a sorcery memory cache with an invalid configuration option 'tacos'\n");
+               sorcery_memory_cache_close(cache);
+               res = AST_TEST_FAIL;
+       }
+
+       return res;
+}
+
+AST_TEST_DEFINE(create_and_retrieve)
+{
+       int res = AST_TEST_FAIL;
+       struct ast_sorcery *sorcery = NULL;
+       struct sorcery_memory_cache *cache = NULL;
+       RAII_VAR(void *, object, NULL, ao2_cleanup);
+       RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "create";
+               info->category = "/res/res_sorcery_memory_cache/";
+               info->summary = "Attempt to create an object in the cache";
+               info->description = "This test performs the following:\n"
+                       "\t* Creates a memory cache with default options\n"
+                       "\t* Creates a sorcery instance with a test object\n"
+                       "\t* Creates a test object with an id of test\n"
+                       "\t* Pushes the test object into the memory cache\n"
+                       "\t* Confirms that the test object is in the cache\n";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       cache = sorcery_memory_cache_open("");
+       if (!cache) {
+               ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
+               goto cleanup;
+       }
+
+       if (ao2_container_count(cache->objects)) {
+               ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
+               goto cleanup;
+       }
+
+       sorcery = alloc_and_initialize_sorcery();
+       if (!sorcery) {
+               ast_test_status_update(test, "Failed to create a test sorcery instance\n");
+               goto cleanup;
+       }
+
+       object = ast_sorcery_alloc(sorcery, "test", "test");
+       if (!object) {
+               ast_test_status_update(test, "Failed to allocate a test object\n");
+               goto cleanup;
+       }
+
+       sorcery_memory_cache_create(sorcery, cache, object);
+
+       if (!ao2_container_count(cache->objects)) {
+               ast_test_status_update(test, "Added test object to memory cache but cache remains empty\n");
+               goto cleanup;
+       }
+
+       cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
+       if (!cached_object) {
+               ast_test_status_update(test, "Object placed into memory cache could not be retrieved\n");
+               goto cleanup;
+       }
+
+       if (cached_object != object) {
+               ast_test_status_update(test, "Object retrieved from memory cached is not the one we cached\n");
+               goto cleanup;
+       }
+
+       res = AST_TEST_PASS;
+
+cleanup:
+       if (cache) {
+               sorcery_memory_cache_close(cache);
+       }
+       if (sorcery) {
+               ast_sorcery_unref(sorcery);
+       }
+
+       return res;
+}
+
+AST_TEST_DEFINE(update)
+{
+       int res = AST_TEST_FAIL;
+       struct ast_sorcery *sorcery = NULL;
+       struct sorcery_memory_cache *cache = NULL;
+       RAII_VAR(void *, original_object, NULL, ao2_cleanup);
+       RAII_VAR(void *, updated_object, NULL, ao2_cleanup);
+       RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "create";
+               info->category = "/res/res_sorcery_memory_cache/";
+               info->summary = "Attempt to create and then update an object in the cache";
+               info->description = "This test performs the following:\n"
+                       "\t* Creates a memory cache with default options\n"
+                       "\t* Creates a sorcery instance with a test object\n"
+                       "\t* Creates a test object with an id of test\n"
+                       "\t* Pushes the test object into the memory cache\n"
+                       "\t* Confirms that the test object is in the cache\n"
+                       "\t* Creates a new test object with the same id of test\n"
+                       "\t* Pushes the new test object into the memory cache\n"
+                       "\t* Confirms that the new test object has replaced the old one\n";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       cache = sorcery_memory_cache_open("");
+       if (!cache) {
+               ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
+               goto cleanup;
+       }
+
+       if (ao2_container_count(cache->objects)) {
+               ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
+               goto cleanup;
+       }
+
+       sorcery = alloc_and_initialize_sorcery();
+       if (!sorcery) {
+               ast_test_status_update(test, "Failed to create a test sorcery instance\n");
+               goto cleanup;
+       }
+
+       original_object = ast_sorcery_alloc(sorcery, "test", "test");
+       if (!original_object) {
+               ast_test_status_update(test, "Failed to allocate a test object\n");
+               goto cleanup;
+       }
+
+       sorcery_memory_cache_create(sorcery, cache, original_object);
+
+       updated_object = ast_sorcery_alloc(sorcery, "test", "test");
+       if (!updated_object) {
+               ast_test_status_update(test, "Failed to allocate an updated test object\n");
+               goto cleanup;
+       }
+
+       sorcery_memory_cache_create(sorcery, cache, updated_object);
+
+       if (ao2_container_count(cache->objects) != 1) {
+               ast_test_status_update(test, "Added updated test object to memory cache but cache now contains %d objects instead of 1\n",
+                       ao2_container_count(cache->objects));
+               goto cleanup;
+       }
+
+       cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
+       if (!cached_object) {
+               ast_test_status_update(test, "Updated object placed into memory cache could not be retrieved\n");
+               goto cleanup;
+       }
+
+       if (cached_object == original_object) {
+               ast_test_status_update(test, "Updated object placed into memory cache but old one is being retrieved\n");
+               goto cleanup;
+       } else if (cached_object != updated_object) {
+               ast_test_status_update(test, "Updated object placed into memory cache but different one is being retrieved\n");
+               goto cleanup;
+       }
+
+       res = AST_TEST_PASS;
+
+cleanup:
+       if (cache) {
+               sorcery_memory_cache_close(cache);
+       }
+       if (sorcery) {
+               ast_sorcery_unref(sorcery);
+       }
+
+       return res;
+}
+
+AST_TEST_DEFINE(delete)
+{
+       int res = AST_TEST_FAIL;
+       struct ast_sorcery *sorcery = NULL;
+       struct sorcery_memory_cache *cache = NULL;
+       RAII_VAR(void *, object, NULL, ao2_cleanup);
+       RAII_VAR(void *, cached_object, NULL, ao2_cleanup);
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = "delete";
+               info->category = "/res/res_sorcery_memory_cache/";
+               info->summary = "Attempt to create and then delete an object in the cache";
+               info->description = "This test performs the following:\n"
+                       "\t* Creates a memory cache with default options\n"
+                       "\t* Creates a sorcery instance with a test object\n"
+                       "\t* Creates a test object with an id of test\n"
+                       "\t* Pushes the test object into the memory cache\n"
+                       "\t* Confirms that the test object is in the cache\n"
+                       "\t* Deletes the test object from the cache\n"
+                       "\t* Confirms that the test object is no longer in the cache\n";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       cache = sorcery_memory_cache_open("");
+       if (!cache) {
+               ast_test_status_update(test, "Failed to create a sorcery memory cache using default options\n");
+               goto cleanup;
+       }
+
+       if (ao2_container_count(cache->objects)) {
+               ast_test_status_update(test, "Memory cache contains cached objects before we added one\n");
+               goto cleanup;
+       }
+
+       sorcery = alloc_and_initialize_sorcery();
+       if (!sorcery) {
+               ast_test_status_update(test, "Failed to create a test sorcery instance\n");
+               goto cleanup;
+       }
+
+       object = ast_sorcery_alloc(sorcery, "test", "test");
+       if (!object) {
+               ast_test_status_update(test, "Failed to allocate a test object\n");
+               goto cleanup;
+       }
+
+       sorcery_memory_cache_create(sorcery, cache, object);
+
+       if (!ao2_container_count(cache->objects)) {
+               ast_test_status_update(test, "Added test object to memory cache but cache contains no objects\n");
+               goto cleanup;
+       }
+
+       cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
+       if (!cached_object) {
+               ast_test_status_update(test, "Test object placed into memory cache could not be retrieved\n");
+               goto cleanup;
+       }
+
+       ao2_ref(cached_object, -1);
+       cached_object = NULL;
+
+       sorcery_memory_cache_delete(sorcery, cache, object);
+
+       cached_object = sorcery_memory_cache_retrieve_id(sorcery, cache, "test", "test");
+       if (cached_object) {
+               ast_test_status_update(test, "Test object deleted from memory cache can still be retrieved\n");
+               goto cleanup;
+       }
+
+       res = AST_TEST_PASS;
+
+cleanup:
+       if (cache) {
+               sorcery_memory_cache_close(cache);
+       }
+       if (sorcery) {
+               ast_sorcery_unref(sorcery);
+       }
+
+       return res;
+}
+
+#endif
+
+static int unload_module(void)
+{
+       if (sched) {
+               ast_sched_context_destroy(sched);
+               sched = NULL;
+       }
+
+       ao2_cleanup(caches);
+
+       ast_sorcery_wizard_unregister(&memory_cache_object_wizard);
+
+       AST_TEST_UNREGISTER(open_with_valid_options);
+       AST_TEST_UNREGISTER(open_with_invalid_options);
+       AST_TEST_UNREGISTER(create_and_retrieve);
+       AST_TEST_UNREGISTER(update);
+       AST_TEST_UNREGISTER(delete);
+
+       return 0;
+}
+
+static int load_module(void)
+{
+       sched = ast_sched_context_create();
+       if (!sched) {
+               ast_log(LOG_ERROR, "Failed to create scheduler for cache management\n");
+               unload_module();
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       if (ast_sched_start_thread(sched)) {
+               ast_log(LOG_ERROR, "Failed to create scheduler thread for cache management\n");
+               unload_module();
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       caches = ao2_container_alloc(CACHES_CONTAINER_BUCKET_SIZE, sorcery_memory_cache_hash,
+               sorcery_memory_cache_cmp);
+       if (!caches) {
+               ast_log(LOG_ERROR, "Failed to create container for configured caches\n");
+               unload_module();
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       if (ast_sorcery_wizard_register(&memory_cache_object_wizard)) {
+               unload_module();
+               return AST_MODULE_LOAD_DECLINE;
+       }
+
+       AST_TEST_REGISTER(open_with_valid_options);
+       AST_TEST_REGISTER(open_with_invalid_options);
+       AST_TEST_REGISTER(create_and_retrieve);
+       AST_TEST_REGISTER(update);
+       AST_TEST_REGISTER(delete);
+
+       return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS | AST_MODFLAG_LOAD_ORDER, "Sorcery Memory Cache Object Wizard",
+       .support_level = AST_MODULE_SUPPORT_CORE,
+       .load = load_module,
+       .unload = unload_module,
+       .load_pri = AST_MODPRI_REALTIME_DRIVER,
+);
index f6fe72a..6dcdb78 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/applications.{format}",
index f13b049..263bfd6 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/asterisk.{format}",
index 4f7f724..b608be6 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/bridges.{format}",
index 9bcfda9..bc0879b 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/channels.{format}",
index 02d5d20..e232273 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "Kevin Harwell <kharwell@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/deviceStates.{format}",
index 17b8847..4fd077c 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/endpoints.{format}",
index 35c9377..392b0ac 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.2",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/events.{format}",
index 87c6f03..324e378 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2013, Digium, Inc.",
        "_author": "Jonathan Rose <jrose@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/mailboxes.{format}",
index 65ef524..63df3f2 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/playbacks.{format}",
index d96184b..51f0a21 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/recordings.{format}",
index 906eb6b..628aa19 100644 (file)
@@ -2,7 +2,7 @@
        "_copyright": "Copyright (C) 2012 - 2013, Digium, Inc.",
        "_author": "David M. Lee, II <dlee@digium.com>",
        "_svn_revision": "$Revision$",
-       "apiVersion": "1.6.0",
+       "apiVersion": "1.7.0",
        "swaggerVersion": "1.1",
        "basePath": "http://localhost:8088/ari",
        "resourcePath": "/api-docs/sounds.{format}",