Newer
Older
* Asterisk -- An open source telephony toolkit.
* Copyright (C) 1999 - 2006, Digium, Inc.
* Mark Spencer <markster@digium.com>
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
*
* \brief Comedian Mail - Voicemail System
*
* \author Mark Spencer <markster@digium.com>
*
* \par See also
* \arg \ref Config_vm
* \ingroup applications
* \note This module requires res_adsi to load.
Russell Bryant
committed
/*** MAKEOPTS
<category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o">
Russell Bryant
committed
<member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
<depend>unixodbc</depend>
<defaultenabled>no</defaultenabled>
</member>
Kevin P. Fleming
committed
<member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
<depend>imap_tk</depend>
Kevin P. Fleming
committed
<defaultenabled>no</defaultenabled>
</member>
Russell Bryant
committed
</category>
***/
Kevin P. Fleming
committed
#include "asterisk.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <time.h>
#include <dirent.h>
#ifdef IMAP_STORAGE
#include <ctype.h>
#include <signal.h>
#include <pwd.h>
#include "c-client.h"
#include "imap4r1.h"
#include "linkage.h"
#endif
Kevin P. Fleming
committed
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/options.h"
#include "asterisk/config.h"
#include "asterisk/say.h"
#include "asterisk/module.h"
#include "asterisk/adsi.h"
#include "asterisk/app.h"
#include "asterisk/manager.h"
#include "asterisk/dsp.h"
#include "asterisk/localtime.h"
#include "asterisk/cli.h"
#include "asterisk/utils.h"
#include "asterisk/stringfields.h"
Matthew Fredrickson
committed
#include "asterisk/smdi.h"
Russell Bryant
committed
#ifdef ODBC_STORAGE
Kevin P. Fleming
committed
#include "asterisk/res_odbc.h"
static char *curhst = NULL; /* currently connected host */
static char *curusr = NULL; /* current login user */
static char imapserver[48];
static char imapport[8];
static char imapflags[128];
static char authuser[32];
static char authpassword[42];
static int expungeonhangup = 1;
static char delimiter = '\0';
struct vm_state;
static int init_mailstream (struct vm_state *vms);
static void write_file (char *filename, char *buffer, unsigned long len);
static void status (MAILSTREAM *stream);
static void display_body (BODY *body, char *pfx, long i);
static char *get_header_by_tag(char *header, char *tag);
static void vm_imap_delete(int msgnum, struct vm_state *vms);
static char *get_user_by_mailbox(char *mailbox);
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 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);
static int make_gsm_file(char *dest, char *imapuser, char *dir, int num);
static void get_mailbox_delimiter(MAILSTREAM *stream);
static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
};
AST_MUTEX_DEFINE_STATIC(vmstate_lock);
static struct vmstate *vmstates = NULL;
#endif
#define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
/* Don't modify these here; set your umask at runtime instead */
#define VOICEMAIL_DIR_MODE 0777
#define VOICEMAIL_FILE_MODE 0666
#define VOICEMAIL_CONFIG "voicemail.conf"
#define ASTERISK_USERNAME "asterisk"
/* Default mail command to mail voicemail. Change it with the
mailcmd= command in voicemail.conf */
#define SENDMAIL "/usr/sbin/sendmail -t"
#define INTRO "vm-intro"
#define MAXMSG 100
#define MAXMSGLIMIT 9999
#define BASEMAXINLINE 256
#define BASELINELEN 72
#define BASEMAXINLINE 256
#define eol "\r\n"
#define MAX_DATETIME_FORMAT 512
Olle Johansson
committed
#define MAX_NUM_CID_CONTEXTS 10
#define VM_REVIEW (1 << 0)
#define VM_OPERATOR (1 << 1)
#define VM_SAYCID (1 << 2)
#define VM_SVMAIL (1 << 3)
#define VM_ENVELOPE (1 << 4)
#define VM_SAYDURATION (1 << 5)
#define VM_SKIPAFTERCMD (1 << 6)
#define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
#define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
#define VM_PBXSKIP (1 << 9)
#define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
#define VM_ATTACH (1 << 11)
#define VM_DELETE (1 << 12)
#define VM_ALLOCED (1 << 13)
#define VM_SEARCH (1 << 14)
#define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
#define ERROR_LOCK_PATH -100
OPT_SILENT = (1 << 0),
OPT_BUSY_GREETING = (1 << 1),
OPT_UNAVAIL_GREETING = (1 << 2),
OPT_RECORDGAIN = (1 << 3),
OPT_PREPEND_MAILBOX = (1 << 4),
OPT_PRIORITY_JUMP = (1 << 5),
OPT_AUTOPLAY = (1 << 6),
} vm_option_flags;
enum {
OPT_ARG_RECORDGAIN = 0,
OPT_ARG_PLAYFOLDER = 1,
/* This *must* be the last value in this enum! */
OPT_ARG_ARRAY_SIZE = 2,
} vm_option_args;
AST_APP_OPTIONS(vm_app_options, {
AST_APP_OPTION('s', OPT_SILENT),
AST_APP_OPTION('b', OPT_BUSY_GREETING),
AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
Kevin P. Fleming
committed
});
static int load_config(void);
/*! \page vmlang Voicemail Language Syntaxes Supported
\par Syntaxes supported, not really language codes.
\arg \b en - English
\arg \b de - German
\arg \b es - Spanish
\arg \b fr - French
\arg \b it - Italian
\arg \b nl - Dutch
\arg \b pt - Portuguese
\arg \b pt_BR - Portuguese (Brazil)
\arg \b gr - Greek
\arg \b no - Norwegian
\arg \b se - Swedish
German requires the following additional soundfile:
Spanish requires the following additional soundfile:
Dutch, Portuguese & Spanish require the following additional soundfiles:
\arg \b vm-INBOXs singular of 'new'
\arg \b vm-Olds singular of 'old/heard/read'
NB these are plural:
Russell Bryant
committed
Polish uses:
\arg \b vm-new-a 'new', feminine singular accusative
\arg \b vm-new-e 'new', feminine plural accusative
\arg \b vm-new-ych 'new', feminine plural genitive
\arg \b vm-old-a 'old', feminine singular accusative
\arg \b vm-old-e 'old', feminine plural accusative
\arg \b vm-old-ych 'old', feminine plural genitive
\arg \b digits/1-a 'one', not always same as 'digits/1'
\arg \b digits/2-ie 'two', not always same as 'digits/2'
Swedish uses:
\arg \b vm-nytt singular of 'new'
\arg \b vm-nya plural of 'new'
\arg \b vm-gammalt singular of 'old'
\arg \b vm-gamla plural of 'old'
\arg \b digits/ett 'one', not always same as 'digits/1'
Norwegian uses:
\arg \b vm-ny singular of 'new'
\arg \b vm-nye plural of 'new'
\arg \b vm-gammel singular of 'old'
\arg \b vm-gamle plural of 'old'
Italian requires the following additional soundfile:
For vm_intro_it:
\arg \b vm-nuovo new
\arg \b vm-nuovi new plural
\arg \b vm-vecchio old
\arg \b vm-vecchi old plural
\note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
spelled among others when you have to change folder. For the above reasons, vm-INBOX
and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
struct baseio {
int iocp;
int iolen;
int linelength;
int ateof;
unsigned char iobuf[BASEMAXINLINE];
};
struct ast_vm_user {
char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
char password[80]; /*!< Secret pin code, numbers only */
char fullname[80]; /*!< Full name, for directory app */
char email[80]; /*!< E-mail address */
char pager[80]; /*!< E-mail address to pager (no attachment) */
char serveremail[80]; /*!< From: Mail address */
char mailcmd[160]; /*!< Configurable mail command */
char language[MAX_LANGUAGE]; /*!< Config: Language setting */
char zonetag[80]; /*!< Time zone */
char callback[80];
char dialout[80];
char attachfmt[20]; /*!< Attachment format */
unsigned int flags; /*!< VM_ flags */
int saydurationm;
int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
Tilghman Lesher
committed
double volgain; /*!< Volume gain for voicemails sent via email */
AST_LIST_ENTRY(ast_vm_user) list;
};
struct vm_zone {
Russell Bryant
committed
AST_LIST_ENTRY(vm_zone) list;
char name[80];
char timezone[80];
char msg_format[512];
};
Martin Pycko
committed
struct vm_state {
Olle Johansson
committed
char curbox[80];
char username[80];
char curdir[256];
char vmbox[256];
char fn[256];
char fn2[256];
int *deleted;
int *heard;
Martin Pycko
committed
int curmsg;
int lastmsg;
int newmessages;
int oldmessages;
int starting;
int repeats;
#ifdef IMAP_STORAGE
int updated; /* decremented on each mail check until 1 -allows delay */
long msgArray[256];
MAILSTREAM *mailstream;
int vmArrayIndex;
char imapuser[80]; /* IMAP server login */
int interactive;
unsigned int quota_limit;
unsigned int quota_usage;
struct vm_state *persist_vms;
#endif
Martin Pycko
committed
};
static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
Kevin P. Fleming
committed
static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
signed char record_gain);
Kevin P. Fleming
committed
static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
#ifndef ODBC_STORAGE
static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
#endif
static void apply_options(struct ast_vm_user *vmu, const char *options);
Russell Bryant
committed
#ifdef ODBC_STORAGE
static char odbc_database[80];
static char odbc_table[80];
#define RETRIEVE(a,b) retrieve_file(a,b)
#define DISPOSE(a,b) remove_file(a,b)
#define STORE(a,b,c,d) 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))
#else
#ifdef IMAP_STORAGE
#define RETRIEVE(a,b)
#define DISPOSE(a,b)
#define STORE(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))
#else
#define RETRIEVE(a,b)
#define DISPOSE(a,b)
#define STORE(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 DELETE(a,b,c) (vm_delete(c))
#endif
Kevin P. Fleming
committed
static char VM_SPOOL_DIR[PATH_MAX];
Olle Johansson
committed
static char ext_pass_cmd[128];
#if ODBC_STORAGE
#define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
#elif IMAP_STORAGE
#define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
#define tdesc "Comedian Mail (Voicemail System)"
static char userscontext[AST_MAX_EXTENSION] = "default";
"Leave a Voicemail message";
" VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
"application allows the calling party to leave a message for the specified\n"
"list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
"be taken from the first mailbox specified. Dialplan execution will stop if the\n"
"specified mailbox does not exist.\n"
" The Voicemail application will exit if any of the following DTMF digits are\n"
"received:\n"
" 0 - Jump to the 'o' extension in the current dialplan context.\n"
" * - Jump to the 'a' extension in the current dialplan context.\n"
" This application will set the following channel variable upon completion:\n"
" VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
" application. The possible values are:\n"
" SUCCESS | USEREXIT | FAILED\n\n"
" Options:\n"
" b - Play the 'busy' greeting to the calling party.\n"
" g(#) - Use the specified amount of gain when recording the voicemail\n"
" message. The units are whole-number decibels (dB).\n"
" s - Skip the playback of instructions for leaving a message to the\n"
" calling party.\n"
" u - Play the 'unavailble greeting.\n"
" j - Jump to priority n+101 if the mailbox is not found or some other\n"
" error occurs.\n";
"Check Voicemail messages";
" VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
"calling party to check voicemail messages. A specific mailbox, and optional\n"
"corresponding context, may be specified. If a mailbox is not provided, the\n"
"calling party will be prompted to enter one. If a context is not specified,\n"
"the 'default' context will be used.\n\n"
" Options:\n"
" p - Consider the mailbox parameter as a prefix to the mailbox that\n"
" is entered by the caller.\n"
" g(#) - Use the specified amount of gain when recording a voicemail\n"
" message. The units are whole-number decibels (dB).\n"
" s - Skip checking the passcode for the mailbox.\n"
" a(#) - Skip folder prompt and go directly to folder specified.\n"
static char *synopsis_vm_box_exists =
"Check to see if Voicemail mailbox exists";
static char *descrip_vm_box_exists =
" MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
"mailbox exists. If no voicemail context is specified, the 'default' context\n"
"will be used.\n"
" This application will set the following channel variable upon completion:\n"
" VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
" MailboxExists application. Possible values include:\n"
" SUCCESS | FAILED\n\n"
" Options:\n"
" j - Jump to priority n+101 if the mailbox is found.\n";
static char *synopsis_vmauthenticate =
"Authenticate with Voicemail passwords";
static char *descrip_vmauthenticate =
" VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
"same way as the Authenticate application, but the passwords are taken from\n"
"voicemail.conf.\n"
" If the mailbox is specified, only that mailbox's password will be considered\n"
"valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
"be set with the authenticated mailbox.\n\n"
" Options:\n"
" s - Skip playing the initial prompts.\n";
/* Leave a message */
static char *app = "VoiceMail";
/* Check mail, control, etc */
static char *app2 = "VoiceMailMain";
static char *app3 = "MailboxExists";
static char *app4 = "VMAuthenticate";
static AST_LIST_HEAD_STATIC(users, ast_vm_user);
Russell Bryant
committed
static AST_LIST_HEAD_STATIC(zones, vm_zone);
static int maxsilence;
static int maxmsg;
static int silencethreshold = 128;
static char serveremail[80];
Olle Johansson
committed
static char mailcmd[160]; /* Configurable mail cmd */
static char externnotify[160];
Matthew Fredrickson
committed
static struct ast_smdi_interface *smdi_iface = NULL;
static char vmfmts[80];
Tilghman Lesher
committed
static double volgain;
static int vmminmessage;
static int vmmaxmessage;
static int maxgreet;
static int skipms;
static int maxlogins;
static char vm_password[80] = "vm-password";
static char vm_newpassword[80] = "vm-newpassword";
static char vm_passchanged[80] = "vm-passchanged";
static char vm_reenterpassword[80] = "vm-reenterpassword";
static char vm_mismatch[80] = "vm-mismatch";
static struct ast_flags globalflags = {0};
static int saydurationminfo;
static char dialcontext[AST_MAX_CONTEXT];
static char callcontext[AST_MAX_CONTEXT];
static char exitcontext[AST_MAX_CONTEXT];
static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
static char *emailbody = NULL;
static char *emailsubject = NULL;
static char *pagerbody = NULL;
static char *pagersubject = NULL;
static char fromstring[100];
static char pagerfromstring[100];
static char emailtitle[100];
static char charset[32] = "ISO-8859-1";
static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
Kevin P. Fleming
committed
static char emaildateformat[32] = "%A, %B %d, %Y at %r";
static void populate_defaults(struct ast_vm_user *vmu)
ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
Kevin P. Fleming
committed
if (saydurationminfo)
vmu->saydurationm = saydurationminfo;
ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
Kevin P. Fleming
committed
if (maxmsg)
vmu->maxmsg = maxmsg;
Tilghman Lesher
committed
vmu->volgain = volgain;
static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
} else if (!strcasecmp(var, "attachfmt")) {
ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
ast_copy_string(vmu->language, value, sizeof(vmu->language));
ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
#ifdef IMAP_STORAGE
} else if (!strcasecmp(var, "imapuser")) {
ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
} else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
ast_set2_flag(vmu, ast_true(value), VM_DELETE);
ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
} else if (!strcasecmp(var, "tempgreetwarn")){
ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
} else if (!strcasecmp(var, "sayduration")){
ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
} else if (!strcasecmp(var, "saydurationm")){
if (sscanf(value, "%d", &x) == 1) {
vmu->saydurationm = x;
} else {
ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
}
ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
} else if (!strcasecmp(var, "maxmsg")) {
vmu->maxmsg = atoi(value);
ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
vmu->maxmsg = MAXMSG;
} else if (vmu->maxmsg > MAXMSGLIMIT) {
ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
vmu->maxmsg = MAXMSGLIMIT;
}
Tilghman Lesher
committed
} else if (!strcasecmp(var, "volgain")) {
sscanf(value, "%lf", &vmu->volgain);
} else if (!strcasecmp(var, "options")) {
apply_options(vmu, value);
static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
int res;
if (!ast_strlen_zero(vmu->uniqueid)) {
res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
Russell Bryant
committed
if (res > 0) {
ast_copy_string(vmu->password, password, sizeof(vmu->password));
Russell Bryant
committed
res = 0;
} else if (!res) {
res = -1;
}
}
static void apply_options(struct ast_vm_user *vmu, const char *options)
{ /* Destructively Parse options and apply */
char *stringp;
char *s;
char *var, *value;
stringp = ast_strdupa(options);
while ((s = strsep(&stringp, "|"))) {
value = s;
if ((var = strsep(&value, "=")) && value) {
apply_option(vmu, var, value);
}
}
static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
{
struct ast_variable *tmp;
tmp = var;
while (tmp) {
if (!strcasecmp(tmp->name, "password")) {
ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
} else if (!strcasecmp(tmp->name, "uniqueid")) {
ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
} else if (!strcasecmp(tmp->name, "pager")) {
ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
} else if (!strcasecmp(tmp->name, "email")) {
ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
} else if (!strcasecmp(tmp->name, "fullname")) {
ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
} else if (!strcasecmp(tmp->name, "context")) {
ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
} else
apply_option(retval, tmp->name, tmp->value);
tmp = tmp->next;
}
}
static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
struct ast_variable *var;
struct ast_vm_user *retval;
BJ Weschke
committed
if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
if (mailbox)
ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
populate_defaults(retval);
var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
else
var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
apply_options_full(retval, var);
free(retval);
}
static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
{
/* This function could be made to generate one from a database, too */
struct ast_vm_user *vmu=NULL, *cur;
AST_LIST_LOCK(&users);
if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
AST_LIST_TRAVERSE(&users, cur, list) {
if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
break;
if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
}
if (cur) {
BJ Weschke
committed
/* Make a copy, so that on a reload, we have no race */
if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
ast_set2_flag(vmu, !ivm, VM_ALLOCED);
AST_LIST_NEXT(vmu, list) = NULL;
} else
vmu = find_user_realtime(ivm, context, mailbox);
AST_LIST_UNLOCK(&users);
return vmu;
}
static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
{
/* This function could be made to generate one from a database, too */
struct ast_vm_user *cur;
int res = -1;
AST_LIST_LOCK(&users);
AST_LIST_TRAVERSE(&users, cur, list) {
if ((!context || !strcasecmp(context, cur->context)) &&
(!strcasecmp(mailbox, cur->mailbox)))
break;
}
if (cur) {
ast_copy_string(cur->password, newpass, sizeof(cur->password));
res = 0;
}
AST_LIST_UNLOCK(&users);
return res;
static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
/* There's probably a better way of doing this. */
/* That's why I've put the password change in a separate function. */
/* This could also be done with a database function */
FILE *configin;
FILE *configout;
int linenum=0;
char inbuf[256];
char orig[256];
char currcontext[256] ="";
Kevin P. Fleming
committed
char tmpin[PATH_MAX];
char tmpout[PATH_MAX];
if (!change_password_realtime(vmu, newpassword))
return;
snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
configin = fopen(tmpin,"r");
if (configin)
configout = fopen(tmpout,"w+");
else
configout = NULL;
if (!configin || !configout) {
if (configin)
else
ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
if (configout)
fclose(configout);
else
ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
return;
char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
if (fgets(inbuf, sizeof(inbuf), configin) == NULL)
/* Make a backup of it */
ast_copy_string(orig, inbuf, sizeof(orig));
/*
Read the file line by line, split each line into a comment and command section
only parse the command portion of the line
*/
if (inbuf[strlen(inbuf) - 1] == '\n')
inbuf[strlen(inbuf) - 1] = '\0';
if ((comment = strchr(inbuf, ';')))
*comment++ = '\0'; /* Now inbuf is terminated just before the comment */
if (ast_strlen_zero(inbuf)) {
fprintf(configout, "%s", orig);
continue;
}
/* Check for a context, first '[' to first ']' */
if ((tmpctx = strchr(inbuf, '['))) {
tmpctxend = strchr(tmpctx, ']');
if (tmpctxend) {
/* Valid context */
ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
}
/* This isn't a context line, check for MBX => PSWD... */
user = inbuf;
if ((pass = strchr(user, '='))) {
/* We have a line in the form of aaaaa=aaaaaa */
*pass++ = '\0';
user = ast_strip(user);
if (*pass == '>')
pass = ast_skip_blanks(pass);
/*
Since no whitespace allowed in fields, or more correctly white space
inside the fields is there for a purpose, we can just terminate pass
at the comma or EOL whichever comes first.
*/
if ((rest = strchr(pass, ',')))
*rest++ = '\0';
} else {
user = NULL;
}
/* Compare user, pass AND context */
if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
!ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
!strcasecmp(currcontext, vmu->context)) {
/* This is the line */
if (rest) {
fprintf(configout, "%s => %s,%s", user, newpassword, rest);
fprintf(configout, "%s => %s", user, newpassword);
}
/* If there was a comment on the line print it out */
if (comment) {
fprintf(configout, ";%s\n", comment);
fprintf(configout, "\n");
} else {
/* Put it back like it was */
fprintf(configout, "%s", orig);
}
}
fclose(configin);
fclose(configout);
stat(tmpin, &statbuf);
chmod(tmpout, statbuf.st_mode);
chown(tmpout, statbuf.st_uid, statbuf.st_gid);
unlink(tmpin);
rename(tmpout, tmpin);
reset_user_pw(vmu->context, vmu->mailbox, newpassword);
ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
}
Mark Spencer
committed
static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
{
Olle Johansson
committed
char buf[255];
snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
if (!ast_safe_system(buf))
ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
Mark Spencer
committed
}
static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
}
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
#ifdef IMAP_STORAGE
static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
{
char gsmdir[256];
sprintf(gsmdir,"%s/%s",dir,imapuser);
if (mkdir(gsmdir, 01777) && (errno != EEXIST)) {
ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", gsmdir, strerror(errno));
return sprintf(dest, "%s/msg%04d", dir, num);
}
/* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
return sprintf(dest, "%s/%s/msg%04d", dir, imapuser, num);
}
static void vm_imap_delete(int msgnum, struct vm_state *vms)
{
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(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);
sprintf (arg,"%lu",messageNum);
mail_setflag (vms->mailstream,arg,"\\DELETED");
static int make_file(char *dest, int len, char *dir, int num)
{
return snprintf(dest, len, "%s/msg%04d", dir, num);
/*! \brief basically mkdir -p $dest/$context/$ext/$folder
* \param context String. Ignored if is null or empty string.
* \param ext String. Ignored if is null or empty string.
* \param folder String. Ignored if is null or empty string.
* \return 0 on failure, 1 on success.
*/
static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
{
mode_t mode = VOICEMAIL_DIR_MODE;
if (!ast_strlen_zero(context)) {
make_dir(dest, len, context, "", "");
ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
return 0;
}
}
if (!ast_strlen_zero(ext)) {
make_dir(dest, len, context, ext, "");
ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
return 0;
}
}
if (!ast_strlen_zero(folder)) {
make_dir(dest, len, context, ext, folder);
ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
return 0;
}
}
return 1;
}
/* 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;
}
}
Russell Bryant
committed
#ifdef ODBC_STORAGE
static int retrieve_file(char *dir, int msgnum)
{
int x = 0;
int res;
int fd=-1;
size_t fdlen = 0;
void *fdm=NULL;
SQLSMALLINT colcount=0;
SQLHSTMT stmt;
char sql[256];
char fmt[80]="";
char *c;
char coltitle[256];