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
* \extref unixODBC (http://www.unixodbc.org/)
* \extref A source distribution of University of Washington's IMAP c-client
* (http://www.washington.edu/imap/)
* \note For information about voicemail IMAP storage, read doc/imapstorage.txt
* \note This module requires res_adsi to load. This needs to be optional
* during compilation.
*
* \note 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.
Russell Bryant
committed
/*** MAKEOPTS
<category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_voicemail.so apps/app_directory.o apps/app_directory.so">
<member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
<conflict>ODBC_STORAGE</conflict>
<conflict>IMAP_STORAGE</conflict>
<defaultenabled>yes</defaultenabled>
</member>
Russell Bryant
committed
<member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
Kevin P. Fleming
committed
<depend>generic_odbc</depend>
<conflict>IMAP_STORAGE</conflict>
<conflict>FILE_STORAGE</conflict>
Russell Bryant
committed
<defaultenabled>no</defaultenabled>
</member>
Kevin P. Fleming
committed
<member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
<depend>imap_tk</depend>
<conflict>ODBC_STORAGE</conflict>
<conflict>FILE_STORAGE</conflict>
Kevin P. Fleming
committed
<use>openssl</use>
Kevin P. Fleming
committed
<defaultenabled>no</defaultenabled>
</member>
Russell Bryant
committed
</category>
Russell Bryant
committed
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
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#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>
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/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"
#include "asterisk/taskprocessor.h"
Russell Bryant
committed
#ifdef ODBC_STORAGE
Kevin P. Fleming
committed
#include "asterisk/res_odbc.h"
#ifdef IMAP_STORAGE
#include "asterisk/threadstorage.h"
#endif
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
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
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
/*** 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="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="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>
</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="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>
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
</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>
</description>
</application>
<application name="MailboxExists" language="en_US">
<synopsis>
Check to see if Voicemail mailbox exists.
</synopsis>
<syntax>
<parameter name="mailbox" required="true" argsep="@">
<argument name="mailbox" required="true" />
<argument name="context" />
</parameter>
<parameter name="options">
<para>None options.</para>
</parameter>
</syntax>
<description>
<para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
<replaceable>context</replaceable> is specified, the <literal>default</literal> context
will be used.</para>
<para>This application will set the following channel variable upon completion:</para>
<variablelist>
<variable name="VMBOXEXISTSSTATUS">
<para>This will contain the status of the execution of the MailboxExists application.
Possible values include:</para>
<value name="SUCCESS" />
<value name="FAILED" />
</variable>
</variablelist>
</description>
</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>
</description>
</application>
<function name="MAILBOX_EXISTS" language="en_US">
<synopsis>
Tell if a mailbox is configured.
</synopsis>
<syntax argsep="@">
<parameter name="mailbox" required="true" />
<parameter name="context" />
</syntax>
<description>
<para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
context.</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>
***/
#ifdef IMAP_STORAGE
static char imapserver[48];
static char imapport[8];
static char imapflags[128];
Matt O'Gorman
committed
static char imapfolder[64];
static char imapparentfolder[64] = "\0";
Mark Michelson
committed
static char greetingfolder[64];
static char authuser[32];
static char authpassword[42];
Matt O'Gorman
committed
static int expungeonhangup = 1;
Mark Michelson
committed
static int imapgreetings = 0;
static char delimiter = '\0';
struct vm_state;
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(MAILSTREAM *stream);
static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
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);
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 */
#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
mailcmd= command in voicemail.conf */
#define SENDMAIL "/usr/sbin/sendmail -t"
#define INTRO "vm-intro"
#define MAXMSG 100
#define MAXMSGLIMIT 9999
#define MINPASSWORD 0 /*!< Default minimum mailbox password length */
#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) /*!< 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_DIRECFORWARD (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 */
enum vm_box {
Mark Michelson
committed
NEW_FOLDER,
OLD_FOLDER,
WORK_FOLDER,
FAMILY_FOLDER,
FRIENDS_FOLDER,
GREETINGS_FOLDER
};
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)
};
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
OPT_ARG_ARRAY_SIZE = 3,
};
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('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)
Kevin P. Fleming
committed
});
static int load_config(int reload);
/*! \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
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.
struct baseio {
int iocp;
int iolen;
int linelength;
int ateof;
unsigned char iobuf[BASEMAXINLINE];
};
/*! Structure for linked list of users
* Use ast_vm_user_destroy() to free one of these structures. */
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 *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 mailcmd[160]; /*!< Configurable mail command */
char language[MAX_LANGUAGE]; /*!< Config: Language setting */
char zonetag[80]; /*!< Time zone */
char callback[80];
char dialout[80];
char uniqueid[80]; /*!< Unique integer identifier */
unsigned int flags; /*!< VM_ flags */
int saydurationm;
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 imapuser[80]; /*!< IMAP server login */
char imappassword[80]; /*!< IMAP server password if authpassword not defined */
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;
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[VMSTATE_MAX_MSG_ARRAY];
MAILSTREAM *mailstream;
int vmArrayIndex;
char imapuser[80]; /*!< IMAP server login */
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];
static char odbc_table[80];
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) 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))
Mark Michelson
committed
#define DISPOSE(a,b) (imap_remove_file(a,b))
#define STORE(a,b,c,d,e,f,g,h,i,j) (imap_store_file(a,b,c,d,e,f,g,h,i,j))
#define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
#define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
#define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
Mark Michelson
committed
#define RETRIEVE(a,b,c,d)
#define STORE(a,b,c,d,e,f,g,h,i,j)
#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
#define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
#define DELETE(a,b,c,d) (vm_delete(c))
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";
/* 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 vmminsecs;
static int vmmaxsecs;
static int maxgreet;
static int skipms;
static int maxlogins;
static int minpassword;
Tilghman Lesher
committed
static int passwordlocation;
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
/*! Poll mailboxes for changes since there is something external to
* app_voicemail that may change them. */
static unsigned int poll_mailboxes;
/*! Polling frequency */
static unsigned int poll_freq;
/*! By default, poll every 30 seconds */
#define DEFAULT_POLL_FREQ 30
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;
/*! Subscription to ... MWI event subscriptions */
static struct ast_event_sub *mwi_sub_sub;
/*! Subscription to ... MWI event un-subscriptions */
static struct ast_event_sub *mwi_unsub_sub;
/*!
* \brief An MWI subscription
*
* This is so we can keep track of which mailboxes are subscribed to.
* This way, we know which mailboxes to poll when the pollmailboxes
* option is being used.
*/
struct mwi_sub {
AST_RWLIST_ENTRY(mwi_sub) entry;
int old_urgent;
int old_new;
int old_old;
uint32_t uniqueid;
char mailbox[1];
};
struct mwi_sub_task {
const char *mailbox;
const char *context;
uint32_t uniqueid;
};
static struct ast_taskprocessor *mwi_subscription_tps;
static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
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_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";
static struct ast_flags globalflags = {0};
static int saydurationminfo;
Joshua Colp
committed
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 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 int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
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);
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, struct vm_state *vms, char *flag);
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, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag);
static void apply_options(struct ast_vm_user *vmu, const char *options);
static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum);
Mark Michelson
committed
static int is_valid_dtmf(const char *key);
Tilghman Lesher
committed
static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
static int write_password_to_file(const char *secretfn, const char *password);
#if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
#endif
static char *strip_control(const char *input, char *buf, size_t buflen)
{
char *bufptr = buf;
for (; *input; input++) {
if (*input < 32) {
continue;
}
*bufptr++ = *input;
if (bufptr == buf + buflen - 1) {
break;
}
}
*bufptr = '\0';
return buf;
}
/*!
* \brief Sets default voicemail system options to a voicemail user.
*
* This applies select global settings to a newly created (dynamic) instance of a voicemail user.
* - all the globalflags
* - the saydurationminfo
* - the callcontext
* - the dialcontext
* - the exitcontext
* - vmmaxsecs, vmmaxmsg, maxdeletedmsg
* - volume gain.
*/
static void populate_defaults(struct ast_vm_user *vmu)
ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
Tilghman Lesher
committed
vmu->passwordlocation = passwordlocation;
Kevin P. Fleming
committed
if (saydurationminfo)
vmu->saydurationm = saydurationminfo;
Joshua Colp
committed
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));
ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
if (vmmaxsecs)
vmu->maxsecs = vmmaxsecs;
Kevin P. Fleming
committed
if (maxmsg)
vmu->maxmsg = maxmsg;
if (maxdeletedmsg)
vmu->maxdeletedmsg = maxdeletedmsg;
Tilghman Lesher
committed
vmu->volgain = volgain;
vmu->emailsubject = NULL;
vmu->emailbody = NULL;
/*!
* \brief Sets a a specific property value.
* \param vmu The voicemail user object to work with.
* \param var The name of the property to be set.
* \param value The value to be set to the property.
*
* The property name must be one of the understood properties. See the source for details.
*/
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));
vmu->imapversion = imapversion;
} else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
Sean Bright
committed
ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
vmu->imapversion = imapversion;
} else if (!strcasecmp(var, "imapvmshareid")) {
ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
vmu->imapversion = imapversion;
} else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
ast_set2_flag(vmu, ast_true(value), VM_DELETE);
} else if (!strcasecmp(var, "saycid")){
ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
} else if (!strcasecmp(var, "sendvoicemail")){
ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
} else if (!strcasecmp(var, "review")){
ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
} else if (!strcasecmp(var, "tempgreetwarn")){
ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
} else if (!strcasecmp(var, "messagewrap")){
ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);
ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
} else if (!strcasecmp(var, "envelope")){
ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
} else if (!strcasecmp(var, "moveheard")){
} else if (!strcasecmp(var, "sayduration")){
ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
} else if (!strcasecmp(var, "saydurationm")){
vmu->saydurationm = x;
} else {
ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
} else if (!strcasecmp(var, "forcename")){
ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
} else if (!strcasecmp(var, "forcegreetings")){
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, "maxmessage") || !strcasecmp(var, "maxsecs")) {
vmu->maxsecs = atoi(value);
if (vmu->maxsecs <= 0) {
ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
vmu->maxsecs = vmmaxsecs;
} else {
vmu->maxsecs = atoi(value);
}
if (!strcasecmp(var, "maxmessage"))
ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
} else if (!strcasecmp(var, "maxmsg")) {
vmu->maxmsg = atoi(value);
ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
vmu->maxmsg = MAXMSG;
} else if (vmu->maxmsg > MAXMSGLIMIT) {
ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
vmu->maxmsg = MAXMSGLIMIT;
}
} else if (!strcasecmp(var, "backupdeleted")) {
vmu->maxdeletedmsg = x;
else if (ast_true(value))
vmu->maxdeletedmsg = MAXMSG;
else
vmu->maxdeletedmsg = 0;