[asterisk-commits] tilghman: branch group/ast_storage r48122 - in /team/group/ast_storage: inclu...

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Wed Nov 29 18:29:35 MST 2006


Author: tilghman
Date: Wed Nov 29 19:29:34 2006
New Revision: 48122

URL: http://svn.digium.com/view/asterisk?view=rev&rev=48122
Log:
Lots of revisions on storage types

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

Modified: team/group/ast_storage/include/asterisk/storage.h
URL: http://svn.digium.com/view/asterisk/team/group/ast_storage/include/asterisk/storage.h?view=diff&rev=48122&r1=48121&r2=48122
==============================================================================
--- team/group/ast_storage/include/asterisk/storage.h (original)
+++ team/group/ast_storage/include/asterisk/storage.h Wed Nov 29 19:29:34 2006
@@ -31,34 +31,77 @@
 extern "C" {
 #endif
 
-struct ast_storage {
+struct ast_storage_be {
 	const char *name;
-	int type;
-	int (*get)(int blah);
-	int (*put)(int blah);
-	int (*delete)(int blah);
-	int (*count)(int blah);
-	int (*parseoptions)(const char *var, const char *value);
-	AST_LIST_ENTRY(ast_storage) list;
+	struct ast_storage *(*new)(const char *uri);
+	int (*free)(struct ast_storage *st);
+	int (*get)(struct ast_storage *st, const char *objectname, char *localfile, size_t localfilesize);
+	int (*put)(struct ast_storage *st, const char *objectname, const char *localfile);
+	int (*delete)(struct ast_storage *st, const char *objectname);
+	int (*count)(struct ast_storage *st, const char *objectpath);
+	int (*parseoptions)(struct ast_storage *st, const char *var, const char *value);
+	AST_LIST_ENTRY(ast_storage_be) list;
 	struct ast_module *module;
 };
 
-#define AST_STORAGE_FILE	(1 << 0)
-#define AST_STORAGE_ODBC	(1 << 1)
-#define AST_STORAGE_IMAP	(1 << 2)
+/* Most specific backends will have longer structures */
+struct ast_storage {
+	struct ast_storage_be *be;
+};
 
-int __ast_register_storage(const struct ast_storage *e, struct ast_module *mod);
+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)
 
 int ast_unregister_storage(const char *name);
 
 int ast_storage_engine_init(void);
 
-int se_get(struct ast_storage *e);
-int se_put(struct ast_storage *e);
-int se_delete(struct ast_storage *e);
-int se_count(struct ast_storage *e);
-int se_parseoptions(struct ast_storage *e, const char *var, const char *value);
+/*! \brief Retrieves an instance of a storage method
+ * \param storage_type Name of the backend
+ * \param root Backend specific parameter.  Generally is a hostname or path.
+ * \param auth Backend specific parameter.  Generally is the user in an authentication scheme.
+ * \param passwd Backend specific parameter.  Generally is the password in an authentication scheme.
+ * \param flags Backend specific parameter
+ */
+struct ast_storage *ast_storage_request(const char *storage_type, const char *uri);
+
+/*! \brief Releases a storage instance created with ast_storage_request
+ */
+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 localfile Buffer in which to place the name of the resulting temporary file.
+ * \param localfilesize Length of the above buffer
+ */
+int ast_storage_get(struct ast_storage *st, const char *objectname, char *localfile, size_t localfilesize);
+
+/*! \brief Stores a file into the storage medium
+ * \param st Storage instance
+ * \param objectname Logical name of the file
+ * \param localfile Location of the file to store
+ */
+int ast_storage_put(struct ast_storage *st, const char *objectname, const char *localfile);
+
+/*! \brief Removes a file from the storage medium
+ * \param st Storage instance
+ * \param objectname Logical name of the file
+ */
+int ast_storage_delete(struct ast_storage *st, const char *objectname);
+
+/*! \brief Retrieves a count of objects at a particular path
+ * \param st Storage instance
+ * \param objectname Logical name of the file
+ */
+int ast_storage_count(struct ast_storage *st, const char *objectname);
+
+/*! \brief Specify a backend specific parameter
+ * \param st Storage instance
+ * \param var Parameter name
+ * \param value Parameter value
+ */
+int ast_storage_parseoptions(struct ast_storage *st, const char *var, const char *value);
 
 #if defined(__cplusplus) || defined(c_plusplus)
 }

Modified: team/group/ast_storage/main/storage.c
URL: http://svn.digium.com/view/asterisk/team/group/ast_storage/main/storage.c?view=diff&rev=48122&r1=48121&r2=48122
==============================================================================
--- team/group/ast_storage/main/storage.c (original)
+++ team/group/ast_storage/main/storage.c Wed Nov 29 19:29:34 2006
@@ -33,11 +33,11 @@
 #include "asterisk/linkedlists.h"
 #include "asterisk/module.h"
 
-static AST_LIST_HEAD_STATIC(storage_engines, ast_storage);
+static AST_LIST_HEAD_STATIC(storage_engines, ast_storage_be);
 
-int __ast_register_storage(const struct ast_storage *e, struct ast_module *mod)
+int __ast_register_storage(const struct ast_storage_be *e, struct ast_module *mod)
 {
-	struct ast_storage *tmp;
+	struct ast_storage_be *tmp;
 
 	if (AST_LIST_LOCK(&storage_engines)) {
 		ast_log(LOG_WARNING, "Unable to lock storage engine list\n");
@@ -69,7 +69,7 @@
 
 int ast_unregister_storage(const char *name)
 {
-	struct ast_storage *tmp;
+	struct ast_storage_be *tmp;
 	int res = -1;
 
 	if (AST_LIST_LOCK(&storage_engines)) {
@@ -95,29 +95,50 @@
 	return res;
 }
 
-int ast_se_get(struct ast_storage *e) {
-	return e->get(0);
+struct ast_storage *ast_storage_request(const char *storage_type, const char *uri)
+{
+	struct ast_storage_be *e;
+
+	AST_LIST_TRAVERSE(&storage_engines, e, list) {
+		if (!strcasecmp(e->name, storage_type))
+			return e->new(uri);
+	}
+	return NULL;
 }
 
-int ast_se_put(struct ast_storage *e) {
-	return e->put(0);
+int ast_storage_release(struct ast_storage *st)
+{
+	return st && st->be->free ? st->be->free(st) : -1;
 }
 
-int ast_se_delete(struct ast_storage *e) {
-	return e->delete(0);
+int ast_storage_get(struct ast_storage *st, const char *objectname, char *localfile, size_t localfilesize)
+{
+	return st->be->get ? st->be->get(st, objectname, localfile, localfilesize) : -1;
 }
 
-int ast_se_count(struct ast_storage *e) {
-	return e->count(0);
+int ast_storage_put(struct ast_storage *st, const char *objectname, const char *localfile)
+{
+	return st->be->put ? st->be->put(st, objectname, localfile) : -1;
 }
 
-int ast_se_parseoptions(struct ast_storage *e, const char *var, const char *value) {
-	return e->parseoptions(var, value);
+int ast_storage_delete(struct ast_storage *st, const char *objectname)
+{
+	return st->be->delete ? st->be->delete(st, objectname) : -1;
+}
+
+int ast_storage_count(struct ast_storage *st, const char *objectpath)
+{
+	return st->be->count ? st->be->count(st, objectpath) : -1;
+}
+
+int ast_storage_parseoptions(struct ast_storage *st, const char *var, const char *value)
+{
+	return st->be->parseoptions ? st->be->parseoptions(st, var, value) : -1;
 }
 
 static int show_storage_engines(int fd, int argc, char *argv[])
 {
-	struct ast_storage *e;
+	struct ast_storage_be *e;
 	int count_se = 0;
 
 	if (argc != 4)
@@ -153,3 +174,4 @@
 	ast_cli_register_multiple(cli_storage, sizeof(cli_storage) / sizeof(struct ast_cli_entry));
 	return 0;
 }
+

Modified: team/group/ast_storage/res/res_storage_file.c
URL: http://svn.digium.com/view/asterisk/team/group/ast_storage/res/res_storage_file.c?view=diff&rev=48122&r1=48121&r2=48122
==============================================================================
--- team/group/ast_storage/res/res_storage_file.c (original)
+++ team/group/ast_storage/res/res_storage_file.c Wed Nov 29 19:29:34 2006
@@ -29,30 +29,146 @@
 
 #include "asterisk/storage.h"
 #include "asterisk/module.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <errno.h>
 
-static int se_get_file(int blah) {
+struct ast_storage_file {
+	const struct ast_storage_be *be;
+	char rootpath[128];
+};
+
+static const struct ast_storage_be file_se;
+
+static struct ast_storage *se_new_file(const char *uri)
+{
+	struct ast_storage_file *fst = malloc(sizeof(struct ast_storage_file));
+	ast_copy_string(fst->rootpath, uri + 6, sizeof(fst->rootpath));
+	fst->be = &file_se;
+	return ((struct ast_storage *)fst);
+}
+
+static int se_free_file(struct ast_storage *st)
+{
+	free(st);
 	return 0;
 }
 
-static int se_put_file(int blah) {
+static int se_get_file(struct ast_storage *st, const char *objectname, char *localfile, size_t localfilesize)
+{
+	struct ast_storage_file *fst = (struct ast_storage_file *)st;
+	char dirname[256], *dirnameptr;
+	DIR *dir;
+	struct dirent *dirent;
+	int res = -1;
+
+	snprintf(localfile, localfilesize, "%s/%s", fst->rootpath, objectname);
+
+	/* Ensure the file exists. */
+	/* NOTE: while this is similar to a function already in main/file.c,
+	 * this has to remain coded here, because the version in file.c will
+	 * eventually depend upon this one. */
+	ast_copy_string(dirname, localfile, sizeof(dirname));
+	dirnameptr = strrchr(dirname, '/');
+	if (dirnameptr)
+		*dirnameptr++ = '\0';
+	else
+		return -1;
+	dir = opendir(dirname);
+	while ((dirent = readdir(dir))) {
+		if (!strcmp(dirent->d_name, dirnameptr)) {
+			res = 0;
+			break;
+		}
+	}
+	closedir(dir);
+
+	return res;
+}
+
+static int se_put_file(struct ast_storage *st, const char *objectname, const char *localfile)
+{
+	struct ast_storage_file *fst = (struct ast_storage_file *)st;
+	int ifd;
+	int ofd;
+	int res = 0;
+	int len;
+	char dest[256];
+	char buf[4096];
+
+	snprintf(dest, sizeof(dest), "%s/%s", fst->rootpath, objectname);
+
+	if ((ifd = open(localfile, O_RDONLY)) < 0) {
+		ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", localfile);
+		return -1;
+	}
+	if ((ofd = open(dest, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0) {
+		ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", dest);
+		close(ifd);
+		return -1;
+	}
+	do {
+		len = read(ifd, buf, sizeof(buf));
+		if (len < 0) {
+			ast_log(LOG_WARNING, "Read failed on %s: %s\n", localfile, strerror(errno));
+			close(ifd);
+			close(ofd);
+			unlink(dest);
+		}
+		if (len) {
+			res = write(ofd, buf, len);
+			if (errno == ENOMEM || errno == ENOSPC || res != len) {
+				ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", dest, res, len, strerror(errno));
+				close(ifd);
+				close(ofd);
+				unlink(dest);
+			}
+		}
+	} while (len);
+	close(ifd);
+	close(ofd);
+	return res;
+}
+
+static int se_delete_file(struct ast_storage *st, const char *objectname)
+{
+	struct ast_storage_file *fst = (struct ast_storage_file *)st;
+	char dest[256];
+	snprintf(dest, sizeof(dest), "%s/%s", fst->rootpath, objectname);
+	return unlink(dest);
+}
+
+static int se_count_file(struct ast_storage *st, const char *objectpath)
+{
+	struct ast_storage_file *fst = (struct ast_storage_file *)st;
+	char dest[256];
+	DIR *dir;
+	struct dirent *dirent;
+	int count = 0;
+	snprintf(dest, sizeof(dest), "%s/%s", fst->rootpath, objectpath);
+	dir = opendir(dest);
+	while ((dirent = readdir(dir))) {
+		/* Probably not exactly correct */
+		if (strncmp(dirent->d_name, ".", 1) != 0)
+			count++;
+	}
+	closedir(dir);
+
 	return 0;
 }
 
-static int se_delete_file(int blah) {
+static int se_parseoptions_file(struct ast_storage *st, const char *var, const char *value)
+{
 	return 0;
 }
 
-static int se_count_file(int blah) {
-	return 0;
-}
-
-static int se_parseoptions_file(const char *var, const char *value) {
-	return 0;
-}
-
-static const struct ast_storage file_se = {
+static const struct ast_storage_be file_se = {
 	.name = "file",
-	.type = AST_STORAGE_FILE,
+	.new = se_new_file,
+	.free = se_free_file,
 	.get = se_get_file,
 	.put = se_put_file,
 	.delete = se_delete_file,
@@ -62,12 +178,12 @@
 
 static int load_module(void)
 {
-        return ast_register_storage(&file_se);
+	return ast_register_storage(&file_se);
 }
 
 static int unload_module(void)
 {
-        return ast_unregister_storage(file_se.name);
+	return ast_unregister_storage(file_se.name);
 }
 
 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Files based storage engine");

Added: team/group/ast_storage/res/res_storage_odbc.c
URL: http://svn.digium.com/view/asterisk/team/group/ast_storage/res/res_storage_odbc.c?view=auto&rev=48122
==============================================================================
--- team/group/ast_storage/res/res_storage_odbc.c (added)
+++ team/group/ast_storage/res/res_storage_odbc.c Wed Nov 29 19:29:34 2006
@@ -1,0 +1,522 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2006, Tilghman Lesher
+ *
+ * Tilghman Lesher <res_storage_odbc__v001 at the-tilghman.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 based storage engine
+ * \author Tilghman Lesher <res_storage_odbc__v001 at the-tilghman.com>
+ * \ingroup res
+ */
+ 
+#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 <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>
+
+/* Number of bytes to transfer when moving a large field */
+#define	CHUNKSIZE	65536
+
+struct ast_storage_odbc {
+	const struct ast_storage_be *be;
+	char odbc_class[128];
+	char tablename[128];
+	char pathname[128];
+	struct odbc_obj *conn;
+	SQLHSTMT sth;
+};
+
+static const struct ast_storage_be odbc_se;
+
+static void copy_file(const char *from, const char *to)
+{
+	int ifd;
+	int ofd;
+	int res = 0;
+	int len;
+	char buf[4096];
+
+	if ((ifd = open(from, O_RDONLY)) < 0) {
+		ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", from);
+		return;
+	}
+	if ((ofd = open(to, O_WRONLY | O_TRUNC | O_CREAT, 0666)) < 0) {
+		ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", to);
+		close(ifd);
+		return;
+	}
+	do {
+		len = read(ifd, buf, sizeof(buf));
+		if (len < 0) {
+			ast_log(LOG_WARNING, "Read failed on %s: %s\n", from, strerror(errno));
+			close(ifd);
+			close(ofd);
+			unlink(to);
+		}
+		if (len) {
+			res = write(ofd, buf, len);
+			if (errno == ENOMEM || errno == ENOSPC || res != len) {
+				ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", to, res, len, strerror(errno));
+				close(ifd);
+				close(ofd);
+				unlink(to);
+			}
+		}
+	} while (len);
+	close(ifd);
+	close(ofd);
+	return;
+}
+
+static struct ast_storage *se_new_odbc(const char *uri)
+{
+	char tmp[256];
+	struct ast_storage_odbc *ost = ast_calloc(1, sizeof(struct ast_storage_odbc));
+	AST_DECLARE_APP_ARGS(args,
+		AST_APP_ARG(class);
+		AST_APP_ARG(tablename);
+		AST_APP_ARG(pathname);
+	);
+
+	/* odbc:// */
+	ast_copy_string(tmp, uri + 7, sizeof(tmp));
+	AST_NONSTANDARD_APP_ARGS(args, tmp, '/');
+
+	ost->be = &odbc_se;
+	ast_copy_string(ost->odbc_class, args.class, sizeof(ost->odbc_class));
+	ast_copy_string(ost->tablename, args.tablename, sizeof(ost->tablename));
+	ast_copy_string(ost->pathname, args.pathname, sizeof(ost->pathname));
+	ost->conn = ast_odbc_request_obj(ost->odbc_class, 0);
+
+	if (!ost->conn) {
+		free(ost);
+		return NULL;
+	}
+
+	return ((struct ast_storage *)ost);
+}
+
+static int se_free_odbc(struct ast_storage *st)
+{
+	struct ast_storage_odbc *ost = (struct ast_storage_odbc *)st;
+	if (ost->sth)
+		SQLFreeHandle(SQL_HANDLE_STMT, ost->sth);
+	ast_odbc_release_obj(ost->conn);
+	free(ost);
+	return 0;
+}
+
+static void get_path_file(struct ast_storage_odbc *ost, const char *objectname, char *pathname, size_t pathnamesize, char *filename, size_t filenamesize)
+{
+	char tmp[256], *filename2;
+
+	ast_copy_string(tmp, objectname, sizeof(tmp));
+	if ((filename2 = strrchr(tmp, '/'))) {
+		if (filename2 == tmp) {
+			ast_copy_string(pathname, "/", pathnamesize);
+			*filename2++ = '\0';
+		} else {
+			*filename2++ = '\0';
+
+			/* Absolute or relative path? */
+			if (tmp[0] == '/')
+				ast_copy_string(pathname, tmp, sizeof(pathname));
+			else
+				snprintf(pathname, sizeof(pathname), "%s/%s", ost->pathname, tmp);
+		}
+	} else {
+		ast_copy_string(pathname, ost->pathname, sizeof(pathname));
+		filename2 = tmp;
+	}
+	ast_copy_string(filename, filename2, filenamesize);
+}
+
+static int se_get_odbc(struct ast_storage *st, const char *objectname, char *localfile, size_t localfilesize)
+{
+	struct ast_storage_odbc *ost = (struct ast_storage_odbc *)st;
+	int res = -1, tempfd, x, gotten = 0;
+	char sql[128];
+	char pathname[256], filename[256], reallocalfile[256];
+	char fileformat[10], rowdata[256], coltitle[128];
+	SQLHSTMT stmt;
+	SQLSMALLINT colcount, collen, datatype;
+	FILE *txt;
+
+	ast_copy_string(localfile, "/var/tmp/XXXXXX", localfilesize);
+	tempfd = mkstemp(localfile);
+	if (tempfd == -1)
+		return -1;
+
+	snprintf(reallocalfile, sizeof(reallocalfile), "%s.txt", localfile);
+	txt = fopen(reallocalfile, "w");
+	if (!txt) {
+		close(tempfd);
+		unlink(localfile);
+		return -1;
+	}
+
+	fprintf(txt, "[info]\n");
+
+	get_path_file(ost, objectname, pathname, sizeof(pathname), filename, sizeof(filename));
+
+	snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE pathname=? AND filename=?", ost->tablename);
+
+	res = SQLAllocHandle(SQL_HANDLE_STMT, ost->conn->con, &stmt);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+		fclose(txt);
+		close(tempfd);
+		unlink(localfile);
+		return -1;
+	}
+
+	res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+		goto endget;
+	}
+
+	SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(pathname), 0, (void *)pathname, 0, NULL);
+	SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(filename), 0, (void *)filename, 0, NULL);
+
+	res = ast_odbc_smart_execute(ost->conn, stmt);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n", sql);
+		goto endget;
+	}
+
+	while ((res = SQLFetch(stmt)) != SQL_NO_DATA) {
+		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+			ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n", sql);
+			goto endget;
+		}
+
+		res = SQLNumResultCols(stmt, &colcount);
+		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {	
+			ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n", sql);
+			goto endget;
+		}
+
+		fileformat[0] = '\0';
+		for (x = 0; x < colcount; x++) {
+			int fd = -1;
+			SQLUINTEGER colsize;
+			SQLSMALLINT decimaldigits, nullable;
+			rowdata[0] = '\0';
+			collen = sizeof(coltitle);
+			res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
+						&datatype, &colsize, &decimaldigits, &nullable);
+
+			if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+				ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n", sql);
+				goto endget;
+			}
+
+			if (!strcasecmp(coltitle, "recording")) {
+				int fdlen;
+				off_t chunk_offset;
+				void *fdm = NULL;
+				SQLINTEGER colsize2;
+
+				if (!ast_strlen_zero(fileformat)) {
+					/* We have a file format, just go ahead and create the destination */
+					snprintf(reallocalfile, sizeof(reallocalfile), "%s.%s", localfile, fileformat);
+					fd = open(reallocalfile, O_CREAT | O_WRONLY | O_EXCL);
+					if (fd == -1) {
+						ast_log(LOG_ERROR, "Unable to write recording: %s (%d)\n", strerror(errno), errno);
+						goto endget;
+					}
+				} else {
+					fd = tempfd;
+					truncate(localfile, 0);
+					lseek(fd, 0, SEEK_SET);
+				}
+
+				res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize2);
+				fdlen = colsize2;
+				if (fd > -1) {
+					char tmp[1]="";
+					lseek(fd, fdlen - 1, SEEK_SET);
+					if (write(fd, tmp, 1) != 1) {
+						if (fd != tempfd)
+							close(fd);
+						fd = -1;
+						continue;
+					}
+				}
+
+				/* Grab data in 64k chunks */
+				for (chunk_offset = 0; chunk_offset < fdlen; chunk_offset += CHUNKSIZE) {
+					/* Add one to the CHUNKSIZE, because ODBC always adds a terminating NULL */
+					if ((fdm = mmap(NULL, CHUNKSIZE + 1, PROT_READ | PROT_WRITE, MAP_SHARED, fd, chunk_offset)) == (void *)-1) {
+						ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
+						goto endget;
+					} else {
+						res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE + 1, NULL);
+						munmap(fdm, 0);
+						if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+							ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+							unlink(reallocalfile);
+							goto endget;
+						}
+					}
+				}
+				/* Ensure the file is exactly the right length */
+				truncate(reallocalfile, fdlen);
+			} else {
+				res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+				if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+					ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+					goto endget;
+				}
+				if (!strcasecmp(coltitle, "fileformat")) {
+					/* Is there a recording waiting for us? */
+					if (lseek(tempfd, 0, SEEK_CUR) > 0) {
+						snprintf(reallocalfile, sizeof(reallocalfile), "%s.%s", localfile, rowdata);
+						copy_file(localfile, reallocalfile);
+						lseek(tempfd, 0, SEEK_SET);
+						truncate(localfile, 0);
+					} else
+						ast_copy_string(fileformat, rowdata, sizeof(fileformat));
+				} else if (strcasecmp(coltitle, "filename") && strcasecmp(coltitle, "pathname") && !ast_strlen_zero(rowdata) && !gotten)
+					fprintf(txt, "%s=%s\n", coltitle, rowdata);
+			}
+			if (fd > -1 && fd != tempfd)
+				close(fd);
+		}
+		gotten = 1;
+	}
+	res = 0;
+endget:
+	SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+	fclose(txt);
+	close(tempfd);
+	/* This is just the temp marker */
+	unlink(localfile);
+
+	return res ? -1 : 0;
+}
+
+static int se_put_odbc(struct ast_storage *st, const char *objectname, const char *localfile)
+{
+	struct ast_storage_odbc *ost = (struct ast_storage_odbc *)st;
+	int res = -1, x;
+	struct ast_config *cfg;
+	struct ast_variable *cfgvar, *var;
+	char sql[768], sqlfields[256] = ") VALUES (?,?,?,?";
+	char needlong[10] = "";
+	char pathname[256], *localfile2, reallocalfile[256];
+	char *fileformat;
+	SQLHSTMT stmt = NULL;
+	DIR *dir;
+	struct dirent *dirent;
+
+	snprintf(reallocalfile, sizeof(reallocalfile), "%s.txt", localfile);
+	cfg = ast_config_load(reallocalfile);
+	cfgvar = ast_variable_browse(cfg, "info");
+
+	snprintf(sql, sizeof(sql), "INSERT INTO %s (pathname,filename,recording,fileformat", ost->tablename);
+
+	for (var = cfgvar; var; var = var->next) {
+		strncat(sql, var->name, sizeof(sql) - 1);
+		strncat(sql, ",", sizeof(sql) - 1);
+		strncat(sqlfields, ",?", sizeof(sqlfields) - 1);
+	}
+	strncat(sql, sqlfields, sizeof(sql) - 1);
+	strncat(sql, ")", sizeof(sql) - 1);
+
+	if ((strlen(sqlfields) == sizeof(sqlfields) - 1) || (strlen(sql) == sizeof(sql) - 1)) {
+		ast_log(LOG_ERROR, "Data overflow.  Increase SQL buffer size in se_put_odbc() (%d/%d)\n", strlen(sqlfields), strlen(sql));
+		ast_config_destroy(cfg);
+		return -1;
+	}
+
+	SQLGetInfo(ost->conn->con, SQL_NEED_LONG_DATA_LEN, (SQLPOINTER)&needlong, sizeof(needlong), NULL);
+
+	ast_copy_string(pathname, localfile, sizeof(pathname));
+	if ((localfile2 = strrchr(pathname, '/')))
+		*localfile2++ = '\0';
+	else {
+		ast_log(LOG_ERROR, "Specified file is not absolute path: %s\n", localfile);
+		ast_config_destroy(cfg);
+		return -1;
+	}
+
+	dir = opendir(pathname);
+	if (!dir) {
+		ast_log(LOG_ERROR, "Could not read directory %s: %s (%d)\n", pathname, strerror(errno), errno);
+		ast_config_destroy(cfg);
+		return -1;
+	}
+
+	while ((dirent = readdir(dir))) {
+		int fd = -1;
+		SQLINTEGER filesize;
+		off_t offset;
+
+		if (strcmp(dirent->d_name, localfile2) != 0)
+			continue;
+
+		if (!(fileformat = strrchr(dirent->d_name, '.')))
+			continue;
+		fileformat++;
+
+		snprintf(reallocalfile, sizeof(reallocalfile), "%s.%s", pathname, dirent->d_name);
+		fd = open(reallocalfile, O_RDONLY);
+
+		if (fd == -1) {
+			ast_log(LOG_WARNING, "Could not open file %s: %s (%d)\n", reallocalfile, strerror(errno), errno);
+			continue;
+		}
+
+		res = SQLAllocHandle(SQL_HANDLE_STMT, ost->conn->con, &stmt);
+		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+			ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+			ast_config_destroy(cfg);
+			return -1;
+		}
+
+		res = SQLPrepare(stmt, (unsigned char *)sql, SQL_NTS);
+		if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+			ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+			goto endput;
+		}
+
+		filesize = (SQLINTEGER)lseek(fd, 0, SEEK_END);
+
+		SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(pathname), 0, (void *)pathname, 0, NULL);
+		SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(localfile2), 0, (void *)localfile2, 0, NULL);
+		if (needlong[0] == 'Y') {
+			SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, 0, 0, (SQLPOINTER)3, 0, &filesize);
+		} else
+			SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, 0, 0, (SQLPOINTER)3, 0, NULL);
+		SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(fileformat), 0, (void *)fileformat, 0, NULL);
+		for (var = cfgvar, x = 5; var; var = var->next, x++) {
+			SQLBindParameter(stmt, x, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(var->value), 0, (void *)var->value, 0, NULL);
+		}
+
+		res = SQLExecute(stmt);
+
+		if (res == SQL_NEED_DATA) {
+			for (offset = 0; offset < filesize; offset += CHUNKSIZE) {
+				void *fdm = mmap(NULL, CHUNKSIZE + 1, PROT_READ, MAP_SHARED, fd, offset);
+				if (fdm == (void *)-1) {
+					ast_log(LOG_ERROR, "Unable to mmap file %s: %s (%d)\n", reallocalfile, strerror(errno), errno);
+					goto endput;
+				}
+				SQLPutData(stmt, fdm, CHUNKSIZE);
+				munmap(fdm, 0);
+			}
+		} else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+			ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n", sql);
+			goto endput;
+		}
+
+		SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+		stmt = NULL;
+	}
+	res = 0;
+endput:
+	if (stmt)
+		SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+	ast_config_destroy(cfg);
+	return res ? -1 : 0;
+}
+
+static int se_delete_odbc(struct ast_storage *st, const char *objectname)
+{
+	char pathname[256], filename[256], sql[256];
+	struct ast_storage_odbc *ost = (struct ast_storage_odbc *)st;
+	int res;
+	SQLHSTMT sth;
+
+	get_path_file(ost, objectname, pathname, sizeof(pathname), filename, sizeof(filename));
+
+	snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE pathname=? AND filename=?", ost->tablename);
+
+	res = SQLAllocHandle(SQL_HANDLE_STMT, ost->conn->con, &sth);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
+		return -1;
+	}
+
+	res = SQLPrepare(sth, (unsigned char *)sql, SQL_NTS);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
+		goto enddel;
+	}
+
+	SQLBindParameter(sth, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(pathname), 0, (void *)pathname, 0, NULL);
+	SQLBindParameter(sth, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(filename), 0, (void *)filename, 0, NULL);
+
+	res = ast_odbc_smart_execute(ost->conn, sth);
+	if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+		ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n", sql);
+		goto enddel;
+	}
+	res = 0;
+enddel:
+	SQLFreeHandle(SQL_HANDLE_STMT, sth);
+
+	return res ? -1 : 0;
+}
+
+static int se_count_odbc(struct ast_storage *st, const char *objectpath)
+{
+	return -1;
+}
+
+static int se_parseoptions_odbc(struct ast_storage *st, const char *var, const char *value)
+{
+	return 0;
+}
+
+static const struct ast_storage_be odbc_se = {
+	.name = "odbc",
+	.new = se_new_odbc,
+	.free = se_free_odbc,
+	.get = se_get_odbc,
+	.put = se_put_odbc,
+	.delete = se_delete_odbc,
+	.count = se_count_odbc,
+	.parseoptions = se_parseoptions_odbc,
+};
+
+static int load_module(void)
+{
+	return ast_register_storage(&odbc_se);
+}
+
+static int unload_module(void)
+{
+	return ast_unregister_storage(odbc_se.name);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "ODBC based storage engine");

Propchange: team/group/ast_storage/res/res_storage_odbc.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: team/group/ast_storage/res/res_storage_odbc.c
------------------------------------------------------------------------------
    svn:keywords = Author Date Id Revision

Propchange: team/group/ast_storage/res/res_storage_odbc.c
------------------------------------------------------------------------------
    svn:mime-type = text/plain



More information about the asterisk-commits mailing list