Newer
Older
long debug;
char tmp[256];
ast_log(LOG_ERROR, "vm_state is NULL!\n");
ast_debug(3, "vm_state user is:%s\n", vms->imapuser);
if (vms->mailstream == NIL || !vms->mailstream) {
ast_debug(1, "mailstream not set.\n");
} else {
stream = vms->mailstream;
}
/* debug = T; user wants protocol telemetry? */
debug = NIL; /* NO protocol telemetry? */
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"
/* Connect to INBOX first to get folders delimiter */
imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
ast_mutex_lock(&vms->lock);
Matthew Jordan
committed
ast_mutex_lock(&mail_open_lock);
stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
Matthew Jordan
committed
ast_mutex_unlock(&mail_open_lock);
ast_mutex_unlock(&vms->lock);
if (stream == NIL) {
ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
get_mailbox_delimiter(vms, stream);
/* update delimiter in imapfolder */
for (cp = vms->imapfolder; *cp; cp++)
if (*cp == '/')
*cp = delimiter;
}
/* Now connect to the target folder */
imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
ast_debug(3, "Before mail_open, server: %s, box:%d\n", tmp, box);
ast_mutex_lock(&vms->lock);
Matthew Jordan
committed
ast_mutex_lock(&mail_open_lock);
vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
/* Create the folder if it dosn't exist */
if (vms->mailstream && !mail_status(vms->mailstream, tmp, SA_UIDNEXT)) {
mail_create(vms->mailstream, tmp);
}
Matthew Jordan
committed
ast_mutex_unlock(&mail_open_lock);
ast_mutex_unlock(&vms->lock);
if (vms->mailstream == NIL) {
return -1;
} else {
return 0;
}
}
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
SEARCHPGM *pgm;
SEARCHHEADER *hdr;
int urgent = 0;
/* If Urgent, then look at INBOX */
if (box == 11) {
box = NEW_FOLDER;
urgent = 1;
ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
ast_copy_string(vms->imapserver, vmu->imapserver, sizeof(vms->imapserver));
ast_copy_string(vms->imapport, vmu->imapport, sizeof(vms->imapport));
ast_copy_string(vms->imapflags, vmu->imapflags, sizeof(vms->imapflags));
vms->imapversion = vmu->imapversion;
ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
if (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(vmu, box));
check_quota(vms, (char *) mbox(vmu, box));
ast_mutex_lock(&vms->lock);
pgm = mail_newsearchpgm();
/* Check IMAP folder for Asterisk messages only... */
hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
pgm->header = hdr;
pgm->deleted = 0;
pgm->undeleted = 1;
/* 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;
ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
vms->vmArrayIndex = 0;
mail_search_full (vms->mailstream, NULL, pgm, NIL);
vms->lastmsg = vms->vmArrayIndex - 1;
mail_free_searchpgm(&pgm);
/* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
* ensure to allocate enough space to account for all of them. Warn if old messages
* have not been checked first as that is required.
*/
if (box == 0 && !vms->dh_arraysize) {
ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
}
if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
ast_mutex_unlock(&vms->lock);
static void write_file(char *filename, char *buffer, unsigned long len)
if (!filename || !buffer) {
return;
}
if (!(output = fopen(filename, "w"))) {
ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", filename, strerror(errno));
return;
}
if (fwrite(buffer, len, 1, output) != 1) {
Sean Bright
committed
if (ferror(output)) {
ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
Sean Bright
committed
}
}
static void update_messages_by_imapuser(const char *user, unsigned long number)
struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
return;
ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
/* Ensure we have room for the next message. */
if (vms->vmArrayIndex >= vms->msg_array_max) {
long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
if (!new_mem) {
return;
}
vms->msgArray = new_mem;
vms->msg_array_max *= 2;
}
vms->msgArray[vms->vmArrayIndex++] = number;
void mm_searched(MAILSTREAM *stream, unsigned long number)
{
char *mailbox = stream->mailbox, buf[1024] = "", *user;
if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
return;
update_messages_by_imapuser(user, number);
static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
struct ast_variable *var;
struct ast_vm_user *vmu;
vmu = ast_calloc(1, sizeof *vmu);
if (!vmu)
return NULL;
Matthew Jordan
committed
Matthew Jordan
committed
ast_set_flag(vmu, VM_ALLOCED);
var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
if (var) {
apply_options_full(vmu, var);
ast_variables_destroy(var);
return vmu;
} else {
/* Interfaces to C-client */
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);
}
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);
}
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);
}
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;
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");
}
void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
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");
void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
struct ast_str *str;
if (!DEBUG_ATLEAST(5) || !(str = ast_str_create(256))) {
3293
3294
3295
3296
3297
3298
3299
3300
3301
3302
3303
3304
3305
3306
3307
3308
3309
3310
3311
3312
3313
3314
return;
}
ast_str_append(&str, 0, " Mailbox %s", mailbox);
if (status->flags & SA_MESSAGES) {
ast_str_append(&str, 0, ", %lu messages", status->messages);
}
if (status->flags & SA_RECENT) {
ast_str_append(&str, 0, ", %lu recent", status->recent);
}
if (status->flags & SA_UNSEEN) {
ast_str_append(&str, 0, ", %lu unseen", status->unseen);
}
if (status->flags & SA_UIDVALIDITY) {
ast_str_append(&str, 0, ", %lu UID validity", status->uidvalidity);
}
if (status->flags & SA_UIDNEXT) {
ast_str_append(&str, 0, ", %lu next UID", status->uidnext);
}
ast_log(LOG_DEBUG, "%s\n", ast_str_buffer(str));
ast_free(str);
void mm_log(char *string, long errflg)
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);
void mm_dlog(char *string)
ast_log(AST_LOG_NOTICE, "%s\n", string);
}
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);
}
}
if (!vmu) {
if ((vmu = find_user_realtime_imapuser(mb->user))) {
ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
free_user(vmu);
}
Russell Bryant
committed
}
void mm_critical(MAILSTREAM * stream)
void mm_nocritical(MAILSTREAM * stream)
Mark Michelson
committed
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 (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || (!(vms = get_vm_state_by_imapuser(user, 2)) && !(vms = get_vm_state_by_imapuser(user, 0)))) {
ast_log(AST_LOG_ERROR, "No state found.\n");
return;
ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
Mark Michelson
committed
vms->quota_usage = usage;
vms->quota_limit = limit;
}
Matt O'Gorman
committed
static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
{
char *start, *eol_pnt;
int taglen;
Matt O'Gorman
committed
if (ast_strlen_zero(header) || ast_strlen_zero(tag))
return NULL;
taglen = strlen(tag) + 1;
if (taglen < 1)
return NULL;
Matthew Jordan
committed
if (!(start = strcasestr(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, *eol_pnt, *quote;
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';
}
if ((eol_pnt = strchr(quote + 1, '"'))) {
*eol_pnt = '\0';
}
return quote + 1;
}
static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
{
struct vm_state *vms_p;
pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
return vms_p;
}
ast_debug(5, "Adding new vmstate for %s\n", vmu->imapuser);
/* XXX: Is this correctly freed always? */
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->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
ast_copy_string(vms_p->imapserver, vmu->imapserver, sizeof(vms_p->imapserver));
ast_copy_string(vms_p->imapport, vmu->imapport, sizeof(vms_p->imapport));
ast_copy_string(vms_p->imapflags, vmu->imapflags, sizeof(vms_p->imapflags));
ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
vms_p->mailstream = NIL; /* save for access from interactive entry point */
vms_p->imapversion = vmu->imapversion;
ast_debug(5, "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(vmu, 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(const char *user, int interactive)
{
struct vmstate *vlist = NULL;
if (interactive) {
struct vm_state *vms;
pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
if ((vms = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms->imapuser, user)) {
return vms;
}
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->imapversion != imapversion) {
continue;
}
if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
AST_LIST_UNLOCK(&vmstates);
return vlist->vms;
AST_LIST_UNLOCK(&vmstates);
Matt O'Gorman
committed
ast_debug(3, "%s not found in vmstates\n", user);
}
static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
{
struct vmstate *vlist = NULL;
const char *local_context = S_OR(context, "default");
if (interactive) {
struct vm_state *vms;
pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
if ((vms = pthread_getspecific(ts_vmstate.key)) &&
!strcmp(vms->username,mailbox) && !strcmp(vms->context, local_context)) {
return vms;
}
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->imapversion != imapversion) {
continue;
}
ast_debug(3, "comparing mailbox %s@%s (i=%d) to vmstate mailbox %s@%s (i=%d)\n", mailbox, local_context, interactive, vlist->vms->username, vlist->vms->context, vlist->vms->interactive);
if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
ast_debug(3, "Found it!\n");
AST_LIST_UNLOCK(&vmstates);
return vlist->vms;
Matt O'Gorman
committed
}
AST_LIST_UNLOCK(&vmstates);
ast_debug(3, "%s not found in vmstates\n", mailbox);
return NULL;
Matt O'Gorman
committed
}
static void vmstate_insert(struct vm_state *vms)
Matt O'Gorman
committed
{
struct vmstate *v;
struct vm_state *altvms;
Matt O'Gorman
committed
/* 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, vms->context, 0);
ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
vms->newmessages = altvms->newmessages;
vms->oldmessages = altvms->oldmessages;
vms->vmArrayIndex = altvms->vmArrayIndex;
/* XXX: no msgArray copying? */
vms->lastmsg = altvms->lastmsg;
vms->curmsg = altvms->curmsg;
/* get a pointer to the persistent store */
vms->persist_vms = altvms;
/* Reuse the mailstream? */
#ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
vms->mailstream = altvms->mailstream;
#else
vms->mailstream = NIL;
#endif
if (!(v = ast_calloc(1, sizeof(*v))))
return;
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 void vmstate_delete(struct vm_state *vms)
struct vmstate *vc = NULL;
struct vm_state *altvms = NULL;
/* 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;
vms->mailstream = mail_close(vms->mailstream);
/* Interactive states are not stored within the persistent list */
return;
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_LIST_TRAVERSE_SAFE_END
AST_LIST_UNLOCK(&vmstates);
if (vc) {
ast_mutex_destroy(&vc->vms->lock);
ast_free(vc->vms->msgArray);
vc->vms->msgArray = NULL;
vc->vms->msg_array_max = 0;
/* XXX: is no one supposed to free vms itself? */
} else {
ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
static void set_update(MAILSTREAM * stream)
struct vm_state *vms;
char *mailbox = stream->mailbox, *user;
char buf[1024] = "";
if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
if (user && DEBUG_ATLEAST(3))
ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
return;
}
ast_debug(3, "User %s mailbox set for update.\n", user);
vms->updated = 1; /* Set updated flag since mailbox changed */
}
static void init_vm_state(struct vm_state *vms)
vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
if (!vms->msgArray) {
/* Out of mem? This can't be good. */
vms->msg_array_max = 0;
vms->vmArrayIndex = 0;
ast_mutex_init(&vms->lock);
}
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 = 0;
unsigned long newlen = 0;
if (!body || body == NIL)
return -1;
ast_mutex_lock(&vms->lock);
body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
ast_mutex_unlock(&vms->lock);
if (len > MAX_MAIL_BODY_CONTENT_SIZE) {
ast_log(AST_LOG_ERROR,
"Msgno %ld, section %s. The body's content size %ld is huge (max %ld). User:%s, mailbox %s\n",
vms->msgArray[vms->curmsg], section, len, MAX_MAIL_BODY_CONTENT_SIZE, vms->imapuser, vms->username);
return -1;
}
if (body_content != NIL && len) {
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 || !body_decoded) {
write_file(filename, (char *) body_decoded, newlen);
} else {
ast_debug(5, "Body of message is NULL.\n");
return -1;
/*!
* \brief Get delimiter via mm_list callback
* \param vms The voicemail state object
* \param stream
*
* Determines the delimiter character that is used by the underlying IMAP based mail store.
*/
/* MUTEX should already be held */
static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream) {
snprintf(tmp, sizeof(tmp), "{%s}", S_OR(vms->imapserver, imapserver));
mail_list(stream, tmp, "*");
}
/*!
* \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) {
ast_mutex_lock(&vms->lock);
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);
}
ast_mutex_unlock(&vms->lock);
}
#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)
switch (ast_lock_path(path)) {
case AST_LOCK_TIMEOUT:
return -1;
default:
return 0;
}
}
#define MSG_ID_LEN 256
/* Used to attach a unique identifier to an msg_id */
static int msg_id_incrementor;
/*!
* \brief Sets the destination string to a uniquely identifying msg_id string
* \param dst pointer to a character buffer that should contain MSG_ID_LEN characters.
*/
static void generate_msg_id(char *dst);
#ifdef ODBC_STORAGE
struct generic_prepare_struct {
char *sql;
int argc;
char **argv;
};
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
struct generic_prepare_struct *gps = data;
int res, i;
SQLHSTMT stmt;
res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
if (!SQL_SUCCEEDED(res)) {
ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
return NULL;
}
res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
if (!SQL_SUCCEEDED(res)) {
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;
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834
3835
static void odbc_update_msg_id(char *dir, int msg_num, char *msg_id)
{
SQLHSTMT stmt;
char sql[PATH_MAX];
struct odbc_obj *obj;
char msg_num_str[20];
char *argv[] = { msg_id, dir, msg_num_str };
struct generic_prepare_struct gps = { .sql = sql, .argc = 3, .argv = argv };
obj = ast_odbc_request_obj(odbc_database, 0);
if (!obj) {
ast_log(LOG_WARNING, "Unable to update message ID for message %d in %s\n", msg_num, dir);
return;
}
snprintf(msg_num_str, sizeof(msg_num_str), "%d", msg_num);
snprintf(sql, sizeof(sql), "UPDATE %s SET msg_id=? WHERE dir=? AND msgnum=?", 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);
} else {
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
}
ast_odbc_release_obj(obj);
return;
}
* \brief Retrieves a file from an ODBC data store.
* \param dir the path to the file to be retrieved.
* \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)
int fd = -1;
size_t fdlen = 0;
void *fdm = MAP_FAILED;
SQLSMALLINT colcount = 0;
SQLHSTMT stmt;
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 odbc_obj *obj;
obj = ast_odbc_request_obj(odbc_database, 0);
if (!obj) {
ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
return -1;
}
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894
3895
3896
3897
3898
3899
3900
3901
3902
3903
3904
3905
3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
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 bail;
}
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 bail;
}
res = SQLFetch(stmt);
if (!SQL_SUCCEEDED(res)) {
if (res != SQL_NO_DATA) {
ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3919
3920
3921
3922
3923
3924
3925
3926
3927
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
goto bail_with_handle;
}
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));
goto bail_with_handle;
}
res = SQLNumResultCols(stmt, &colcount);
if (!SQL_SUCCEEDED(res)) {
ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
goto bail_with_handle;
}
fprintf(f, "[message]\n");
for (x = 0; x < colcount; x++) {
rowdata[0] = '\0';
colsize = 0;
collen = sizeof(coltitle);
res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen,
&datatype, &colsize, &decimaldigits, &nullable);
if (!SQL_SUCCEEDED(res)) {
ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
goto bail_with_handle;
}
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);
goto bail_with_handle;
res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
munmap(fdm, CHUNKSIZE);
if (!SQL_SUCCEEDED(res)) {
ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
unlink(full_fn);
goto bail_with_handle;
if (truncate(full_fn, fdlen) < 0) {
ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
}
} else {
res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
if (res == SQL_NULL_DATA && !strcasecmp(coltitle, "msg_id")) {
char msg_id[MSG_ID_LEN];
generate_msg_id(msg_id);
snprintf(rowdata, sizeof(rowdata), "%s", msg_id);
odbc_update_msg_id(dir, msgnum, msg_id);
} else if (!SQL_SUCCEEDED(res)) {
ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
goto bail_with_handle;
}
if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir")) {
fprintf(f, "%s=%s\n", coltitle, rowdata);
}
bail_with_handle:
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
bail:
if (f)
fclose(f);
if (fd > -1)
close(fd);