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.
*/
/*!
* \file
* \author Mark Spencer <markster@digium.com>
* \brief Comedian Mail - Voicemail System
* unixODBC (http://www.unixodbc.org/)
* A source distribution of University of Washington's IMAP c-client
* (http://www.washington.edu/imap/)
* \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
* \todo This module requires res_adsi to load. This needs to be optional
* \todo This file is now almost impossible to work with, due to all \#ifdefs.
* Feels like the database code before realtime. Someone - please come up
* with a plan to clean this up.
/*! \li \ref app_voicemail.c uses configuration file \ref voicemail.conf
* \addtogroup configuration_file Configuration Files
*/
* \page voicemail.conf voicemail.conf
* \verbinclude voicemail.conf.sample
*/
Sean Bright
committed
#include "asterisk.h"
#ifdef IMAP_STORAGE
#include <ctype.h>
#include <signal.h>
#include <pwd.h>
Russell Bryant
committed
#ifdef USE_SYSTEM_IMAP
#include <imap/c-client.h>
#include <imap/imap4r1.h>
#include <imap/linkage.h>
#elif defined (USE_SYSTEM_CCLIENT)
#include <c-client/c-client.h>
#include <c-client/imap4r1.h>
#include <c-client/linkage.h>
Russell Bryant
committed
#else
#include "c-client.h"
#include "imap4r1.h"
#include "linkage.h"
#endif
Russell Bryant
committed
#endif
Mark Michelson
committed
#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <time.h>
#include <dirent.h>
#if defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/wait.h>
#endif
Mark Michelson
committed
Sean Bright
committed
#include "asterisk/logger.h"
Kevin P. Fleming
committed
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/say.h"
#include "asterisk/module.h"
#include "asterisk/adsi.h"
#include "asterisk/app.h"
#include "asterisk/mwi.h"
Kevin P. Fleming
committed
#include "asterisk/manager.h"
#include "asterisk/dsp.h"
#include "asterisk/localtime.h"
#include "asterisk/cli.h"
#include "asterisk/utils.h"
#include "asterisk/stringfields.h"
#include "asterisk/strings.h"
Matthew Fredrickson
committed
#include "asterisk/smdi.h"
#include "asterisk/astobj2.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/test.h"
#include "asterisk/format_cache.h"
Russell Bryant
committed
#ifdef ODBC_STORAGE
Kevin P. Fleming
committed
#include "asterisk/res_odbc.h"
#ifdef IMAP_STORAGE
#include "asterisk/threadstorage.h"
#endif
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
/*** DOCUMENTATION
<application name="VoiceMail" language="en_US">
<synopsis>
Leave a Voicemail message.
</synopsis>
<syntax>
<parameter name="mailboxs" argsep="&" required="true">
<argument name="mailbox1" argsep="@" required="true">
<argument name="mailbox" required="true" />
<argument name="context" />
</argument>
<argument name="mailbox2" argsep="@" multiple="true">
<argument name="mailbox" required="true" />
<argument name="context" />
</argument>
</parameter>
<parameter name="options">
<optionlist>
<option name="b">
<para>Play the <literal>busy</literal> greeting to the calling party.</para>
</option>
<option name="d">
<argument name="c" />
<para>Accept digits for a new extension in context <replaceable>c</replaceable>,
if played during the greeting. Context defaults to the current context.</para>
</option>
<option name="e">
<para>Play greetings as early media -- only answer the channel just
before accepting the voice message.</para>
</option>
<option name="g">
<argument name="#" required="true" />
<para>Use the specified amount of gain when recording the voicemail
message. The units are whole-number decibels (dB). Only works on supported
technologies, which is DAHDI only.</para>
</option>
<option name="s">
<para>Skip the playback of instructions for leaving a message to the
calling party.</para>
</option>
<option name="S">
<para>Skip the playback of instructions for leaving a message to the
calling party, but only if a greeting has been recorded by the
mailbox user.</para>
</option>
<option name="t">
<argument name="x" required="false" />
<para>Play a custom beep tone to the caller instead of the default one.
If this option is used but no file is specified, the beep is suppressed.</para>
</option>
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
<option name="u">
<para>Play the <literal>unavailable</literal> greeting.</para>
</option>
<option name="U">
<para>Mark message as <literal>URGENT</literal>.</para>
</option>
<option name="P">
<para>Mark message as <literal>PRIORITY</literal>.</para>
</option>
</optionlist>
</parameter>
</syntax>
<description>
<para>This application allows the calling party to leave a message for the specified
list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
exist.</para>
<para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
<enumlist>
<enum name="0">
<para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
</enum>
<enum name="*">
<para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
</enum>
</enumlist>
<para>This application will set the following channel variable upon completion:</para>
<variablelist>
<variable name="VMSTATUS">
<para>This indicates the status of the execution of the VoiceMail application.</para>
<value name="SUCCESS" />
<value name="USEREXIT" />
<value name="FAILED" />
</variable>
</variablelist>
</description>
<see-also>
<ref type="application">VoiceMailMain</ref>
</see-also>
</application>
<application name="VoiceMailMain" language="en_US">
<synopsis>
Check Voicemail messages.
</synopsis>
<syntax>
<parameter name="mailbox" required="true" argsep="@">
<argument name="mailbox" />
<argument name="context" />
</parameter>
<parameter name="options">
<optionlist>
<option name="p">
<para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
the mailbox that is entered by the caller.</para>
</option>
<option name="g">
<argument name="#" required="true" />
<para>Use the specified amount of gain when recording a voicemail message.
The units are whole-number decibels (dB).</para>
</option>
<option name="r">
<para>"Read only". Prevent user from deleting any messages.</para>
<para>This applies only to specific executions of VoiceMailMain, NOT the mailbox itself.</para>
</option>
<option name="s">
<para>Skip checking the passcode for the mailbox.</para>
</option>
<option name="a">
<argument name="folder" required="true" />
<para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
Sean Bright
committed
Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
<enumlist>
<enum name="0"><para>INBOX</para></enum>
<enum name="1"><para>Old</para></enum>
<enum name="2"><para>Work</para></enum>
<enum name="3"><para>Family</para></enum>
<enum name="4"><para>Friends</para></enum>
<enum name="5"><para>Cust1</para></enum>
<enum name="6"><para>Cust2</para></enum>
<enum name="7"><para>Cust3</para></enum>
<enum name="8"><para>Cust4</para></enum>
<enum name="9"><para>Cust5</para></enum>
</enumlist>
</option>
</optionlist>
</parameter>
</syntax>
<description>
<para>This application allows the calling party to check voicemail messages. A specific
<replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
<literal>default</literal> context will be used.</para>
Alec L Davis
committed
<para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
or Password, and the extension exists:</para>
<enumlist>
<enum name="*">
<para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
</enum>
</enumlist>
</description>
<see-also>
<ref type="application">VoiceMail</ref>
</see-also>
</application>
<application name="VMAuthenticate" language="en_US">
<synopsis>
Authenticate with Voicemail passwords.
</synopsis>
<syntax>
<parameter name="mailbox" required="true" argsep="@">
<argument name="mailbox" />
<argument name="context" />
</parameter>
<parameter name="options">
<optionlist>
<option name="s">
<para>Skip playing the initial prompts.</para>
</option>
</optionlist>
</parameter>
</syntax>
<description>
<para>This application behaves the same way as the Authenticate application, but the passwords
are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
mailbox.</para>
Alec L Davis
committed
<para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
or Password, and the extension exists:</para>
<enumlist>
<enum name="*">
<para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
</enum>
</enumlist>
</description>
</application>
<application name="VoiceMailPlayMsg" language="en_US">
<synopsis>
Play a single voice mail msg from a mailbox by msg id.
</synopsis>
<syntax>
<parameter name="mailbox" required="true" argsep="@">
<argument name="mailbox" />
<argument name="context" />
</parameter>
<parameter name="msg_id" required="true">
<para>The msg id of the msg to play back. </para>
</parameter>
</syntax>
<description>
<para>This application sets the following channel variable upon completion:</para>
<variablelist>
<variable name="VOICEMAIL_PLAYBACKSTATUS">
<para>The status of the playback attempt as a text string.</para>
<value name="SUCCESS"/>
<value name="FAILED"/>
</variable>
</variablelist>
</description>
</application>
<application name="VMSayName" language="en_US">
<synopsis>
Play the name of a voicemail user
</synopsis>
<syntax>
<parameter name="mailbox" required="true" argsep="@">
<argument name="mailbox" />
<argument name="context" />
</parameter>
</syntax>
<description>
<para>This application will say the recorded name of the voicemail user specified as the
argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
<para>Similar to the Background() application, playback of the recorded
name can be interrupted by entering an extension, which will be searched
for in the current context.</para>
</description>
</application>
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
<function name="VM_INFO" language="en_US">
<synopsis>
Returns the selected attribute from a mailbox.
</synopsis>
<syntax argsep=",">
<parameter name="mailbox" argsep="@" required="true">
<argument name="mailbox" required="true" />
<argument name="context" />
</parameter>
<parameter name="attribute" required="true">
<optionlist>
<option name="count">
<para>Count of messages in specified <replaceable>folder</replaceable>.
If <replaceable>folder</replaceable> is not specified, defaults to <literal>INBOX</literal>.</para>
</option>
<option name="email">
<para>E-mail address associated with the mailbox.</para>
</option>
<option name="exists">
<para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.</para>
</option>
<option name="fullname">
<para>Full name associated with the mailbox.</para>
</option>
<option name="language">
<para>Mailbox language if overridden, otherwise the language of the channel.</para>
</option>
<option name="locale">
<para>Mailbox locale if overridden, otherwise global locale.</para>
</option>
<option name="pager">
<para>Pager e-mail address associated with the mailbox.</para>
</option>
<option name="password">
<para>Mailbox access password.</para>
</option>
<option name="tz">
<para>Mailbox timezone if overridden, otherwise global timezone</para>
</option>
</optionlist>
</parameter>
<parameter name="folder" required="false">
<para>If not specified, <literal>INBOX</literal> is assumed.</para>
</parameter>
</syntax>
<description>
<para>Returns the selected attribute from the specified <replaceable>mailbox</replaceable>.
If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
context. Where the <replaceable>folder</replaceable> can be specified, common folders
include <literal>INBOX</literal>, <literal>Old</literal>, <literal>Work</literal>,
<literal>Family</literal> and <literal>Friends</literal>.</para>
</description>
</function>
<manager name="VoicemailUsersList" language="en_US">
<synopsis>
List All Voicemail User Information.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
</syntax>
<description>
</description>
</manager>
<manager name="VoicemailUserStatus" language="en_US">
<synopsis>
Show the status of given voicemail user's info.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
<parameter name="Context" required="true">
<para>The context you want to check.</para>
</parameter>
<parameter name="Mailbox" required="true">
<para>The mailbox you want to check.</para>
</parameter>
</syntax>
<description>
<para>Retrieves the status of the given voicemail user.</para>
</description>
</manager>
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
<manager name="VoicemailRefresh" language="en_US">
<synopsis>
Tell Asterisk to poll mailboxes for a change
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
<parameter name="Context" />
<parameter name="Mailbox" />
</syntax>
<description>
<para>Normally, MWI indicators are only sent when Asterisk itself
changes a mailbox. With external programs that modify the content
of a mailbox from outside the application, an option exists called
<literal>pollmailboxes</literal> that will cause voicemail to
continually scan all mailboxes on a system for changes. This can
cause a large amount of load on a system. This command allows
external applications to signal when a particular mailbox has
changed, thus permitting external applications to modify mailboxes
and MWI to work without introducing considerable CPU load.</para>
<para>If <replaceable>Context</replaceable> is not specified, all
mailboxes on the system will be polled for changes. If
<replaceable>Context</replaceable> is specified, but
<replaceable>Mailbox</replaceable> is omitted, then all mailboxes
within <replaceable>Context</replaceable> will be polled.
Otherwise, only a single mailbox will be polled for changes.</para>
</description>
</manager>
***/
static char imapserver[48] = "localhost";
static char imapport[8] = "143";
static char imapflags[128];
static char imapfolder[64] = "INBOX";
static char imapparentfolder[64];
static char greetingfolder[64] = "INBOX";
static char authuser[32];
static char authpassword[42];
Matt O'Gorman
committed
static int expungeonhangup = 1;
static int imapgreetings;
static int imap_poll_logout;
static char delimiter;
Matthew Jordan
committed
/* mail_open cannot be protected on a stream basis */
ast_mutex_t mail_open_lock;
Matt O'Gorman
committed
struct ast_vm_user;
/* Forward declarations for IMAP */
static int init_mailstream(struct vm_state *vms, int box);
Russell Bryant
committed
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(char *file, 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(const char *user, int interactive);
static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
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 int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
static void get_mailbox_delimiter(struct vm_state *vms, 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(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag, const char *msg_id);
static void vm_imap_update_msg_id(char *dir, int msgnum, const char *msg_id, struct ast_vm_user *vmu, struct ast_config *msg_cfg, int folder);
static void update_messages_by_imapuser(const char *user, unsigned long number);
static int vm_delete(char *file);
Mark Michelson
committed
static int imap_remove_file (char *dir, int msgnum);
static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
Mark Michelson
committed
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);
static void imap_logout(const char *mailbox_id);
AST_LIST_ENTRY(vmstate) list;
static AST_LIST_HEAD_STATIC(vmstates, vmstate);
#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"
Mark Michelson
committed
/* Define fast-forward, pause, restart, and reverse keys
* while listening to a voicemail message - these are
* strings, not characters */
Mark Michelson
committed
#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
#define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
#define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
#define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
#define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
/* Default mail command to mail voicemail. Change it with the
#define SENDMAIL "/usr/sbin/sendmail -t"
#define INTRO "vm-intro"
#define MAX_MAIL_BODY_CONTENT_SIZE 134217728L // 128 Mbyte
#define MAXMSGLIMIT 9999
#define MINPASSWORD 0 /*!< Default minimum mailbox password length */
#ifdef IMAP_STORAGE
#define ENDL "\r\n"
#else
#define ENDL "\n"
#endif
#define MAX_DATETIME_FORMAT 512
Olle Johansson
committed
#define MAX_NUM_CID_CONTEXTS 10
#define VM_REVIEW (1 << 0) /*!< After recording, permit the caller to review the recording before saving */
#define VM_OPERATOR (1 << 1) /*!< Allow 0 to be pressed to go to 'o' extension */
#define VM_SAYCID (1 << 2) /*!< Repeat the CallerID info during envelope playback */
#define VM_SVMAIL (1 << 3) /*!< Allow the user to compose a new VM from within VoicemailMain */
#define VM_ENVELOPE (1 << 4) /*!< Play the envelope information (who-from, time received, etc.) */
#define VM_SAYDURATION (1 << 5) /*!< Play the length of the message during envelope playback */
#define VM_SKIPAFTERCMD (1 << 6) /*!< After deletion, assume caller wants to go to the next message */
#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) /*!< Skip the [PBX] preamble in the Subject line of emails */
#define VM_DIRECTFORWARD (1 << 10) /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
#define VM_ATTACH (1 << 11) /*!< Attach message to voicemail notifications? */
#define VM_DELETE (1 << 12) /*!< Delete message after sending notification */
#define VM_ALLOCED (1 << 13) /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
#define VM_SEARCH (1 << 14) /*!< Search all contexts for a matching mailbox */
#define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
#define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
#define VM_MESSAGEWRAP (1 << 17) /*!< Wrap around from the last message to the first, and vice-versa */
#define VM_FWDURGAUTO (1 << 18) /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
#define VM_EMAIL_EXT_RECS (1 << 19) /*!< Send voicemail emails when an external recording is added to a mailbox */
#define ERROR_MAX_MSGS -101
enum vm_box {
NEW_FOLDER = 0,
OLD_FOLDER = 1,
WORK_FOLDER = 2,
FAMILY_FOLDER = 3,
FRIENDS_FOLDER = 4,
GREETINGS_FOLDER = -1
};
Mark Michelson
committed
enum vm_option_flags {
OPT_SILENT = (1 << 0),
OPT_BUSY_GREETING = (1 << 1),
OPT_UNAVAIL_GREETING = (1 << 2),
OPT_RECORDGAIN = (1 << 3),
OPT_PREPEND_MAILBOX = (1 << 4),
OPT_AUTOPLAY = (1 << 6),
Tilghman Lesher
committed
OPT_DTMFEXIT = (1 << 7),
OPT_MESSAGE_Urgent = (1 << 8),
OPT_MESSAGE_PRIORITY = (1 << 9),
OPT_EARLYM_GREETING = (1 << 10),
OPT_BEEP = (1 << 11),
OPT_SILENT_IF_GREET = (1 << 12),
OPT_READONLY = (1 << 13),
};
enum vm_option_args {
OPT_ARG_RECORDGAIN = 0,
OPT_ARG_PLAYFOLDER = 1,
Tilghman Lesher
committed
OPT_ARG_DTMFEXIT = 2,
/* This *must* be the last value in this enum! */
};
Tilghman Lesher
committed
enum vm_passwordlocation {
OPT_PWLOC_VOICEMAILCONF = 0,
OPT_PWLOC_SPOOLDIR = 1,
OPT_PWLOC_USERSCONF = 2,
};
AST_APP_OPTIONS(vm_app_options, {
AST_APP_OPTION('s', OPT_SILENT),
AST_APP_OPTION('S', OPT_SILENT_IF_GREET),
AST_APP_OPTION('b', OPT_BUSY_GREETING),
AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
Tilghman Lesher
committed
AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY),
AST_APP_OPTION('e', OPT_EARLYM_GREETING),
AST_APP_OPTION_ARG('t', OPT_BEEP, OPT_ARG_BEEP_TONE),
AST_APP_OPTION('r', OPT_READONLY),
Kevin P. Fleming
committed
});
static const char * const mailbox_folders[] = {
#ifdef IMAP_STORAGE
imapfolder,
#else
"INBOX",
#endif
"Old",
"Work",
"Family",
"Friends",
"Cust1",
"Cust2",
"Cust3",
"Cust4",
"Cust5",
"Deleted",
"Urgent",
};
static int load_config(int reload);
#ifdef TEST_FRAMEWORK
static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
#endif
static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
/*! \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
\arg \b tw - Chinese (Taiwan)
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
Japanese requires the following additional soundfile:
\arg \b jp-arimasu there is
\arg \b jp-arimasen there is not
\arg \b jp-oshitekudasai please press
\arg \b jp-ni article ni
\arg \b jp-ga article ga
\arg \b jp-wa article wa
\arg \b jp-wo article wo
Chinese (Taiwan) requires the following additional soundfile:
\arg \b vm-tong A class-word for call (tong1)
\arg \b vm-ri A class-word for day (ri4)
\arg \b vm-you You (ni3)
\arg \b vm-haveno Have no (mei2 you3)
\arg \b vm-listen To listen (yao4 ting1)
\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.
#define MAX_VM_MBOX_ID_LEN (AST_MAX_EXTENSION)
#define MAX_VM_CONTEXT_LEN (AST_MAX_CONTEXT)
/* MAX_VM_MAILBOX_LEN allows enough room for the '@' and NULL terminator */
#define MAX_VM_MAILBOX_LEN (MAX_VM_MBOX_ID_LEN + MAX_VM_CONTEXT_LEN)
/*! Structure for linked list of users
* Use ast_vm_user_destroy() to free one of these structures. */
struct ast_vm_user {
char context[MAX_VM_CONTEXT_LEN];/*!< Voicemail context */
char mailbox[MAX_VM_MBOX_ID_LEN];/*!< Mailbox id, unique within vm context */
char password[80]; /*!< Secret pin code, numbers only */
char fullname[80]; /*!< Full name, for directory app */
char *email; /*!< E-mail address */
char *emailsubject; /*!< E-mail subject */
char *emailbody; /*!< E-mail body */
char pager[80]; /*!< E-mail address to pager (no attachment) */
char serveremail[80]; /*!< From: Mail address */
char fromstring[100]; /*!< From: Username */
char language[MAX_LANGUAGE]; /*!< Config: Language setting */
char zonetag[80]; /*!< Time zone */
Tilghman Lesher
committed
char locale[20]; /*!< The locale (for presentation of date/time) */
char callback[80];
char dialout[80];
char uniqueid[80]; /*!< Unique integer identifier */
unsigned int flags; /*!< VM_ flags */
int saydurationm;
int minsecs; /*!< Minimum number of seconds per message for this mailbox */
int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
Tilghman Lesher
committed
int passwordlocation; /*!< Storage location of the password */
char imapserver[48]; /*!< IMAP server address */
char imapport[8]; /*!< IMAP server port */
char imapflags[128]; /*!< IMAP optional flags */
char imapuser[80]; /*!< IMAP server login */
char imappassword[80]; /*!< IMAP server password if authpassword not defined */
char imapfolder[64]; /*!< IMAP voicemail folder */
char imapvmshareid[80]; /*!< Shared mailbox ID to use rather than the dialed one */
int imapversion; /*!< If configuration changes, use the new values */
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];
};
#define VMSTATE_MAX_MSG_ARRAY 256
Martin Pycko
committed
struct vm_state {
Olle Johansson
committed
char curbox[80];
char username[80];
char curdir[PATH_MAX];
char vmbox[PATH_MAX];
char fn[PATH_MAX];
char intro[PATH_MAX];
int *deleted;
int *heard;
int dh_arraysize; /* used for deleted / heard allocation */
Martin Pycko
committed
int curmsg;
int lastmsg;
int newmessages;
int oldmessages;
int urgentmessages;
Martin Pycko
committed
int starting;
int repeats;
int updated; /*!< decremented on each mail check until 1 -allows delay */
long *msgArray;
unsigned msg_array_max;
MAILSTREAM *mailstream;
int vmArrayIndex;
char imapuser[80]; /*!< IMAP server login */
char imapfolder[64]; /*!< IMAP voicemail folder */
char imapserver[48]; /*!< IMAP server address */
char imapport[8]; /*!< IMAP server port */
char imapflags[128]; /*!< IMAP optional flags */
char introfn[PATH_MAX]; /*!< Name of prepended file */
unsigned int quota_limit;
unsigned int quota_usage;
struct vm_state *persist_vms;
#endif
Martin Pycko
committed
};
Russell Bryant
committed
#ifdef ODBC_STORAGE
static char odbc_database[80] = "asterisk";
static char odbc_table[80] = "voicemessages";
Mark Michelson
committed
#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 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,d) (delete_file(a,b))
#define UPDATE_MSG_ID(a, b, c, d, e, f) (odbc_update_msg_id((a), (b), (c)))
Mark Michelson
committed
#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 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 DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
#define UPDATE_MSG_ID(a, b, c, d, e, f) (vm_imap_update_msg_id((a), (b), (c), (d), (e), (f)))
Mark Michelson
committed
#define RETRIEVE(a,b,c,d)
#define STORE(a,b,c,d,e,f,g,h,i,j,k)
#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,d) (vm_delete(c))
#define UPDATE_MSG_ID(a, b, c, d, e, f)
Kevin P. Fleming
committed
static char VM_SPOOL_DIR[PATH_MAX];
Olle Johansson
committed
static char ext_pass_cmd[128];
static char ext_pass_check_cmd[128];
Jason Parker
committed
#define PWDCHANGE_INTERNAL (1 << 1)
#define PWDCHANGE_EXTERNAL (1 << 2)
static int pwdchange = PWDCHANGE_INTERNAL;
Kevin P. Fleming
committed
#ifdef ODBC_STORAGE
#define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
Russell Bryant
committed
# ifdef IMAP_STORAGE
# define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
# else
# define tdesc "Comedian Mail (Voicemail System)"
# endif
static char userscontext[AST_MAX_EXTENSION] = "default";
static char *voicemail_app = "VoiceMail";
static char *voicemailmain_app = "VoiceMailMain";
static char *vmauthenticate_app = "VMAuthenticate";
static char *playmsg_app = "VoiceMailPlayMsg";
static char *sayname_app = "VMSayName";
static AST_LIST_HEAD_STATIC(users, ast_vm_user);
Russell Bryant
committed
static AST_LIST_HEAD_STATIC(zones, vm_zone);
Tilghman Lesher
committed
static char locale[20];
static int maxsilence;
static int maxmsg = MAXMSG;
static int silencethreshold = 128;
static char serveremail[80] = ASTERISK_USERNAME;
static char mailcmd[160] = SENDMAIL; /* Configurable mail cmd */
static char externnotify[160];
Matthew Fredrickson
committed
static struct ast_smdi_interface *smdi_iface = NULL;
static char vmfmts[80] = "wav";
Tilghman Lesher
committed
static double volgain;
static int vmminsecs;
static int vmmaxsecs;
static int maxgreet;
static int skipms = 3000;
static int maxlogins = 3;
static int minpassword = MINPASSWORD;
Tilghman Lesher
committed
static int passwordlocation;
static char aliasescontext[MAX_VM_CONTEXT_LEN];
/*! Poll mailboxes for changes since there is something external to
* app_voicemail that may change them. */
static unsigned int poll_mailboxes;
/*! By default, poll every 30 seconds */
#define DEFAULT_POLL_FREQ 30
/*! Polling frequency */
static unsigned int poll_freq = DEFAULT_POLL_FREQ;
AST_MUTEX_DEFINE_STATIC(poll_lock);
static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
static pthread_t poll_thread = AST_PTHREADT_NULL;
static unsigned char poll_thread_run;
static struct ast_taskprocessor *mwi_subscription_tps;
struct alias_mailbox_mapping {
char *alias;
char *mailbox;
char buf[0];
};
struct mailbox_alias_mapping {
char *alias;
char *mailbox;
char buf[0];
};
#define MAPPING_BUCKETS 511
static struct ao2_container *alias_mailbox_mappings;
AO2_STRING_FIELD_HASH_FN(alias_mailbox_mapping, alias);
AO2_STRING_FIELD_CMP_FN(alias_mailbox_mapping, alias);
static struct ao2_container *mailbox_alias_mappings;
AO2_STRING_FIELD_HASH_FN(mailbox_alias_mapping, mailbox);
AO2_STRING_FIELD_CMP_FN(mailbox_alias_mapping, mailbox);
Mark Michelson
committed
/* custom audio control prompts for voicemail playback */
static char listen_control_forward_key[12];
static char listen_control_reverse_key[12];
static char listen_control_pause_key[12];
static char listen_control_restart_key[12];
static char listen_control_stop_key[12];
static char vm_login[80] = "vm-login";
static char vm_newuser[80] = "vm-newuser";
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 char vm_invalid_password[80] = "vm-invalid-password";
static char vm_pls_try_again[80] = "vm-pls-try-again";
/*
* XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
* 1. create a sound along the lines of "Please try again. When done, press the pound key" which could be spliced
* from existing sound clips. This would require some programming changes in the area of vm_forward options and also
* app.c's __ast_play_and_record function