static int init_mailstream(struct vm_state *vms, int box);
static void write_file(char *filename, char *buffer, unsigned long len);
static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
-static void vm_imap_delete(int msgnum, struct vm_state *vms);
+static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu);
static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
-static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu, char *mailbox);
+static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
static void vmstate_insert(struct vm_state *vms);
static void vmstate_delete(struct vm_state *vms);
static void set_update(MAILSTREAM * stream);
static void init_vm_state(struct vm_state *vms);
-static void check_msgArray(struct vm_state *vms);
-static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
-static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, char *altfile);
-static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num, char *prefix);
+static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
static void get_mailbox_delimiter(MAILSTREAM *stream);
static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
-static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, char *introfile, const char *flag);
+static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
static void update_messages_by_imapuser(const char *user, unsigned long number);
static int imap_remove_file (char *dir, int msgnum);
-static int imap_retrieve_file (char *dir, int msgnum, const char *mailbox, const char *context);
+static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
static void check_quota(struct vm_state *vms, char *mailbox);
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
int vmArrayIndex;
char imapuser[80]; /*!< IMAP server login */
int interactive;
+ char introfn[PATH_MAX]; /*!< Name of prepended file */
unsigned int quota_limit;
unsigned int quota_usage;
struct vm_state *persist_vms;
static char odbc_table[80];
#define RETRIEVE(a,b,c,d) retrieve_file(a,b)
#define DISPOSE(a,b) remove_file(a,b)
-#define STORE(a,b,c,d,e,f,g,h,i,j,k) store_file(a,b,c,d)
+#define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
#define EXISTS(a,b,c,d) (message_exists(a,b))
#define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
#define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
-#define DELETE(a,b,c) (delete_file(a,b))
+#define DELETE(a,b,c,d) (delete_file(a,b))
#else
#ifdef IMAP_STORAGE
-#define RETRIEVE(a,b,c,d) (imap_retrieve_file(a,b,c,d ))
#define DISPOSE(a,b) (imap_remove_file(a,b))
-#define STORE(a,b,c,d,e,f,g,h,i,j,k) (imap_store_file(a,b,c,d,e,f,g,h,i,j,k))
+#define STORE(a,b,c,d,e,f,g,h,i,j) (imap_store_file(a,b,c,d,e,f,g,h,i,j))
+#define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
#define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
-#define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
-#define DELETE(a,b,c) (vm_delete(c))
+#define DELETE(a,b,c,d) (vm_imap_delete(b,d))
#else
#define RETRIEVE(a,b,c,d)
#define DISPOSE(a,b)
-#define STORE(a,b,c,d,e,f,g,h,i,j,k)
+#define STORE(a,b,c,d,e,f,g,h,i,j)
#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
#define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
-#define DELETE(a,b,c) (vm_delete(c))
+#define DELETE(a,b,c,d) (vm_delete(c))
#endif
#endif
return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
}
-#ifdef IMAP_STORAGE
-static int make_gsm_file(char *dest, size_t len, char *imapuser, char *dir, int num, char *prefix)
+/*!
+ * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
+ * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
+ * \param len The length of the path string that was written out.
+ *
+ * The path is constructed as
+ * VM_SPOOL_DIRcontext/ext/folder
+ *
+ * \return zero on success, -1 on error.
+ */
+static int make_file(char *dest, const int len, const char *dir, const int num)
{
- int res;
- if ((res = ast_mkdir(dir, 01777))) {
- ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dir, strerror(res));
- return snprintf(dest, len, "%s/%s%04d", dir, prefix, num);
- }
- return snprintf(dest, len, "%s/%s%04d", dir, prefix, num);
+ return snprintf(dest, len, "%s/msg%04d", dir, num);
}
-static void vm_imap_delete(int msgnum, struct vm_state *vms)
+/* same as mkstemp, but return a FILE * */
+static FILE *vm_mkftemp(char *template)
{
- unsigned long messageNum = 0;
- char arg[10];
-
- /* find real message number based on msgnum */
- /* this may be an index into vms->msgArray based on the msgnum. */
-
- messageNum = vms->msgArray[msgnum];
- if (messageNum == 0) {
- ast_log(AST_LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
- return;
+ FILE *p = NULL;
+ int pfd = mkstemp(template);
+ chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
+ if (pfd > -1) {
+ p = fdopen(pfd, "w+");
+ if (!p) {
+ close(pfd);
+ pfd = -1;
+ }
}
- ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
- /* delete message */
- snprintf (arg, sizeof(arg), "%lu",messageNum);
- mail_setflag (vms->mailstream,arg,"\\DELETED");
-}
-
-#endif
-static int make_file(char *dest, int len, char *dir, int num)
-{
- return snprintf(dest, len, "%s/msg%04d", dir, num);
+ return p;
}
/*! \brief basically mkdir -p $dest/$context/$ext/$folder
return 0;
}
-/*! \brief Lock file path
- only return failure if ast_lock_path returns 'timeout',
- not if the path does not exist or any other reason
-*/
-static int vm_lock_path(const char *path)
+static const char *mbox(int id)
{
- switch (ast_lock_path(path)) {
- case AST_LOCK_TIMEOUT:
- return -1;
- default:
- return 0;
- }
+ static const char *msgs[] = {
+#ifdef IMAP_STORAGE
+ imapfolder,
+#else
+ "INBOX",
+#endif
+ "Old",
+ "Work",
+ "Family",
+ "Friends",
+ "Cust1",
+ "Cust2",
+ "Cust3",
+ "Cust4",
+ "Cust5",
+ "Deleted",
+ "Urgent"
+ };
+ return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
}
+static void free_user(struct ast_vm_user *vmu)
+{
+ if (ast_test_flag(vmu, VM_ALLOCED))
+ ast_free(vmu);
+}
-#ifdef ODBC_STORAGE
-struct generic_prepare_struct {
- char *sql;
- int argc;
- char **argv;
-};
-
-static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
+/* All IMAP-specific functions should go in this block. This
+ * keeps them from being spread out all over the code */
+#ifdef IMAP_STORAGE
+static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu)
{
- struct generic_prepare_struct *gps = data;
- int res, i;
- SQLHSTMT stmt;
+ char arg[10];
+ struct vm_state *vms;
+ unsigned long messageNum;
- res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
- return NULL;
+ /* Greetings aren't stored in IMAP, so we can't delete them there */
+ if (msgnum < 0) {
+ return;
}
- res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- return NULL;
+
+ if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, 0))) {
+ ast_log(LOG_WARNING, "Couldn't find a vm_state for mailbox %s. Unable to set \\DELETED flag for message %d\n", vmu->mailbox, msgnum);
+ return;
}
- for (i = 0; i < gps->argc; i++)
- SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
- return stmt;
+ /* find real message number based on msgnum */
+ /* this may be an index into vms->msgArray based on the msgnum. */
+ messageNum = vms->msgArray[msgnum];
+ if (messageNum == 0) {
+ ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
+ return;
+ }
+ if (option_debug > 2)
+ ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
+ /* delete message */
+ snprintf (arg, sizeof(arg), "%lu",messageNum);
+ mail_setflag (vms->mailstream,arg,"\\DELETED");
}
-/*!
- * \brief Retrieves a file from an ODBC data store.
- * \param dir the path to the file to be retreived.
- * \param msgnum the message number, such as within a mailbox folder.
- *
- * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
- * The purpose is to get the message from the database store to the local file system, so that the message may be played, or the information file may be read.
- *
- * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
- * The output is the message information file with the name msgnum and the extension .txt
- * and the message file with the extension of its format, in the directory with base file name of the msgnum.
- *
- * \return 0 on success, -1 on error.
- */
-static int retrieve_file(char *dir, int msgnum)
+static int imap_retrieve_greeting (const char *dir, const int msgnum, struct ast_vm_user *vmu)
{
- int x = 0;
- int res;
- int fd=-1;
- size_t fdlen = 0;
- void *fdm = MAP_FAILED;
- SQLSMALLINT colcount=0;
- SQLHSTMT stmt;
- char sql[PATH_MAX];
- char fmt[80]="";
- char *c;
- char coltitle[256];
- SQLSMALLINT collen;
- SQLSMALLINT datatype;
- SQLSMALLINT decimaldigits;
- SQLSMALLINT nullable;
- SQLULEN colsize;
- SQLLEN colsize2;
- FILE *f=NULL;
- char rowdata[80];
- char fn[PATH_MAX];
- char full_fn[PATH_MAX];
- char msgnums[80];
- char *argv[] = { dir, msgnums };
- struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
+ struct vm_state *vms_p;
+ char *file, *filename;
+ char *attachment;
+ int ret = 0, i;
+ BODY *body;
- struct odbc_obj *obj;
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- ast_copy_string(fmt, vmfmts, sizeof(fmt));
- c = strchr(fmt, '|');
- if (c)
- *c = '\0';
- if (!strcasecmp(fmt, "wav49"))
- strcpy(fmt, "WAV");
- snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
- if (msgnum > -1)
- make_file(fn, sizeof(fn), dir, msgnum);
- else
- ast_copy_string(fn, dir, sizeof(fn));
+ /* This function is only used for retrieval of IMAP greetings
+ * regular messages are not retrieved this way, nor are greetings
+ * if they are stored locally*/
+ if (msgnum > -1 || !imapgreetings) {
+ return 0;
+ } else {
+ file = strrchr(ast_strdupa(dir), '/');
+ if (file)
+ *file++ = '\0';
+ else {
+ ast_debug (1, "Failed to procure file name from directory passed.\n");
+ return -1;
+ }
+ }
- /* Create the information file */
- snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
-
- if (!(f = fopen(full_fn, "w+"))) {
- ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
- goto yuck;
+ /* check if someone is accessing this box right now... */
+ if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, 1)) ||!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, 0))) {
+ ast_log(AST_LOG_ERROR, "Voicemail state not found!\n");
+ return -1;
+ }
+
+ /* Greetings will never have a prepended message */
+ *vms_p->introfn = '\0';
+
+ ret = init_mailstream(vms_p, GREETINGS_FOLDER);
+ if (!vms_p->mailstream) {
+ ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
+ return -1;
+ }
+
+ /*XXX Yuck, this could probably be done a lot better */
+ for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
+ mail_fetchstructure(vms_p->mailstream, i + 1, &body);
+ /* We have the body, now we extract the file name of the first attachment. */
+ if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
+ attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
+ } else {
+ ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
+ return -1;
}
-
- snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
- snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt) {
- ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- ast_odbc_release_obj(obj);
- goto yuck;
+ filename = strsep(&attachment, ".");
+ if (!strcmp(filename, file)) {
+ ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
+ vms_p->msgArray[vms_p->curmsg] = i + 1;
+ save_body(body, vms_p, "2", attachment, 0);
+ return 0;
}
- res = SQLFetch(stmt);
- if (res == SQL_NO_DATA) {
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
+ }
+
+ return -1;
+}
+
+static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
+{
+ BODY *body;
+ char *header_content;
+ char *attachedfilefmt;
+ char buf[80];
+ struct vm_state *vms;
+ char text_file[PATH_MAX];
+ FILE *text_file_ptr;
+ int res = 0;
+ struct ast_vm_user *vmu;
+
+ if (!(vmu = find_user(NULL, context, mailbox))) {
+ ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
+ return -1;
+ }
+
+ if (msgnum < 0) {
+ if (imapgreetings) {
+ res = imap_retrieve_greeting(dir, msgnum, vmu);
+ goto exit;
+ } else {
+ res = 0;
+ goto exit;
}
- fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
- if (fd < 0) {
- ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLNumResultCols(stmt, &colcount);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- if (f)
- fprintf(f, "[message]\n");
- for (x=0;x<colcount;x++) {
- 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(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- if (!strcasecmp(coltitle, "recording")) {
- off_t offset;
- res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
- fdlen = colsize2;
- if (fd > -1) {
- char tmp[1]="";
- lseek(fd, fdlen - 1, SEEK_SET);
- if (write(fd, tmp, 1) != 1) {
- close(fd);
- fd = -1;
- continue;
- }
- /* Read out in small chunks */
- for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
- if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
- ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- } else {
- res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
- munmap(fdm, CHUNKSIZE);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
- unlink(full_fn);
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- }
- }
- truncate(full_fn, fdlen);
- }
- } else {
- res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
- fprintf(f, "%s=%s\n", coltitle, rowdata);
- }
- }
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- } else
- ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
- if (f)
- fclose(f);
- if (fd > -1)
- close(fd);
- return x - 1;
-}
+ }
-/*!
- * \brief Removes a voicemail message file.
- * \param dir the path to the message file.
- * \param msgnum the unique number for the message within the mailbox.
- *
- * Removes the message content file and the information file.
- * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
- * Typical use is to clean up after a RETRIEVE operation.
- * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
- * \return zero on success, -1 on error.
- */
-static int remove_file(char *dir, int msgnum)
-{
- char fn[PATH_MAX];
- char full_fn[PATH_MAX];
- char msgnums[80];
+ /* Before anything can happen, we need a vm_state so that we can
+ * actually access the imap server through the vms->mailstream
+ */
+ if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, 0))) {
+ /* This should not happen. If it does, then I guess we'd
+ * need to create the vm_state, extract which mailbox to
+ * open, and then set up the msgArray so that the correct
+ * IMAP message could be accessed. If I have seen correctly
+ * though, the vms should be obtainable from the vmstates list
+ * and should have its msgArray properly set up.
+ */
+ ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
+ res = -1;
+ goto exit;
+ }
- if (msgnum > -1) {
- snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
- make_file(fn, sizeof(fn), dir, msgnum);
- } else
- ast_copy_string(fn, dir, sizeof(fn));
- ast_filedelete(fn, NULL);
- if (ast_check_realtime("voicemail_data")) {
- ast_destroy_realtime("voicemail_data", "filename", fn, SENTINEL);
+ make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
+ snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
+
+ /* Don't try to retrieve a message from IMAP if it already is on the file system */
+ if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
+ res = 0;
+ goto exit;
}
- snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
- unlink(full_fn);
- return 0;
+
+ if (option_debug > 2)
+ ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
+ if (vms->msgArray[msgnum] == 0) {
+ ast_log (LOG_WARNING,"Trying to access unknown message\n");
+ res = -1;
+ goto exit;
+ }
+
+ /* This will only work for new messages... */
+ header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
+ /* empty string means no valid header */
+ if (ast_strlen_zero(header_content)) {
+ ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
+ res = -1;
+ goto exit;
+ }
+
+ mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
+
+ /* We have the body, now we extract the file name of the first attachment. */
+ if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
+ attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
+ ast_log(LOG_NOTICE, "%s is the attached file\n", attachedfilefmt);
+ } else {
+ ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
+ res = -1;
+ goto exit;
+ }
+
+ /* Find the format of the attached file */
+
+ strsep(&attachedfilefmt, ".");
+ if (!attachedfilefmt) {
+ ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
+ res = -1;
+ goto exit;
+ }
+
+ save_body(body, vms, "2", attachedfilefmt, 0);
+ if (save_body(body, vms, "3", attachedfilefmt, 1)) {
+ ast_log(LOG_NOTICE, "Nulling the introfn cuz ain't nothing in part 3\n");
+ *vms->introfn = '\0';
+ }
+
+ /* Get info from headers!! */
+ snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
+
+ if (!(text_file_ptr = fopen(text_file, "w"))) {
+ ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
+ }
+
+ fprintf(text_file_ptr, "%s\n", "[message]");
+
+ get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
+ fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
+ get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
+ fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
+ get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
+ fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
+ get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
+ fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
+ get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
+ fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
+ get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
+ fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
+ fclose(text_file_ptr);
+
+exit:
+ free_user(vmu);
+ return res;
}
-/*!
- * \brief Determines the highest message number in use for a given user and mailbox folder.
- * \param vmu
- * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
- *
- * This method is used when mailboxes are stored in an ODBC back end.
- * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
- *
- * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
- */
-static int last_message_index(struct ast_vm_user *vmu, char *dir)
+static int folder_int(const char *folder)
{
- int x = 0;
- int res;
- SQLHSTMT stmt;
- char sql[PATH_MAX];
- char rowdata[20];
- char *argv[] = { dir };
- struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
-
- struct odbc_obj *obj;
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt) {
- ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLFetch(stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- if (sscanf(rowdata, "%d", &x) != 1)
- ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- } else
- ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
- return x - 1;
+ /*assume a NULL folder means INBOX*/
+ if (!folder)
+ return 0;
+#ifdef IMAP_STORAGE
+ if (!strcasecmp(folder, imapfolder))
+#else
+ if (!strcasecmp(folder, "INBOX"))
+#endif
+ return 0;
+ else if (!strcasecmp(folder, "Old"))
+ return 1;
+ else if (!strcasecmp(folder, "Work"))
+ return 2;
+ else if (!strcasecmp(folder, "Family"))
+ return 3;
+ else if (!strcasecmp(folder, "Friends"))
+ return 4;
+ else if (!strcasecmp(folder, "Cust1"))
+ return 5;
+ else if (!strcasecmp(folder, "Cust2"))
+ return 6;
+ else if (!strcasecmp(folder, "Cust3"))
+ return 7;
+ else if (!strcasecmp(folder, "Cust4"))
+ return 8;
+ else if (!strcasecmp(folder, "Cust5"))
+ return 9;
+ else /*assume they meant INBOX if folder is not found otherwise*/
+ return 0;
}
/*!
- * \brief Determines if the specified message exists.
- * \param dir the folder the mailbox folder to look for messages.
- * \param msgnum the message index to query for.
- *
- * This method is used when mailboxes are stored in an ODBC back end.
- *
- * \return greater than zero if the message exists, zero when the message does not exist or on error.
+ * \brief Gets the number of messages that exist in a mailbox folder.
+ * \param context
+ * \param mailbox
+ * \param folder
+ *
+ * This method is used when IMAP backend is used.
+ * \return The number of messages in this mailbox folder (zero or more).
*/
-static int message_exists(char *dir, int msgnum)
+static int messagecount(const char *context, const char *mailbox, const char *folder)
{
- int x = 0;
- int res;
- SQLHSTMT stmt;
- char sql[PATH_MAX];
- char rowdata[20];
- char msgnums[20];
- char *argv[] = { dir, msgnums };
- struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
+ SEARCHPGM *pgm;
+ SEARCHHEADER *hdr;
- struct odbc_obj *obj;
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
- snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt) {
- ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- ast_odbc_release_obj(obj);
- goto yuck;
+ struct ast_vm_user *vmu, vmus;
+ struct vm_state *vms_p;
+ int ret = 0;
+ int fold = folder_int(folder);
+ int urgent = 0;
+
+ if (ast_strlen_zero(mailbox))
+ return 0;
+
+ /* We have to get the user before we can open the stream! */
+ vmu = find_user(&vmus, context, mailbox);
+ if (!vmu) {
+ ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
+ return -1;
+ } else {
+ /* No IMAP account available */
+ if (vmu->imapuser[0] == '\0') {
+ ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
+ return -1;
}
- res = SQLFetch(stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
+ }
+
+ /* No IMAP account available */
+ if (vmu->imapuser[0] == '\0') {
+ ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
+ free_user(vmu);
+ return -1;
+ }
+
+ /* check if someone is accessing this box right now... */
+ vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
+ if (!vms_p) {
+ vms_p = get_vm_state_by_mailbox(mailbox,1);
+ }
+ if (vms_p) {
+ ast_debug(3, "Returning before search - user is logged in\n");
+ if (fold == 0) { /* INBOX */
+ return vms_p->newmessages;
}
- res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
+ if (fold == 1) { /* Old messages */
+ return vms_p->oldmessages;
}
- if (sscanf(rowdata, "%d", &x) != 1)
- ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- } else
- ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-yuck:
- return x;
-}
-
-/*!
- * \brief returns the one-based count for messages.
- * \param vmu
- * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
- *
- * This method is used when mailboxes are stored in an ODBC back end.
- * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
- * one-based messages.
- * This method just calls last_message_index and returns +1 of its value.
- *
- * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
- */
-static int count_messages(struct ast_vm_user *vmu, char *dir)
-{
- return last_message_index(vmu, dir) + 1;
-}
+ if (fold == 11) {/*Urgent messages*/
+ return vms_p->urgentmessages;
+ }
+ }
-/*!
- * \brief Deletes a message from the mailbox folder.
- * \param sdir The mailbox folder to work in.
- * \param smsg The message index to be deleted.
- *
- * This method is used when mailboxes are stored in an ODBC back end.
- * The specified message is directly deleted from the database 'voicemessages' table.
- *
- * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
- */
-static void delete_file(char *sdir, int smsg)
-{
- SQLHSTMT stmt;
- char sql[PATH_MAX];
- char msgnums[20];
- char *argv[] = { sdir, msgnums };
- struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
+ /* add one if not there... */
+ vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
+ if (!vms_p) {
+ vms_p = get_vm_state_by_mailbox(mailbox,0);
+ }
- struct odbc_obj *obj;
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- snprintf(msgnums, sizeof(msgnums), "%d", smsg);
- snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt)
- ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- else
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- } else
- ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
- return;
-}
+ /* If URGENT, then look at INBOX */
+ if (fold == 11) {
+ fold = NEW_FOLDER;
+ urgent = 1;
+ }
-/*!
- * \brief Copies a voicemail from one mailbox to another.
- * \param sdir the folder for which to look for the message to be copied.
- * \param smsg the index of the message to be copied.
- * \param ddir the destination folder to copy the message into.
- * \param dmsg the index to be used for the copied message.
- * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
- * \param dmailboxcontext The context for the destination user.
- *
- * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
- */
-static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
-{
- SQLHSTMT stmt;
- char sql[512];
- char msgnums[20];
- char msgnumd[20];
- struct odbc_obj *obj;
- char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
- struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
+ if (!vms_p) {
+ ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
+ if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
+ return -1;
+ }
+ ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
+ ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
+ vms_p->mailstream = NIL; /* save for access from interactive entry point */
+ ast_debug(3, "Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
+ vms_p->updated = 1;
+ ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
+ init_vm_state(vms_p);
+ vmstate_insert(vms_p);
+ }
+ ret = init_mailstream(vms_p, fold);
+ if (!vms_p->mailstream) {
+ ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
+ return -1;
+ }
+ if (ret == 0) {
+ pgm = mail_newsearchpgm ();
+ hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
+ pgm->header = hdr;
+ if (fold != 1) {
+ pgm->unseen = 1;
+ pgm->seen = 0;
+ }
+ /* In the special case where fold is 1 (old messages) we have to do things a bit
+ * differently. Old messages are stored in the INBOX but are marked as "seen"
+ */
+ else {
+ pgm->unseen = 0;
+ pgm->seen = 1;
+ }
+ /* look for urgent messages */
+ if (urgent) {
+ pgm->flagged = 1;
+ pgm->unflagged = 0;
+ }
+ pgm->undeleted = 1;
+ pgm->deleted = 0;
- delete_file(ddir, dmsg);
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- snprintf(msgnums, sizeof(msgnums), "%d", smsg);
- snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
- snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext, flag) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt)
- ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
- else
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- } else
- ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
- return;
+ vms_p->vmArrayIndex = 0;
+ mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
+ if (fold == 0 && urgent == 0)
+ vms_p->newmessages = vms_p->vmArrayIndex;
+ if (fold == 1)
+ vms_p->oldmessages = vms_p->vmArrayIndex;
+ if (fold == 0 && urgent == 1)
+ vms_p->urgentmessages = vms_p->vmArrayIndex;
+ /*Freeing the searchpgm also frees the searchhdr*/
+ mail_free_searchpgm(&pgm);
+ vms_p->updated = 0;
+ return vms_p->vmArrayIndex;
+ } else {
+ mail_ping(vms_p->mailstream);
+ }
+ return 0;
}
-struct insert_data {
- char *sql;
- char *dir;
- char *msgnums;
- void *data;
- SQLLEN datalen;
- SQLLEN indlen;
- const char *context;
- const char *macrocontext;
- const char *callerid;
- const char *origtime;
- const char *duration;
- char *mailboxuser;
- char *mailboxcontext;
- const char *category;
- const char *flag;
-};
-
-static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
+static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag)
{
- struct insert_data *data = vdata;
- int res;
- SQLHSTMT stmt;
+ char *myserveremail = serveremail;
+ char fn[PATH_MAX];
+ char introfn[PATH_MAX];
+ char mailbox[256];
+ char *stringp;
+ FILE *p=NULL;
+ char tmp[80] = "/tmp/astmail-XXXXXX";
+ long len;
+ void *buf;
+ int tempcopy = 0;
+ STRING str;
+ int ret; /* for better error checking */
+ char *imapflags = NIL;
- res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- return NULL;
+ /* Set urgent flag for IMAP message */
+ if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
+ ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
+ imapflags="\\FLAGGED";
}
+
+ /* Attach only the first format */
+ fmt = ast_strdupa(fmt);
+ stringp = fmt;
+ strsep(&stringp, "|");
- SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
- SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
- SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
- SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
- SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
- SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
- SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
- SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
- SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
- SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
- SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
- if (!ast_strlen_zero(data->category)) {
- SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
+ if (!ast_strlen_zero(vmu->serveremail))
+ myserveremail = vmu->serveremail;
+
+ if (msgnum > -1)
+ make_file(fn, sizeof(fn), dir, msgnum);
+ else
+ ast_copy_string (fn, dir, sizeof(fn));
+
+ snprintf(introfn, sizeof(introfn), "%sintro", fn);
+ if (ast_fileexists(introfn, NULL, NULL) <= 0) {
+ *introfn = '\0';
}
- res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- return NULL;
+
+ if (ast_strlen_zero(vmu->email)) {
+ /* We need the vmu->email to be set when we call make_email_file, but
+ * if we keep it set, a duplicate e-mail will be created. So at the end
+ * of this function, we will revert back to an empty string if tempcopy
+ * is 1.
+ */
+ ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
+ tempcopy = 1;
}
- return stmt;
-}
-
-/*!
- * \brief Stores a voicemail into the database.
- * \param dir the folder the mailbox folder to store the message.
- * \param mailboxuser the user owning the mailbox folder.
- * \param mailboxcontext
- * \param msgnum the message index for the message to be stored.
- *
- * This method is used when mailboxes are stored in an ODBC back end.
- * The message sound file and information file is looked up on the file system.
- * A SQL query is invoked to store the message into the (MySQL) database.
- *
- * \return the zero on success -1 on error.
- */
-static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
-{
- int res = 0;
- int fd = -1;
- void *fdm = MAP_FAILED;
- size_t fdlen = -1;
- SQLHSTMT stmt;
- char sql[PATH_MAX];
- char msgnums[20];
- char fn[PATH_MAX];
- char full_fn[PATH_MAX];
- char fmt[80]="";
- char *c;
- struct ast_config *cfg=NULL;
- struct odbc_obj *obj;
- struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext };
- struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
+ if (!strcmp(fmt, "wav49"))
+ fmt = "WAV";
+ ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
- delete_file(dir, msgnum);
- if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
- ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+ /* Make a temporary file instead of piping directly to sendmail, in case the mail
+ command hangs. */
+ if (!(p = vm_mkftemp(tmp))) {
+ ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
+ if (tempcopy)
+ *(vmu->email) = '\0';
return -1;
}
- do {
- ast_copy_string(fmt, vmfmts, sizeof(fmt));
- c = strchr(fmt, '|');
- if (c)
- *c = '\0';
- if (!strcasecmp(fmt, "wav49"))
- strcpy(fmt, "WAV");
- snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
- if (msgnum > -1)
- make_file(fn, sizeof(fn), dir, msgnum);
- else
- ast_copy_string(fn, dir, sizeof(fn));
- snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
- cfg = ast_config_load(full_fn, config_flags);
- snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
- fd = open(full_fn, O_RDWR);
- if (fd < 0) {
- ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
- res = -1;
- break;
- }
- if (cfg) {
- if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
- idata.context = "";
- }
- if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
- idata.macrocontext = "";
- }
- if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
- idata.callerid = "";
- }
- if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
- idata.origtime = "";
- }
- if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
- idata.duration = "";
- }
- if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
- idata.category = "";
- }
- if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
- idata.flag = "";
- }
- }
- fdlen = lseek(fd, 0, SEEK_END);
- lseek(fd, 0, SEEK_SET);
- printf("Length is %zd\n", fdlen);
- fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
- if (fdm == MAP_FAILED) {
- ast_log(AST_LOG_WARNING, "Memory map failed!\n");
- res = -1;
- break;
- }
- idata.data = fdm;
- idata.datalen = idata.indlen = fdlen;
-
- if (!ast_strlen_zero(idata.category))
- snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
- else
- snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
-
- if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- } else {
- ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- res = -1;
+ if (msgnum < 0 && imapgreetings) {
+ if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
+ ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
+ return -1;
}
- } while (0);
- if (obj) {
- ast_odbc_release_obj(obj);
+ imap_delete_old_greeting(fn, vms);
}
- if (cfg)
- ast_config_destroy(cfg);
- if (fdm != MAP_FAILED)
- munmap(fdm, fdlen);
- if (fd > -1)
- close(fd);
- return res;
+
+ make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
+ /* read mail file to memory */
+ len = ftell(p);
+ rewind(p);
+ if (!(buf = ast_malloc(len + 1))) {
+ ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
+ fclose(p);
+ if (tempcopy)
+ *(vmu->email) = '\0';
+ return -1;
+ }
+ fread(buf, len, 1, p);
+ ((char *)buf)[len] = '\0';
+ INIT(&str, mail_string, buf, len);
+ ret = init_mailstream(vms, NEW_FOLDER);
+ if (ret == 0) {
+ imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
+ if(!mail_append_full(vms->mailstream, mailbox, imapflags, NIL, &str))
+ ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
+ fclose(p);
+ unlink(tmp);
+ ast_free(buf);
+ } else {
+ ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
+ fclose(p);
+ unlink(tmp);
+ ast_free(buf);
+ return -1;
+ }
+ ast_debug(3, "%s stored\n", fn);
+
+ if (tempcopy)
+ *(vmu->email) = '\0';
+
+ return 0;
+
}
/*!
- * \brief Renames a message in a mailbox folder.
- * \param sdir The folder of the message to be renamed.
- * \param smsg The index of the message to be renamed.
- * \param mailboxuser The user to become the owner of the message after it is renamed. Usually this will be the same as the original owner.
- * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
- * \param ddir The destination folder for the message to be renamed into
- * \param dmsg The destination message for the message to be renamed.
+ * \brief Gets the number of messages that exist in the inbox folder.
+ * \param mailbox_context
+ * \param newmsgs The variable that is updated with the count of new messages within this inbox.
+ * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
+ * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
+ *
+ * This method is used when IMAP backend is used.
+ * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
*
- * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
- * The is usually used to resequence the messages in the mailbox, such as to delete messag index 0, it would be called successively to slide all the other messages down one index.
- * But in theory, because the SQL query performs an update on (dir, msgnum, mailboxuser, mailboxcontext) in the database, it should be possible to have the message relocated to another mailbox or context as well.
+ * \return zero on success, -1 on error.
*/
-static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
+
+static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
{
- SQLHSTMT stmt;
- char sql[PATH_MAX];
- char msgnums[20];
- char msgnumd[20];
- struct odbc_obj *obj;
- char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
- struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
+ char tmp[PATH_MAX] = "";
+ char *mailboxnc;
+ char *context;
+ char *mb;
+ char *cur;
+ if (newmsgs)
+ *newmsgs = 0;
+ if (oldmsgs)
+ *oldmsgs = 0;
+ if (urgentmsgs)
+ *urgentmsgs = 0;
- delete_file(ddir, dmsg);
- obj = ast_odbc_request_obj(odbc_database, 0);
- if (obj) {
- snprintf(msgnums, sizeof(msgnums), "%d", smsg);
- snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
- snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt)
- ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- else
- SQLFreeHandle(SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- } else
- ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
- return;
+ ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox_context))
+ return 0;
+
+ ast_copy_string(tmp, mailbox_context, sizeof(tmp));
+ context = strchr(tmp, '@');
+ if (strchr(mailbox_context, ',')) {
+ int tmpnew, tmpold, tmpurgent;
+ ast_copy_string(tmp, mailbox_context, sizeof(tmp));
+ mb = tmp;
+ while ((cur = strsep(&mb, ", "))) {
+ if (!ast_strlen_zero(cur)) {
+ if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
+ return -1;
+ else {
+ if (newmsgs)
+ *newmsgs += tmpnew;
+ if (oldmsgs)
+ *oldmsgs += tmpold;
+ if (urgentmsgs)
+ *urgentmsgs += tmpurgent;
+ }
+ }
+ }
+ return 0;
+ }
+ if (context) {
+ *context = '\0';
+ mailboxnc = tmp;
+ context++;
+ } else {
+ context = "default";
+ mailboxnc = (char *)mailbox_context;
+ }
+ if (newmsgs) {
+ if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
+ return -1;
+ }
+ if (oldmsgs) {
+ if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
+ return -1;
+ }
+ if (urgentmsgs) {
+ if((*urgentmsgs = messagecount(context, mailboxnc, "Urgent")) < 0)
+ return -1;
+ }
+ return 0;
}
-#else
-#ifndef IMAP_STORAGE
-/*!
- * \brief Find all .txt files - even if they are not in sequence from 0000.
- * \param vmu
- * \param dir
- *
- * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
- *
- * \return the count of messages, zero or more.
- */
-static int count_messages(struct ast_vm_user *vmu, char *dir)
+static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
{
+ return inboxcount2(mailbox_context, NULL, newmsgs, oldmsgs);
+}
- int vmcount = 0;
- DIR *vmdir = NULL;
- struct dirent *vment = NULL;
-
- if (vm_lock_path(dir))
- return ERROR_LOCK_PATH;
+/**
+ * \brief Determines if the given folder has messages.
+ * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
+ * \param folder the folder to look in
+ *
+ * This function is used when the mailbox is stored in an IMAP back end.
+ * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
+ * \return 1 if the folder has one or more messages. zero otherwise.
+ */
- if ((vmdir = opendir(dir))) {
- while ((vment = readdir(vmdir))) {
- if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
- vmcount++;
+static int has_voicemail(const char *mailbox, const char *folder)
+{
+ char tmp[256], *tmp2, *mbox, *context;
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+ tmp2 = tmp;
+ if (strchr(tmp2, ',')) {
+ while ((mbox = strsep(&tmp2, ","))) {
+ if (!ast_strlen_zero(mbox)) {
+ if (has_voicemail(mbox, folder))
+ return 1;
}
}
- closedir(vmdir);
}
- ast_unlock_path(dir);
-
- return vmcount;
+ if ((context= strchr(tmp, '@')))
+ *context++ = '\0';
+ else
+ context = "default";
+ return messagecount(context, tmp, folder) ? 1 : 0;
}
/*!
- * \brief Renames a message in a mailbox folder.
- * \param sfn The path to the mailbox information and data file to be renamed.
- * \param dfn The path for where the message data and information files will be renamed to.
+ * \brief Copies a message from one mailbox to another.
+ * \param chan
+ * \param vmu
+ * \param imbox
+ * \param msgnum
+ * \param duration
+ * \param recip
+ * \param fmt
+ * \param dir
*
- * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
+ * This works with IMAP storage based mailboxes.
+ *
+ * \return zero on success, -1 on error.
*/
-static void rename_file(char *sfn, char *dfn)
+static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, char *flag)
{
- char stxt[PATH_MAX];
- char dtxt[PATH_MAX];
- ast_filerename(sfn,dfn,NULL);
- snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
- snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
- if (ast_check_realtime("voicemail_data")) {
- ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
+ struct vm_state *sendvms = NULL, *destvms = NULL;
+ char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
+ if (msgnum >= recip->maxmsg) {
+ ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
+ return -1;
}
- rename(stxt, dtxt);
+ if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
+ ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
+ return -1;
+ }
+ if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
+ ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
+ return -1;
+ }
+ snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
+ if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
+ return 0;
+ ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
+ return -1;
}
-/*!
- * \brief Determines the highest message number in use for a given user and mailbox folder.
- * \param vmu
- * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
- *
- * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
- * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
- *
- * \note Should always be called with a lock already set on dir.
- * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
- */
-static int last_message_index(struct ast_vm_user *vmu, char *dir)
+static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
{
- int x;
- unsigned char map[MAXMSGLIMIT] = "";
- DIR *msgdir;
- struct dirent *msgdirent;
- int msgdirint;
-
- /* Reading the entire directory into a file map scales better than
- * doing a stat repeatedly on a predicted sequence. I suspect this
- * is partially due to stat(2) internally doing a readdir(2) itself to
- * find each file. */
- msgdir = opendir(dir);
- while ((msgdirent = readdir(msgdir))) {
- if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
- map[msgdirint] = 1;
+ char tmp[256], *t = tmp;
+ size_t left = sizeof(tmp);
+
+ if (box == OLD_FOLDER) {
+ ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
+ } else {
+ ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
}
- closedir(msgdir);
- for (x = 0; x < vmu->maxmsg; x++) {
- if (map[x] == 0)
- break;
+ if (box == NEW_FOLDER) {
+ ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
+ } else {
+ snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
}
- return x - 1;
-}
+ /* Build up server information */
+ ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
+
+ /* Add authentication user if present */
+ if (!ast_strlen_zero(authuser))
+ ast_build_string(&t, &left, "/authuser=%s", authuser);
+ /* Add flags if present */
+ if (!ast_strlen_zero(imapflags))
+ ast_build_string(&t, &left, "/%s", imapflags);
-/*!
- * \brief Utility function to copy a file.
- * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
- * \param outfile The path for which to copy the file to. The directory permissions must allow the creation (or truncation) of the file, and allow for opening the file in write only mode.
- *
- * When the compiler option HARDLINK_WHEN_POSSIBLE is set, the copy operation will attempt to use the hard link facility instead of copy the file (to save disk space). If the link operation fails, it falls back to the copy operation.
- * The copy operation copies up to 4096 bytes at once.
- *
- * \return zero on success, -1 on error.
- */
-static int copy(char *infile, char *outfile)
+ /* End with username */
+ ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
+ if (box == NEW_FOLDER || box == OLD_FOLDER)
+ snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
+ else if (box == GREETINGS_FOLDER)
+ snprintf(spec, len, "%s%s", tmp, greetingfolder);
+ else { /* Other folders such as Friends, Family, etc... */
+ if (!ast_strlen_zero(imapparentfolder)) {
+ /* imapparentfolder would typically be set to INBOX */
+ snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
+ } else {
+ snprintf(spec, len, "%s%s", tmp, mbox(box));
+ }
+ }
+}
+
+static int init_mailstream(struct vm_state *vms, int box)
{
- int ifd;
- int ofd;
- int res;
- int len;
- char buf[4096];
+ MAILSTREAM *stream = NIL;
+ long debug;
+ char tmp[256];
+
+ if (!vms) {
+ ast_log (LOG_ERROR,"vm_state is NULL!\n");
+ return -1;
+ }
+ if (option_debug > 2)
+ ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
+ if (vms->mailstream == NIL || !vms->mailstream) {
+ if (option_debug)
+ ast_log (LOG_DEBUG,"mailstream not set.\n");
+ } else {
+ stream = vms->mailstream;
+ }
+ /* debug = T; user wants protocol telemetry? */
+ debug = NIL; /* NO protocol telemetry? */
-#ifdef HARDLINK_WHEN_POSSIBLE
- /* Hard link if possible; saves disk space & is faster */
- if (link(infile, outfile)) {
+ if (delimiter == '\0') { /* did not probe the server yet */
+ char *cp;
+#ifdef USE_SYSTEM_IMAP
+#include <imap/linkage.c>
+#elif defined(USE_SYSTEM_CCLIENT)
+#include <c-client/linkage.c>
+#else
+#include "linkage.c"
#endif
- if ((ifd = open(infile, O_RDONLY)) < 0) {
- ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
- return -1;
- }
- if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
- ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
- close(ifd);
+ /* Connect to INBOX first to get folders delimiter */
+ imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
+ ast_mutex_lock(&vms->lock);
+ stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
+ ast_mutex_unlock(&vms->lock);
+ if (stream == NIL) {
+ ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
return -1;
}
- do {
- len = read(ifd, buf, sizeof(buf));
- if (len < 0) {
- ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
- close(ifd);
- close(ofd);
- unlink(outfile);
- }
- if (len) {
- res = write(ofd, buf, len);
- if (errno == ENOMEM || errno == ENOSPC || res != len) {
- ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
- close(ifd);
- close(ofd);
- unlink(outfile);
- }
- }
- } while (len);
- close(ifd);
- close(ofd);
- return 0;
-#ifdef HARDLINK_WHEN_POSSIBLE
+ get_mailbox_delimiter(stream);
+ /* update delimiter in imapfolder */
+ for (cp = imapfolder; *cp; cp++)
+ if (*cp == '/')
+ *cp = delimiter;
+ }
+ /* Now connect to the target folder */
+ imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
+ if (option_debug > 2)
+ ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
+ ast_mutex_lock(&vms->lock);
+ vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
+ ast_mutex_unlock(&vms->lock);
+ if (vms->mailstream == NIL) {
+ return -1;
} else {
- /* Hard link succeeded */
return 0;
}
-#endif
}
-/*!
- * \brief Copies a voicemail information (envelope) file.
- * \param frompath
- * \param topath
- *
- * Every voicemail has the data (.wav) file, and the information file.
- * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
- * This is used by the COPY macro when not using IMAP storage.
- */
-static void copy_plain_file(char *frompath, char *topath)
+static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
{
- char frompath2[PATH_MAX], topath2[PATH_MAX];
- struct ast_variable *tmp,*var = NULL;
- const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
- ast_filecopy(frompath, topath, NULL);
- snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
- snprintf(topath2, sizeof(topath2), "%s.txt", topath);
- if (ast_check_realtime("voicemail_data")) {
- var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
- /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
- for (tmp = var; tmp; tmp = tmp->next) {
- if (!strcasecmp(tmp->name, "origmailbox")) {
- origmailbox = tmp->value;
- } else if (!strcasecmp(tmp->name, "context")) {
- context = tmp->value;
- } else if (!strcasecmp(tmp->name, "macrocontext")) {
- macrocontext = tmp->value;
- } else if (!strcasecmp(tmp->name, "exten")) {
- exten = tmp->value;
- } else if (!strcasecmp(tmp->name, "priority")) {
- priority = tmp->value;
- } else if (!strcasecmp(tmp->name, "callerchan")) {
- callerchan = tmp->value;
- } else if (!strcasecmp(tmp->name, "callerid")) {
- callerid = tmp->value;
- } else if (!strcasecmp(tmp->name, "origdate")) {
- origdate = tmp->value;
- } else if (!strcasecmp(tmp->name, "origtime")) {
- origtime = tmp->value;
- } else if (!strcasecmp(tmp->name, "category")) {
- category = tmp->value;
- } else if (!strcasecmp(tmp->name, "duration")) {
- duration = tmp->value;
- }
- }
- ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, SENTINEL);
+ SEARCHPGM *pgm;
+ SEARCHHEADER *hdr;
+ int ret, urgent = 0;
+
+ /* If Urgent, then look at INBOX */
+ if (box == 11) {
+ box = NEW_FOLDER;
+ urgent = 1;
}
- copy(frompath2, topath2);
- ast_variables_destroy(var);
-}
-#endif /* #ifndef IMAP_STORAGE */
-#endif /* #else of #ifdef ODBC_STORAGE */
-#ifndef ODBC_STORAGE
-/*!
- * \brief Removes the voicemail sound and information file.
- * \param file The path to the sound file. This will be the the folder and message index, without the extension.
- *
- * This is used by the DELETE macro when voicemails are stored on the file system.
- *
- * \return zero on success, -1 on error.
- */
-static int vm_delete(char *file)
-{
- char *txt;
- int txtsize = 0;
+ ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
+ ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
- txtsize = (strlen(file) + 5)*sizeof(char);
- txt = alloca(txtsize);
- /* Sprintf here would safe because we alloca'd exactly the right length,
- * but trying to eliminate all sprintf's anyhow
- */
- if (ast_check_realtime("voicemail_data")) {
- ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
+ if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
+ ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
+ return -1;
+ }
+
+ create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
+
+ /* Check Quota */
+ if (box == 0) {
+ ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
+ check_quota(vms,(char *)mbox(box));
}
- snprintf(txt, txtsize, "%s.txt", file);
- unlink(txt);
- return ast_filedelete(file, NULL);
-}
-#endif
-
-/*!
- * \brief utility used by inchar(), for base_encode()
- */
-static int inbuf(struct baseio *bio, FILE *fi)
-{
- int l;
- if (bio->ateof)
- return 0;
+ pgm = mail_newsearchpgm();
- if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
- if (ferror(fi))
- return -1;
+ /* Check IMAP folder for Asterisk messages only... */
+ hdr = mail_newsearchheader("X-Asterisk-VM-Extension", vmu->mailbox);
+ pgm->header = hdr;
+ pgm->deleted = 0;
+ pgm->undeleted = 1;
- bio->ateof = 1;
- return 0;
+ /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
+ if (box == NEW_FOLDER && urgent == 1) {
+ pgm->unseen = 1;
+ pgm->seen = 0;
+ pgm->flagged = 1;
+ pgm->unflagged = 0;
+ } else if (box == NEW_FOLDER && urgent == 0) {
+ pgm->unseen = 1;
+ pgm->seen = 0;
+ pgm->flagged = 0;
+ pgm->unflagged = 1;
+ } else if (box == OLD_FOLDER) {
+ pgm->seen = 1;
+ pgm->unseen = 0;
}
- bio->iolen= l;
- bio->iocp= 0;
+ ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
- return 1;
+ vms->vmArrayIndex = 0;
+ mail_search_full (vms->mailstream, NULL, pgm, NIL);
+ vms->lastmsg = vms->vmArrayIndex - 1;
+ mail_free_searchpgm(&pgm);
+
+ return 0;
}
-/*!
- * \brief utility used by base_encode()
- */
-static int inchar(struct baseio *bio, FILE *fi)
+static void write_file(char *filename, char *buffer, unsigned long len)
{
- if (bio->iocp>=bio->iolen) {
- if (!inbuf(bio, fi))
- return EOF;
- }
+ FILE *output;
+ ast_log(LOG_NOTICE, "The name of the file I'm writing is %s\n", filename);
- return bio->iobuf[bio->iocp++];
+ output = fopen (filename, "w");
+ fwrite (buffer, len, 1, output);
+ fclose (output);
}
-/*!
- * \brief utility used by base_encode()
- */
-static int ochar(struct baseio *bio, int c, FILE *so)
+static void update_messages_by_imapuser(const char *user, unsigned long number)
{
- if (bio->linelength >= BASELINELEN) {
- if (fputs(eol,so) == EOF)
- return -1;
+ struct vmstate *vlist = NULL;
- bio->linelength= 0;
+ AST_LIST_LOCK(&vmstates);
+ AST_LIST_TRAVERSE(&vmstates, vlist, list) {
+ if (!vlist->vms) {
+ ast_debug(3, "error: vms is NULL for %s\n", user);
+ continue;
+ }
+ if (!vlist->vms->imapuser) {
+ ast_debug(3, "error: imapuser is NULL for %s\n", user);
+ continue;
+ }
+ ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vlist->vms->vmArrayIndex, vlist->vms->interactive);
+ vlist->vms->msgArray[vlist->vms->vmArrayIndex++] = number;
}
+ AST_LIST_UNLOCK(&vmstates);
+}
- if (putc(((unsigned char)c),so) == EOF)
- return -1;
+void mm_searched(MAILSTREAM *stream, unsigned long number)
+{
+ char *mailbox = stream->mailbox, buf[1024] = "", *user;
- bio->linelength++;
+ if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
+ return;
- return 1;
+ update_messages_by_imapuser(user, number);
}
-/*!
- * \brief Performs a base 64 encode algorithm on the contents of a File
- * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
- * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
- *
- * TODO: shouldn't this (and the above 3 support functions) be put into some kind of external utility location, such as funcs/func_base64.c ?
- *
- * \return zero on success, -1 on error.
- */
-static int base_encode(char *filename, FILE *so)
+static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
{
- static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
- 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
- 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
- '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
- int i,hiteof= 0;
- FILE *fi;
- struct baseio bio;
-
- memset(&bio, 0, sizeof(bio));
- bio.iocp = BASEMAXINLINE;
+ struct ast_variable *var;
+ struct ast_vm_user *vmu;
- if (!(fi = fopen(filename, "rb"))) {
- ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
- return -1;
+ vmu = ast_calloc(1, sizeof *vmu);
+ if (!vmu)
+ return NULL;
+ ast_set_flag(vmu, VM_ALLOCED);
+ populate_defaults(vmu);
+
+ var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
+ if (var) {
+ apply_options_full(vmu, var);
+ ast_variables_destroy(var);
+ return vmu;
+ } else {
+ free(vmu);
+ return NULL;
}
+}
- while (!hiteof){
- unsigned char igroup[3], ogroup[4];
- int c,n;
+/* Interfaces to C-client */
- igroup[0]= igroup[1]= igroup[2]= 0;
+void mm_exists(MAILSTREAM * stream, unsigned long number)
+{
+ /* mail_ping will callback here if new mail! */
+ ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
+ if (number == 0) return;
+ set_update(stream);
+}
- for (n= 0;n<3;n++) {
- if ((c = inchar(&bio, fi)) == EOF) {
- hiteof= 1;
- break;
- }
- igroup[n]= (unsigned char)c;
- }
+void mm_expunged(MAILSTREAM * stream, unsigned long number)
+{
+ /* mail_ping will callback here if expunged mail! */
+ ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
+ if (number == 0) return;
+ set_update(stream);
+}
- if (n> 0) {
- ogroup[0]= dtable[igroup[0]>>2];
- ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
- ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
- ogroup[3]= dtable[igroup[2]&0x3F];
- if (n<3) {
- ogroup[3]= '=';
+void mm_flags(MAILSTREAM * stream, unsigned long number)
+{
+ /* mail_ping will callback here if read mail! */
+ ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
+ if (number == 0) return;
+ set_update(stream);
+}
- if (n<2)
- ogroup[2]= '=';
- }
- for (i= 0;i<4;i++)
- ochar(&bio, ogroup[i], so);
- }
+void mm_notify(MAILSTREAM * stream, char *string, long errflg)
+{
+ ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
+ mm_log (string, errflg);
+}
+
+
+void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
+{
+ if (delimiter == '\0') {
+ delimiter = delim;
}
- fclose(fi);
-
- if (fputs(eol,so)==EOF)
- return 0;
+ ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
+ if (attributes & LATT_NOINFERIORS)
+ ast_debug(5, "no inferiors\n");
+ if (attributes & LATT_NOSELECT)
+ ast_debug(5, "no select\n");
+ if (attributes & LATT_MARKED)
+ ast_debug(5, "marked\n");
+ if (attributes & LATT_UNMARKED)
+ ast_debug(5, "unmarked\n");
+}
- return 1;
+
+void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
+{
+ ast_debug(5, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
+ if (attributes & LATT_NOINFERIORS)
+ ast_debug(5, "no inferiors\n");
+ if (attributes & LATT_NOSELECT)
+ ast_debug(5, "no select\n");
+ if (attributes & LATT_MARKED)
+ ast_debug(5, "marked\n");
+ if (attributes & LATT_UNMARKED)
+ ast_debug(5, "unmarked\n");
}
-static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category, const char *flag)
+
+void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
{
- char callerid[256];
- /* Prepare variables for substitution in email body and subject */
- pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
- pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
- snprintf(passdata, passdatasize, "%d", msgnum);
- pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
- pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
- pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
- pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
- pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
- pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
- pbx_builtin_setvar_helper(ast, "VM_DATE", date);
- pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
- pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
+ ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
+ if (status->flags & SA_MESSAGES)
+ ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
+ if (status->flags & SA_RECENT)
+ ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
+ if (status->flags & SA_UNSEEN)
+ ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
+ if (status->flags & SA_UIDVALIDITY)
+ ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
+ if (status->flags & SA_UIDNEXT)
+ ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
+ ast_log(AST_LOG_NOTICE, "\n");
}
-/*!
- * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
- * \param from The string to work with.
- * \param to The string to write the modified quoted string. This buffer should be sufficiently larger than the from string, so as to allow it to be expanded by the surrounding quotes and escaping of internal quotes.
- *
- * \return The destination string with quotes wrapped on it (the to field).
- */
-static char *quote(const char *from, char *to, size_t len)
+
+void mm_log(char *string, long errflg)
{
- char *ptr = to;
- *ptr++ = '"';
- for (; ptr < to + len - 1; from++) {
- if (*from == '"')
- *ptr++ = '\\';
- else if (*from == '\0')
+ switch ((short) errflg) {
+ case NIL:
+ ast_debug(1,"IMAP Info: %s\n", string);
+ break;
+ case PARSE:
+ case WARN:
+ ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
+ break;
+ case ERROR:
+ ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
break;
- *ptr++ = *from;
}
- if (ptr < to + len - 1)
- *ptr++ = '"';
- *ptr = '\0';
- return to;
}
-/*! \brief
- * fill in *tm for current time according to the proper timezone, if any.
- * Return tm so it can be used as a function argument.
- */
-static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
+
+void mm_dlog(char *string)
{
- const struct vm_zone *z = NULL;
- struct timeval t = ast_tvnow();
+ ast_log(AST_LOG_NOTICE, "%s\n", string);
+}
- /* Does this user have a timezone specified? */
- if (!ast_strlen_zero(vmu->zonetag)) {
- /* Find the zone in the list */
- AST_LIST_LOCK(&zones);
- AST_LIST_TRAVERSE(&zones, z, list) {
- if (!strcmp(z->name, vmu->zonetag))
+
+void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
+{
+ struct ast_vm_user *vmu;
+
+ ast_debug(4, "Entering callback mm_login\n");
+
+ ast_copy_string(user, mb->user, MAILTMPLEN);
+
+ /* We should only do this when necessary */
+ if (!ast_strlen_zero(authpassword)) {
+ ast_copy_string(pwd, authpassword, MAILTMPLEN);
+ } else {
+ AST_LIST_TRAVERSE(&users, vmu, list) {
+ if (!strcasecmp(mb->user, vmu->imapuser)) {
+ ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
break;
+ }
+ }
+ if (!vmu) {
+ if ((vmu = find_user_realtime_imapuser(mb->user))) {
+ ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
+ free_user(vmu);
+ }
}
- AST_LIST_UNLOCK(&zones);
}
- ast_localtime(&t, tm, z ? z->timezone : NULL);
- return tm;
}
-/*! \brief same as mkstemp, but return a FILE * */
-static FILE *vm_mkftemp(char *template)
+
+void mm_critical(MAILSTREAM * stream)
{
- FILE *p = NULL;
- int pfd = mkstemp(template);
- chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
- if (pfd > -1) {
- p = fdopen(pfd, "w+");
- if (!p) {
- close(pfd);
- pfd = -1;
- }
- }
- return p;
}
-/*!
- * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
- * \param p The output file to generate the email contents into.
- * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
- * \param vmu The voicemail user who is sending the voicemail.
- * \param msgnum The message index in the mailbox folder.
- * \param context
- * \param mailbox The voicemail box to read the voicemail to be notified in this email.
- * \param cidnum The caller ID number.
- * \param cidname The caller ID name.
- * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
- * \param format The message sound file format. i.e. .wav
- * \param duration The time of the message content, in seconds.
- * \param attach_user_voicemail if 1, the sound file is attached to the email.
- * \param chan
- * \param category
- * \param imap if == 1, indicates the target folder for the email notification to be sent to will be an IMAP mailstore. This causes additional mailbox headers to be set, which would facilitate searching for the email in the destination IMAP folder.
- *
- * The email body, and base 64 encoded attachement (if any) are stored to the file identified by *p. This method does not actually send the email. That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function.
- */
-static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag)
+
+void mm_nocritical(MAILSTREAM * stream)
{
- char date[256];
- char host[MAXHOSTNAMELEN] = "";
- char who[256];
- char bound[256];
- char dur[256];
- struct ast_tm tm;
- char enc_cidnum[256] = "", enc_cidname[256] = "";
- char *passdata2;
- size_t len_passdata;
- char *greeting_attachment;
- char filename[256];
+}
-#ifdef IMAP_STORAGE
-#define ENDL "\r\n"
-#else
-#define ENDL "\n"
-#endif
- if (cidnum) {
- strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
+long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
+{
+ kill (getpid (), SIGSTOP);
+ return NIL;
+}
+
+
+void mm_fatal(char *string)
+{
+ ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
+}
+
+/* C-client callback to handle quota */
+static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
+{
+ struct vm_state *vms;
+ char *mailbox = stream->mailbox, *user;
+ char buf[1024] = "";
+ unsigned long usage = 0, limit = 0;
+
+ while (pquota) {
+ usage = pquota->usage;
+ limit = pquota->limit;
+ pquota = pquota->next;
}
- if (cidname) {
- strip_control(cidname, enc_cidname, sizeof(enc_cidname));
+
+ if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 2))) {
+ ast_log(AST_LOG_ERROR, "No state found.\n");
+ return;
}
- gethostname(host, sizeof(host) - 1);
- if (strchr(srcemail, '@'))
- ast_copy_string(who, srcemail, sizeof(who));
- else
- snprintf(who, sizeof(who), "%s@%s", srcemail, host);
-
- greeting_attachment = strrchr(ast_strdupa(attach), '/');
- if (greeting_attachment)
- *greeting_attachment++ = '\0';
+ ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
- snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
- ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
- fprintf(p, "Date: %s" ENDL, date);
+ vms->quota_usage = usage;
+ vms->quota_limit = limit;
+}
- /* Set date format for voicemail mail */
- ast_strftime(date, sizeof(date), emaildateformat, &tm);
+static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
+{
+ char *start, *eol_pnt;
+ int taglen;
- if (!ast_strlen_zero(fromstring)) {
- struct ast_channel *ast;
- if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
- char *passdata;
- int vmlen = strlen(fromstring)*3 + 200;
- passdata = alloca(vmlen);
- memset(passdata, 0, vmlen);
- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, enc_cidnum, enc_cidname, dur, date, passdata, vmlen, category, flag);
- pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
- len_passdata = strlen(passdata) * 2 + 3;
- passdata2 = alloca(len_passdata);
- fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
- ast_channel_free(ast);
- } else {
- ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
- }
+ if (ast_strlen_zero(header) || ast_strlen_zero(tag))
+ return NULL;
+
+ taglen = strlen(tag) + 1;
+ if (taglen < 1)
+ return NULL;
+
+ if (!(start = strstr(header, tag)))
+ return NULL;
+
+ /* Since we can be called multiple times we should clear our buffer */
+ memset(buf, 0, len);
+
+ ast_copy_string(buf, start+taglen, len);
+ if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
+ *eol_pnt = '\0';
+ return buf;
+}
+
+static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
+{
+ char *start, *quote, *eol_pnt;
+
+ if (ast_strlen_zero(mailbox))
+ return NULL;
+
+ if (!(start = strstr(mailbox, "/user=")))
+ return NULL;
+
+ ast_copy_string(buf, start+6, len);
+
+ if (!(quote = strchr(buf, '\"'))) {
+ if (!(eol_pnt = strchr(buf, '/')))
+ eol_pnt = strchr(buf,'}');
+ *eol_pnt = '\0';
+ return buf;
} else {
- fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
+ eol_pnt = strchr(buf+1,'\"');
+ *eol_pnt = '\0';
+ return buf+1;
}
- len_passdata = strlen(vmu->fullname) * 2 + 3;
- passdata2 = alloca(len_passdata);
- fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
- if (!ast_strlen_zero(emailsubject)) {
- struct ast_channel *ast;
- if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
- char *passdata;
- int vmlen = strlen(emailsubject) * 3 + 200;
- passdata = alloca(vmlen);
- memset(passdata, 0, vmlen);
- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, enc_cidnum, enc_cidname, dur, date, passdata, vmlen, category, flag);
- pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
- fprintf(p, "Subject: %s" ENDL, passdata);
- ast_channel_free(ast);
- } else {
- ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
- }
- } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
- if (ast_strlen_zero(flag)) {
- fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
- } else {
- fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
+}
+
+static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
+{
+ struct vm_state *vms_p;
+
+ if (option_debug > 4)
+ ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
+ if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
+ return NULL;
+ ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
+ ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
+ vms_p->mailstream = NIL; /* save for access from interactive entry point */
+ if (option_debug > 4)
+ ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
+ vms_p->updated = 1;
+ /* set mailbox to INBOX! */
+ ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
+ init_vm_state(vms_p);
+ vmstate_insert(vms_p);
+ return vms_p;
+}
+
+static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
+{
+ struct vmstate *vlist = NULL;
+
+ AST_LIST_LOCK(&vmstates);
+ AST_LIST_TRAVERSE(&vmstates, vlist, list) {
+ if (!vlist->vms) {
+ ast_debug(3, "error: vms is NULL for %s\n", user);
+ continue;
}
- } else {
- if (ast_strlen_zero(flag)) {
- fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
- } else {
- fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
+ if (!vlist->vms->imapuser) {
+ ast_debug(3, "error: imapuser is NULL for %s\n", user);
+ continue;
}
- }
- fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
- if (imap) {
- /* additional information needed for IMAP searching */
- fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
- /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
- fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
- fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
- fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
- /* flag added for Urgent */
- fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
- fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
- fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
- fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
- fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
- fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
- if (!ast_strlen_zero(category)) {
- fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
+ if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
+ AST_LIST_UNLOCK(&vmstates);
+ return vlist->vms;
}
- fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
- fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
- fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
- }
- if (!ast_strlen_zero(cidnum)) {
- fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
- }
- if (!ast_strlen_zero(cidname)) {
- fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
}
- fprintf(p, "MIME-Version: 1.0" ENDL);
- if (attach_user_voicemail) {
- /* Something unique. */
- snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
+ AST_LIST_UNLOCK(&vmstates);
- fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
- fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
- fprintf(p, "--%s" ENDL, bound);
- }
- fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
- if (emailbody) {
- struct ast_channel *ast;
- if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
- char *passdata;
- int vmlen = strlen(emailbody)*3 + 200;
- passdata = alloca(vmlen);
- memset(passdata, 0, vmlen);
- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
- pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
- fprintf(p, "%s" ENDL, passdata);
- ast_channel_free(ast);
- } else
- ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
- } else if (msgnum > -1){
- fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long %s message (number %d)" ENDL
- "in mailbox %s from %s, on %s so you might" ENDL
- "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
- dur, flag, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
- } else {
- fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
- "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
- }
+ ast_debug(3, "%s not found in vmstates\n", user);
- if (imap || attach_user_voicemail) {
- if (!ast_strlen_zero(attach2)) {
- snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
- ast_debug(5, "creating attachment filename %s\n", filename);
- add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 0, msgnum);
- snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
- ast_debug(5, "creating second attachment filename %s\n", filename);
- add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
- } else {
- snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
- ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
- add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
- }
- }
+ return NULL;
}
-static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
+static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive)
{
- char tmpdir[256], newtmp[256];
- char fname[256];
- char tmpcmd[256];
- int tmpfd = -1;
- /* Eww. We want formats to tell us their own MIME type */
- char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
+ struct vmstate *vlist = NULL;
- if (vmu->volgain < -.001 || vmu->volgain > .001) {
- create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
- snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
- tmpfd = mkstemp(newtmp);
- chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
- ast_debug(3, "newtmp: %s\n", newtmp);
- if (tmpfd > -1) {
- int soxstatus;
- snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
- if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
- attach = newtmp;
- ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
- } else {
- ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
- soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
- ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
- }
+ AST_LIST_LOCK(&vmstates);
+ AST_LIST_TRAVERSE(&vmstates, vlist, list) {
+ if (!vlist->vms) {
+ ast_debug(3, "error: vms is NULL for %s\n", mailbox);
+ continue;
+ }
+ if (!vlist->vms->username) {
+ ast_debug(3, "error: username is NULL for %s\n", mailbox);
+ continue;
+ }
+
+ ast_debug(3, "comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n", mailbox, interactive, vlist->vms->username, vlist->vms->interactive);
+
+ if (!strcmp(vlist->vms->username,mailbox) && vlist->vms->interactive == interactive) {
+ ast_debug(3, "Found it!\n");
+ AST_LIST_UNLOCK(&vmstates);
+ return vlist->vms;
}
}
- fprintf(p, "--%s" ENDL, bound);
- if (msgnum > -1)
- fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
- else
- fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, attach, format);
- fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
- fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
- if (msgnum > -1)
- fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
- else
- fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, attach, format);
- snprintf(fname, sizeof(fname), "%s.%s", attach, format);
- base_encode(fname, p);
- if (last)
- fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
- if (tmpfd > -1) {
- unlink(fname);
- close(tmpfd);
- unlink(newtmp);
- }
- return 0;
+ AST_LIST_UNLOCK(&vmstates);
+
+ ast_debug(3, "%s not found in vmstates\n", mailbox);
+
+ return NULL;
}
-#undef ENDL
-static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag)
+static void vmstate_insert(struct vm_state *vms)
{
- FILE *p=NULL;
- char tmp[80] = "/tmp/astmail-XXXXXX";
- char tmp2[256];
+ struct vmstate *v;
+ struct vm_state *altvms;
- if (vmu && ast_strlen_zero(vmu->email)) {
- ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
- return(0);
- }
- if (!strcmp(format, "wav49"))
- format = "WAV";
- ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
- /* Make a temporary file instead of piping directly to sendmail, in case the mail
- command hangs */
- if ((p = vm_mkftemp(tmp)) == NULL) {
- ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
- return -1;
- } else {
- make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
- fclose(p);
- snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
- ast_safe_system(tmp2);
- ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
+ /* If interactive, it probably already exists, and we should
+ use the one we already have since it is more up to date.
+ We can compare the username to find the duplicate */
+ if (vms->interactive == 1) {
+ altvms = get_vm_state_by_mailbox(vms->username,0);
+ if (altvms) {
+ ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
+ vms->newmessages = altvms->newmessages;
+ vms->oldmessages = altvms->oldmessages;
+ vms->vmArrayIndex = altvms->vmArrayIndex;
+ vms->lastmsg = altvms->lastmsg;
+ vms->curmsg = altvms->curmsg;
+ /* get a pointer to the persistent store */
+ vms->persist_vms = altvms;
+ /* Reuse the mailstream? */
+ vms->mailstream = altvms->mailstream;
+ /* vms->mailstream = NIL; */
+ }
}
- return 0;
+
+ if (!(v = ast_calloc(1, sizeof(*v))))
+ return;
+
+ v->vms = vms;
+
+ ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
+
+ AST_LIST_LOCK(&vmstates);
+ AST_LIST_INSERT_TAIL(&vmstates, v, list);
+ AST_LIST_UNLOCK(&vmstates);
}
-static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category, const char *flag)
+static void vmstate_delete(struct vm_state *vms)
{
- char date[256];
- char host[MAXHOSTNAMELEN] = "";
- char who[256];
- char dur[PATH_MAX];
- char tmp[80] = "/tmp/astmail-XXXXXX";
- char tmp2[PATH_MAX];
- struct ast_tm tm;
- FILE *p;
+ struct vmstate *vc = NULL;
+ struct vm_state *altvms = NULL;
- if ((p = vm_mkftemp(tmp)) == NULL) {
- ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
- return -1;
+ /* If interactive, we should copy pertinent info
+ back to the persistent state (to make update immediate) */
+ if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
+ ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
+ altvms->newmessages = vms->newmessages;
+ altvms->oldmessages = vms->oldmessages;
+ altvms->updated = 1;
}
- gethostname(host, sizeof(host)-1);
- if (strchr(srcemail, '@'))
- ast_copy_string(who, srcemail, sizeof(who));
- else
- snprintf(who, sizeof(who), "%s@%s", srcemail, host);
- snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
- ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
- fprintf(p, "Date: %s\n", date);
-
- if (*pagerfromstring) {
- struct ast_channel *ast;
- if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
- char *passdata;
- int vmlen = strlen(fromstring)*3 + 200;
- passdata = alloca(vmlen);
- memset(passdata, 0, vmlen);
- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
- pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
- fprintf(p, "From: %s <%s>\n", passdata, who);
- ast_channel_free(ast);
- } else
- ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
- } else
- fprintf(p, "From: Asterisk PBX <%s>\n", who);
- fprintf(p, "To: %s\n", pager);
- if (pagersubject) {
- struct ast_channel *ast;
- if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
- char *passdata;
- int vmlen = strlen(pagersubject) * 3 + 200;
- passdata = alloca(vmlen);
- memset(passdata, 0, vmlen);
- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
- pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
- fprintf(p, "Subject: %s\n\n", passdata);
- ast_channel_free(ast);
- } else
- ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
- } else {
- if (ast_strlen_zero(flag)) {
- fprintf(p, "Subject: New VM\n\n");
- } else {
- fprintf(p, "Subject: New %s VM\n\n", flag);
+
+ ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
+
+ AST_LIST_LOCK(&vmstates);
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
+ if (vc->vms == vms) {
+ AST_LIST_REMOVE_CURRENT(list);
+ break;
}
}
-
- ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
- if (pagerbody) {
- struct ast_channel *ast;
- if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
- char *passdata;
- int vmlen = strlen(pagerbody) * 3 + 200;
- passdata = alloca(vmlen);
- memset(passdata, 0, vmlen);
- prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
- pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
- fprintf(p, "%s\n", passdata);
- ast_channel_free(ast);
- } else
- ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
- } else {
- fprintf(p, "New %s long %s msg in box %s\n"
- "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
+ AST_LIST_TRAVERSE_SAFE_END
+ AST_LIST_UNLOCK(&vmstates);
+
+ if (vc) {
+ ast_mutex_destroy(&vc->vms->lock);
+ ast_free(vc);
}
- fclose(p);
- snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
- ast_safe_system(tmp2);
- ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
- return 0;
+ else
+ ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
}
-/*!
- * \brief Gets the current date and time, as formatted string.
- * \param s The buffer to hold the output formatted date.
- * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
- *
- * The date format string used is "%a %b %e %r UTC %Y".
- *
- * \return zero on success, -1 on error.
- */
-static int get_date(char *s, int len)
+static void set_update(MAILSTREAM * stream)
{
- struct ast_tm tm;
- struct timeval t = ast_tvnow();
-
- ast_localtime(&t, &tm, "UTC");
+ struct vm_state *vms;
+ char *mailbox = stream->mailbox, *user;
+ char buf[1024] = "";
- return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
-}
+ if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
+ if (user && option_debug > 2)
+ ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
+ return;
+ }
-static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
-{
- int res;
- char fn[PATH_MAX];
- char dest[PATH_MAX];
+ ast_debug(3, "User %s mailbox set for update.\n", user);
- snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
+ vms->updated = 1; /* Set updated flag since mailbox changed */
+}
- if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
- ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
- return -1;
+static void init_vm_state(struct vm_state *vms)
+{
+ int x;
+ vms->vmArrayIndex = 0;
+ for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
+ vms->msgArray[x] = 0;
}
+ ast_mutex_init(&vms->lock);
+}
- RETRIEVE(fn, -1, ext, context);
- if (ast_fileexists(fn, NULL, NULL) > 0) {
- res = ast_stream_and_wait(chan, fn, ecodes);
- if (res) {
- DISPOSE(fn, -1);
- return res;
+static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro)
+{
+ char *body_content;
+ char *body_decoded;
+ char *fn = is_intro ? vms->introfn : vms->fn;
+ unsigned long len;
+ unsigned long newlen;
+ char filename[256];
+
+ if (!body || body == NIL)
+ return -1;
+
+ body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
+ if (body_content != NIL) {
+ snprintf(filename, sizeof(filename), "%s.%s", fn, format);
+ /* ast_debug(1,body_content); */
+ body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
+ /* If the body of the file is empty, return an error */
+ if (!newlen) {
+ return -1;
}
+ write_file(filename, (char *) body_decoded, newlen);
} else {
- /* Dispose just in case */
- DISPOSE(fn, -1);
- res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
- if (res)
- return res;
- res = ast_say_digit_str(chan, ext, ecodes, chan->language);
- if (res)
- return res;
+ ast_debug(5, "Body of message is NULL.\n");
+ return -1;
}
- res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
- return res;
+ return 0;
}
-static void free_user(struct ast_vm_user *vmu)
-{
- if (!ast_test_flag(vmu, VM_ALLOCED))
- return;
+/*!
+ * \brief Get delimiter via mm_list callback
+ * \param stream
+ *
+ * Determines the delimiter character that is used by the underlying IMAP based mail store.
+ */
+static void get_mailbox_delimiter(MAILSTREAM *stream) {
+ char tmp[50];
+ snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
+ mail_list(stream, tmp, "*");
+}
- ast_free(vmu);
+/*!
+ * \brief Check Quota for user
+ * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
+ * \param mailbox the mailbox to check the quota for.
+ *
+ * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
+ */
+static void check_quota(struct vm_state *vms, char *mailbox) {
+ mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
+ ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
+ if (vms && vms->mailstream != NULL) {
+ imap_getquotaroot(vms->mailstream, mailbox);
+ } else {
+ ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
+ }
}
-static void free_zone(struct vm_zone *z)
+#endif /* IMAP_STORAGE */
+
+/*! \brief Lock file path
+ only return failure if ast_lock_path returns 'timeout',
+ not if the path does not exist or any other reason
+*/
+static int vm_lock_path(const char *path)
{
- ast_free(z);
+ switch (ast_lock_path(path)) {
+ case AST_LOCK_TIMEOUT:
+ return -1;
+ default:
+ return 0;
+ }
}
-/*!
- * \brief Gets the name of the mailbox folder from the numeric index.
- * \param id The numerical index for the folder name.
- *
- * When an invalid number is entered, or one that exceeds the pre-configured list of folder names, the name "tmp" is returned.
- *
- * \return the String name that coresponds to this folder index.
- */
-static const char *mbox(int id)
+
+#ifdef ODBC_STORAGE
+struct generic_prepare_struct {
+ char *sql;
+ int argc;
+ char **argv;
+};
+
+static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
{
- static const char *msgs[] = {
-#ifdef IMAP_STORAGE
- imapfolder,
-#else
- "INBOX",
-#endif
- "Old",
- "Work",
- "Family",
- "Friends",
- "Cust1",
- "Cust2",
- "Cust3",
- "Cust4",
- "Cust5",
- "Deleted",
- "Urgent"
- };
- return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
+ struct generic_prepare_struct *gps = data;
+ int res, i;
+ SQLHSTMT stmt;
+
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
+ return NULL;
+ }
+ res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ return NULL;
+ }
+ for (i = 0; i < gps->argc; i++)
+ SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
+
+ return stmt;
}
-#ifdef IMAP_STORAGE
+
/*!
- * \brief Converts a string folder name into the numerical identifier.
- * \param folder the string folder name to be converted to an id.
- *
- * This is the opposite of the mbox() function.
+ * \brief Retrieves a file from an ODBC data store.
+ * \param dir the path to the file to be retreived.
+ * \param msgnum the message number, such as within a mailbox folder.
+ *
+ * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
+ * The purpose is to get the message from the database store to the local file system, so that the message may be played, or the information file may be read.
*
- * \return the id that coresponds to the folder name
+ * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
+ * The output is the message information file with the name msgnum and the extension .txt
+ * and the message file with the extension of its format, in the directory with base file name of the msgnum.
+ *
+ * \return 0 on success, -1 on error.
*/
-static int folder_int(const char *folder)
-{
- /* assume a NULL folder means INBOX */
- if (!folder)
- return 0;
-#ifdef IMAP_STORAGE
- if (!strcasecmp(folder, imapfolder))
-#else
- if (!strcasecmp(folder, "INBOX"))
-#endif
- return 0;
- else if (!strcasecmp(folder, "Old"))
- return 1;
- else if (!strcasecmp(folder, "Work"))
- return 2;
- else if (!strcasecmp(folder, "Family"))
- return 3;
- else if (!strcasecmp(folder, "Friends"))
- return 4;
- else if (!strcasecmp(folder, "Cust1"))
- return 5;
- else if (!strcasecmp(folder, "Cust2"))
- return 6;
- else if (!strcasecmp(folder, "Cust3"))
- return 7;
- else if (!strcasecmp(folder, "Cust4"))
- return 8;
- else if (!strcasecmp(folder, "Cust5"))
- return 9;
- else if (!strcasecmp(folder, "Deleted"))
- return 10;
- else if (!strcasecmp(folder, "Urgent"))
- return 11;
- else /*assume they meant INBOX if folder is not found otherwise*/
- return 0;
-}
-#endif
-
-#ifdef ODBC_STORAGE
-/*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
-static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+static int retrieve_file(char *dir, int msgnum)
{
- int x = -1;
+ int x = 0;
int res;
+ int fd=-1;
+ size_t fdlen = 0;
+ void *fdm = MAP_FAILED;
+ SQLSMALLINT colcount=0;
SQLHSTMT stmt;
char sql[PATH_MAX];
- char rowdata[20];
- char tmp[PATH_MAX] = "";
- struct odbc_obj *obj;
- char *context;
- struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
-
- if (newmsgs)
- *newmsgs = 0;
- if (oldmsgs)
- *oldmsgs = 0;
- if (urgentmsgs)
- *urgentmsgs = 0;
-
- /* If no mailbox, return immediately */
- if (ast_strlen_zero(mailbox))
- return 0;
+ char fmt[80]="";
+ char *c;
+ char coltitle[256];
+ SQLSMALLINT collen;
+ SQLSMALLINT datatype;
+ SQLSMALLINT decimaldigits;
+ SQLSMALLINT nullable;
+ SQLULEN colsize;
+ SQLLEN colsize2;
+ FILE *f=NULL;
+ char rowdata[80];
+ char fn[PATH_MAX];
+ char full_fn[PATH_MAX];
+ char msgnums[80];
+ char *argv[] = { dir, msgnums };
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
- ast_copy_string(tmp, mailbox, sizeof(tmp));
-
- context = strchr(tmp, '@');
- if (context) {
- *context = '\0';
- context++;
- } else
- context = "default";
-
+ struct odbc_obj *obj;
obj = ast_odbc_request_obj(odbc_database, 0);
if (obj) {
- snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
- stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
- if (!stmt) {
- ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLFetch(stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
- goto yuck;
- }
- res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- ast_odbc_release_obj(obj);
+ ast_copy_string(fmt, vmfmts, sizeof(fmt));
+ c = strchr(fmt, '|');
+ if (c)
+ *c = '\0';
+ if (!strcasecmp(fmt, "wav49"))
+ strcpy(fmt, "WAV");
+ snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
+ if (msgnum > -1)
+ make_file(fn, sizeof(fn), dir, msgnum);
+ else
+ ast_copy_string(fn, dir, sizeof(fn));
+
+ /* Create the information file */
+ snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+
+ if (!(f = fopen(full_fn, "w+"))) {
+ ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
goto yuck;
}
- *newmsgs = atoi(rowdata);
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
-
- snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
+
+ snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
+ snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
if (!stmt) {
ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
goto yuck;
}
res = SQLFetch(stmt);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ if (res == SQL_NO_DATA) {
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
ast_odbc_release_obj(obj);
goto yuck;
}
- res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
- if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
+ if (fd < 0) {
+ ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
ast_odbc_release_obj(obj);
goto yuck;
}
- SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- *oldmsgs = atoi(rowdata);
-
- if (!urgentmsgs) {
- x = 0;
+ res = SQLNumResultCols(stmt, &colcount);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
ast_odbc_release_obj(obj);
goto yuck;
}
+ if (f)
+ fprintf(f, "[message]\n");
+ for (x=0;x<colcount;x++) {
+ 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(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ if (!strcasecmp(coltitle, "recording")) {
+ off_t offset;
+ res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
+ fdlen = colsize2;
+ if (fd > -1) {
+ char tmp[1]="";
+ lseek(fd, fdlen - 1, SEEK_SET);
+ if (write(fd, tmp, 1) != 1) {
+ close(fd);
+ fd = -1;
+ continue;
+ }
+ /* Read out in small chunks */
+ for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
+ if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
+ ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ } else {
+ res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
+ munmap(fdm, CHUNKSIZE);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ unlink(full_fn);
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ }
+ }
+ truncate(full_fn, fdlen);
+ }
+ } else {
+ res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
+ fprintf(f, "%s=%s\n", coltitle, rowdata);
+ }
+ }
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ } else
+ ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+yuck:
+ if (f)
+ fclose(f);
+ if (fd > -1)
+ close(fd);
+ return x - 1;
+}
- snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
+/*!
+ * \brief Determines the highest message number in use for a given user and mailbox folder.
+ * \param vmu
+ * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
+ *
+ * This method is used when mailboxes are stored in an ODBC back end.
+ * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
+ *
+ * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
+ */
+static int last_message_index(struct ast_vm_user *vmu, char *dir)
+{
+ int x = 0;
+ int res;
+ SQLHSTMT stmt;
+ char sql[PATH_MAX];
+ char rowdata[20];
+ char *argv[] = { dir };
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
+
+ struct odbc_obj *obj;
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
if (!stmt) {
- ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
ast_odbc_release_obj(obj);
goto yuck;
}
res = SQLFetch(stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
- ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
ast_odbc_release_obj(obj);
goto yuck;
}
res = SQLGetData(stmt, 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);
+ ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
ast_odbc_release_obj(obj);
goto yuck;
}
- *urgentmsgs = atoi(rowdata);
+ if (sscanf(rowdata, "%d", &x) != 1)
+ ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
ast_odbc_release_obj(obj);
- x = 0;
} else
ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-
yuck:
- return x;
-}
-
-static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
-{
- return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
+ return x - 1;
}
/*!
- * \brief Gets the number of messages that exist in a mailbox folder.
- * \param context
- * \param mailbox
- * \param folder
- *
- * This method is used when ODBC backend is used.
- * \return The number of messages in this mailbox folder (zero or more).
+ * \brief Determines if the specified message exists.
+ * \param dir the folder the mailbox folder to look for messages.
+ * \param msgnum the message index to query for.
+ *
+ * This method is used when mailboxes are stored in an ODBC back end.
+ *
+ * \return greater than zero if the message exists, zero when the message does not exist or on error.
*/
-static int messagecount(const char *context, const char *mailbox, const char *folder)
+static int message_exists(char *dir, int msgnum)
{
- struct odbc_obj *obj = NULL;
- int nummsgs = 0;
+ int x = 0;
int res;
- SQLHSTMT stmt = NULL;
+ SQLHSTMT stmt;
char sql[PATH_MAX];
char rowdata[20];
- struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
- if (!folder)
- folder = "INBOX";
- /* If no mailbox, return immediately */
- if (ast_strlen_zero(mailbox))
- return 0;
+ char msgnums[20];
+ char *argv[] = { dir, msgnums };
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
+ struct odbc_obj *obj;
obj = ast_odbc_request_obj(odbc_database, 0);
if (obj) {
- snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
+ snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
if (!stmt) {
ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ ast_odbc_release_obj(obj);
goto yuck;
}
res = SQLFetch(stmt);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
goto yuck;
}
res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
goto yuck;
}
- nummsgs = atoi(rowdata);
+ if (sscanf(rowdata, "%d", &x) != 1)
+ ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
} else
ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
-
-yuck:
- if (obj)
- ast_odbc_release_obj(obj);
- return nummsgs;
+yuck:
+ return x;
}
-/**
- * \brief Determines if the given folder has messages.
- * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
- *
- * This function is used when the mailbox is stored in an ODBC back end.
- * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
- * \return 1 if the folder has one or more messages. zero otherwise.
+/*!
+ * \brief returns the one-based count for messages.
+ * \param vmu
+ * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
+ *
+ * This method is used when mailboxes are stored in an ODBC back end.
+ * The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
+ * one-based messages.
+ * This method just calls last_message_index and returns +1 of its value.
+ *
+ * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
*/
-static int has_voicemail(const char *mailbox, const char *folder)
+static int count_messages(struct ast_vm_user *vmu, char *dir)
{
- char tmp[256], *tmp2 = tmp, *mbox, *context;
- ast_copy_string(tmp, mailbox, sizeof(tmp));
- while ((context = mbox = strsep(&tmp2, ","))) {
- strsep(&context, "@");
- if (ast_strlen_zero(context))
- context = "default";
- if (messagecount(context, mbox, folder))
- return 1;
- }
- return 0;
+ return last_message_index(vmu, dir) + 1;
}
-#elif defined(IMAP_STORAGE)
-
-static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, char *introfile, const char *flag)
+/*!
+ * \brief Deletes a message from the mailbox folder.
+ * \param sdir The mailbox folder to work in.
+ * \param smsg The message index to be deleted.
+ *
+ * This method is used when mailboxes are stored in an ODBC back end.
+ * The specified message is directly deleted from the database 'voicemessages' table.
+ *
+ * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
+ */
+static void delete_file(char *sdir, int smsg)
{
- char *myserveremail = serveremail;
- char fn[PATH_MAX];
- char intro[PATH_MAX];
- char mailbox[256];
- char *stringp;
- FILE *p=NULL;
- char tmp[80] = "/tmp/astmail-XXXXXX";
- long len;
- void *buf;
- int tempcopy = 0;
- STRING str;
- int ret; /* for better error checking */
- char *imapflags = NIL;
+ SQLHSTMT stmt;
+ char sql[PATH_MAX];
+ char msgnums[20];
+ char *argv[] = { sdir, msgnums };
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
- /* Set urgent flag for IMAP message */
- if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
- ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
- imapflags="\\FLAGGED";
- }
-
- /* Attach only the first format */
- fmt = ast_strdupa(fmt);
- stringp = fmt;
- strsep(&stringp, "|");
+ struct odbc_obj *obj;
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+ snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt)
+ ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ else
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ } else
+ ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+ return;
+}
- if (!ast_strlen_zero(vmu->serveremail))
- myserveremail = vmu->serveremail;
+/*!
+ * \brief Copies a voicemail from one mailbox to another.
+ * \param sdir the folder for which to look for the message to be copied.
+ * \param smsg the index of the message to be copied.
+ * \param ddir the destination folder to copy the message into.
+ * \param dmsg the index to be used for the copied message.
+ * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
+ * \param dmailboxcontext The context for the destination user.
+ *
+ * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
+ */
+static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
+{
+ SQLHSTMT stmt;
+ char sql[512];
+ char msgnums[20];
+ char msgnumd[20];
+ struct odbc_obj *obj;
+ char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
- if (msgnum > -1)
- make_file(fn, sizeof(fn), dir, msgnum);
- else
- ast_copy_string (fn, dir, sizeof(fn));
-
- if (ast_strlen_zero(vmu->email)) {
- /* We need the vmu->email to be set when we call make_email_file, but
- * if we keep it set, a duplicate e-mail will be created. So at the end
- * of this function, we will revert back to an empty string if tempcopy
- * is 1.
- */
- ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
- tempcopy = 1;
- }
+ delete_file(ddir, dmsg);
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+ snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext, flag) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt)
+ ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
+ else
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ } else
+ ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+ return;
+}
- if (!ast_strlen_zero(introfile)) {
- snprintf(intro, sizeof(intro), "%s/msgintro%04d", dir, msgnum);
- } else {
- intro[0] = '\0';
- }
+struct insert_data {
+ char *sql;
+ char *dir;
+ char *msgnums;
+ void *data;
+ SQLLEN datalen;
+ SQLLEN indlen;
+ const char *context;
+ const char *macrocontext;
+ const char *callerid;
+ const char *origtime;
+ const char *duration;
+ char *mailboxuser;
+ char *mailboxcontext;
+ const char *category;
+ const char *flag;
+};
- if (!strcmp(fmt, "wav49"))
- fmt = "WAV";
- ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
+static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
+{
+ struct insert_data *data = vdata;
+ int res;
+ SQLHSTMT stmt;
- /* Make a temporary file instead of piping directly to sendmail, in case the mail
- command hangs. */
- if (!(p = vm_mkftemp(tmp))) {
- ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
- if (tempcopy)
- *(vmu->email) = '\0';
- return -1;
+ res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ return NULL;
}
- if (msgnum < 0 && imapgreetings) {
- if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
- ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
- return -1;
- }
- imap_delete_old_greeting(fn, vms);
- }
-
- make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, intro, fmt, duration, 1, chan, NULL, 1, flag);
- /* read mail file to memory */
- len = ftell(p);
- rewind(p);
- if (!(buf = ast_malloc(len + 1))) {
- ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
- fclose(p);
- if (tempcopy)
- *(vmu->email) = '\0';
- return -1;
+ SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
+ SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
+ SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
+ SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
+ SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
+ SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
+ SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
+ SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
+ SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
+ SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
+ SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
+ if (!ast_strlen_zero(data->category)) {
+ SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
}
- fread(buf, len, 1, p);
- ((char *)buf)[len] = '\0';
- INIT(&str, mail_string, buf, len);
- ret = init_mailstream(vms, NEW_FOLDER);
- if (ret == 0) {
- imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
- if(!mail_append_full(vms->mailstream, mailbox, imapflags, NIL, &str))
- ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
- fclose(p);
- unlink(tmp);
- ast_free(buf);
- } else {
- ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
- fclose(p);
- unlink(tmp);
- ast_free(buf);
- return -1;
+ res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ return NULL;
}
- ast_debug(3, "%s stored\n", fn);
-
- if (tempcopy)
- *(vmu->email) = '\0';
-
- return 0;
+ return stmt;
}
/*!
- * \brief Gets the number of messages that exist in a mailbox folder.
- * \param context
- * \param mailbox
- * \param folder
- *
- * This method is used when IMAP backend is used.
- * \return The number of messages in this mailbox folder (zero or more).
+ * \brief Stores a voicemail into the database.
+ * \param dir the folder the mailbox folder to store the message.
+ * \param mailboxuser the user owning the mailbox folder.
+ * \param mailboxcontext
+ * \param msgnum the message index for the message to be stored.
+ *
+ * This method is used when mailboxes are stored in an ODBC back end.
+ * The message sound file and information file is looked up on the file system.
+ * A SQL query is invoked to store the message into the (MySQL) database.
+ *
+ * \return the zero on success -1 on error.
*/
-static int messagecount(const char *context, const char *mailbox, const char *folder)
+static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
{
- SEARCHPGM *pgm;
- SEARCHHEADER *hdr;
-
- struct ast_vm_user *vmu, vmus;
- struct vm_state *vms_p;
- int ret = 0;
- int fold = folder_int(folder);
- int urgent = 0;
-
- if (ast_strlen_zero(mailbox))
- return 0;
+ int res = 0;
+ int fd = -1;
+ void *fdm = MAP_FAILED;
+ size_t fdlen = -1;
+ SQLHSTMT stmt;
+ char sql[PATH_MAX];
+ char msgnums[20];
+ char fn[PATH_MAX];
+ char full_fn[PATH_MAX];
+ char fmt[80]="";
+ char *c;
+ struct ast_config *cfg=NULL;
+ struct odbc_obj *obj;
+ struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext };
+ struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
- /* We have to get the user before we can open the stream! */
- vmu = find_user(&vmus, context, mailbox);
- if (!vmu) {
- ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
- return -1;
- } else {
- /* No IMAP account available */
- if (vmu->imapuser[0] == '\0') {
- ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
- return -1;
- }
- }
-
- /* No IMAP account available */
- if (vmu->imapuser[0] == '\0') {
- ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
- free_user(vmu);
+ delete_file(dir, msgnum);
+ if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
+ ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
return -1;
}
- /* check if someone is accessing this box right now... */
- vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
- if (!vms_p) {
- vms_p = get_vm_state_by_mailbox(mailbox,1);
- }
- if (vms_p) {
- ast_debug(3, "Returning before search - user is logged in\n");
- if (fold == 0) { /* INBOX */
- return vms_p->newmessages;
- }
- if (fold == 1) { /* Old messages */
- return vms_p->oldmessages;
+ do {
+ ast_copy_string(fmt, vmfmts, sizeof(fmt));
+ c = strchr(fmt, '|');
+ if (c)
+ *c = '\0';
+ if (!strcasecmp(fmt, "wav49"))
+ strcpy(fmt, "WAV");
+ snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
+ if (msgnum > -1)
+ make_file(fn, sizeof(fn), dir, msgnum);
+ else
+ ast_copy_string(fn, dir, sizeof(fn));
+ snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+ cfg = ast_config_load(full_fn, config_flags);
+ snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
+ fd = open(full_fn, O_RDWR);
+ if (fd < 0) {
+ ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
+ res = -1;
+ break;
}
- if (fold == 11) {/*Urgent messages*/
- return vms_p->urgentmessages;
+ if (cfg) {
+ if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
+ idata.context = "";
+ }
+ if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
+ idata.macrocontext = "";
+ }
+ if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
+ idata.callerid = "";
+ }
+ if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
+ idata.origtime = "";
+ }
+ if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
+ idata.duration = "";
+ }
+ if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
+ idata.category = "";
+ }
+ if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
+ idata.flag = "";
+ }
}
- }
-
- /* add one if not there... */
- vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
- if (!vms_p) {
- vms_p = get_vm_state_by_mailbox(mailbox,0);
- }
+ fdlen = lseek(fd, 0, SEEK_END);
+ lseek(fd, 0, SEEK_SET);
+ printf("Length is %zd\n", fdlen);
+ fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
+ if (fdm == MAP_FAILED) {
+ ast_log(AST_LOG_WARNING, "Memory map failed!\n");
+ res = -1;
+ break;
+ }
+ idata.data = fdm;
+ idata.datalen = idata.indlen = fdlen;
- /* If URGENT, then look at INBOX */
- if (fold == 11) {
- fold = NEW_FOLDER;
- urgent = 1;
- }
+ if (!ast_strlen_zero(idata.category))
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
+ else
+ snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
- if (!vms_p) {
- ast_debug(3,"Adding new vmstate for %s\n",vmu->imapuser);
- if (!(vms_p = ast_calloc(1, sizeof(*vms_p)))) {
- return -1;
+ if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ } else {
+ ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ res = -1;
}
- ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
- ast_copy_string(vms_p->username, mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
- vms_p->mailstream = NIL; /* save for access from interactive entry point */
- ast_debug(3, "Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
- vms_p->updated = 1;
- ast_copy_string(vms_p->curbox, mbox(fold), sizeof(vms_p->curbox));
- init_vm_state(vms_p);
- vmstate_insert(vms_p);
- }
- ret = init_mailstream(vms_p, fold);
- if (!vms_p->mailstream) {
- ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
- return -1;
+ } while (0);
+ if (obj) {
+ ast_odbc_release_obj(obj);
}
- if (ret == 0) {
- pgm = mail_newsearchpgm ();
- hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
- pgm->header = hdr;
- if (fold != 1) {
- pgm->unseen = 1;
- pgm->seen = 0;
- }
- /* In the special case where fold is 1 (old messages) we have to do things a bit
- * differently. Old messages are stored in the INBOX but are marked as "seen"
- */
- else {
- pgm->unseen = 0;
- pgm->seen = 1;
- }
- /* look for urgent messages */
- if (urgent == 1) {
- pgm->flagged = 1;
- pgm->unflagged = 0;
- }
- pgm->undeleted = 1;
- pgm->deleted = 0;
+ if (cfg)
+ ast_config_destroy(cfg);
+ if (fdm != MAP_FAILED)
+ munmap(fdm, fdlen);
+ if (fd > -1)
+ close(fd);
+ return res;
+}
- vms_p->vmArrayIndex = 0;
- mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
- if (fold == 0 && urgent == 0)
- vms_p->newmessages = vms_p->vmArrayIndex;
- if (fold == 1)
- vms_p->oldmessages = vms_p->vmArrayIndex;
- if (fold == 0 && urgent == 1)
- vms_p->urgentmessages = vms_p->vmArrayIndex;
- /*Freeing the searchpgm also frees the searchhdr*/
- mail_free_searchpgm(&pgm);
- vms_p->updated = 0;
- return vms_p->vmArrayIndex;
- } else {
- mail_ping(vms_p->mailstream);
- }
- return 0;
+/*!
+ * \brief Renames a message in a mailbox folder.
+ * \param sdir The folder of the message to be renamed.
+ * \param smsg The index of the message to be renamed.
+ * \param mailboxuser The user to become the owner of the message after it is renamed. Usually this will be the same as the original owner.
+ * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
+ * \param ddir The destination folder for the message to be renamed into
+ * \param dmsg The destination message for the message to be renamed.
+ *
+ * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
+ * The is usually used to resequence the messages in the mailbox, such as to delete messag index 0, it would be called successively to slide all the other messages down one index.
+ * But in theory, because the SQL query performs an update on (dir, msgnum, mailboxuser, mailboxcontext) in the database, it should be possible to have the message relocated to another mailbox or context as well.
+ */
+static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
+{
+ SQLHSTMT stmt;
+ char sql[PATH_MAX];
+ char msgnums[20];
+ char msgnumd[20];
+ struct odbc_obj *obj;
+ char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
+
+ delete_file(ddir, dmsg);
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(msgnums, sizeof(msgnums), "%d", smsg);
+ snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
+ snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt)
+ ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ else
+ SQLFreeHandle(SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ } else
+ ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+ return;
}
+#else
+#ifndef IMAP_STORAGE
/*!
- * \brief Gets the number of messages that exist in the inbox folder.
- * \param mailbox_context
- * \param newmsgs The variable that is updated with the count of new messages within this inbox.
- * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
- *
- * This method is used when IMAP backend is used.
- * Simultaneously determines the count of new and old messages. The total messages would then be the sum of these two.
+ * \brief Find all .txt files - even if they are not in sequence from 0000.
+ * \param vmu
+ * \param dir
*
- * \return zero on success, -1 on error.
+ * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
+ *
+ * \return the count of messages, zero or more.
*/
-static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+static int count_messages(struct ast_vm_user *vmu, char *dir)
{
- char tmp[PATH_MAX] = "";
- char *mailboxnc;
- char *context;
- char *mb;
- char *cur;
- if (newmsgs)
- *newmsgs = 0;
- if (oldmsgs)
- *oldmsgs = 0;
- if (urgentmsgs)
- *urgentmsgs = 0;
- ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
- /* If no mailbox, return immediately */
- if (ast_strlen_zero(mailbox_context))
- return 0;
-
- ast_copy_string(tmp, mailbox_context, sizeof(tmp));
- context = strchr(tmp, '@');
- if (strchr(mailbox_context, ',')) {
- int tmpnew, tmpold, tmpurgent;
- ast_copy_string(tmp, mailbox_context, sizeof(tmp));
- mb = tmp;
- while ((cur = strsep(&mb, ", "))) {
- if (!ast_strlen_zero(cur)) {
- if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
- return -1;
- else {
- if (newmsgs)
- *newmsgs += tmpnew;
- if (oldmsgs)
- *oldmsgs += tmpold;
- if (urgentmsgs)
- *urgentmsgs += tmpurgent;
- }
+ int vmcount = 0;
+ DIR *vmdir = NULL;
+ struct dirent *vment = NULL;
+
+ if (vm_lock_path(dir))
+ return ERROR_LOCK_PATH;
+
+ if ((vmdir = opendir(dir))) {
+ while ((vment = readdir(vmdir))) {
+ if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
+ vmcount++;
}
}
- return 0;
+ closedir(vmdir);
}
- if (context) {
- *context = '\0';
- mailboxnc = tmp;
- context++;
- } else {
- context = "default";
- mailboxnc = (char *)mailbox_context;
- }
- if (newmsgs) {
- if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
- return -1;
- }
- if (oldmsgs) {
- if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
- return -1;
- }
- if (urgentmsgs) {
- if((*urgentmsgs = messagecount(context, mailboxnc, "Urgent")) < 0)
- return -1;
- }
- return 0;
-}
-
-static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
-{
- return inboxcount2(mailbox_context, NULL, newmsgs, oldmsgs);
+ ast_unlock_path(dir);
+
+ return vmcount;
}
-/**
- * \brief Determines if the given folder has messages.
- * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
- * \param folder the folder to look in
+/*!
+ * \brief Renames a message in a mailbox folder.
+ * \param sfn The path to the mailbox information and data file to be renamed.
+ * \param dfn The path for where the message data and information files will be renamed to.
*
- * This function is used when the mailbox is stored in an IMAP back end.
- * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
- * \return 1 if the folder has one or more messages. zero otherwise.
+ * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
*/
-static int has_voicemail(const char *mailbox, const char *folder)
+static void rename_file(char *sfn, char *dfn)
{
- char tmp[256], *tmp2, *mbox, *context;
- ast_copy_string(tmp, mailbox, sizeof(tmp));
- tmp2 = tmp;
- if (strchr(tmp2, ',')) {
- while ((mbox = strsep(&tmp2, ","))) {
- if (!ast_strlen_zero(mbox)) {
- if (has_voicemail(mbox, folder))
- return 1;
- }
- }
+ char stxt[PATH_MAX];
+ char dtxt[PATH_MAX];
+ ast_filerename(sfn,dfn,NULL);
+ snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
+ snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
+ if (ast_check_realtime("voicemail_data")) {
+ ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
}
- if ((context= strchr(tmp, '@')))
- *context++ = '\0';
- else
- context = "default";
- return messagecount(context, tmp, folder) ? 1 : 0;
+ rename(stxt, dtxt);
}
-/*!
- * \brief Copies a message from one mailbox to another.
- * \param chan
- * \param vmu
- * \param imbox
- * \param msgnum
- * \param duration
- * \param recip
- * \param fmt
- * \param dir
+/*!
+ * \brief Determines the highest message number in use for a given user and mailbox folder.
+ * \param vmu
+ * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
*
- * This works with IMAP storage based mailboxes.
+ * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
+ * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
*
- * \return zero on success, -1 on error.
+ * \note Should always be called with a lock already set on dir.
+ * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
*/
-static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag)
+static int last_message_index(struct ast_vm_user *vmu, char *dir)
{
- struct vm_state *sendvms = NULL, *destvms = NULL;
- char messagestring[10]; /* I guess this could be a problem if someone has more than 999,999,999 messages... */
- if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
- ast_log(AST_LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
- return -1;
+ int x;
+ unsigned char map[MAXMSGLIMIT] = "";
+ DIR *msgdir;
+ struct dirent *msgdirent;
+ int msgdirint;
+
+ /* Reading the entire directory into a file map scales better than
+ * doing a stat repeatedly on a predicted sequence. I suspect this
+ * is partially due to stat(2) internally doing a readdir(2) itself to
+ * find each file. */
+ msgdir = opendir(dir);
+ while ((msgdirent = readdir(msgdir))) {
+ if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
+ map[msgdirint] = 1;
}
- if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
- ast_log(AST_LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
- return -1;
+ closedir(msgdir);
+
+ for (x = 0; x < vmu->maxmsg; x++) {
+ if (map[x] == 0)
+ break;
}
- snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
- if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
- return 0;
- ast_log(AST_LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
- return -1;
-}
-#endif
-#ifndef IMAP_STORAGE
-/*!
- * \brief Copies a message from one mailbox to another.
- * \param chan
- * \param vmu
- * \param imbox
- * \param msgnum
- * \param duration
- * \param recip
- * \param fmt
- * \param dir
- *
- * This is only used by file storage based mailboxes.
+ return x - 1;
+}
+#if (defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
+/*!
+ * \brief Removes a voicemail message file.
+ * \param dir the path to the message file.
+ * \param msgnum the unique number for the message within the mailbox.
*
+ * Removes the message content file and the information file.
+ * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
+ * Typical use is to clean up after a RETRIEVE operation.
+ * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
* \return zero on success, -1 on error.
*/
-static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag)
+static int remove_file(char *dir, int msgnum)
{
- char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
- const char *frombox = mbox(imbox);
- int recipmsgnum;
-
- ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
-
- if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
- create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
- } else {
- create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
- }
-
- if (!dir)
- make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
- else
- ast_copy_string(fromdir, dir, sizeof(fromdir));
-
- make_file(frompath, sizeof(frompath), fromdir, msgnum);
- make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
-
- if (vm_lock_path(todir))
- return ERROR_LOCK_PATH;
-
- recipmsgnum = last_message_index(recip, todir) + 1;
- if (recipmsgnum < recip->maxmsg) {
- make_file(topath, sizeof(topath), todir, recipmsgnum);
- COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
- } else {
- ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
- }
- ast_unlock_path(todir);
- notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
+ char fn[PATH_MAX];
+ char full_fn[PATH_MAX];
+ char msgnums[80];
+ if (msgnum > -1) {
+ snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
+ make_file(fn, sizeof(fn), dir, msgnum);
+ } else
+ ast_copy_string(fn, dir, sizeof(fn));
+ ast_filedelete(fn, NULL);
+ snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
+ unlink(full_fn);
return 0;
}
#endif
-#if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
-static int messagecount(const char *context, const char *mailbox, const char *folder)
-{
- return __has_voicemail(context, mailbox, folder, 0);
-}
-static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
+/*!
+ * \brief Utility function to copy a file.
+ * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
+ * \param outfile The path for which to copy the file to. The directory permissions must allow the creation (or truncation) of the file, and allow for opening the file in write only mode.
+ *
+ * When the compiler option HARDLINK_WHEN_POSSIBLE is set, the copy operation will attempt to use the hard link facility instead of copy the file (to save disk space). If the link operation fails, it falls back to the copy operation.
+ * The copy operation copies up to 4096 bytes at once.
+ *
+ * \return zero on success, -1 on error.
+ */
+static int copy(char *infile, char *outfile)
{
- DIR *dir;
- struct dirent *de;
- char fn[256];
- int ret = 0;
+ int ifd;
+ int ofd;
+ int res;
+ int len;
+ char buf[4096];
- /* If no mailbox, return immediately */
- if (ast_strlen_zero(mailbox))
+#ifdef HARDLINK_WHEN_POSSIBLE
+ /* Hard link if possible; saves disk space & is faster */
+ if (link(infile, outfile)) {
+#endif
+ if ((ifd = open(infile, O_RDONLY)) < 0) {
+ ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
+ return -1;
+ }
+ if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
+ ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
+ close(ifd);
+ return -1;
+ }
+ do {
+ len = read(ifd, buf, sizeof(buf));
+ if (len < 0) {
+ ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
+ close(ifd);
+ close(ofd);
+ unlink(outfile);
+ }
+ if (len) {
+ res = write(ofd, buf, len);
+ if (errno == ENOMEM || errno == ENOSPC || res != len) {
+ ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
+ close(ifd);
+ close(ofd);
+ unlink(outfile);
+ }
+ }
+ } while (len);
+ close(ifd);
+ close(ofd);
return 0;
-
- if (ast_strlen_zero(folder))
- folder = "INBOX";
- if (ast_strlen_zero(context))
- context = "default";
-
- snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
-
- if (!(dir = opendir(fn)))
+#ifdef HARDLINK_WHEN_POSSIBLE
+ } else {
+ /* Hard link succeeded */
return 0;
+ }
+#endif
+}
- while ((de = readdir(dir))) {
- if (!strncasecmp(de->d_name, "msg", 3)) {
- if (shortcircuit) {
- ret = 1;
- break;
- } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
- if (shortcircuit) return 1;
- ret++;
+/*!
+ * \brief Copies a voicemail information (envelope) file.
+ * \param frompath
+ * \param topath
+ *
+ * Every voicemail has the data (.wav) file, and the information file.
+ * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
+ * This is used by the COPY macro when not using IMAP storage.
+ */
+static void copy_plain_file(char *frompath, char *topath)
+{
+ char frompath2[PATH_MAX], topath2[PATH_MAX];
+ struct ast_variable *tmp,*var = NULL;
+ const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
+ ast_filecopy(frompath, topath, NULL);
+ snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
+ snprintf(topath2, sizeof(topath2), "%s.txt", topath);
+ if (ast_check_realtime("voicemail_data")) {
+ var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
+ /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
+ for (tmp = var; tmp; tmp = tmp->next) {
+ if (!strcasecmp(tmp->name, "origmailbox")) {
+ origmailbox = tmp->value;
+ } else if (!strcasecmp(tmp->name, "context")) {
+ context = tmp->value;
+ } else if (!strcasecmp(tmp->name, "macrocontext")) {
+ macrocontext = tmp->value;
+ } else if (!strcasecmp(tmp->name, "exten")) {
+ exten = tmp->value;
+ } else if (!strcasecmp(tmp->name, "priority")) {
+ priority = tmp->value;
+ } else if (!strcasecmp(tmp->name, "callerchan")) {
+ callerchan = tmp->value;
+ } else if (!strcasecmp(tmp->name, "callerid")) {
+ callerid = tmp->value;
+ } else if (!strcasecmp(tmp->name, "origdate")) {
+ origdate = tmp->value;
+ } else if (!strcasecmp(tmp->name, "origtime")) {
+ origtime = tmp->value;
+ } else if (!strcasecmp(tmp->name, "category")) {
+ category = tmp->value;
+ } else if (!strcasecmp(tmp->name, "duration")) {
+ duration = tmp->value;
}
}
+ ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, SENTINEL);
}
-
- closedir(dir);
-
- /* If we are checking INBOX, we should check Urgent as well */
- if (strcmp(folder, "INBOX") == 0) {
- return (ret + __has_voicemail(context, mailbox, "Urgent", shortcircuit));
- } else {
- return ret;
- }
+ copy(frompath2, topath2);
+ ast_variables_destroy(var);
}
-/**
- * \brief Determines if the given folder has messages.
- * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
- * \param folder the folder to look in
+#endif /* #ifndef IMAP_STORAGE */
+#endif /* #else of #ifdef ODBC_STORAGE */
+#if (!defined(ODBC_STORAGE) && !defined(IMAP_STORAGE))
+/*!
+ * \brief Removes the voicemail sound and information file.
+ * \param file The path to the sound file. This will be the the folder and message index, without the extension.
*
- * This function is used when the mailbox is stored in a filesystem back end.
- * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
- * \return 1 if the folder has one or more messages. zero otherwise.
+ * This is used by the DELETE macro when voicemails are stored on the file system.
+ *
+ * \return zero on success, -1 on error.
*/
-static int has_voicemail(const char *mailbox, const char *folder)
+static int vm_delete(char *file)
{
- char tmp[256], *tmp2 = tmp, *mbox, *context;
- ast_copy_string(tmp, mailbox, sizeof(tmp));
- while ((mbox = strsep(&tmp2, ","))) {
- if ((context = strchr(mbox, '@')))
- *context++ = '\0';
- else
- context = "default";
- if (__has_voicemail(context, mbox, folder, 1))
- return 1;
+ char *txt;
+ int txtsize = 0;
+
+ txtsize = (strlen(file) + 5)*sizeof(char);
+ txt = alloca(txtsize);
+ /* Sprintf here would safe because we alloca'd exactly the right length,
+ * but trying to eliminate all sprintf's anyhow
+ */
+ if (ast_check_realtime("voicemail_data")) {
+ ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
}
- return 0;
+ snprintf(txt, txtsize, "%s.txt", file);
+ unlink(txt);
+ return ast_filedelete(file, NULL);
}
+#endif
-
-static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+/*!
+ * \brief utility used by inchar(), for base_encode()
+ */
+static int inbuf(struct baseio *bio, FILE *fi)
{
- char tmp[256];
- char *context;
+ int l;
- /* If no mailbox, return immediately */
- if (ast_strlen_zero(mailbox))
+ if (bio->ateof)
return 0;
- if (newmsgs)
- *newmsgs = 0;
- if (oldmsgs)
- *oldmsgs = 0;
- if (urgentmsgs)
- *urgentmsgs = 0;
-
- if (strchr(mailbox, ',')) {
- int tmpnew, tmpold, tmpurgent;
- char *mb, *cur;
+ if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
+ if (ferror(fi))
+ return -1;
- ast_copy_string(tmp, mailbox, sizeof(tmp));
- mb = tmp;
- while ((cur = strsep(&mb, ", "))) {
- if (!ast_strlen_zero(cur)) {
- if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
- return -1;
- else {
- if (newmsgs)
- *newmsgs += tmpnew;
- if (oldmsgs)
- *oldmsgs += tmpold;
- if (urgentmsgs)
- *urgentmsgs += tmpurgent;
- }
- }
- }
+ bio->ateof = 1;
return 0;
}
- ast_copy_string(tmp, mailbox, sizeof(tmp));
-
- if ((context = strchr(tmp, '@')))
- *context++ = '\0';
- else
- context = "default";
-
- if (newmsgs)
- *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
- if (oldmsgs)
- *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
- if (urgentmsgs)
- *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
+ bio->iolen= l;
+ bio->iocp= 0;
- return 0;
+ return 1;
}
-static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
+/*!
+ * \brief utility used by base_encode()
+ */
+static int inchar(struct baseio *bio, FILE *fi)
{
- return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
-}
+ if (bio->iocp>=bio->iolen) {
+ if (!inbuf(bio, fi))
+ return EOF;
+ }
-#endif
+ return bio->iobuf[bio->iocp++];
+}
-static void run_externnotify(char *context, char *extension, const char *flag)
+/*!
+ * \brief utility used by base_encode()
+ */
+static int ochar(struct baseio *bio, int c, FILE *so)
{
- char arguments[255];
- char ext_context[256] = "";
- int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
- struct ast_smdi_mwi_message *mwi_msg;
+ if (bio->linelength >= BASELINELEN) {
+ if (fputs(eol,so) == EOF)
+ return -1;
- if (!ast_strlen_zero(context))
- snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
- else
- ast_copy_string(ext_context, extension, sizeof(ext_context));
+ bio->linelength= 0;
+ }
- if (smdi_iface) {
- if (ast_app_has_voicemail(ext_context, NULL))
- ast_smdi_mwi_set(smdi_iface, extension);
- else
- ast_smdi_mwi_unset(smdi_iface, extension);
+ if (putc(((unsigned char)c),so) == EOF)
+ return -1;
- if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
- ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
- if (!strncmp(mwi_msg->cause, "INV", 3))
- ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
- else if (!strncmp(mwi_msg->cause, "BLK", 3))
- ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
- ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
- ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
- } else {
- ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
- }
- }
+ bio->linelength++;
- if (!ast_strlen_zero(externnotify)) {
- if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
- ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
- } else {
- snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
- ast_debug(1, "Executing %s\n", arguments);
- ast_safe_system(arguments);
- }
- }
+ return 1;
}
/*!
- * \brief Variables used for saving a voicemail.
+ * \brief Performs a base 64 encode algorithm on the contents of a File
+ * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
+ * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
+ *
+ * TODO: shouldn't this (and the above 3 support functions) be put into some kind of external utility location, such as funcs/func_base64.c ?
*
- * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
- */
-struct leave_vm_options {
- unsigned int flags;
- signed char record_gain;
- char *exitcontext;
-};
-
-/*!
- * \brief Prompts the user and records a voicemail to a mailbox.
- * \param chan
- * \param ext
- * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
- *
- *
- *
* \return zero on success, -1 on error.
*/
-static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
+static int base_encode(char *filename, FILE *so)
{
-#ifdef IMAP_STORAGE
- int newmsgs, oldmsgs;
- struct vm_state *vms = NULL;
-#else
- char urgdir[PATH_MAX];
-#endif
- char txtfile[PATH_MAX];
- char tmptxtfile[PATH_MAX];
- char callerid[256];
- FILE *txt;
- char date[256];
- int txtdes;
- int res = 0;
- int msgnum;
- int duration = 0;
- int ausemacro = 0;
- int ousemacro = 0;
- int ouseexten = 0;
- int rtmsgid = 0;
- char tmpid[16];
- char tmpdur[16];
- char priority[16];
- char origtime[16];
- char dir[PATH_MAX];
- char tmpdir[PATH_MAX];
- char fn[PATH_MAX];
- char prefile[PATH_MAX] = "";
- char tempfile[PATH_MAX] = "";
- char ext_context[256] = "";
- char fmt[80];
- char *context;
- char ecodes[17] = "#";
- char tmp[1024] = "";
- char *tmpptr;
- struct ast_vm_user *vmu;
- struct ast_vm_user svm;
- const char *category = NULL;
- const char *code;
- const char *alldtmf = "0123456789ABCD*#";
- char flag[80];
-
- ast_copy_string(tmp, ext, sizeof(tmp));
- ext = tmp;
- if ((context = strchr(tmp, '@'))) {
- *context++ = '\0';
- tmpptr = strchr(context, '&');
- } else {
- tmpptr = strchr(ext, '&');
- }
-
- if (tmpptr)
- *tmpptr++ = '\0';
-
- ast_channel_lock(chan);
- if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
- category = ast_strdupa(category);
- }
- ast_channel_unlock(chan);
-
- if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
- ast_copy_string(flag, "Urgent", sizeof(flag));
- } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
- ast_copy_string(flag, "PRIORITY", sizeof(flag));
- } else {
- flag[0] = '\0';
- }
+ static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
+ 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
+ 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
+ '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
+ int i,hiteof= 0;
+ FILE *fi;
+ struct baseio bio;
- ast_debug(3, "Before find_user\n");
- if (!(vmu = find_user(&svm, context, ext))) {
- ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
- return res;
- }
- /* Setup pre-file if appropriate */
- if (strcmp(vmu->context, "default"))
- snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
- else
- ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
+ memset(&bio, 0, sizeof(bio));
+ bio.iocp = BASEMAXINLINE;
- /* Set the path to the prefile. Will be one of
- VM_SPOOL_DIRcontext/ext/busy
- VM_SPOOL_DIRcontext/ext/unavail
- Depending on the flag set in options.
- */
- if (ast_test_flag(options, OPT_BUSY_GREETING)) {
- snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
- } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
- snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
- }
- /* Set the path to the tmpfile as
- VM_SPOOL_DIR/context/ext/temp
- and attempt to create the folder structure.
- */
- snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
- if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
- ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
+ if (!(fi = fopen(filename, "rb"))) {
+ ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
return -1;
}
- RETRIEVE(tempfile, -1, ext, context);
-
- if (ast_fileexists(tempfile, NULL, NULL) > 0)
- ast_copy_string(prefile, tempfile, sizeof(prefile));
+ while (!hiteof){
+ unsigned char igroup[3], ogroup[4];
+ int c,n;
- DISPOSE(tempfile, -1);
- /* It's easier just to try to make it than to check for its existence */
- create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
+ igroup[0]= igroup[1]= igroup[2]= 0;
- /* Check current or macro-calling context for special extensions */
- if (ast_test_flag(vmu, VM_OPERATOR)) {
- if (!ast_strlen_zero(vmu->exit)) {
- if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
- strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
- ouseexten = 1;
+ for (n= 0;n<3;n++) {
+ if ((c = inchar(&bio, fi)) == EOF) {
+ hiteof= 1;
+ break;
}
- } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
- strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
- ouseexten = 1;
- } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
- strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
- ousemacro = 1;
+
+ igroup[n]= (unsigned char)c;
}
- }
- if (!ast_strlen_zero(vmu->exit)) {
- if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
- strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
- } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
- strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
- else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
- strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
- ausemacro = 1;
- }
+ if (n> 0) {
+ ogroup[0]= dtable[igroup[0]>>2];
+ ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
+ ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
+ ogroup[3]= dtable[igroup[2]&0x3F];
- if (ast_test_flag(options, OPT_DTMFEXIT)) {
- for (code = alldtmf; *code; code++) {
- char e[2] = "";
- e[0] = *code;
- if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
- strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
- }
- }
+ if (n<3) {
+ ogroup[3]= '=';
- /* Play the beginning intro if desired */
- if (!ast_strlen_zero(prefile)) {
-#ifdef ODBC_STORAGE
- int success =
-#endif
- RETRIEVE(prefile, -1, ext, context);
- if (ast_fileexists(prefile, NULL, NULL) > 0) {
- if (ast_streamfile(chan, prefile, chan->language) > -1)
- res = ast_waitstream(chan, ecodes);
-#ifdef ODBC_STORAGE
- if (success == -1) {
- /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
- ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
- store_file(prefile, vmu->mailbox, vmu->context, -1);
+ if (n<2)
+ ogroup[2]= '=';
}
-#endif
- } else {
- ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
- res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
- }
- DISPOSE(prefile, -1);
- if (res < 0) {
- ast_debug(1, "Hang up during prefile playback\n");
- free_user(vmu);
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
- return -1;
- }
- }
- if (res == '#') {
- /* On a '#' we skip the instructions */
- ast_set_flag(options, OPT_SILENT);
- res = 0;
- }
- if (!res && !ast_test_flag(options, OPT_SILENT)) {
- res = ast_stream_and_wait(chan, INTRO, ecodes);
- if (res == '#') {
- ast_set_flag(options, OPT_SILENT);
- res = 0;
- }
- }
- if (res > 0)
- ast_stopstream(chan);
- /* Check for a '*' here in case the caller wants to escape from voicemail to something
- other than the operator -- an automated attendant or mailbox login for example */
- if (res == '*') {
- chan->exten[0] = 'a';
- chan->exten[1] = '\0';
- if (!ast_strlen_zero(vmu->exit)) {
- ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
- } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
- ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
- }
- chan->priority = 0;
- free_user(vmu);
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
- return 0;
- }
- /* Check for a '0' here */
- if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
- transfer:
- if (ouseexten || ousemacro) {
- chan->exten[0] = 'o';
- chan->exten[1] = '\0';
- if (!ast_strlen_zero(vmu->exit)) {
- ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
- } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
- ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
- }
- ast_play_and_wait(chan, "transfer");
- chan->priority = 0;
- free_user(vmu);
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
+ for (i= 0;i<4;i++)
+ ochar(&bio, ogroup[i], so);
}
- return 0;
}
- /* Allow all other digits to exit Voicemail and return to the dialplan */
- if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
- if (!ast_strlen_zero(options->exitcontext))
- ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
- free_user(vmu);
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
- return res;
- }
+ fclose(fi);
+
+ if (fputs(eol,so)==EOF)
+ return 0;
- if (res < 0) {
- free_user(vmu);
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
- return -1;
- }
- /* The meat of recording the message... All the announcements and beeps have been played*/
- ast_copy_string(fmt, vmfmts, sizeof(fmt));
- if (!ast_strlen_zero(fmt)) {
- msgnum = 0;
+ return 1;
+}
-#ifdef IMAP_STORAGE
- /* Is ext a mailbox? */
- /* must open stream for this user to get info! */
- res = inboxcount(ext_context, &newmsgs, &oldmsgs);
- if (res < 0) {
- ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
- return -1;
- }
- if (!(vms = get_vm_state_by_mailbox(ext,0))) {
- /* It is possible under certain circumstances that inboxcount did not
- * create a vm_state when it was needed. This is a catchall which will
- * rarely be used.
- */
- if (!(vms = ast_calloc(1, sizeof(*vms)))) {
- ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
- return -1;
- }
- ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
- ast_copy_string(vms->username, ext, sizeof(vms->username));
- vms->mailstream = NIL;
- ast_debug(3, "Copied %s to %s\n", vmu->imapuser, vms->imapuser);
- vms->updated=1;
- ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
- init_vm_state(vms);
- vmstate_insert(vms);
- vms = get_vm_state_by_mailbox(ext,0);
- }
- vms->newmessages++;
-
- /* here is a big difference! We add one to it later */
- msgnum = newmsgs + oldmsgs;
- ast_debug(3, "Messagecount set to %d\n",msgnum);
- snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
- /* set variable for compatibility */
- pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
+static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category, const char *flag)
+{
+ char callerid[256];
+ /* Prepare variables for substitution in email body and subject */
+ pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
+ pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
+ snprintf(passdata, passdatasize, "%d", msgnum);
+ pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
+ pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
+ pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
+ pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
+ pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
+ pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
+ pbx_builtin_setvar_helper(ast, "VM_DATE", date);
+ pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
+ pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
+}
- /* Check if mailbox is full */
- check_quota(vms, imapfolder);
- if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
- ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
- ast_play_and_wait(chan, "vm-mailboxfull");
- return -1;
- }
-
- /* Check if we have exceeded maxmsg */
- if (msgnum >= vmu->maxmsg) {
- ast_log(AST_LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
- ast_play_and_wait(chan, "vm-mailboxfull");
- return -1;
- }
+/*!
+ * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
+ * \param from The string to work with.
+ * \param to The string to write the modified quoted string. This buffer should be sufficiently larger than the from string, so as to allow it to be expanded by the surrounding quotes and escaping of internal quotes.
+ *
+ * \return The destination string with quotes wrapped on it (the to field).
+ */
+static char *quote(const char *from, char *to, size_t len)
+{
+ char *ptr = to;
+ *ptr++ = '"';
+ for (; ptr < to + len - 1; from++) {
+ if (*from == '"')
+ *ptr++ = '\\';
+ else if (*from == '\0')
+ break;
+ *ptr++ = *from;
+ }
+ if (ptr < to + len - 1)
+ *ptr++ = '"';
+ *ptr = '\0';
+ return to;
+}
-#else
- if (count_messages(vmu, dir) >= vmu->maxmsg) {
- res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
- if (!res)
- res = ast_waitstream(chan, "");
- ast_log(AST_LOG_WARNING, "No more messages possible\n");
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
- goto leave_vm_out;
- }
+/*! \brief
+ * fill in *tm for current time according to the proper timezone, if any.
+ * Return tm so it can be used as a function argument.
+ */
+static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
+{
+ const struct vm_zone *z = NULL;
+ struct timeval t = ast_tvnow();
-#endif
- snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
- txtdes = mkstemp(tmptxtfile);
- chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
- if (txtdes < 0) {
- res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
- if (!res)
- res = ast_waitstream(chan, "");
- ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
- goto leave_vm_out;
+ /* Does this user have a timezone specified? */
+ if (!ast_strlen_zero(vmu->zonetag)) {
+ /* Find the zone in the list */
+ AST_LIST_LOCK(&zones);
+ AST_LIST_TRAVERSE(&zones, z, list) {
+ if (!strcmp(z->name, vmu->zonetag))
+ break;
}
+ AST_LIST_UNLOCK(&zones);
+ }
+ ast_localtime(&t, tm, z ? z->timezone : NULL);
+ return tm;
+}
- /* Now play the beep once we have the message number for our next message. */
- if (res >= 0) {
- /* Unless we're *really* silent, try to send the beep */
- res = ast_stream_and_wait(chan, "beep", "");
- }
-
- /* Store information in real-time storage */
- if (ast_check_realtime("voicemail_data")) {
- snprintf(priority, sizeof(priority), "%d", chan->priority);
- snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
- get_date(date, sizeof(date));
- rtmsgid = ast_store_realtime("voicemail_data", "origmailbox", ext, "context", chan->context, "macrocontext", chan->macrocontext, "exten", chan->exten, "priority", priority, "callerchan", chan->name, "callerid", ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"), "origdate", date, "origtime", origtime, "category", S_OR(category,""), SENTINEL);
- }
+/*!
+ * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
+ * \param p The output file to generate the email contents into.
+ * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
+ * \param vmu The voicemail user who is sending the voicemail.
+ * \param msgnum The message index in the mailbox folder.
+ * \param context
+ * \param mailbox The voicemail box to read the voicemail to be notified in this email.
+ * \param cidnum The caller ID number.
+ * \param cidname The caller ID name.
+ * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
+ * \param format The message sound file format. i.e. .wav
+ * \param duration The time of the message content, in seconds.
+ * \param attach_user_voicemail if 1, the sound file is attached to the email.
+ * \param chan
+ * \param category
+ * \param imap if == 1, indicates the target folder for the email notification to be sent to will be an IMAP mailstore. This causes additional mailbox headers to be set, which would facilitate searching for the email in the destination IMAP folder.
+ *
+ * The email body, and base 64 encoded attachement (if any) are stored to the file identified by *p. This method does not actually send the email. That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function.
+ */
+static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag)
+{
+ char date[256];
+ char host[MAXHOSTNAMELEN] = "";
+ char who[256];
+ char bound[256];
+ char dur[256];
+ struct ast_tm tm;
+ char enc_cidnum[256] = "", enc_cidname[256] = "";
+ char *passdata2;
+ size_t len_passdata;
+ char *greeting_attachment;
+ char filename[256];
- /* Store information */
- txt = fdopen(txtdes, "w+");
- if (txt) {
- get_date(date, sizeof(date));
- fprintf(txt,
- ";\n"
- "; Message Information file\n"
- ";\n"
- "[message]\n"
- "origmailbox=%s\n"
- "context=%s\n"
- "macrocontext=%s\n"
- "exten=%s\n"
- "priority=%d\n"
- "callerchan=%s\n"
- "callerid=%s\n"
- "origdate=%s\n"
- "origtime=%ld\n"
- "category=%s\n",
- ext,
- chan->context,
- chan->macrocontext,
- chan->exten,
- chan->priority,
- chan->name,
- ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
- date, (long)time(NULL),
- category ? category : "");
- } else
- ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
#ifdef IMAP_STORAGE
- res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
+#define ENDL "\r\n"
#else
- res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, NULL, flag);
+#define ENDL "\n"
#endif
- if (txt) {
- fprintf(txt, "flag=%s\n", flag);
- if (duration < vmminsecs) {
- fclose(txt);
- if (option_verbose > 2)
- ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
- ast_filedelete(tmptxtfile, NULL);
- unlink(tmptxtfile);
- if (ast_check_realtime("voicemail_data")) {
- snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
- ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
- }
- } else {
- fprintf(txt, "duration=%d\n", duration);
- fclose(txt);
- if (vm_lock_path(dir)) {
- ast_log(AST_LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
- /* Delete files */
- ast_filedelete(tmptxtfile, NULL);
- unlink(tmptxtfile);
- } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
- ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
- unlink(tmptxtfile);
- ast_unlock_path(dir);
- if (ast_check_realtime("voicemail_data")) {
- snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
- ast_destroy_realtime("voicemail_data", "id", tmpid, SENTINEL);
- }
- } else {
-#ifndef IMAP_STORAGE
- msgnum = last_message_index(vmu, dir) + 1;
-#endif
- make_file(fn, sizeof(fn), dir, msgnum);
+ if (cidnum) {
+ strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
+ }
+ if (cidname) {
+ strip_control(cidname, enc_cidname, sizeof(enc_cidname));
+ }
+ gethostname(host, sizeof(host) - 1);
- /* assign a variable with the name of the voicemail file */
-#ifndef IMAP_STORAGE
- pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
-#else
- pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
-#endif
+ if (strchr(srcemail, '@'))
+ ast_copy_string(who, srcemail, sizeof(who));
+ else
+ snprintf(who, sizeof(who), "%s@%s", srcemail, host);
+
+ greeting_attachment = strrchr(ast_strdupa(attach), '/');
+ if (greeting_attachment)
+ *greeting_attachment++ = '\0';
- snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
- ast_filerename(tmptxtfile, fn, NULL);
- rename(tmptxtfile, txtfile);
+ snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
+ ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
+ fprintf(p, "Date: %s" ENDL, date);
- /* Properly set permissions on voicemail text descriptor file.
- Unfortunately mkstemp() makes this file 0600 on most unix systems. */
- if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
- ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
-
- ast_unlock_path(dir);
- if (ast_check_realtime("voicemail_data")) {
- snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
- snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
- ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, SENTINEL);
- }
- /* We must store the file first, before copying the message, because
- * ODBC storage does the entire copy with SQL.
- */
- if (ast_fileexists(fn, NULL, NULL) > 0) {
- STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, NULL, flag);
- }
+ /* Set date format for voicemail mail */
+ ast_strftime(date, sizeof(date), emaildateformat, &tm);
- /* Are there to be more recipients of this message? */
- while (tmpptr) {
- struct ast_vm_user recipu, *recip;
- char *exten, *context;
-
- exten = strsep(&tmpptr, "&");
- context = strchr(exten, '@');
- if (context) {
- *context = '\0';
- context++;
- }
- if ((recip = find_user(&recipu, context, exten))) {
- copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
- free_user(recip);
- }
- }
-#ifndef IMAP_STORAGE
- if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
- /* Move the message from INBOX to Urgent folder if this is urgent! */
- char sfn[PATH_MAX];
- char dfn[PATH_MAX];
- int x;
- /* It's easier just to try to make it than to check for its existence */
- create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
- ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n",sfn,dfn);
- x = last_message_index(vmu, urgdir) + 1;
- make_file(sfn, sizeof(sfn), dir, msgnum);
- make_file(dfn, sizeof(dfn), urgdir, x);
- RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
- }
-#endif
- /* Notification needs to happen after the copy, though. */
- if (ast_fileexists(fn, NULL, NULL)) {
-#ifdef IMAP_STORAGE
- notify_new_message(chan, vmu, vms, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
-#else
- notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
-#endif
- }
+ if (!ast_strlen_zero(fromstring)) {
+ struct ast_channel *ast;
+ if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+ char *passdata;
+ int vmlen = strlen(fromstring)*3 + 200;
+ passdata = alloca(vmlen);
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, enc_cidnum, enc_cidname, dur, date, passdata, vmlen, category, flag);
+ pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
+ len_passdata = strlen(passdata) * 2 + 3;
+ passdata2 = alloca(len_passdata);
+ fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
+ ast_channel_free(ast);
+ } else {
+ ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ }
+ } else {
+ fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
+ }
+ len_passdata = strlen(vmu->fullname) * 2 + 3;
+ passdata2 = alloca(len_passdata);
+ fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
+ if (!ast_strlen_zero(emailsubject)) {
+ struct ast_channel *ast;
+ if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+ char *passdata;
+ int vmlen = strlen(emailsubject) * 3 + 200;
+ passdata = alloca(vmlen);
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, enc_cidnum, enc_cidname, dur, date, passdata, vmlen, category, flag);
+ pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
+ fprintf(p, "Subject: %s" ENDL, passdata);
+ ast_channel_free(ast);
+ } else {
+ ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ }
+ } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
+ if (ast_strlen_zero(flag)) {
+ fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
+ } else {
+ fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
+ }
+ } else {
+ if (ast_strlen_zero(flag)) {
+ fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
+ } else {
+ fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
+ }
+ }
- /* Disposal needs to happen after the optional move and copy */
- if (ast_fileexists(fn, NULL, NULL)) {
- DISPOSE(dir, msgnum);
- }
- }
- }
+ fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
+ if (imap) {
+ /* additional information needed for IMAP searching */
+ fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
+ /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
+ fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
+ fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
+ fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
+ /* flag added for Urgent */
+ fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
+ fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
+ fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
+ fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
+ fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
+ fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
+ if (!ast_strlen_zero(category)) {
+ fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
+ } else {
+ fprintf(p, "X-Asterisk-VM-Category: " ENDL);
}
- if (res == '0') {
- goto transfer;
- } else if (res > 0)
- res = 0;
+ fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
+ fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
+ fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
+ }
+ if (!ast_strlen_zero(cidnum)) {
+ fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
+ }
+ if (!ast_strlen_zero(cidname)) {
+ fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
+ }
+ fprintf(p, "MIME-Version: 1.0" ENDL);
+ if (attach_user_voicemail) {
+ /* Something unique. */
+ snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
- if (duration < vmminsecs)
- /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
- else
- pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
- } else
- ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
-leave_vm_out:
- free_user(vmu);
-
- return res;
-}
+ fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
+ fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
+ fprintf(p, "--%s" ENDL, bound);
+ }
+ fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
+ if (emailbody) {
+ struct ast_channel *ast;
+ if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+ char *passdata;
+ int vmlen = strlen(emailbody)*3 + 200;
+ passdata = alloca(vmlen);
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
+ pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
+ fprintf(p, "%s" ENDL, passdata);
+ ast_channel_free(ast);
+ } else
+ ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else if (msgnum > -1){
+ fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long %s message (number %d)" ENDL
+ "in mailbox %s from %s, on %s so you might" ENDL
+ "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
+ dur, flag, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
+ } else {
+ fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
+ "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
+ }
-static int say_and_wait(struct ast_channel *chan, int num, const char *language)
-{
- int d;
- d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
- return d;
+ if (imap || attach_user_voicemail) {
+ if (!ast_strlen_zero(attach2)) {
+ snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
+ ast_debug(5, "creating second attachment filename %s\n", filename);
+ add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
+ snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
+ ast_debug(5, "creating attachment filename %s\n", filename);
+ add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
+ } else {
+ snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
+ ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
+ add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
+ }
+ }
}
-static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
+static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
{
-#ifdef IMAP_STORAGE
- /* we must use mbox(x) folder names, and copy the message there */
- /* simple. huh? */
- long res;
- char sequence[10];
- char mailbox[256];
-
- /* if save to Old folder, just leave in INBOX */
- if (box == 1) return 10;
- /* get the real IMAP message number for this message */
- snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
- /* Create the folder if it don't exist */
- imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
- ast_debug(5, "Checking if folder exists: %s\n",mailbox);
- if (mail_create(vms->mailstream, mailbox) == NIL)
- ast_debug(5, "Folder exists.\n");
- else
- ast_log(AST_LOG_NOTICE, "Folder %s created!\n",mbox(box));
-
- ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
- res = mail_copy(vms->mailstream, sequence, (char *)mbox(box));
- if (res == 1) return 0;
- return 1;
-#else
- char *dir = vms->curdir;
- char *username = vms->username;
- char *context = vmu->context;
- char sfn[PATH_MAX];
- char dfn[PATH_MAX];
- char ddir[PATH_MAX];
- const char *dbox = mbox(box);
- int x, i;
- create_dirpath(ddir, sizeof(ddir), context, username, dbox);
-
- if (vm_lock_path(ddir))
- return ERROR_LOCK_PATH;
+ char tmpdir[256], newtmp[256];
+ char fname[256];
+ char tmpcmd[256];
+ int tmpfd = -1;
- x = last_message_index(vmu, ddir) + 1;
+ /* Eww. We want formats to tell us their own MIME type */
+ char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
- if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
- x--;
- for (i = 1; i <= x; i++) {
- /* Push files down a "slot". The oldest file (msg0000) will be deleted. */
- make_file(sfn, sizeof(sfn), ddir, i);
- make_file(dfn, sizeof(dfn), ddir, i - 1);
- if (EXISTS(ddir, i, sfn, NULL)) {
- RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
- } else
- break;
- }
- } else {
- if (x >= vmu->maxmsg) {
- ast_unlock_path(ddir);
- return -1;
+ if (vmu->volgain < -.001 || vmu->volgain > .001) {
+ create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
+ snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
+ tmpfd = mkstemp(newtmp);
+ chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
+ ast_debug(3, "newtmp: %s\n", newtmp);
+ if (tmpfd > -1) {
+ int soxstatus;
+ snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
+ if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
+ attach = newtmp;
+ ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
+ } else {
+ ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
+ soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
+ ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
+ }
}
}
- make_file(sfn, sizeof(sfn), dir, msg);
- make_file(dfn, sizeof(dfn), ddir, x);
- if (strcmp(sfn, dfn)) {
- COPY(dir, msg, ddir, x, username, context, sfn, dfn);
+ fprintf(p, "--%s" ENDL, bound);
+ if (msgnum > -1)
+ fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
+ else
+ fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, attach, format);
+ fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
+ fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
+ if (msgnum > -1)
+ fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
+ else
+ fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, attach, format);
+ snprintf(fname, sizeof(fname), "%s.%s", attach, format);
+ base_encode(fname, p);
+ if (last)
+ fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
+ if (tmpfd > -1) {
+ unlink(fname);
+ close(tmpfd);
+ unlink(newtmp);
}
- ast_unlock_path(ddir);
-#endif
return 0;
}
+#undef ENDL
-static int adsi_logo(unsigned char *buf)
+static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag)
{
- int bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
- return bytes;
+ FILE *p=NULL;
+ char tmp[80] = "/tmp/astmail-XXXXXX";
+ char tmp2[256];
+
+ if (vmu && ast_strlen_zero(vmu->email)) {
+ ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
+ return(0);
+ }
+ if (!strcmp(format, "wav49"))
+ format = "WAV";
+ ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
+ /* Make a temporary file instead of piping directly to sendmail, in case the mail
+ command hangs */
+ if ((p = vm_mkftemp(tmp)) == NULL) {
+ ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
+ return -1;
+ } else {
+ make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
+ fclose(p);
+ snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
+ ast_safe_system(tmp2);
+ ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
+ }
+ return 0;
}
-static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
+static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category, const char *flag)
{
- unsigned char buf[256];
- int bytes=0;
- int x;
- char num[5];
-
- *useadsi = 0;
- bytes += ast_adsi_data_mode(buf + bytes);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ char date[256];
+ char host[MAXHOSTNAMELEN] = "";
+ char who[256];
+ char dur[PATH_MAX];
+ char tmp[80] = "/tmp/astmail-XXXXXX";
+ char tmp2[PATH_MAX];
+ struct ast_tm tm;
+ FILE *p;
- bytes = 0;
- bytes += adsi_logo(buf);
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
-#ifdef DISPLAY
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
-#endif
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_data_mode(buf + bytes);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ if ((p = vm_mkftemp(tmp)) == NULL) {
+ ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
+ return -1;
+ }
+ gethostname(host, sizeof(host)-1);
+ if (strchr(srcemail, '@'))
+ ast_copy_string(who, srcemail, sizeof(who));
+ else
+ snprintf(who, sizeof(who), "%s@%s", srcemail, host);
+ snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
+ ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
+ fprintf(p, "Date: %s\n", date);
- if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
- bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
- return 0;
+ if (*pagerfromstring) {
+ struct ast_channel *ast;
+ if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+ char *passdata;
+ int vmlen = strlen(fromstring)*3 + 200;
+ passdata = alloca(vmlen);
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
+ pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
+ fprintf(p, "From: %s <%s>\n", passdata, who);
+ ast_channel_free(ast);
+ } else
+ ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else
+ fprintf(p, "From: Asterisk PBX <%s>\n", who);
+ fprintf(p, "To: %s\n", pager);
+ if (pagersubject) {
+ struct ast_channel *ast;
+ if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+ char *passdata;
+ int vmlen = strlen(pagersubject) * 3 + 200;
+ passdata = alloca(vmlen);
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
+ pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
+ fprintf(p, "Subject: %s\n\n", passdata);
+ ast_channel_free(ast);
+ } else
+ ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else {
+ if (ast_strlen_zero(flag)) {
+ fprintf(p, "Subject: New VM\n\n");
+ } else {
+ fprintf(p, "Subject: New %s VM\n\n", flag);
+ }
}
-#ifdef DISPLAY
- /* Add a dot */
- bytes = 0;
- bytes += ast_adsi_logo(buf);
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
- bytes = 0;
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
+ ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
+ if (pagerbody) {
+ struct ast_channel *ast;
+ if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
+ char *passdata;
+ int vmlen = strlen(pagerbody) * 3 + 200;
+ passdata = alloca(vmlen);
+ memset(passdata, 0, vmlen);
+ prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
+ pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
+ fprintf(p, "%s\n", passdata);
+ ast_channel_free(ast);
+ } else
+ ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
+ } else {
+ fprintf(p, "New %s long %s msg in box %s\n"
+ "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
+ }
+ fclose(p);
+ snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
+ ast_safe_system(tmp2);
+ ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
+ return 0;
+}
-#ifdef DISPLAY
- /* Add another dot */
- bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
+/*!
+ * \brief Gets the current date and time, as formatted string.
+ * \param s The buffer to hold the output formatted date.
+ * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
+ *
+ * The date format string used is "%a %b %e %r UTC %Y".
+ *
+ * \return zero on success, -1 on error.
+ */
+static int get_date(char *s, int len)
+{
+ struct ast_tm tm;
+ struct timeval t = ast_tvnow();
+
+ ast_localtime(&t, &tm, "UTC");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
+ return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
+}
- bytes = 0;
- /* These buttons we load but don't use yet */
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
+static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
+{
+ int res;
+ char fn[PATH_MAX];
+ char dest[PATH_MAX];
-#ifdef DISPLAY
- /* Add another dot */
- bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
+ snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
- bytes = 0;
- for (x=0;x<5;x++) {
- snprintf(num, sizeof(num), "%d", x);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
+ if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
+ ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
+ return -1;
}
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
-#ifdef DISPLAY
- /* Add another dot */
- bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-#endif
-
- if (ast_adsi_end_download(chan)) {
- bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
- return 0;
+ RETRIEVE(fn, -1, ext, context);
+ if (ast_fileexists(fn, NULL, NULL) > 0) {
+ res = ast_stream_and_wait(chan, fn, ecodes);
+ if (res) {
+ DISPOSE(fn, -1);
+ return res;
+ }
+ } else {
+ /* Dispose just in case */
+ DISPOSE(fn, -1);
+ res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
+ if (res)
+ return res;
+ res = ast_say_digit_str(chan, ext, ecodes, chan->language);
+ if (res)
+ return res;
}
- bytes = 0;
- bytes += ast_adsi_download_disconnect(buf + bytes);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
-
- ast_debug(1, "Done downloading scripts...\n");
-
-#ifdef DISPLAY
- /* Add last dot */
- bytes = 0;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-#endif
- ast_debug(1, "Restarting session...\n");
-
- bytes = 0;
- /* Load the session now */
- if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
- *useadsi = 1;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
- } else
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
-
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
- return 0;
+ res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
+ return res;
}
-static void adsi_begin(struct ast_channel *chan, int *useadsi)
+static void free_zone(struct vm_zone *z)
{
- int x;
- if (!ast_adsi_available(chan))
- return;
- x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
- if (x < 0)
- return;
- if (!x) {
- if (adsi_load_vmail(chan, useadsi)) {
- ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
- return;
- }
- } else
- *useadsi = 1;
+ ast_free(z);
}
-static void adsi_login(struct ast_channel *chan)
+#ifdef ODBC_STORAGE
+/*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
+static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
{
- unsigned char buf[256];
- int bytes=0;
- unsigned char keys[8];
- int x;
- if (!ast_adsi_available(chan))
- return;
+ int x = -1;
+ int res;
+ SQLHSTMT stmt;
+ char sql[PATH_MAX];
+ char rowdata[20];
+ char tmp[PATH_MAX] = "";
+ struct odbc_obj *obj;
+ char *context;
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
- for (x=0;x<8;x++)
- keys[x] = 0;
- /* Set one key for next */
- keys[3] = ADSI_KEY_APPS + 3;
+ if (newmsgs)
+ *newmsgs = 0;
+ if (oldmsgs)
+ *oldmsgs = 0;
+ if (urgentmsgs)
+ *urgentmsgs = 0;
- bytes += adsi_logo(buf + bytes);
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
- bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
- bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
- bytes += ast_adsi_set_keys(buf + bytes, keys);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-}
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox))
+ return 0;
-static void adsi_password(struct ast_channel *chan)
-{
- unsigned char buf[256];
- int bytes=0;
- unsigned char keys[8];
- int x;
- if (!ast_adsi_available(chan))
- return;
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+
+ context = strchr(tmp, '@');
+ if (context) {
+ *context = '\0';
+ context++;
+ } else
+ context = "default";
+
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt) {
+ ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ *newmsgs = atoi(rowdata);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
- for (x=0;x<8;x++)
- keys[x] = 0;
- /* Set one key for next */
- keys[3] = ADSI_KEY_APPS + 3;
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt) {
+ ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ *oldmsgs = atoi(rowdata);
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
- bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
- bytes += ast_adsi_set_keys(buf + bytes, keys);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ if (!urgentmsgs) {
+ x = 0;
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt) {
+ ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 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);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ goto yuck;
+ }
+ *urgentmsgs = atoi(rowdata);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ ast_odbc_release_obj(obj);
+ x = 0;
+ } else
+ ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
+
+yuck:
+ return x;
}
-static void adsi_folders(struct ast_channel *chan, int start, char *label)
+static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
{
- unsigned char buf[256];
- int bytes=0;
- unsigned char keys[8];
- int x,y;
-
- if (!ast_adsi_available(chan))
- return;
-
- for (x=0;x<5;x++) {
- y = ADSI_KEY_APPS + 12 + start + x;
- if (y > ADSI_KEY_APPS + 12 + 4)
- y = 0;
- keys[x] = ADSI_KEY_SKT | y;
- }
- keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
- keys[6] = 0;
- keys[7] = 0;
-
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_set_keys(buf + bytes, keys);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
-
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
}
-static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
+/*!
+ * \brief Gets the number of messages that exist in a mailbox folder.
+ * \param context
+ * \param mailbox
+ * \param folder
+ *
+ * This method is used when ODBC backend is used.
+ * \return The number of messages in this mailbox folder (zero or more).
+ */
+static int messagecount(const char *context, const char *mailbox, const char *folder)
{
- int bytes=0;
- unsigned char buf[256];
- char buf1[256], buf2[256];
- char fn2[PATH_MAX];
-
- char cid[256]="";
- char *val;
- char *name, *num;
- char datetime[21]="";
- FILE *f;
-
- unsigned char keys[8];
-
- int x;
-
- if (!ast_adsi_available(chan))
- return;
+ struct odbc_obj *obj = NULL;
+ int nummsgs = 0;
+ int res;
+ SQLHSTMT stmt = NULL;
+ char sql[PATH_MAX];
+ char rowdata[20];
+ struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
+ if (!folder)
+ folder = "INBOX";
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox))
+ return 0;
- /* Retrieve important info */
- snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
- f = fopen(fn2, "r");
- if (f) {
- while (!feof(f)) {
- fgets((char *)buf, sizeof(buf), f);
- if (!feof(f)) {
- char *stringp=NULL;
- stringp = (char *)buf;
- strsep(&stringp, "=");
- val = strsep(&stringp, "=");
- if (!ast_strlen_zero(val)) {
- if (!strcmp((char *)buf, "callerid"))
- ast_copy_string(cid, val, sizeof(cid));
- if (!strcmp((char *)buf, "origdate"))
- ast_copy_string(datetime, val, sizeof(datetime));
- }
- }
+ obj = ast_odbc_request_obj(odbc_database, 0);
+ if (obj) {
+ snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
+ stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
+ if (!stmt) {
+ ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
+ goto yuck;
}
- fclose(f);
- }
- /* New meaning for keys */
- for (x=0;x<5;x++)
- keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
- keys[6] = 0x0;
- keys[7] = 0x0;
+ res = SQLFetch(stmt);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
+ if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
+ ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ goto yuck;
+ }
+ nummsgs = atoi(rowdata);
+ SQLFreeHandle (SQL_HANDLE_STMT, stmt);
+ } else
+ ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
- if (!vms->curmsg) {
- /* No prev key, provide "Folder" instead */
- keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
- }
- if (vms->curmsg >= vms->lastmsg) {
- /* If last message ... */
- if (vms->curmsg) {
- /* but not only message, provide "Folder" instead */
- keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
+yuck:
+ if (obj)
+ ast_odbc_release_obj(obj);
+ return nummsgs;
+}
- } else {
- /* Otherwise if only message, leave blank */
- keys[3] = 1;
- }
+/**
+ * \brief Determines if the given folder has messages.
+ * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
+ *
+ * This function is used when the mailbox is stored in an ODBC back end.
+ * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
+ * \return 1 if the folder has one or more messages. zero otherwise.
+ */
+static int has_voicemail(const char *mailbox, const char *folder)
+{
+ char tmp[256], *tmp2 = tmp, *mbox, *context;
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+ while ((context = mbox = strsep(&tmp2, ","))) {
+ strsep(&context, "@");
+ if (ast_strlen_zero(context))
+ context = "default";
+ if (messagecount(context, mbox, folder))
+ return 1;
}
+ return 0;
+}
+#endif
+#ifndef IMAP_STORAGE
+/*!
+ * \brief Copies a message from one mailbox to another.
+ * \param chan
+ * \param vmu
+ * \param imbox
+ * \param msgnum
+ * \param duration
+ * \param recip
+ * \param fmt
+ * \param dir
+ *
+ * This is only used by file storage based mailboxes.
+ *
+ * \return zero on success, -1 on error.
+ */
+static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag)
+{
+ char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
+ const char *frombox = mbox(imbox);
+ int recipmsgnum;
- if (!ast_strlen_zero(cid)) {
- ast_callerid_parse(cid, &name, &num);
- if (!name)
- name = num;
- } else
- name = "Unknown Caller";
+ ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
- /* If deleted, show "undeleted" */
+ if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
+ create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
+ } else {
+ create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
+ }
+
+ if (!dir)
+ make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
+ else
+ ast_copy_string(fromdir, dir, sizeof(fromdir));
- if (vms->deleted[vms->curmsg])
- keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
+ make_file(frompath, sizeof(frompath), fromdir, msgnum);
+ make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
- /* Except "Exit" */
- keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
- snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
- strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
- snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
+ if (vm_lock_path(todir))
+ return ERROR_LOCK_PATH;
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_set_keys(buf + bytes, keys);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
+ recipmsgnum = last_message_index(recip, todir) + 1;
+ if (recipmsgnum < recip->maxmsg) {
+ make_file(topath, sizeof(topath), todir, recipmsgnum);
+ COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
+ } else {
+ ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
+ }
+ ast_unlock_path(todir);
+ notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
+
+ return 0;
+}
+#endif
+#if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+static int messagecount(const char *context, const char *mailbox, const char *folder)
+{
+ return __has_voicemail(context, mailbox, folder, 0);
}
-static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
+static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
{
- int bytes=0;
- unsigned char buf[256];
- unsigned char keys[8];
+ DIR *dir;
+ struct dirent *de;
+ char fn[256];
+ int ret = 0;
- int x;
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox))
+ return 0;
- if (!ast_adsi_available(chan))
- return;
+ if (ast_strlen_zero(folder))
+ folder = "INBOX";
+ if (ast_strlen_zero(context))
+ context = "default";
- /* New meaning for keys */
- for (x=0;x<5;x++)
- keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
+ snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
- keys[6] = 0x0;
- keys[7] = 0x0;
+ if (!(dir = opendir(fn)))
+ return 0;
- if (!vms->curmsg) {
- /* No prev key, provide "Folder" instead */
- keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
- }
- if (vms->curmsg >= vms->lastmsg) {
- /* If last message ... */
- if (vms->curmsg) {
- /* but not only message, provide "Folder" instead */
- keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
- } else {
- /* Otherwise if only message, leave blank */
- keys[3] = 1;
+ while ((de = readdir(dir))) {
+ if (!strncasecmp(de->d_name, "msg", 3)) {
+ if (shortcircuit) {
+ ret = 1;
+ break;
+ } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
+ if (shortcircuit) return 1;
+ ret++;
+ }
}
}
- /* If deleted, show "undeleted" */
- if (vms->deleted[vms->curmsg])
- keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
+ closedir(dir);
- /* Except "Exit" */
- keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
- bytes += ast_adsi_set_keys(buf + bytes, keys);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
-
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-}
-
-static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
-{
- unsigned char buf[256] = "";
- char buf1[256] = "", buf2[256] = "";
- int bytes=0;
- unsigned char keys[8];
- int x;
-
- char *newm = (vms->newmessages == 1) ? "message" : "messages";
- char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
- if (!ast_adsi_available(chan))
- return;
- if (vms->newmessages) {
- snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
- if (vms->oldmessages) {
- strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
- snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
- } else {
- snprintf(buf2, sizeof(buf2), "%s.", newm);
- }
- } else if (vms->oldmessages) {
- snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
- snprintf(buf2, sizeof(buf2), "%s.", oldm);
+ /* If we are checking INBOX, we should check Urgent as well */
+ if (strcmp(folder, "INBOX") == 0) {
+ return (ret + __has_voicemail(context, mailbox, "Urgent", shortcircuit));
} else {
- strcpy(buf1, "You have no messages.");
- buf2[0] = ' ';
- buf2[1] = '\0';
+ return ret;
}
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
-
- for (x=0;x<6;x++)
- keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
- keys[6] = 0;
- keys[7] = 0;
-
- /* Don't let them listen if there are none */
- if (vms->lastmsg < 0)
- keys[0] = 1;
- bytes += ast_adsi_set_keys(buf + bytes, keys);
-
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
-
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
}
-static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
+/**
+ * \brief Determines if the given folder has messages.
+ * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
+ * \param folder the folder to look in
+ *
+ * This function is used when the mailbox is stored in a filesystem back end.
+ * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
+ * \return 1 if the folder has one or more messages. zero otherwise.
+ */
+static int has_voicemail(const char *mailbox, const char *folder)
{
- unsigned char buf[256] = "";
- char buf1[256] = "", buf2[256] = "";
- int bytes=0;
- unsigned char keys[8];
- int x;
+ char tmp[256], *tmp2 = tmp, *mbox, *context;
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+ while ((mbox = strsep(&tmp2, ","))) {
+ if ((context = strchr(mbox, '@')))
+ *context++ = '\0';
+ else
+ context = "default";
+ if (__has_voicemail(context, mbox, folder, 1))
+ return 1;
+ }
+ return 0;
+}
- char *mess = (vms->lastmsg == 0) ? "message" : "messages";
- if (!ast_adsi_available(chan))
- return;
+static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
+{
+ char tmp[256];
+ char *context;
- /* Original command keys */
- for (x=0;x<6;x++)
- keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
+ /* If no mailbox, return immediately */
+ if (ast_strlen_zero(mailbox))
+ return 0;
- keys[6] = 0;
- keys[7] = 0;
+ if (newmsgs)
+ *newmsgs = 0;
+ if (oldmsgs)
+ *oldmsgs = 0;
+ if (urgentmsgs)
+ *urgentmsgs = 0;
- if ((vms->lastmsg + 1) < 1)
- keys[0] = 0;
+ if (strchr(mailbox, ',')) {
+ int tmpnew, tmpold, tmpurgent;
+ char *mb, *cur;
- snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
- strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+ mb = tmp;
+ while ((cur = strsep(&mb, ", "))) {
+ if (!ast_strlen_zero(cur)) {
+ if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
+ return -1;
+ else {
+ if (newmsgs)
+ *newmsgs += tmpnew;
+ if (oldmsgs)
+ *oldmsgs += tmpold;
+ if (urgentmsgs)
+ *urgentmsgs += tmpurgent;
+ }
+ }
+ }
+ return 0;
+ }
- if (vms->lastmsg + 1)
- snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
+ ast_copy_string(tmp, mailbox, sizeof(tmp));
+
+ if ((context = strchr(tmp, '@')))
+ *context++ = '\0';
else
- strcpy(buf2, "no messages.");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_set_keys(buf + bytes, keys);
+ context = "default";
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
+ if (newmsgs)
+ *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
+ if (oldmsgs)
+ *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
+ if (urgentmsgs)
+ *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-
+ return 0;
}
-/*
-static void adsi_clear(struct ast_channel *chan)
+static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
{
- char buf[256];
- int bytes=0;
- if (!ast_adsi_available(chan))
- return;
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
-
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
+ return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
}
-*/
-static void adsi_goodbye(struct ast_channel *chan)
+#endif
+
+static void run_externnotify(char *context, char *extension, const char *flag)
{
- unsigned char buf[256];
- int bytes=0;
+ char arguments[255];
+ char ext_context[256] = "";
+ int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
+ struct ast_smdi_mwi_message *mwi_msg;
- if (!ast_adsi_available(chan))
- return;
- bytes += adsi_logo(buf + bytes);
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
- bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
- bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
- bytes += ast_adsi_voice_mode(buf + bytes, 0);
+ if (!ast_strlen_zero(context))
+ snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
+ else
+ ast_copy_string(ext_context, extension, sizeof(ext_context));
- ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
-}
+ if (smdi_iface) {
+ if (ast_app_has_voicemail(ext_context, NULL))
+ ast_smdi_mwi_set(smdi_iface, extension);
+ else
+ ast_smdi_mwi_unset(smdi_iface, extension);
-/*!\brief get_folder: Folder menu
- * Plays "press 1 for INBOX messages" etc.
- * Should possibly be internationalized
- */
-static int get_folder(struct ast_channel *chan, int start)
-{
- int x;
- int d;
- char fn[PATH_MAX];
- d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
- if (d)
- return d;
- for (x = start; x< 5; x++) { /* For all folders */
- if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
- return d;
- d = ast_play_and_wait(chan, "vm-for"); /* "for" */
- if (d)
- return d;
- snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
- d = vm_play_folder_name(chan, fn);
- if (d)
- return d;
- d = ast_waitfordigit(chan, 500);
- if (d)
- return d;
+ if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
+ ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
+ if (!strncmp(mwi_msg->cause, "INV", 3))
+ ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
+ else if (!strncmp(mwi_msg->cause, "BLK", 3))
+ ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
+ ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
+ ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
+ } else {
+ ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
+ }
+ }
+
+ if (!ast_strlen_zero(externnotify)) {
+ if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
+ ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
+ } else {
+ snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
+ ast_debug(1, "Executing %s\n", arguments);
+ ast_safe_system(arguments);
+ }
}
- d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
- if (d)
- return d;
- d = ast_waitfordigit(chan, 4000);
- return d;
}
/*!
- * \brief plays a prompt and waits for a keypress.
- * \param chan
- * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
- * \param start Does not appear to be used at this time.
+ * \brief Variables used for saving a voicemail.
*
- * This is used by the main menu option to move a message to a folder or to save a message into a folder.
- * After playing the message identified by the fn parameter value, it calls get_folder(), which plays the
- * prompting for the number inputs that correspond to the available folders.
- *
- * \return zero on success, or -1 on error.
+ * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
*/
-static int get_folder2(struct ast_channel *chan, char *fn, int start)
-{
- int res = 0;
- res = ast_play_and_wait(chan, fn); /* Folder name */
- while (((res < '0') || (res > '9')) &&
- (res != '#') && (res >= 0)) {
- res = get_folder(chan, 0);
- }
- return res;
-}
+struct leave_vm_options {
+ unsigned int flags;
+ signed char record_gain;
+ char *exitcontext;
+};
/*!
- * \brief presents the option to prepend to an existing message when forwarding it.
+ * \brief Prompts the user and records a voicemail to a mailbox.
* \param chan
- * \param vmu
- * \param curdir
- * \param curmsg
- * \param vmfmts
- * \param context
- * \param record_gain
- * \param duration
- * \param vms
- *
- * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
- *
- * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
+ * \param ext
+ * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
+ *
+ *
+ *
* \return zero on success, -1 on error.
*/
-static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
- char *context, signed char record_gain, long *duration, struct vm_state *vms, char *introfile, char *flag)
+static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
{
#ifdef IMAP_STORAGE
- int res;
-#endif
- int cmd = 0;
- int retries = 0, prepend_duration = 0, already_recorded = 0;
- char msgfile[PATH_MAX], backup[PATH_MAX];
- char textfile[PATH_MAX];
- struct ast_config *msg_cfg;
- struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
-#ifndef IMAP_STORAGE
- signed char zero_gain = 0;
+ int newmsgs, oldmsgs;
+#else
+ char urgdir[PATH_MAX];
#endif
- const char *duration_str;
-
- /* Must always populate duration correctly */
- make_file(msgfile, sizeof(msgfile), curdir, curmsg);
- strcpy(textfile, msgfile);
- strcpy(backup, msgfile);
- strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
- strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
+ char txtfile[PATH_MAX];
+ char tmptxtfile[PATH_MAX];
+ struct vm_state *vms = NULL;
+ char callerid[256];
+ FILE *txt;
+ char date[256];
+ int txtdes;
+ int res = 0;
+ int msgnum;
+ int duration = 0;
+ int ausemacro = 0;
+ int ousemacro = 0;
+ int ouseexten = 0;
+ int rtmsgid = 0;
+ char tmpid[16];
+ char tmpdur[16];
+ char priority[16];
+ char origtime[16];
+ char dir[PATH_MAX];
+ char tmpdir[PATH_MAX];
+ char fn[PATH_MAX];
+ char prefile[PATH_MAX] = "";
+ char tempfile[PATH_MAX] = "";
+ char ext_context[256] = "";
+ char fmt[80];
+ char *context;
+ char ecodes[17] = "#";
+ char tmp[1024] = "";
+ char *tmpptr;
+ struct ast_vm_user *vmu;
+ struct ast_vm_user svm;
+ const char *category = NULL;
+ const char *code;
+ const char *alldtmf = "0123456789ABCD*#";
+ char flag[80];
- if ((msg_cfg = ast_config_load(textfile, config_flags)) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
- *duration = atoi(duration_str);
+ ast_copy_string(tmp, ext, sizeof(tmp));
+ ext = tmp;
+ if ((context = strchr(tmp, '@'))) {
+ *context++ = '\0';
+ tmpptr = strchr(context, '&');
} else {
- *duration = 0;
+ tmpptr = strchr(ext, '&');
}
- while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
- if (cmd)
- retries = 0;
- switch (cmd) {
- case '1':
-
-#ifdef IMAP_STORAGE
- /* Record new intro file */
- res = ast_play_and_wait(chan, INTRO);
- res = ast_play_and_wait(chan, "beep");
- res = play_record_review(chan, NULL, introfile, vmu->maxsecs, vmfmts, 1, vmu, (int *)duration, NULL, record_gain, vms, flag);
- cmd = 't';
-#else
-
- /* prepend a message to the current message, update the metadata and return */
-
- make_file(msgfile, sizeof(msgfile), curdir, curmsg);
- strcpy(textfile, msgfile);
- strncat(textfile, ".txt", sizeof(textfile) - 1);
- *duration = 0;
+ if (tmpptr)
+ *tmpptr++ = '\0';
- /* if we can't read the message metadata, stop now */
- if (!msg_cfg) {
- cmd = 0;
- break;
- }
-
- /* Back up the original file, so we can retry the prepend */
- if (already_recorded)
- ast_filecopy(backup, msgfile, NULL);
- else
- ast_filecopy(msgfile, backup, NULL);
- already_recorded = 1;
+ ast_channel_lock(chan);
+ if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
+ category = ast_strdupa(category);
+ }
+ ast_channel_unlock(chan);
- if (record_gain)
- ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
+ if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
+ ast_copy_string(flag, "Urgent", sizeof(flag));
+ } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
+ ast_copy_string(flag, "PRIORITY", sizeof(flag));
+ } else {
+ flag[0] = '\0';
+ }
- cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
- if (record_gain)
- ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
+ ast_debug(3, "Before find_user\n");
+ if (!(vmu = find_user(&svm, context, ext))) {
+ ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ return res;
+ }
+ /* Setup pre-file if appropriate */
+ if (strcmp(vmu->context, "default"))
+ snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
+ else
+ ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
-
- if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
- *duration = atoi(duration_str);
+ /* Set the path to the prefile. Will be one of
+ VM_SPOOL_DIRcontext/ext/busy
+ VM_SPOOL_DIRcontext/ext/unavail
+ Depending on the flag set in options.
+ */
+ if (ast_test_flag(options, OPT_BUSY_GREETING)) {
+ snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
+ } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
+ snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
+ }
+ /* Set the path to the tmpfile as
+ VM_SPOOL_DIR/context/ext/temp
+ and attempt to create the folder structure.
+ */
+ snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
+ if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
+ ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
+ return -1;
+ }
+ RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
+ if (ast_fileexists(tempfile, NULL, NULL) > 0)
+ ast_copy_string(prefile, tempfile, sizeof(prefile));
- if (prepend_duration) {
- struct ast_category *msg_cat;
- /* need enough space for a maximum-length message duration */
- char duration_str[12];
+ DISPOSE(tempfile, -1);
+ /* It's easier just to try to make it than to check for its existence */
+ create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
- *duration += prepend_duration;
- msg_cat = ast_category_get(msg_cfg, "message");
- snprintf(duration_str, 11, "%ld", *duration);
- if (!ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
- config_text_file_save(textfile, msg_cfg, "app_voicemail");
- STORE(curdir, vmu->mailbox, context, curmsg, chan, vmu, vmfmts, prepend_duration, vms, NULL, NULL);
- }
+ /* Check current or macro-calling context for special extensions */
+ if (ast_test_flag(vmu, VM_OPERATOR)) {
+ if (!ast_strlen_zero(vmu->exit)) {
+ if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
+ ouseexten = 1;
}
-
-#endif
- break;
- case '2':
- /* NULL out introfile so we know there is no intro! */
- introfile[0] = '\0';
- cmd = 't';
- break;
- case '*':
- cmd = '*';
- break;
- default:
- cmd = ast_play_and_wait(chan,"vm-forwardoptions");
- /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
- if (!cmd)
- cmd = ast_play_and_wait(chan,"vm-starmain");
- /* "press star to return to the main menu" */
- if (!cmd)
- cmd = ast_waitfordigit(chan,6000);
- if (!cmd)
- retries++;
- if (retries > 3)
- cmd = 't';
+ } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
+ ouseexten = 1;
+ } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
+ ousemacro = 1;
}
}
- if (msg_cfg)
- ast_config_destroy(msg_cfg);
- if (already_recorded)
- ast_filedelete(backup, NULL);
- if (prepend_duration)
- *duration = prepend_duration;
-
- if (cmd == 't' || cmd == 'S')
- cmd = 0;
- return cmd;
-}
-
-static void queue_mwi_event(const char *mbox, int urgent, int new, int old)
-{
- struct ast_event *event;
- char *mailbox, *context;
+ if (!ast_strlen_zero(vmu->exit)) {
+ if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
+ strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
+ } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
+ strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
+ else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
+ strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
+ ausemacro = 1;
+ }
- /* Strip off @default */
- context = mailbox = ast_strdupa(mbox);
- strsep(&context, "@");
- if (ast_strlen_zero(context))
- context = "default";
-
- if (!(event = ast_event_new(AST_EVENT_MWI,
- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
- AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
- AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
- AST_EVENT_IE_END))) {
- return;
+ if (ast_test_flag(options, OPT_DTMFEXIT)) {
+ for (code = alldtmf; *code; code++) {
+ char e[2] = "";
+ e[0] = *code;
+ if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
+ strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
+ }
}
- ast_event_queue_and_cache(event,
- AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR,
- AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR,
- AST_EVENT_IE_END);
-}
-
-/*!
- * \brief Sends email notification that a user has a new voicemail waiting for them.
- * \param chan
- * \param vmu
- * \param vms
- * \param msgnum
- * \param duration
- * \param fmt
- * \param cidnum The Caller ID phone number value.
- * \param cidname The Caller ID name value.
- *
- * \return zero on success, -1 on error.
- */
-static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag)
-{
- char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
- int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
- const char *category;
- char *myserveremail = serveremail;
-
- ast_channel_lock(chan);
- if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
- category = ast_strdupa(category);
+ /* Play the beginning intro if desired */
+ if (!ast_strlen_zero(prefile)) {
+#ifdef ODBC_STORAGE
+ int success =
+#endif
+ RETRIEVE(prefile, -1, ext, context);
+ if (ast_fileexists(prefile, NULL, NULL) > 0) {
+ if (ast_streamfile(chan, prefile, chan->language) > -1)
+ res = ast_waitstream(chan, ecodes);
+#ifdef ODBC_STORAGE
+ if (success == -1) {
+ /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
+ ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
+ store_file(prefile, vmu->mailbox, vmu->context, -1);
+ }
+#endif
+ } else {
+ ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
+ res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
+ }
+ DISPOSE(prefile, -1);
+ if (res < 0) {
+ ast_debug(1, "Hang up during prefile playback\n");
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ return -1;
+ }
}
- ast_channel_unlock(chan);
-
- make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
- make_file(fn, sizeof(fn), todir, msgnum);
- snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
-
- if (!ast_strlen_zero(vmu->attachfmt)) {
- if (strstr(fmt, vmu->attachfmt))
- fmt = vmu->attachfmt;
- else
- ast_log(AST_LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->attachfmt, fmt, vmu->mailbox, vmu->context);
+ if (res == '#') {
+ /* On a '#' we skip the instructions */
+ ast_set_flag(options, OPT_SILENT);
+ res = 0;
}
-
- /* Attach only the first format */
- fmt = ast_strdupa(fmt);
- stringp = fmt;
- strsep(&stringp, "|");
-
- if (!ast_strlen_zero(vmu->serveremail))
- myserveremail = vmu->serveremail;
-
- if (!ast_strlen_zero(vmu->email)) {
- int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
- if (!attach_user_voicemail)
- attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
-
- if (attach_user_voicemail)
- RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
-
- /* XXX possible imap issue, should category be NULL XXX */
- sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
-
- if (attach_user_voicemail)
- DISPOSE(todir, msgnum);
+ if (!res && !ast_test_flag(options, OPT_SILENT)) {
+ res = ast_stream_and_wait(chan, INTRO, ecodes);
+ if (res == '#') {
+ ast_set_flag(options, OPT_SILENT);
+ res = 0;
+ }
+ }
+ if (res > 0)
+ ast_stopstream(chan);
+ /* Check for a '*' here in case the caller wants to escape from voicemail to something
+ other than the operator -- an automated attendant or mailbox login for example */
+ if (res == '*') {
+ chan->exten[0] = 'a';
+ chan->exten[1] = '\0';
+ if (!ast_strlen_zero(vmu->exit)) {
+ ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
+ } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
+ ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
+ }
+ chan->priority = 0;
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
+ return 0;
}
- if (!ast_strlen_zero(vmu->pager))
- sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category, flag);
+ /* Check for a '0' here */
+ if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
+ transfer:
+ if (ouseexten || ousemacro) {
+ chan->exten[0] = 'o';
+ chan->exten[1] = '\0';
+ if (!ast_strlen_zero(vmu->exit)) {
+ ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
+ } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
+ ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
+ }
+ ast_play_and_wait(chan, "transfer");
+ chan->priority = 0;
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
+ }
+ return 0;
+ }
- if (ast_test_flag(vmu, VM_DELETE))
- DELETE(todir, msgnum, fn);
+ /* Allow all other digits to exit Voicemail and return to the dialplan */
+ if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
+ if (!ast_strlen_zero(options->exitcontext))
+ ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
+ return res;
+ }
- /* Leave voicemail for someone */
- if (ast_app_has_voicemail(ext_context, NULL))
- ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
+ if (res < 0) {
+ free_user(vmu);
+ pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
+ return -1;
+ }
+ /* The meat of recording the message... All the announcements and beeps have been played*/
+ ast_copy_string(fmt, vmfmts, sizeof(fmt));
+ if (!ast_strlen_zero(fmt)) {
+ msgnum = 0;
- queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
+#ifdef IMAP_STORAGE
+ /* Is ext a mailbox? */
+ /* must open stream for this user to get info! */
+ res = inboxcount(ext_context, &newmsgs, &oldmsgs);
+ if (res < 0) {
+ ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
+ return -1;
+ }
+ if (!(vms = get_vm_state_by_mailbox(ext,0))) {
+ /* It is possible under certain circumstances that inboxcount did not
+ * create a vm_state when it was needed. This is a catchall which will
+ * rarely be used.
+ */
+ if (!(vms = create_vm_state_from_user(vmu))) {
+ ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
+ return -1;
+ }
+ }
+ vms->newmessages++;
+
+ /* here is a big difference! We add one to it later */
+ msgnum = newmsgs + oldmsgs;
+ ast_debug(3, "Messagecount set to %d\n",msgnum);
+ snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
+