Sat Nov 1 06:28:22 2008

Asterisk developer's documentation


app_voicemail.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 1999 - 2006, Digium, Inc.
00005  *
00006  * Mark Spencer <markster@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Comedian Mail - Voicemail System
00022  *
00023  * \author Mark Spencer <markster@digium.com>
00024  * 
00025  * \par See also
00026  * \arg \ref Config_vm
00027  * \ingroup applications
00028  * \note This module requires res_adsi to load.
00029  */
00030 
00031 /*** MODULEINFO
00032    <depend>res_adsi</depend>
00033    <depend>res_smdi</depend>
00034  ***/
00035 
00036 /*** MAKEOPTS
00037 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_directory.o">
00038    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00039       <depend>unixodbc</depend>
00040       <depend>ltdl</depend>
00041       <conflict>IMAP_STORAGE</conflict>
00042       <defaultenabled>no</defaultenabled>
00043    </member>
00044    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00045       <depend>imap_tk</depend>
00046       <conflict>ODBC_STORAGE</conflict>
00047       <use>ssl</use>
00048       <defaultenabled>no</defaultenabled>
00049    </member>
00050 </category>
00051  ***/
00052 
00053 #include "asterisk.h"
00054 
00055 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 152463 $")
00056 
00057 #include <stdlib.h>
00058 #include <errno.h>
00059 #include <unistd.h>
00060 #include <string.h>
00061 #include <stdlib.h>
00062 #include <stdio.h>
00063 #include <sys/time.h>
00064 #include <sys/stat.h>
00065 #include <sys/types.h>
00066 #include <sys/mman.h>
00067 #include <time.h>
00068 #include <dirent.h>
00069 #ifdef IMAP_STORAGE
00070 #include <ctype.h>
00071 #include <signal.h>
00072 #include <pwd.h>
00073 #ifdef USE_SYSTEM_IMAP
00074 #include <imap/c-client.h>
00075 #include <imap/imap4r1.h>
00076 #include <imap/linkage.h>
00077 #elif defined (USE_SYSTEM_CCLIENT)
00078 #include <c-client/c-client.h>
00079 #include <c-client/imap4r1.h>
00080 #include <c-client/linkage.h>
00081 #else
00082 #include "c-client.h"
00083 #include "imap4r1.h"
00084 #include "linkage.h"
00085 #endif
00086 #endif
00087 #include "asterisk/lock.h"
00088 #include "asterisk/file.h"
00089 #include "asterisk/logger.h"
00090 #include "asterisk/channel.h"
00091 #include "asterisk/pbx.h"
00092 #include "asterisk/options.h"
00093 #include "asterisk/config.h"
00094 #include "asterisk/say.h"
00095 #include "asterisk/module.h"
00096 #include "asterisk/adsi.h"
00097 #include "asterisk/app.h"
00098 #include "asterisk/manager.h"
00099 #include "asterisk/dsp.h"
00100 #include "asterisk/localtime.h"
00101 #include "asterisk/cli.h"
00102 #include "asterisk/utils.h"
00103 #include "asterisk/stringfields.h"
00104 #include "asterisk/smdi.h"
00105 #ifdef ODBC_STORAGE
00106 #include "asterisk/res_odbc.h"
00107 #endif
00108 
00109 #ifdef IMAP_STORAGE
00110 AST_MUTEX_DEFINE_STATIC(imaptemp_lock);
00111 static char imaptemp[1024];
00112 static char imapserver[48];
00113 static char imapport[8];
00114 static char imapflags[128];
00115 static char imapfolder[64];
00116 static char authuser[32];
00117 static char authpassword[42];
00118 
00119 static int expungeonhangup = 1;
00120 static char delimiter = '\0';
00121 static const long DEFAULT_IMAP_TCP_TIMEOUT = 60L;
00122 
00123 struct vm_state;
00124 struct ast_vm_user;
00125 
00126 static int init_mailstream (struct vm_state *vms, int box);
00127 static void write_file (char *filename, char *buffer, unsigned long len);
00128 /*static void status (MAILSTREAM *stream); */ /* No need for this. */
00129 static char *get_header_by_tag(char *header, char *tag);
00130 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu);
00131 static char *get_user_by_mailbox(char *mailbox);
00132 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
00133 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
00134 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00135 static void vmstate_insert(struct vm_state *vms);
00136 static void vmstate_delete(struct vm_state *vms);
00137 static void set_update(MAILSTREAM * stream);
00138 static void init_vm_state(struct vm_state *vms);
00139 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
00140 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
00141 static void get_mailbox_delimiter(MAILSTREAM *stream);
00142 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00143 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00144 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);
00145 static void check_quota(struct vm_state *vms, char *mailbox);
00146 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
00147 struct vmstate {
00148    struct vm_state *vms;
00149    struct vmstate *next;
00150 };
00151 AST_MUTEX_DEFINE_STATIC(vmstate_lock);
00152 static struct vmstate *vmstates = NULL;
00153 #endif
00154 
00155 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00156 
00157 #define COMMAND_TIMEOUT 5000
00158 /* Don't modify these here; set your umask at runtime instead */
00159 #define  VOICEMAIL_DIR_MODE   0777
00160 #define  VOICEMAIL_FILE_MODE  0666
00161 #define  CHUNKSIZE   65536
00162 
00163 #define VOICEMAIL_CONFIG "voicemail.conf"
00164 #define ASTERISK_USERNAME "asterisk"
00165 
00166 /* Default mail command to mail voicemail. Change it with the
00167     mailcmd= command in voicemail.conf */
00168 #define SENDMAIL "/usr/sbin/sendmail -t"
00169 
00170 #define INTRO "vm-intro"
00171 
00172 #define MAXMSG 100
00173 #ifndef IMAP_STORAGE
00174 #define MAXMSGLIMIT 9999
00175 #else
00176 #define MAXMSGLIMIT 255
00177 #endif
00178 
00179 #define BASEMAXINLINE 256
00180 #define BASELINELEN 72
00181 #define BASEMAXINLINE 256
00182 #define eol "\r\n"
00183 
00184 #define MAX_DATETIME_FORMAT   512
00185 #define MAX_NUM_CID_CONTEXTS 10
00186 
00187 #define VM_REVIEW        (1 << 0)
00188 #define VM_OPERATOR      (1 << 1)
00189 #define VM_SAYCID        (1 << 2)
00190 #define VM_SVMAIL        (1 << 3)
00191 #define VM_ENVELOPE      (1 << 4)
00192 #define VM_SAYDURATION   (1 << 5)
00193 #define VM_SKIPAFTERCMD  (1 << 6)
00194 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00195 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00196 #define VM_PBXSKIP       (1 << 9)
00197 #define VM_DIRECFORWARD  (1 << 10)  /*!< directory_forward */
00198 #define VM_ATTACH        (1 << 11)
00199 #define VM_DELETE        (1 << 12)
00200 #define VM_ALLOCED       (1 << 13)
00201 #define VM_SEARCH        (1 << 14)
00202 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00203 #define ERROR_LOCK_PATH  -100
00204 #define ERROR_MAILBOX_FULL -200
00205 
00206 
00207 enum {
00208    OPT_SILENT =           (1 << 0),
00209    OPT_BUSY_GREETING =    (1 << 1),
00210    OPT_UNAVAIL_GREETING = (1 << 2),
00211    OPT_RECORDGAIN =       (1 << 3),
00212    OPT_PREPEND_MAILBOX =  (1 << 4),
00213    OPT_PRIORITY_JUMP =    (1 << 5),
00214    OPT_AUTOPLAY =         (1 << 6),
00215 } vm_option_flags;
00216 
00217 enum {
00218    OPT_ARG_RECORDGAIN = 0,
00219    OPT_ARG_PLAYFOLDER = 1,
00220    /* This *must* be the last value in this enum! */
00221    OPT_ARG_ARRAY_SIZE = 2,
00222 } vm_option_args;
00223 
00224 AST_APP_OPTIONS(vm_app_options, {
00225    AST_APP_OPTION('s', OPT_SILENT),
00226    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00227    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00228    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00229    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00230    AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
00231    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00232 });
00233 
00234 static int load_config(void);
00235 
00236 /*! \page vmlang Voicemail Language Syntaxes Supported
00237 
00238    \par Syntaxes supported, not really language codes.
00239    \arg \b en - English
00240    \arg \b de - German
00241    \arg \b es - Spanish
00242    \arg \b fr - French
00243    \arg \b it = Italian
00244    \arg \b nl - Dutch
00245    \arg \b pt - Polish
00246    \arg \b pt - Portuguese
00247    \arg \b pt_BR - Portuguese (Brazil)
00248    \arg \b gr - Greek
00249    \arg \b no - Norwegian
00250    \arg \b se - Swedish
00251    \arg \b ua - Ukrainian
00252    \arg \b he - Hebrew
00253 
00254 German requires the following additional soundfile:
00255 \arg \b 1F  einE (feminine)
00256 
00257 Spanish requires the following additional soundfile:
00258 \arg \b 1M      un (masculine)
00259 
00260 Dutch, Portuguese & Spanish require the following additional soundfiles:
00261 \arg \b vm-INBOXs singular of 'new'
00262 \arg \b vm-Olds      singular of 'old/heard/read'
00263 
00264 NB these are plural:
00265 \arg \b vm-INBOX  nieuwe (nl)
00266 \arg \b vm-Old    oude (nl)
00267 
00268 Polish uses:
00269 \arg \b vm-new-a  'new', feminine singular accusative
00270 \arg \b vm-new-e  'new', feminine plural accusative
00271 \arg \b vm-new-ych   'new', feminine plural genitive
00272 \arg \b vm-old-a  'old', feminine singular accusative
00273 \arg \b vm-old-e  'old', feminine plural accusative
00274 \arg \b vm-old-ych   'old', feminine plural genitive
00275 \arg \b digits/1-a   'one', not always same as 'digits/1'
00276 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00277 
00278 Swedish uses:
00279 \arg \b vm-nytt      singular of 'new'
00280 \arg \b vm-nya    plural of 'new'
00281 \arg \b vm-gammalt   singular of 'old'
00282 \arg \b vm-gamla  plural of 'old'
00283 \arg \b digits/ett   'one', not always same as 'digits/1'
00284 
00285 Norwegian uses:
00286 \arg \b vm-ny     singular of 'new'
00287 \arg \b vm-nye    plural of 'new'
00288 \arg \b vm-gammel singular of 'old'
00289 \arg \b vm-gamle  plural of 'old'
00290 
00291 Dutch also uses:
00292 \arg \b nl-om     'at'?
00293 
00294 Spanish also uses:
00295 \arg \b vm-youhaveno
00296 
00297 Ukrainian requires the following additional soundfile:
00298 \arg \b vm-nove      'nove'
00299 \arg \b vm-stare  'stare'
00300 \arg \b digits/ua/1e 'odne'
00301 
00302 Italian requires the following additional soundfile:
00303 
00304 For vm_intro_it:
00305 \arg \b vm-nuovo  new
00306 \arg \b vm-nuovi  new plural
00307 \arg \b vm-vecchio   old
00308 \arg \b vm-vecchi old plural
00309 
00310 Hebrew also uses:
00311 \arg \b vm-INBOX1 '1 new message'
00312 \arg \b vm-OLD1   '1 old message'
00313 \arg \b vm-shtei  'shtei'
00314 \arg \b vm-nomessages 'you have no new messages'
00315 
00316 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00317 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00318 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00319 
00320 */
00321 
00322 struct baseio {
00323    int iocp;
00324    int iolen;
00325    int linelength;
00326    int ateof;
00327    unsigned char iobuf[BASEMAXINLINE];
00328 };
00329 
00330 /*! Structure for linked list of users */
00331 struct ast_vm_user {
00332    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00333    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00334    char password[80];               /*!< Secret pin code, numbers only */
00335    char fullname[80];               /*!< Full name, for directory app */
00336    char email[80];                  /*!< E-mail address */
00337    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00338    char serveremail[80];            /*!< From: Mail address */
00339    char mailcmd[160];               /*!< Configurable mail command */
00340    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00341    char zonetag[80];                /*!< Time zone */
00342    char callback[80];
00343    char dialout[80];
00344    char uniqueid[80];               /*!< Unique integer identifier */
00345    char exit[80];
00346    char attachfmt[20];              /*!< Attachment format */
00347    unsigned int flags;              /*!< VM_ flags */ 
00348    int saydurationm;
00349    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00350 #ifdef IMAP_STORAGE
00351    char imapuser[80];   /* IMAP server login */
00352    char imappassword[80];  /* IMAP server password if authpassword not defined */
00353 #endif
00354    double volgain;      /*!< Volume gain for voicemails sent via email */
00355    AST_LIST_ENTRY(ast_vm_user) list;
00356 };
00357 
00358 struct vm_zone {
00359    AST_LIST_ENTRY(vm_zone) list;
00360    char name[80];
00361    char timezone[80];
00362    char msg_format[512];
00363 };
00364 
00365 struct vm_state {
00366    char curbox[80];
00367    char username[80];
00368    char context[80];
00369    char curdir[PATH_MAX];
00370    char vmbox[PATH_MAX];
00371    char fn[PATH_MAX];
00372    char fn2[PATH_MAX];
00373    int *deleted;
00374    int *heard;
00375    int curmsg;
00376    int lastmsg;
00377    int newmessages;
00378    int oldmessages;
00379    int starting;
00380    int repeats;
00381 #ifdef IMAP_STORAGE
00382    ast_mutex_t lock;
00383    int updated; /* decremented on each mail check until 1 -allows delay */
00384    long msgArray[256];
00385    MAILSTREAM *mailstream;
00386    int vmArrayIndex;
00387    char imapuser[80]; /* IMAP server login */
00388    int interactive;
00389    unsigned int quota_limit;
00390    unsigned int quota_usage;
00391    struct vm_state *persist_vms;
00392 #endif
00393 };
00394 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);
00395 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00396 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00397          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00398          signed char record_gain, struct vm_state *vms);
00399 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00400 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00401 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
00402 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
00403 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00404 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00405 #endif
00406 static void apply_options(struct ast_vm_user *vmu, const char *options);
00407 
00408 #ifdef ODBC_STORAGE
00409 static char odbc_database[80];
00410 static char odbc_table[80];
00411 #define RETRIEVE(a,b,c) retrieve_file(a,b)
00412 #define DISPOSE(a,b) remove_file(a,b)
00413 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
00414 #define EXISTS(a,b,c,d) (message_exists(a,b))
00415 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00416 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00417 #define DELETE(a,b,c,d) (delete_file(a,b))
00418 #else
00419 #ifdef IMAP_STORAGE
00420 #define RETRIEVE(a,b,c) imap_retrieve_file(a,b,c)
00421 #define DISPOSE(a,b) remove_file(a,b)
00422 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
00423 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00424 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00425 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00426 #define DELETE(a,b,c,d) (vm_imap_delete(b,d))
00427 #else
00428 #define RETRIEVE(a,b,c)
00429 #define DISPOSE(a,b)
00430 #define STORE(a,b,c,d,e,f,g,h,i)
00431 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00432 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00433 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
00434 #define DELETE(a,b,c,d) (vm_delete(c))
00435 #endif
00436 #endif
00437 
00438 static char VM_SPOOL_DIR[PATH_MAX];
00439 
00440 static char ext_pass_cmd[128];
00441 
00442 int my_umask;
00443 
00444 #if ODBC_STORAGE
00445 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00446 #elif IMAP_STORAGE
00447 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00448 #else
00449 #define tdesc "Comedian Mail (Voicemail System)"
00450 #endif
00451 
00452 static char userscontext[AST_MAX_EXTENSION] = "default";
00453 
00454 static char *addesc = "Comedian Mail";
00455 
00456 static char *synopsis_vm =
00457 "Leave a Voicemail message";
00458 
00459 static char *descrip_vm =
00460 "  VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
00461 "application allows the calling party to leave a message for the specified\n"
00462 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
00463 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
00464 "specified mailbox does not exist.\n"
00465 "  The Voicemail application will exit if any of the following DTMF digits are\n"
00466 "received:\n"
00467 "    0 - Jump to the 'o' extension in the current dialplan context.\n"
00468 "    * - Jump to the 'a' extension in the current dialplan context.\n"
00469 "  This application will set the following channel variable upon completion:\n"
00470 "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
00471 "               application. The possible values are:\n"
00472 "               SUCCESS | USEREXIT | FAILED\n\n"
00473 "  Options:\n"
00474 "    b    - Play the 'busy' greeting to the calling party.\n"
00475 "    g(#) - Use the specified amount of gain when recording the voicemail\n"
00476 "           message. The units are whole-number decibels (dB).\n"
00477 "           Only works on supported technologies, which is Zap only.\n"
00478 "    s    - Skip the playback of instructions for leaving a message to the\n"
00479 "           calling party.\n"
00480 "    u    - Play the 'unavailable' greeting.\n"
00481 "    j    - Jump to priority n+101 if the mailbox is not found or some other\n"
00482 "           error occurs.\n";
00483 
00484 static char *synopsis_vmain =
00485 "Check Voicemail messages";
00486 
00487 static char *descrip_vmain =
00488 "  VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
00489 "calling party to check voicemail messages. A specific mailbox, and optional\n"
00490 "corresponding context, may be specified. If a mailbox is not provided, the\n"
00491 "calling party will be prompted to enter one. If a context is not specified,\n"
00492 "the 'default' context will be used.\n\n"
00493 "  Options:\n"
00494 "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
00495 "           is entered by the caller.\n"
00496 "    g(#) - Use the specified amount of gain when recording a voicemail\n"
00497 "           message. The units are whole-number decibels (dB).\n"
00498 "    s    - Skip checking the passcode for the mailbox.\n"
00499 "    a(#) - Skip folder prompt and go directly to folder specified.\n"
00500 "           Defaults to INBOX\n";
00501 
00502 static char *synopsis_vm_box_exists =
00503 "Check to see if Voicemail mailbox exists";
00504 
00505 static char *descrip_vm_box_exists =
00506 "  MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
00507 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
00508 "will be used.\n"
00509 "  This application will set the following channel variable upon completion:\n"
00510 "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
00511 "                        MailboxExists application. Possible values include:\n"
00512 "                        SUCCESS | FAILED\n\n"
00513 "  Options:\n"
00514 "    j - Jump to priority n+101 if the mailbox is found.\n";
00515 
00516 static char *synopsis_vmauthenticate =
00517 "Authenticate with Voicemail passwords";
00518 
00519 static char *descrip_vmauthenticate =
00520 "  VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
00521 "same way as the Authenticate application, but the passwords are taken from\n"
00522 "voicemail.conf.\n"
00523 "  If the mailbox is specified, only that mailbox's password will be considered\n"
00524 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
00525 "be set with the authenticated mailbox.\n\n"
00526 "  Options:\n"
00527 "    s - Skip playing the initial prompts.\n";
00528 
00529 /* Leave a message */
00530 static char *app = "VoiceMail";
00531 
00532 /* Check mail, control, etc */
00533 static char *app2 = "VoiceMailMain";
00534 
00535 static char *app3 = "MailboxExists";
00536 static char *app4 = "VMAuthenticate";
00537 
00538 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00539 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00540 static int maxsilence;
00541 static int maxmsg;
00542 static int silencethreshold = 128;
00543 static char serveremail[80];
00544 static char mailcmd[160];  /* Configurable mail cmd */
00545 static char externnotify[160]; 
00546 static struct ast_smdi_interface *smdi_iface = NULL;
00547 static char vmfmts[80];
00548 static double volgain;
00549 static int vmminmessage;
00550 static int vmmaxmessage;
00551 static int maxgreet;
00552 static int skipms;
00553 static int maxlogins;
00554 
00555 static struct ast_flags globalflags = {0};
00556 
00557 static int saydurationminfo;
00558 
00559 static char dialcontext[AST_MAX_CONTEXT];
00560 static char callcontext[AST_MAX_CONTEXT];
00561 static char exitcontext[AST_MAX_CONTEXT];
00562 
00563 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00564 
00565 
00566 static char *emailbody = NULL;
00567 static char *emailsubject = NULL;
00568 static char *pagerbody = NULL;
00569 static char *pagersubject = NULL;
00570 static char fromstring[100];
00571 static char pagerfromstring[100];
00572 static char emailtitle[100];
00573 static char charset[32] = "ISO-8859-1";
00574 
00575 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00576 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00577 static int adsiver = 1;
00578 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00579 
00580 
00581 static char *strip_control(const char *input, char *buf, size_t buflen)
00582 {
00583    char *bufptr = buf;
00584    for (; *input; input++) {
00585       if (*input < 32) {
00586          continue;
00587       }
00588       *bufptr++ = *input;
00589       if (bufptr == buf + buflen - 1) {
00590          break;
00591       }
00592    }
00593    *bufptr = '\0';
00594    return buf;
00595 }
00596 
00597 static void populate_defaults(struct ast_vm_user *vmu)
00598 {
00599    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00600    if (saydurationminfo)
00601       vmu->saydurationm = saydurationminfo;
00602    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00603    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00604    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00605    if (maxmsg)
00606       vmu->maxmsg = maxmsg;
00607    vmu->volgain = volgain;
00608 }
00609 
00610 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00611 {
00612    int x;
00613    if (!strcasecmp(var, "attach")) {
00614       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
00615    } else if (!strcasecmp(var, "attachfmt")) {
00616       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
00617    } else if (!strcasecmp(var, "serveremail")) {
00618       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00619    } else if (!strcasecmp(var, "language")) {
00620       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00621    } else if (!strcasecmp(var, "tz")) {
00622       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00623 #ifdef IMAP_STORAGE
00624    } else if (!strcasecmp(var, "imapuser")) {
00625       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
00626    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
00627       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
00628 #endif
00629    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00630       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00631    } else if (!strcasecmp(var, "saycid")){
00632       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00633    } else if (!strcasecmp(var,"sendvoicemail")){
00634       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00635    } else if (!strcasecmp(var, "review")){
00636       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
00637    } else if (!strcasecmp(var, "tempgreetwarn")){
00638       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
00639    } else if (!strcasecmp(var, "operator")){
00640       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00641    } else if (!strcasecmp(var, "envelope")){
00642       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00643    } else if (!strcasecmp(var, "sayduration")){
00644       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
00645    } else if (!strcasecmp(var, "saydurationm")){
00646       if (sscanf(value, "%d", &x) == 1) {
00647          vmu->saydurationm = x;
00648       } else {
00649          ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
00650       }
00651    } else if (!strcasecmp(var, "forcename")){
00652       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
00653    } else if (!strcasecmp(var, "forcegreetings")){
00654       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
00655    } else if (!strcasecmp(var, "callback")) {
00656       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
00657    } else if (!strcasecmp(var, "dialout")) {
00658       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
00659    } else if (!strcasecmp(var, "exitcontext")) {
00660       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
00661    } else if (!strcasecmp(var, "maxmsg")) {
00662       vmu->maxmsg = atoi(value);
00663       if (vmu->maxmsg <= 0) {
00664          ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
00665          vmu->maxmsg = MAXMSG;
00666       } else if (vmu->maxmsg > MAXMSGLIMIT) {
00667          ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
00668          vmu->maxmsg = MAXMSGLIMIT;
00669       }
00670    } else if (!strcasecmp(var, "volgain")) {
00671       sscanf(value, "%lf", &vmu->volgain);
00672    } else if (!strcasecmp(var, "options")) {
00673       apply_options(vmu, value);
00674    }
00675 }
00676 
00677 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
00678 {
00679    int res;
00680    if (!ast_strlen_zero(vmu->uniqueid)) {
00681       res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
00682       if (res > 0) {
00683          ast_copy_string(vmu->password, password, sizeof(vmu->password));
00684          res = 0;
00685       } else if (!res) {
00686          res = -1;
00687       }
00688       return res;
00689    }
00690    return -1;
00691 }
00692 
00693 static void apply_options(struct ast_vm_user *vmu, const char *options)
00694 {  /* Destructively Parse options and apply */
00695    char *stringp;
00696    char *s;
00697    char *var, *value;
00698    stringp = ast_strdupa(options);
00699    while ((s = strsep(&stringp, "|"))) {
00700       value = s;
00701       if ((var = strsep(&value, "=")) && value) {
00702          apply_option(vmu, var, value);
00703       }
00704    }  
00705 }
00706 
00707 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
00708 {
00709    struct ast_variable *tmp;
00710    tmp = var;
00711    while (tmp) {
00712       if (!strcasecmp(tmp->name, "vmsecret")) {
00713          ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
00714       } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
00715          if (ast_strlen_zero(retval->password))
00716             ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
00717       } else if (!strcasecmp(tmp->name, "uniqueid")) {
00718          ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
00719       } else if (!strcasecmp(tmp->name, "pager")) {
00720          ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
00721       } else if (!strcasecmp(tmp->name, "email")) {
00722          ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
00723       } else if (!strcasecmp(tmp->name, "fullname")) {
00724          ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
00725       } else if (!strcasecmp(tmp->name, "context")) {
00726          ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
00727 #ifdef IMAP_STORAGE
00728       } else if (!strcasecmp(tmp->name, "imapuser")) {
00729          ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
00730       } else if (!strcasecmp(tmp->name, "imappassword") || !strcasecmp(tmp->name, "imapsecret")) {
00731          ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
00732 #endif
00733       } else
00734          apply_option(retval, tmp->name, tmp->value);
00735       tmp = tmp->next;
00736    } 
00737 }
00738 
00739 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
00740 {
00741    struct ast_variable *var;
00742    struct ast_vm_user *retval;
00743 
00744    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
00745       if (!ivm)
00746          ast_set_flag(retval, VM_ALLOCED);   
00747       else
00748          memset(retval, 0, sizeof(*retval));
00749       if (mailbox) 
00750          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
00751       populate_defaults(retval);
00752       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
00753          var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
00754       else
00755          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
00756       if (var) {
00757          apply_options_full(retval, var);
00758          ast_variables_destroy(var);
00759       } else { 
00760          if (!ivm) 
00761             free(retval);
00762          retval = NULL;
00763       }  
00764    } 
00765    return retval;
00766 }
00767 
00768 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
00769 {
00770    /* This function could be made to generate one from a database, too */
00771    struct ast_vm_user *vmu=NULL, *cur;
00772    AST_LIST_LOCK(&users);
00773 
00774    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
00775       context = "default";
00776 
00777    AST_LIST_TRAVERSE(&users, cur, list) {
00778       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
00779          break;
00780       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
00781          break;
00782    }
00783    if (cur) {
00784       /* Make a copy, so that on a reload, we have no race */
00785       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
00786          memcpy(vmu, cur, sizeof(*vmu));
00787          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
00788          AST_LIST_NEXT(vmu, list) = NULL;
00789       }
00790    } else
00791       vmu = find_user_realtime(ivm, context, mailbox);
00792    AST_LIST_UNLOCK(&users);
00793    return vmu;
00794 }
00795