Author: mjordan
Date: Thu Jan 29 08:38:23 2015
New Revision: 431402

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=431402
Log:
Add some initial media caching stuff

- Add the media cache in the core. This acts as a thin wrapper over some bucket
  implementation, which must be provided by other modules. Mostly, it provides
  the actual in-memory cache of the media files and their metadata.
- Add some unit tests for the media cache core.
- Add a skeleton module for the HTTP retrieval of items for the media cache.
  Mostly this just has 'create' callback implemented (although currently it
  has only been compile checked).


Added:
    team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h   
(with props)
    team/mjordan/trunk-http-stuff-and-things/main/media_cache.c   (with props)
    team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c   (with 
props)
    team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c   (with 
props)
Modified:
    team/mjordan/trunk-http-stuff-and-things/main/asterisk.c
    team/mjordan/trunk-http-stuff-and-things/main/bucket.c
    team/mjordan/trunk-http-stuff-and-things/res/res_curl.c

Added: team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h
URL: 
http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h?view=auto&rev=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h 
(added)
+++ team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h Thu 
Jan 29 08:38:23 2015
@@ -1,0 +1,59 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2014, Digium, Inc.
+ *
+ * Matt Jordan <mjor...@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 AGI Extension interfaces - Asterisk Gateway Interface
+ */
+
+#ifndef _ASTERISK_MEDIA_CACHE_H
+#define _ASTERISK_MEDIA_CACHE_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+struct ast_variable;
+
+/*!
+ * \brief
+ *
+ * \param uri
+ *
+ * \retval 0 uri does not exist in cache
+ * \retval 1 uri does exist in cache
+ */
+int ast_media_cache_exists(const char *uri);
+
+int ast_media_cache_retrieve(const char *uri, const char *preferred_file_name,
+       char *file_path, size_t len);
+
+int ast_media_cache_retrieve_metadata(const char *uri, const char *key,
+       char *value, size_t len);
+
+int ast_media_cache_create_or_update(const char *uri, const char *file_path,
+       struct ast_variable *metadata);
+
+int ast_media_cache_delete(const char *uri);
+
+int ast_media_cache_init(void);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_MEDIA_CACHE_H */

Propchange: 
team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: 
team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: 
team/mjordan/trunk-http-stuff-and-things/include/asterisk/media_cache.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/mjordan/trunk-http-stuff-and-things/main/asterisk.c
URL: 
http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/main/asterisk.c?view=diff&rev=431402&r1=431401&r2=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/main/asterisk.c (original)
+++ team/mjordan/trunk-http-stuff-and-things/main/asterisk.c Thu Jan 29 
08:38:23 2015
@@ -250,6 +250,7 @@
 #include "asterisk/endpoints.h"
 #include "asterisk/codec.h"
 #include "asterisk/format_cache.h"
+#include "asterisk/media_cache.h"
 
 #include "../defaults.h"
 
@@ -4469,6 +4470,11 @@
                exit(1);
        }
 
+       if (ast_media_cache_init()) {
+               printf("%s", term_quit());
+               exit(1);
+       }
+
        if ((moduleresult = load_modules(1))) {         /* Load modules, 
pre-load only */
                printf("%s", term_quit());
                exit(moduleresult == -2 ? 2 : 1);

Modified: team/mjordan/trunk-http-stuff-and-things/main/bucket.c
URL: 
http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/main/bucket.c?view=diff&rev=431402&r1=431401&r2=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/main/bucket.c (original)
+++ team/mjordan/trunk-http-stuff-and-things/main/bucket.c Thu Jan 29 08:38:23 
2015
@@ -258,7 +258,7 @@
 
        if (ast_strlen_zero(name) || !bucket || !file ||
            !bucket->create || !bucket->delete || !bucket->retrieve_id ||
-           !create_cb) {
+           (!bucket->create && !create_cb)) {
                return -1;
        }
 
@@ -679,7 +679,7 @@
 
        ast_string_field_set(file, scheme, uri_scheme);
 
-       if (scheme->create(file)) {
+       if (scheme->create && scheme->create(file)) {
                ao2_ref(file, -1);
                return NULL;
        }

Added: team/mjordan/trunk-http-stuff-and-things/main/media_cache.c
URL: 
http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/main/media_cache.c?view=auto&rev=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/main/media_cache.c (added)
+++ team/mjordan/trunk-http-stuff-and-things/main/media_cache.c Thu Jan 29 
08:38:23 2015
@@ -1,0 +1,374 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) <Year>, <Your Name Here>
+ *
+ * <Your Name Here> <<Your Email Here>>
+ *
+ * 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 media_cache
+ *
+ * \author \verbatim <Your Name Here> <<Your Email Here>> \endverbatim
+ *
+ * This is a skeleton for development of an Asterisk test module
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+       <depend>TEST_FRAMEWORK</depend>
+       <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <sys/stat.h>
+#include "asterisk/config.h"
+#include "asterisk/bucket.h"
+#include "asterisk/astdb.h"
+#include "asterisk/media_cache.h"
+
+#define AST_DB_FAMILY "MediaCache"
+
+/* Length of 'MediaCache' + 2 '/' characters */
+#define AST_DB_FAMILY_LEN 12
+
+#define AO2_BUCKETS 61
+
+static struct ao2_container *media_cache;
+
+/*! \brief Hashing function for file metadata */
+static int media_cache_hash(const void *obj, const int flags)
+{
+       const struct ast_bucket_file *object;
+       const char *key;
+
+       switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
+       case OBJ_KEY:
+               key = obj;
+               return ast_str_hash(key);
+       case OBJ_POINTER:
+               object = obj;
+               return ast_str_hash(ast_sorcery_object_get_id(object));
+       default:
+               /* Hash can only work on something with a full key */
+               ast_assert(0);
+               return 0;
+       }
+}
+
+/*! \brief Comparison function for file metadata */
+static int media_cache_cmp(void *obj, void *arg, int flags)
+{
+       struct ast_bucket_file *left = obj;
+       struct ast_bucket_file *right = arg;
+       const char *name = arg;
+
+       return !strcmp(ast_sorcery_object_get_id(left), flags & OBJ_KEY ?
+               name : ast_sorcery_object_get_id(right)) ? CMP_MATCH | CMP_STOP 
: 0;
+}
+
+
+int ast_media_cache_exists(const char *uri)
+{
+       struct ast_bucket_file *bucket_file;
+
+       if (ast_strlen_zero(uri)) {
+               return 0;
+       }
+
+       bucket_file = ast_bucket_file_retrieve(uri);
+       if (bucket_file) {
+               ao2_ref(bucket_file, -1);
+               return 1;
+       }
+
+       return 0;
+}
+
+static void metadata_sync_to_astdb(struct ast_bucket_file *bucket_file,
+       const char *hash, const char *name)
+{
+       struct ast_bucket_metadata *metadata;
+
+       metadata = ast_bucket_file_metadata_get(bucket_file, name);
+       if (metadata) {
+               ast_db_put(hash, metadata->name, metadata->value);
+               ao2_ref(metadata, -1);
+       }       
+}
+
+static void media_cache_item_sync_to_astdb(struct ast_bucket_file *bucket_file)
+{
+       char hash[41]; /* 40 character SHA1 hash */
+
+       ast_sha1_hash(hash, ast_sorcery_object_get_id(bucket_file));
+       if (ast_db_put(AST_DB_FAMILY, ast_sorcery_object_get_id(bucket_file), 
hash)) {
+               return;
+       }
+
+       ast_db_put(hash, "path", bucket_file->path);
+       metadata_sync_to_astdb(bucket_file, hash, "size");
+       metadata_sync_to_astdb(bucket_file, hash, "ext");
+       metadata_sync_to_astdb(bucket_file, hash, "accessed");
+}
+
+static void media_cache_item_del_from_astdb(struct ast_bucket_file 
*bucket_file)
+{
+       char *hash_value;
+
+       if (ast_db_get_allocated(AST_DB_FAMILY, 
ast_sorcery_object_get_id(bucket_file), &hash_value)) {
+               return;
+       }
+
+       ast_db_deltree(hash_value, NULL);
+       ast_db_del(AST_DB_FAMILY, hash_value);
+       ast_free(hash_value);
+}
+
+int ast_media_cache_retrieve(const char *uri, const char *preferred_file_name,
+       char *file_path, size_t len)
+{
+       struct ast_bucket_file *bucket_file;
+       SCOPED_AO2LOCK(media_lock, media_cache);
+
+       if (ast_strlen_zero(uri)) {
+               return -1;
+       }
+
+       bucket_file = ao2_find(media_cache, uri, OBJ_KEY | OBJ_NOLOCK);
+       if (bucket_file) {
+               ao2_lock(bucket_file);
+               ast_bucket_file_update(bucket_file);
+               ast_copy_string(file_path, bucket_file->path, len);
+               media_cache_item_sync_to_astdb(bucket_file);
+               ao2_unlock(bucket_file);
+               ao2_ref(bucket_file, -1);
+               return 0;
+       }
+
+       bucket_file = ast_bucket_file_retrieve(uri);
+       if (!bucket_file) {
+               bucket_file = ast_bucket_file_alloc(uri);
+               if (!bucket_file) {
+                       ast_log(LOG_WARNING, "Failed to create storage for 
%s\n", uri);
+                       return -1;
+               }
+
+               if (!ast_strlen_zero(preferred_file_name)) {
+                       ast_copy_string(bucket_file->path, preferred_file_name,
+                               sizeof(bucket_file->path));
+               } else if (ast_bucket_file_temporary_create(bucket_file)) {
+                       ast_log(LOG_WARNING, "Failed to create temp storage for 
%s\n", uri);
+                       ao2_ref(bucket_file, -1);
+                       return -1;
+               }
+
+               if (ast_bucket_file_create(bucket_file)) {
+                       ast_log(LOG_WARNING, "Failed to obtain media at %s\n", 
uri);
+                       ao2_ref(bucket_file, -1);
+                       return -1;
+               }
+       } else if (!ast_strlen_zero(preferred_file_name)) {
+               ao2_lock(bucket_file);
+               rename(bucket_file->path, preferred_file_name);
+               ast_copy_string(bucket_file->path, preferred_file_name,
+                       sizeof(bucket_file->path));
+               ao2_unlock(bucket_file);
+       }
+       ast_copy_string(file_path, bucket_file->path, len);
+       media_cache_item_sync_to_astdb(bucket_file);
+       ao2_link_flags(media_cache, bucket_file, OBJ_NOLOCK);
+
+       ao2_ref(bucket_file, -1);
+
+       return 0;
+}
+
+int ast_media_cache_retrieve_metadata(const char *uri, const char *key,
+       char *value, size_t len)
+{
+       struct ast_bucket_file *bucket_file;
+       struct ast_bucket_metadata *metadata;
+
+       if (ast_strlen_zero(uri) || ast_strlen_zero(key) || !value) {
+               return -1;
+       }
+
+       bucket_file = ao2_find(media_cache, uri, OBJ_KEY);
+       if (!bucket_file) {
+               return -1;
+       }
+
+       metadata = ao2_find(bucket_file->metadata, key, OBJ_KEY);
+       if (!metadata) {
+               ao2_ref(bucket_file, -1);
+               return -1;
+       }
+       ast_copy_string(value, metadata->value, len);
+
+       ao2_ref(metadata, -1);
+       ao2_ref(bucket_file, -1);
+       return 0;
+}
+
+int ast_media_cache_create_or_update(const char *uri, const char *file_path,
+       struct ast_variable *metadata)
+{
+       struct ast_bucket_file *bucket_file;
+       struct stat st;
+       char tmp[128];
+       char *ext;
+       char *file_path_ptr;
+       struct ast_variable *it_metadata;
+       SCOPED_AO2LOCK(media_lock, media_cache);
+
+       if (ast_strlen_zero(file_path) || ast_strlen_zero(uri)) {
+               return -1;
+       }
+       file_path_ptr = ast_strdupa(file_path);
+
+       if (stat(file_path, &st)) {
+               ast_log(LOG_WARNING, "Unable to obtain information for file %s 
for URI %s\n",
+                       file_path, uri);
+               return -1;
+       }
+
+       bucket_file = ao2_find(media_cache, uri, OBJ_KEY | OBJ_NOLOCK);
+       if (!bucket_file) {
+               bucket_file = ast_bucket_file_retrieve(uri);
+               if (!bucket_file) {
+                       bucket_file = ast_bucket_file_alloc(uri);
+                       if (!bucket_file) {
+                               ast_log(LOG_WARNING, "Failed to create file 
storage for %s and %s\n",
+                                       uri, file_path);
+                               return -1;
+                       }
+               }
+               ao2_link_flags(media_cache, bucket_file, OBJ_NOLOCK);
+       }
+
+       ao2_lock(bucket_file);
+       strcpy(bucket_file->path, file_path);
+       bucket_file->created.tv_sec = st.st_ctime;
+       bucket_file->modified.tv_sec = st.st_mtime;
+
+       snprintf(tmp, sizeof(tmp), "%ld", (long)st.st_atime);
+       ast_bucket_file_metadata_set(bucket_file, "accessed", tmp);
+
+       snprintf(tmp, sizeof(tmp), "%zu", st.st_size);
+       ast_bucket_file_metadata_set(bucket_file, "size", tmp);
+
+       ext = strrchr(file_path_ptr, '.');
+       if (ext) {
+               ast_bucket_file_metadata_set(bucket_file, "ext", ext + 1);
+       }
+
+       for (it_metadata = metadata; it_metadata; it_metadata = 
it_metadata->next) {
+               ast_bucket_file_metadata_set(bucket_file, it_metadata->name, 
it_metadata->value);
+       }
+
+       if (ast_bucket_file_create(bucket_file)) {
+               ao2_unlock(bucket_file);
+               ast_log(LOG_WARNING, "Failed to create media for %s\n", uri);
+               ao2_ref(bucket_file, -1);
+               return -1;
+       }
+       media_cache_item_sync_to_astdb(bucket_file);
+       ao2_unlock(bucket_file);
+
+       ao2_ref(bucket_file, -1);
+       return 0;
+}
+
+int ast_media_cache_delete(const char *uri)
+{
+       struct ast_bucket_file *bucket_file;
+       int res;
+
+       if (ast_strlen_zero(uri)) {
+               return -1;
+       }
+
+       bucket_file = ao2_find(media_cache, uri, OBJ_KEY | OBJ_UNLINK);
+       if (!bucket_file) {
+               return -1;
+       }
+
+       res = ast_bucket_file_delete(bucket_file);
+       media_cache_item_del_from_astdb(bucket_file);
+
+       ao2_ref(bucket_file, -1);
+
+       return res;
+}
+
+static void media_cache_shutdown(void)
+{
+       ao2_ref(media_cache, -1);
+       media_cache = NULL;
+}
+
+static void media_cache_remove_from_astdb(const char *uri, const char *hash)
+{
+       ast_db_del(AST_DB_FAMILY, uri + AST_DB_FAMILY_LEN);
+       ast_db_deltree(hash, NULL);
+}
+
+static int media_cache_item_populate_from_astdb(const char *uri, const char 
*hash)
+{
+       char path[PATH_MAX];
+
+       if (ast_db_get(hash, "path", path, sizeof(path))) {
+               ast_log(LOG_WARNING, "Failed to restore media cache item for 
'%s' from AstDB: no 'path' specified\n",
+                       uri);
+               return -1;              
+       }
+
+       /* TODO: do we care about the metadata stored, since we are about to 
force
+          a refresh?
+        */
+       return ast_media_cache_create_or_update(uri + AST_DB_FAMILY_LEN, path, 
NULL);
+}
+
+static void media_cache_populate_from_astdb(void)
+{
+       struct ast_db_entry *db_entry;
+       struct ast_db_entry *db_tree;
+
+       db_tree = ast_db_gettree(AST_DB_FAMILY, NULL);
+       for (db_entry = db_tree; db_entry; db_entry = db_entry->next) {
+               if (media_cache_item_populate_from_astdb(db_entry->key, 
db_entry->data)) {
+                       media_cache_remove_from_astdb(db_entry->key, 
db_entry->data);
+               }
+       }
+       ast_db_freetree(db_tree);
+}
+
+int ast_media_cache_init(void)
+{
+       ast_register_atexit(media_cache_shutdown);
+
+       media_cache = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_RWLOCK, 
AO2_BUCKETS,
+               media_cache_hash, media_cache_cmp);
+       if (!media_cache) {
+               return -1;
+       }
+
+       media_cache_populate_from_astdb();
+
+       return 0;
+}

Propchange: team/mjordan/trunk-http-stuff-and-things/main/media_cache.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/mjordan/trunk-http-stuff-and-things/main/media_cache.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/mjordan/trunk-http-stuff-and-things/main/media_cache.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/mjordan/trunk-http-stuff-and-things/res/res_curl.c
URL: 
http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/res/res_curl.c?view=diff&rev=431402&r1=431401&r2=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/res/res_curl.c (original)
+++ team/mjordan/trunk-http-stuff-and-things/res/res_curl.c Thu Jan 29 08:38:23 
2015
@@ -51,6 +51,7 @@
 static const char *dependents[] = {
        "func_curl.so",
        "res_config_curl.so",
+       "res_http_media_cache.so",
 };
 
 static int unload_module(void)

Added: team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c
URL: 
http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c?view=auto&rev=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c (added)
+++ team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c Thu Jan 
29 08:38:23 2015
@@ -1,0 +1,219 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2015, Matt Jordan
+ *
+ * Matt Jordan <mjor...@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
+ *
+ * \author\verbatim <Your Name Here> <<Your Email Here>> \endverbatim
+ *
+ * This is a skeleton for development of an Asterisk test module
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+       <depend>curl</depend>
+       <depend>res_curl</depend>
+       <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <curl/curl.h>
+
+#include "asterisk/module.h"
+#include "asterisk/bucket.h"
+#include "asterisk/sorcery.h"
+
+#define GLOBAL_USERAGENT "asterisk-libcurl-agent/1.0"
+
+struct curl_bucket_file_data {
+       struct ast_bucket_file *bucket_file;
+       FILE *out_file;
+};
+
+static size_t curl_header_callback(char *buffer, size_t size, size_t nitems, 
void *data)
+{
+       struct curl_bucket_file_data *cb_data = data;
+       size_t realsize;
+       size_t offset;
+       size_t value_len;
+       char *value;
+       char *dupd_value;
+       char *clean_value;
+
+       realsize = size * nitems;
+
+       /* buffer may not be NULL terminated */
+       value = memchr(buffer, ':', realsize);
+       if (!value) {
+               ast_log(LOG_WARNING, "Failed to split received header in cURL 
request\n");
+               return 0;
+       }
+       offset = value - buffer;
+       value_len = realsize - offset;
+       *value++ = '\0';
+
+       if (strcmp(buffer, "ETag")
+               && strcmp(buffer, "Cache-Control")
+               && strcmp(buffer, "Last-Modified")) {
+               return realsize;
+       }
+
+       dupd_value = ast_malloc(value_len + 1);
+       if (!dupd_value) {
+               return 0;
+       }
+       strncpy(dupd_value, value, value_len);
+       dupd_value[value_len] = '\0';
+       clean_value = dupd_value;
+       clean_value = ast_skip_blanks(clean_value);
+       clean_value = ast_trim_blanks(clean_value);
+
+       ast_bucket_file_metadata_set(cb_data->bucket_file, buffer, clean_value);
+
+       ast_free(dupd_value);
+       return realsize;
+}
+
+static size_t curl_body_callback(void *ptr, size_t size, size_t nitems, void 
*data)
+{
+       struct curl_bucket_file_data *cb_data = data;
+       size_t realsize;
+
+       realsize = fwrite(ptr, size, nitems, cb_data->out_file);
+
+       return realsize;
+}
+
+static int bucket_http_wizard_create(const struct ast_sorcery *sorcery, void 
*data,
+       void *object)
+{
+       char curl_errbuf[CURL_ERROR_SIZE + 1]; /* add one to be safe */
+       struct ast_bucket_file *bucket_file = object;
+       const char *uri = ast_sorcery_object_get_id(bucket_file);
+       CURL *curl;
+       struct curl_bucket_file_data cb_data = {
+               .bucket_file = bucket_file,
+       };
+
+       cb_data.out_file = fopen(bucket_file->path, "wb");
+       if (!cb_data.out_file) {
+               return -1;
+       }
+
+       /* TODO:
+        * -- force a refresh by pulling down the URI
+        * -- populate the object
+        */     
+       curl = curl_easy_init();
+       if (!curl) {
+               fclose(cb_data.out_file);
+               return -1;
+       }
+
+       curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
+       curl_easy_setopt(curl, CURLOPT_TIMEOUT, 180);
+       curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_body_callback);
+       curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, curl_header_callback);
+       curl_easy_setopt(curl, CURLOPT_USERAGENT, GLOBAL_USERAGENT);
+
+       curl_easy_setopt(curl, CURLOPT_URL, uri);
+       curl_easy_setopt(curl, CURLOPT_FILE, (void*)&cb_data);
+       curl_errbuf[CURL_ERROR_SIZE] = '\0';
+       curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_errbuf);
+
+       if (curl_easy_perform(curl)) {
+               ast_log(LOG_WARNING, "%s ('%s')\n", curl_errbuf, uri);
+       }
+
+       fclose(cb_data.out_file);
+       return -1;
+}
+
+static int bucket_http_wizard_update(const struct ast_sorcery *sorcery, void 
*data,
+       void *object)
+{
+       /*if (!strcmp(ast_sorcery_object_get_id(object), VALID_RESOURCE)) {
+               return 0;
+       }*/
+
+       return -1;
+}
+
+static void *bucket_http_wizard_retrieve_id(const struct ast_sorcery *sorcery,
+       void *data, const char *type, const char *id)
+{
+       /* TODO:
+        * -- hit the provided URI and see if we need to download it
+        *   -- if we do, pull it down and update the resource
+        *   -- if not, simply return what's there
+        */
+       if (strcmp(type, "file")) {
+               return NULL;
+       }
+       return NULL;
+}
+
+static int bucket_http_wizard_delete(const struct ast_sorcery *sorcery, void 
*data,
+       void *object)
+{
+       return -1;
+}
+
+static struct ast_sorcery_wizard bucket_wizard = {
+       .name = "http",
+       .create = bucket_http_wizard_create,
+       .retrieve_id = bucket_http_wizard_retrieve_id,
+       .delete = bucket_http_wizard_delete,
+};
+
+static struct ast_sorcery_wizard bucket_file_wizard = {
+       .name = "http",
+       .create = bucket_http_wizard_create,
+       .update = bucket_http_wizard_update,
+       .retrieve_id = bucket_http_wizard_retrieve_id,
+       .delete = bucket_http_wizard_delete,
+};
+
+
+static int unload_module(void)
+{
+       return 0;
+}
+
+static int load_module(void)
+{
+       if (ast_bucket_scheme_register("http", &bucket_wizard, 
&bucket_file_wizard,
+                       NULL, NULL)) {
+               ast_log(LOG_ERROR, "Failed to register Bucket HTTP wizard 
scheme implementation\n");
+               return AST_MODULE_LOAD_FAILURE;
+       }
+
+       return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "FOOBAR!",
+               .support_level = AST_MODULE_SUPPORT_CORE,
+               .load = load_module,
+               .unload = unload_module,
+               .load_pri = AST_MODPRI_DEFAULT,
+       );
+

Propchange: team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/mjordan/trunk-http-stuff-and-things/res/res_http_media_cache.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c
URL: 
http://svnview.digium.com/svn/asterisk/team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c?view=auto&rev=431402
==============================================================================
--- team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c (added)
+++ team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c Thu Jan 
29 08:38:23 2015
@@ -1,0 +1,395 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) <Year>, <Your Name Here>
+ *
+ * <Your Name Here> <<Your Email Here>>
+ *
+ * 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 Skeleton Test
+ *
+ * \author\verbatim <Your Name Here> <<Your Email Here>> \endverbatim
+ *
+ * This is a skeleton for development of an Asterisk test module
+ * \ingroup tests
+ */
+
+/*** MODULEINFO
+       <depend>TEST_FRAMEWORK</depend>
+       <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/utils.h"
+#include "asterisk/module.h"
+#include "asterisk/test.h"
+#include "asterisk/bucket.h"
+#include "asterisk/media_cache.h"
+
+#define CATEGORY "/main/media_cache/"
+
+#define VALID_RESOURCE "httptest://localhost:8088/test_media_cache/monekys.wav"
+
+#define INVALID_RESOURCE "httptest://localhost:8088/test_media_cache/bad.wav"
+
+#define INVALID_SCHEME "foo://localhost:8088/test_media_cache/monkeys.wav"
+
+#define NO_SCHEME "localhost:8088/test_media_cache/monkeys.wav"
+
+static int bucket_http_test_wizard_create(const struct ast_sorcery *sorcery, 
void *data,
+       void *object)
+{
+       if (!strcmp(ast_sorcery_object_get_id(object), VALID_RESOURCE)) {
+               return 0;
+       }
+
+       return -1;
+}
+
+static int bucket_http_test_wizard_update(const struct ast_sorcery *sorcery, 
void *data,
+       void *object)
+{
+       if (!strcmp(ast_sorcery_object_get_id(object), VALID_RESOURCE)) {
+               return 0;
+       }
+
+       return -1;
+}
+
+static void *bucket_http_test_wizard_retrieve_id(const struct ast_sorcery 
*sorcery,
+       void *data, const char *type, const char *id)
+{
+       struct ast_bucket_file *bucket_file;
+
+       if (!strcmp(type, "file") && !strcmp(id, VALID_RESOURCE)) {
+               bucket_file = ast_bucket_file_alloc(id);
+               if (!bucket_file) {
+                       return NULL;
+               }
+
+               ast_bucket_file_temporary_create(bucket_file);
+               return bucket_file;
+       }
+       return NULL;
+}
+
+static int bucket_http_test_wizard_delete(const struct ast_sorcery *sorcery, 
void *data,
+       void *object)
+{
+       if (!strcmp(ast_sorcery_object_get_id(object), VALID_RESOURCE)) {
+               return 0;
+       }
+
+       return -1;
+}
+
+static struct ast_sorcery_wizard bucket_test_wizard = {
+       .name = "httptest",
+       .create = bucket_http_test_wizard_create,
+       .retrieve_id = bucket_http_test_wizard_retrieve_id,
+       .delete = bucket_http_test_wizard_delete,
+};
+
+static struct ast_sorcery_wizard bucket_file_test_wizard = {
+       .name = "httptest",
+       .create = bucket_http_test_wizard_create,
+       .update = bucket_http_test_wizard_update,
+       .retrieve_id = bucket_http_test_wizard_retrieve_id,
+       .delete = bucket_http_test_wizard_delete,
+};
+
+AST_TEST_DEFINE(exists_nominal)
+{
+       int res;
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = __func__;
+               info->category = CATEGORY;
+               info->summary = "Test nominal existance of resources in the 
cache";
+               info->description =
+                       "This test verifies that if a known resource is in the 
cache, "
+                       "calling ast_media_cache_exists will return logical 
True. If "
+                       "a resource does not exist, the same function call will 
return "
+                       "logical False.\n";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       res = ast_media_cache_exists(INVALID_RESOURCE);
+       ast_test_validate(test, res == 0);
+
+       res = ast_media_cache_exists(VALID_RESOURCE);
+       ast_test_validate(test, res == 1);
+
+       return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(exists_off_nominal)
+{
+       int res;
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = __func__;
+               info->category = CATEGORY;
+               info->summary = "Test off nominal existance of resources in the 
cache";
+               info->description =
+                       "This test verifies that checking for bad resources 
(NULL, bad "
+                       "scheme, etc.) does not result in false positivies.\n";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       res = ast_media_cache_exists("");
+       ast_test_validate(test, res != 1);
+
+       res = ast_media_cache_exists(NULL);
+       ast_test_validate(test, res != 1);
+
+       res = ast_media_cache_exists(NO_SCHEME);
+       ast_test_validate(test, res != 1);
+
+       res = ast_media_cache_exists(INVALID_SCHEME);
+       ast_test_validate(test, res != 1);
+
+       return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(create_update_nominal)
+{
+       int res;
+       char file_path[PATH_MAX];
+       char tmp_path_one[PATH_MAX] = "/tmp/test-media-cache-XXXXXX";
+       char tmp_path_two[PATH_MAX] = "/tmp/test-media-cache-XXXXXX";
+       int fd;
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = __func__;
+               info->category = CATEGORY;
+               info->summary = "Test nominal creation/updating of a resource";
+               info->description =
+                       "This test creates a resource and associates it with a 
file. "
+                       "It then updates the resource with a new file. In both 
cases, "
+                       "the test verifies that the resource is associated with 
the "
+                       "file.\n";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       /* Create two local files to associate with a resource */
+       fd = mkstemp(tmp_path_one);
+       if (fd < 0) {
+               ast_test_status_update(test, "Failed to create first tmp file: 
%s\n",
+                       tmp_path_one);
+               return AST_TEST_FAIL;
+       }
+       /* We don't need anything in the file */
+       close(fd);
+
+       fd = mkstemp(tmp_path_two);
+       if (fd < 0) {
+               ast_test_status_update(test, "Failed to create second tmp file: 
%s\n",
+                       tmp_path_two);
+               return AST_TEST_FAIL;
+       }
+       close(fd);
+
+       ast_test_status_update(test, "Creating resource with %s\n", 
tmp_path_one);
+       res = ast_media_cache_create_or_update(VALID_RESOURCE, tmp_path_one, 
NULL);
+       ast_test_validate(test, res == 0);
+
+       res = ast_media_cache_retrieve(VALID_RESOURCE, NULL, file_path, 
PATH_MAX);
+       ast_test_status_update(test, "Got %s for first file path\n", file_path);
+       ast_test_validate(test, res == 0);
+       ast_test_validate(test, strcmp(file_path, tmp_path_one) == 0);
+
+       ast_test_status_update(test, "Creating resource with %s\n", 
tmp_path_two);
+       res = ast_media_cache_create_or_update(VALID_RESOURCE, tmp_path_two, 
NULL);
+       ast_test_validate(test, res == 0);
+
+       res = ast_media_cache_retrieve(VALID_RESOURCE, NULL, file_path, 
PATH_MAX);
+       ast_test_status_update(test, "Got %s for second file path\n", 
file_path);
+       ast_test_validate(test, res == 0);
+       ast_test_validate(test, strcmp(file_path, tmp_path_two) == 0);
+
+       unlink(tmp_path_one);
+       unlink(tmp_path_two);
+
+       return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(create_update_off_nominal)
+{
+       int res;
+       char tmp_path[PATH_MAX] = "/tmp/test-media-cache-XXXXXX";
+       int fd;
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = __func__;
+               info->category = CATEGORY;
+               info->summary = "Test off nominal creation/updating of a 
resource";
+               info->description =
+                       "Test creation/updating of a resource with a variety of 
invalid\n"
+                       "inputs.\n";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       /* Create two local files to associate with a resource */
+       fd = mkstemp(tmp_path);
+       if (fd < 0) {
+               ast_test_status_update(test, "Failed to create first tmp file: 
%s\n",
+                       tmp_path);
+               return AST_TEST_FAIL;
+       }
+       /* We don't need anything in the file */
+       close(fd);
+
+       res = ast_media_cache_create_or_update(VALID_RESOURCE, NULL, NULL);
+       ast_test_validate(test, res != 0);
+
+       res = ast_media_cache_create_or_update(VALID_RESOURCE, "", NULL);
+       ast_test_validate(test, res != 0);
+
+       res = ast_media_cache_create_or_update(VALID_RESOURCE, "I don't exist", 
NULL);
+       ast_test_validate(test, res != 0);
+
+       res = ast_media_cache_create_or_update(INVALID_RESOURCE, tmp_path, 
NULL);
+       ast_test_validate(test, res != 0);
+
+       res = ast_media_cache_create_or_update(INVALID_SCHEME, tmp_path, NULL);
+       ast_test_validate(test, res != 0);
+
+       res = ast_media_cache_create_or_update(NO_SCHEME, tmp_path, NULL);
+       ast_test_validate(test, res != 0);
+
+       unlink(tmp_path);
+
+       return AST_TEST_PASS;
+}
+
+AST_TEST_DEFINE(create_update_metadata)
+{
+       int res;
+       char tmp_path[PATH_MAX] = "/tmp/test-media-cache-XXXXXX";
+       char file_path[PATH_MAX];
+       char actual_metadata[32];
+       struct ast_variable *meta_list = NULL;
+       struct ast_variable *tmp;
+       int fd;
+
+       switch (cmd) {
+       case TEST_INIT:
+               info->name = __func__;
+               info->category = CATEGORY;
+               info->summary = "Test nominal creation/updating of a resource";
+               info->description =
+                       "This test creates a resource and associates it with a 
file. "
+                       "It then updates the resource with a new file. In both 
cases, "
+                       "the test verifies that the resource is associated with 
the "
+                       "file.\n";
+               return AST_TEST_NOT_RUN;
+       case TEST_EXECUTE:
+               break;
+       }
+
+       /* Create two local files to associate with a resource */
+       fd = mkstemp(tmp_path);
+       if (fd < 0) {
+               ast_test_status_update(test, "Failed to create first tmp file: 
%s\n",
+                       tmp_path);
+               return AST_TEST_FAIL;
+       }
+       /* We don't need anything in the file */
+       close(fd);
+
+       tmp = ast_variable_new("meta1", "value1", __FILE__);
+       if (!tmp) {
+               ast_test_status_update(test, "Failed to create metadata 1 for 
test\n");
+               return AST_TEST_FAIL;
+       }
+       ast_variable_list_append(&meta_list, tmp);
+
+       tmp = ast_variable_new("meta2", "value2", __FILE__);
+       if (!tmp) {
+               ast_test_status_update(test, "Failed to create metadata 2 for 
test\n");
+               return AST_TEST_FAIL;
+       }
+       ast_variable_list_append(&meta_list, tmp);
+
+       res = ast_media_cache_create_or_update(VALID_RESOURCE, tmp_path, 
meta_list);
+       ast_test_validate(test, res == 0);
+
+       res = ast_media_cache_retrieve(VALID_RESOURCE, NULL, file_path, 
PATH_MAX);
+       ast_test_status_update(test, "Got %s for second file path\n", 
file_path);
+       ast_test_validate(test, res == 0);
+       ast_test_validate(test, strcmp(file_path, tmp_path) == 0);
+
+       res = ast_media_cache_retrieve_metadata(VALID_RESOURCE, "meta1",
+               actual_metadata, sizeof(actual_metadata));
+       ast_test_validate(test, res == 0);
+       ast_test_validate(test, strcmp(actual_metadata, "value1") == 0);
+
+       res = ast_media_cache_retrieve_metadata(VALID_RESOURCE, "meta2",
+               actual_metadata, sizeof(actual_metadata));
+       ast_test_validate(test, res == 0);
+       ast_test_validate(test, strcmp(actual_metadata, "value2") == 0);
+
+       unlink(tmp_path);
+
+       return AST_TEST_PASS;
+}
+
+static int unload_module(void)
+{
+       AST_TEST_UNREGISTER(exists_nominal);
+       AST_TEST_UNREGISTER(exists_off_nominal);
+
+       AST_TEST_UNREGISTER(create_update_nominal);
+       AST_TEST_UNREGISTER(create_update_metadata);
+       AST_TEST_UNREGISTER(create_update_off_nominal);
+
+       return 0;
+}
+
+static int load_module(void)
+{
+       if (ast_bucket_scheme_register("httptest", &bucket_test_wizard,
+               &bucket_file_test_wizard, NULL, NULL)) {
+               ast_log(LOG_ERROR, "Failed to register Bucket HTTP test wizard 
scheme implementation\n");
+               return AST_MODULE_LOAD_FAILURE;
+       }
+
+       AST_TEST_REGISTER(exists_nominal);
+       AST_TEST_REGISTER(exists_off_nominal);
+
+       AST_TEST_REGISTER(create_update_nominal);
+       AST_TEST_REGISTER(create_update_metadata);
+       AST_TEST_REGISTER(create_update_off_nominal);
+
+       return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Media Cache Tests");

Propchange: team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/mjordan/trunk-http-stuff-and-things/tests/test_media_cache.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