[svn-commits] tilghman: branch tilghman/ast_storage r332755 - in /team/tilghman/ast_storage...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Sun Aug 21 22:41:44 CDT 2011


Author: tilghman
Date: Sun Aug 21 22:41:38 2011
New Revision: 332755

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=332755
Log:
Adding the core files into the new branch.  Compiles okay, but still need to work on imap support.

Added:
    team/tilghman/ast_storage/include/asterisk/storage.h   (with props)
    team/tilghman/ast_storage/main/storage.c   (with props)
    team/tilghman/ast_storage/res/res_storage_odbc.c   (with props)
Modified:
    team/tilghman/ast_storage/main/asterisk.c

Added: team/tilghman/ast_storage/include/asterisk/storage.h
URL: http://svnview.digium.com/svn/asterisk/team/tilghman/ast_storage/include/asterisk/storage.h?view=auto&rev=332755
==============================================================================
--- team/tilghman/ast_storage/include/asterisk/storage.h (added)
+++ team/tilghman/ast_storage/include/asterisk/storage.h Sun Aug 21 22:41:38 2011
@@ -1,0 +1,278 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * John Khvatov <ivaxer at gmail.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 Generic File Storage Engine Support.
+ *
+ * \author John Khvatov <ivaxer at gmail.com>
+ */
+
+#ifndef _ASTERISK_STORAGE_H
+#define _ASTERISK_STORAGE_H
+
+#include "asterisk/linkedlists.h"
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*
+ * File instance.
+ */
+struct ast_storage_fileinst {
+	int fd;
+	AST_RWLIST_ENTRY(ast_storage_fileinst) list;
+	char ext[16];
+	unsigned int temporary:1;
+	char localfile[64];
+};
+
+/*
+ * Virtual file in storage medium.
+ * Note: file instance and file object are not equivalent in any sense.
+ */
+struct ast_storage_fileobject {
+	char objectname[256];
+	char pathname[256];
+	struct ast_storage_fileinst *metadata;
+	AST_RWLIST_HEAD(, ast_storage_fileinst) files;
+	AST_RWLIST_ENTRY(ast_storage_fileobject) list;
+};
+
+AST_RWLIST_HEAD(ast_storage_fileobjects, ast_storage_fileobject);
+
+struct ast_storage_be {
+	const char *name;
+	struct ast_storage *(*create)(const char *uri);
+	int (*release)(struct ast_storage *st);
+	struct ast_storage_fileobject *(*get)(struct ast_storage *st, const char *objectname, const char *exts);
+	int (*put)(struct ast_storage *st, struct ast_storage_fileobject *fo, const char *exts);
+	int (*del)(struct ast_storage *st, const char *fileobject);
+	ssize_t (*read)(struct ast_storage *st, struct ast_storage_fileinst *fi, void *buf, size_t count);
+	ssize_t (*write)(struct ast_storage *st, struct ast_storage_fileinst *fi, void *buf, size_t count);
+	struct ast_storage_fileobjects *(*listdir)(struct ast_storage *st, const char *objectpath);
+	off_t (*tell)(struct ast_storage *st, struct ast_storage_fileinst *fi);
+	off_t (*seek)(struct ast_storage *st, struct ast_storage_fileinst *fi, off_t offset, int whence);
+	int (*copy)(struct ast_storage *st, const char *from, const char *to);
+	AST_RWLIST_ENTRY(ast_storage_be) list;
+	struct ast_module *module;
+};
+
+/* Most specific backends will have longer structures */
+struct ast_storage {
+	const struct ast_storage_be *be;
+	void *storage_pvt; /* Backend specific structure */
+};
+
+/*! \brief Register a storage backenda
+ * \retval Returns 1 on success or 0 on failure
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int __ast_register_storage(const struct ast_storage_be *e, struct ast_module *mod);
+#define ast_register_storage(e) __ast_register_storage(e, ast_module_info->self)
+
+/*! \brief Unregister a storage backend
+ * \param name Storage backend name
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_unregister_storage(const char *name);
+
+/*! \brief Retrieves an instance of a storage engine
+ * \param uri Backend specific URI to the resource
+ * \retval non-NULL Success
+ * \retval NULL Failure
+ */
+struct ast_storage *ast_storage_request(const char *uri);
+
+/*! \brief Releases a storage instance created with ast_storage_request
+ * \param st Storage instance
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_storage_release(struct ast_storage *st);
+
+/*! \brief Retrieves a file from the storage medium
+ * \param st Storage instance
+ * \param objectname Logical name of the file
+ * \param exts File extensions to retrieve (separated by | character)
+ * \retval non-NULL Success
+ * \retval NULL Failure
+ */
+struct ast_storage_fileobject *ast_storage_get(struct ast_storage *st, const char *objectname, const char *exts);
+
+/*! \brief Stores a file into the storage medium
+ * \param st Storage instance
+ * \param fo File object to store
+ * \param exts File extensions to store (separated by '|' character)
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_storage_put(struct ast_storage *st, struct ast_storage_fileobject *fo, const char *exts);
+
+/*! \brief Reads from a file instance
+ * \param st Storage instance
+ * \param fi File instance to read
+ * \param buf Buffer to read into
+ * \param count Number of bytes to read
+ * \retval 0 Seccess
+ * \retval <0 Failure
+ * \return Returns standard POSIX codes as documented in 'man 3p read'
+ *
+ * \note For more info see 'man 3p read'
+ */
+int ast_storage_read(struct ast_storage *st, struct ast_storage_fileinst *fi, void *buf, size_t count);
+
+/*! \brief Writes on a file instance
+ * \param st Storage instance
+ * \param fi File instance to write
+ * \param buf Buffer to write from
+ * \param count Number of bytes to write
+ * \retval 0 Seccess
+ * \retval <0 Failure
+ * \return Returns standard POSIX codes as documented in 'man 3p write'
+ *
+ * \note For more info see 'man 3p write'
+ */
+int ast_storage_write(struct ast_storage *st, struct ast_storage_fileinst *fi, void *buf, size_t count);
+
+/*! \brief Removes a file from the storage medium
+ * \param st Storage instance
+ * \param fileobject Logical name of the file to delete
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_storage_del(struct ast_storage *st, const char *fileobject);
+
+/*! \brief Copies a file in the storage medium
+ * \param st Storage instance
+ * \param from Logical name of the source file
+ * \param to Logical name of the destination
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_storage_copy(struct ast_storage *st, const char *from, const char *to);
+
+/*! \brief Retrieves a list of files in the pathname in the storage medium
+ * \param st Storage instance
+ * \param pathname Logical name of the path
+ * \retval non-NULL Success
+ * \retval NULL Failure
+ */
+struct ast_storage_fileobjects *ast_storage_listdir(struct ast_storage *st, const char *pathname);
+
+/*! \brief Returns the current read/write file offset
+ * \param st Storage instance
+ * \param fi File instance
+ * \return Upon successful completion, returns the current offset, as measured
+ * in bytes from the beginning of the file. Returns (off_t)-1 on failure.
+ *
+ * /note For more info see 'man 3p lseek'.
+ */
+off_t ast_storage_tell(struct ast_storage *st, struct ast_storage_fileinst *fi);
+
+/*! \brief Moves the read/write file offset
+ * \param st Storage instance
+ * \param fi File instance
+ * \param offset Offset to set
+ * \param whence Whence
+ * \return Upon successful completion, returns the resulting offset, as measured
+ * in bytes from the beginning of the file. Returns (off_t)-1 on failure.
+ *
+ * /note For more info see 'man 3p lseek'.
+ */
+off_t ast_storage_seek(struct ast_storage *st, struct ast_storage_fileinst *fi, off_t offset, int whence);
+
+/*! \brief Creates new fileobject
+ * \retval non-NULL Success
+ * \retval NULL Failure
+ * \return Successfully allocated and initialized fileobject.
+ */
+struct ast_storage_fileobject *ast_storage_fileobject_create(void);
+
+/*! \brief Releases fileobject
+ * \param fo File object to release
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_storage_fileobject_release(struct ast_storage_fileobject *fo);
+
+/*! \brief Creates new file instance
+ * \retval non-NULL Success
+ * \retval NULL Failure
+ * \return Successfully allocated and initialized file instance.
+ */
+struct ast_storage_fileinst *ast_storage_fileinst_create(void);
+
+/*! \brief Releasesfile instance
+ * \param fi File instance to release
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_storage_fileinst_release(struct ast_storage_fileinst *fi);
+
+/*! \brief Creates a new list of fileobjects
+ * \retval non-NULL Success
+ * \retval NULL Failure
+ * \return Head of list of fileobjects.
+ */
+struct ast_storage_fileobjects *ast_storage_fileobjects_create(void);
+
+/*! \brief Releases list of fileobjects
+ * \param fobjs Head of list of fileobjects
+ * \retval 0 Success
+ * \retval -1 Failure
+ */
+int ast_storage_fileobjects_release(struct ast_storage_fileobjects *fobjs);
+
+/*! \brief Opens file instance
+ * \param fi File instance to open
+ * \param flags status flags and access modes
+ * \retval -1 Failure
+ * \retval >=0 Success
+ * \return Returns standard POSIX codes as documented in 'man 3p open' and sets
+ * errno.
+ *
+ * \note For more info see 'man 3p open'.
+ */
+int ast_storage_open(struct ast_storage_fileinst *fi, int flags);
+
+/*! \brief Closes file instance
+ * \param fi File instance to close
+ * \retval -1 Failure
+ * \retval 0 Success
+ * \return Returns standard POSIX codes as documented in 'man 3p open' and sets
+ * errno.
+ *
+ * \note For more info see 'man 3p close'.
+ */
+int ast_storage_close(struct ast_storage_fileinst *fi);
+
+/*! \brief Get a storage engine by name
+ * \param name Name of storage engine
+ * \retval non-NULL Success
+ * \retval NULL Failure
+ */
+struct ast_storage_be *ast_storage_getbyname(const char *name);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_STORAGE_H */

Propchange: team/tilghman/ast_storage/include/asterisk/storage.h
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/tilghman/ast_storage/include/asterisk/storage.h
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision Yoyo

Propchange: team/tilghman/ast_storage/include/asterisk/storage.h
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: team/tilghman/ast_storage/main/asterisk.c
URL: http://svnview.digium.com/svn/asterisk/team/tilghman/ast_storage/main/asterisk.c?view=diff&rev=332755&r1=332754&r2=332755
==============================================================================
--- team/tilghman/ast_storage/main/asterisk.c (original)
+++ team/tilghman/ast_storage/main/asterisk.c Sun Aug 21 22:41:38 2011
@@ -130,6 +130,7 @@
 #include "asterisk/lock.h"
 #include "asterisk/utils.h"
 #include "asterisk/file.h"
+#include "asterisk/storage.h"
 #include "asterisk/io.h"
 #include "editline/histedit.h"
 #include "asterisk/config.h"
@@ -3831,6 +3832,11 @@
 		exit(1);
 	}
 
+	if (ast_storage_engine_init()) {
+		printf("%s", term_quit());
+		exit(1);
+	}
+
 	if (load_pbx()) {
 		printf("%s", term_quit());
 		exit(1);

Added: team/tilghman/ast_storage/main/storage.c
URL: http://svnview.digium.com/svn/asterisk/team/tilghman/ast_storage/main/storage.c?view=auto&rev=332755
==============================================================================
--- team/tilghman/ast_storage/main/storage.c (added)
+++ team/tilghman/ast_storage/main/storage.c Sun Aug 21 22:41:38 2011
@@ -1,0 +1,546 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2010, Digium, Inc.
+ *
+ * John Khvatov <ivaxer at gmail.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 Generic File Storage Engine Support.
+ *
+ * \author John Khvatov <ivaxer at gmail.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/_private.h"
+#include "asterisk/storage.h"
+#include "asterisk/cli.h"
+#include "asterisk/file.h"
+#include "asterisk/frame.h"
+#include "asterisk/options.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/module.h"
+#include "asterisk/test.h"
+
+static AST_RWLIST_HEAD_STATIC(storage_engines, ast_storage_be);
+
+int __ast_register_storage(const struct ast_storage_be *be, struct ast_module *mod)
+{
+	struct ast_storage_be *tmp;
+
+	if (AST_RWLIST_WRLOCK(&storage_engines)) {
+		ast_log(LOG_WARNING, "Unable to lock storage engine list\n");
+		return -1;
+	}
+	AST_RWLIST_TRAVERSE(&storage_engines, tmp, list) {
+		if (!strcasecmp(be->name, tmp->name)) {
+			AST_RWLIST_UNLOCK(&storage_engines);
+			ast_log(LOG_WARNING, "Tried to register storage engine '%s', not found in list (was it already unregistered?)\n", be->name);
+			return -1;
+		}
+	}
+	if (!(tmp = ast_calloc(1, sizeof(*tmp)))) {
+		AST_RWLIST_UNLOCK(&storage_engines);
+		return -1;
+	}
+	*tmp = *be;
+	tmp->module = mod;
+
+	AST_RWLIST_INSERT_TAIL(&storage_engines, tmp, list);
+	AST_RWLIST_UNLOCK(&storage_engines);
+	if (option_verbose > 1) {
+		ast_verbose(VERBOSE_PREFIX_2 "Registered storage engine '%s'\n", be->name);
+	}
+
+	return 0;
+}
+
+int ast_unregister_storage(const char *name)
+{
+	struct ast_storage_be *tmp;
+	int res = -1;
+
+	if (AST_RWLIST_WRLOCK(&storage_engines)) {
+		ast_log(LOG_WARNING, "Unable to lock storage engine list\n");
+		return -1;
+	}
+	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&storage_engines, tmp, list) {
+		if (!strcasecmp(name, tmp->name)) {
+			AST_RWLIST_REMOVE_CURRENT(list);
+			ast_free(tmp);
+			res = 0;
+		}
+	}
+	AST_RWLIST_TRAVERSE_SAFE_END
+	AST_RWLIST_UNLOCK(&storage_engines);
+
+	if (!res) {
+		ast_verb(2, "Unregistered storage engine '%s'\n", name);
+	} else {
+		ast_log(LOG_WARNING, "Tried to unregister storage engine '%s', already unregistered\n", name);
+	}
+
+	return res;
+}
+
+struct ast_storage_be *ast_storage_getbyname(const char *name)
+{
+	struct ast_storage_be *be;
+	if (AST_RWLIST_RDLOCK(&storage_engines)) {
+		ast_log(LOG_WARNING, "Unable to lock storage engine list\n");
+		return NULL;
+	}
+
+	AST_RWLIST_TRAVERSE(&storage_engines, be, list) {
+		if (!strcasecmp(name, be->name)) {
+			break;
+		}
+	}
+	AST_RWLIST_UNLOCK(&storage_engines);
+	return be;
+}
+
+struct ast_storage *ast_storage_request(const char *uri)
+{
+	struct ast_storage_be *be;
+	struct ast_storage *st = NULL;
+	char *tmp;
+	char *sename;
+	char *dir;
+
+	tmp = ast_strdupa(uri);
+	if ((dir = strstr(tmp, "://"))) {
+		*dir = '\0';
+		dir += 3;
+	}
+	sename = tmp;
+
+	if (!(be = ast_storage_getbyname(sename))) {
+		return NULL;
+	}
+
+	st = be->create(S_OR(dir, ""));
+	return st;
+}
+
+int ast_storage_release(struct ast_storage *st)
+{
+	return st && st->be->release ? st->be->release(st) : -1;
+}
+
+struct ast_storage_fileobject *ast_storage_get(struct ast_storage *st, const char *objectname, const char *exts)
+{
+	return st && st->be->get ? st->be->get(st, objectname, exts) : NULL;
+}
+
+int ast_storage_put(struct ast_storage *st, struct ast_storage_fileobject *fo, const char *exts)
+{
+	return st && st->be->put ? st->be->put(st, fo, exts) : -1;
+}
+
+int ast_storage_open(struct ast_storage_fileinst *fi, int flags)
+{
+	if (!fi) {
+		return -1;
+	}
+
+	fi->fd = open(fi->localfile, flags);
+	return fi->fd;
+}
+
+int ast_storage_close(struct ast_storage_fileinst *fi)
+{
+	int ret = -1;
+
+	if (!fi) {
+		return -1;
+	}
+
+	ret = close(fi->fd);
+	if (!ret) {
+		fi->fd = -1;
+	}
+	return ret;
+}
+
+int ast_storage_read(struct ast_storage *st, struct ast_storage_fileinst *fi, void *buf, size_t count)
+{
+	return st && st->be->read ? st->be->read(st, fi, buf, count) : -1;
+}
+
+int ast_storage_write(struct ast_storage *st, struct ast_storage_fileinst *fi, void *buf, size_t count)
+{
+	return st && st->be->write ? st->be->write(st, fi, buf, count) : -1;
+}
+
+int ast_storage_del(struct ast_storage *st, const char *fileobject)
+{
+	return st && st->be->del ? st->be->del(st, fileobject) : -1;
+}
+
+int ast_storage_copy(struct ast_storage *st, const char *from, const char *to)
+{
+	struct ast_storage_fileobject *fo;
+
+	if (st && st->be->copy) {
+		return st->be->copy(st, from, to);
+	}
+
+	fo = ast_storage_get(st, from, NULL);
+	if (!fo) {
+		ast_log(LOG_WARNING, "Unable to copy '%s' to '%s': get() failed\n", from, to);
+		return -1;
+	}
+
+	ast_copy_string(fo->objectname, to, sizeof(fo->objectname));
+
+	if (ast_storage_put(st, fo, NULL)) {
+		ast_log(LOG_WARNING, "Unable to copy '%s' to '%s': put() failed\n", from, to);
+		ast_storage_fileobject_release(fo);
+		return -1;
+	}
+
+	ast_storage_fileobject_release(fo);
+	return 0;
+}
+
+struct ast_storage_fileobjects *ast_storage_listdir(struct ast_storage *st, const char *pathname)
+{
+	return st && st->be->listdir ? st->be->listdir(st, pathname) : NULL;
+}
+
+struct ast_storage_fileobject *ast_storage_fileobject_create(void)
+{
+	struct ast_storage_fileobject *fo = ast_calloc(1, sizeof(*fo));
+	if (!fo) {
+		return NULL;
+	}
+
+	AST_RWLIST_HEAD_INIT(&fo->files);
+	return fo;
+}
+
+int ast_storage_fileobject_release(struct ast_storage_fileobject *fo)
+{
+	struct ast_storage_fileinst *inst;
+
+	if (!fo) {
+		return -1;
+	}
+
+	if (fo->metadata) {
+		return ast_storage_fileinst_release(fo->metadata);
+	}
+
+	if (!AST_RWLIST_WRLOCK(&fo->files)) {
+		ast_log(LOG_WARNING, "Unable to lock file instances list\n");
+		return -1;
+	}
+
+	while ((inst = AST_RWLIST_REMOVE_HEAD(&fo->files, list))) {
+		if (!ast_storage_fileinst_release(inst)) {
+			AST_RWLIST_UNLOCK(&fo->files);
+			return -1;
+		}
+	}
+
+	AST_RWLIST_UNLOCK(&fo->files);
+
+	ast_free(fo);
+	return 0;
+}
+
+struct ast_storage_fileinst *ast_storage_fileinst_create(void)
+{
+	struct ast_storage_fileinst *fi = ast_calloc(1, sizeof(*fi));
+	if (!fi) {
+		return NULL;
+	}
+
+	fi->fd = -1;
+	return fi;
+}
+
+int ast_storage_fileinst_release(struct ast_storage_fileinst *fi)
+{
+	if (!fi) {
+		return -1;
+	}
+
+	if (fi->fd != -1) {
+		ast_storage_close(fi);
+	}
+
+	if (fi->temporary && fi->localfile[0]) {
+		unlink(fi->localfile);
+	}
+
+	ast_free(fi);
+	return 0;
+}
+
+struct ast_storage_fileobjects *ast_storage_fileobjects_create(void)
+{
+	struct ast_storage_fileobjects *fobjs = ast_calloc(1, sizeof(*fobjs));
+	if (!fobjs) {
+		return NULL;
+	}
+
+	AST_RWLIST_HEAD_INIT(fobjs);
+	return fobjs;
+}
+
+int ast_storage_fileobjects_release(struct ast_storage_fileobjects *fobjs)
+{
+	struct ast_storage_fileobject *fo;
+
+	if (!fobjs) {
+		return -1;
+	}
+
+	if (!AST_RWLIST_WRLOCK(fobjs)) {
+		ast_log(LOG_WARNING, "Unable to lock fileobjects list\n");
+		return -1;
+	}
+
+	while ((fo = AST_RWLIST_REMOVE_HEAD(fobjs, list))) {
+		if (!ast_storage_fileobject_release(fo)) {
+			AST_RWLIST_UNLOCK(fobjs);
+			return -1;
+		}
+	}
+
+	AST_RWLIST_UNLOCK(fobjs);
+
+	ast_free(fobjs);
+	return 0;
+}
+
+#if 0
+static char *handle_storage_show_file(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct ast_storage *st;
+	struct ast_storage_fileinst *fi;
+	int count_files = 0;
+	/* XXX We want to remove the need to pass in a char array like this now.. */
+	char blah[PATH_MAX];
+	char *fn;
+	char *uri;
+
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "storage show file";
+		e->usage =
+			"Usage: storage show file <engine://filename>\n"
+			"       Displays information about a file via the storage engine\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+
+	if (a->argc != 4) {
+		return CLI_SHOWUSAGE;
+	}
+
+	uri = ast_strdupa(a->argv[3]);
+	if ((fn = strrchr(uri, '/'))) {
+		*fn++ = '\0';
+	}
+	else {
+		return CLI_SUCCESS;
+	}
+
+	if ((st = ast_storage_request(uri))) {
+		ast_cli(a->fd, "File information\n");
+		/* We don't care about format/extension here, so just send NULL */
+		if (!(ast_storage_get(st, fn, NULL, blah, sizeof(blah)))) {
+			AST_RWLIST_TRAVERSE(&st->fl->files, fi, list) {
+				char *ext;
+
+				/* Parse out the file extension */
+				if ((ext = strrchr(fi->ent->name, '.'))) {
+					ext++;
+				}
+
+				count_files++;
+				ast_cli(a->fd, "Name  : %s\n", fi->ent->name);
+				ast_cli(a->fd, "Format: %s\n", ast_getformatname(ast_getformatbyextension(ext)));
+			}
+		}
+		ast_cli(a->fd, "\n");
+		ast_cli(a->fd, "Found %d matching file(s).\n", count_files);
+	}
+	ast_storage_release(st);
+	return CLI_SUCCESS;
+}
+
+static char *handle_storage_show_engines(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct ast_storage_be *be;
+	int count_se = 0;
+
+	switch(cmd) {
+	case CLI_INIT:
+		e->command = "storage show engines";
+		e->usage =
+			"Usage: storage show engines\n"
+			"       Displays currently registered storage engines (if any)\n";
+		return NULL;
+	case CLI_GENERATE:
+			return NULL;
+	}
+
+	if(a->argc != 3) {
+		return CLI_SHOWUSAGE;
+	}
+
+	ast_cli(a->fd, "Storage Engine\n");
+
+	if (AST_RWLIST_RDLOCK(&storage_engines)) {
+		ast_log(LOG_WARNING, "Unable to lock storage engine list\n");
+		return CLI_FAILURE;
+	}
+
+	AST_RWLIST_TRAVERSE(&storage_engines, be, list) {
+		ast_cli(a->fd, "%s\n", be->name);
+		count_se++;
+	}
+	AST_RWLIST_UNLOCK(&storage_engines);
+	ast_cli(a->fd, "%d storage engine(s) registered.\n", count_se);
+	return CLI_SUCCESS;
+}
+
+static char *complete_storage_show_engines(struct ast_cli_args *a)
+{
+	struct ast_storage_be *be;
+	char *ret = NULL;
+	int which = 0;
+	int wordlen = strlen(a->word);
+
+	AST_RWLIST_RDLOCK(&storage_engines);
+	AST_RWLIST_TRAVERSE(&storage_engines, be, list) {
+		if (!strncasecmp(a->word, be->name, wordlen) && ++which > a->n) {
+			ret = strdup(be->name);
+			break;
+		}
+	}
+	AST_RWLIST_UNLOCK(&storage_engines);
+
+	return ret;
+}
+static char *handle_storage_show_engine(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	struct ast_storage_be *be;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "storage show engine";
+		e->usage =
+			"Usage: storage show engine <engine>\n"
+			"       Displays capabilities of requested storage engine\n";
+		return NULL;
+	case CLI_GENERATE:
+		return complete_storage_show_engines(a);
+	}
+
+	if (a->argc != 4) {
+		return CLI_SHOWUSAGE;
+	}
+
+	if ((be = ast_storage_getbyname(a->argv[3]))) {
+		ast_cli(a->fd, "Storage Engine: %s\n", be->name);
+		ast_cli(a->fd, "Can get       : %s\n", be->get ? "yes" : "no");
+#if 0
+		ast_cli(a->fd, "Can put       : %s\n", be->put ? "yes" : "no");
+#endif
+		ast_cli(a->fd, "Can del       : %s\n", be->del ? "yes" : "no");
+		ast_cli(a->fd, "Can opendir   : %s\n", be->opendir ? "yes" : "no");
+		ast_cli(a->fd, "Can readdir   : %s\n", be->readdir ? "yes" : "no");
+		ast_cli(a->fd, "Can closedir  : %s\n", be->closedir ? "yes" : "no");
+		if (be->status) {
+			ast_cli(a->fd, "\n");
+			ast_cli(a->fd, "Additional status\n");
+			/* XXX Need to rethink this.
+			 * This requires a struct ast_storage *, and we can't cast to it from ast_storage_be
+			be->status((struct ast_storage *)be, fd);
+			 */
+		}
+	} else {
+		ast_cli(a->fd, "No storage engine named '%s' found.\n", a->argv[3]);
+	}
+	return CLI_SUCCESS;
+}
+#endif
+
+
+struct ast_cli_entry cli_storage[] = {
+/*	AST_CLI_DEFINE(handle_storage_show_engines,"Show a storage engine"),
+	AST_CLI_DEFINE(handle_storage_show_engine,"List of storage engines"),
+	AST_CLI_DEFINE(handle_storage_show_file,"Show a file within a storage engine"), */
+};
+
+#ifdef TEST_FRAMEWORK
+
+AST_TEST_DEFINE(copy_test)
+{
+	struct ast_storage *st;
+	int ret;
+	const char uri[] = "odbc://asterisk-storage-test/storage/testpath";
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "copy_test";
+		info->category = "main/storage/generic";
+		info->summary = "copy test";
+		info->description =
+			"Tests the ast_storage_copy function";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	st = ast_storage_request(uri);
+	if (!st) {
+		return AST_TEST_FAIL;
+	}
+
+	ret = ast_storage_copy(st, "test", "huest");
+	if (ret) {
+		return AST_TEST_FAIL;
+	}
+
+	/* TODO: add checks here */
+
+	ret = ast_storage_release(st);
+	if (ret) {
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+#endif
+
+int ast_storage_engine_init(void)
+{
+#ifdef TEST_FRAMEWORK
+	AST_TEST_REGISTER(copy_test);
+#endif
+	ast_cli_register_multiple(cli_storage, ARRAY_LEN(cli_storage));
+	return 0;
+}

Propchange: team/tilghman/ast_storage/main/storage.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/tilghman/ast_storage/main/storage.c
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision Yoyo

Propchange: team/tilghman/ast_storage/main/storage.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: team/tilghman/ast_storage/res/res_storage_odbc.c
URL: http://svnview.digium.com/svn/asterisk/team/tilghman/ast_storage/res/res_storage_odbc.c?view=auto&rev=332755
==============================================================================
--- team/tilghman/ast_storage/res/res_storage_odbc.c (added)
+++ team/tilghman/ast_storage/res/res_storage_odbc.c Sun Aug 21 22:41:38 2011
@@ -1,0 +1,1216 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2010, John Khvatov
+ *
+ * John Khvatov <ivaxer at gmail.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 ODBC storage engine backend
+ * \author John Khvatov <ivaxer at gmail.com>
+ * \ingroup res
+ */
+
+/*** MODULEINFO
+	<depend>unixodbc</depend>
+	<depend>res_odbc</depend>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/storage.h"
+#include "asterisk/module.h"
+#include "asterisk/res_odbc.h"
+#include "asterisk/app.h"
+#include "asterisk/config.h"
+#include "asterisk/test.h"
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <errno.h>
+
+struct odbc_storage_pvt {
+	char odbc_class[128];
+	char tablename[128];
+	struct odbc_obj *conn;
+	char pathname[1];
+};
+
+struct get_query_data {
+	const char *sql;
+	const char *pathname;
+	const char *objectname;
+	const char *ext;
+};
+
+struct put_query_data {
+	char *sql;
+	char *pathname;
+	char *objectname;
+	char *ext;
+	void *data;
+	void *metadata;
+	SQLLEN data_len;
+	SQLLEN data_ind;
+	SQLLEN metadata_len;
+	SQLLEN metadata_ind;
+};
+
+struct del_query_data {
+	const char *sql;
+	const char *pathname;
+	const char *objectname;
+};
+
+struct copy_query_data {
+	const char *sql;
+	const char *from_pathname;
+	const char *to_pathname;
+	const char *from_objectname;
+	const char *to_objectname;
+};
+
+struct listdir_query_data {
+	const char *sql;
+	const char *pathname;
+};
+
+static const struct ast_storage_be odbc_se;
+
+static int se_release(struct ast_storage *st)
+{
+	struct odbc_storage_pvt *pvt;
+
+	if (!st) {
+		return 0;
+	}
+
+	if (st->storage_pvt) {
+		pvt = (struct odbc_storage_pvt *) st->storage_pvt;
+		if (pvt->conn) {
+			ast_odbc_release_obj(pvt->conn);
+		}
+		free(pvt);
+	}
+	free(st);
+	return 0;
+}
+
+static struct ast_storage *se_create(const char *uri)
+{
+	char tmp[256];
+	SQLRETURN ret;
+	struct ast_storage *st;
+	struct odbc_storage_pvt *pvt;
+	int pathname_len;
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(class);
+		AST_APP_ARG(tablename);
+		AST_APP_ARG(pathname);
+	);
+	SQLHSTMT stmt = NULL;
+
+	st = ast_calloc(1, sizeof(*st));
+	if (!st) {
+		return NULL;
+	}
+
+	if (ast_strlen_zero(uri)) {
+		ast_log(LOG_ERROR, "Invalid request uri.\n");
+		se_release(st);
+		return NULL;
+	}
+
+	ast_copy_string(tmp, uri, sizeof(tmp));
+
+	AST_NONSTANDARD_APP_ARGS(args, tmp, '/');
+
+	pathname_len = sizeof(char) * strlen(args.pathname);
+
+	pvt = ast_calloc(1, sizeof(*pvt) + pathname_len);
+	if (!pvt) {
+		se_release(st);
+		return NULL;
+	}
+
+	st->storage_pvt = (void *)pvt;
+	st->be = &odbc_se;
+
+	ast_copy_string(pvt->odbc_class, args.class, sizeof(pvt->odbc_class));
+	ast_copy_string(pvt->tablename, args.tablename, sizeof(pvt->tablename));
+	strcpy(pvt->pathname, args.pathname);
+
+	pvt->conn = ast_odbc_request_obj(pvt->odbc_class, 0);
+	if (!pvt->conn) {
+		ast_log(LOG_ERROR, "Retrieving a connected ODBC object (ast_odbc_request_obj) failed.\n");
+		se_release(st);
+		return NULL;
+	}
+
+	ret = SQLAllocHandle(SQL_HANDLE_STMT, pvt->conn->con, &stmt);
+	if (!SQL_SUCCEEDED(ret)) {
+		ast_log(LOG_ERROR, "Allocation of database handle failed.\n");
+		se_release(st);
+		return NULL;
+	}
+
+	return st;
+}
+
+static struct ast_storage_fileinst *fetch_fileinst(SQLHSTMT stmt)
+{
+	SQLRETURN ret;
+	SQLSMALLINT colcount, colid, collen;
+	SQLULEN colsize;
+	SQLSMALLINT decimaldigits, nullable, datatype;
+	struct ast_storage_fileinst *fi;
+	char coltitle[128];
+	char rowdata[32];
+	char tmpfile_fmt[] = "/tmp/storage-odbc-XXXXXX";
+	int fd;
+
+	ret = SQLFetch(stmt);
+	if (ret == SQL_NO_DATA) {
+		return NULL;
+	}
+	if (!SQL_SUCCEEDED(ret)) {
+		ast_log(LOG_WARNING, "SQL Fetch error!");
+		return NULL;
+	}
+
+	ret = SQLNumResultCols(stmt, &colcount);
+	if (!SQL_SUCCEEDED(ret)) {
+		ast_log(LOG_WARNING, "SQL Column Count error!");
+		return NULL;
+	}
+
+	fi = ast_storage_fileinst_create();
+	if (!fi) {
+		return NULL;
+	}
+
+	fi->temporary = 1;
+
+	for (colid = 1; colid <= colcount; colid++) {
+		ret = SQLDescribeCol(stmt, colid, (SQLCHAR *)coltitle, sizeof(coltitle), &collen,
+				&datatype, &colsize, &decimaldigits, &nullable);
+		if (!SQL_SUCCEEDED(ret)) {
+			ast_log(LOG_WARNING, "SQL Describe Column error!");
+			goto error;
+		}
+		if (!strcasecmp(coltitle, "recording")) {
+			size_t ndata;
+			off_t offset;
+			void *fdm;
+			int chunksize = 0x4000000; /* Try 64MB chunks initially */
+
+			fd = mkstemp(tmpfile_fmt);
+			if (fd == -1) {
+				ast_log(LOG_WARNING, "Could not create temp file");
+				goto error;
+			}
+
+			ast_copy_string(fi->localfile, tmpfile_fmt, sizeof(fi->localfile));
+
+			ret = SQLGetData(stmt, colid, SQL_BINARY, NULL, 0, (SQLLEN *)&ndata);
+
+			lseek(fd, ndata - 1, SEEK_SET);
+			if (write(fd, "", 1) != 1) {
+				close(fd);
+				fd = -1;
+				ast_log(LOG_WARNING, "Unable to write '%s': [%d]: %s\n", fi->localfile, errno, strerror(errno));
+				goto error;
+			}
+
+			for (offset = 0; offset < ndata; offset += chunksize) {
+				if ((fdm = mmap(NULL, chunksize, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
+					chunksize >>= 1;
+					offset -= chunksize;
+
+					if (chunksize < 4096) {
+						ast_log(LOG_WARNING, "Could not mmap the output file: [%d] %s\n", errno, strerror(errno));
+						goto error;
+					}
+
+					continue;
+				} else {
+					ret = SQLGetData(stmt, colid, SQL_BINARY, fdm, chunksize, NULL);
+					munmap(fdm, chunksize);
+					if (!SQL_SUCCEEDED(ret)) {
+						ast_log(LOG_WARNING, "SQL Get Data error!\n");
+						goto error;
+					}
+				}
+			}
+
+			fsync(fd);
+
+			if (ftruncate(fd, ndata) < 0) {
+				ast_log(LOG_WARNING, "Unable to truncate '%s': [%d]: %s\n", fi->localfile, errno, strerror(errno));
+			}
+
+			ast_copy_string(fi->localfile, tmpfile_fmt, sizeof(fi->localfile));
+		}
+		else if (!strcasecmp(coltitle, "extension")) {
+			ret = SQLGetData(stmt, colid, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+			if (!SQL_SUCCEEDED(ret)) {
+				ast_log(LOG_WARNING, "SQL Get Data error!");
+				goto error;
+			}
+
+			ast_copy_string(fi->ext, rowdata, sizeof(fi->ext));
+		}
+	}
+
+	return fi;
+error:
+	ast_storage_fileinst_release(fi);
+	return NULL;
+}
+
+static SQLHSTMT prepare_get_query(struct odbc_obj *obj, void *arg)
+{
+	SQLHSTMT stmt;
+	SQLRETURN ret;
+	struct get_query_data *qdata = (struct get_query_data *) arg;
+
+	ret = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+	if (!SQL_SUCCEEDED(ret)) {
+		ast_log(LOG_WARNING, "SQL Alloc Handle failed!");
+		return NULL;
+	}
+
+	ret = SQLPrepare(stmt, (SQLCHAR *)qdata->sql, SQL_NTS);
+	if (!SQL_SUCCEEDED(ret)) {
+		ast_log(LOG_WARNING, "SQL Prepare failed!\n[%s]", qdata->sql);
+		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+		return NULL;
+	}
+
+	SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(qdata->pathname), 0, (void *)qdata->pathname, 0, NULL);
+	SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(qdata->objectname), 0, (void *)qdata->objectname, 0, NULL);
+	if (qdata->ext) {
+		SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(qdata->ext), 0, (void *)qdata->ext, 0, NULL);
+		SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 3, 0, (void *)"txt", 0, NULL);
+	}
+
+	return stmt;
+}
+
+static struct ast_storage_fileobject *se_get(struct ast_storage *st, const char *objectname, const char *exts)
+{
+	struct odbc_storage_pvt *ost = (struct odbc_storage_pvt *)st->storage_pvt;
+	struct get_query_data qdata;
+	struct ast_storage_fileobject *fo = NULL;
+	struct ast_storage_fileinst *fi = NULL;
+	char ext[16];
+	char sql[128];
+	char *pathname;
+	SQLHSTMT stmt;
+
+	if (ast_strlen_zero(objectname)) {
+		ast_log(LOG_WARNING, "objectname is null.\n");
+		return NULL;
+	}
+
+	if (exts) {
+		char *ptr;
+		ast_copy_string(ext, exts, sizeof(ext));
+		ptr = strchr(ext, '|');
+		if (ptr) {
+			*ptr = '\0';
+		}
+	}
+
+	/*
+	 * TODO: objectname may contain relative or absolute path.
+	 */
+	pathname = ost->pathname;
+
+	if (exts) {
+		snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE pathname=? AND filename=? AND (extension=? OR extension=?)", ost->tablename);
+	} else {
+		snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE pathname=? AND filename=?", ost->tablename);
+	}
+
+	qdata.sql = sql;
+	qdata.objectname = objectname;
+	qdata.pathname = pathname;
+	if (exts) {
+		qdata.ext = ext;
+	} else {
+		qdata.ext = NULL;
+	}
+
+	stmt = ast_odbc_prepare_and_execute(ost->conn, prepare_get_query, &qdata);
+	if (!stmt) {
+		ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n", sql);
+		return NULL;
+	}
+
+	while ((fi = fetch_fileinst(stmt)) != NULL) {
+		if (!fo) {
+			fo = ast_storage_fileobject_create();
+			if (!fo) {
+				goto error;
+			}
+
+			ast_copy_string(fo->objectname, objectname, sizeof(fo->objectname));
+			ast_copy_string(fo->pathname, pathname, sizeof(fo->pathname));
+		}
+
+		if (!strcasecmp(fi->ext, "txt")) {
+			fo->metadata = fi;
+			continue;
+		}
+
+		AST_RWLIST_WRLOCK(&fo->files);
+		AST_RWLIST_INSERT_TAIL(&fo->files, fi, list);
+		AST_RWLIST_UNLOCK(&fo->files);
+	}
+
+	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+	return fo;
+
+error:
+	SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+	if (fo) {
+		ast_storage_fileobject_release(fo);
+	}
+	return NULL;
+}
+
+static SQLHSTMT execute_put_query(struct odbc_obj *obj, void *arg)
+{
+	SQLRETURN ret;
+	SQLHSTMT stmt;
+	struct put_query_data *qdata = (struct put_query_data *) arg;
+
+	ret = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+	if (!SQL_SUCCEEDED(ret)) {
+		ast_log(LOG_WARNING, "SQL Alloc Handle failed!");
+		return NULL;
+	}
+
+	SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(qdata->pathname), 0, (void *)qdata->pathname, 0, NULL);
+	SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(qdata->objectname), 0, (void *)qdata->objectname, 0, NULL);
+	SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(qdata->ext), 0, (void *)qdata->ext, 0, NULL);
+	SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, qdata->data_len, 0, (void *)qdata->data, 0, &qdata->data_ind);
+
+	if (qdata->metadata) {
+		SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(qdata->pathname), 0, (void *)qdata->pathname, 0, NULL);
+		SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(qdata->objectname), 0, (void *)qdata->objectname, 0, NULL);
+		SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, 3, 0, (void *)"txt", 0, NULL);
+		SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, qdata->metadata_len, 0, (void *)qdata->metadata, 0, &qdata->metadata_ind);
+	}
+
+	ret = SQLExecDirect(stmt, (unsigned char *)qdata->sql, SQL_NTS);
+	if (!SQL_SUCCEEDED(ret)) {
+		ast_log(LOG_WARNING, "SQL Exec Direct failed!");
+		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+		return NULL;
+	}
+
+	return stmt;
+}
+
+static int open_fileinst(struct ast_storage_fileinst *fi, int *fd, int *fdlen, void **fdm)
+{
+	int local_fd, local_fdlen;
+	void *local_fdm;
+
+	local_fd = open(fi->localfile, O_RDONLY);
+	if (local_fd == -1) {
+		ast_log(LOG_WARNING, "Unable to open file '%s': [%d]: %s\n", fi->localfile, errno, strerror(errno));
+		return -1;
+	}
+
+	local_fdlen = lseek(local_fd, 0, SEEK_END);
+
+	local_fdm = mmap(NULL, local_fdlen, PROT_READ, MAP_SHARED, local_fd, 0);
+	if (local_fdm == MAP_FAILED) {
+		ast_log(LOG_WARNING, "Unable to map file '%s': [%d]: %s\n", fi->localfile, errno, strerror(errno));
+		close(local_fd);
+		return -1;
+	}
+
+	*fdm = local_fdm;
+	*fdlen = local_fdlen;
+	*fd = local_fd;
+
+	return 0;
+}
+
+static int se_put(struct ast_storage *st, struct ast_storage_fileobject *fo, const char *exts)
+{
+	SQLHSTMT stmt = NULL;
+	struct odbc_storage_pvt *ost = (struct odbc_storage_pvt *) st->storage_pvt;
+	struct ast_storage_fileinst *fi, *mfi;
+	char sql[128], ext[16];
+	struct put_query_data qdata;
+	int fd, fdlen, mfd, mfdlen;
+	void *fdm, *mfdm;
+	int putret = -1;
+
+	if (!fo) {
+		ast_log(LOG_WARNING, "fileobject is null!\n");
+		return -1;
+	}
+
+	if (exts) {
+		char *ptr;
+		ast_copy_string(ext, exts, sizeof(ext));
+		ptr = strchr(ext, '|');
+		if (ptr) {
+			*ptr = '\0';
+		}
+	}
+
+	AST_RWLIST_RDLOCK(&fo->files);
+	AST_RWLIST_TRAVERSE(&fo->files, fi, list) {
+		if (!exts || !strcasecmp(fi->ext, ext)) {
+			break;
+		}
+	}
+	AST_RWLIST_UNLOCK(&fo->files);
+
+	mfi = fo->metadata;
+
+	if (!fi) {
+		ast_log(LOG_WARNING, "Unable to find file instance for put()\n");
+		return -1;
+	}
+
+	fdm = mfdm = NULL;
+
+	if (open_fileinst(fi, &fd, &fdlen, &fdm) == -1) {
+		return -1;
+	}
+	if (mfi) {
+		if (open_fileinst(mfi, &mfd, &mfdlen, &mfdm) == -1) {
+			goto error;
+		}
+	}
+
+	if (mfi) {
+		snprintf(sql, sizeof(sql), "INSERT INTO %s (pathname,filename,extension,recording) VALUES (?,?,?,?), (?,?,?,?)", ost->tablename);
+	}
+	else {
+		snprintf(sql, sizeof(sql), "INSERT INTO %s (pathname,filename,extension,recording) VALUES (?,?,?,?)", ost->tablename);
+	}
+
+	memset(&qdata, 0, sizeof(qdata));
+	qdata.sql = sql;
+	qdata.pathname = fo->pathname;
+	qdata.objectname = fo->objectname;
+	qdata.ext = fi->ext;
+	qdata.data = fdm;
+	qdata.data_len = qdata.data_ind = fdlen;
+	if (mfi) {
+		qdata.metadata = mfdm;
+		qdata.metadata_len = qdata.metadata_ind = mfdlen;
+	}
+
+	stmt = ast_odbc_direct_execute(ost->conn, execute_put_query, &qdata);
+	if (!stmt) {
+		ast_log(LOG_WARNING, "SQL Execute failed!\n");
+		goto error;
+	}
+
+	putret = 0;
+error:
+	if (stmt) {
+		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+	}
+	if (fdm) {

[... 686 lines stripped ...]



More information about the svn-commits mailing list