Skip to content
Snippets Groups Projects
app_voicemail.c 414 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    /*
    
     * Asterisk -- An open source telephony toolkit.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Copyright (C) 1999 - 2006, Digium, Inc.
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * Mark Spencer <markster@digium.com>
    
    Mark Spencer's avatar
    Mark Spencer committed
     *
    
     * 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/)
    
    Russell Bryant's avatar
    Russell Bryant committed
     * \par See also
     * \arg \ref Config_vm
    
     * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
    
    Russell Bryant's avatar
    Russell Bryant committed
     * \ingroup applications
    
     * \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.
    
    	<depend>res_smdi</depend>
    
    <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" touch_on_change="apps/app_voicemail.c apps/app_directory.c">
    
    	<member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
    		<conflict>ODBC_STORAGE</conflict>
    		<conflict>IMAP_STORAGE</conflict>
    		<defaultenabled>yes</defaultenabled>
    	</member>
    
    	<member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
    
    		<depend>ltdl</depend>
    
    		<conflict>IMAP_STORAGE</conflict>
    
    		<conflict>FILE_STORAGE</conflict>
    
    		<defaultenabled>no</defaultenabled>
    	</member>
    
    	<member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
    		<depend>imap_tk</depend>
    
    		<conflict>ODBC_STORAGE</conflict>
    
    		<conflict>FILE_STORAGE</conflict>
    
    		<defaultenabled>no</defaultenabled>
    	</member>
    
    #ifdef IMAP_STORAGE
    #include <ctype.h>
    #include <signal.h>
    #include <pwd.h>
    
    #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>
    
    #include "c-client.h"
    #include "imap4r1.h"
    #include "linkage.h"
    #endif
    
    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>
    
    #if defined(__FreeBSD__) || defined(__OpenBSD__)
    
    #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"
    
    #include "asterisk/astobj2.h"
    
    #include "asterisk/event.h"
    
    #include "asterisk/taskprocessor.h"
    
    #include "asterisk/test.h"
    
    #ifdef IMAP_STORAGE
    #include "asterisk/threadstorage.h"
    #endif
    
    
    /*** DOCUMENTATION
    	<application name="VoiceMail" language="en_US">
    		<synopsis>
    			Leave a Voicemail message.
    		</synopsis>
    		<syntax>
    			<parameter name="mailboxs" argsep="&amp;" 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.
    
    						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>
    		</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>
    
    	<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>
    		</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];
    
    static char imapparentfolder[64] = "\0";
    
    static char authuser[32];
    static char authpassword[42];
    
    static int imapversion = 1;
    
    static int expungeonhangup = 1;
    
    static char delimiter = '\0';
    
    struct vm_state;
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    AST_THREADSTORAGE(ts_vmstate);
    
    /* Forward declarations for IMAP */
    static int init_mailstream(struct vm_state *vms, int box);
    
    static void write_file(char *filename, char *buffer, unsigned long len);
    
    static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
    
    static void vm_imap_delete(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(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);
    
    static void update_messages_by_imapuser(const char *user, unsigned long number);
    
    static int vm_delete(char *file);
    
    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);
    
    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);
    
    Matt O'Gorman's avatar
    Matt O'Gorman committed
    	struct vm_state *vms;
    
    	AST_LIST_ENTRY(vmstate) list;
    
    static AST_LIST_HEAD_STATIC(vmstates, vmstate);
    
    #endif
    
    #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define COMMAND_TIMEOUT 5000
    
    /* Don't modify these here; set your umask at runtime instead */
    #define	VOICEMAIL_DIR_MODE	0777
    #define	VOICEMAIL_FILE_MODE	0666
    
    #define	CHUNKSIZE	65536
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    #define VOICEMAIL_CONFIG "voicemail.conf"
    #define ASTERISK_USERNAME "asterisk"
    
    
    /* 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 */
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define SENDMAIL "/usr/sbin/sendmail -t"
    
    #define INTRO "vm-intro"
    
    #define MAXMSG 100
    
    #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    #define BASELINELEN 72
    #define BASEMAXINLINE 256
    
    #ifdef IMAP_STORAGE
    #define ENDL "\r\n"
    #else
    #define ENDL "\n"
    #endif
    
    #define MAX_DATETIME_FORMAT	512
    
    #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 */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    #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 */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
    
    Jason Parker's avatar
    Jason Parker committed
    #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 */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    #define ERROR_LOCK_PATH  -100
    
    	NEW_FOLDER,
    	OLD_FOLDER,
    	WORK_FOLDER,
    	FAMILY_FOLDER,
    	FRIENDS_FOLDER,
    	GREETINGS_FOLDER
    
    	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),
    
    	OPT_MESSAGE_Urgent =   (1 << 8),
    	OPT_MESSAGE_PRIORITY = (1 << 9)
    
    	OPT_ARG_PLAYFOLDER = 1,
    
    	/* This *must* be the last value in this enum! */
    
    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),
    
    	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)
    
    static int load_config(int reload);
    
    Russell Bryant's avatar
    Russell Bryant committed
    /*! \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)
    
    	\arg \b ua - Ukrainian
    
    
    German requires the following additional soundfile:
    
    Russell Bryant's avatar
    Russell Bryant committed
    \arg \b 1F	einE (feminine)
    
    
    Spanish requires the following additional soundfile:
    
    Russell Bryant's avatar
    Russell Bryant committed
    \arg \b 1M      un (masculine)
    
    
    Dutch, Portuguese & Spanish require the following additional soundfiles:
    
    Russell Bryant's avatar
    Russell Bryant committed
    \arg \b vm-INBOXs	singular of 'new'
    \arg \b vm-Olds		singular of 'old/heard/read'
    
    Russell Bryant's avatar
    Russell Bryant committed
    \arg \b vm-INBOX	nieuwe (nl)
    \arg \b vm-Old		oude (nl)
    
    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'
    
    
    Russell Bryant's avatar
    Russell Bryant committed
    \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'
    
    Russell Bryant's avatar
    Russell Bryant committed
    \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'
    
    Russell Bryant's avatar
    Russell Bryant committed
    \arg \b nl-om		'at'?
    
    Russell Bryant's avatar
    Russell Bryant committed
    \arg \b vm-youhaveno
    
    Italian requires the following additional soundfile:
    
    For vm_intro_it:
    
    Russell Bryant's avatar
    Russell Bryant committed
    \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)
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    \arg \b vm-have     Have (you3)
    
    \arg \b vm-listen   To listen (yao4 ting1)
    
    
    
    Russell Bryant's avatar
    Russell Bryant committed
    \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. */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	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 */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	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 */
    
    	char exit[80];
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	char attachfmt[20];              /*!< Attachment format */
    
    	unsigned int flags;              /*!< VM_ flags */	
    
    	int minsecs;                     /*!< Minimum number of seconds per message for this mailbox */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	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 */
    
    	int passwordlocation;            /*!< Storage location of the password */
    
    	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 */
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    	double volgain;                  /*!< Volume gain for voicemails sent via email */
    
    	AST_LIST_ENTRY(ast_vm_user) list;
    
    /*! Voicemail time zones */
    
    	char name[80];
    	char timezone[80];
    	char msg_format[512];
    };
    
    #define VMSTATE_MAX_MSG_ARRAY 256
    
    
    /*! Voicemail mailbox state */
    
    	char context[80];
    
    	char curdir[PATH_MAX];
    	char vmbox[PATH_MAX];
    	char fn[PATH_MAX];
    
    	int curmsg;
    	int lastmsg;
    	int newmessages;
    	int oldmessages;
    
    	ast_mutex_t lock;
    
    	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 imapfolder[64];                 /*!< IMAP voicemail folder */
    
    	char introfn[PATH_MAX];              /*!< Name of prepended file */
    
    	unsigned int quota_limit;
    	unsigned int quota_usage;
    	struct vm_state *persist_vms;
    #endif
    
    static char odbc_database[80];
    
    static char odbc_table[80];
    
    #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))
    
    #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))
    
    #define DISPOSE(a,b)
    
    #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))
    
    static char ext_pass_check_cmd[128];
    
    static int my_umask;
    
    #define PWDCHANGE_INTERNAL (1 << 1)
    #define PWDCHANGE_EXTERNAL (1 << 2)
    static int pwdchange = PWDCHANGE_INTERNAL;
    
    
    #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
    
    # 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";
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    static char *addesc = "Comedian Mail";
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    /* Leave a message */
    static char *app = "VoiceMail";
    
    /* Check mail, control, etc */
    static char *app2 = "VoiceMailMain";
    
    
    static char *app4 = "VMAuthenticate";
    
    static char *sayname_app = "VMSayName";
    
    
    static AST_LIST_HEAD_STATIC(users, ast_vm_user);
    
    static char zonetag[80];
    
    static int maxdeletedmsg;
    
    static int silencethreshold = 128;
    static char serveremail[80];
    
    static char mailcmd[160];	/* Configurable mail cmd */
    static char externnotify[160]; 
    
    static struct ast_smdi_interface *smdi_iface = NULL;
    
    static int vmminsecs;
    static int vmmaxsecs;
    
    static int maxgreet;
    static int skipms;
    static int maxlogins;
    
    /*! 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_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);
    
    
    /* 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];
    
    
    Olle Johansson's avatar
    Olle Johansson committed
    /* custom password sounds */
    
    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;
    
    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 *emailsubject = NULL;
    
    static char *pagerbody = NULL;
    static char *pagersubject = NULL;
    
    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";
    
    static int adsiver = 1;
    
    static char emaildateformat[32] = "%A, %B %d, %Y at %r";
    
    static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
    
    /* Forward declarations - generic */
    
    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);
    
    static int is_valid_dtmf(const char *key);
    
    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);
    
    struct ao2_container *inprocess_container;
    
    struct inprocess {
    	int count;
    	char *context;
    	char mailbox[0];
    };
    
    static int inprocess_hash_fn(const void *obj, const int flags)
    {
    	const struct inprocess *i = obj;
    	return atoi(i->mailbox);
    }
    
    static int inprocess_cmp_fn(void *obj, void *arg, int flags)
    {
    	struct inprocess *i = obj, *j = arg;
    	if (!strcmp(i->mailbox, j->mailbox)) {
    		return 0;
    	}
    	return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
    }
    
    static int inprocess_count(const char *context, const char *mailbox, int delta)
    {
    	struct inprocess *i, *arg = alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
    	arg->context = arg->mailbox + strlen(mailbox) + 1;
    	strcpy(arg->mailbox, mailbox); /* SAFE */
    	strcpy(arg->context, context); /* SAFE */
    	ao2_lock(inprocess_container);
    
    	if ((i = ao2_find(inprocess_container, arg, 0))) {
    
    		int ret = ast_atomic_fetchadd_int(&i->count, delta);
    		ao2_unlock(inprocess_container);
    		ao2_ref(i, -1);
    
    	}
    	if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
    		ao2_unlock(inprocess_container);
    		return 0;
    	}
    	i->context = i->mailbox + strlen(mailbox) + 1;
    	strcpy(i->mailbox, mailbox); /* SAFE */
    	strcpy(i->context, context); /* SAFE */
    	i->count = delta;
    	ao2_link(inprocess_container, i);
    	ao2_unlock(inprocess_container);
    	ao2_ref(i, -1);
    
    #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
    static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
    #endif
    
    
    /*!
     * \brief Strips control and non 7-bit clean characters from input string.
     *
     * \note To map control and none 7-bit characters to a 7-bit clean characters
     *  please use ast_str_encode_mine().
     */
    static char *strip_control_and_high(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)
    
    Mark Spencer's avatar
    Mark Spencer committed
    {
    
    	ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);	
    
    		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));
    
    	ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
    
    	if (vmminsecs) {
    		vmu->minsecs = vmminsecs;
    	}
    	if (vmmaxsecs) {
    
    		vmu->maxdeletedmsg = maxdeletedmsg;
    
    	vmu->emailsubject = NULL;
    	vmu->emailbody = NULL;
    
    #ifdef IMAP_STORAGE
    	ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
    #endif