Thu Jul 9 13:40:21 2009

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  * \extref Unixodbc - http://www.unixodbc.org
00026  * \extref A source distribution of University of Washington's IMAP
00027 c-client (http://www.washington.edu/imap/
00028  * 
00029  * \par See also
00030  * \arg \ref Config_vm
00031  * \note For information about voicemail IMAP storage, read doc/imapstorage.txt
00032  * \ingroup applications
00033  * \note This module requires res_adsi to load. This needs to be optional
00034  * during compilation.
00035  *
00036  *
00037  *
00038  * \note  This file is now almost impossible to work with, due to all \#ifdefs.
00039  *        Feels like the database code before realtime. Someone - please come up
00040  *        with a plan to clean this up.
00041  */
00042 
00043 /*** MODULEINFO
00044    <depend>res_smdi</depend>
00045  ***/
00046 
00047 /*** MAKEOPTS
00048 <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">
00049    <member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
00050       <conflict>ODBC_STORAGE</conflict>
00051       <conflict>IMAP_STORAGE</conflict>
00052       <defaultenabled>yes</defaultenabled>
00053    </member>
00054    <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
00055       <depend>unixodbc</depend>
00056       <depend>ltdl</depend>
00057       <conflict>IMAP_STORAGE</conflict>
00058       <conflict>FILE_STORAGE</conflict>
00059       <defaultenabled>no</defaultenabled>
00060    </member>
00061    <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
00062       <depend>imap_tk</depend>
00063       <conflict>ODBC_STORAGE</conflict>
00064       <conflict>FILE_STORAGE</conflict>
00065       <use>ssl</use>
00066       <defaultenabled>no</defaultenabled>
00067    </member>
00068 </category>
00069  ***/
00070 
00071 #include "asterisk.h"
00072 
00073 #ifdef IMAP_STORAGE
00074 #include <ctype.h>
00075 #include <signal.h>
00076 #include <pwd.h>
00077 #ifdef USE_SYSTEM_IMAP
00078 #include <imap/c-client.h>
00079 #include <imap/imap4r1.h>
00080 #include <imap/linkage.h>
00081 #elif defined (USE_SYSTEM_CCLIENT)
00082 #include <c-client/c-client.h>
00083 #include <c-client/imap4r1.h>
00084 #include <c-client/linkage.h>
00085 #else
00086 #include "c-client.h"
00087 #include "imap4r1.h"
00088 #include "linkage.h"
00089 #endif
00090 #endif
00091 
00092 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 200945 $")
00093 
00094 #include "asterisk/paths.h"   /* use ast_config_AST_SPOOL_DIR */
00095 #include <sys/time.h>
00096 #include <sys/stat.h>
00097 #include <sys/mman.h>
00098 #include <time.h>
00099 #include <dirent.h>
00100 
00101 #include "asterisk/logger.h"
00102 #include "asterisk/lock.h"
00103 #include "asterisk/file.h"
00104 #include "asterisk/channel.h"
00105 #include "asterisk/pbx.h"
00106 #include "asterisk/config.h"
00107 #include "asterisk/say.h"
00108 #include "asterisk/module.h"
00109 #include "asterisk/adsi.h"
00110 #include "asterisk/app.h"
00111 #include "asterisk/manager.h"
00112 #include "asterisk/dsp.h"
00113 #include "asterisk/localtime.h"
00114 #include "asterisk/cli.h"
00115 #include "asterisk/utils.h"
00116 #include "asterisk/stringfields.h"
00117 #include "asterisk/smdi.h"
00118 #include "asterisk/event.h"
00119 
00120 #ifdef ODBC_STORAGE
00121 #include "asterisk/res_odbc.h"
00122 #endif
00123 
00124 #ifdef IMAP_STORAGE
00125 #include "asterisk/threadstorage.h"
00126 
00127 static char imapserver[48];
00128 static char imapport[8];
00129 static char imapflags[128];
00130 static char imapfolder[64];
00131 static char imapparentfolder[64] = "\0";
00132 static char greetingfolder[64];
00133 static char authuser[32];
00134 static char authpassword[42];
00135 
00136 static int expungeonhangup = 1;
00137 static int imapgreetings = 0;
00138 static char delimiter = '\0';
00139 
00140 struct vm_state;
00141 struct ast_vm_user;
00142 
00143 AST_THREADSTORAGE(ts_vmstate);
00144 
00145 /* Forward declarations for IMAP */
00146 static int init_mailstream(struct vm_state *vms, int box);
00147 static void write_file(char *filename, char *buffer, unsigned long len);
00148 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
00149 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu);
00150 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
00151 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
00152 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
00153 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
00154 static void vmstate_insert(struct vm_state *vms);
00155 static void vmstate_delete(struct vm_state *vms);
00156 static void set_update(MAILSTREAM * stream);
00157 static void init_vm_state(struct vm_state *vms);
00158 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
00159 static void get_mailbox_delimiter(MAILSTREAM *stream);
00160 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
00161 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
00162 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);
00163 static void update_messages_by_imapuser(const char *user, unsigned long number);
00164 
00165 static int imap_remove_file (char *dir, int msgnum);
00166 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
00167 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
00168 static void check_quota(struct vm_state *vms, char *mailbox);
00169 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00170 struct vmstate {
00171    struct vm_state *vms;
00172    AST_LIST_ENTRY(vmstate) list;
00173 };
00174 
00175 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
00176 
00177 #endif
00178 
00179 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
00180 
00181 #define COMMAND_TIMEOUT 5000
00182 /* Don't modify these here; set your umask at runtime instead */
00183 #define  VOICEMAIL_DIR_MODE   0777
00184 #define  VOICEMAIL_FILE_MODE  0666
00185 #define  CHUNKSIZE   65536
00186 
00187 #define VOICEMAIL_CONFIG "voicemail.conf"
00188 #define ASTERISK_USERNAME "asterisk"
00189 
00190 /* Define fast-forward, pause, restart, and reverse keys
00191    while listening to a voicemail message - these are
00192    strings, not characters */
00193 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
00194 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
00195 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
00196 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
00197 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
00198 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
00199 
00200 /* Default mail command to mail voicemail. Change it with the
00201     mailcmd= command in voicemail.conf */
00202 #define SENDMAIL "/usr/sbin/sendmail -t"
00203 
00204 #define INTRO "vm-intro"
00205 
00206 #define MAXMSG 100
00207 #define MAXMSGLIMIT 9999
00208 
00209 #define BASELINELEN 72
00210 #define BASEMAXINLINE 256
00211 #define eol "\r\n"
00212 
00213 #define MAX_DATETIME_FORMAT   512
00214 #define MAX_NUM_CID_CONTEXTS 10
00215 
00216 #define VM_REVIEW        (1 << 0)
00217 #define VM_OPERATOR      (1 << 1)
00218 #define VM_SAYCID        (1 << 2)
00219 #define VM_SVMAIL        (1 << 3)
00220 #define VM_ENVELOPE      (1 << 4)
00221 #define VM_SAYDURATION   (1 << 5)
00222 #define VM_SKIPAFTERCMD  (1 << 6)
00223 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
00224 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
00225 #define VM_PBXSKIP       (1 << 9)
00226 #define VM_DIRECFORWARD  (1 << 10)  /*!< directory_forward */
00227 #define VM_ATTACH        (1 << 11)
00228 #define VM_DELETE        (1 << 12)
00229 #define VM_ALLOCED       (1 << 13)
00230 #define VM_SEARCH        (1 << 14)
00231 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
00232 #define VM_MOVEHEARD     (1 << 16)  /*!< Move a "heard" message to Old after listening to it */
00233 #define ERROR_LOCK_PATH  -100
00234 #define ERROR_MAILBOX_FULL -200
00235 
00236 
00237 enum {
00238    NEW_FOLDER,
00239    OLD_FOLDER,
00240    WORK_FOLDER,
00241    FAMILY_FOLDER,
00242    FRIENDS_FOLDER,
00243    GREETINGS_FOLDER
00244 } vm_box;
00245 
00246 enum {
00247    OPT_SILENT =           (1 << 0),
00248    OPT_BUSY_GREETING =    (1 << 1),
00249    OPT_UNAVAIL_GREETING = (1 << 2),
00250    OPT_RECORDGAIN =       (1 << 3),
00251    OPT_PREPEND_MAILBOX =  (1 << 4),
00252    OPT_AUTOPLAY =         (1 << 6),
00253    OPT_DTMFEXIT =         (1 << 7),
00254 } vm_option_flags;
00255 
00256 enum {
00257    OPT_ARG_RECORDGAIN = 0,
00258    OPT_ARG_PLAYFOLDER = 1,
00259    OPT_ARG_DTMFEXIT   = 2,
00260    /* This *must* be the last value in this enum! */
00261    OPT_ARG_ARRAY_SIZE = 3,
00262 } vm_option_args;
00263 
00264 AST_APP_OPTIONS(vm_app_options, {
00265    AST_APP_OPTION('s', OPT_SILENT),
00266    AST_APP_OPTION('b', OPT_BUSY_GREETING),
00267    AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
00268    AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
00269    AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
00270    AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
00271    AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
00272 });
00273 
00274 static int load_config(int reload);
00275 
00276 /*! \page vmlang Voicemail Language Syntaxes Supported
00277 
00278    \par Syntaxes supported, not really language codes.
00279    \arg \b en    - English
00280    \arg \b de    - German
00281    \arg \b es    - Spanish
00282    \arg \b fr    - French
00283    \arg \b it    - Italian
00284    \arg \b nl    - Dutch
00285    \arg \b pt    - Portuguese
00286    \arg \b pt_BR - Portuguese (Brazil)
00287    \arg \b gr    - Greek
00288    \arg \b no    - Norwegian
00289    \arg \b se    - Swedish
00290    \arg \b tw    - Chinese (Taiwan)
00291    \arg \b ua - Ukrainian
00292 
00293 German requires the following additional soundfile:
00294 \arg \b 1F  einE (feminine)
00295 
00296 Spanish requires the following additional soundfile:
00297 \arg \b 1M      un (masculine)
00298 
00299 Dutch, Portuguese & Spanish require the following additional soundfiles:
00300 \arg \b vm-INBOXs singular of 'new'
00301 \arg \b vm-Olds      singular of 'old/heard/read'
00302 
00303 NB these are plural:
00304 \arg \b vm-INBOX  nieuwe (nl)
00305 \arg \b vm-Old    oude (nl)
00306 
00307 Polish uses:
00308 \arg \b vm-new-a  'new', feminine singular accusative
00309 \arg \b vm-new-e  'new', feminine plural accusative
00310 \arg \b vm-new-ych   'new', feminine plural genitive
00311 \arg \b vm-old-a  'old', feminine singular accusative
00312 \arg \b vm-old-e  'old', feminine plural accusative
00313 \arg \b vm-old-ych   'old', feminine plural genitive
00314 \arg \b digits/1-a   'one', not always same as 'digits/1'
00315 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00316 
00317 Swedish uses:
00318 \arg \b vm-nytt      singular of 'new'
00319 \arg \b vm-nya    plural of 'new'
00320 \arg \b vm-gammalt   singular of 'old'
00321 \arg \b vm-gamla  plural of 'old'
00322 \arg \b digits/ett   'one', not always same as 'digits/1'
00323 
00324 Norwegian uses:
00325 \arg \b vm-ny     singular of 'new'
00326 \arg \b vm-nye    plural of 'new'
00327 \arg \b vm-gammel singular of 'old'
00328 \arg \b vm-gamle  plural of 'old'
00329 
00330 Dutch also uses:
00331 \arg \b nl-om     'at'?
00332 
00333 Spanish also uses:
00334 \arg \b vm-youhaveno
00335 
00336 Italian requires the following additional soundfile:
00337 
00338 For vm_intro_it:
00339 \arg \b vm-nuovo  new
00340 \arg \b vm-nuovi  new plural
00341 \arg \b vm-vecchio   old
00342 \arg \b vm-vecchi old plural
00343 
00344 Chinese (Taiwan) requires the following additional soundfile:
00345 \arg \b vm-tong      A class-word for call (tong1)
00346 \arg \b vm-ri     A class-word for day (ri4)
00347 \arg \b vm-you    You (ni3)
00348 \arg \b vm-haveno   Have no (mei2 you3)
00349 \arg \b vm-have     Have (you3)
00350 \arg \b vm-listen   To listen (yao4 ting1)
00351 
00352 
00353 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00354 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00355 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00356 
00357 */
00358 
00359 struct baseio {
00360    int iocp;
00361    int iolen;
00362    int linelength;
00363    int ateof;
00364    unsigned char iobuf[BASEMAXINLINE];
00365 };
00366 
00367 /*! Structure for linked list of users 
00368  * Use ast_vm_user_destroy() to free one of these structures. */
00369 struct ast_vm_user {
00370    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00371    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00372    char password[80];               /*!< Secret pin code, numbers only */
00373    char fullname[80];               /*!< Full name, for directory app */
00374    char email[80];                  /*!< E-mail address */
00375    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00376    char serveremail[80];            /*!< From: Mail address */
00377    char mailcmd[160];               /*!< Configurable mail command */
00378    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00379    char zonetag[80];                /*!< Time zone */
00380    char callback[80];
00381    char dialout[80];
00382    char uniqueid[80];               /*!< Unique integer identifier */
00383    char exit[80];
00384    char attachfmt[20];              /*!< Attachment format */
00385    unsigned int flags;              /*!< VM_ flags */ 
00386    int saydurationm;
00387    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00388    int maxdeletedmsg;               /*!< Maximum number of deleted msgs saved for this mailbox */
00389    int maxsecs;                     /*!< Maximum number of seconds per message for this mailbox */
00390 #ifdef IMAP_STORAGE
00391    char imapuser[80];               /*!< IMAP server login */
00392    char imappassword[80];           /*!< IMAP server password if authpassword not defined */
00393    char imapvmshareid[80];          /*!< Shared mailbox ID to use rather than the dialed one */
00394 #endif
00395    double volgain;                  /*!< Volume gain for voicemails sent via email */
00396    AST_LIST_ENTRY(ast_vm_user) list;
00397 };
00398 
00399 /*! Voicemail time zones */
00400 struct vm_zone {
00401    AST_LIST_ENTRY(vm_zone) list;
00402    char name[80];
00403    char timezone[80];
00404    char msg_format[512];
00405 };
00406 
00407 /*! Voicemail mailbox state */
00408 struct vm_state {
00409    char curbox[80];
00410    char username[80];
00411    char curdir[PATH_MAX];
00412    char vmbox[PATH_MAX];
00413    char fn[PATH_MAX];
00414    char fn2[PATH_MAX];
00415    int *deleted;
00416    int *heard;
00417    int curmsg;
00418    int lastmsg;
00419    int newmessages;
00420    int oldmessages;
00421    int starting;
00422    int repeats;
00423 #ifdef IMAP_STORAGE
00424    ast_mutex_t lock;
00425    int updated;                         /*!< decremented on each mail check until 1 -allows delay */
00426    long msgArray[256];
00427    MAILSTREAM *mailstream;
00428    int vmArrayIndex;
00429    char imapuser[80];                   /*!< IMAP server login */
00430    int interactive;
00431    unsigned int quota_limit;
00432    unsigned int quota_usage;
00433    struct vm_state *persist_vms;
00434 #endif
00435 };
00436 
00437 #ifdef ODBC_STORAGE
00438 static char odbc_database[80];
00439 static char odbc_table[80];
00440 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
00441 #define DISPOSE(a,b) remove_file(a,b)
00442 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
00443 #define EXISTS(a,b,c,d) (message_exists(a,b))
00444 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00445 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00446 #define DELETE(a,b,c,d) (delete_file(a,b))
00447 #else
00448 #ifdef IMAP_STORAGE
00449 #define DISPOSE(a,b) (imap_remove_file(a,b))
00450 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
00451 #define EXISTS(a,b,c,d) (ast_fileexists(c, NULL, d) > 0)
00452 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
00453 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00454 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00455 #define DELETE(a,b,c,d) (vm_imap_delete(b,d))
00456 #else
00457 #define RETRIEVE(a,b,c,d)
00458 #define DISPOSE(a,b)
00459 #define STORE(a,b,c,d,e,f,g,h,i)
00460 #define EXISTS(a,b,c,d) (ast_fileexists(c, NULL, d) > 0)
00461 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00462 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
00463 #define DELETE(a,b,c,d) (vm_delete(c))
00464 #endif
00465 #endif
00466 
00467 static char VM_SPOOL_DIR[PATH_MAX];
00468 
00469 static char ext_pass_cmd[128];
00470 
00471 static int my_umask;
00472 
00473 #define PWDCHANGE_INTERNAL (1 << 1)
00474 #define PWDCHANGE_EXTERNAL (1 << 2)
00475 static int pwdchange = PWDCHANGE_INTERNAL;
00476 
00477 #ifdef ODBC_STORAGE
00478 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00479 #else
00480 # ifdef IMAP_STORAGE
00481 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00482 # else
00483 # define tdesc "Comedian Mail (Voicemail System)"
00484 # endif
00485 #endif
00486 
00487 static char userscontext[AST_MAX_EXTENSION] = "default";
00488 
00489 static char *addesc = "Comedian Mail";
00490 
00491 static char *synopsis_vm = "Leave a Voicemail message";
00492 
00493 static char *descrip_vm =
00494    "  VoiceMail(mailbox[@context][&mailbox[@context]][...][,options]): This\n"
00495    "application allows the calling party to leave a message for the specified\n"
00496    "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
00497    "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
00498    "specified mailbox does not exist.\n"
00499    "  The Voicemail application will exit if any of the following DTMF digits are\n"
00500    "received:\n"
00501    "    0 - Jump to the 'o' extension in the current dialplan context.\n"
00502    "    * - Jump to the 'a' extension in the current dialplan context.\n"
00503    "  This application will set the following channel variable upon completion:\n"
00504    "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
00505    "               application. The possible values are:\n"
00506    "               SUCCESS | USEREXIT | FAILED\n\n"
00507    "  Options:\n"
00508    "    b      - Play the 'busy' greeting to the calling party.\n"
00509    "    d([c]) - Accept digits for a new extension in context c, if played during\n"
00510    "             the greeting.  Context defaults to the current context.\n"
00511    "    g(#)   - Use the specified amount of gain when recording the voicemail\n"
00512    "             message. The units are whole-number decibels (dB).\n"
00513    "             Only works on supported technologies, which is DAHDI only.\n"
00514    "    s      - Skip the playback of instructions for leaving a message to the\n"
00515    "             calling party.\n"
00516    "    u      - Play the 'unavailable' greeting.\n";
00517 
00518 static char *synopsis_vmain = "Check Voicemail messages";
00519 
00520 static char *descrip_vmain =
00521    "  VoiceMailMain([mailbox][@context][,options]): This application allows the\n"
00522    "calling party to check voicemail messages. A specific mailbox, and optional\n"
00523    "corresponding context, may be specified. If a mailbox is not provided, the\n"
00524    "calling party will be prompted to enter one. If a context is not specified,\n"
00525    "the 'default' context will be used.\n\n"
00526    "  Options:\n"
00527    "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
00528    "           is entered by the caller.\n"
00529    "    g(#) - Use the specified amount of gain when recording a voicemail\n"
00530    "           message. The units are whole-number decibels (dB).\n"
00531    "    s    - Skip checking the passcode for the mailbox.\n"
00532    "    a(#) - Skip folder prompt and go directly to folder specified.\n"
00533    "           Defaults to INBOX\n";
00534 
00535 static char *synopsis_vm_box_exists =
00536 "Check to see if Voicemail mailbox exists";
00537 
00538 static char *descrip_vm_box_exists =
00539    "  MailboxExists(mailbox[@context][,options]): Check to see if the specified\n"
00540    "mailbox exists. If no voicemail context is specified, the 'default' context\n"
00541    "will be used.\n"
00542    "  This application will set the following channel variable upon completion:\n"
00543    "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
00544    "                        MailboxExists application. Possible values include:\n"
00545    "                        SUCCESS | FAILED\n\n"
00546    "  Options: (none)\n";
00547 
00548 static char *synopsis_vmauthenticate = "Authenticate with Voicemail passwords";
00549 
00550 static char *descrip_vmauthenticate =
00551    "  VMAuthenticate([mailbox][@context][,options]): This application behaves the\n"
00552    "same way as the Authenticate application, but the passwords are taken from\n"
00553    "voicemail.conf.\n"
00554    "  If the mailbox is specified, only that mailbox's password will be considered\n"
00555    "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
00556    "be set with the authenticated mailbox.\n\n"
00557    "  Options:\n"
00558    "    s - Skip playing the initial prompts.\n";
00559 
00560 /* Leave a message */
00561 static char *app = "VoiceMail";
00562 
00563 /* Check mail, control, etc */
00564 static char *app2 = "VoiceMailMain";
00565 
00566 static char *app3 = "MailboxExists";
00567 static char *app4 = "VMAuthenticate";
00568 
00569 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
00570 static AST_LIST_HEAD_STATIC(zones, vm_zone);
00571 static char zonetag[80];
00572 static int maxsilence;
00573 static int maxmsg;
00574 static int maxdeletedmsg;
00575 static int silencethreshold = 128;
00576 static char serveremail[80];
00577 static char mailcmd[160];  /* Configurable mail cmd */
00578 static char externnotify[160]; 
00579 static struct ast_smdi_interface *smdi_iface = NULL;
00580 static char vmfmts[80];
00581 static double volgain;
00582 static int vmminsecs;
00583 static int vmmaxsecs;
00584 static int maxgreet;
00585 static int skipms;
00586 static int maxlogins;
00587 
00588 /*! Poll mailboxes for changes since there is something external to
00589  *  app_voicemail that may change them. */
00590 static unsigned int poll_mailboxes;
00591 
00592 /*! Polling frequency */
00593 static unsigned int poll_freq;
00594 /*! By default, poll every 30 seconds */
00595 #define DEFAULT_POLL_FREQ 30
00596 
00597 AST_MUTEX_DEFINE_STATIC(poll_lock);
00598 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
00599 static pthread_t poll_thread = AST_PTHREADT_NULL;
00600 static unsigned char poll_thread_run;
00601 
00602 /*! Subscription to ... MWI event subscriptions */
00603 static struct ast_event_sub *mwi_sub_sub;
00604 /*! Subscription to ... MWI event un-subscriptions */
00605 static struct ast_event_sub *mwi_unsub_sub;
00606 
00607 /*!
00608  * \brief An MWI subscription
00609  *
00610  * This is so we can keep track of which mailboxes are subscribed to.
00611  * This way, we know which mailboxes to poll when the pollmailboxes
00612  * option is being used.
00613  */
00614 struct mwi_sub {
00615    AST_RWLIST_ENTRY(mwi_sub) entry;
00616    int old_new;
00617    int old_old;
00618    uint32_t uniqueid;
00619    char mailbox[1];
00620 };
00621 
00622 static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
00623 
00624 /* custom audio control prompts for voicemail playback */
00625 static char listen_control_forward_key[12];
00626 static char listen_control_reverse_key[12];
00627 static char listen_control_pause_key[12];
00628 static char listen_control_restart_key[12];
00629 static char listen_control_stop_key[12];
00630 
00631 /* custom password sounds */
00632 static char vm_password[80] = "vm-password";
00633 static char vm_newpassword[80] = "vm-newpassword";
00634 static char vm_passchanged[80] = "vm-passchanged";
00635 static char vm_reenterpassword[80] = "vm-reenterpassword";
00636 static char vm_mismatch[80] = "vm-mismatch";
00637 
00638 static struct ast_flags globalflags = {0};
00639 
00640 static int saydurationminfo;
00641 
00642 static char dialcontext[AST_MAX_CONTEXT] = "";
00643 static char callcontext[AST_MAX_CONTEXT] = "";
00644 static char exitcontext[AST_MAX_CONTEXT] = "";
00645 
00646 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
00647 
00648 
00649 static char *emailbody = NULL;
00650 static char *emailsubject = NULL;
00651 static char *pagerbody = NULL;
00652 static char *pagersubject = NULL;
00653 static char fromstring[100];
00654 static char pagerfromstring[100];
00655 static char charset[32] = "ISO-8859-1";
00656 
00657 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
00658 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
00659 static int adsiver = 1;
00660 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
00661 
00662 /* Forward declarations - generic */
00663 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
00664 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);
00665 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00666 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00667          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00668          signed char record_gain, struct vm_state *vms);
00669 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00670 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00671 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);
00672 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 *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
00673 static void apply_options(struct ast_vm_user *vmu, const char *options);
00674 static int is_valid_dtmf(const char *key);
00675 
00676 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00677 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00678 #endif
00679 
00680 static char *strip_control(const char *input, char *buf, size_t buflen)
00681 {
00682    char *bufptr = buf;
00683    for (; *input; input++) {
00684       if (*input < 32) {
00685          continue;
00686       }
00687       *bufptr++ = *input;
00688       if (bufptr == buf + buflen - 1) {
00689          break;
00690       }
00691    }
00692    *bufptr = '\0';
00693    return buf;
00694 }
00695 
00696 
00697 static void populate_defaults(struct ast_vm_user *vmu)
00698 {
00699    ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);   
00700    if (saydurationminfo)
00701       vmu->saydurationm = saydurationminfo;
00702    ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
00703    ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
00704    ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
00705    ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
00706    if (vmmaxsecs)
00707       vmu->maxsecs = vmmaxsecs;
00708    if (maxmsg)
00709       vmu->maxmsg = maxmsg;
00710    if (maxdeletedmsg)
00711       vmu->maxdeletedmsg = maxdeletedmsg;
00712    vmu->volgain = volgain;
00713 }
00714 
00715 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
00716 {
00717    int x;
00718    if (!strcasecmp(var, "attach")) {
00719       ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
00720    } else if (!strcasecmp(var, "attachfmt")) {
00721       ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
00722    } else if (!strcasecmp(var, "serveremail")) {
00723       ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
00724    } else if (!strcasecmp(var, "language")) {
00725       ast_copy_string(vmu->language, value, sizeof(vmu->language));
00726    } else if (!strcasecmp(var, "tz")) {
00727       ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
00728 #ifdef IMAP_STORAGE
00729    } else if (!strcasecmp(var, "imapuser")) {
00730       ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
00731    } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
00732       ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
00733    } else if (!strcasecmp(var, "imapvmshareid")) {
00734       ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
00735 #endif
00736    } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
00737       ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
00738    } else if (!strcasecmp(var, "saycid")) {
00739       ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
00740    } else if (!strcasecmp(var, "sendvoicemail")) {
00741       ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
00742    } else if (!strcasecmp(var, "review")) {
00743       ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
00744    } else if (!strcasecmp(var, "tempgreetwarn")) {
00745       ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);   
00746    } else if (!strcasecmp(var, "operator")) {
00747       ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);  
00748    } else if (!strcasecmp(var, "envelope")) {
00749       ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);  
00750    } else if (!strcasecmp(var, "moveheard")) {
00751       ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
00752    } else if (!strcasecmp(var, "sayduration")) {
00753       ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);  
00754    } else if (!strcasecmp(var, "saydurationm")) {
00755       if (sscanf(value, "%d", &x) == 1) {
00756          vmu->saydurationm = x;
00757       } else {
00758          ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
00759       }
00760    } else if (!strcasecmp(var, "forcename")) {
00761       ast_set2_flag(vmu, ast_true(value), VM_FORCENAME); 
00762    } else if (!strcasecmp(var, "forcegreetings")) {
00763       ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);   
00764    } else if (!strcasecmp(var, "callback")) {
00765       ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
00766    } else if (!strcasecmp(var, "dialout")) {
00767       ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
00768    } else if (!strcasecmp(var, "exitcontext")) {
00769       ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
00770    } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
00771       if (vmu->maxsecs <= 0) {
00772          ast_log(LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
00773          vmu->maxsecs = vmmaxsecs;
00774       } else {
00775          vmu->maxsecs = atoi(value);
00776       }
00777       if (!strcasecmp(var, "maxmessage"))
00778          ast_log(LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'.  Please make that change in your voicemail config.\n");
00779    } else if (!strcasecmp(var, "maxmsg")) {
00780       vmu->maxmsg = atoi(value);
00781       if (vmu->maxmsg <= 0) {
00782          ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
00783          vmu->maxmsg = MAXMSG;
00784       } else if (vmu->maxmsg > MAXMSGLIMIT) {
00785          ast_log(LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
00786          vmu->maxmsg = MAXMSGLIMIT;
00787       }
00788    } else if (!strcasecmp(var, "backupdeleted")) {
00789       if (sscanf(value, "%d", &x) == 1)
00790          vmu->maxdeletedmsg = x;
00791       else if (ast_true(value))
00792          vmu->maxdeletedmsg = MAXMSG;
00793       else
00794          vmu->maxdeletedmsg = 0;
00795 
00796       if (vmu->maxdeletedmsg < 0) {
00797          ast_log(LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
00798          vmu->maxdeletedmsg = MAXMSG;
00799       } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
00800          ast_log(LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
00801          vmu->maxdeletedmsg = MAXMSGLIMIT;
00802       }
00803    } else if (!strcasecmp(var, "volgain")) {
00804       sscanf(value, "%lf", &vmu->volgain);
00805    } else if (!strcasecmp(var, "options")) {
00806       apply_options(vmu, value);
00807    }
00808 }
00809 
00810 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
00811 {
00812    int res;
00813    if (!ast_strlen_zero(vmu->uniqueid)) {
00814       res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
00815       if (res > 0) {
00816          ast_copy_string(vmu->password, password, sizeof(vmu->password));
00817          res = 0;
00818       } else if (!res) {
00819          res = -1;
00820       }
00821       return res;
00822    }
00823    return -1;
00824 }
00825 
00826 static void apply_options(struct ast_vm_user *vmu, const char *options)
00827 {  /* Destructively Parse options and apply */
00828    char *stringp;
00829    char *s;
00830    char *var, *value;
00831    stringp = ast_strdupa(options);
00832    while ((s = strsep(&stringp, "|"))) {
00833       value = s;
00834       if ((var = strsep(&value, "=")) && value) {
00835          apply_option(vmu, var, value);
00836       }
00837    }  
00838 }
00839 
00840 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
00841 {
00842    struct ast_variable *tmp;
00843    tmp = var;
00844    while (tmp) {
00845       if (!strcasecmp(tmp->name, "vmsecret")) {
00846          ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
00847       } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
00848          if (ast_strlen_zero(retval->password))
00849             ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
00850       } else if (!strcasecmp(tmp->name, "uniqueid")) {
00851          ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
00852       } else if (!strcasecmp(tmp->name, "pager")) {
00853          ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
00854       } else if (!strcasecmp(tmp->name, "email")) {
00855          ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
00856       } else if (!strcasecmp(tmp->name, "fullname")) {
00857          ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
00858       } else if (!strcasecmp(tmp->name, "context")) {
00859          ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
00860 #ifdef IMAP_STORAGE
00861       } else if (!strcasecmp(tmp->name, "imapuser")) {
00862          ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
00863       } else if (!strcasecmp(tmp->name, "imappassword") || !strcasecmp(tmp->name, "imapsecret")) {
00864          ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
00865       } else if (!strcasecmp(tmp->name, "imapvmshareid")) {
00866          ast_copy_string(retval->imapvmshareid, tmp->value, sizeof(retval->imapvmshareid));
00867 #endif
00868       } else
00869          apply_option(retval, tmp->name, tmp->value);
00870       tmp = tmp->next;
00871    } 
00872 }
00873 
00874 static int is_valid_dtmf(const char *key)
00875 {
00876    int i;
00877    char *local_key = ast_strdupa(key);
00878 
00879    for (i = 0; i < strlen(key); ++i) {
00880       if (!strchr(VALID_DTMF, *local_key)) {
00881          ast_log(LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
00882          return 0;
00883       }
00884       local_key++;
00885    }
00886    return 1;
00887 }
00888 
00889 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
00890 {
00891    struct ast_variable *var;
00892    struct ast_vm_user *retval;
00893 
00894    if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
00895       if (!ivm)
00896          ast_set_flag(retval, VM_ALLOCED);   
00897       else
00898          memset(retval, 0, sizeof(*retval));
00899       if (mailbox) 
00900          ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
00901       populate_defaults(retval);
00902       if (!context && ast_test_flag((&globalflags), VM_SEARCH))
00903          var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
00904       else
00905          var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
00906       if (var) {
00907          apply_options_full(retval, var);
00908          ast_variables_destroy(var);
00909       } else { 
00910          if (!ivm) 
00911             ast_free(retval);
00912          retval = NULL;
00913       }  
00914    } 
00915    return retval;
00916 }
00917 
00918 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
00919 {
00920    /* This function could be made to generate one from a database, too */
00921    struct ast_vm_user *vmu = NULL, *cur;
00922    AST_LIST_LOCK(&users);
00923 
00924    if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
00925       context = "default";
00926 
00927    AST_LIST_TRAVERSE(&users, cur, list) {
00928       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
00929          break;
00930       if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
00931          break;
00932    }
00933    if (cur) {
00934       /* Make a copy, so that on a reload, we have no race */
00935       if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
00936          memcpy(vmu, cur, sizeof(*vmu));
00937          ast_set2_flag(vmu, !ivm, VM_ALLOCED);
00938          AST_LIST_NEXT(vmu, list) = NULL;
00939       }
00940    } else
00941       vmu = find_user_realtime(ivm, context, mailbox);
00942    AST_LIST_UNLOCK(&users);
00943    return vmu;
00944 }
00945 
00946 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
00947 {
00948    /* This function could be made to generate one from a database, too */
00949    struct ast_vm_user *cur;
00950    int res = -1;
00951    AST_LIST_LOCK(&users);
00952    AST_LIST_TRAVERSE(&users, cur, list) {
00953       if ((!context || !strcasecmp(context, cur->context)) &&
00954          (!strcasecmp(mailbox, cur->mailbox)))
00955             break;
00956    }
00957    if (cur) {
00958       ast_copy_string(cur->password, newpass, sizeof(cur->password));
00959       res = 0;
00960    }
00961    AST_LIST_UNLOCK(&users);
00962    return res;
00963 }
00964 
00965 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
00966 {
00967    struct ast_config *cfg = NULL;
00968    struct ast_variable *var = NULL;
00969    struct ast_category *cat = NULL;
00970    char *category = NULL, *value = NULL, *new = NULL;
00971    const char *tmp = NULL;
00972    struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
00973    if (!change_password_realtime(vmu, newpassword))
00974       return;
00975 
00976    /* check voicemail.conf */
00977    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags))) {
00978       while ((category = ast_category_browse(cfg, category))) {
00979          if (!strcasecmp(category, vmu->context)) {
00980             if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
00981                ast_log(LOG_WARNING, "We could not find the mailbox.\n");
00982                break;
00983             }
00984             value = strstr(tmp, ",");
00985             if (!value) {
00986                ast_log(LOG_WARNING, "variable has bad format.\n");
00987                break;
00988             }
00989             new = alloca(strlen(value) + strlen(newpassword) + 1);
00990             sprintf(new, "%s%s", newpassword, value);
00991             if (!(cat = ast_category_get(cfg, category))) {
00992                ast_log(LOG_WARNING, "Failed to get category structure.\n");
00993                break;
00994             }
00995             ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
00996          }
00997       }
00998       /* save the results */
00999       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01000       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01001       config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
01002    }
01003    category = NULL;
01004    var = NULL;
01005    /* check users.conf and update the password stored for the mailbox*/
01006    /* if no vmsecret entry exists create one. */
01007    if ((cfg = ast_config_load("users.conf", config_flags))) {
01008       ast_debug(4, "we are looking for %s\n", vmu->mailbox);
01009       while ((category = ast_category_browse(cfg, category))) {
01010          ast_debug(4, "users.conf: %s\n", category);
01011          if (!strcasecmp(category, vmu->mailbox)) {
01012             if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
01013                ast_debug(3, "looks like we need to make vmsecret!\n");
01014                var = ast_variable_new("vmsecret", newpassword, "");
01015             } 
01016             new = alloca(strlen(newpassword) + 1);
01017             sprintf(new, "%s", newpassword);
01018             if (!(cat = ast_category_get(cfg, category))) {
01019                ast_debug(4, "failed to get category!\n");
01020                break;
01021             }
01022             if (!var)      
01023                ast_variable_update(cat, "vmsecret", new, NULL, 0);
01024             else
01025                ast_variable_append(cat, var);
01026          }
01027       }
01028       /* save the results and clean things up */
01029       reset_user_pw(vmu->context, vmu->mailbox, newpassword);  
01030       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01031       config_text_file_save("users.conf", cfg, "AppVoicemail");
01032    }
01033 }
01034 
01035 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
01036 {
01037    char buf[255];
01038    snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
01039    if (!ast_safe_system(buf)) {
01040       ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
01041       /* Reset the password in memory, too */
01042       reset_user_pw(vmu->context, vmu->mailbox, newpassword);
01043    }
01044 }
01045 
01046 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
01047 {
01048    return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
01049 }
01050 
01051 /*! 
01052  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
01053  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
01054  * \param len The length of the path string that was written out.
01055  * 
01056  * The path is constructed as 
01057  *    VM_SPOOL_DIRcontext/ext/folder
01058  *
01059  * \return zero on success, -1 on error.
01060  */
01061 static int make_file(char *dest, const int len, const char *dir, const int num)
01062 {
01063    return snprintf(dest, len, "%s/msg%04d", dir, num);
01064 }
01065 
01066 /* same as mkstemp, but return a FILE * */
01067 static FILE *vm_mkftemp(char *template)
01068 {
01069    FILE *p = NULL;
01070    int pfd = mkstemp(template);
01071    chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
01072    if (pfd > -1) {
01073       p = fdopen(pfd, "w+");
01074       if (!p) {
01075          close(pfd);
01076          pfd = -1;
01077       }
01078    }
01079    return p;
01080 }
01081 
01082 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
01083  * \param dest    String. base directory.
01084  * \param len     Length of dest.
01085  * \param context String. Ignored if is null or empty string.
01086  * \param ext     String. Ignored if is null or empty string.
01087  * \param folder  String. Ignored if is null or empty string. 
01088  * \return -1 on failure, 0 on success.
01089  */
01090 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
01091 {
01092    mode_t   mode = VOICEMAIL_DIR_MODE;
01093    int res;
01094 
01095    make_dir(dest, len, context, ext, folder);
01096    if ((res = ast_mkdir(dest, mode))) {
01097       ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
01098       return -1;
01099    }
01100    return 0;
01101 }
01102 
01103 static const char *mbox(int id)
01104 {
01105    static const char *msgs[] = {
01106 #ifdef IMAP_STORAGE
01107       imapfolder,
01108 #else
01109       "INBOX",
01110 #endif
01111       "Old",
01112       "Work",
01113       "Family",
01114       "Friends",
01115       "Cust1",
01116       "Cust2",
01117       "Cust3",
01118       "Cust4",
01119       "Cust5",
01120       "Deleted",
01121       "Urgent"
01122    };
01123    return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
01124 }
01125 
01126 static void free_user(struct ast_vm_user *vmu)
01127 {
01128    if (ast_test_flag(vmu, VM_ALLOCED))
01129       ast_free(vmu);
01130 }
01131 
01132 /* All IMAP-specific functions should go in this block. This
01133 * keeps them from being spread out all over the code */
01134 #ifdef IMAP_STORAGE
01135 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu)
01136 {
01137    char arg[10];
01138    struct vm_state *vms;
01139    unsigned long messageNum;
01140 
01141    /* Greetings aren't stored in IMAP, so we can't delete them there */
01142    if (msgnum < 0) {
01143       return;
01144    }
01145 
01146    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, 0))) {
01147       ast_log(LOG_WARNING, "Couldn't find a vm_state for mailbox %s. Unable to set \\DELETED flag for message %d\n", vmu->mailbox, msgnum);
01148       return;
01149    }
01150 
01151    /* find real message number based on msgnum */
01152    /* this may be an index into vms->msgArray based on the msgnum. */
01153    messageNum = vms->msgArray[msgnum];
01154    if (messageNum == 0) {
01155       ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
01156       return;
01157    }
01158    ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
01159    /* delete message */
01160    snprintf(arg, sizeof(arg), "%lu", messageNum);
01161    ast_mutex_lock(&vms->lock);
01162    mail_setflag(vms->mailstream, arg, "\\DELETED");
01163    ast_mutex_unlock(&vms->lock);
01164 }
01165 
01166 static int imap_retrieve_greeting (const char *dir, const int msgnum, struct ast_vm_user *vmu)
01167 {
01168    struct vm_state *vms_p;
01169    char *file, *filename;
01170    char *attachment;
01171    int ret = 0, i;
01172    BODY *body;
01173 
01174    /* This function is only used for retrieval of IMAP greetings
01175    * regular messages are not retrieved this way, nor are greetings
01176    * if they are stored locally*/
01177    if (msgnum > -1 || !imapgreetings) {
01178       return 0;
01179    } else {
01180       file = strrchr(ast_strdupa(dir), '/');
01181       if (file)
01182          *file++ = '\0';
01183       else {
01184          ast_debug (1, "Failed to procure file name from directory passed.\n");
01185          return -1;
01186       }
01187    }
01188 
01189    /* check if someone is accessing this box right now... */
01190    if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, 1)) ||!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, 0))) {
01191       ast_log(LOG_ERROR, "Voicemail state not found!\n");
01192       return -1;
01193    }
01194    
01195    ast_mutex_lock(&vms_p->lock);
01196    ret = init_mailstream(vms_p, GREETINGS_FOLDER);
01197    if (!vms_p->mailstream) {
01198       ast_log(LOG_ERROR, "IMAP mailstream is NULL\n");
01199       ast_mutex_unlock(&vms_p->lock);
01200       return -1;
01201    }
01202 
01203    /*XXX Yuck, this could probably be done a lot better */
01204    for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
01205       mail_fetchstructure(vms_p->mailstream, i + 1, &body);
01206       /* We have the body, now we extract the file name of the first attachment. */
01207       if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01208          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
01209       } else {
01210          ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01211          ast_mutex_unlock(&vms_p->lock);
01212          return -1;
01213       }
01214       filename = strsep(&attachment, ".");
01215       if (!strcmp(filename, file)) {
01216          ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
01217          vms_p->msgArray[vms_p->curmsg] = i + 1;
01218          save_body(body, vms_p, "2", attachment);
01219          ast_mutex_unlock(&vms_p->lock);
01220          return 0;
01221       }
01222    }
01223    ast_mutex_unlock(&vms_p->lock);
01224 
01225    return -1;
01226 }
01227 
01228 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
01229 {
01230    BODY *body;
01231    char *header_content;
01232    char *attachedfilefmt;
01233    char buf[80];
01234    struct vm_state *vms;
01235    char text_file[PATH_MAX];
01236    FILE *text_file_ptr;
01237    int res = 0;
01238    struct ast_vm_user *vmu;
01239 
01240    if (!(vmu = find_user(NULL, context, mailbox))) {
01241       ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
01242       return -1;
01243    }
01244    
01245    if (msgnum < 0) {
01246       if (imapgreetings) {
01247          res = imap_retrieve_greeting(dir, msgnum, vmu);
01248          goto exit;
01249       } else {
01250          res = 0;
01251          goto exit;
01252       }
01253    }
01254 
01255    /* Before anything can happen, we need a vm_state so that we can
01256    * actually access the imap server through the vms->mailstream
01257    */
01258    if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, 0))) {
01259       /* This should not happen. If it does, then I guess we'd
01260       * need to create the vm_state, extract which mailbox to
01261       * open, and then set up the msgArray so that the correct
01262       * IMAP message could be accessed. If I have seen correctly
01263       * though, the vms should be obtainable from the vmstates list
01264       * and should have its msgArray properly set up.
01265       */
01266       ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
01267       res = -1;
01268       goto exit;
01269    }
01270    
01271    make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
01272 
01273    /* Don't try to retrieve a message from IMAP if it already is on the file system */
01274    if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
01275       res = 0;
01276       goto exit;
01277    }
01278 
01279    if (option_debug > 2)
01280       ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
01281    if (vms->msgArray[msgnum] == 0) {
01282       ast_log (LOG_WARNING,"Trying to access unknown message\n");
01283       res = -1;
01284       goto exit;
01285    }
01286 
01287    /* This will only work for new messages... */
01288    ast_mutex_lock(&vms->lock);
01289    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
01290    ast_mutex_unlock(&vms->lock);
01291    /* empty string means no valid header */
01292    if (ast_strlen_zero(header_content)) {
01293       ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
01294       res = -1;
01295       goto exit;
01296    }
01297 
01298    ast_mutex_lock(&vms->lock);
01299    mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
01300    ast_mutex_unlock(&vms->lock);
01301 
01302    /* We have the body, now we extract the file name of the first attachment. */
01303    if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
01304       attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
01305    } else {
01306       ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
01307       res = -1;
01308       goto exit;
01309    }
01310    
01311    /* Find the format of the attached file */
01312 
01313    strsep(&attachedfilefmt, ".");
01314    if (!attachedfilefmt) {
01315       ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
01316       res = -1;
01317       goto exit;
01318    }
01319    
01320    save_body(body, vms, "2", attachedfilefmt);
01321 
01322    /* Get info from headers!! */
01323    snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
01324 
01325    if (!(text_file_ptr = fopen(text_file, "w"))) {
01326       ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
01327    }
01328 
01329    fprintf(text_file_ptr, "%s\n", "[message]");
01330 
01331    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
01332    fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
01333    get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
01334    fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
01335    get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
01336    fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
01337    get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
01338    fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
01339    get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
01340    fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
01341    get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
01342    fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
01343    get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
01344    fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
01345    fclose(text_file_ptr);
01346 
01347 exit:
01348    free_user(vmu);
01349    return res;
01350 }
01351 
01352 static int folder_int(const char *folder)
01353 {
01354    /*assume a NULL folder means INBOX*/
01355    if (!folder)
01356       return 0;
01357    if (!strcasecmp(folder, imapfolder))
01358       return 0;
01359    else if (!strcasecmp(folder, "Old"))
01360       return 1;
01361    else if (!strcasecmp(folder, "Work"))
01362       return 2;
01363    else if (!strcasecmp(folder, "Family"))
01364       return 3;
01365    else if (!strcasecmp(folder, "Friends"))
01366       return 4;
01367    else if (!strcasecmp(folder, "Cust1"))
01368       return 5;
01369    else if (!strcasecmp(folder, "Cust2"))
01370       return 6;
01371    else if (!strcasecmp(folder, "Cust3"))
01372       return 7;
01373    else if (!strcasecmp(folder, "Cust4"))
01374       return 8;
01375    else if (!strcasecmp(folder, "Cust5"))
01376       return 9;
01377    else /*assume they meant INBOX if folder is not found otherwise*/
01378       return 0;
01379 }
01380 
01381 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)
01382 {
01383    char *myserveremail = serveremail;
01384    char fn[PATH_MAX];
01385    char mailbox[256];
01386    char *stringp;
01387    FILE *p = NULL;
01388    char tmp[80] = "/tmp/astmail-XXXXXX";
01389    long len;
01390    void *buf;
01391    int tempcopy = 0;
01392    STRING str;
01393    
01394    /* Attach only the first format */
01395    fmt = ast_strdupa(fmt);
01396    stringp = fmt;
01397    strsep(&stringp, "|");
01398 
01399    if (!ast_strlen_zero(vmu->serveremail))
01400       myserveremail = vmu->serveremail;
01401 
01402    if (msgnum > -1)
01403       make_file(fn, sizeof(fn), dir, msgnum);
01404    else
01405       ast_copy_string (fn, dir, sizeof(fn));
01406    
01407    if (ast_strlen_zero(vmu->email)) {
01408       /* We need the vmu->email to be set when we call make_email_file, but
01409       * if we keep it set, a duplicate e-mail will be created. So at the end
01410       * of this function, we will revert back to an empty string if tempcopy
01411       * is 1.
01412       */
01413       ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
01414       tempcopy = 1;
01415    }
01416 
01417    if (!strcmp(fmt, "wav49"))
01418       fmt = "WAV";
01419    ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
01420 
01421    /* Make a temporary file instead of piping directly to sendmail, in case the mail
01422    command hangs. */
01423    if (!(p = vm_mkftemp(tmp))) {
01424       ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
01425       if (tempcopy)
01426          *(vmu->email) = '\0';
01427       return -1;
01428    }
01429 
01430    if (msgnum < 0 && imapgreetings) {
01431       init_mailstream(vms, GREETINGS_FOLDER);
01432       imap_delete_old_greeting(fn, vms);
01433    }
01434    
01435    make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX", S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, fmt, duration, 1, chan, NULL, 1);
01436    /* read mail file to memory */      
01437    len = ftell(p);
01438    rewind(p);
01439    if (!(buf = ast_malloc(len + 1))) {
01440       ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
01441       fclose(p);
01442       if (tempcopy)
01443          *(vmu->email) = '\0';
01444       return -1;
01445    }
01446    if (fread(buf, len, 1, p) < len) {
01447       if (ferror(p)) {
01448          ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
01449          return -1;
01450       }
01451    }
01452    ((char *)buf)[len] = '\0';
01453    INIT(&str, mail_string, buf, len);
01454    init_mailstream(vms, NEW_FOLDER);
01455    imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
01456    if (!mail_append(vms->mailstream, mailbox, &str))
01457       ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
01458    fclose(p);
01459    unlink(tmp);
01460    ast_free(buf);
01461    ast_debug(3, "%s stored\n", fn);
01462    
01463    if (tempcopy)
01464       *(vmu->email) = '\0';
01465    
01466    return 0;
01467 
01468 }
01469 
01470 static int messagecount(const char *context, const char *mailbox, const char *folder)
01471 {
01472    SEARCHPGM *pgm;
01473    SEARCHHEADER *hdr;
01474 
01475    struct ast_vm_user *vmu, vmus;
01476    struct vm_state *vms_p;
01477    int ret = 0;
01478    int fold = folder_int(folder);
01479    
01480    if (ast_strlen_zero(mailbox))
01481       return 0;
01482 
01483    /* We have to get the user before we can open the stream! */
01484    /* ast_log(LOG_DEBUG, "Before find_user, context is %s and mailbox is %s\n", context, mailbox); */
01485    vmu = find_user(&vmus, context, mailbox);
01486    if (!vmu) {
01487       ast_log(LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
01488       return -1;
01489    } else {
01490       /* No IMAP account available */
01491       if (vmu->imapuser[0] == '\0') {
01492          ast_log(LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01493          return -1;
01494       }
01495    }
01496    
01497    /* No IMAP account available */
01498    if (vmu->imapuser[0] == '\0') {
01499       ast_log(LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
01500       free_user(vmu);
01501       return -1;
01502    }
01503 
01504    /* check if someone is accessing this box right now... */
01505    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
01506    if (!vms_p) {
01507       vms_p = get_vm_state_by_mailbox(mailbox, 1);
01508    }
01509    if (vms_p) {
01510       ast_debug(3, "Returning before search - user is logged in\n");
01511       if (fold == 0) { /* INBOX */
01512          return vms_p->newmessages;
01513       }
01514       if (fold == 1) { /* Old messages */
01515          return vms_p->oldmessages;
01516       }
01517    }
01518 
01519    /* add one if not there... */
01520    vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
01521    if (!vms_p) {
01522       vms_p = get_vm_state_by_mailbox(mailbox, 0);
01523    }
01524 
01525    if (!vms_p) {
01526       vms_p = create_vm_state_from_user(vmu);
01527    }
01528    ret = init_mailstream(vms_p, fold);
01529    if (!vms_p->mailstream) {
01530       ast_log(LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
01531       return -1;
01532    }
01533    if (ret == 0) {
01534       ast_mutex_lock(&vms_p->lock);
01535       pgm = mail_newsearchpgm ();
01536       hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
01537       hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
01538       pgm->header = hdr;
01539       if (fold != 1) {
01540          pgm->unseen = 1;
01541          pgm->seen = 0;
01542       }
01543       /* In the special case where fold is 1 (old messages) we have to do things a bit
01544       * differently. Old messages are stored in the INBOX but are marked as "seen"
01545       */
01546       else {
01547          pgm->unseen = 0;
01548          pgm->seen = 1;
01549       }
01550       pgm->undeleted = 1;
01551       pgm->deleted = 0;
01552 
01553       vms_p->vmArrayIndex = 0;
01554       mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
01555       if (fold == 0)
01556          vms_p->newmessages = vms_p->vmArrayIndex;
01557       if (fold == 1)
01558          vms_p->oldmessages = vms_p->vmArrayIndex;
01559       /* Freeing the searchpgm also frees the searchhdr */
01560       mail_free_searchpgm(&pgm);
01561       ast_mutex_unlock(&vms_p->lock);
01562       vms_p->updated = 0;
01563       return vms_p->vmArrayIndex;
01564    } else {
01565       ast_mutex_lock(&vms_p->lock);
01566       mail_ping(vms_p->mailstream);
01567       ast_mutex_unlock(&vms_p->lock);
01568    }
01569    return 0;
01570 }
01571 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
01572 {
01573    char tmp[PATH_MAX] = "";
01574    char *mailboxnc;
01575    char *context;
01576    char *mb;
01577    char *cur;
01578    if (newmsgs)
01579       *newmsgs = 0;
01580    if (oldmsgs)
01581       *oldmsgs = 0;
01582 
01583    ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
01584    /* If no mailbox, return immediately */
01585    if (ast_strlen_zero(mailbox_context))
01586       return 0;
01587    
01588    ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01589    context = strchr(tmp, '@');
01590    if (strchr(mailbox_context, ',')) {
01591       int tmpnew, tmpold;
01592       ast_copy_string(tmp, mailbox_context, sizeof(tmp));
01593       mb = tmp;
01594       while ((cur = strsep(&mb, ", "))) {
01595          if (!ast_strlen_zero(cur)) {
01596             if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
01597                return -1;
01598             else {
01599                if (newmsgs)
01600                   *newmsgs += tmpnew; 
01601                if (oldmsgs)
01602                   *oldmsgs += tmpold;
01603             }
01604          }
01605       }
01606       return 0;
01607    }
01608    if (context) {
01609       *context = '\0';
01610       mailboxnc = tmp;
01611       context++;
01612    } else {
01613       context = "default";
01614       mailboxnc = (char *)mailbox_context;
01615    }
01616    if (newmsgs) {
01617       if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
01618          return -1;
01619    }
01620    if (oldmsgs) {
01621       if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
01622          return -1;
01623    }
01624    return 0;
01625 }
01626 
01627 /** 
01628 * \brief Determines if the given folder has messages.
01629 * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
01630 * \param folder the folder to look in
01631 *
01632 * This function is used when the mailbox is stored in an IMAP back end.
01633 * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
01634 * \return 1 if the folder has one or more messages. zero otherwise.
01635 */
01636 
01637 static int has_voicemail(const char *mailbox, const char *folder)
01638 {
01639    char tmp[256], *tmp2, *mbox, *context;
01640    ast_copy_string(tmp, mailbox, sizeof(tmp));
01641    tmp2 = tmp;
01642    if (strchr(tmp2, ',')) {
01643       while ((mbox = strsep(&tmp2, ","))) {
01644          if (!ast_strlen_zero(mbox)) {
01645             if (has_voicemail(mbox, folder))
01646                return 1;
01647          }
01648       }
01649    }
01650    if ((context= strchr(tmp, '@')))
01651       *context++ = '\0';
01652    else
01653       context = "default";
01654    return messagecount(context, tmp, folder) ? 1 : 0;
01655 }
01656 
01657 /*!
01658 * \brief Copies a message from one mailbox to another.
01659 * \param chan
01660 * \param vmu
01661 * \param imbox
01662 * \param msgnum
01663 * \param duration
01664 * \param recip
01665 * \param fmt
01666 * \param dir
01667 *
01668 * This works with IMAP storage based mailboxes.
01669 *
01670 * \return zero on success, -1 on error.
01671 */
01672 static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir)
01673 {
01674    struct vm_state *sendvms = NULL, *destvms = NULL;
01675    char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
01676    if (msgnum >= recip->maxmsg) {
01677       ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
01678       return -1;
01679    }
01680    if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
01681       ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
01682       return -1;
01683    }
01684    if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
01685       ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
01686       return -1;
01687    }
01688    snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
01689    ast_mutex_lock(&sendvms->lock);
01690    if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
01691       ast_mutex_unlock(&sendvms->lock);
01692       return 0;
01693    }
01694    ast_mutex_unlock(&sendvms->lock);
01695    ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
01696    return -1;
01697 }
01698 
01699 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
01700 {
01701    char tmp[256], *t = tmp;
01702    size_t left = sizeof(tmp);
01703    
01704    if (box == OLD_FOLDER) {
01705       ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
01706    } else {
01707       ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
01708    }
01709 
01710    if (box == NEW_FOLDER) {
01711       ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
01712    } else {
01713       snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
01714    }
01715 
01716    /* Build up server information */
01717    ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
01718 
01719    /* Add authentication user if present */
01720    if (!ast_strlen_zero(authuser))
01721       ast_build_string(&t, &left, "/authuser=%s", authuser);
01722 
01723    /* Add flags if present */
01724    if (!ast_strlen_zero(imapflags))
01725       ast_build_string(&t, &left, "/%s", imapflags);
01726 
01727    /* End with username */
01728    ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
01729    if (box == NEW_FOLDER || box == OLD_FOLDER)
01730       snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
01731    else if (box == GREETINGS_FOLDER)
01732       snprintf(spec, len, "%s%s", tmp, greetingfolder);
01733    else {   /* Other folders such as Friends, Family, etc... */
01734       if (!ast_strlen_zero(imapparentfolder)) {
01735          /* imapparentfolder would typically be set to INBOX */
01736          snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
01737       } else {
01738          snprintf(spec, len, "%s%s", tmp, mbox(box));
01739       }
01740    }
01741 }
01742 
01743 static int init_mailstream(struct vm_state *vms, int box)
01744 {
01745    MAILSTREAM *stream = NIL;
01746    long debug;
01747    char tmp[256];
01748    
01749    if (!vms) {
01750       ast_log (LOG_ERROR,"vm_state is NULL!\n");
01751       return -1;
01752    }
01753    if (option_debug > 2)
01754       ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
01755    if (vms->mailstream == NIL || !vms->mailstream) {
01756       if (option_debug)
01757          ast_log (LOG_DEBUG,"mailstream not set.\n");
01758    } else {
01759       stream = vms->mailstream;
01760    }
01761    /* debug = T;  user wants protocol telemetry? */
01762    debug = NIL;  /* NO protocol telemetry? */
01763 
01764    if (delimiter == '\0') {      /* did not probe the server yet */
01765       char *cp;
01766 #ifdef USE_SYSTEM_IMAP
01767 #include <imap/linkage.c>
01768 #elif defined(USE_SYSTEM_CCLIENT)
01769 #include <c-client/linkage.c>
01770 #else
01771 #include "linkage.c"
01772 #endif
01773       /* Connect to INBOX first to get folders delimiter */
01774       imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
01775       ast_mutex_lock(&vms->lock);
01776       stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
01777       ast_mutex_unlock(&vms->lock);
01778       if (stream == NIL) {
01779          ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
01780          return -1;
01781       }
01782       get_mailbox_delimiter(stream);
01783       /* update delimiter in imapfolder */
01784       for (cp = imapfolder; *cp; cp++)
01785          if (*cp == '/')
01786             *cp = delimiter;
01787    }
01788    /* Now connect to the target folder */
01789    imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
01790    if (option_debug > 2)
01791       ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
01792    ast_mutex_lock(&vms->lock);
01793    vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
01794    ast_mutex_unlock(&vms->lock);
01795    if (vms->mailstream == NIL) {
01796       return -1;
01797    } else {
01798       return 0;
01799    }
01800 }
01801 
01802 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
01803 {
01804    SEARCHPGM *pgm;
01805    SEARCHHEADER *hdr;
01806    int ret, urgent = 0;
01807 
01808    /* If Urgent, then look at INBOX */
01809    if (box == 11) {
01810       box = NEW_FOLDER;
01811       urgent = 1;
01812    }
01813 
01814    ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
01815    ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
01816 
01817    if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
01818       ast_log(LOG_ERROR, "Could not initialize mailstream\n");
01819       return -1;
01820    }
01821    
01822    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
01823    
01824    /* Check Quota */
01825    if  (box == 0)  {
01826       ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
01827       check_quota(vms,(char *)mbox(box));
01828    }
01829 
01830    ast_mutex_lock(&vms->lock);
01831    pgm = mail_newsearchpgm();
01832 
01833    /* Check IMAP folder for Asterisk messages only... */
01834    hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
01835    hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
01836    pgm->header = hdr;
01837    pgm->deleted = 0;
01838    pgm->undeleted = 1;
01839 
01840    /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
01841    if (box == NEW_FOLDER && urgent == 1) {
01842       pgm->unseen = 1;
01843       pgm->seen = 0;
01844       pgm->flagged = 1;
01845       pgm->unflagged = 0;
01846    } else if (box == NEW_FOLDER && urgent == 0) {
01847       pgm->unseen = 1;
01848       pgm->seen = 0;
01849       pgm->flagged = 0;
01850       pgm->unflagged = 1;
01851    } else if (box == OLD_FOLDER) {
01852       pgm->seen = 1;
01853       pgm->unseen = 0;
01854    }
01855 
01856    ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
01857 
01858    vms->vmArrayIndex = 0;
01859    mail_search_full (vms->mailstream, NULL, pgm, NIL);
01860    vms->lastmsg = vms->vmArrayIndex - 1;
01861    mail_free_searchpgm(&pgm);
01862 
01863    ast_mutex_unlock(&vms->lock);
01864    return 0;
01865 }
01866 
01867 static void write_file(char *filename, char *buffer, unsigned long len)
01868 {
01869    FILE *output;
01870 
01871    output = fopen (filename, "w");
01872    if (fwrite(buffer, len, 1, output) != 1) {
01873       if (ferror(output)) {
01874          ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
01875       }
01876    }
01877    fclose (output);
01878 }
01879 
01880 static void update_messages_by_imapuser(const char *user, unsigned long number)
01881 {
01882    struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
01883 
01884    if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
01885       return;
01886    }
01887 
01888    ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
01889    vms->msgArray[vms->vmArrayIndex++] = number;
01890 }
01891 
01892 void mm_searched(MAILSTREAM *stream, unsigned long number)
01893 {
01894    char *mailbox = stream->mailbox, buf[1024] = "", *user;
01895 
01896    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
01897       return;
01898 
01899    update_messages_by_imapuser(user, number);
01900 }
01901 
01902 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
01903 {
01904    struct ast_variable *var;
01905    struct ast_vm_user *vmu;
01906 
01907    vmu = ast_calloc(1, sizeof *vmu);
01908    if (!vmu)
01909       return NULL;
01910    ast_set_flag(vmu, VM_ALLOCED);
01911    populate_defaults(vmu);
01912 
01913    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
01914    if (var) {
01915       apply_options_full(vmu, var);
01916       ast_variables_destroy(var);
01917       return vmu;
01918    } else {
01919       free(vmu);
01920       return NULL;
01921    }
01922 }
01923 
01924 /* Interfaces to C-client */
01925 
01926 void mm_exists(MAILSTREAM * stream, unsigned long number)
01927 {
01928    /* mail_ping will callback here if new mail! */
01929    ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
01930    if (number == 0) return;
01931    set_update(stream);
01932 }
01933 
01934 
01935 void mm_expunged(MAILSTREAM * stream, unsigned long number)
01936 {
01937    /* mail_ping will callback here if expunged mail! */
01938    ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
01939    if (number == 0) return;
01940    set_update(stream);
01941 }
01942 
01943 
01944 void mm_flags(MAILSTREAM * stream, unsigned long number)
01945 {
01946    /* mail_ping will callback here if read mail! */
01947    ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
01948    if (number == 0) return;
01949    set_update(stream);
01950 }
01951 
01952 
01953 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
01954 {
01955    ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
01956    mm_log (string, errflg);
01957 }
01958 
01959 
01960 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
01961 {
01962    if (delimiter == '\0') {
01963       delimiter = delim;
01964    }
01965 
01966    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
01967    if (attributes & LATT_NOINFERIORS)
01968       ast_debug(5, "no inferiors\n");
01969    if (attributes & LATT_NOSELECT)
01970       ast_debug(5, "no select\n");
01971    if (attributes & LATT_MARKED)
01972       ast_debug(5, "marked\n");
01973    if (attributes & LATT_UNMARKED)
01974       ast_debug(5, "unmarked\n");
01975 }
01976 
01977 
01978 void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
01979 {
01980    ast_debug(5, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
01981    if (attributes & LATT_NOINFERIORS)
01982       ast_debug(5, "no inferiors\n");
01983    if (attributes & LATT_NOSELECT)
01984       ast_debug(5, "no select\n");
01985    if (attributes & LATT_MARKED)
01986       ast_debug(5, "marked\n");
01987    if (attributes & LATT_UNMARKED)
01988       ast_debug(5, "unmarked\n");
01989 }
01990 
01991 
01992 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
01993 {
01994    ast_log(LOG_NOTICE, " Mailbox %s", mailbox);
01995    if (status->flags & SA_MESSAGES)
01996       ast_log(LOG_NOTICE, ", %lu messages", status->messages);
01997    if (status->flags & SA_RECENT)
01998       ast_log(LOG_NOTICE, ", %lu recent", status->recent);
01999    if (status->flags & SA_UNSEEN)
02000       ast_log(LOG_NOTICE, ", %lu unseen", status->unseen);
02001    if (status->flags & SA_UIDVALIDITY)
02002       ast_log(LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
02003    if (status->flags & SA_UIDNEXT)
02004       ast_log(LOG_NOTICE, ", %lu next UID", status->uidnext);
02005    ast_log(LOG_NOTICE, "\n");
02006 }
02007 
02008 
02009 void mm_log(char *string, long errflg)
02010 {
02011    switch ((short) errflg) {
02012       case NIL:
02013          ast_debug(1,"IMAP Info: %s\n", string);
02014          break;
02015       case PARSE:
02016       case WARN:
02017          ast_log(LOG_WARNING, "IMAP Warning: %s\n", string);
02018          break;
02019       case ERROR:
02020          ast_log(LOG_ERROR, "IMAP Error: %s\n", string);
02021          break;
02022    }
02023 }
02024 
02025 
02026 void mm_dlog(char *string)
02027 {
02028    ast_log(LOG_NOTICE, "%s\n", string);
02029 }
02030 
02031 
02032 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
02033 {
02034    struct ast_vm_user *vmu;
02035 
02036    ast_debug(4, "Entering callback mm_login\n");
02037 
02038    ast_copy_string(user, mb->user, MAILTMPLEN);
02039 
02040    /* We should only do this when necessary */
02041    if (!ast_strlen_zero(authpassword)) {
02042       ast_copy_string(pwd, authpassword, MAILTMPLEN);
02043    } else {
02044       AST_LIST_TRAVERSE(&users, vmu, list) {
02045          if (!strcasecmp(mb->user, vmu->imapuser)) {
02046             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02047             break;
02048          }
02049       }
02050       if (!vmu) {
02051          if ((vmu = find_user_realtime_imapuser(mb->user))) {
02052             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
02053             free_user(vmu);
02054          }
02055       }
02056    }
02057 }
02058 
02059 
02060 void mm_critical(MAILSTREAM * stream)
02061 {
02062 }
02063 
02064 
02065 void mm_nocritical(MAILSTREAM * stream)
02066 {
02067 }
02068 
02069 
02070 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
02071 {
02072    kill (getpid (), SIGSTOP);
02073    return NIL;
02074 }
02075 
02076 
02077 void mm_fatal(char *string)
02078 {
02079    ast_log(LOG_ERROR, "IMAP access FATAL error: %s\n", string);
02080 }
02081 
02082 /* C-client callback to handle quota */
02083 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
02084 {
02085    struct vm_state *vms;
02086    char *mailbox = stream->mailbox, *user;
02087    char buf[1024] = "";
02088    unsigned long usage = 0, limit = 0;
02089    
02090    while (pquota) {
02091       usage = pquota->usage;
02092       limit = pquota->limit;
02093       pquota = pquota->next;
02094    }
02095    
02096    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || (!(vms = get_vm_state_by_imapuser(user, 2)) && !(vms = get_vm_state_by_imapuser(user, 0)))) {
02097       ast_log(LOG_ERROR, "No state found.\n");
02098       return;
02099    }
02100 
02101    ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
02102 
02103    vms->quota_usage = usage;
02104    vms->quota_limit = limit;
02105 }
02106 
02107 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
02108 {
02109    char *start, *eol_pnt;
02110    int taglen;
02111 
02112    if (ast_strlen_zero(header) || ast_strlen_zero(tag))
02113       return NULL;
02114 
02115    taglen = strlen(tag) + 1;
02116    if (taglen < 1)
02117       return NULL;
02118 
02119    if (!(start = strstr(header, tag)))
02120       return NULL;
02121 
02122    /* Since we can be called multiple times we should clear our buffer */
02123    memset(buf, 0, len);
02124 
02125    ast_copy_string(buf, start+taglen, len);
02126    if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
02127       *eol_pnt = '\0';
02128    return buf;
02129 }
02130 
02131 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
02132 {
02133    char *start, *quote, *eol_pnt;
02134 
02135    if (ast_strlen_zero(mailbox))
02136       return NULL;
02137 
02138    if (!(start = strstr(mailbox, "/user=")))
02139       return NULL;
02140 
02141    ast_copy_string(buf, start+6, len);
02142 
02143    if (!(quote = strchr(buf, '\"'))) {
02144       if (!(eol_pnt = strchr(buf, '/')))
02145          eol_pnt = strchr(buf,'}');
02146       *eol_pnt = '\0';
02147       return buf;
02148    } else {
02149       eol_pnt = strchr(buf+1,'\"');
02150       *eol_pnt = '\0';
02151       return buf+1;
02152    }
02153 }
02154 
02155 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
02156 {
02157    struct vm_state *vms_p;
02158 
02159    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02160    if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
02161       return vms_p;
02162    }
02163    if (option_debug > 4)
02164       ast_log(LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
02165    if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
02166       return NULL;
02167    ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
02168    ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
02169    vms_p->mailstream = NIL; /* save for access from interactive entry point */
02170    if (option_debug > 4)
02171       ast_log(LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
02172    vms_p->updated = 1;
02173    /* set mailbox to INBOX! */
02174    ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
02175    init_vm_state(vms_p);
02176    vmstate_insert(vms_p);
02177    return vms_p;
02178 }
02179 
02180 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
02181 {
02182    struct vmstate *vlist = NULL;
02183 
02184    if (interactive) {
02185       struct vm_state *vms;
02186       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02187       vms = pthread_getspecific(ts_vmstate.key);
02188       return vms;
02189    }
02190 
02191    AST_LIST_LOCK(&vmstates);
02192    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02193       if (!vlist->vms) {
02194          ast_debug(3, "error: vms is NULL for %s\n", user);
02195          continue;
02196       }
02197       if (!vlist->vms->imapuser) {
02198          ast_debug(3, "error: imapuser is NULL for %s\n", user);
02199          continue;
02200       }
02201 
02202       if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
02203          AST_LIST_UNLOCK(&vmstates);
02204          return vlist->vms;
02205       }
02206    }
02207    AST_LIST_UNLOCK(&vmstates);
02208 
02209    ast_debug(3, "%s not found in vmstates\n", user);
02210 
02211    return NULL;
02212 }
02213 
02214 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive)
02215 {
02216 
02217    struct vmstate *vlist = NULL;
02218 
02219    if (interactive) {
02220       struct vm_state *vms;
02221       pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
02222       vms = pthread_getspecific(ts_vmstate.key);
02223       return vms;
02224    }
02225 
02226    AST_LIST_LOCK(&vmstates);
02227    AST_LIST_TRAVERSE(&vmstates, vlist, list) {
02228       if (!vlist->vms) {
02229          ast_debug(3, "error: vms is NULL for %s\n", mailbox);
02230          continue;
02231       }
02232       if (!vlist->vms->username) {
02233          ast_debug(3, "error: username is NULL for %s\n", mailbox);
02234          continue;
02235       }
02236 
02237       ast_debug(3, "comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n", mailbox, interactive, vlist->vms->username, vlist->vms->interactive);
02238       
02239       if (!strcmp(vlist->vms->username,mailbox) && vlist->vms->interactive == interactive) {
02240          ast_debug(3, "Found it!\n");
02241          AST_LIST_UNLOCK(&vmstates);
02242          return vlist->vms;
02243       }
02244    }
02245    AST_LIST_UNLOCK(&vmstates);
02246 
02247    ast_debug(3, "%s not found in vmstates\n", mailbox);
02248 
02249    return NULL;
02250 }
02251 
02252 static void vmstate_insert(struct vm_state *vms) 
02253 {
02254    struct vmstate *v;
02255    struct vm_state *altvms;
02256 
02257    /* If interactive, it probably already exists, and we should
02258    use the one we already have since it is more up to date.
02259    We can compare the username to find the duplicate */
02260    if (vms->interactive == 1) {
02261       altvms = get_vm_state_by_mailbox(vms->username,0);
02262       if (altvms) {  
02263          ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
02264          vms->newmessages = altvms->newmessages;
02265          vms->oldmessages = altvms->oldmessages;
02266          vms->vmArrayIndex = altvms->vmArrayIndex;
02267          vms->lastmsg = altvms->lastmsg;
02268          vms->curmsg = altvms->curmsg;
02269          /* get a pointer to the persistent store */
02270          vms->persist_vms = altvms;
02271          /* Reuse the mailstream? */
02272 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
02273          vms->mailstream = altvms->mailstream;
02274 #else
02275          vms->mailstream = NIL;
02276 #endif
02277       }
02278       return;
02279    }
02280 
02281    if (!(v = ast_calloc(1, sizeof(*v))))
02282       return;
02283    
02284    v->vms = vms;
02285 
02286    ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
02287 
02288    AST_LIST_LOCK(&vmstates);
02289    AST_LIST_INSERT_TAIL(&vmstates, v, list);
02290    AST_LIST_UNLOCK(&vmstates);
02291 }
02292 
02293 static void vmstate_delete(struct vm_state *vms) 
02294 {
02295    struct vmstate *vc = NULL;
02296    struct vm_state *altvms = NULL;
02297 
02298    /* If interactive, we should copy pertinent info
02299    back to the persistent state (to make update immediate) */
02300    if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
02301       ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
02302       altvms->newmessages = vms->newmessages;
02303       altvms->oldmessages = vms->oldmessages;
02304       altvms->updated = 1;
02305       vms->mailstream = mail_close(vms->mailstream);
02306 
02307       /* Interactive states are not stored within the persistent list */
02308       return;
02309    }
02310    
02311    ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02312    
02313    AST_LIST_LOCK(&vmstates);
02314    AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
02315       if (vc->vms == vms) {
02316          AST_LIST_REMOVE_CURRENT(list);
02317          break;
02318       }
02319    }
02320    AST_LIST_TRAVERSE_SAFE_END
02321    AST_LIST_UNLOCK(&vmstates);
02322    
02323    if (vc) {
02324       ast_mutex_destroy(&vc->vms->lock);
02325       ast_free(vc);
02326    }
02327    else
02328       ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
02329 }
02330 
02331 static void set_update(MAILSTREAM * stream) 
02332 {
02333    struct vm_state *vms;
02334    char *mailbox = stream->mailbox, *user;
02335    char buf[1024] = "";
02336 
02337    if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
02338       if (user && option_debug > 2)
02339          ast_log(LOG_WARNING, "User %s mailbox not found for update.\n", user);
02340       return;
02341    }
02342 
02343    ast_debug(3, "User %s mailbox set for update.\n", user);
02344 
02345    vms->updated = 1; /* Set updated flag since mailbox changed */
02346 }
02347 
02348 static void init_vm_state(struct vm_state *vms) 
02349 {
02350    int x;
02351    vms->vmArrayIndex = 0;
02352    for (x = 0; x < 256; x++) {
02353       vms->msgArray[x] = 0;
02354    }
02355    ast_mutex_init(&vms->lock);
02356 }
02357 
02358 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format) 
02359 {
02360    char *body_content;
02361    char *body_decoded;
02362    unsigned long len;
02363    unsigned long newlen;
02364    char filename[256];
02365    
02366    if (!body || body == NIL)
02367       return -1;
02368    ast_mutex_lock(&vms->lock);
02369    body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
02370    ast_mutex_unlock(&vms->lock);
02371    if (body_content != NIL) {
02372       snprintf(filename, sizeof(filename), "%s.%s", vms->fn, format);
02373       /* ast_log (LOG_DEBUG,body_content); */
02374       body_decoded = rfc822_base64 ((unsigned char *)body_content, len, &newlen);
02375       write_file (filename, (char *) body_decoded, newlen);
02376    }
02377    return 0;
02378 }
02379 /*! 
02380 * \brief Get delimiter via mm_list callback 
02381 * \param stream
02382 *
02383 * Determines the delimiter character that is used by the underlying IMAP based mail store.
02384 */
02385 /* MUTEX should already be held */
02386 static void get_mailbox_delimiter(MAILSTREAM *stream) {
02387    char tmp[50];
02388    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
02389    mail_list(stream, tmp, "*");
02390 }
02391 
02392 /*! 
02393 * \brief Check Quota for user 
02394 * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
02395 * \param mailbox the mailbox to check the quota for.
02396 *
02397 * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
02398 */
02399 static void check_quota(struct vm_state *vms, char *mailbox) {
02400    ast_mutex_lock(&vms->lock);
02401    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
02402    ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
02403    if (vms && vms->mailstream != NULL) {
02404       imap_getquotaroot(vms->mailstream, mailbox);
02405    } else {
02406       ast_log(LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
02407    }
02408    ast_mutex_unlock(&vms->lock);
02409 }
02410 
02411 #endif /* IMAP_STORAGE */
02412 
02413    /*! \brief Lock file path
02414       only return failure if ast_lock_path returns 'timeout',
02415    not if the path does not exist or any other reason
02416    */
02417    static int vm_lock_path(const char *path)
02418    {
02419       switch (ast_lock_path(path)) {
02420       case AST_LOCK_TIMEOUT:
02421          return -1;
02422       default:
02423          return 0;
02424       }
02425    }
02426 
02427 
02428 #ifdef ODBC_STORAGE
02429 struct generic_prepare_struct {
02430    char *sql;
02431    int argc;
02432    char **argv;
02433 };
02434 
02435 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
02436 {
02437    struct generic_prepare_struct *gps = data;
02438    int res, i;
02439    SQLHSTMT stmt;
02440 
02441    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02442    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02443       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
02444       return NULL;
02445    }
02446    res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
02447    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02448       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
02449       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02450       return NULL;
02451    }
02452    for (i = 0; i < gps->argc; i++)
02453       SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
02454 
02455    return stmt;
02456 }
02457 
02458 static int retrieve_file(char *dir, int msgnum)
02459 {
02460    int x = 0;
02461    int res;
02462    int fd = -1;
02463    size_t fdlen = 0;
02464    void *fdm = MAP_FAILED;
02465    SQLSMALLINT colcount = 0;
02466    SQLHSTMT stmt;
02467    char sql[PATH_MAX];
02468    char fmt[80] = "";
02469    char *c;
02470    char coltitle[256];
02471    SQLSMALLINT collen;
02472    SQLSMALLINT datatype;
02473    SQLSMALLINT decimaldigits;
02474    SQLSMALLINT nullable;
02475    SQLULEN colsize;
02476    SQLLEN colsize2;
02477    FILE *f = NULL;
02478    char rowdata[80];
02479    char fn[PATH_MAX];
02480    char full_fn[PATH_MAX];
02481    char msgnums[80];
02482    char *argv[] = { dir, msgnums };
02483    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02484 
02485    struct odbc_obj *obj;
02486    obj = ast_odbc_request_obj(odbc_database, 0);
02487    if (obj) {
02488       ast_copy_string(fmt, vmfmts, sizeof(fmt));
02489       c = strchr(fmt, '|');
02490       if (c)
02491          *c = '\0';
02492       if (!strcasecmp(fmt, "wav49"))
02493          strcpy(fmt, "WAV");
02494       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
02495       if (msgnum > -1)
02496          make_file(fn, sizeof(fn), dir, msgnum);
02497       else
02498          ast_copy_string(fn, dir, sizeof(fn));
02499       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
02500       
02501       if (!(f = fopen(full_fn, "w+"))) {
02502          ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
02503          goto yuck;
02504       }
02505       
02506       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
02507       snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
02508       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02509       if (!stmt) {
02510          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02511          ast_odbc_release_obj(obj);
02512          goto yuck;
02513       }
02514       res = SQLFetch(stmt);
02515       if (res == SQL_NO_DATA) {
02516          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02517          ast_odbc_release_obj(obj);
02518          goto yuck;
02519       } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02520          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02521          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02522          ast_odbc_release_obj(obj);
02523          goto yuck;
02524       }
02525       fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
02526       if (fd < 0) {
02527          ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
02528          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02529          ast_odbc_release_obj(obj);
02530          goto yuck;
02531       }
02532       res = SQLNumResultCols(stmt, &colcount);
02533       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
02534          ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
02535          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02536          ast_odbc_release_obj(obj);
02537          goto yuck;
02538       }
02539       if (f) 
02540          fprintf(f, "[message]\n");
02541       for (x = 0; x < colcount; x++) {
02542          rowdata[0] = '\0';
02543          collen = sizeof(coltitle);
02544          res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen, 
02545                   &datatype, &colsize, &decimaldigits, &nullable);
02546          if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02547             ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
02548             SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02549             ast_odbc_release_obj(obj);
02550             goto yuck;
02551          }
02552          if (!strcasecmp(coltitle, "recording")) {
02553             off_t offset;
02554             res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
02555             fdlen = colsize2;
02556             if (fd > -1) {
02557                char tmp[1] = "";
02558                lseek(fd, fdlen - 1, SEEK_SET);
02559                if (write(fd, tmp, 1) != 1) {
02560                   close(fd);
02561                   fd = -1;
02562                   continue;
02563                }
02564                /* Read out in small chunks */
02565                for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
02566                   if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
02567                      ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
02568                      SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02569                      ast_odbc_release_obj(obj);
02570                      goto yuck;
02571                   } else {
02572                      res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
02573                      munmap(fdm, CHUNKSIZE);
02574                      if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02575                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02576                         unlink(full_fn);
02577                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02578                         ast_odbc_release_obj(obj);
02579                         goto yuck;
02580                      }
02581                   }
02582                }
02583                if (truncate(full_fn, fdlen) < 0) {
02584                   ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
02585                }
02586             }
02587          } else {
02588             SQLLEN ind;
02589             res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &ind);
02590             if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02591                SQLINTEGER nativeerror = 0;
02592                SQLSMALLINT diagbytes = 0;
02593                unsigned char state[10], diagnostic[256];
02594                SQLGetDiagRec(SQL_HANDLE_STMT, stmt, 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
02595                ast_log(LOG_WARNING, "SQL Get Data error: %s: %s!\n[%s]\n\n", state, diagnostic, sql);
02596                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02597                ast_odbc_release_obj(obj);
02598                goto yuck;
02599             }
02600             if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
02601                fprintf(f, "%s=%s\n", coltitle, rowdata);
02602          }
02603       }
02604       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02605       ast_odbc_release_obj(obj);
02606    } else
02607       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02608 yuck: 
02609    if (f)
02610       fclose(f);
02611    if (fd > -1)
02612       close(fd);
02613    return x - 1;
02614 }
02615 
02616 /*!
02617 * \brief Determines the highest message number in use for a given user and mailbox folder.
02618 * \param vmu 
02619 * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
02620 *
02621 * This method is used when mailboxes are stored in an ODBC back end.
02622 * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
02623 *
02624 * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
02625 */
02626 static int last_message_index(struct ast_vm_user *vmu, char *dir)
02627 {
02628    int x = 0;
02629    int res;
02630    SQLHSTMT stmt;
02631    char sql[PATH_MAX];
02632    char rowdata[20];
02633    char *argv[] = { dir };
02634    struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
02635 
02636    struct odbc_obj *obj;
02637    obj = ast_odbc_request_obj(odbc_database, 0);
02638    if (obj) {
02639       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
02640       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02641       if (!stmt) {
02642          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02643          ast_odbc_release_obj(obj);
02644          goto yuck;
02645       }
02646       res = SQLFetch(stmt);
02647       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02648          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02649          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02650          ast_odbc_release_obj(obj);
02651          goto yuck;
02652       }
02653       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02654       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02655          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02656          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02657          ast_odbc_release_obj(obj);
02658          goto yuck;
02659       }
02660       if (sscanf(rowdata, "%d", &x) != 1)
02661          ast_log(LOG_WARNING, "Failed to read message count!\n");
02662       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02663       ast_odbc_release_obj(obj);
02664    } else
02665       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02666 yuck: 
02667    return x - 1;
02668 }
02669 
02670 static int message_exists(char *dir, int msgnum)
02671 {
02672    int x = 0;
02673    int res;
02674    SQLHSTMT stmt;
02675    char sql[PATH_MAX];
02676    char rowdata[20];
02677    char msgnums[20];
02678    char *argv[] = { dir, msgnums };
02679    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02680 
02681    struct odbc_obj *obj;
02682    obj = ast_odbc_request_obj(odbc_database, 0);
02683    if (obj) {
02684       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
02685       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
02686       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02687       if (!stmt) {
02688          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02689          ast_odbc_release_obj(obj);
02690          goto yuck;
02691       }
02692       res = SQLFetch(stmt);
02693       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02694          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
02695          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02696          ast_odbc_release_obj(obj);
02697          goto yuck;
02698       }
02699       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
02700       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02701          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
02702          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02703          ast_odbc_release_obj(obj);
02704          goto yuck;
02705       }
02706       if (sscanf(rowdata, "%d", &x) != 1)
02707          ast_log(LOG_WARNING, "Failed to read message count!\n");
02708       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02709       ast_odbc_release_obj(obj);
02710    } else
02711       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02712 yuck: 
02713    return x;
02714 }
02715 
02716 static int count_messages(struct ast_vm_user *vmu, char *dir)
02717 {
02718    return last_message_index(vmu, dir) + 1;
02719 }
02720 
02721 static void delete_file(char *sdir, int smsg)
02722 {
02723    SQLHSTMT stmt;
02724    char sql[PATH_MAX];
02725    char msgnums[20];
02726    char *argv[] = { sdir, msgnums };
02727    struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
02728 
02729    struct odbc_obj *obj;
02730    obj = ast_odbc_request_obj(odbc_database, 0);
02731    if (obj) {
02732       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
02733       snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
02734       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02735       if (!stmt)
02736          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02737       else
02738          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
02739       ast_odbc_release_obj(obj);
02740    } else
02741       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02742    return;  
02743 }
02744 
02745 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
02746 {
02747    SQLHSTMT stmt;
02748    char sql[512];
02749    char msgnums[20];
02750    char msgnumd[20];
02751    struct odbc_obj *obj;
02752    char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
02753    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
02754 
02755    delete_file(ddir, dmsg);
02756    obj = ast_odbc_request_obj(odbc_database, 0);
02757    if (obj) {
02758       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
02759       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
02760       snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,?,? FROM %s WHERE dir=? AND msgnum=?", odbc_table, odbc_table);
02761       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02762       if (!stmt)
02763          ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
02764       else
02765          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02766       ast_odbc_release_obj(obj);
02767    } else
02768       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02769    return;  
02770 }
02771 
02772 struct insert_cb_struct {
02773    char *dir;
02774    char *msgnum;
02775    void *recording;
02776    size_t recordinglen;
02777    SQLLEN indlen;
02778    const char *context;
02779    const char *macrocontext;
02780    const char *callerid;
02781    const char *origtime;
02782    const char *duration;
02783    char *mailboxuser;
02784    char *mailboxcontext;
02785    const char *category;
02786    char *sql;
02787 };
02788 
02789 static SQLHSTMT insert_cb(struct odbc_obj *obj, void *vd)
02790 {
02791    struct insert_cb_struct *d = vd;
02792    int res;
02793    SQLHSTMT stmt;
02794 
02795    res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
02796    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02797       ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
02798       return NULL;
02799    }
02800 
02801    res = SQLPrepare(stmt, (unsigned char *)d->sql, SQL_NTS);
02802    if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
02803       ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", d->sql);
02804       SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02805       return NULL;
02806    }
02807 
02808    SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->dir), 0, (void *)d->dir, 0, NULL);
02809    SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->msgnum), 0, (void *)d->msgnum, 0, NULL);
02810    SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, d->recordinglen, 0, (void *)d->recording, 0, &d->indlen);
02811    SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->context), 0, (void *)d->context, 0, NULL);
02812    SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->macrocontext), 0, (void *)d->macrocontext, 0, NULL);
02813    SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->callerid), 0, (void *)d->callerid, 0, NULL);
02814    SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->origtime), 0, (void *)d->origtime, 0, NULL);
02815    SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->duration), 0, (void *)d->duration, 0, NULL);
02816    SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->mailboxuser), 0, (void *)d->mailboxuser, 0, NULL);
02817    SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->mailboxcontext), 0, (void *)d->mailboxcontext, 0, NULL);
02818    if (!ast_strlen_zero(d->category)) {
02819       SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->category), 0, (void *)d->category, 0, NULL);
02820    }
02821 
02822    return stmt;
02823 }
02824 
02825 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
02826 {
02827    int x = 0;
02828    int fd = -1;
02829    void *fdm = MAP_FAILED;
02830    size_t fdlen = -1;
02831    SQLHSTMT stmt;
02832    char sql[PATH_MAX];
02833    char msgnums[20];
02834    char fn[PATH_MAX];
02835    char full_fn[PATH_MAX];
02836    char fmt[80] = "";
02837    char *c;
02838    struct insert_cb_struct d = {
02839       .dir = dir,
02840       .msgnum = msgnums,
02841       .context = "",
02842       .macrocontext = "",
02843       .callerid = "",
02844       .origtime = "",
02845       .duration = "",
02846       .mailboxuser = mailboxuser,
02847       .mailboxcontext = mailboxcontext,
02848       .category = "",
02849       .sql = sql
02850    };
02851    struct ast_config *cfg = NULL;
02852    struct odbc_obj *obj;
02853    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
02854 
02855    delete_file(dir, msgnum);
02856    obj = ast_odbc_request_obj(odbc_database, 0);
02857    if (obj) {
02858       ast_copy_string(fmt, vmfmts, sizeof(fmt));
02859       c = strchr(fmt, '|');
02860       if (c)
02861          *c = '\0';
02862       if (!strcasecmp(fmt, "wav49"))
02863          strcpy(fmt, "WAV");
02864       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
02865       if (msgnum > -1)
02866          make_file(fn, sizeof(fn), dir, msgnum);
02867       else
02868          ast_copy_string(fn, dir, sizeof(fn));
02869       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
02870       cfg = ast_config_load(full_fn, config_flags);
02871       snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
02872       fd = open(full_fn, O_RDWR);
02873       if (fd < 0) {
02874          ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
02875          ast_odbc_release_obj(obj);
02876          goto yuck;
02877       }
02878       if (cfg) {
02879          d.context = ast_variable_retrieve(cfg, "message", "context");
02880          if (!d.context) d.context = "";
02881          d.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
02882          if (!d.macrocontext) d.macrocontext = "";
02883          d.callerid = ast_variable_retrieve(cfg, "message", "callerid");
02884          if (!d.callerid) d.callerid = "";
02885          d.origtime = ast_variable_retrieve(cfg, "message", "origtime");
02886          if (!d.origtime) d.origtime = "";
02887          d.duration = ast_variable_retrieve(cfg, "message", "duration");
02888          if (!d.duration) d.duration = "";
02889          d.category = ast_variable_retrieve(cfg, "message", "category");
02890          if (!d.category) d.category = "";
02891       }
02892       fdlen = lseek(fd, 0, SEEK_END);
02893       lseek(fd, 0, SEEK_SET);
02894       printf("Length is %zd\n", fdlen);
02895       fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
02896       if (fdm == MAP_FAILED) {
02897          ast_log(LOG_WARNING, "Memory map failed!\n");
02898          ast_odbc_release_obj(obj);
02899          goto yuck;
02900       } 
02901       d.recording = fdm;
02902       d.recordinglen = d.indlen = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
02903       if (!ast_strlen_zero(d.category)) 
02904          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
02905       else
02906          snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)", odbc_table);
02907       stmt = ast_odbc_prepare_and_execute(obj, insert_cb, &d);
02908       if (stmt) {
02909          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02910       }
02911       ast_odbc_release_obj(obj);
02912    } else
02913       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02914 yuck: 
02915    if (cfg)
02916       ast_config_destroy(cfg);
02917    if (fdm != MAP_FAILED)
02918       munmap(fdm, fdlen);
02919    if (fd > -1)
02920       close(fd);
02921    return x;
02922 }
02923 
02924 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
02925 {
02926    SQLHSTMT stmt;
02927    char sql[PATH_MAX];
02928    char msgnums[20];
02929    char msgnumd[20];
02930    struct odbc_obj *obj;
02931    char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
02932    struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
02933 
02934    delete_file(ddir, dmsg);
02935    obj = ast_odbc_request_obj(odbc_database, 0);
02936    if (obj) {
02937       snprintf(msgnums, sizeof(msgnums), "%d", smsg);
02938       snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
02939       snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
02940       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
02941       if (!stmt)
02942          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
02943       else
02944          SQLFreeHandle(SQL_HANDLE_STMT, stmt);
02945       ast_odbc_release_obj(obj);
02946    } else
02947       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
02948    return;  
02949 }
02950 
02951 /*!
02952  * \brief Removes a voicemail message file.
02953  * \param dir the path to the message file.
02954  * \param msgnum the unique number for the message within the mailbox.
02955  *
02956  * Removes the message content file and the information file.
02957  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
02958  * Typical use is to clean up after a RETRIEVE operation. 
02959  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
02960  * \return zero on success, -1 on error.
02961  */
02962 static int remove_file(char *dir, int msgnum)
02963 {
02964    char fn[PATH_MAX];
02965    char full_fn[PATH_MAX];
02966    char msgnums[80];
02967    
02968    if (msgnum > -1) {
02969       snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
02970       make_file(fn, sizeof(fn), dir, msgnum);
02971    } else
02972       ast_copy_string(fn, dir, sizeof(fn));
02973    ast_filedelete(fn, NULL);  
02974    snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
02975    unlink(full_fn);
02976    return 0;
02977 }
02978 #else
02979 #ifndef IMAP_STORAGE
02980 static int count_messages(struct ast_vm_user *vmu, char *dir)
02981 {
02982    /* Find all .txt files - even if they are not in sequence from 0000 */
02983 
02984    int vmcount = 0;
02985    DIR *vmdir = NULL;
02986    struct dirent *vment = NULL;
02987 
02988    if (vm_lock_path(dir))
02989       return ERROR_LOCK_PATH;
02990 
02991    if ((vmdir = opendir(dir))) {
02992       while ((vment = readdir(vmdir))) {
02993          if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
02994             vmcount++;
02995       }
02996       closedir(vmdir);
02997    }
02998    ast_unlock_path(dir);
02999    
03000    return vmcount;
03001 }
03002 
03003 static void rename_file(char *sfn, char *dfn)
03004 {
03005    char stxt[PATH_MAX];
03006    char dtxt[PATH_MAX];
03007    ast_filerename(sfn, dfn, NULL);
03008    snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
03009    snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
03010    if (ast_check_realtime("voicemail_data")) {
03011       ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, NULL);
03012    }
03013    rename(stxt, dtxt);
03014 }
03015 #endif
03016 
03017 #ifndef IMAP_STORAGE
03018 /*! \brief
03019 * A negative return value indicates an error.
03020 * \note Should always be called with a lock already set on dir.
03021 */
03022 static int last_message_index(struct ast_vm_user *vmu, char *dir)
03023 {
03024    int x;
03025    unsigned char map[MAXMSGLIMIT] = "";
03026    DIR *msgdir;
03027    struct dirent *msgdirent;
03028    int msgdirint;
03029 
03030    /* Reading the entire directory into a file map scales better than
03031    * doing a stat repeatedly on a predicted sequence.  I suspect this
03032    * is partially due to stat(2) internally doing a readdir(2) itself to
03033    * find each file. */
03034    msgdir = opendir(dir);
03035    while ((msgdirent = readdir(msgdir))) {
03036       if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
03037          map[msgdirint] = 1;
03038    }
03039    closedir(msgdir);
03040 
03041    for (x = 0; x < vmu->maxmsg; x++) {
03042       if (map[x] == 0)
03043          break;
03044    }
03045 
03046    return x - 1;
03047 }
03048 
03049 #endif /* #ifndef IMAP_STORAGE */
03050 #endif /* #else of #ifdef ODBC_STORAGE */
03051 
03052 static int copy(char *infile, char *outfile)
03053 {
03054    int ifd;
03055    int ofd;
03056    int res;
03057    int len;
03058    char buf[4096];
03059 
03060 #ifdef HARDLINK_WHEN_POSSIBLE
03061    /* Hard link if possible; saves disk space & is faster */
03062    if (link(infile, outfile)) {
03063 #endif
03064       if ((ifd = open(infile, O_RDONLY)) < 0) {
03065          ast_log(LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
03066          return -1;
03067       }
03068       if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
03069          ast_log(LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
03070          close(ifd);
03071          return -1;
03072       }
03073       do {
03074          len = read(ifd, buf, sizeof(buf));
03075          if (len < 0) {
03076             ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
03077             close(ifd);
03078             close(ofd);
03079             unlink(outfile);
03080          }
03081          if (len) {
03082             res = write(ofd, buf, len);
03083             if (errno == ENOMEM || errno == ENOSPC || res != len) {
03084                ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
03085                close(ifd);
03086                close(ofd);
03087                unlink(outfile);
03088             }
03089          }
03090       } while (len);
03091       close(ifd);
03092       close(ofd);
03093       return 0;
03094 #ifdef HARDLINK_WHEN_POSSIBLE
03095    } else {
03096       /* Hard link succeeded */
03097       return 0;
03098    }
03099 #endif
03100 }
03101 
03102 static void copy_plain_file(char *frompath, char *topath)
03103 {
03104    char frompath2[PATH_MAX], topath2[PATH_MAX];
03105    struct ast_variable *tmp, *var = NULL;
03106    const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
03107    ast_filecopy(frompath, topath, NULL);
03108    snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
03109    snprintf(topath2, sizeof(topath2), "%s.txt", topath);
03110    if (ast_check_realtime("voicemail_data")) {
03111       var = ast_load_realtime("voicemail_data", "filename", frompath, NULL);
03112       /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
03113       for (tmp = var; tmp; tmp = tmp->next) {
03114          if (!strcasecmp(tmp->name, "origmailbox")) {
03115             origmailbox = tmp->value;
03116          } else if (!strcasecmp(tmp->name, "context")) {
03117             context = tmp->value;
03118          } else if (!strcasecmp(tmp->name, "macrocontext")) {
03119             macrocontext = tmp->value;
03120          } else if (!strcasecmp(tmp->name, "exten")) {
03121             exten = tmp->value;
03122          } else if (!strcasecmp(tmp->name, "priority")) {
03123             priority = tmp->value;
03124          } else if (!strcasecmp(tmp->name, "callerchan")) {
03125             callerchan = tmp->value;
03126          } else if (!strcasecmp(tmp->name, "callerid")) {
03127             callerid = tmp->value;
03128          } else if (!strcasecmp(tmp->name, "origdate")) {
03129             origdate = tmp->value;
03130          } else if (!strcasecmp(tmp->name, "origtime")) {
03131             origtime = tmp->value;
03132          } else if (!strcasecmp(tmp->name, "category")) {
03133             category = tmp->value;
03134          } else if (!strcasecmp(tmp->name, "duration")) {
03135             duration = tmp->value;
03136          }
03137       }
03138       ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, NULL);
03139    }
03140    copy(frompath2, topath2);
03141    ast_variables_destroy(var);
03142 }
03143 
03144 
03145 #if (!defined(ODBC_STORAGE) && !defined(IMAP_STORAGE))
03146 /*! 
03147 * \brief Removes the voicemail sound and information file.
03148 * \param file The path to the sound file. This will be the the folder and message index, without the extension.
03149 *
03150 * This is used by the DELETE macro when voicemails are stored on the file system.
03151 *
03152 * \return zero on success, -1 on error.
03153 */
03154 static int vm_delete(char *file)
03155 {
03156    char *txt;
03157    int txtsize = 0;
03158 
03159    txtsize = (strlen(file) + 5) * sizeof(char);
03160    txt = alloca(txtsize);
03161    /* Sprintf here would safe because we alloca'd exactly the right length,
03162    * but trying to eliminate all sprintf's anyhow
03163    */
03164    if (ast_check_realtime("voicemail_data")) {
03165       ast_destroy_realtime("voicemail_data", "filename", file, NULL);
03166    }
03167    snprintf(txt, txtsize, "%s.txt", file);
03168    unlink(txt);
03169    return ast_filedelete(file, NULL);
03170 }
03171 #endif
03172 static int inbuf(struct baseio *bio, FILE *fi)
03173 {
03174    int l;
03175 
03176    if (bio->ateof)
03177       return 0;
03178 
03179    if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
03180       if (ferror(fi))
03181          return -1;
03182 
03183       bio->ateof = 1;
03184       return 0;
03185    }
03186 
03187    bio->iolen = l;
03188    bio->iocp = 0;
03189 
03190    return 1;
03191 }
03192 
03193 static int inchar(struct baseio *bio, FILE *fi)
03194 {
03195    if (bio->iocp >= bio->iolen) {
03196       if (!inbuf(bio, fi))
03197          return EOF;
03198    }
03199 
03200    return bio->iobuf[bio->iocp++];
03201 }
03202 
03203 static int ochar(struct baseio *bio, int c, FILE *so)
03204 {
03205    if (bio->linelength >= BASELINELEN) {
03206       if (fputs(eol, so) == EOF)
03207          return -1;
03208 
03209       bio->linelength= 0;
03210    }
03211 
03212    if (putc(((unsigned char)c), so) == EOF)
03213       return -1;
03214 
03215    bio->linelength++;
03216 
03217    return 1;
03218 }
03219 
03220 static int base_encode(char *filename, FILE *so)
03221 {
03222    static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
03223       'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
03224       'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
03225       '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
03226    int i, hiteof = 0;
03227    FILE *fi;
03228    struct baseio bio;
03229 
03230    memset(&bio, 0, sizeof(bio));
03231    bio.iocp = BASEMAXINLINE;
03232 
03233    if (!(fi = fopen(filename, "rb"))) {
03234       ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
03235       return -1;
03236    }
03237 
03238    while (!hiteof) {
03239       unsigned char igroup[3], ogroup[4];
03240       int c, n;
03241 
03242       igroup[0] = igroup[1] = igroup[2] = 0;
03243 
03244       for (n = 0; n < 3; n++) {
03245          if ((c = inchar(&bio, fi)) == EOF) {
03246             hiteof = 1;
03247             break;
03248          }
03249 
03250          igroup[n] = (unsigned char)c;
03251       }
03252 
03253       if (n > 0) {
03254          ogroup[0] = dtable[igroup[0] >> 2];
03255          ogroup[1] = dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
03256          ogroup[2] = dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
03257          ogroup[3] = dtable[igroup[2] & 0x3F];
03258 
03259          if (n < 3) {
03260             ogroup[3] = '=';
03261 
03262             if (n < 2)
03263                ogroup[2] = '=';
03264          }
03265 
03266          for (i = 0; i < 4; i++)
03267             ochar(&bio, ogroup[i], so);
03268       }
03269    }
03270 
03271    fclose(fi);
03272    
03273    if (fputs(eol, so) == EOF)
03274       return 0;
03275 
03276    return 1;
03277 }
03278 
03279 static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category)
03280 {
03281    char callerid[256];
03282    char fromdir[256], fromfile[256];
03283    struct ast_config *msg_cfg;
03284    const char *origcallerid, *origtime;
03285    char origcidname[80], origcidnum[80], origdate[80];
03286    int inttime;
03287    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03288 
03289    /* Prepare variables for substitution in email body and subject */
03290    pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
03291    pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
03292    snprintf(passdata, passdatasize, "%d", msgnum);
03293    pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
03294    pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
03295    pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
03296    pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
03297       ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
03298    pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
03299    pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
03300    pbx_builtin_setvar_helper(ast, "VM_DATE", date);
03301    pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
03302 
03303    /* Retrieve info from VM attribute file */
03304    make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
03305    make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
03306    if (strlen(fromfile) < sizeof(fromfile) - 5) {
03307       strcat(fromfile, ".txt");
03308    }
03309    if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
03310       if (option_debug > 0) {
03311          ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
03312       }
03313       return;
03314    }
03315 
03316    if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
03317       pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
03318       ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
03319       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
03320       pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
03321    }
03322 
03323    if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%d", &inttime) == 1) {
03324       struct timeval tv = { inttime, };
03325       struct ast_tm tm;
03326       ast_localtime(&tv, &tm, NULL);
03327       ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
03328       pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
03329    }
03330    ast_config_destroy(msg_cfg);
03331 }
03332 
03333 static char *quote(const char *from, char *to, size_t len)
03334 {
03335    char *ptr = to;
03336    *ptr++ = '"';
03337    for (; ptr < to + len - 1; from++) {
03338       if (*from == '"')
03339          *ptr++ = '\\';
03340       else if (*from == '\0')
03341          break;
03342       *ptr++ = *from;
03343    }
03344    if (ptr < to + len - 1)
03345       *ptr++ = '"';
03346    *ptr = '\0';
03347    return to;
03348 }
03349 
03350 /*! \brief
03351 * fill in *tm for current time according to the proper timezone, if any.
03352 * Return tm so it can be used as a function argument.
03353 */
03354 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
03355 {
03356    const struct vm_zone *z = NULL;
03357    struct timeval t = ast_tvnow();
03358 
03359    /* Does this user have a timezone specified? */
03360    if (!ast_strlen_zero(vmu->zonetag)) {
03361       /* Find the zone in the list */
03362       AST_LIST_LOCK(&zones);
03363       AST_LIST_TRAVERSE(&zones, z, list) {
03364          if (!strcmp(z->name, vmu->zonetag))
03365             break;
03366       }
03367       AST_LIST_UNLOCK(&zones);
03368    }
03369    ast_localtime(&t, tm, z ? z->timezone : NULL);
03370    return tm;
03371 }
03372 
03373 /*!\brief Check if the string would need encoding within the MIME standard, to
03374  * avoid confusing certain mail software that expects messages to be 7-bit
03375  * clean.
03376  */
03377 static int check_mime(const char *str)
03378 {
03379    for (; *str; str++) {
03380       if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
03381          return 1;
03382       }
03383    }
03384    return 0;
03385 }
03386 
03387 /*!\brief Encode a string according to the MIME rules for encoding strings
03388  * that are not 7-bit clean or contain control characters.
03389  *
03390  * Additionally, if the encoded string would exceed the MIME limit of 76
03391  * characters per line, then the encoding will be broken up into multiple
03392  * sections, separated by a space character, in order to facilitate
03393  * breaking up the associated header across multiple lines.
03394  *
03395  * \param start A string to be encoded
03396  * \param end An expandable buffer for holding the result
03397  * \param preamble The length of the first line already used for this string,
03398  * to ensure that each line maintains a maximum length of 76 chars.
03399  * \param postamble the length of any additional characters appended to the
03400  * line, used to ensure proper field wrapping.
03401  * \retval The encoded string.
03402  */
03403 static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
03404 {
03405    char tmp[80];
03406    int first_section = 1;
03407    size_t endlen = 0, tmplen = 0;
03408    *end = '\0';
03409 
03410    tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
03411    for (; *start; start++) {
03412       int need_encoding = 0;
03413       if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
03414          need_encoding = 1;
03415       }
03416       if ((first_section && need_encoding && preamble + tmplen > 70) ||
03417          (first_section && !need_encoding && preamble + tmplen > 72) ||
03418          (!first_section && need_encoding && tmplen > 70) ||
03419          (!first_section && !need_encoding && tmplen > 72)) {
03420          /* Start new line */
03421          endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
03422          tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
03423          first_section = 0;
03424       }
03425       if (need_encoding && *start == ' ') {
03426          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
03427       } else if (need_encoding) {
03428          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
03429       } else {
03430          tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
03431       }
03432    }
03433    snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
03434    return end;
03435 }
03436 
03437 /*!
03438  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
03439  * \param p The output file to generate the email contents into.
03440  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
03441  * \param vmu The voicemail user who is sending the voicemail.
03442  * \param msgnum The message index in the mailbox folder.
03443  * \param context 
03444  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
03445  * \param cidnum The caller ID number.
03446  * \param cidname The caller ID name.
03447  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
03448  * \param format The message sound file format. i.e. .wav
03449  * \param duration The time of the message content, in seconds.
03450  * \param attach_user_voicemail if 1, the sound file is attached to the email.
03451  * \param chan
03452  * \param category
03453  * \param imap if == 1, indicates the target folder for the email notification to be sent to will be an IMAP mailstore. This causes additional mailbox headers to be set, which would facilitate searching for the email in the destination IMAP folder.
03454  *
03455  * The email body, and base 64 encoded attachement (if any) are stored to the file identified by *p. This method does not actually send the email.  That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function.
03456  */
03457 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 *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap)
03458 {
03459    char date[256];
03460    char host[MAXHOSTNAMELEN] = "";
03461    char who[256];
03462    char bound[256];
03463    char fname[256];
03464    char dur[256];
03465    char tmpcmd[256];
03466    struct ast_tm tm;
03467    char enc_cidnum[256] = "", enc_cidname[256] = "";
03468    char *passdata = NULL, *passdata2;
03469    size_t len_passdata = 0, len_passdata2, tmplen;
03470    char *greeting_attachment;
03471 
03472 #ifdef IMAP_STORAGE
03473 #define ENDL "\r\n"
03474 #else
03475 #define ENDL "\n"
03476 #endif
03477 
03478    /* One alloca for multiple fields */
03479    len_passdata2 = strlen(vmu->fullname);
03480    if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
03481       len_passdata2 = tmplen;
03482    }
03483    if ((tmplen = strlen(fromstring)) > len_passdata2) {
03484       len_passdata2 = tmplen;
03485    }
03486    len_passdata2 = len_passdata2 * 3 + 200;
03487    passdata2 = alloca(len_passdata2);
03488 
03489    if (!ast_strlen_zero(cidnum)) {
03490       strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
03491    }
03492    if (!ast_strlen_zero(cidname)) {
03493       strip_control(cidname, enc_cidname, sizeof(enc_cidname));
03494    }
03495    gethostname(host, sizeof(host) - 1);
03496 
03497    if (strchr(srcemail, '@'))
03498       ast_copy_string(who, srcemail, sizeof(who));
03499    else 
03500       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
03501    
03502    greeting_attachment = strrchr(ast_strdupa(attach), '/');
03503    if (greeting_attachment)
03504       *greeting_attachment++ = '\0';
03505 
03506    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
03507    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
03508    fprintf(p, "Date: %s" ENDL, date);
03509 
03510    /* Set date format for voicemail mail */
03511    ast_strftime(date, sizeof(date), emaildateformat, &tm);
03512 
03513    if (!ast_strlen_zero(fromstring)) {
03514       struct ast_channel *ast;
03515       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03516          char *ptr;
03517          memset(passdata2, 0, len_passdata2);
03518          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category);
03519          pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
03520          len_passdata = strlen(passdata2) * 3 + 300;
03521          passdata = alloca(len_passdata);
03522          if (check_mime(passdata2)) {
03523             int first_line = 1;
03524             encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
03525             while ((ptr = strchr(passdata, ' '))) {
03526                *ptr = '\0';
03527                fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
03528                first_line = 0;
03529                passdata = ptr + 1;
03530             }
03531             fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
03532          } else {
03533             fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
03534          }
03535          ast_channel_free(ast);
03536       } else {
03537          ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03538       }
03539    } else {
03540       fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
03541    }
03542 
03543    if (check_mime(vmu->fullname)) {
03544       int first_line = 1;
03545       char *ptr;
03546       encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
03547       while ((ptr = strchr(passdata2, ' '))) {
03548          *ptr = '\0';
03549          fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
03550          first_line = 0;
03551          passdata2 = ptr + 1;
03552       }
03553       fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
03554    } else {
03555       fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
03556    }
03557    if (!ast_strlen_zero(emailsubject)) {
03558       struct ast_channel *ast;
03559       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03560          int vmlen = strlen(emailsubject) * 3 + 200;
03561          /* Only allocate more space if the previous was not large enough */
03562          if (vmlen > len_passdata) {
03563             passdata = alloca(vmlen);
03564             len_passdata = vmlen;
03565          }
03566 
03567          memset(passdata, 0, len_passdata);
03568          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category);
03569          pbx_substitute_variables_helper(ast, emailsubject, passdata, len_passdata);
03570          if (check_mime(passdata)) {
03571             int first_line = 1;
03572             char *ptr;
03573             encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
03574             while ((ptr = strchr(passdata2, ' '))) {
03575                *ptr = '\0';
03576                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
03577                first_line = 0;
03578                passdata2 = ptr + 1;
03579             }
03580             fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
03581          } else {
03582             fprintf(p, "Subject: %s" ENDL, passdata);
03583          }
03584          ast_channel_free(ast);
03585       } else {
03586          ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03587       }
03588    } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
03589       fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
03590    } else {
03591       fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
03592    }
03593 
03594    fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
03595    if (imap) {
03596       /* additional information needed for IMAP searching */
03597       fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
03598       /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
03599       fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
03600       fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
03601 #ifdef IMAP_STORAGE
03602       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
03603 #else
03604       fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
03605 #endif
03606       fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
03607       fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
03608       fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
03609       fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
03610       fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
03611       if (!ast_strlen_zero(category)) {
03612          fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
03613       } else {
03614          fprintf(p, "X-Asterisk-VM-Category: " ENDL);
03615       }
03616       fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
03617       fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
03618       fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
03619    }
03620    if (!ast_strlen_zero(cidnum)) {
03621       fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
03622    }
03623    if (!ast_strlen_zero(cidname)) {
03624       fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
03625    }
03626    fprintf(p, "MIME-Version: 1.0" ENDL);
03627    if (attach_user_voicemail) {
03628       /* Something unique. */
03629       snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
03630 
03631       fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
03632       fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
03633       fprintf(p, "--%s" ENDL, bound);
03634    }
03635    fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
03636    if (emailbody) {
03637       struct ast_channel *ast;
03638       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03639          char *passdata;
03640          int vmlen = strlen(emailbody) * 3 + 200;
03641          passdata = alloca(vmlen);
03642          memset(passdata, 0, vmlen);
03643          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03644          pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
03645          fprintf(p, "%s" ENDL, passdata);
03646          ast_channel_free(ast);
03647       } else
03648          ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03649    } else if (msgnum > -1) {
03650       if (strcmp(vmu->mailbox, mailbox)) {
03651          /* Forwarded type */
03652          struct ast_config *msg_cfg;
03653          const char *v;
03654          int inttime;
03655          char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
03656          struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
03657          /* Retrieve info from VM attribute file */
03658          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
03659          make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
03660          if (strlen(fromfile) < sizeof(fromfile) - 5) {
03661             strcat(fromfile, ".txt");
03662          }
03663          if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
03664             if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
03665                ast_copy_string(origcallerid, v, sizeof(origcallerid));
03666             }
03667 
03668             /* You might be tempted to do origdate, except that a) it's in the wrong
03669              * format, and b) it's missing for IMAP recordings. */
03670             if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%d", &inttime) == 1) {
03671                struct timeval tv = { inttime, };
03672                struct ast_tm tm;
03673                ast_localtime(&tv, &tm, NULL);
03674                ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
03675             }
03676             fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
03677                " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
03678                "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
03679                " chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
03680                msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
03681                date, origcallerid, origdate);
03682             ast_config_destroy(msg_cfg);
03683          } else {
03684             goto plain_message;
03685          }
03686       } else {
03687 plain_message:
03688          fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
03689             "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
03690             "want to check it when you get a chance.  Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
03691             ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
03692             (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
03693       }
03694    } else {
03695       fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
03696             "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
03697    }
03698    if (attach_user_voicemail) {
03699       /* Eww. We want formats to tell us their own MIME type */
03700       char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
03701       char tmpdir[256], newtmp[256];
03702       int tmpfd = -1;
03703    
03704       if (vmu->volgain < -.001 || vmu->volgain > .001) {
03705          create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
03706          snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
03707          tmpfd = mkstemp(newtmp);
03708          chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
03709          ast_debug(3, "newtmp: %s\n", newtmp);
03710          if (tmpfd > -1) {
03711             int soxstatus;
03712             snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
03713             if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
03714                attach = newtmp;
03715                ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
03716             } else {
03717                ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
03718                   soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
03719                ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
03720             }
03721          }
03722       }
03723       fprintf(p, "--%s" ENDL, bound);
03724       if (msgnum > -1)
03725          fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
03726       else
03727          fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
03728       fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
03729       fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
03730       if (msgnum > -1)
03731          fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
03732       else
03733          fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
03734       snprintf(fname, sizeof(fname), "%s.%s", attach, format);
03735       base_encode(fname, p);
03736       fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
03737       if (tmpfd > -1) {
03738          unlink(fname);
03739          close(tmpfd);
03740          unlink(newtmp);
03741       }
03742    }
03743 #undef ENDL
03744 }
03745 
03746 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category)
03747 {
03748    FILE *p = NULL;
03749    char tmp[80] = "/tmp/astmail-XXXXXX";
03750    char tmp2[256];
03751 
03752    if (vmu && ast_strlen_zero(vmu->email)) {
03753       ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
03754       return(0);
03755    }
03756    if (!strcmp(format, "wav49"))
03757       format = "WAV";
03758    ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
03759    /* Make a temporary file instead of piping directly to sendmail, in case the mail
03760    command hangs */
03761    if ((p = vm_mkftemp(tmp)) == NULL) {
03762       ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
03763       return -1;
03764    } else {
03765       make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
03766       fclose(p);
03767       snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
03768       ast_safe_system(tmp2);
03769       ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
03770    }
03771    return 0;
03772 }
03773 
03774 static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category)
03775 {
03776    char date[256];
03777    char host[MAXHOSTNAMELEN] = "";
03778    char who[256];
03779    char dur[PATH_MAX];
03780    char tmp[80] = "/tmp/astmail-XXXXXX";
03781    char tmp2[PATH_MAX];
03782    struct ast_tm tm;
03783    FILE *p;
03784 
03785    if ((p = vm_mkftemp(tmp)) == NULL) {
03786       ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
03787       return -1;
03788    }
03789    gethostname(host, sizeof(host) - 1);
03790    if (strchr(srcemail, '@'))
03791       ast_copy_string(who, srcemail, sizeof(who));
03792    else 
03793       snprintf(who, sizeof(who), "%s@%s", srcemail, host);
03794    snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
03795    ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
03796    fprintf(p, "Date: %s\n", date);
03797 
03798    if (*pagerfromstring) {
03799       struct ast_channel *ast;
03800       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03801          char *passdata;
03802          int vmlen = strlen(fromstring) * 3 + 200;
03803          passdata = alloca(vmlen);
03804          memset(passdata, 0, vmlen);
03805          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03806          pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
03807          fprintf(p, "From: %s <%s>\n", passdata, who);
03808          ast_channel_free(ast);
03809       } else 
03810          ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03811    } else
03812       fprintf(p, "From: Asterisk PBX <%s>\n", who);
03813    fprintf(p, "To: %s\n", pager);
03814    if (pagersubject) {
03815       struct ast_channel *ast;
03816       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03817          char *passdata;
03818          int vmlen = strlen(pagersubject) * 3 + 200;
03819          passdata = alloca(vmlen);
03820          memset(passdata, 0, vmlen);
03821          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03822          pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
03823          fprintf(p, "Subject: %s\n\n", passdata);
03824          ast_channel_free(ast);
03825       } else
03826          ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03827    } else
03828       fprintf(p, "Subject: New VM\n\n");
03829 
03830    ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
03831    if (pagerbody) {
03832       struct ast_channel *ast;
03833       if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
03834          char *passdata;
03835          int vmlen = strlen(pagerbody) * 3 + 200;
03836          passdata = alloca(vmlen);
03837          memset(passdata, 0, vmlen);
03838          prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category);
03839          pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
03840          fprintf(p, "%s\n", passdata);
03841          ast_channel_free(ast);
03842       } else
03843          ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
03844    } else {
03845       fprintf(p, "New %s long msg in box %s\n"
03846             "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
03847    }
03848    fclose(p);
03849    snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
03850    ast_safe_system(tmp2);
03851    ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
03852    return 0;
03853 }
03854 
03855 static int get_date(char *s, int len)
03856 {
03857    struct ast_tm tm;
03858    struct timeval t = ast_tvnow();
03859    
03860    ast_localtime(&t, &tm, "UTC");
03861 
03862    return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
03863 }
03864 
03865 static int play_greeting(struct ast_channel *chan, struct ast_vm_user *vmu, char *filename, char *ecodes)
03866 {
03867    int res = -2;
03868    
03869 #ifdef ODBC_STORAGE
03870    int success = 
03871 #endif
03872    RETRIEVE(filename, -1, vmu->mailbox, vmu->context);
03873    if (ast_fileexists(filename, NULL, NULL) > 0) {
03874       res = ast_streamfile(chan, filename, chan->language);
03875       if (res > -1) 
03876          res = ast_waitstream(chan, ecodes);
03877 #ifdef ODBC_STORAGE
03878       if (success == -1) {
03879          /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
03880          ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
03881          store_file(filename, vmu->mailbox, vmu->context, -1);
03882       }
03883 #endif
03884    }
03885    DISPOSE(filename, -1);
03886 
03887    return res;
03888 }
03889 
03890 static int invent_message(struct ast_channel *chan, struct ast_vm_user *vmu, char *ext, int busy, char *ecodes)
03891 {
03892    int res;
03893    char fn[PATH_MAX];
03894    char dest[PATH_MAX];
03895 
03896    snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, ext);
03897 
03898    if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, ""))) {
03899       ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
03900       return -1;
03901    }
03902 
03903    res = play_greeting(chan, vmu, fn, ecodes);
03904    if (res == -2) {
03905       /* File did not exist */
03906       res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
03907       if (res)
03908          return res;
03909       res = ast_say_digit_str(chan, ext, ecodes, chan->language);
03910    }
03911    if (res)
03912       return res;
03913 
03914    res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
03915    return res;
03916 }
03917 
03918 static void free_zone(struct vm_zone *z)
03919 {
03920    ast_free(z);
03921 }
03922 
03923 #ifdef ODBC_STORAGE
03924 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
03925 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
03926 {
03927    int x = -1;
03928    int res;
03929    SQLHSTMT stmt;
03930    char sql[PATH_MAX];
03931    char rowdata[20];
03932    char tmp[PATH_MAX] = "";
03933    struct odbc_obj *obj;
03934    char *context;
03935    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
03936 
03937    if (newmsgs)
03938       *newmsgs = 0;
03939    if (oldmsgs)
03940       *oldmsgs = 0;
03941 
03942    /* If no mailbox, return immediately */
03943    if (ast_strlen_zero(mailbox))
03944       return 0;
03945 
03946    ast_copy_string(tmp, mailbox, sizeof(tmp));
03947    
03948    context = strchr(tmp, '@');
03949    if (context) {
03950       *context = '\0';
03951       context++;
03952    } else
03953       context = "default";
03954    
03955    obj = ast_odbc_request_obj(odbc_database, 0);
03956    if (obj) {
03957       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
03958       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03959       if (!stmt) {
03960          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03961          ast_odbc_release_obj(obj);
03962          goto yuck;
03963       }
03964       res = SQLFetch(stmt);
03965       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03966          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03967          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03968          ast_odbc_release_obj(obj);
03969          goto yuck;
03970       }
03971       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03972       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03973          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03974          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03975          ast_odbc_release_obj(obj);
03976          goto yuck;
03977       }
03978       *newmsgs = atoi(rowdata);
03979       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03980 
03981       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
03982       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
03983       if (!stmt) {
03984          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
03985          ast_odbc_release_obj(obj);
03986          goto yuck;
03987       }
03988       res = SQLFetch(stmt);
03989       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03990          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
03991          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03992          ast_odbc_release_obj(obj);
03993          goto yuck;
03994       }
03995       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
03996       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
03997          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
03998          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
03999          ast_odbc_release_obj(obj);
04000          goto yuck;
04001       }
04002       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04003       ast_odbc_release_obj(obj);
04004       *oldmsgs = atoi(rowdata);
04005       x = 0;
04006    } else
04007       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04008       
04009 yuck: 
04010    return x;
04011 }
04012 
04013 static int messagecount(const char *context, const char *mailbox, const char *folder)
04014 {
04015    struct odbc_obj *obj = NULL;
04016    int nummsgs = 0;
04017    int res;
04018    SQLHSTMT stmt = NULL;
04019    char sql[PATH_MAX];
04020    char rowdata[20];
04021    struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
04022    if (!folder)
04023       folder = "INBOX";
04024    /* If no mailbox, return immediately */
04025    if (ast_strlen_zero(mailbox))
04026       return 0;
04027 
04028    obj = ast_odbc_request_obj(odbc_database, 0);
04029    if (obj) {
04030       snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
04031       stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
04032       if (!stmt) {
04033          ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
04034          goto yuck;
04035       }
04036       res = SQLFetch(stmt);
04037       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04038          ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
04039          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04040          goto yuck;
04041       }
04042       res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
04043       if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
04044          ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
04045          SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04046          goto yuck;
04047       }
04048       nummsgs = atoi(rowdata);
04049       SQLFreeHandle (SQL_HANDLE_STMT, stmt);
04050    } else
04051       ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
04052 
04053 yuck:
04054    if (obj)
04055       ast_odbc_release_obj(obj);
04056    return nummsgs;
04057 }
04058 
04059 static int has_voicemail(const char *mailbox, const char *folder)
04060 {
04061    char tmp[256], *tmp2 = tmp, *mbox, *context;
04062    ast_copy_string(tmp, mailbox, sizeof(tmp));
04063    while ((context = mbox = strsep(&tmp2, ","))) {
04064       strsep(&context, "@");
04065       if (ast_strlen_zero(context))
04066          context = "default";
04067       if (messagecount(context, mbox, folder))
04068          return 1;
04069    }
04070    return 0;
04071 }
04072 
04073 #elif defined(IMAP_STORAGE)
04074 
04075 #endif
04076 #ifndef IMAP_STORAGE
04077    /* copy message only used by file storage */
04078    static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir)
04079    {
04080       char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
04081       const char *frombox = mbox(imbox);
04082       int recipmsgnum;
04083 
04084       ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
04085 
04086       create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04087       
04088       if (!dir)
04089          make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
04090       else
04091          ast_copy_string(fromdir, dir, sizeof(fromdir));
04092 
04093       make_file(frompath, sizeof(frompath), fromdir, msgnum);
04094       make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
04095 
04096       if (vm_lock_path(todir))
04097          return ERROR_LOCK_PATH;
04098 
04099       recipmsgnum = last_message_index(recip, todir) + 1;
04100       if (recipmsgnum < recip->maxmsg) {
04101          make_file(topath, sizeof(topath), todir, recipmsgnum);
04102          COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
04103       } else {
04104          ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
04105       }
04106       ast_unlock_path(todir);
04107       notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
04108       
04109       return 0;
04110    }
04111 #endif
04112 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
04113    static int messagecount(const char *context, const char *mailbox, const char *folder)
04114    {
04115       return __has_voicemail(context, mailbox, folder, 0);
04116    }
04117 
04118 
04119    static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
04120    {
04121       DIR *dir;
04122       struct dirent *de;
04123       char fn[256];
04124       int ret = 0;
04125 
04126       /* If no mailbox, return immediately */
04127       if (ast_strlen_zero(mailbox))
04128          return 0;
04129 
04130       if (ast_strlen_zero(folder))
04131          folder = "INBOX";
04132       if (ast_strlen_zero(context))
04133          context = "default";
04134 
04135       snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
04136 
04137       if (!(dir = opendir(fn)))
04138          return 0;
04139 
04140       while ((de = readdir(dir))) {
04141          if (!strncasecmp(de->d_name, "msg", 3)) {
04142             if (shortcircuit) {
04143                ret = 1;
04144                break;
04145             } else if (!strncasecmp(de->d_name + 8, "txt", 3))
04146                ret++;
04147          }
04148       }
04149 
04150       closedir(dir);
04151 
04152       return ret;
04153    }
04154 
04155 
04156    static int has_voicemail(const char *mailbox, const char *folder)
04157    {
04158       char tmp[256], *tmp2 = tmp, *mbox, *context;
04159       ast_copy_string(tmp, mailbox, sizeof(tmp));
04160       while ((mbox = strsep(&tmp2, ","))) {
04161          if ((context = strchr(mbox, '@')))
04162             *context++ = '\0';
04163          else
04164             context = "default";
04165          if (__has_voicemail(context, mbox, folder, 1))
04166             return 1;
04167       }
04168       return 0;
04169    }
04170 
04171 
04172    static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
04173    {
04174       char tmp[256];
04175       char *context;
04176 
04177       /* If no mailbox, return immediately */
04178       if (ast_strlen_zero(mailbox))
04179          return 0;
04180 
04181       if (newmsgs)
04182          *newmsgs = 0;
04183       if (oldmsgs)
04184          *oldmsgs = 0;
04185 
04186       if (strchr(mailbox, ',')) {
04187          int tmpnew, tmpold;
04188          char *mb, *cur;
04189 
04190          ast_copy_string(tmp, mailbox, sizeof(tmp));
04191          mb = tmp;
04192          while ((cur = strsep(&mb, ", "))) {
04193             if (!ast_strlen_zero(cur)) {
04194                if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
04195                   return -1;
04196                else {
04197                   if (newmsgs)
04198                      *newmsgs += tmpnew; 
04199                   if (oldmsgs)
04200                      *oldmsgs += tmpold;
04201                }
04202             }
04203          }
04204          return 0;
04205       }
04206 
04207       ast_copy_string(tmp, mailbox, sizeof(tmp));
04208       
04209       if ((context = strchr(tmp, '@')))
04210          *context++ = '\0';
04211       else
04212          context = "default";
04213 
04214       if (newmsgs)
04215          *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
04216       if (oldmsgs)
04217          *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
04218 
04219       return 0;
04220    }
04221 
04222 #endif
04223 
04224    static void run_externnotify(char *context, char *extension)
04225    {
04226       char arguments[255];
04227       char ext_context[256] = "";
04228       int newvoicemails = 0, oldvoicemails = 0;
04229       struct ast_smdi_mwi_message *mwi_msg;
04230 
04231       if (!ast_strlen_zero(context))
04232          snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
04233       else
04234          ast_copy_string(ext_context, extension, sizeof(ext_context));
04235 
04236       if (smdi_iface) {
04237          if (ast_app_has_voicemail(ext_context, NULL)) 
04238             ast_smdi_mwi_set(smdi_iface, extension);
04239          else
04240             ast_smdi_mwi_unset(smdi_iface, extension);
04241 
04242          if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
04243             ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
04244             if (!strncmp(mwi_msg->cause, "INV", 3))
04245                ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
04246             else if (!strncmp(mwi_msg->cause, "BLK", 3))
04247                ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
04248             ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
04249             ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
04250          } else {
04251             ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
04252          }
04253       }
04254 
04255       if (!ast_strlen_zero(externnotify)) {
04256          if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
04257             ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
04258          } else {
04259             snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
04260             ast_debug(1, "Executing %s\n", arguments);
04261             ast_safe_system(arguments);
04262          }
04263       }
04264    }
04265 
04266 struct leave_vm_options {
04267    unsigned int flags;
04268    signed char record_gain;
04269    char *exitcontext;
04270 };
04271 
04272 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
04273 {
04274 #ifdef IMAP_STORAGE
04275    int newmsgs, oldmsgs;
04276 #endif
04277    char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
04278    struct vm_state *vms = NULL;
04279    char callerid[256];
04280    FILE *txt;
04281    char date[256];
04282    int txtdes;
04283    int res = 0;
04284    int msgnum;
04285    int duration = 0;
04286    int ausemacro = 0;
04287    int ousemacro = 0;
04288    int ouseexten = 0;
04289    int rtmsgid = 0;
04290    char tmpid[16];
04291    char tmpdur[16];
04292    char priority[16];
04293    char origtime[16];
04294    char dir[PATH_MAX], tmpdir[PATH_MAX];
04295    char fn[PATH_MAX];
04296    char prefile[PATH_MAX] = "";
04297    char tempfile[PATH_MAX] = "";
04298    char ext_context[256] = "";
04299    char fmt[80];
04300    char *context;
04301    char ecodes[17] = "#";
04302    char *tmpptr;
04303    struct ast_str *tmp = ast_str_create(16);
04304    struct ast_vm_user *vmu;
04305    struct ast_vm_user svm;
04306    const char *category = NULL, *code, *alldtmf = "0123456789ABCD*#";
04307 
04308    ast_str_set(&tmp, 0, "%s", ext);
04309    ext = ast_str_buffer(tmp);
04310    if ((context = strchr(ext, '@'))) {
04311       *context++ = '\0';
04312       tmpptr = strchr(context, '&');
04313    } else {
04314       tmpptr = strchr(ext, '&');
04315    }
04316 
04317    if (tmpptr) {
04318       *tmpptr++ = '\0';
04319    }
04320 
04321    category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
04322 
04323    ast_debug(3, "Before find_user\n");
04324    if (!(vmu = find_user(&svm, context, ext))) {
04325       ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
04326       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04327       ast_free(tmp);
04328       return res;
04329    }
04330    /* Setup pre-file if appropriate */
04331    if (strcmp(vmu->context, "default")) {
04332       snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
04333    } else {
04334       ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
04335    }
04336    if (ast_test_flag(options, OPT_BUSY_GREETING)) {
04337       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
04338    } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
04339       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
04340    }
04341    snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
04342    if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
04343       ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
04344       ast_free(tmp);
04345       return -1;
04346    }
04347    RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
04348    if (ast_fileexists(tempfile, NULL, NULL) > 0) {
04349       ast_copy_string(prefile, tempfile, sizeof(prefile));
04350    }
04351    DISPOSE(tempfile, -1);
04352    /* It's easier just to try to make it than to check for its existence */
04353    create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
04354 
04355    /* Check current or macro-calling context for special extensions */
04356    if (ast_test_flag(vmu, VM_OPERATOR)) {
04357       if (!ast_strlen_zero(vmu->exit)) {
04358          if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
04359             strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04360             ouseexten = 1;
04361          }
04362       } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
04363          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04364          ouseexten = 1;
04365       } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
04366          strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
04367          ousemacro = 1;
04368       }
04369    }
04370 
04371    if (!ast_strlen_zero(vmu->exit)) {
04372       if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num)) {
04373          strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04374       }
04375    } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num)) {
04376       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04377    } else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
04378       strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
04379       ausemacro = 1;
04380    }
04381 
04382    if (ast_test_flag(options, OPT_DTMFEXIT)) {
04383       for (code = alldtmf; *code; code++) {
04384          char e[2] = "";
04385          e[0] = *code;
04386          if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num)) {
04387             strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
04388          }
04389       }
04390    }
04391 
04392    /* Play the beginning intro if desired */
04393    if (!ast_strlen_zero(prefile)) {
04394       res = play_greeting(chan, vmu, prefile, ecodes);
04395       if (res == -2) {
04396          /* The file did not exist */
04397          ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
04398          res = invent_message(chan, vmu, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
04399       }
04400       if (res < 0) {
04401          ast_debug(1, "Hang up during prefile playback\n");
04402          free_user(vmu);
04403          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04404          ast_free(tmp);
04405          return -1;
04406       }
04407    }
04408    if (res == '#') {
04409       /* On a '#' we skip the instructions */
04410       ast_set_flag(options, OPT_SILENT);
04411       res = 0;
04412    }
04413    if (!res && !ast_test_flag(options, OPT_SILENT)) {
04414       res = ast_stream_and_wait(chan, INTRO, ecodes);
04415       if (res == '#') {
04416          ast_set_flag(options, OPT_SILENT);
04417          res = 0;
04418       }
04419    }
04420    if (res > 0) {
04421       ast_stopstream(chan);
04422    }
04423    /* Check for a '*' here in case the caller wants to escape from voicemail to something
04424     * other than the operator -- an automated attendant or mailbox login for example */
04425    if (res == '*') {
04426       chan->exten[0] = 'a';
04427       chan->exten[1] = '\0';
04428       if (!ast_strlen_zero(vmu->exit)) {
04429          ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
04430       } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
04431          ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
04432       }
04433       chan->priority = 0;
04434       free_user(vmu);
04435       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
04436       ast_free(tmp);
04437       return 0;
04438    }
04439 
04440    /* Check for a '0' here */
04441    if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
04442 transfer:
04443       if (ouseexten || ousemacro) {
04444          chan->exten[0] = 'o';
04445          chan->exten[1] = '\0';
04446          if (!ast_strlen_zero(vmu->exit)) {
04447             ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
04448          } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
04449             ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
04450          }
04451          ast_play_and_wait(chan, "transfer");
04452          chan->priority = 0;
04453          free_user(vmu);
04454          pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
04455       }
04456       ast_free(tmp);
04457       return 0;
04458    }
04459 
04460    /* Allow all other digits to exit Voicemail and return to the dialplan */
04461    if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
04462       if (!ast_strlen_zero(options->exitcontext)) {
04463          ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
04464       }
04465       free_user(vmu);
04466       pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
04467       return res;
04468    }
04469 
04470    if (res < 0) {
04471       free_user(vmu);
04472       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04473       ast_free(tmp);
04474       return -1;
04475    }
04476    /* The meat of recording the message...  All the announcements and beeps have been played*/
04477    ast_copy_string(fmt, vmfmts, sizeof(fmt));
04478    if (!ast_strlen_zero(fmt)) {
04479       msgnum = 0;
04480 
04481 #ifdef IMAP_STORAGE
04482       /* Is ext a mailbox? */
04483       /* must open stream for this user to get info! */
04484       res = inboxcount(ext_context, &newmsgs, &oldmsgs);
04485       if (res < 0) {
04486          ast_log(LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
04487          ast_free(tmp);
04488          return -1;
04489       }
04490       if (!(vms = get_vm_state_by_mailbox(ext, 0))) {
04491          /* It is possible under certain circumstances that inboxcount did not
04492           * create a vm_state when it was needed. This is a catchall which will
04493           * rarely be used.
04494           */
04495          if (!(vms = create_vm_state_from_user(vmu))) {
04496             ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
04497             ast_free(tmp);
04498             return -1;
04499          }
04500       }
04501       vms->newmessages++;
04502 
04503       /* here is a big difference! We add one to it later */
04504       msgnum = newmsgs + oldmsgs;
04505       ast_debug(3, "Messagecount set to %d\n", msgnum);
04506       snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
04507       /* set variable for compatibility */
04508       pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
04509 
04510       /* Check if mailbox is full */
04511       check_quota(vms, imapfolder);
04512       if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
04513          ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
04514          ast_play_and_wait(chan, "vm-mailboxfull");
04515          ast_free(tmp);
04516          return -1;
04517       }
04518 
04519       /* Check if we have exceeded maxmsg */
04520       if (msgnum >= vmu->maxmsg) {
04521          ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
04522          ast_play_and_wait(chan, "vm-mailboxfull");
04523          ast_free(tmp);
04524          return -1;
04525       }
04526 #else
04527       if (count_messages(vmu, dir) >= vmu->maxmsg) {
04528          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
04529          if (!res) {
04530             res = ast_waitstream(chan, "");
04531          }
04532          ast_log(LOG_WARNING, "No more messages possible\n");
04533          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04534          goto leave_vm_out;
04535       }
04536 
04537 #endif
04538       snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
04539       txtdes = mkstemp(tmptxtfile);
04540       chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
04541       if (txtdes < 0) {
04542          res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
04543          if (!res) {
04544             res = ast_waitstream(chan, "");
04545          }
04546          ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
04547          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04548          goto leave_vm_out;
04549       }
04550 
04551       /* Now play the beep once we have the message number for our next message. */
04552       if (res >= 0) {
04553          /* Unless we're *really* silent, try to send the beep */
04554          res = ast_stream_and_wait(chan, "beep", "");
04555       }
04556 
04557       /* Store information in real-time storage */
04558       if (ast_check_realtime("voicemail_data")) {
04559          snprintf(priority, sizeof(priority), "%d", chan->priority);
04560          snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
04561          get_date(date, sizeof(date));
04562          rtmsgid = ast_store_realtime("voicemail_data", "origmailbox", ext, "context", chan->context, "macrocontext", chan->macrocontext, "exten", chan->exten, "priority", priority, "callerchan", chan->name, "callerid", ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"), "origdate", date, "origtime", origtime, "category", category ? category : "", NULL);
04563       }
04564 
04565       /* Store information */
04566       txt = fdopen(txtdes, "w+");
04567       if (txt) {
04568          get_date(date, sizeof(date));
04569          fprintf(txt, 
04570             ";\n"
04571             "; Message Information file\n"
04572             ";\n"
04573             "[message]\n"
04574             "origmailbox=%s\n"
04575             "context=%s\n"
04576             "macrocontext=%s\n"
04577             "exten=%s\n"
04578             "priority=%d\n"
04579             "callerchan=%s\n"
04580             "callerid=%s\n"
04581             "origdate=%s\n"
04582             "origtime=%ld\n"
04583             "category=%s\n",
04584             ext,
04585             chan->context,
04586             chan->macrocontext, 
04587             chan->exten,
04588             chan->priority,
04589             chan->name,
04590             ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
04591             date, (long)time(NULL),
04592             category ? category : "");
04593       } else {
04594          ast_log(LOG_WARNING, "Error opening text file for output\n");
04595       }
04596       res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
04597 
04598       if (txt) {
04599          if (duration < vmminsecs) {
04600             fclose(txt);
04601             ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
04602             ast_filedelete(tmptxtfile, NULL);
04603             unlink(tmptxtfile);
04604             if (ast_check_realtime("voicemail_data")) {
04605                snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
04606                ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
04607             }
04608          } else {
04609             fprintf(txt, "duration=%d\n", duration);
04610             fclose(txt);
04611             if (vm_lock_path(dir)) {
04612                ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
04613                /* Delete files */
04614                ast_filedelete(tmptxtfile, NULL);
04615                unlink(tmptxtfile);
04616             } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
04617                ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
04618                unlink(tmptxtfile);
04619                ast_unlock_path(dir);
04620                if (ast_check_realtime("voicemail_data")) {
04621                   snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
04622                   ast_destroy_realtime("voicemail_data", "id", tmpid, NULL);
04623                }
04624             } else {
04625 #ifndef IMAP_STORAGE
04626                msgnum = last_message_index(vmu, dir) + 1;
04627 #endif
04628                make_file(fn, sizeof(fn), dir, msgnum);
04629 
04630                /* assign a variable with the name of the voicemail file */ 
04631 #ifndef IMAP_STORAGE
04632                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
04633 #else
04634                pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
04635 #endif
04636 
04637                snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
04638                ast_filerename(tmptxtfile, fn, NULL);
04639                rename(tmptxtfile, txtfile);
04640 
04641                /* Properly set permissions on voicemail text descriptor file.
04642                Unfortunately mkstemp() makes this file 0600 on most unix systems. */
04643                if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0) {
04644                   ast_log(LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
04645                }
04646 
04647                ast_unlock_path(dir);
04648                if (ast_check_realtime("voicemail_data")) {
04649                   snprintf(tmpid, sizeof(tmpid), "%d", rtmsgid);
04650                   snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
04651                   ast_update_realtime("voicemail_data", "id", tmpid, "filename", fn, "duration", tmpdur, NULL);
04652                }
04653                /* We must store the file first, before copying the message, because
04654                 * ODBC storage does the entire copy with SQL.
04655                 */
04656                if (ast_fileexists(fn, NULL, NULL) > 0) {
04657                   STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
04658                }
04659 
04660                /* Are there to be more recipients of this message? */
04661                while (tmpptr) {
04662                   struct ast_vm_user recipu, *recip;
04663                   char *exten, *context;
04664 
04665                   exten = strsep(&tmpptr, "&");
04666                   context = strchr(exten, '@');
04667                   if (context) {
04668                      *context = '\0';
04669                      context++;
04670                   }
04671                   if ((recip = find_user(&recipu, context, exten))) {
04672                      copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
04673                      free_user(recip);
04674                   }
04675                }
04676                /* Notification and disposal needs to happen after the copy, though. */
04677                if (ast_fileexists(fn, NULL, NULL)) {
04678 #ifdef IMAP_STORAGE
04679                   notify_new_message(chan, vmu, vms, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
04680 #else
04681                   notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
04682 #endif
04683                   DISPOSE(dir, msgnum);
04684                }
04685             }
04686          }
04687       }
04688       if (res == '0') {
04689          goto transfer;
04690       } else if (res > 0) {
04691          res = 0;
04692       }
04693 
04694       if (duration < vmminsecs) {
04695          /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
04696          pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
04697       } else {
04698          pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
04699       }
04700    } else {
04701       ast_log(LOG_WARNING, "No format for saving voicemail?\n");
04702    }
04703 leave_vm_out:
04704    free_user(vmu);
04705 
04706    ast_free(tmp);
04707    return res;
04708 }
04709 
04710 #ifndef IMAP_STORAGE
04711    static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
04712    {
04713       /* we know max messages, so stop process when number is hit */
04714 
04715       int x, dest;
04716       char sfn[PATH_MAX];
04717       char dfn[PATH_MAX];
04718 
04719       if (vm_lock_path(dir))
04720          return ERROR_LOCK_PATH;
04721 
04722       for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
04723          make_file(sfn, sizeof(sfn), dir, x);
04724          if (EXISTS(dir, x, sfn, NULL)) {
04725             
04726             if (x != dest) {
04727                make_file(dfn, sizeof(dfn), dir, dest);
04728                RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
04729             }
04730             
04731             dest++;
04732          }
04733       }
04734       ast_unlock_path(dir);
04735 
04736       return 0;
04737    }
04738 #endif
04739 
04740    static int say_and_wait(struct ast_channel *chan, int num, const char *language)
04741    {
04742       int d;
04743       d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
04744       return d;
04745    }
04746 
04747    static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
04748    {
04749 #ifdef IMAP_STORAGE
04750       /* we must use mbox(x) folder names, and copy the message there */
04751       /* simple. huh? */
04752       char sequence[10];
04753       char mailbox[256];
04754       int res;
04755 
04756       /* get the real IMAP message number for this message */
04757       snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
04758       
04759       ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
04760       ast_mutex_lock(&vms->lock);
04761 
04762       if (box == OLD_FOLDER) {
04763          mail_setflag(vms->mailstream, sequence, "\\Seen");
04764          mail_clearflag(vms->mailstream, sequence, "\\Unseen");
04765       } else if (box == NEW_FOLDER) {
04766          mail_clearflag(vms->mailstream, sequence, "\\Seen");
04767          mail_setflag(vms->mailstream, sequence, "\\Unseen");
04768       }
04769       if (!strcasecmp(mbox(NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
04770          ast_mutex_unlock(&vms->lock);
04771          return 0;
04772       }
04773       /* Create the folder if it don't exist */
04774       imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
04775       ast_debug(5, "Checking if folder exists: %s\n",mailbox);
04776       if (mail_create(vms->mailstream, mailbox) == NIL) 
04777          ast_debug(5, "Folder exists.\n");
04778       else
04779          ast_log(LOG_NOTICE, "Folder %s created!\n",mbox(box));
04780       res = !mail_copy(vms->mailstream, sequence, (char *)mbox(box));
04781       ast_mutex_unlock(&vms->lock);
04782       return res;
04783 #else
04784       char *dir = vms->curdir;
04785       char *username = vms->username;
04786       char *context = vmu->context;
04787       char sfn[PATH_MAX];
04788       char dfn[PATH_MAX];
04789       char ddir[PATH_MAX];
04790       const char *dbox = mbox(box);
04791       int x, i;
04792       create_dirpath(ddir, sizeof(ddir), context, username, dbox);
04793 
04794       if (vm_lock_path(ddir))
04795          return ERROR_LOCK_PATH;
04796 
04797       x = last_message_index(vmu, ddir) + 1;
04798 
04799       if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
04800          x--;
04801          for (i = 1; i <= x; i++) {
04802             /* Push files down a "slot".  The oldest file (msg0000) will be deleted. */
04803             make_file(sfn, sizeof(sfn), ddir, i);
04804             make_file(dfn, sizeof(dfn), ddir, i - 1);
04805             if (EXISTS(ddir, i, sfn, NULL)) {
04806                RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
04807             } else
04808                break;
04809          }
04810       } else {
04811          if (x >= vmu->maxmsg) {
04812             ast_unlock_path(ddir);
04813             return ERROR_MAILBOX_FULL;
04814          }
04815       }
04816       make_file(sfn, sizeof(sfn), dir, msg);
04817       make_file(dfn, sizeof(dfn), ddir, x);
04818       if (strcmp(sfn, dfn)) {
04819          COPY(dir, msg, ddir, x, username, context, sfn, dfn);
04820       }
04821       ast_unlock_path(ddir);
04822 #endif
04823       return 0;
04824    }
04825 
04826    static int adsi_logo(unsigned char *buf)
04827    {
04828       int bytes = 0;
04829       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
04830       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
04831       return bytes;
04832    }
04833 
04834    static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
04835    {
04836       unsigned char buf[256];
04837       int bytes = 0;
04838       int x;
04839       char num[5];
04840 
04841       *useadsi = 0;
04842       bytes += ast_adsi_data_mode(buf + bytes);
04843       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04844 
04845       bytes = 0;
04846       bytes += adsi_logo(buf);
04847       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
04848 #ifdef DISPLAY
04849       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
04850 #endif
04851       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04852       bytes += ast_adsi_data_mode(buf + bytes);
04853       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04854 
04855       if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
04856          bytes = 0;
04857          bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
04858          bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
04859          bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04860          bytes += ast_adsi_voice_mode(buf + bytes, 0);
04861          ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04862          return 0;
04863       }
04864 
04865 #ifdef DISPLAY
04866       /* Add a dot */
04867       bytes = 0;
04868       bytes += ast_adsi_logo(buf);
04869       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
04870       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
04871       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04872       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04873 #endif
04874       bytes = 0;
04875       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
04876       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
04877       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
04878       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
04879       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
04880       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
04881       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04882 
04883 #ifdef DISPLAY
04884       /* Add another dot */
04885       bytes = 0;
04886       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
04887       bytes += ast_adsi_voice_mode(buf + bytes, 0);
04888 
04889       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04890       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04891 #endif
04892 
04893       bytes = 0;
04894       /* These buttons we load but don't use yet */
04895       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
04896       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
04897       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
04898       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
04899       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
04900       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
04901       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04902 
04903 #ifdef DISPLAY
04904       /* Add another dot */
04905       bytes = 0;
04906       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
04907       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04908       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04909 #endif
04910 
04911       bytes = 0;
04912       for (x = 0; x < 5; x++) {
04913          snprintf(num, sizeof(num), "%d", x);
04914          bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
04915       }
04916       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
04917       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04918 
04919 #ifdef DISPLAY
04920       /* Add another dot */
04921       bytes = 0;
04922       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
04923       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04924       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04925 #endif
04926 
04927       if (ast_adsi_end_download(chan)) {
04928          bytes = 0;
04929          bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
04930          bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
04931          bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04932          bytes += ast_adsi_voice_mode(buf + bytes, 0);
04933          ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04934          return 0;
04935       }
04936       bytes = 0;
04937       bytes += ast_adsi_download_disconnect(buf + bytes);
04938       bytes += ast_adsi_voice_mode(buf + bytes, 0);
04939       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
04940 
04941       ast_debug(1, "Done downloading scripts...\n");
04942 
04943 #ifdef DISPLAY
04944       /* Add last dot */
04945       bytes = 0;
04946       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
04947       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04948 #endif
04949       ast_debug(1, "Restarting session...\n");
04950 
04951       bytes = 0;
04952       /* Load the session now */
04953       if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
04954          *useadsi = 1;
04955          bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
04956       } else
04957          bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
04958 
04959       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
04960       return 0;
04961    }
04962 
04963    static void adsi_begin(struct ast_channel *chan, int *useadsi)
04964    {
04965       int x;
04966       if (!ast_adsi_available(chan))
04967          return;
04968       x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
04969       if (x < 0)
04970          return;
04971       if (!x) {
04972          if (adsi_load_vmail(chan, useadsi)) {
04973             ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
04974             return;
04975          }
04976       } else
04977          *useadsi = 1;
04978    }
04979 
04980    static void adsi_login(struct ast_channel *chan)
04981    {
04982       unsigned char buf[256];
04983       int bytes = 0;
04984       unsigned char keys[8];
04985       int x;
04986       if (!ast_adsi_available(chan))
04987          return;
04988 
04989       for (x = 0; x < 8; x++)
04990          keys[x] = 0;
04991       /* Set one key for next */
04992       keys[3] = ADSI_KEY_APPS + 3;
04993 
04994       bytes += adsi_logo(buf + bytes);
04995       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
04996       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
04997       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
04998       bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
04999       bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
05000       bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
05001       bytes += ast_adsi_set_keys(buf + bytes, keys);
05002       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05003       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05004    }
05005 
05006    static void adsi_password(struct ast_channel *chan)
05007    {
05008       unsigned char buf[256];
05009       int bytes = 0;
05010       unsigned char keys[8];
05011       int x;
05012       if (!ast_adsi_available(chan))
05013          return;
05014 
05015       for (x = 0; x < 8; x++)
05016          keys[x] = 0;
05017       /* Set one key for next */
05018       keys[3] = ADSI_KEY_APPS + 3;
05019 
05020       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05021       bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
05022       bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
05023       bytes += ast_adsi_set_keys(buf + bytes, keys);
05024       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05025       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05026    }
05027 
05028    static void adsi_folders(struct ast_channel *chan, int start, char *label)
05029    {
05030       unsigned char buf[256];
05031       int bytes = 0;
05032       unsigned char keys[8];
05033       int x, y;
05034 
05035       if (!ast_adsi_available(chan))
05036          return;
05037 
05038       for (x = 0; x < 5; x++) {
05039          y = ADSI_KEY_APPS + 12 + start + x;
05040          if (y > ADSI_KEY_APPS + 12 + 4)
05041             y = 0;
05042          keys[x] = ADSI_KEY_SKT | y;
05043       }
05044       keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
05045       keys[6] = 0;
05046       keys[7] = 0;
05047 
05048       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
05049       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
05050       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05051       bytes += ast_adsi_set_keys(buf + bytes, keys);
05052       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05053 
05054       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05055    }
05056 
05057    static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
05058    {
05059       int bytes = 0;
05060       unsigned char buf[256]; 
05061       char buf1[256], buf2[256];
05062       char fn2[PATH_MAX];
05063 
05064       char cid[256] = "";
05065       char *val;
05066       char *name, *num;
05067       char datetime[21] = "";
05068       FILE *f;
05069 
05070       unsigned char keys[8];
05071 
05072       int x;
05073 
05074       if (!ast_adsi_available(chan))
05075          return;
05076 
05077       /* Retrieve important info */
05078       snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
05079       f = fopen(fn2, "r");
05080       if (f) {
05081          while (!feof(f)) {   
05082             if (!fgets((char *)buf, sizeof(buf), f)) {
05083                continue;
05084             }
05085             if (!feof(f)) {
05086                char *stringp = NULL;
05087                stringp = (char *)buf;
05088                strsep(&stringp, "=");
05089                val = strsep(&stringp, "=");
05090                if (!ast_strlen_zero(val)) {
05091                   if (!strcmp((char *)buf, "callerid"))
05092                      ast_copy_string(cid, val, sizeof(cid));
05093                   if (!strcmp((char *)buf, "origdate"))
05094                      ast_copy_string(datetime, val, sizeof(datetime));
05095                }
05096             }
05097          }
05098          fclose(f);
05099       }
05100       /* New meaning for keys */
05101       for (x = 0; x < 5; x++)
05102          keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05103       keys[6] = 0x0;
05104       keys[7] = 0x0;
05105 
05106       if (!vms->curmsg) {
05107          /* No prev key, provide "Folder" instead */
05108          keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05109       }
05110       if (vms->curmsg >= vms->lastmsg) {
05111          /* If last message ... */
05112          if (vms->curmsg) {
05113             /* but not only message, provide "Folder" instead */
05114             keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05115             bytes += ast_adsi_voice_mode(buf + bytes, 0);
05116 
05117          } else {
05118             /* Otherwise if only message, leave blank */
05119             keys[3] = 1;
05120          }
05121       }
05122 
05123       if (!ast_strlen_zero(cid)) {
05124          ast_callerid_parse(cid, &name, &num);
05125          if (!name)
05126             name = num;
05127       } else
05128          name = "Unknown Caller";
05129 
05130       /* If deleted, show "undeleted" */
05131 
05132       if (vms->deleted[vms->curmsg])
05133          keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05134 
05135       /* Except "Exit" */
05136       keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05137       snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
05138          strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
05139       snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
05140 
05141       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05142       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05143       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
05144       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
05145       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05146       bytes += ast_adsi_set_keys(buf + bytes, keys);
05147       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05148 
05149       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05150    }
05151 
05152    static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
05153    {
05154       int bytes = 0;
05155       unsigned char buf[256];
05156       unsigned char keys[8];
05157 
05158       int x;
05159 
05160       if (!ast_adsi_available(chan))
05161          return;
05162 
05163       /* New meaning for keys */
05164       for (x = 0; x < 5; x++)
05165          keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
05166 
05167       keys[6] = 0x0;
05168       keys[7] = 0x0;
05169 
05170       if (!vms->curmsg) {
05171          /* No prev key, provide "Folder" instead */
05172          keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05173       }
05174       if (vms->curmsg >= vms->lastmsg) {
05175          /* If last message ... */
05176          if (vms->curmsg) {
05177             /* but not only message, provide "Folder" instead */
05178             keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
05179          } else {
05180             /* Otherwise if only message, leave blank */
05181             keys[3] = 1;
05182          }
05183       }
05184 
05185       /* If deleted, show "undeleted" */
05186       if (vms->deleted[vms->curmsg]) 
05187          keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
05188 
05189       /* Except "Exit" */
05190       keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
05191       bytes += ast_adsi_set_keys(buf + bytes, keys);
05192       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05193 
05194       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05195    }
05196 
05197    static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
05198    {
05199       unsigned char buf[256] = "";
05200       char buf1[256] = "", buf2[256] = "";
05201       int bytes = 0;
05202       unsigned char keys[8];
05203       int x;
05204 
05205       char *newm = (vms->newmessages == 1) ? "message" : "messages";
05206       char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
05207       if (!ast_adsi_available(chan))
05208          return;
05209       if (vms->newmessages) {
05210          snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
05211          if (vms->oldmessages) {
05212             strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
05213             snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
05214          } else {
05215             snprintf(buf2, sizeof(buf2), "%s.", newm);
05216          }
05217       } else if (vms->oldmessages) {
05218          snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
05219          snprintf(buf2, sizeof(buf2), "%s.", oldm);
05220       } else {
05221          strcpy(buf1, "You have no messages.");
05222          buf2[0] = ' ';
05223          buf2[1] = '\0';
05224       }
05225       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05226       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05227       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05228 
05229       for (x = 0; x < 6; x++)
05230          keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05231       keys[6] = 0;
05232       keys[7] = 0;
05233 
05234       /* Don't let them listen if there are none */
05235       if (vms->lastmsg < 0)
05236          keys[0] = 1;
05237       bytes += ast_adsi_set_keys(buf + bytes, keys);
05238 
05239       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05240 
05241       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05242    }
05243 
05244    static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
05245    {
05246       unsigned char buf[256] = "";
05247       char buf1[256] = "", buf2[256] = "";
05248       int bytes = 0;
05249       unsigned char keys[8];
05250       int x;
05251 
05252       char *mess = (vms->lastmsg == 0) ? "message" : "messages";
05253 
05254       if (!ast_adsi_available(chan))
05255          return;
05256 
05257       /* Original command keys */
05258       for (x = 0; x < 6; x++)
05259          keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
05260 
05261       keys[6] = 0;
05262       keys[7] = 0;
05263 
05264       if ((vms->lastmsg + 1) < 1)
05265          keys[0] = 0;
05266 
05267       snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
05268          strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
05269 
05270       if (vms->lastmsg + 1)
05271          snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
05272       else
05273          strcpy(buf2, "no messages.");
05274       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
05275       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
05276       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
05277       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05278       bytes += ast_adsi_set_keys(buf + bytes, keys);
05279 
05280       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05281 
05282       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05283       
05284    }
05285 
05286    /*
05287    static void adsi_clear(struct ast_channel *chan)
05288    {
05289       char buf[256];
05290       int bytes = 0;
05291       if (!ast_adsi_available(chan))
05292          return;
05293       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05294       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05295 
05296       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05297    }
05298    */
05299 
05300    static void adsi_goodbye(struct ast_channel *chan)
05301    {
05302       unsigned char buf[256];
05303       int bytes = 0;
05304 
05305       if (!ast_adsi_available(chan))
05306          return;
05307       bytes += adsi_logo(buf + bytes);
05308       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
05309       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
05310       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
05311       bytes += ast_adsi_voice_mode(buf + bytes, 0);
05312 
05313       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
05314    }
05315 
05316    /*!\brief get_folder: Folder menu
05317    *  Plays "press 1 for INBOX messages" etc.
05318    *  Should possibly be internationalized
05319    */
05320    static int get_folder(struct ast_channel *chan, int start)
05321    {
05322       int x;
05323       int d;
05324       char fn[PATH_MAX];
05325       d = ast_play_and_wait(chan, "vm-press");  /* "Press" */
05326       if (d)
05327          return d;
05328       for (x = start; x < 5; x++) { /* For all folders */
05329          if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
05330             return d;
05331          d = ast_play_and_wait(chan, "vm-for"); /* "for" */
05332          if (d)
05333             return d;
05334          snprintf(fn, sizeof(fn), "vm-%s", mbox(x));  /* Folder name */
05335          d = vm_play_folder_name(chan, fn);
05336          if (d)
05337             return d;
05338          d = ast_waitfordigit(chan, 500);
05339          if (d)
05340             return d;
05341       }
05342       d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
05343       if (d)
05344          return d;
05345       d = ast_waitfordigit(chan, 4000);
05346       return d;
05347    }
05348 
05349    static int get_folder2(struct ast_channel *chan, char *fn, int start)
05350    {
05351       int res = 0;
05352       res = ast_play_and_wait(chan, fn);  /* Folder name */
05353       while (((res < '0') || (res > '9')) &&
05354             (res != '#') && (res >= 0)) {
05355          res = get_folder(chan, 0);
05356       }
05357       return res;
05358    }
05359 
05360    static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
05361             char *context, signed char record_gain, long *duration, struct vm_state *vms)
05362    {
05363       int cmd = 0;
05364       int retries = 0, prepend_duration = 0, already_recorded = 0;
05365       signed char zero_gain = 0;
05366       struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
05367       struct ast_config *msg_cfg;
05368       const char *duration_str;
05369       char msgfile[PATH_MAX], backup[PATH_MAX];
05370       char textfile[PATH_MAX];
05371 
05372       /* Must always populate duration correctly */
05373       make_file(msgfile, sizeof(msgfile), curdir, curmsg);
05374       strcpy(textfile, msgfile);
05375       strcpy(backup, msgfile);
05376       strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
05377       strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
05378 
05379       if ((msg_cfg = ast_config_load(textfile, config_flags)) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
05380          *duration = atoi(duration_str);
05381       } else {
05382          *duration = 0;
05383       }
05384 
05385       while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
05386          if (cmd)
05387             retries = 0;
05388          switch (cmd) {
05389          case '1': 
05390             /* prepend a message to the current message, update the metadata and return */
05391             prepend_duration = 0;
05392 
05393             /* if we can't read the message metadata, stop now */
05394             if (!msg_cfg) {
05395                cmd = 0;
05396                break;
05397             }
05398 
05399             /* Back up the original file, so we can retry the prepend */
05400             if (already_recorded)
05401                ast_filecopy(backup, msgfile, NULL);
05402             else
05403                ast_filecopy(msgfile, backup, NULL);
05404             already_recorded = 1;
05405 
05406             if (record_gain)
05407                ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
05408 
05409             cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
05410             if (record_gain)
05411                ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
05412 
05413             if (prepend_duration) {
05414                struct ast_category *msg_cat;
05415                /* need enough space for a maximum-length message duration */
05416                char duration_str[12];
05417 
05418                prepend_duration += *duration;
05419                msg_cat = ast_category_get(msg_cfg, "message");
05420                snprintf(duration_str, sizeof(duration_str), "%d", prepend_duration);
05421                if (!ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
05422                   config_text_file_save(textfile, msg_cfg, "app_voicemail");
05423                   STORE(curdir, vmu->mailbox, context, curmsg, chan, vmu, vmfmts, prepend_duration, vms);
05424                }
05425             }
05426 
05427             break;
05428          case '2': 
05429             cmd = 't';
05430             break;
05431          case '*':
05432             cmd = '*';
05433             break;
05434          default: 
05435             cmd = ast_play_and_wait(chan, "vm-forwardoptions");
05436                /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
05437             if (!cmd)
05438                cmd = ast_play_and_wait(chan, "vm-starmain");
05439                /* "press star to return to the main menu" */
05440             if (!cmd)
05441                cmd = ast_waitfordigit(chan, 6000);
05442             if (!cmd)
05443                retries++;
05444             if (retries > 3)
05445                cmd = 't';
05446          }
05447       }
05448 
05449       if (msg_cfg)
05450          ast_config_destroy(msg_cfg);
05451       if (already_recorded)
05452          ast_filedelete(backup, NULL);
05453       if (prepend_duration)
05454          *duration = prepend_duration;
05455 
05456       if (cmd == 't' || cmd == 'S')
05457          cmd = 0;
05458       return cmd;
05459    }
05460 
05461    static void queue_mwi_event(const char *mbox, int new, int old)
05462    {
05463       struct ast_event *event;
05464       char *mailbox, *context;
05465 
05466       /* Strip off @default */
05467       context = mailbox = ast_strdupa(mbox);
05468       strsep(&context, "@");
05469       if (ast_strlen_zero(context))
05470          context = "default";
05471 
05472       if (!(event = ast_event_new(AST_EVENT_MWI,
05473             AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
05474             AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
05475             AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, new,
05476             AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
05477             AST_EVENT_IE_END))) {
05478          return;
05479       }
05480 
05481       ast_event_queue_and_cache(event,
05482          AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR,
05483          AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR,
05484          AST_EVENT_IE_END);
05485    }
05486 
05487    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)
05488    {
05489       char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
05490       int newmsgs = 0, oldmsgs = 0;
05491       const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
05492       char *myserveremail = serveremail;
05493 
05494       make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
05495       make_file(fn, sizeof(fn), todir, msgnum);
05496       snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
05497 
05498       if (!ast_strlen_zero(vmu->attachfmt)) {
05499          if (strstr(fmt, vmu->attachfmt))
05500             fmt = vmu->attachfmt;
05501          else
05502             ast_log(LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'.  Falling back to default format for '%s@%s'.\n", vmu->attachfmt, fmt, vmu->mailbox, vmu->context);
05503       }
05504 
05505       /* Attach only the first format */
05506       fmt = ast_strdupa(fmt);
05507       stringp = fmt;
05508       strsep(&stringp, "|");
05509 
05510       if (!ast_strlen_zero(vmu->serveremail))
05511          myserveremail = vmu->serveremail;
05512 
05513       if (!ast_strlen_zero(vmu->email)) {
05514          int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
05515          if (!attach_user_voicemail)
05516             attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
05517 
05518          if (attach_user_voicemail)
05519             RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
05520 
05521          /* XXX possible imap issue, should category be NULL XXX */
05522          sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
05523 
05524          if (attach_user_voicemail)
05525             DISPOSE(todir, msgnum);
05526       }
05527 
05528       if (!ast_strlen_zero(vmu->pager)) {
05529          sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category);
05530       }
05531 
05532       if (ast_test_flag(vmu, VM_DELETE))
05533          DELETE(todir, msgnum, fn, vmu);
05534 
05535       /* Leave voicemail for someone */
05536       if (ast_app_has_voicemail(ext_context, NULL)) 
05537          ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
05538 
05539       queue_mwi_event(ext_context, newmsgs, oldmsgs);
05540 
05541       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
05542       run_externnotify(vmu->context, vmu->mailbox);
05543 
05544       return 0;
05545    }
05546 
05547    static int forward_message(struct ast_channel *chan, char *context, struct vm_state *vms, struct ast_vm_user *sender, char *fmt, int flag, signed char record_gain)
05548    {
05549 #ifdef IMAP_STORAGE
05550       int todircount = 0;
05551       struct vm_state *dstvms;
05552 #endif
05553       char username[70] = "";
05554       char fn[PATH_MAX]; /* for playback of name greeting */
05555       char ecodes[16] = "#";
05556       int res = 0, cmd = 0;
05557       struct ast_vm_user *receiver = NULL, *vmtmp;
05558       AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
05559       char *stringp;
05560       const char *s;
05561       int saved_messages = 0, found = 0;
05562       int valid_extensions = 0;
05563       char *dir;
05564       int curmsg;
05565 
05566       if (vms == NULL) return -1;
05567       dir = vms->curdir;
05568       curmsg = vms->curmsg;
05569       
05570       while (!res && !valid_extensions) {
05571          int use_directory = 0;
05572          if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
05573             int done = 0;
05574             int retries = 0;
05575             cmd = 0;
05576             while ((cmd >= 0) && !done) {
05577                if (cmd)
05578                   retries = 0;
05579                switch (cmd) {
05580                case '1': 
05581                   use_directory = 0;
05582                   done = 1;
05583                   break;
05584                case '2': 
05585                   use_directory = 1;
05586                   done = 1;
05587                   break;
05588                case '*': 
05589                   cmd = 't';
05590                   done = 1;
05591                   break;
05592                default: 
05593                   /* Press 1 to enter an extension press 2 to use the directory */
05594                   cmd = ast_play_and_wait(chan, "vm-forward");
05595                   if (!cmd)
05596                      cmd = ast_waitfordigit(chan, 3000);
05597                   if (!cmd)
05598                      retries++;
05599                   if (retries > 3) {
05600                      cmd = 't';
05601                      done = 1;
05602                   }
05603                   
05604                }
05605             }
05606             if (cmd < 0 || cmd == 't')
05607                break;
05608          }
05609          
05610          if (use_directory) {
05611             /* use app_directory */
05612             
05613             char old_context[sizeof(chan->context)];
05614             char old_exten[sizeof(chan->exten)];
05615             int old_priority;
05616             struct ast_app* app;
05617 
05618             app = pbx_findapp("Directory");
05619             if (app) {
05620                char vmcontext[256];
05621                /* make backup copies */
05622                memcpy(old_context, chan->context, sizeof(chan->context));
05623                memcpy(old_exten, chan->exten, sizeof(chan->exten));
05624                old_priority = chan->priority;
05625                
05626                /* call the the Directory, changes the channel */
05627                snprintf(vmcontext, sizeof(vmcontext), "%s||v", context ? context : "default");
05628                res = pbx_exec(chan, app, vmcontext);
05629                
05630                ast_copy_string(username, chan->exten, sizeof(username));
05631                
05632                /* restore the old context, exten, and priority */
05633                memcpy(chan->context, old_context, sizeof(chan->context));
05634                memcpy(chan->exten, old_exten, sizeof(chan->exten));
05635                chan->priority = old_priority;
05636                
05637             } else {
05638                ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
05639                ast_clear_flag((&globalflags), VM_DIRECFORWARD);
05640             }
05641          } else {
05642             /* Ask for an extension */
05643             res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
05644             if (res)
05645                break;
05646             if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
05647                break;
05648          }
05649          
05650          /* start all over if no username */
05651          if (ast_strlen_zero(username))
05652             continue;
05653          stringp = username;
05654          s = strsep(&stringp, "*");
05655          /* start optimistic */
05656          valid_extensions = 1;
05657          while (s) {
05658             /* Don't forward to ourselves but allow leaving a message for ourselves (flag == 1).  find_user is going to malloc since we have a NULL as first argument */
05659             if ((flag == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
05660                AST_LIST_INSERT_HEAD(&extensions, receiver, list);
05661                found++;
05662             } else {
05663                while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
05664                   free_user(receiver);
05665                }
05666                ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
05667                valid_extensions = 0;
05668                break;
05669             }
05670 
05671             /* play name if available, else play extension number */
05672             snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
05673             RETRIEVE(fn, -1, s, receiver->context);
05674             if (ast_fileexists(fn, NULL, NULL) > 0) {
05675                res = ast_stream_and_wait(chan, fn, ecodes);
05676                if (res) {
05677                   DISPOSE(fn, -1);
05678                   return res;
05679                }
05680             } else {
05681                res = ast_say_digit_str(chan, s, ecodes, chan->language);
05682             }
05683             DISPOSE(fn, -1);
05684 
05685             s = strsep(&stringp, "*");
05686          }
05687          /* break from the loop of reading the extensions */
05688          if (valid_extensions)
05689             break;
05690          /* "I am sorry, that's not a valid extension.  Please try again." */
05691          res = ast_play_and_wait(chan, "pbx-invalid");
05692       }
05693       /* check if we're clear to proceed */
05694       if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
05695          return res;
05696       if (flag==1) {
05697          struct leave_vm_options leave_options;
05698          char mailbox[AST_MAX_EXTENSION * 2 + 2];
05699          /* Make sure that context doesn't get set as a literal "(null)" (or else find_user won't find it) */
05700          if (context)
05701             snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
05702          else
05703             ast_copy_string(mailbox, username, sizeof(mailbox));
05704 
05705          /* Send VoiceMail */
05706          memset(&leave_options, 0, sizeof(leave_options));
05707          leave_options.record_gain = record_gain;
05708          cmd = leave_voicemail(chan, mailbox, &leave_options);
05709       } else {
05710          /* Forward VoiceMail */
05711          long duration = 0;
05712          char origmsgfile[PATH_MAX], msgfile[PATH_MAX];
05713          struct vm_state vmstmp;
05714          memcpy(&vmstmp, vms, sizeof(vmstmp));
05715 
05716          make_file(origmsgfile, sizeof(origmsgfile), dir, curmsg);
05717          create_dirpath(vmstmp.curdir, sizeof(vmstmp.curdir), sender->context, vmstmp.username, "tmp");
05718          make_file(msgfile, sizeof(msgfile), vmstmp.curdir, curmsg);
05719          
05720          RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
05721          
05722          /* Alter a surrogate file, only */
05723          copy_plain_file(origmsgfile, msgfile);
05724 
05725          cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp);
05726          if (!cmd) {
05727             AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
05728 #ifdef IMAP_STORAGE
05729                char *myserveremail = serveremail;
05730                int attach_user_voicemail;
05731                /* get destination mailbox */
05732                dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, 0);
05733                if (!dstvms) {
05734                   dstvms = create_vm_state_from_user(vmtmp);
05735                }
05736                if (dstvms) {
05737                   init_mailstream(dstvms, 0);
05738                   if (!dstvms->mailstream) {
05739                      ast_log(LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
05740                   } else {
05741                      STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
05742                      run_externnotify(vmtmp->context, vmtmp->mailbox); 
05743                   }
05744                } else {
05745                   ast_log(LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
05746                }
05747                if (!ast_strlen_zero(vmtmp->serveremail))
05748                   myserveremail = vmtmp->serveremail;
05749                attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
05750                /* NULL category for IMAP storage */
05751                sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, dstvms->curbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), vms->fn, fmt, duration, attach_user_voicemail, chan, NULL);
05752 #else
05753                copy_message(chan, sender, -1, curmsg, duration, vmtmp, fmt, vmstmp.curdir);
05754 #endif
05755                saved_messages++;
05756                AST_LIST_REMOVE_CURRENT(list);
05757                free_user(vmtmp);
05758                if (res)
05759                   break;
05760             }
05761             AST_LIST_TRAVERSE_SAFE_END;
05762             if (saved_messages > 0) {
05763                /* give confirmation that the message was saved */
05764                /* commented out since we can't forward batches yet
05765                if (saved_messages == 1)
05766                   res = ast_play_and_wait(chan, "vm-message");
05767                else
05768                   res = ast_play_and_wait(chan, "vm-messages");
05769                if (!res)
05770                   res = ast_play_and_wait(chan, "vm-saved"); */
05771                res = ast_play_and_wait(chan, "vm-msgsaved");
05772             }  
05773          }
05774          /* Remove surrogate file */
05775          DISPOSE(dir, curmsg);
05776       }
05777 
05778       /* If anything failed above, we still have this list to free */
05779       while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
05780          free_user(vmtmp);
05781       return res ? res : cmd;
05782    }
05783 
05784    static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
05785    {
05786       int res;
05787       if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0) 
05788          ast_log(LOG_WARNING, "Unable to play message %s\n", file); 
05789       return res;
05790    }
05791 
05792    static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
05793    {
05794       return ast_control_streamfile(chan, file, listen_control_forward_key, listen_control_reverse_key, listen_control_stop_key, listen_control_pause_key, listen_control_restart_key, skipms, NULL);
05795    }
05796 
05797    static int play_message_category(struct ast_channel *chan, const char *category)
05798    {
05799       int res = 0;
05800 
05801       if (!ast_strlen_zero(category))
05802          res = ast_play_and_wait(chan, category);
05803 
05804       if (res) {
05805          ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
05806          res = 0;
05807       }
05808 
05809       return res;
05810    }
05811 
05812    static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
05813    {
05814       int res = 0;
05815       struct vm_zone *the_zone = NULL;
05816       time_t t;
05817 
05818       if (ast_get_time_t(origtime, &t, 0, NULL)) {
05819          ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
05820          return 0;
05821       }
05822 
05823       /* Does this user have a timezone specified? */
05824       if (!ast_strlen_zero(vmu->zonetag)) {
05825          /* Find the zone in the list */
05826          struct vm_zone *z;
05827          AST_LIST_LOCK(&zones);
05828          AST_LIST_TRAVERSE(&zones, z, list) {
05829             if (!strcmp(z->name, vmu->zonetag)) {
05830                the_zone = z;
05831                break;
05832             }
05833          }
05834          AST_LIST_UNLOCK(&zones);
05835       }
05836 
05837    /* No internal variable parsing for now, so we'll comment it out for the time being */
05838 #if 0
05839       /* Set the DIFF_* variables */
05840       ast_localtime(&t, &time_now, NULL);
05841       tv_now = ast_tvnow();
05842       ast_localtime(&tv_now, &time_then, NULL);
05843 
05844       /* Day difference */
05845       if (time_now.tm_year == time_then.tm_year)
05846          snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
05847       else
05848          snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
05849       pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
05850 
05851       /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
05852 #endif
05853       if (the_zone) {
05854          res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
05855       }
05856       else if (!strcasecmp(chan->language,"pl"))       /* POLISH syntax */
05857          res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
05858       else if (!strcasecmp(chan->language, "se"))       /* SWEDISH syntax */
05859          res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
05860       else if (!strcasecmp(chan->language, "no"))       /* NORWEGIAN syntax */
05861          res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
05862       else if (!strcasecmp(chan->language, "de"))       /* GERMAN syntax */
05863          res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
05864       else if (!strcasecmp(chan->language, "nl"))      /* DUTCH syntax */
05865          res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
05866       else if (!strcasecmp(chan->language, "it"))      /* ITALIAN syntax */
05867          res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
05868       else if (!strcasecmp(chan->language, "gr"))
05869          res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q  H 'digits/kai' M ", NULL);
05870       else if (!strcasecmp(chan->language, "pt_BR"))
05871          res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
05872       else if (!strcasecmp(chan->language, "tw"))      /* CHINESE (Taiwan) syntax */
05873          res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);     
05874       else {
05875          res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
05876       }
05877 #if 0
05878       pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
05879 #endif
05880       return res;
05881    }
05882 
05883 
05884 
05885    static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
05886    {
05887       int res = 0;
05888       int i;
05889       char *callerid, *name;
05890       char prefile[PATH_MAX] = "";
05891       
05892 
05893       /* If voicemail cid is not enabled, or we didn't get cid or context from
05894       * the attribute file, leave now.
05895       *
05896       * TODO Still need to change this so that if this function is called by the
05897       * message envelope (and someone is explicitly requesting to hear the CID),
05898       * it does not check to see if CID is enabled in the config file.
05899       */
05900       if ((cid == NULL)||(context == NULL))
05901          return res;
05902 
05903       /* Strip off caller ID number from name */
05904       ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
05905       ast_callerid_parse(cid, &name, &callerid);
05906       if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
05907          /* Check for internal contexts and only */
05908          /* say extension when the call didn't come from an internal context in the list */
05909          for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++) {
05910             ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
05911             if ((strcmp(cidinternalcontexts[i], context) == 0))
05912                break;
05913          }
05914          if (i != MAX_NUM_CID_CONTEXTS) { /* internal context? */
05915             if (!res) {
05916                snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
05917                if (!ast_strlen_zero(prefile)) {
05918                /* See if we can find a recorded name for this person instead of their extension number */
05919                   if (ast_fileexists(prefile, NULL, NULL) > 0) {
05920                      ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
05921                      if (!callback)
05922                         res = wait_file2(chan, vms, "vm-from");
05923                      res = ast_stream_and_wait(chan, prefile, "");
05924                   } else {
05925                      ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
05926                      /* Say "from extension" as one saying to sound smoother */
05927                      if (!callback)
05928                         res = wait_file2(chan, vms, "vm-from-extension");
05929                      res = ast_say_digit_str(chan, callerid, "", chan->language);
05930                   }
05931                }
05932             }
05933          } else if (!res) {
05934             ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
05935             /* Since this is all nicely figured out, why not say "from phone number" in this case? */
05936             if (!callback)
05937                res = wait_file2(chan, vms, "vm-from-phonenumber");
05938             res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
05939          }
05940       } else {
05941          /* Number unknown */
05942          ast_debug(1, "VM-CID: From an unknown number\n");
05943          /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
05944          res = wait_file2(chan, vms, "vm-unknown-caller");
05945       }
05946       return res;
05947    }
05948 
05949    static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
05950    {
05951       int res = 0;
05952       int durationm;
05953       int durations;
05954       /* Verify that we have a duration for the message */
05955       if (duration == NULL)
05956          return res;
05957 
05958       /* Convert from seconds to minutes */
05959       durations = atoi(duration);
05960       durationm = durations / 60;
05961 
05962       ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
05963 
05964       if ((!res) && (durationm >= minduration)) {
05965          res = wait_file2(chan, vms, "vm-duration");
05966 
05967          /* POLISH syntax */
05968          if (!strcasecmp(chan->language, "pl")) {
05969             div_t num = div(durationm, 10);
05970 
05971             if (durationm == 1) {
05972                res = ast_play_and_wait(chan, "digits/1z");
05973                res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
05974             } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
05975                if (num.rem == 2) {
05976                   if (!num.quot) {
05977                      res = ast_play_and_wait(chan, "digits/2-ie");
05978                   } else {
05979                      res = say_and_wait(chan, durationm - 2 , chan->language);
05980                      res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
05981                   }
05982                } else {
05983                   res = say_and_wait(chan, durationm, chan->language);
05984                }
05985                res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
05986             } else {
05987                res = say_and_wait(chan, durationm, chan->language);
05988                res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
05989             }
05990          /* DEFAULT syntax */
05991          } else {
05992             res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
05993             res = wait_file2(chan, vms, "vm-minutes");
05994          }
05995       }
05996       return res;
05997    }
05998 
05999    static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
06000    {
06001       int res = 0;
06002       char filename[256], *cid;
06003       const char *origtime, *context, *category, *duration;
06004       struct ast_config *msg_cfg;
06005       struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
06006 
06007       vms->starting = 0; 
06008       make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
06009       adsi_message(chan, vms);
06010       if (!vms->curmsg)
06011          res = wait_file2(chan, vms, "vm-first");  /* "First" */
06012       else if (vms->curmsg == vms->lastmsg)
06013          res = wait_file2(chan, vms, "vm-last");      /* "last" */
06014       if (!res) {
06015          /* POLISH syntax */
06016          if (!strcasecmp(chan->language, "pl")) { 
06017             if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
06018                int ten, one;
06019                char nextmsg[256];
06020                ten = (vms->curmsg + 1) / 10;
06021                one = (vms->curmsg + 1) % 10;
06022                
06023                if (vms->curmsg < 20) {
06024                   snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
06025                   res = wait_file2(chan, vms, nextmsg);
06026                } else {
06027                   snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
06028                   res = wait_file2(chan, vms, nextmsg);
06029                   if (one > 0) {
06030                      if (!res) {
06031                         snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
06032                         res = wait_file2(chan, vms, nextmsg);
06033                      }
06034                   }
06035                }
06036             }
06037             if (!res)
06038                res = wait_file2(chan, vms, "vm-message");
06039          } else {
06040             if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
06041                res = wait_file2(chan, vms, "vm-meddelandet");  /* "message" */
06042             else /* DEFAULT syntax */ {
06043                res = wait_file2(chan, vms, "vm-message");
06044             }
06045             if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
06046                if (!res) {
06047                   res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
06048                }
06049             }
06050          }
06051       }
06052 
06053       /* Retrieve info from VM attribute file */
06054       make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
06055       snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
06056       RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
06057       msg_cfg = ast_config_load(filename, config_flags);
06058       if (!msg_cfg) {
06059          ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
06060          return 0;
06061       }
06062 
06063       if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
06064          ast_log(LOG_WARNING, "No origtime?!\n");
06065          DISPOSE(vms->curdir, vms->curmsg);
06066          ast_config_destroy(msg_cfg);
06067          return 0;
06068       }
06069 
06070       cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
06071       duration = ast_variable_retrieve(msg_cfg, "message", "duration");
06072       category = ast_variable_retrieve(msg_cfg, "message", "category");
06073 
06074       context = ast_variable_retrieve(msg_cfg, "message", "context");
06075       if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
06076          context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
06077       if (!res) {
06078          res = play_message_category(chan, category);
06079       }
06080       if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
06081          res = play_message_datetime(chan, vmu, origtime, filename);
06082       if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
06083          res = play_message_callerid(chan, vms, cid, context, 0);
06084       if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
06085          res = play_message_duration(chan, vms, duration, vmu->saydurationm);
06086       /* Allow pressing '1' to skip envelope / callerid */
06087       if (res == '1')
06088          res = 0;
06089       ast_config_destroy(msg_cfg);
06090 
06091       if (!res) {
06092          make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
06093          vms->heard[vms->curmsg] = 1;
06094          if ((res = wait_file(chan, vms, vms->fn)) < 0) {
06095             ast_log(LOG_WARNING, "Playback of message %s failed\n", vms->fn);
06096             res = 0;
06097          }
06098       }
06099       DISPOSE(vms->curdir, vms->curmsg);
06100       return res;
06101    }
06102 
06103 #ifdef IMAP_STORAGE
06104 static int imap_remove_file(char *dir, int msgnum)
06105 {
06106    char fn[PATH_MAX];
06107    char full_fn[PATH_MAX];
06108    
06109    if (msgnum > -1) {
06110       make_file(fn, sizeof(fn), dir, msgnum);
06111    } else
06112       ast_copy_string(fn, dir, sizeof(fn));
06113    
06114    if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
06115       ast_filedelete(fn, NULL);
06116       snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
06117       unlink(full_fn);
06118    }
06119    return 0;
06120 }
06121 
06122 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
06123 {
06124    char *file, *filename;
06125    char *attachment;
06126    char arg[10];
06127    int i;
06128    BODY* body;
06129 
06130    
06131    file = strrchr(ast_strdupa(dir), '/');
06132    if (file)
06133       *file++ = '\0';
06134    else {
06135       ast_log(LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
06136       return -1;
06137    }
06138 
06139    ast_mutex_lock(&vms->lock);
06140    for (i = 0; i < vms->mailstream->nmsgs; i++) {
06141       mail_fetchstructure(vms->mailstream, i + 1, &body);
06142       /* We have the body, now we extract the file name of the first attachment. */
06143       if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
06144          attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
06145       } else {
06146          ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
06147          ast_mutex_unlock(&vms->lock);
06148          return -1;
06149       }
06150       filename = strsep(&attachment, ".");
06151       if (!strcmp(filename, file)) {
06152          sprintf (arg, "%d", i + 1);
06153          mail_setflag(vms->mailstream, arg, "\\DELETED");
06154       }
06155    }
06156    mail_expunge(vms->mailstream);
06157    ast_mutex_unlock(&vms->lock);
06158    return 0;
06159 }
06160 
06161 #else
06162 #ifndef IMAP_STORAGE
06163 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
06164 {
06165    int res = 0;
06166    int count_msg, last_msg;
06167 
06168    ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
06169    
06170    /* Rename the member vmbox HERE so that we don't try to return before
06171     * we know what's going on.
06172     */
06173    snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
06174    
06175    /* Faster to make the directory than to check if it exists. */
06176    create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
06177 
06178    count_msg = count_messages(vmu, vms->curdir);
06179    if (count_msg < 0)
06180       return count_msg;
06181    else
06182       vms->lastmsg = count_msg - 1;
06183 
06184    /*
06185    The following test is needed in case sequencing gets messed up.
06186    There appears to be more than one way to mess up sequence, so
06187    we will not try to find all of the root causes--just fix it when
06188    detected.
06189    */
06190 
06191    if (vm_lock_path(vms->curdir)) {
06192       ast_log(LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
06193       return -1;
06194    }
06195 
06196    last_msg = last_message_index(vmu, vms->curdir);
06197    ast_unlock_path(vms->curdir);
06198 
06199    if (last_msg < 0) 
06200       return last_msg;
06201    else if (vms->lastmsg != last_msg) {
06202       ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
06203       res = resequence_mailbox(vmu, vms->curdir);
06204       if (res)
06205          return res;
06206    }
06207 
06208    return 0;
06209 }
06210 #endif
06211 #endif
06212 
06213 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
06214 {
06215    int x = 0;
06216 #ifndef IMAP_STORAGE
06217    int res = 0, nummsg;
06218 #endif
06219 
06220    if (vms->lastmsg <= -1)
06221       goto done;
06222 
06223    vms->curmsg = -1; 
06224 #ifndef IMAP_STORAGE
06225    /* Get the deleted messages fixed */ 
06226    if (vm_lock_path(vms->curdir))
06227       return ERROR_LOCK_PATH;
06228 
06229    for (x = 0; x < vmu->maxmsg; x++) { 
06230       if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) { 
06231          /* Save this message.  It's not in INBOX or hasn't been heard */ 
06232          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x); 
06233          if (!EXISTS(vms->curdir, x, vms->fn, NULL)) 
06234             break;
06235          vms->curmsg++; 
06236          make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg); 
06237          if (strcmp(vms->fn, vms->fn2)) { 
06238             RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
06239          } 
06240       } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) { 
06241          /* Move to old folder before deleting */ 
06242          res = save_to_folder(vmu, vms, x, 1);
06243          if (res == ERROR_LOCK_PATH || res == ERROR_MAILBOX_FULL) {
06244             /* If save failed do not delete the message */
06245             ast_log(LOG_WARNING, "Save failed.  Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
06246             vms->deleted[x] = 0;
06247             vms->heard[x] = 0;
06248             --x;
06249          }
06250       } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
06251          /* Move to deleted folder */ 
06252          res = save_to_folder(vmu, vms, x, 10);
06253          if (res == ERROR_LOCK_PATH) {
06254             /* If save failed do not delete the message */
06255             vms->deleted[x] = 0;
06256             vms->heard[x] = 0;
06257             --x;
06258          }
06259       } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
06260          /* If realtime storage enabled - we should explicitly delete this message,
06261          cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
06262          make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
06263          if (EXISTS(vms->curdir, x, vms->fn, NULL))
06264             DELETE(vms->curdir, x, vms->fn, vmu);
06265       }
06266    } 
06267 
06268    /* Delete ALL remaining messages */
06269    nummsg = x - 1;
06270    for (x = vms->curmsg + 1; x <= nummsg; x++) {
06271       make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
06272       if (EXISTS(vms->curdir, x, vms->fn, NULL))
06273          DELETE(vms->curdir, x, vms->fn, vmu);
06274    }
06275    ast_unlock_path(vms->curdir);
06276 #else
06277    if (vms->deleted) {
06278       for (x = 0; x < vmu->maxmsg; x++) { 
06279          if (vms->deleted[x]) { 
06280             ast_debug(3, "IMAP delete of %d\n", x);
06281             DELETE(vms->curdir, x, vms->fn, vmu);
06282          }
06283       }
06284    }
06285 #endif
06286 
06287 done:
06288    if (vms->deleted)
06289       memset(vms->deleted, 0, vmu->maxmsg * sizeof(int)); 
06290    if (vms->heard)
06291       memset(vms->heard, 0, vmu->maxmsg * sizeof(int)); 
06292 
06293    return 0;
06294 }
06295 
06296 /* In Greek even though we CAN use a syntax like "friends messages"
06297  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
06298  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed 
06299  * syntax for the above three categories which is more elegant. 
06300  */
06301 
06302 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
06303 {
06304    int cmd;
06305    char *buf;
06306 
06307    buf = alloca(strlen(mbox) + 2); 
06308    strcpy(buf, mbox);
06309    strcat(buf, "s");
06310 
06311    if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
06312       cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
06313       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
06314    } else {
06315       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
06316       return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
06317    }
06318 }
06319 
06320 static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
06321 {
06322    int cmd;
06323 
06324    if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
06325       if (!strcasecmp(mbox, "vm-INBOX"))
06326          cmd = ast_play_and_wait(chan, "vm-new-e");
06327       else
06328          cmd = ast_play_and_wait(chan, "vm-old-e");
06329       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
06330    } else {
06331       cmd = ast_play_and_wait(chan, "vm-messages");
06332       return cmd ? cmd : ast_play_and_wait(chan, mbox);
06333    }
06334 }
06335 
06336 static int vm_play_folder_name_ua(struct ast_channel *chan, char *mbox)
06337 {
06338    int cmd;
06339 
06340    if (!strcasecmp(mbox, "vm-Family") || !strcasecmp(mbox, "vm-Friends") || !strcasecmp(mbox, "vm-Work")) {
06341       cmd = ast_play_and_wait(chan, "vm-messages");
06342       return cmd ? cmd : ast_play_and_wait(chan, mbox);
06343    } else {
06344       cmd = ast_play_and_wait(chan, mbox);
06345       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
06346    }
06347 }
06348 
06349 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
06350 {
06351    int cmd;
06352 
06353    if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
06354       cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
06355       return cmd ? cmd : ast_play_and_wait(chan, mbox);
06356    } else if (!strcasecmp(chan->language, "gr")) {
06357       return vm_play_folder_name_gr(chan, mbox);
06358    } else if (!strcasecmp(chan->language, "pl")) {
06359       return vm_play_folder_name_pl(chan, mbox);
06360    } else if (!strcasecmp(chan->language, "ua")) {  /* Ukrainian syntax */
06361       return vm_play_folder_name_ua(chan, mbox);
06362    } else {  /* Default English */
06363       cmd = ast_play_and_wait(chan, mbox);
06364       return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
06365    }
06366 }
06367 
06368 /* GREEK SYNTAX 
06369    In greek the plural for old/new is
06370    different so we need the following files
06371    We also need vm-denExeteMynhmata because 
06372    this syntax is different.
06373    
06374    -> vm-Olds.wav : "Palia"
06375    -> vm-INBOXs.wav : "Nea"
06376    -> vm-denExeteMynhmata : "den exete mynhmata"
06377 */
06378                
06379    
06380 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
06381 {
06382    int res = 0;
06383 
06384    if (vms->newmessages) {
06385       res = ast_play_and_wait(chan, "vm-youhave");
06386       if (!res) 
06387          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
06388       if (!res) {
06389          if ((vms->newmessages == 1)) {
06390             res = ast_play_and_wait(chan, "vm-INBOX");
06391             if (!res)
06392                res = ast_play_and_wait(chan, "vm-message");
06393          } else {
06394             res = ast_play_and_wait(chan, "vm-INBOXs");
06395             if (!res)
06396                res = ast_play_and_wait(chan, "vm-messages");
06397          }
06398       }
06399    } else if (vms->oldmessages) {
06400       res = ast_play_and_wait(chan, "vm-youhave");
06401       if (!res)
06402          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
06403       if ((vms->oldmessages == 1)) {
06404          res = ast_play_and_wait(chan, "vm-Old");
06405          if (!res)
06406             res = ast_play_and_wait(chan, "vm-message");
06407       } else {
06408          res = ast_play_and_wait(chan, "vm-Olds");
06409          if (!res)
06410             res = ast_play_and_wait(chan, "vm-messages");
06411       }
06412    } else if (!vms->oldmessages && !vms->newmessages) 
06413       res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
06414    return res;
06415 }
06416 
06417 /* Version of vm_intro() designed to work for many languages.
06418  *
06419  * It is hoped that this function can prevent the proliferation of 
06420  * language-specific vm_intro() functions and in time replace the language-
06421  * specific functions which already exist.  An examination of the language-
06422  * specific functions revealed that they all corrected the same deficiencies
06423  * in vm_intro_en() (which was the default function). Namely:
06424  *
06425  *  1) The vm-Old and vm-INBOX sound files were overloaded.  The English 
06426  *     wording of the voicemail greeting hides this problem.  For example,
06427  *     vm-INBOX contains only the word "new".  This means that both of these
06428  *     sequences produce valid utterances:
06429  *      * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
06430  *      * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
06431  *     However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
06432  *     in many languages) the first utterance becomes "you have 1 the new message".
06433  *  2) The function contains hardcoded rules for pluralizing the word "message".
06434  *     These rules are correct for English, but not for many other languages.
06435  *  3) No attempt is made to pluralize the adjectives ("old" and "new") as
06436  *     required in many languages.
06437  *  4) The gender of the word for "message" is not specified. This is a problem
06438  *     because in many languages the gender of the number in phrases such
06439  *     as "you have one new message" must match the gender of the word
06440  *     meaning "message".
06441  *
06442  * Fixing these problems for each new language has meant duplication of effort.
06443  * This new function solves the problems in the following general ways:
06444  *  1) Add new sound files vm-new and vm-old.  These can be linked to vm-INBOX
06445  *     and vm-Old respectively for those languages where it makes sense.
06446  *  2) Call ast_say_counted_noun() to put the proper gender and number prefix
06447  *     on vm-message.
06448  *  3) Call ast_say_counted_adjective() to put the proper gender and number
06449  *     prefix on vm-new and vm-old (none for English).
06450  *  4) Pass the gender of the language's word for "message" as an agument to
06451  *     this function which is can in turn pass on to the functions which 
06452  *     say numbers and put endings on nounds and adjectives.
06453  *
06454  * All languages require these messages:
06455  *  vm-youhave    "You have..."
06456  *  vm-and     "and"
06457  *  vm-no      "no" (in the sense of "none", as in "you have no messages")
06458  *
06459  * To use it for English, you will need these additional sound files:
06460  *  vm-new     "new"
06461  *  vm-message    "message", singular
06462  *  vm-messages      "messages", plural
06463  *
06464  * If you use it for Russian and other slavic languages, you will need these additional sound files:
06465  *
06466  *  vm-newn    "novoye" (singular, neuter)
06467  *  vm-newx    "novikh" (counting plural form, genative plural)
06468  *  vm-message    "sobsheniye" (singular form)
06469  *  vm-messagex1  "sobsheniya" (first counting plural form, genative singular)
06470  *  vm-messagex2  "sobsheniy" (second counting plural form, genative plural)
06471  *  digits/1n     "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
06472  *  digits/2n     "dva" (neuter singular)
06473  */
06474 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
06475 {
06476    int res;
06477    int lastnum = 0;
06478 
06479    res = ast_play_and_wait(chan, "vm-youhave");
06480 
06481    if (!res && vms->newmessages) {
06482       lastnum = vms->newmessages;
06483 
06484       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
06485          res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
06486       }
06487 
06488       if (!res && vms->oldmessages) {
06489          res = ast_play_and_wait(chan, "vm-and");
06490       }
06491    }
06492 
06493    if (!res && vms->oldmessages) {
06494       lastnum = vms->oldmessages;
06495 
06496       if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
06497          res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
06498       }
06499    }
06500 
06501    if (!res) {
06502       if (lastnum == 0) {
06503          res = ast_play_and_wait(chan, "vm-no");
06504       }
06505       if (!res) {
06506          res = ast_say_counted_noun(chan, lastnum, "vm-message");
06507       }
06508    }
06509 
06510    return res;
06511 }
06512 
06513 /* Default English syntax */
06514 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
06515 {
06516    int res;
06517 
06518    /* Introduce messages they have */
06519    res = ast_play_and_wait(chan, "vm-youhave");
06520    if (!res) {
06521       if (vms->newmessages) {
06522          res = say_and_wait(chan, vms->newmessages, chan->language);
06523          if (!res)
06524             res = ast_play_and_wait(chan, "vm-INBOX");
06525          if (vms->oldmessages && !res)
06526             res = ast_play_and_wait(chan, "vm-and");
06527          else if (!res) {
06528             if ((vms->newmessages == 1))
06529                res = ast_play_and_wait(chan, "vm-message");
06530             else
06531                res = ast_play_and_wait(chan, "vm-messages");
06532          }
06533             
06534       }
06535       if (!res && vms->oldmessages) {
06536          res = say_and_wait(chan, vms->oldmessages, chan->language);
06537          if (!res)
06538             res = ast_play_and_wait(chan, "vm-Old");
06539          if (!res) {
06540             if (vms->oldmessages == 1)
06541                res = ast_play_and_wait(chan, "vm-message");
06542             else
06543                res = ast_play_and_wait(chan, "vm-messages");
06544          }
06545       }
06546       if (!res) {
06547          if (!vms->oldmessages && !vms->newmessages) {
06548             res = ast_play_and_wait(chan, "vm-no");
06549             if (!res)
06550                res = ast_play_and_wait(chan, "vm-messages");
06551          }
06552       }
06553    }
06554    return res;
06555 }
06556 
06557 /* ITALIAN syntax */
06558 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
06559 {
06560    /* Introduce messages they have */
06561    int res;
06562    if (!vms->oldmessages && !vms->newmessages)
06563       res = ast_play_and_wait(chan, "vm-no") ||
06564          ast_play_and_wait(chan, "vm-message");
06565    else
06566       res = ast_play_and_wait(chan, "vm-youhave");
06567    if (!res && vms->newmessages) {
06568       res = (vms->newmessages == 1) ?
06569          ast_play_and_wait(chan, "digits/un") ||
06570          ast_play_and_wait(chan, "vm-nuovo") ||
06571          ast_play_and_wait(chan, "vm-message") :
06572          /* 2 or more new messages */
06573          say_and_wait(chan, vms->newmessages, chan->language) ||
06574          ast_play_and_wait(chan, "vm-nuovi") ||
06575          ast_play_and_wait(chan, "vm-messages");
06576       if (!res && vms->oldmessages)
06577          res = ast_play_and_wait(chan, "vm-and");
06578    }
06579    if (!res && vms->oldmessages) {
06580       res = (vms->oldmessages == 1) ?
06581          ast_play_and_wait(chan, "digits/un") ||
06582          ast_play_and_wait(chan, "vm-vecchio") ||
06583          ast_play_and_wait(chan, "vm-message") :
06584          /* 2 or more old messages */
06585          say_and_wait(chan, vms->oldmessages, chan->language) ||
06586          ast_play_and_wait(chan, "vm-vecchi") ||
06587          ast_play_and_wait(chan, "vm-messages");
06588    }
06589    return res;
06590 }
06591 
06592 /* POLISH syntax */
06593 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
06594 {
06595    /* Introduce messages they have */
06596    int res;
06597    div_t num;
06598 
06599    if (!vms->oldmessages && !vms->newmessages) {
06600       res = ast_play_and_wait(chan, "vm-no");
06601       res = res ? res : ast_play_and_wait(chan, "vm-messages");
06602       return res;
06603    } else {
06604       res = ast_play_and_wait(chan, "vm-youhave");
06605    }
06606 
06607    if (vms->newmessages) {
06608       num = div(vms->newmessages, 10);
06609       if (vms->newmessages == 1) {
06610          res = ast_play_and_wait(chan, "digits/1-a");
06611          res = res ? res : ast_play_and_wait(chan, "vm-new-a");
06612          res = res ? res : ast_play_and_wait(chan, "vm-message");
06613       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
06614          if (num.rem == 2) {
06615             if (!num.quot) {
06616                res = ast_play_and_wait(chan, "digits/2-ie");
06617             } else {
06618                res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
06619                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
06620             }
06621          } else {
06622             res = say_and_wait(chan, vms->newmessages, chan->language);
06623          }
06624          res = res ? res : ast_play_and_wait(chan, "vm-new-e");
06625          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06626       } else {
06627          res = say_and_wait(chan, vms->newmessages, chan->language);
06628          res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
06629          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06630       }
06631       if (!res && vms->oldmessages)
06632          res = ast_play_and_wait(chan, "vm-and");
06633    }
06634    if (!res && vms->oldmessages) {
06635       num = div(vms->oldmessages, 10);
06636       if (vms->oldmessages == 1) {
06637          res = ast_play_and_wait(chan, "digits/1-a");
06638          res = res ? res : ast_play_and_wait(chan, "vm-old-a");
06639          res = res ? res : ast_play_and_wait(chan, "vm-message");
06640       } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
06641          if (num.rem == 2) {
06642             if (!num.quot) {
06643                res = ast_play_and_wait(chan, "digits/2-ie");
06644             } else {
06645                res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
06646                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
06647             }
06648          } else {
06649             res = say_and_wait(chan, vms->oldmessages, chan->language);
06650          }
06651          res = res ? res : ast_play_and_wait(chan, "vm-old-e");
06652          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06653       } else {
06654          res = say_and_wait(chan, vms->oldmessages, chan->language);
06655          res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
06656          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06657       }
06658    }
06659 
06660    return res;
06661 }
06662 
06663 /* SWEDISH syntax */
06664 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
06665 {
06666    /* Introduce messages they have */
06667    int res;
06668 
06669    res = ast_play_and_wait(chan, "vm-youhave");
06670    if (res)
06671       return res;
06672 
06673    if (!vms->oldmessages && !vms->newmessages) {
06674       res = ast_play_and_wait(chan, "vm-no");
06675       res = res ? res : ast_play_and_wait(chan, "vm-messages");
06676       return res;
06677    }
06678 
06679    if (vms->newmessages) {
06680       if ((vms->newmessages == 1)) {
06681          res = ast_play_and_wait(chan, "digits/ett");
06682          res = res ? res : ast_play_and_wait(chan, "vm-nytt");
06683          res = res ? res : ast_play_and_wait(chan, "vm-message");
06684       } else {
06685          res = say_and_wait(chan, vms->newmessages, chan->language);
06686          res = res ? res : ast_play_and_wait(chan, "vm-nya");
06687          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06688       }
06689       if (!res && vms->oldmessages)
06690          res = ast_play_and_wait(chan, "vm-and");
06691    }
06692    if (!res && vms->oldmessages) {
06693       if (vms->oldmessages == 1) {
06694          res = ast_play_and_wait(chan, "digits/ett");
06695          res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
06696          res = res ? res : ast_play_and_wait(chan, "vm-message");
06697       } else {
06698          res = say_and_wait(chan, vms->oldmessages, chan->language);
06699          res = res ? res : ast_play_and_wait(chan, "vm-gamla");
06700          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06701       }
06702    }
06703 
06704    return res;
06705 }
06706 
06707 /* NORWEGIAN syntax */
06708 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
06709 {
06710    /* Introduce messages they have */
06711    int res;
06712 
06713    res = ast_play_and_wait(chan, "vm-youhave");
06714    if (res)
06715       return res;
06716 
06717    if (!vms->oldmessages && !vms->newmessages) {
06718       res = ast_play_and_wait(chan, "vm-no");
06719       res = res ? res : ast_play_and_wait(chan, "vm-messages");
06720       return res;
06721    }
06722 
06723    if (vms->newmessages) {
06724       if ((vms->newmessages == 1)) {
06725          res = ast_play_and_wait(chan, "digits/1");
06726          res = res ? res : ast_play_and_wait(chan, "vm-ny");
06727          res = res ? res : ast_play_and_wait(chan, "vm-message");
06728       } else {
06729          res = say_and_wait(chan, vms->newmessages, chan->language);
06730          res = res ? res : ast_play_and_wait(chan, "vm-nye");
06731          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06732       }
06733       if (!res && vms->oldmessages)
06734          res = ast_play_and_wait(chan, "vm-and");
06735    }
06736    if (!res && vms->oldmessages) {
06737       if (vms->oldmessages == 1) {
06738          res = ast_play_and_wait(chan, "digits/1");
06739          res = res ? res : ast_play_and_wait(chan, "vm-gamel");
06740          res = res ? res : ast_play_and_wait(chan, "vm-message");
06741       } else {
06742          res = say_and_wait(chan, vms->oldmessages, chan->language);
06743          res = res ? res : ast_play_and_wait(chan, "vm-gamle");
06744          res = res ? res : ast_play_and_wait(chan, "vm-messages");
06745       }
06746    }
06747 
06748    return res;
06749 }
06750 
06751 /* GERMAN syntax */
06752 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
06753 {
06754    /* Introduce messages they have */
06755    int res;
06756    res = ast_play_and_wait(chan, "vm-youhave");
06757    if (!res) {
06758       if (vms->newmessages) {
06759          if ((vms->newmessages == 1))
06760             res = ast_play_and_wait(chan, "digits/1F");
06761          else
06762             res = say_and_wait(chan, vms->newmessages, chan->language);
06763          if (!res)
06764             res = ast_play_and_wait(chan, "vm-INBOX");
06765          if (vms->oldmessages && !res)
06766             res = ast_play_and_wait(chan, "vm-and");
06767          else if (!res) {
06768             if ((vms->newmessages == 1))
06769                res = ast_play_and_wait(chan, "vm-message");
06770             else
06771                res = ast_play_and_wait(chan, "vm-messages");
06772          }
06773             
06774       }
06775       if (!res && vms->oldmessages) {
06776          if (vms->oldmessages == 1)
06777             res = ast_play_and_wait(chan, "digits/1F");
06778          else
06779             res = say_and_wait(chan, vms->oldmessages, chan->language);
06780          if (!res)
06781             res = ast_play_and_wait(chan, "vm-Old");
06782          if (!res) {
06783             if (vms->oldmessages == 1)
06784                res = ast_play_and_wait(chan, "vm-message");
06785             else
06786                res = ast_play_and_wait(chan, "vm-messages");
06787          }
06788       }
06789       if (!res) {
06790          if (!vms->oldmessages && !vms->newmessages) {
06791             res = ast_play_and_wait(chan, "vm-no");
06792             if (!res)
06793                res = ast_play_and_wait(chan, "vm-messages");
06794          }
06795       }
06796    }
06797    return res;
06798 }
06799 
06800 /* SPANISH syntax */
06801 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
06802 {
06803    /* Introduce messages they have */
06804    int res;
06805    if (!vms->oldmessages && !vms->newmessages) {
06806       res = ast_play_and_wait(chan, "vm-youhaveno");
06807       if (!res)
06808          res = ast_play_and_wait(chan, "vm-messages");
06809    } else {
06810       res = ast_play_and_wait(chan, "vm-youhave");
06811    }
06812    if (!res) {
06813       if (vms->newmessages) {
06814          if (!res) {
06815             if ((vms->newmessages == 1)) {
06816                res = ast_play_and_wait(chan, "digits/1M");
06817                if (!res)
06818                   res = ast_play_and_wait(chan, "vm-message");
06819                if (!res)
06820                   res = ast_play_and_wait(chan, "vm-INBOXs");
06821             } else {
06822                res = say_and_wait(chan, vms->newmessages, chan->language);
06823                if (!res)
06824                   res = ast_play_and_wait(chan, "vm-messages");
06825                if (!res)
06826                   res = ast_play_and_wait(chan, "vm-INBOX");
06827             }
06828          }
06829          if (vms->oldmessages && !res)
06830             res = ast_play_and_wait(chan, "vm-and");
06831       }
06832       if (vms->oldmessages) {
06833          if (!res) {
06834             if (vms->oldmessages == 1) {
06835                res = ast_play_and_wait(chan, "digits/1M");
06836                if (!res)
06837                   res = ast_play_and_wait(chan, "vm-message");
06838                if (!res)
06839                   res = ast_play_and_wait(chan, "vm-Olds");
06840             } else {
06841                res = say_and_wait(chan, vms->oldmessages, chan->language);
06842                if (!res)
06843                   res = ast_play_and_wait(chan, "vm-messages");
06844                if (!res)
06845                   res = ast_play_and_wait(chan, "vm-Old");
06846             }
06847          }
06848       }
06849    }
06850 return res;
06851 }
06852 
06853 /* BRAZILIAN PORTUGUESE syntax */
06854 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
06855    /* Introduce messages they have */
06856    int res;
06857    if (!vms->oldmessages && !vms->newmessages) {
06858       res = ast_play_and_wait(chan, "vm-nomessages");
06859       return res;
06860    } else {
06861       res = ast_play_and_wait(chan, "vm-youhave");
06862    }
06863    if (vms->newmessages) {
06864       if (!res)
06865          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
06866       if ((vms->newmessages == 1)) {
06867          if (!res)
06868             res = ast_play_and_wait(chan, "vm-message");
06869          if (!res)
06870             res = ast_play_and_wait(chan, "vm-INBOXs");
06871       } else {
06872          if (!res)
06873             res = ast_play_and_wait(chan, "vm-messages");
06874          if (!res)
06875             res = ast_play_and_wait(chan, "vm-INBOX");
06876       }
06877       if (vms->oldmessages && !res)
06878          res = ast_play_and_wait(chan, "vm-and");
06879    }
06880    if (vms->oldmessages) {
06881       if (!res)
06882          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
06883       if (vms->oldmessages == 1) {
06884          if (!res)
06885             res = ast_play_and_wait(chan, "vm-message");
06886          if (!res)
06887             res = ast_play_and_wait(chan, "vm-Olds");
06888       } else {
06889          if (!res)
06890             res = ast_play_and_wait(chan, "vm-messages");
06891          if (!res)
06892             res = ast_play_and_wait(chan, "vm-Old");
06893       }
06894    }
06895    return res;
06896 }
06897 
06898 /* FRENCH syntax */
06899 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
06900 {
06901    /* Introduce messages they have */
06902    int res;
06903    res = ast_play_and_wait(chan, "vm-youhave");
06904    if (!res) {
06905       if (vms->newmessages) {
06906          res = say_and_wait(chan, vms->newmessages, chan->language);
06907          if (!res)
06908             res = ast_play_and_wait(chan, "vm-INBOX");
06909          if (vms->oldmessages && !res)
06910             res = ast_play_and_wait(chan, "vm-and");
06911          else if (!res) {
06912             if ((vms->newmessages == 1))
06913                res = ast_play_and_wait(chan, "vm-message");
06914             else
06915                res = ast_play_and_wait(chan, "vm-messages");
06916          }
06917             
06918       }
06919       if (!res && vms->oldmessages) {
06920          res = say_and_wait(chan, vms->oldmessages, chan->language);
06921          if (!res)
06922             res = ast_play_and_wait(chan, "vm-Old");
06923          if (!res) {
06924             if (vms->oldmessages == 1)
06925                res = ast_play_and_wait(chan, "vm-message");
06926             else
06927                res = ast_play_and_wait(chan, "vm-messages");
06928          }
06929       }
06930       if (!res) {
06931          if (!vms->oldmessages && !vms->newmessages) {
06932             res = ast_play_and_wait(chan, "vm-no");
06933             if (!res)
06934                res = ast_play_and_wait(chan, "vm-messages");
06935          }
06936       }
06937    }
06938    return res;
06939 }
06940 
06941 /* DUTCH syntax */
06942 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
06943 {
06944    /* Introduce messages they have */
06945    int res;
06946    res = ast_play_and_wait(chan, "vm-youhave");
06947    if (!res) {
06948       if (vms->newmessages) {
06949          res = say_and_wait(chan, vms->newmessages, chan->language);
06950          if (!res) {
06951             if (vms->newmessages == 1)
06952                res = ast_play_and_wait(chan, "vm-INBOXs");
06953             else
06954                res = ast_play_and_wait(chan, "vm-INBOX");
06955          }
06956          if (vms->oldmessages && !res)
06957             res = ast_play_and_wait(chan, "vm-and");
06958          else if (!res) {
06959             if ((vms->newmessages == 1))
06960                res = ast_play_and_wait(chan, "vm-message");
06961             else
06962                res = ast_play_and_wait(chan, "vm-messages");
06963          }
06964             
06965       }
06966       if (!res && vms->oldmessages) {
06967          res = say_and_wait(chan, vms->oldmessages, chan->language);
06968          if (!res) {
06969             if (vms->oldmessages == 1)
06970                res = ast_play_and_wait(chan, "vm-Olds");
06971             else
06972                res = ast_play_and_wait(chan, "vm-Old");
06973          }
06974          if (!res) {
06975             if (vms->oldmessages == 1)
06976                res = ast_play_and_wait(chan, "vm-message");
06977             else
06978                res = ast_play_and_wait(chan, "vm-messages");
06979          }
06980       }
06981       if (!res) {
06982          if (!vms->oldmessages && !vms->newmessages) {
06983             res = ast_play_and_wait(chan, "vm-no");
06984             if (!res)
06985                res = ast_play_and_wait(chan, "vm-messages");
06986          }
06987       }
06988    }
06989    return res;
06990 }
06991 
06992 /* PORTUGUESE syntax */
06993 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
06994 {
06995    /* Introduce messages they have */
06996    int res;
06997    res = ast_play_and_wait(chan, "vm-youhave");
06998    if (!res) {
06999       if (vms->newmessages) {
07000          res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
07001          if (!res) {
07002             if ((vms->newmessages == 1)) {
07003                res = ast_play_and_wait(chan, "vm-message");
07004                if (!res)
07005                   res = ast_play_and_wait(chan, "vm-INBOXs");
07006             } else {
07007                res = ast_play_and_wait(chan, "vm-messages");
07008                if (!res)
07009                   res = ast_play_and_wait(chan, "vm-INBOX");
07010             }
07011          }
07012          if (vms->oldmessages && !res)
07013             res = ast_play_and_wait(chan, "vm-and");
07014       }
07015       if (!res && vms->oldmessages) {
07016          res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
07017          if (!res) {
07018             if (vms->oldmessages == 1) {
07019                res = ast_play_and_wait(chan, "vm-message");
07020                if (!res)
07021                   res = ast_play_and_wait(chan, "vm-Olds");
07022             } else {
07023                res = ast_play_and_wait(chan, "vm-messages");
07024                if (!res)
07025                   res = ast_play_and_wait(chan, "vm-Old");
07026             }
07027          }
07028       }
07029       if (!res) {
07030          if (!vms->oldmessages && !vms->newmessages) {
07031             res = ast_play_and_wait(chan, "vm-no");
07032             if (!res)
07033                res = ast_play_and_wait(chan, "vm-messages");
07034          }
07035       }
07036    }
07037    return res;
07038 }
07039 
07040 
07041 /* CZECH syntax */
07042 /* in czech there must be declension of word new and message
07043  * czech        : english        : czech      : english
07044  * --------------------------------------------------------
07045  * vm-youhave   : you have 
07046  * vm-novou     : one new        : vm-zpravu  : message
07047  * vm-nove      : 2-4 new        : vm-zpravy  : messages
07048  * vm-novych    : 5-infinite new : vm-zprav   : messages
07049  * vm-starou   : one old
07050  * vm-stare     : 2-4 old 
07051  * vm-starych   : 5-infinite old
07052  * jednu        : one   - falling 4. 
07053  * vm-no        : no  ( no messages )
07054  */
07055 
07056 static int vm_intro_cz(struct ast_channel *chan, struct vm_state *vms)
07057 {
07058    int res;
07059    res = ast_play_and_wait(chan, "vm-youhave");
07060    if (!res) {
07061       if (vms->newmessages) {
07062          if (vms->newmessages == 1) {
07063             res = ast_play_and_wait(chan, "digits/jednu");
07064          } else {
07065             res = say_and_wait(chan, vms->newmessages, chan->language);
07066          }
07067          if (!res) {
07068             if ((vms->newmessages == 1))
07069                res = ast_play_and_wait(chan, "vm-novou");
07070             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
07071                res = ast_play_and_wait(chan, "vm-nove");
07072             if (vms->newmessages > 4)
07073                res = ast_play_and_wait(chan, "vm-novych");
07074          }
07075          if (vms->oldmessages && !res)
07076             res = ast_play_and_wait(chan, "vm-and");
07077          else if (!res) {
07078             if ((vms->newmessages == 1))
07079                res = ast_play_and_wait(chan, "vm-zpravu");
07080             if ((vms->newmessages) > 1 && (vms->newmessages < 5))
07081                res = ast_play_and_wait(chan, "vm-zpravy");
07082             if (vms->newmessages > 4)
07083                res = ast_play_and_wait(chan, "vm-zprav");
07084          }
07085       }
07086       if (!res && vms->oldmessages) {
07087          res = say_and_wait(chan, vms->oldmessages, chan->language);
07088          if (!res) {
07089             if ((vms->oldmessages == 1))
07090                res = ast_play_and_wait(chan, "vm-starou");
07091             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
07092                res = ast_play_and_wait(chan, "vm-stare");
07093             if (vms->oldmessages > 4)
07094                res = ast_play_and_wait(chan, "vm-starych");
07095          }
07096          if (!res) {
07097             if ((vms->oldmessages == 1))
07098                res = ast_play_and_wait(chan, "vm-zpravu");
07099             if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
07100                res = ast_play_and_wait(chan, "vm-zpravy");
07101             if (vms->oldmessages > 4)
07102                res = ast_play_and_wait(chan, "vm-zprav");
07103          }
07104       }
07105       if (!res) {
07106          if (!vms->oldmessages && !vms->newmessages) {
07107             res = ast_play_and_wait(chan, "vm-no");
07108             if (!res)
07109                res = ast_play_and_wait(chan, "vm-zpravy");
07110          }
07111       }
07112    }
07113    return res;
07114 }
07115 
07116 /* CHINESE (Taiwan) syntax */
07117 static int vm_intro_tw(struct ast_channel *chan, struct vm_state *vms)
07118 {
07119    int res;
07120    /* Introduce messages they have */
07121    res = ast_play_and_wait(chan, "vm-you");
07122 
07123    if (!res && vms->newmessages) {
07124       res = ast_play_and_wait(chan, "vm-have");
07125       if (!res)
07126          res = say_and_wait(chan, vms->newmessages, chan->language);
07127       if (!res)
07128          res = ast_play_and_wait(chan, "vm-tong");
07129       if (!res)
07130          res = ast_play_and_wait(chan, "vm-INBOX");
07131       if (vms->oldmessages && !res)
07132          res = ast_play_and_wait(chan, "vm-and");
07133       else if (!res) 
07134          res = ast_play_and_wait(chan, "vm-messages");
07135    }
07136    if (!res && vms->oldmessages) {
07137       res = ast_play_and_wait(chan, "vm-have");
07138       if (!res)
07139          res = say_and_wait(chan, vms->oldmessages, chan->language);
07140       if (!res)
07141          res = ast_play_and_wait(chan, "vm-tong");
07142       if (!res)
07143          res = ast_play_and_wait(chan, "vm-Old");
07144       if (!res)
07145          res = ast_play_and_wait(chan, "vm-messages");
07146    }
07147    if (!res && !vms->oldmessages && !vms->newmessages) {
07148       res = ast_play_and_wait(chan, "vm-haveno");
07149       if (!res)
07150          res = ast_play_and_wait(chan, "vm-messages");
07151    }
07152    return res;
07153 }
07154 
07155 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
07156 {
07157    char prefile[256];
07158    
07159    /* Notify the user that the temp greeting is set and give them the option to remove it */
07160    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
07161    if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
07162       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
07163       if (ast_fileexists(prefile, NULL, NULL) > 0) {
07164          ast_play_and_wait(chan, "vm-tempgreetactive");
07165       }
07166       DISPOSE(prefile, -1);
07167    }
07168 
07169    /* Play voicemail intro - syntax is different for different languages */
07170    if (!strcasecmp(chan->language, "de")) {  /* GERMAN syntax */
07171       return vm_intro_de(chan, vms);
07172    } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
07173       return vm_intro_es(chan, vms);
07174    } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
07175       return vm_intro_it(chan, vms);
07176    } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
07177       return vm_intro_fr(chan, vms);
07178    } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
07179       return vm_intro_nl(chan, vms);
07180    } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
07181       return vm_intro_pt(chan, vms);
07182    } else if (!strcasecmp(chan->language, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
07183       return vm_intro_pt_BR(chan, vms);
07184    } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
07185       return vm_intro_cz(chan, vms);
07186    } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
07187       return vm_intro_gr(chan, vms);
07188    } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
07189       return vm_intro_pl(chan, vms);
07190    } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
07191       return vm_intro_se(chan, vms);
07192    } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
07193       return vm_intro_no(chan, vms);
07194    } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
07195       return vm_intro_multilang(chan, vms, "n");
07196    } else if (!strcasecmp(chan->language, "tw")) { /* CHINESE (Taiwan) syntax */
07197       return vm_intro_tw(chan, vms);
07198    } else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
07199       return vm_intro_multilang(chan, vms, "n");
07200    } else {             /* Default to ENGLISH */
07201       return vm_intro_en(chan, vms);
07202    }
07203 }
07204 
07205 static int vm_instructions_en(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
07206 {
07207    int res = 0;
07208    /* Play instructions and wait for new command */
07209    while (!res) {
07210       if (vms->starting) {
07211          if (vms->lastmsg > -1) {
07212             res = ast_play_and_wait(chan, "vm-onefor");
07213             if (!res)
07214                res = vm_play_folder_name(chan, vms->vmbox);
07215          }
07216          if (!res)
07217             res = ast_play_and_wait(chan, "vm-opts");
07218       } else {
07219          if (vms->curmsg)
07220             res = ast_play_and_wait(chan, "vm-prev");
07221          if (!res && !skipadvanced)
07222             res = ast_play_and_wait(chan, "vm-advopts");
07223          if (!res)
07224             res = ast_play_and_wait(chan, "vm-repeat");
07225          if (!res && (vms->curmsg != vms->lastmsg))
07226             res = ast_play_and_wait(chan, "vm-next");
07227          if (!res) {
07228             if (!vms->deleted[vms->curmsg])
07229                res = ast_play_and_wait(chan, "vm-delete");
07230             else
07231                res = ast_play_and_wait(chan, "vm-undelete");
07232             if (!res)
07233                res = ast_play_and_wait(chan, "vm-toforward");
07234             if (!res)
07235                res = ast_play_and_wait(chan, "vm-savemessage");
07236          }
07237       }
07238       if (!res)
07239          res = ast_play_and_wait(chan, "vm-helpexit");
07240       if (!res)
07241          res = ast_waitfordigit(chan, 6000);
07242       if (!res) {
07243          vms->repeats++;
07244          if (vms->repeats > 2) {
07245             res = 't';
07246          }
07247       }
07248    }
07249    return res;
07250 }
07251 
07252 static int vm_instructions_tw(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
07253 {
07254    int res = 0;
07255    /* Play instructions and wait for new command */
07256    while (!res) {
07257       if (vms->lastmsg > -1) {
07258          res = ast_play_and_wait(chan, "vm-listen");
07259          if (!res)
07260             res = vm_play_folder_name(chan, vms->vmbox);
07261          if (!res)
07262             res = ast_play_and_wait(chan, "press");
07263          if (!res)
07264             res = ast_play_and_wait(chan, "digits/1");
07265       }
07266       if (!res)
07267          res = ast_play_and_wait(chan, "vm-opts");
07268       if (!res) {
07269          vms->starting = 0;
07270          return vm_instructions_en(chan, vms, skipadvanced);
07271       }
07272    }
07273    return res;
07274 }
07275 
07276 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
07277 {
07278    if (vms->starting && !strcasecmp(chan->language, "tw")) { /* CHINESE (Taiwan) syntax */
07279       return vm_instructions_tw(chan, vms, skipadvanced);
07280    } else {             /* Default to ENGLISH */
07281       return vm_instructions_en(chan, vms, skipadvanced);
07282    }
07283 }
07284 
07285 
07286 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
07287 {
07288    int cmd = 0;
07289    int duration = 0;
07290    int tries = 0;
07291    char newpassword[80] = "";
07292    char newpassword2[80] = "";
07293    char prefile[PATH_MAX] = "";
07294    unsigned char buf[256];
07295    int bytes = 0;
07296 
07297    if (ast_adsi_available(chan)) {
07298       bytes += adsi_logo(buf + bytes);
07299       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
07300       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
07301       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07302       bytes += ast_adsi_voice_mode(buf + bytes, 0);
07303       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07304    }
07305 
07306    /* First, have the user change their password 
07307       so they won't get here again */
07308    for (;;) {
07309       newpassword[1] = '\0';
07310       newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
07311       if (cmd == '#')
07312          newpassword[0] = '\0';
07313       if (cmd < 0 || cmd == 't' || cmd == '#')
07314          return cmd;
07315       cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
07316       if (cmd < 0 || cmd == 't' || cmd == '#')
07317          return cmd;
07318       newpassword2[1] = '\0';
07319       newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
07320       if (cmd == '#')
07321          newpassword2[0] = '\0';
07322       if (cmd < 0 || cmd == 't' || cmd == '#')
07323          return cmd;
07324       cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
07325       if (cmd < 0 || cmd == 't' || cmd == '#')
07326          return cmd;
07327       if (!strcmp(newpassword, newpassword2))
07328          break;
07329       ast_log(LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
07330       cmd = ast_play_and_wait(chan, vm_mismatch);
07331       if (++tries == 3)
07332          return -1;
07333    }
07334    if (pwdchange & PWDCHANGE_INTERNAL)
07335       vm_change_password(vmu, newpassword);
07336    if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
07337       vm_change_password_shell(vmu, newpassword);
07338 
07339    ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int)strlen(newpassword));
07340    cmd = ast_play_and_wait(chan, vm_passchanged);
07341 
07342    /* If forcename is set, have the user record their name */  
07343    if (ast_test_flag(vmu, VM_FORCENAME)) {
07344       snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
07345       if (ast_fileexists(prefile, NULL, NULL) < 1) {
07346          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07347          if (cmd < 0 || cmd == 't' || cmd == '#')
07348             return cmd;
07349       }
07350    }
07351 
07352    /* If forcegreetings is set, have the user record their greetings */
07353    if (ast_test_flag(vmu, VM_FORCEGREET)) {
07354       snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
07355       if (ast_fileexists(prefile, NULL, NULL) < 1) {
07356          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07357          if (cmd < 0 || cmd == 't' || cmd == '#')
07358             return cmd;
07359       }
07360 
07361       snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
07362       if (ast_fileexists(prefile, NULL, NULL) < 1) {
07363          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07364          if (cmd < 0 || cmd == 't' || cmd == '#')
07365             return cmd;
07366       }
07367    }
07368 
07369    return cmd;
07370 }
07371 
07372 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
07373 {
07374    int cmd = 0;
07375    int retries = 0;
07376    int duration = 0;
07377    char newpassword[80] = "";
07378    char newpassword2[80] = "";
07379    char prefile[PATH_MAX] = "";
07380    unsigned char buf[256];
07381    int bytes = 0;
07382 
07383    if (ast_adsi_available(chan)) {
07384       bytes += adsi_logo(buf + bytes);
07385       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
07386       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
07387       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07388       bytes += ast_adsi_voice_mode(buf + bytes, 0);
07389       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07390    }
07391    while ((cmd >= 0) && (cmd != 't')) {
07392       if (cmd)
07393          retries = 0;
07394       switch (cmd) {
07395       case '1':
07396          snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
07397          cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07398          break;
07399       case '2': 
07400          snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
07401          cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07402          break;
07403       case '3': 
07404          snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
07405          cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07406          break;
07407       case '4': 
07408          cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
07409          break;
07410       case '5':
07411          if (vmu->password[0] == '-') {
07412             cmd = ast_play_and_wait(chan, "vm-no");
07413             break;
07414          }
07415          newpassword[1] = '\0';
07416          newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
07417          if (cmd == '#')
07418             newpassword[0] = '\0';
07419          else {
07420             if (cmd < 0)
07421                break;
07422             if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
07423                break;
07424             }
07425          }
07426          newpassword2[1] = '\0';
07427          newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
07428          if (cmd == '#')
07429             newpassword2[0] = '\0';
07430          else {
07431             if (cmd < 0)
07432                break;
07433 
07434             if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
07435                break;
07436             }
07437          }
07438          if (strcmp(newpassword, newpassword2)) {
07439             ast_log(LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
07440             cmd = ast_play_and_wait(chan, vm_mismatch);
07441             break;
07442          }
07443          if (pwdchange & PWDCHANGE_INTERNAL)
07444             vm_change_password(vmu, newpassword);
07445          if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
07446             vm_change_password_shell(vmu, newpassword);
07447 
07448          ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int)strlen(newpassword));
07449          cmd = ast_play_and_wait(chan, vm_passchanged);
07450          break;
07451       case '*': 
07452          cmd = 't';
07453          break;
07454       default: 
07455          cmd = 0;
07456          snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
07457          RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
07458          if (ast_fileexists(prefile, NULL, NULL)) {
07459             cmd = ast_play_and_wait(chan, "vm-tmpexists");
07460          }
07461          DISPOSE(prefile, -1);
07462          if (!cmd) {
07463             cmd = ast_play_and_wait(chan, "vm-options");
07464          }
07465          if (!cmd) {
07466             cmd = ast_waitfordigit(chan, 6000);
07467          }
07468          if (!cmd) {
07469             retries++;
07470          }
07471          if (retries > 3) {
07472             cmd = 't';
07473          }
07474       }
07475    }
07476    if (cmd == 't')
07477       cmd = 0;
07478    return cmd;
07479 }
07480 
07481 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
07482 {
07483    int cmd = 0;
07484    int retries = 0;
07485    int duration = 0;
07486    char prefile[PATH_MAX] = "";
07487    unsigned char buf[256];
07488    int bytes = 0;
07489 
07490    if (ast_adsi_available(chan)) {
07491       bytes += adsi_logo(buf + bytes);
07492       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
07493       bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
07494       bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
07495       bytes += ast_adsi_voice_mode(buf + bytes, 0);
07496       ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
07497    }
07498 
07499    snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
07500    while ((cmd >= 0) && (cmd != 't')) {
07501       if (cmd)
07502          retries = 0;
07503       RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
07504       if (ast_fileexists(prefile, NULL, NULL) <= 0) {
07505          play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07506          cmd = 't';  
07507       } else {
07508          switch (cmd) {
07509          case '1':
07510             cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
07511             break;
07512          case '2':
07513             DELETE(prefile, -1, prefile, vmu);
07514             ast_play_and_wait(chan, "vm-tempremoved");
07515             cmd = 't';  
07516             break;
07517          case '*': 
07518             cmd = 't';
07519             break;
07520          default:
07521             cmd = ast_play_and_wait(chan,
07522                ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
07523                   "vm-tempgreeting2" : "vm-tempgreeting");
07524             if (!cmd)
07525                cmd = ast_waitfordigit(chan, 6000);
07526             if (!cmd)
07527                retries++;
07528             if (retries > 3)
07529                cmd = 't';
07530          }
07531       }
07532       DISPOSE(prefile, -1);
07533    }
07534    if (cmd == 't')
07535       cmd = 0;
07536    return cmd;
07537 }
07538 
07539 /* GREEK SYNTAX */
07540    
07541 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07542 {
07543    int cmd = 0;
07544 
07545    if (vms->lastmsg > -1) {
07546       cmd = play_message(chan, vmu, vms);
07547    } else {
07548       cmd = ast_play_and_wait(chan, "vm-youhaveno");
07549       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")) {
07550          if (!cmd) {
07551             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
07552             cmd = ast_play_and_wait(chan, vms->fn);
07553          }
07554          if (!cmd)
07555             cmd = ast_play_and_wait(chan, "vm-messages");
07556       } else {
07557          if (!cmd)
07558             cmd = ast_play_and_wait(chan, "vm-messages");
07559          if (!cmd) {
07560             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
07561             cmd = ast_play_and_wait(chan, vms->fn);
07562          }
07563       }
07564    } 
07565    return cmd;
07566 }
07567 
07568 /* Default English syntax */
07569 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07570 {
07571    int cmd = 0;
07572 
07573    if (vms->lastmsg > -1) {
07574       cmd = play_message(chan, vmu, vms);
07575    } else {
07576       cmd = ast_play_and_wait(chan, "vm-youhave");
07577       if (!cmd) 
07578          cmd = ast_play_and_wait(chan, "vm-no");
07579       if (!cmd) {
07580          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
07581          cmd = ast_play_and_wait(chan, vms->fn);
07582       }
07583       if (!cmd)
07584          cmd = ast_play_and_wait(chan, "vm-messages");
07585    }
07586    return cmd;
07587 }
07588 
07589 /* ITALIAN syntax */
07590 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07591 {
07592    int cmd = 0;
07593 
07594    if (vms->lastmsg > -1) {
07595       cmd = play_message(chan, vmu, vms);
07596    } else {
07597       cmd = ast_play_and_wait(chan, "vm-no");
07598       if (!cmd)
07599          cmd = ast_play_and_wait(chan, "vm-message");
07600       if (!cmd) {
07601          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
07602          cmd = ast_play_and_wait(chan, vms->fn);
07603       }
07604    }
07605    return cmd;
07606 }
07607 
07608 /* SPANISH syntax */
07609 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07610 {
07611    int cmd = 0;
07612 
07613    if (vms->lastmsg > -1) {
07614       cmd = play_message(chan, vmu, vms);
07615    } else {
07616       cmd = ast_play_and_wait(chan, "vm-youhaveno");
07617       if (!cmd)
07618          cmd = ast_play_and_wait(chan, "vm-messages");
07619       if (!cmd) {
07620          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
07621          cmd = ast_play_and_wait(chan, vms->fn);
07622       }
07623    }
07624    return cmd;
07625 }
07626 
07627 /* PORTUGUESE syntax */
07628 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07629 {
07630    int cmd = 0;
07631 
07632    if (vms->lastmsg > -1) {
07633       cmd = play_message(chan, vmu, vms);
07634    } else {
07635       cmd = ast_play_and_wait(chan, "vm-no");
07636       if (!cmd) {
07637          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
07638          cmd = ast_play_and_wait(chan, vms->fn);
07639       }
07640       if (!cmd)
07641          cmd = ast_play_and_wait(chan, "vm-messages");
07642    }
07643    return cmd;
07644 }
07645 
07646 /* Chinese (Taiwan)syntax */
07647 static int vm_browse_messages_tw(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07648 {
07649    int cmd = 0;
07650 
07651    if (vms->lastmsg > -1) {
07652       cmd = play_message(chan, vmu, vms);
07653    } else {
07654       cmd = ast_play_and_wait(chan, "vm-you");
07655       if (!cmd) 
07656          cmd = ast_play_and_wait(chan, "vm-haveno");
07657       if (!cmd)
07658          cmd = ast_play_and_wait(chan, "vm-messages");
07659       if (!cmd) {
07660          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
07661          cmd = ast_play_and_wait(chan, vms->fn);
07662       }
07663    }
07664    return cmd;
07665 }
07666 
07667 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
07668 {
07669    if (!strcasecmp(chan->language, "es")) {  /* SPANISH */
07670       return vm_browse_messages_es(chan, vms, vmu);
07671    } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
07672       return vm_browse_messages_it(chan, vms, vmu);
07673    } else if (!strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) {   /* PORTUGUESE */
07674       return vm_browse_messages_pt(chan, vms, vmu);
07675    } else if (!strcasecmp(chan->language, "gr")) {
07676       return vm_browse_messages_gr(chan, vms, vmu);   /* GREEK */
07677    } else if (!strcasecmp(chan->language, "tw")) {
07678       return vm_browse_messages_tw(chan, vms, vmu);   /* CHINESE (Taiwan) */
07679    } else { /* Default to English syntax */
07680       return vm_browse_messages_en(chan, vms, vmu);
07681    }
07682 }
07683 
07684 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
07685          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
07686          int skipuser, int maxlogins, int silent)
07687 {
07688    int useadsi = 0, valid = 0, logretries = 0;
07689    char password[AST_MAX_EXTENSION] = "", *passptr;
07690    struct ast_vm_user vmus, *vmu = NULL;
07691 
07692    /* If ADSI is supported, setup login screen */
07693    adsi_begin(chan, &useadsi);
07694    if (!skipuser && useadsi)
07695       adsi_login(chan);
07696    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
07697       ast_log(LOG_WARNING, "Couldn't stream login file\n");
07698       return -1;
07699    }
07700    
07701    /* Authenticate them and get their mailbox/password */
07702    
07703    while (!valid && (logretries < maxlogins)) {
07704       /* Prompt for, and read in the username */
07705       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
07706          ast_log(LOG_WARNING, "Couldn't read username\n");
07707          return -1;
07708       }
07709       if (ast_strlen_zero(mailbox)) {
07710          if (chan->cid.cid_num) {
07711             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
07712          } else {
07713             ast_verb(3, "Username not entered\n"); 
07714             return -1;
07715          }
07716       }
07717       if (useadsi)
07718          adsi_password(chan);
07719 
07720       if (!ast_strlen_zero(prefix)) {
07721          char fullusername[80] = "";
07722          ast_copy_string(fullusername, prefix, sizeof(fullusername));
07723          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
07724          ast_copy_string(mailbox, fullusername, mailbox_size);
07725       }
07726 
07727       ast_debug(1, "Before find user for mailbox %s\n", mailbox);
07728       vmu = find_user(&vmus, context, mailbox);
07729       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
07730          /* saved password is blank, so don't bother asking */
07731          password[0] = '\0';
07732       } else {
07733          if (ast_streamfile(chan, vm_password, chan->language)) {
07734             ast_log(LOG_WARNING, "Unable to stream password file\n");
07735             return -1;
07736          }
07737          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
07738             ast_log(LOG_WARNING, "Unable to read password\n");
07739             return -1;
07740          }
07741       }
07742 
07743       if (vmu) {
07744          passptr = vmu->password;
07745          if (passptr[0] == '-') passptr++;
07746       }
07747       if (vmu && !strcmp(passptr, password))
07748          valid++;
07749       else {
07750          ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
07751          if (!ast_strlen_zero(prefix))
07752             mailbox[0] = '\0';
07753       }
07754       logretries++;
07755       if (!valid) {
07756          if (skipuser || logretries >= maxlogins) {
07757             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
07758                ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
07759                return -1;
07760             }
07761          } else {
07762             if (useadsi)
07763                adsi_login(chan);
07764             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
07765                ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
07766                return -1;
07767             }
07768          }
07769          if (ast_waitstream(chan, "")) /* Channel is hung up */
07770             return -1;
07771       }
07772    }
07773    if (!valid && (logretries >= maxlogins)) {
07774       ast_stopstream(chan);
07775       ast_play_and_wait(chan, "vm-goodbye");
07776       return -1;
07777    }
07778    if (vmu && !skipuser) {
07779       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
07780    }
07781    return 0;
07782 }
07783 
07784 static int vm_execmain(struct ast_channel *chan, void *data)
07785 {
07786    /* XXX This is, admittedly, some pretty horrendous code.  For some
07787       reason it just seemed a lot easier to do with GOTO's.  I feel
07788       like I'm back in my GWBASIC days. XXX */
07789    int res = -1;
07790    int cmd = 0;
07791    int valid = 0;
07792    char prefixstr[80] = "";
07793    char ext_context[256] = "";
07794    int box;
07795    int useadsi = 0;
07796    int skipuser = 0;
07797    struct vm_state vms;
07798    struct ast_vm_user *vmu = NULL, vmus;
07799    char *context = NULL;
07800    int silentexit = 0;
07801    struct ast_flags flags = { 0 };
07802    signed char record_gain = 0;
07803    int play_auto = 0;
07804    int play_folder = 0;
07805 #ifdef IMAP_STORAGE
07806    int deleted = 0;
07807 #endif
07808 
07809    /* Add the vm_state to the active list and keep it active */
07810    memset(&vms, 0, sizeof(vms));
07811    vms.lastmsg = -1;
07812 
07813    memset(&vmus, 0, sizeof(vmus));
07814 
07815    if (chan->_state != AST_STATE_UP) {
07816       ast_debug(1, "Before ast_answer\n");
07817       ast_answer(chan);
07818    }
07819 
07820    if (!ast_strlen_zero(data)) {
07821       char *opts[OPT_ARG_ARRAY_SIZE];
07822       char *parse;
07823       AST_DECLARE_APP_ARGS(args,
07824          AST_APP_ARG(argv0);
07825          AST_APP_ARG(argv1);
07826       );
07827 
07828       parse = ast_strdupa(data);
07829 
07830       AST_STANDARD_APP_ARGS(args, parse);
07831 
07832       if (args.argc == 2) {
07833          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
07834             return -1;
07835          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
07836             int gain;
07837             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
07838                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
07839                   ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
07840                   return -1;
07841                } else {
07842                   record_gain = (signed char) gain;
07843                }
07844             } else {
07845                ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
07846             }
07847          }
07848          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
07849             play_auto = 1;
07850             if (opts[OPT_ARG_PLAYFOLDER]) {
07851                if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%d", &play_folder) != 1) {
07852                   ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
07853                }
07854             } else {
07855                ast_log(LOG_WARNING, "Invalid folder set with option a\n");
07856             }  
07857             if (play_folder > 9 || play_folder < 0) {
07858                ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
07859                play_folder = 0;
07860             }
07861          }
07862       } else {
07863          /* old style options parsing */
07864          while (*(args.argv0)) {
07865             if (*(args.argv0) == 's')
07866                ast_set_flag(&flags, OPT_SILENT);
07867             else if (*(args.argv0) == 'p')
07868                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
07869             else 
07870                break;
07871             (args.argv0)++;
07872          }
07873 
07874       }
07875 
07876       valid = ast_test_flag(&flags, OPT_SILENT);
07877 
07878       if ((context = strchr(args.argv0, '@')))
07879          *context++ = '\0';
07880 
07881       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
07882          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
07883       else
07884          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
07885 
07886       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context, vms.username)))
07887          skipuser++;
07888       else
07889          valid = 0;
07890    }
07891 
07892    if (!valid)
07893       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
07894 
07895    ast_debug(1, "After vm_authenticate\n");
07896    if (!res) {
07897       valid = 1;
07898       if (!skipuser)
07899          vmu = &vmus;
07900    } else {
07901       res = 0;
07902    }
07903 
07904    /* If ADSI is supported, setup login screen */
07905    adsi_begin(chan, &useadsi);
07906 
07907    if (!valid) {
07908       goto out;
07909    }
07910 
07911 #ifdef IMAP_STORAGE
07912    pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
07913    pthread_setspecific(ts_vmstate.key, &vms);
07914 
07915    vms.interactive = 1;
07916    vms.updated = 1;
07917    vmstate_insert(&vms);
07918    init_vm_state(&vms);
07919 #endif
07920    if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
07921       ast_log(LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
07922       cmd = ast_play_and_wait(chan, "an-error-has-occured");
07923       return -1;
07924    }
07925    if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
07926       ast_log(LOG_ERROR, "Could not allocate memory for heard message storage!\n");
07927       cmd = ast_play_and_wait(chan, "an-error-has-occured");
07928       return -1;
07929    }
07930    
07931    /* Set language from config to override channel language */
07932    if (!ast_strlen_zero(vmu->language))
07933       ast_string_field_set(chan, language, vmu->language);
07934    /* Retrieve old and new message counts */
07935    ast_debug(1, "Before open_mailbox\n");
07936    res = open_mailbox(&vms, vmu, OLD_FOLDER);
07937    if (res == ERROR_LOCK_PATH)
07938       goto out;
07939    vms.oldmessages = vms.lastmsg + 1;
07940    ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
07941    /* Start in INBOX */
07942    res = open_mailbox(&vms, vmu, NEW_FOLDER);
07943    if (res == ERROR_LOCK_PATH)
07944       goto out;
07945    vms.newmessages = vms.lastmsg + 1;
07946    ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
07947       
07948    /* Select proper mailbox FIRST!! */
07949    if (play_auto) {
07950       res = open_mailbox(&vms, vmu, play_folder);
07951       if (res == ERROR_LOCK_PATH)
07952          goto out;
07953 
07954       /* If there are no new messages, inform the user and hangup */
07955       if (vms.lastmsg == -1) {
07956          cmd = vm_browse_messages(chan, &vms, vmu);
07957          res = 0;
07958          goto out;
07959       }
07960    } else {
07961       if (!vms.newmessages && vms.oldmessages) {
07962          /* If we only have old messages start here */
07963          res = open_mailbox(&vms, vmu, OLD_FOLDER);
07964          play_folder = 1;
07965          if (res == ERROR_LOCK_PATH)
07966             goto out;
07967       }
07968    }
07969 
07970    if (useadsi)
07971       adsi_status(chan, &vms);
07972    res = 0;
07973 
07974    /* Check to see if this is a new user */
07975    if (!strcasecmp(vmu->mailbox, vmu->password) && 
07976       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
07977       if (ast_play_and_wait(chan, "vm-newuser") == -1)
07978          ast_log(LOG_WARNING, "Couldn't stream new user file\n");
07979       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
07980       if ((cmd == 't') || (cmd == '#')) {
07981          /* Timeout */
07982          res = 0;
07983          goto out;
07984       } else if (cmd < 0) {
07985          /* Hangup */
07986          res = -1;
07987          goto out;
07988       }
07989    }
07990 #ifdef IMAP_STORAGE
07991       ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
07992       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
07993          ast_debug(1, "*** QUOTA EXCEEDED!!\n");
07994          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
07995       }
07996       ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
07997       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
07998          ast_log(LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
07999          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
08000       }
08001 #endif
08002    if (play_auto) {
08003       cmd = '1';
08004    } else {
08005       cmd = vm_intro(chan, vmu, &vms);
08006    }
08007 
08008    vms.repeats = 0;
08009    vms.starting = 1;
08010    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
08011       /* Run main menu */
08012       switch (cmd) {
08013       case '1':
08014          vms.curmsg = 0;
08015          /* Fall through */
08016       case '5':
08017          cmd = vm_browse_messages(chan, &vms, vmu);
08018          break;
08019       case '2': /* Change folders */
08020          if (useadsi)
08021             adsi_folders(chan, 0, "Change to folder...");
08022          cmd = get_folder2(chan, "vm-changeto", 0);
08023          if (cmd == '#') {
08024             cmd = 0;
08025          } else if (cmd > 0) {
08026             cmd = cmd - '0';
08027             res = close_mailbox(&vms, vmu);
08028             if (res == ERROR_LOCK_PATH)
08029                goto out;
08030             res = open_mailbox(&vms, vmu, cmd);
08031             if (res == ERROR_LOCK_PATH)
08032                goto out;
08033             play_folder = cmd;
08034             cmd = 0;
08035          }
08036          if (useadsi)
08037             adsi_status2(chan, &vms);
08038             
08039          if (!cmd)
08040             cmd = vm_play_folder_name(chan, vms.vmbox);
08041 
08042          vms.starting = 1;
08043          break;
08044       case '3': /* Advanced options */
08045          cmd = 0;
08046          vms.repeats = 0;
08047          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
08048             switch (cmd) {
08049             case '1': /* Reply */
08050                if (vms.lastmsg > -1 && !vms.starting) {
08051                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
08052                   if (cmd == ERROR_LOCK_PATH) {
08053                      res = cmd;
08054                      goto out;
08055                   }
08056                } else
08057                   cmd = ast_play_and_wait(chan, "vm-sorry");
08058                cmd = 't';
08059                break;
08060             case '2': /* Callback */
08061                if (!vms.starting)
08062                   ast_verb(3, "Callback Requested\n");
08063                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
08064                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
08065                   if (cmd == 9) {
08066                      silentexit = 1;
08067                      goto out;
08068                   } else if (cmd == ERROR_LOCK_PATH) {
08069                      res = cmd;
08070                      goto out;
08071                   }
08072                } else 
08073                   cmd = ast_play_and_wait(chan, "vm-sorry");
08074                cmd = 't';
08075                break;
08076             case '3': /* Envelope */
08077                if (vms.lastmsg > -1 && !vms.starting) {
08078                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
08079                   if (cmd == ERROR_LOCK_PATH) {
08080                      res = cmd;
08081                      goto out;
08082                   }
08083                } else
08084                   cmd = ast_play_and_wait(chan, "vm-sorry");
08085                cmd = 't';
08086                break;
08087             case '4': /* Dialout */
08088                if (!ast_strlen_zero(vmu->dialout)) {
08089                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
08090                   if (cmd == 9) {
08091                      silentexit = 1;
08092                      goto out;
08093                   }
08094                } else 
08095                   cmd = ast_play_and_wait(chan, "vm-sorry");
08096                cmd = 't';
08097                break;
08098 
08099             case '5': /* Leave VoiceMail */
08100                if (ast_test_flag(vmu, VM_SVMAIL)) {
08101                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
08102                   if (cmd == ERROR_LOCK_PATH) {
08103                      res = cmd;
08104                      ast_log(LOG_WARNING, "forward_message failed to lock path.\n");
08105                      goto out;
08106                   }
08107                } else
08108                   cmd = ast_play_and_wait(chan, "vm-sorry");
08109                cmd = 't';
08110                break;
08111                
08112             case '*': /* Return to main menu */
08113                cmd = 't';
08114                break;
08115 
08116             default:
08117                cmd = 0;
08118                if (!vms.starting) {
08119                   cmd = ast_play_and_wait(chan, "vm-toreply");
08120                }
08121                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
08122                   cmd = ast_play_and_wait(chan, "vm-tocallback");
08123                }
08124                if (!cmd && !vms.starting) {
08125                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
08126                }
08127                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
08128                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
08129                }
08130                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
08131                   cmd = ast_play_and_wait(chan, "vm-leavemsg");
08132                if (!cmd)
08133                   cmd = ast_play_and_wait(chan, "vm-starmain");
08134                if (!cmd)
08135                   cmd = ast_waitfordigit(chan, 6000);
08136                if (!cmd)
08137                   vms.repeats++;
08138                if (vms.repeats > 3)
08139                   cmd = 't';
08140             }
08141          }
08142          if (cmd == 't') {
08143             cmd = 0;
08144             vms.repeats = 0;
08145          }
08146          break;
08147       case '4':
08148          if (vms.curmsg > 0) {
08149             vms.curmsg--;
08150             cmd = play_message(chan, vmu, &vms);
08151          } else {
08152             cmd = ast_play_and_wait(chan, "vm-nomore");
08153          }
08154          break;
08155       case '6':
08156          if (vms.curmsg < vms.lastmsg) {
08157             vms.curmsg++;
08158             cmd = play_message(chan, vmu, &vms);
08159          } else {
08160             cmd = ast_play_and_wait(chan, "vm-nomore");
08161          }
08162          break;
08163       case '7':
08164          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
08165             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
08166             if (useadsi)
08167                adsi_delete(chan, &vms);
08168             if (vms.deleted[vms.curmsg]) {
08169                if (play_folder == 0)
08170                   vms.newmessages--;
08171                else if (play_folder == 1)
08172                   vms.oldmessages--;
08173                cmd = ast_play_and_wait(chan, "vm-deleted");
08174             } else {
08175                if (play_folder == 0)
08176                   vms.newmessages++;
08177                else if (play_folder == 1)
08178                   vms.oldmessages++;
08179                cmd = ast_play_and_wait(chan, "vm-undeleted");
08180             }
08181             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
08182                if (vms.curmsg < vms.lastmsg) {
08183                   vms.curmsg++;
08184                   cmd = play_message(chan, vmu, &vms);
08185                } else {
08186                   cmd = ast_play_and_wait(chan, "vm-nomore");
08187                }
08188             }
08189          } else /* Delete not valid if we haven't selected a message */
08190             cmd = 0;
08191 #ifdef IMAP_STORAGE
08192          deleted = 1;
08193 #endif
08194          break;
08195    
08196       case '8':
08197          if (vms.lastmsg > -1) {
08198             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
08199             if (cmd == ERROR_LOCK_PATH) {
08200                res = cmd;
08201                goto out;
08202             }
08203          } else
08204             cmd = ast_play_and_wait(chan, "vm-nomore");
08205          break;
08206       case '9':
08207          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
08208             /* No message selected */
08209             cmd = 0;
08210             break;
08211          }
08212          if (useadsi)
08213             adsi_folders(chan, 1, "Save to folder...");
08214          cmd = get_folder2(chan, "vm-savefolder", 1);
08215          box = 0; /* Shut up compiler */
08216          if (cmd == '#') {
08217             cmd = 0;
08218             break;
08219          } else if (cmd > 0) {
08220             box = cmd = cmd - '0';
08221             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
08222             if (cmd == ERROR_LOCK_PATH) {
08223                res = cmd;
08224                goto out;
08225 #ifndef IMAP_STORAGE
08226             } else if (!cmd) {
08227                vms.deleted[vms.curmsg] = 1;
08228 #endif
08229             } else {
08230                vms.deleted[vms.curmsg] = 0;
08231                vms.heard[vms.curmsg] = 0;
08232             }
08233          }
08234          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
08235          if (useadsi)
08236             adsi_message(chan, &vms);
08237          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
08238          if (!cmd) {
08239             cmd = ast_play_and_wait(chan, "vm-message");
08240             if (!cmd) 
08241                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
08242             if (!cmd)
08243                cmd = ast_play_and_wait(chan, "vm-savedto");
08244             if (!cmd)
08245                cmd = vm_play_folder_name(chan, vms.fn);
08246          } else {
08247             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
08248          }
08249          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
08250             if (vms.curmsg < vms.lastmsg) {
08251                vms.curmsg++;
08252                cmd = play_message(chan, vmu, &vms);
08253             } else {
08254                cmd = ast_play_and_wait(chan, "vm-nomore");
08255             }
08256          }
08257          break;
08258       case '*':
08259          if (!vms.starting) {
08260             cmd = ast_play_and_wait(chan, "vm-onefor");
08261             if (!cmd)
08262                cmd = vm_play_folder_name(chan, vms.vmbox);
08263             if (!cmd)
08264                cmd = ast_play_and_wait(chan, "vm-opts");
08265             if (!cmd)
08266                cmd = vm_instructions(chan, &vms, 1);
08267          } else
08268             cmd = 0;
08269          break;
08270       case '0':
08271          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
08272          if (useadsi)
08273             adsi_status(chan, &vms);
08274          break;
08275       default: /* Nothing */
08276          cmd = vm_instructions(chan, &vms, 0);
08277          break;
08278       }
08279    }
08280    if ((cmd == 't') || (cmd == '#')) {
08281       /* Timeout */
08282       res = 0;
08283    } else {
08284       /* Hangup */
08285       res = -1;
08286    }
08287 
08288 out:
08289    if (res > -1) {
08290       ast_stopstream(chan);
08291       adsi_goodbye(chan);
08292       if (valid) {
08293          if (silentexit)
08294             res = ast_play_and_wait(chan, "vm-dialout");
08295          else 
08296             res = ast_play_and_wait(chan, "vm-goodbye");
08297          if (res > 0)
08298             res = 0;
08299       }
08300       if (useadsi)
08301          ast_adsi_unload_session(chan);
08302    }
08303    if (vmu)
08304       close_mailbox(&vms, vmu);
08305    if (valid) {
08306       int new = 0, old = 0;
08307       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
08308       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
08309       run_externnotify(vmu->context, vmu->mailbox);
08310       ast_app_inboxcount(ext_context, &new, &old);
08311       queue_mwi_event(ext_context, new, old);
08312    }
08313 #ifdef IMAP_STORAGE
08314    /* expunge message - use UID Expunge if supported on IMAP server*/
08315    ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
08316    if (vmu && deleted == 1 && expungeonhangup == 1) {
08317       ast_mutex_lock(&vms.lock);
08318 #ifdef HAVE_IMAP_TK2006
08319       if (LEVELUIDPLUS (vms.mailstream)) {
08320          mail_expunge_full(vms.mailstream, NIL, EX_UID);
08321       } else 
08322 #endif
08323          mail_expunge(vms.mailstream);
08324       ast_mutex_unlock(&vms.lock);
08325    }
08326    /*  before we delete the state, we should copy pertinent info
08327     *  back to the persistent model */
08328    if (vmu) {
08329       vmstate_delete(&vms);
08330    }
08331 #endif
08332    if (vmu)
08333       free_user(vmu);
08334    if (vms.deleted)
08335       ast_free(vms.deleted);
08336    if (vms.heard)
08337       ast_free(vms.heard);
08338 
08339 #ifdef IMAP_STORAGE
08340    pthread_setspecific(ts_vmstate.key, NULL);
08341 #endif
08342    return res;
08343 }
08344 
08345 static int vm_exec(struct ast_channel *chan, void *data)
08346 {
08347    int res = 0;
08348    char *tmp;
08349    struct leave_vm_options leave_options;
08350    struct ast_flags flags = { 0 };
08351    char *opts[OPT_ARG_ARRAY_SIZE];
08352    AST_DECLARE_APP_ARGS(args,
08353       AST_APP_ARG(argv0);
08354       AST_APP_ARG(argv1);
08355    );
08356    
08357    memset(&leave_options, 0, sizeof(leave_options));
08358 
08359    if (chan->_state != AST_STATE_UP)
08360       ast_answer(chan);
08361 
08362    if (!ast_strlen_zero(data)) {
08363       tmp = ast_strdupa(data);
08364       AST_STANDARD_APP_ARGS(args, tmp);
08365       if (args.argc == 2) {
08366          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
08367             return -1;
08368          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_DTMFEXIT);
08369          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
08370             int gain;
08371 
08372             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
08373                ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
08374                return -1;
08375             } else {
08376                leave_options.record_gain = (signed char) gain;
08377             }
08378          }
08379          if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
08380             if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
08381                leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
08382          }
08383       }
08384    } else {
08385       char tmp[256];
08386       res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
08387       if (res < 0)
08388          return res;
08389       if (ast_strlen_zero(tmp))
08390          return 0;
08391       args.argv0 = ast_strdupa(tmp);
08392    }
08393 
08394    res = leave_voicemail(chan, args.argv0, &leave_options);
08395 
08396    if (res == ERROR_LOCK_PATH) {
08397       ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
08398       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
08399       res = 0;
08400    }
08401 
08402    return res;
08403 }
08404 
08405 static struct ast_vm_user *find_or_create(const char *context, const char *mbox)
08406 {
08407    struct ast_vm_user *vmu;
08408 
08409    AST_LIST_TRAVERSE(&users, vmu, list) {
08410       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox)) {
08411          if (strcasecmp(vmu->context, context)) {
08412             ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
08413                   \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
08414                   \n\tconfiguration creates an ambiguity that you likely do not want. Please\
08415                   \n\tamend your voicemail.conf file to avoid this situation.\n", mbox);
08416          }
08417          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", mbox);
08418          return NULL;
08419       }
08420       if (!strcasecmp(context, vmu->context) && !strcasecmp(mbox, vmu->mailbox)) {
08421          ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", mbox, context);
08422          return NULL;
08423       }
08424    }
08425    
08426    if (!(vmu = ast_calloc(1, sizeof(*vmu))))
08427       return NULL;
08428    
08429    ast_copy_string(vmu->context, context, sizeof(vmu->context));
08430    ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
08431 
08432    AST_LIST_INSERT_TAIL(&users, vmu, list);
08433    
08434    return vmu;
08435 }
08436 
08437 static int append_mailbox(const char *context, const char *mbox, const char *data)
08438 {
08439    /* Assumes lock is already held */
08440    char *tmp;
08441    char *stringp;
08442    char *s;
08443    struct ast_vm_user *vmu;
08444    char *mailbox_full;
08445    int new = 0, old = 0;
08446 
08447    tmp = ast_strdupa(data);
08448 
08449    if (!(vmu = find_or_create(context, mbox)))
08450       return -1;
08451    
08452    populate_defaults(vmu);
08453 
08454    stringp = tmp;
08455    if ((s = strsep(&stringp, ","))) 
08456       ast_copy_string(vmu->password, s, sizeof(vmu->password));
08457    if (stringp && (s = strsep(&stringp, ","))) 
08458       ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
08459    if (stringp && (s = strsep(&stringp, ","))) 
08460       ast_copy_string(vmu->email, s, sizeof(vmu->email));
08461    if (stringp && (s = strsep(&stringp, ","))) 
08462       ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
08463    if (stringp && (s = strsep(&stringp, ","))) 
08464       apply_options(vmu, s);
08465 
08466    mailbox_full = alloca(strlen(mbox) + strlen(context) + 1);
08467    strcpy(mailbox_full, mbox);
08468    strcat(mailbox_full, "@");
08469    strcat(mailbox_full, context);
08470 
08471    inboxcount(mailbox_full, &new, &old);
08472    queue_mwi_event(mailbox_full, new, old);
08473 
08474    return 0;
08475 }
08476 
08477 static int vm_box_exists(struct ast_channel *chan, void *data) 
08478 {
08479    struct ast_vm_user svm;
08480    char *context, *box;
08481    AST_DECLARE_APP_ARGS(args,
08482       AST_APP_ARG(mbox);
08483       AST_APP_ARG(options);
08484    );
08485    static int dep_warning = 0;
08486 
08487    if (ast_strlen_zero(data)) {
08488       ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
08489       return -1;
08490    }
08491 
08492    if (!dep_warning) {
08493       dep_warning = 1;
08494       ast_log(LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
08495    }
08496 
08497    box = ast_strdupa(data);
08498 
08499    AST_STANDARD_APP_ARGS(args, box);
08500 
08501    if (args.options) {
08502    }
08503 
08504    if ((context = strchr(args.mbox, '@'))) {
08505       *context = '\0';
08506       context++;
08507    }
08508 
08509    if (find_user(&svm, context, args.mbox)) {
08510       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
08511    } else
08512       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
08513 
08514    return 0;
08515 }
08516 
08517 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
08518 {
08519    struct ast_vm_user svm;
08520    AST_DECLARE_APP_ARGS(arg,
08521       AST_APP_ARG(mbox);
08522       AST_APP_ARG(context);
08523    );
08524 
08525    AST_NONSTANDARD_APP_ARGS(arg, args, '@');
08526 
08527    if (ast_strlen_zero(arg.mbox)) {
08528       ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
08529       return -1;
08530    }
08531 
08532    ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
08533    return 0;
08534 }
08535 
08536 static struct ast_custom_function mailbox_exists_acf = {
08537    .name = "MAILBOX_EXISTS",
08538    .synopsis = "Tell if a mailbox is configured",
08539    .desc =
08540 "Returns a boolean of whether the corresponding mailbox exists.  If context\n"
08541 "is not specified, defaults to the \"default\" context.\n",
08542    .syntax = "MAILBOX_EXISTS(<vmbox>[@<context>])",
08543    .read = acf_mailbox_exists,
08544 };
08545 
08546 static int vmauthenticate(struct ast_channel *chan, void *data)
08547 {
08548    char *s = data, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
08549    struct ast_vm_user vmus;
08550    char *options = NULL;
08551    int silent = 0, skipuser = 0;
08552    int res = -1;
08553    
08554    if (s) {
08555       s = ast_strdupa(s);
08556       user = strsep(&s, ",");
08557       options = strsep(&s, ",");
08558       if (user) {
08559          s = user;
08560          user = strsep(&s, "@");
08561          context = strsep(&s, "");
08562          if (!ast_strlen_zero(user))
08563             skipuser++;
08564          ast_copy_string(mailbox, user, sizeof(mailbox));
08565       }
08566    }
08567 
08568    if (options) {
08569       silent = (strchr(options, 's')) != NULL;
08570    }
08571 
08572    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
08573       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
08574       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
08575       ast_play_and_wait(chan, "auth-thankyou");
08576       res = 0;
08577    }
08578 
08579    return res;
08580 }
08581 
08582 static char *show_users_realtime(int fd, const char *context)
08583 {
08584    struct ast_config *cfg;
08585    const char *cat = NULL;
08586 
08587    if (!(cfg = ast_load_realtime_multientry("voicemail", 
08588       "context", context, NULL))) {
08589       return CLI_FAILURE;
08590    }
08591 
08592    ast_cli(fd,
08593       "\n"
08594       "=============================================================\n"
08595       "=== Configured Voicemail Users ==============================\n"
08596       "=============================================================\n"
08597       "===\n");
08598 
08599    while ((cat = ast_category_browse(cfg, cat))) {
08600       struct ast_variable *var = NULL;
08601       ast_cli(fd,
08602          "=== Mailbox ...\n"
08603          "===\n");
08604       for (var = ast_variable_browse(cfg, cat); var; var = var->next)
08605          ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
08606       ast_cli(fd,
08607          "===\n"
08608          "=== ---------------------------------------------------------\n"
08609          "===\n");
08610    }
08611 
08612    ast_cli(fd,
08613       "=============================================================\n"
08614       "\n");
08615 
08616    ast_config_destroy(cfg);
08617 
08618    return CLI_SUCCESS;
08619 }
08620 
08621 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
08622 {
08623    int which = 0;
08624    int wordlen;
08625    struct ast_vm_user *vmu;
08626    const char *context = "";
08627 
08628    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
08629    if (pos > 4)
08630       return NULL;
08631    if (pos == 3)
08632       return (state == 0) ? ast_strdup("for") : NULL;
08633    wordlen = strlen(word);
08634    AST_LIST_TRAVERSE(&users, vmu, list) {
08635       if (!strncasecmp(word, vmu->context, wordlen)) {
08636          if (context && strcmp(context, vmu->context) && ++which > state)
08637             return ast_strdup(vmu->context);
08638          /* ignore repeated contexts ? */
08639          context = vmu->context;
08640       }
08641    }
08642    return NULL;
08643 }
08644 
08645 /*! \brief Show a list of voicemail users in the CLI */
08646 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
08647 {
08648    struct ast_vm_user *vmu;
08649 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
08650    const char *context = NULL;
08651    int users_counter = 0;
08652 
08653    switch (cmd) {
08654    case CLI_INIT:
08655       e->command = "voicemail show users";
08656       e->usage =
08657          "Usage: voicemail show users [for <context>]\n"
08658          "       Lists all mailboxes currently set up\n";
08659       return NULL;
08660    case CLI_GENERATE:
08661       return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
08662    }  
08663 
08664    if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
08665       return CLI_SHOWUSAGE;
08666    if (a->argc == 5) {
08667       if (strcmp(a->argv[3], "for"))
08668          return CLI_SHOWUSAGE;
08669       context = a->argv[4];
08670    }
08671 
08672    if (ast_check_realtime("voicemail")) {
08673       if (!context) {
08674          ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
08675          return CLI_SHOWUSAGE;
08676       }
08677       return show_users_realtime(a->fd, context);
08678    }
08679 
08680    AST_LIST_LOCK(&users);
08681    if (AST_LIST_EMPTY(&users)) {
08682       ast_cli(a->fd, "There are no voicemail users currently defined\n");
08683       AST_LIST_UNLOCK(&users);
08684       return CLI_FAILURE;
08685    }
08686    if (a->argc == 3)
08687       ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
08688    else {
08689       int count = 0;
08690       AST_LIST_TRAVERSE(&users, vmu, list) {
08691          if (!strcmp(context, vmu->context))
08692             count++;
08693       }
08694       if (count) {
08695          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
08696       } else {
08697          ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
08698          AST_LIST_UNLOCK(&users);
08699          return CLI_FAILURE;
08700       }
08701    }
08702    AST_LIST_TRAVERSE(&users, vmu, list) {
08703       int newmsgs = 0, oldmsgs = 0;
08704       char count[12], tmp[256] = "";
08705 
08706       if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
08707          snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
08708          inboxcount(tmp, &newmsgs, &oldmsgs);
08709          snprintf(count, sizeof(count), "%d", newmsgs);
08710          ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
08711          users_counter++;
08712       }
08713    }
08714    AST_LIST_UNLOCK(&users);
08715    ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
08716    return CLI_SUCCESS;
08717 }
08718 
08719 /*! \brief Show a list of voicemail zones in the CLI */
08720 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
08721 {
08722    struct vm_zone *zone;
08723 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
08724    char *res = CLI_SUCCESS;
08725 
08726    switch (cmd) {
08727    case CLI_INIT:
08728       e->command = "voicemail show zones";
08729       e->usage =
08730          "Usage: voicemail show zones\n"
08731          "       Lists zone message formats\n";
08732       return NULL;
08733    case CLI_GENERATE:
08734       return NULL;
08735    }
08736 
08737    if (a->argc != e->args)
08738       return CLI_SHOWUSAGE;
08739 
08740    AST_LIST_LOCK(&zones);
08741    if (!AST_LIST_EMPTY(&zones)) {
08742       ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
08743       AST_LIST_TRAVERSE(&zones, zone, list) {
08744          ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
08745       }
08746    } else {
08747       ast_cli(a->fd, "There are no voicemail zones currently defined\n");
08748       res = CLI_FAILURE;
08749    }
08750    AST_LIST_UNLOCK(&zones);
08751 
08752    return res;
08753 }
08754 
08755 /*! \brief Reload voicemail configuration from the CLI */
08756 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
08757 {
08758    switch (cmd) {
08759    case CLI_INIT:
08760       e->command = "voicemail reload";
08761       e->usage =
08762          "Usage: voicemail reload\n"
08763          "       Reload voicemail configuration\n";
08764       return NULL;
08765    case CLI_GENERATE:
08766       return NULL;
08767    }
08768 
08769    if (a->argc != e->args)
08770       return CLI_SHOWUSAGE;
08771 
08772    ast_cli(a->fd, "Reloading voicemail configuration...\n");   
08773    load_config(1);
08774    
08775    return CLI_SUCCESS;
08776 }
08777 
08778 static struct ast_cli_entry cli_voicemail[] = {
08779    AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
08780    AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
08781    AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
08782 };
08783 
08784 static void poll_subscribed_mailboxes(void)
08785 {
08786    struct mwi_sub *mwi_sub;
08787 
08788    AST_RWLIST_RDLOCK(&mwi_subs);
08789    AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
08790       int new = 0, old = 0;
08791 
08792       if (ast_strlen_zero(mwi_sub->mailbox))
08793          continue;
08794 
08795       inboxcount(mwi_sub->mailbox, &new, &old);
08796 
08797       if (new != mwi_sub->old_new || old != mwi_sub->old_old) {
08798          mwi_sub->old_new = new;
08799          mwi_sub->old_old = old;
08800          queue_mwi_event(mwi_sub->mailbox, new, old);
08801       }
08802    }
08803    AST_RWLIST_UNLOCK(&mwi_subs);
08804 }
08805 
08806 static void *mb_poll_thread(void *data)
08807 {
08808    while (poll_thread_run) {
08809       struct timespec ts = { 0, };
08810       struct timeval tv;
08811 
08812       tv = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
08813       ts.tv_sec = tv.tv_sec;
08814       ts.tv_nsec = tv.tv_usec * 1000;
08815 
08816       ast_mutex_lock(&poll_lock);
08817       ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
08818       ast_mutex_unlock(&poll_lock);
08819 
08820       if (!poll_thread_run)
08821          break;
08822 
08823       poll_subscribed_mailboxes();
08824    }
08825 
08826    return NULL;
08827 }
08828 
08829 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
08830 {
08831    ast_free(mwi_sub);
08832 }
08833 
08834 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
08835 {
08836    uint32_t uniqueid;
08837    struct mwi_sub *mwi_sub;
08838 
08839    if (ast_event_get_type(event) != AST_EVENT_UNSUB)
08840       return;
08841 
08842    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
08843       return;
08844 
08845    uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
08846 
08847    AST_RWLIST_WRLOCK(&mwi_subs);
08848    AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
08849       if (mwi_sub->uniqueid == uniqueid) {
08850          AST_LIST_REMOVE_CURRENT(entry);
08851          break;
08852       }
08853    }
08854    AST_RWLIST_TRAVERSE_SAFE_END
08855    AST_RWLIST_UNLOCK(&mwi_subs);
08856 
08857    if (mwi_sub)
08858       mwi_sub_destroy(mwi_sub);
08859 }
08860 
08861 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
08862 {
08863    const char *mailbox;
08864    const char *context;
08865    uint32_t uniqueid;
08866    unsigned int len;
08867    struct mwi_sub *mwi_sub;
08868 
08869    if (ast_event_get_type(event) != AST_EVENT_SUB)
08870       return;
08871 
08872    if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
08873       return;
08874 
08875    mailbox = ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX);
08876    context = ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT);
08877    uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
08878 
08879    len = sizeof(*mwi_sub);
08880    if (!ast_strlen_zero(mailbox))
08881       len += strlen(mailbox);
08882 
08883    if (!ast_strlen_zero(context))
08884       len += strlen(context) + 1; /* Allow for seperator */
08885 
08886    if (!(mwi_sub = ast_calloc(1, len)))
08887       return;
08888 
08889    mwi_sub->uniqueid = uniqueid;
08890    if (!ast_strlen_zero(mailbox))
08891       strcpy(mwi_sub->mailbox, mailbox);
08892 
08893    if (!ast_strlen_zero(context)) {
08894       strcat(mwi_sub->mailbox, "@");
08895       strcat(mwi_sub->mailbox, context);
08896    }
08897 
08898    AST_RWLIST_WRLOCK(&mwi_subs);
08899    AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
08900    AST_RWLIST_UNLOCK(&mwi_subs);
08901 }
08902 
08903 static void start_poll_thread(void)
08904 {
08905    pthread_attr_t attr;
08906 
08907    mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
08908       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
08909       AST_EVENT_IE_END);
08910 
08911    mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
08912       AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
08913       AST_EVENT_IE_END);
08914 
08915    if (mwi_sub_sub)
08916       ast_event_report_subs(mwi_sub_sub);
08917 
08918    poll_thread_run = 1;
08919 
08920    pthread_attr_init(&attr);
08921    ast_pthread_create(&poll_thread, &attr, mb_poll_thread, NULL);
08922    pthread_attr_destroy(&attr);
08923 }
08924 
08925 static void stop_poll_thread(void)
08926 {
08927    poll_thread_run = 0;
08928 
08929    if (mwi_sub_sub) {
08930       ast_event_unsubscribe(mwi_sub_sub);
08931       mwi_sub_sub = NULL;
08932    }
08933 
08934    if (mwi_unsub_sub) {
08935       ast_event_unsubscribe(mwi_unsub_sub);
08936       mwi_unsub_sub = NULL;
08937    }
08938 
08939    ast_mutex_lock(&poll_lock);
08940    ast_cond_signal(&poll_cond);
08941    ast_mutex_unlock(&poll_lock);
08942 
08943    pthread_join(poll_thread, NULL);
08944 
08945    poll_thread = AST_PTHREADT_NULL;
08946 }
08947 
08948 /*! \brief Manager list voicemail users command */
08949 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
08950 {
08951    struct ast_vm_user *vmu = NULL;
08952    const char *id = astman_get_header(m, "ActionID");
08953    char actionid[128] = "";
08954 
08955    if (!ast_strlen_zero(id))
08956       snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
08957 
08958    AST_LIST_LOCK(&users);
08959 
08960    if (AST_LIST_EMPTY(&users)) {
08961       astman_send_ack(s, m, "There are no voicemail users currently defined.");
08962       AST_LIST_UNLOCK(&users);
08963       astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
08964       return RESULT_SUCCESS;
08965    }
08966    
08967    astman_send_ack(s, m, "Voicemail user list will follow");
08968    
08969    AST_LIST_TRAVERSE(&users, vmu, list) {
08970       char dirname[256];
08971 
08972 #ifdef IMAP_STORAGE
08973       int new, old;
08974       inboxcount (vmu->mailbox, &new, &old);
08975 #endif
08976       
08977       make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
08978       astman_append(s,
08979          "%s"
08980          "Event: VoicemailUserEntry\r\n"
08981          "VMContext: %s\r\n"
08982          "VoiceMailbox: %s\r\n"
08983          "Fullname: %s\r\n"
08984          "Email: %s\r\n"
08985          "Pager: %s\r\n"
08986          "ServerEmail: %s\r\n"
08987          "MailCommand: %s\r\n"
08988          "Language: %s\r\n"
08989          "TimeZone: %s\r\n"
08990          "Callback: %s\r\n"
08991          "Dialout: %s\r\n"
08992          "UniqueID: %s\r\n"
08993          "ExitContext: %s\r\n"
08994          "SayDurationMinimum: %d\r\n"
08995          "SayEnvelope: %s\r\n"
08996          "SayCID: %s\r\n"
08997          "AttachMessage: %s\r\n"
08998          "AttachmentFormat: %s\r\n"
08999          "DeleteMessage: %s\r\n"
09000          "VolumeGain: %.2f\r\n"
09001          "CanReview: %s\r\n"
09002          "CallOperator: %s\r\n"
09003          "MaxMessageCount: %d\r\n"
09004          "MaxMessageLength: %d\r\n"
09005          "NewMessageCount: %d\r\n"
09006 #ifdef IMAP_STORAGE
09007          "OldMessageCount: %d\r\n"
09008          "IMAPUser: %s\r\n"
09009 #endif
09010          "\r\n",
09011          actionid,
09012          vmu->context,
09013          vmu->mailbox,
09014          vmu->fullname,
09015          vmu->email,
09016          vmu->pager,
09017          vmu->serveremail,
09018          vmu->mailcmd,
09019          vmu->language,
09020          vmu->zonetag,
09021          vmu->callback,
09022          vmu->dialout,
09023          vmu->uniqueid,
09024          vmu->exit,
09025          vmu->saydurationm,
09026          ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
09027          ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
09028          ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
09029          vmu->attachfmt,
09030          ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
09031          vmu->volgain,
09032          ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
09033          ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
09034          vmu->maxmsg,
09035          vmu->maxsecs,
09036 #ifdef IMAP_STORAGE
09037          new, old, vmu->imapuser
09038 #else
09039          count_messages(vmu, dirname)
09040 #endif
09041          );
09042    }     
09043    astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
09044 
09045    AST_LIST_UNLOCK(&users);
09046 
09047    return RESULT_SUCCESS;
09048 }
09049 
09050 /*! \brief Free the users structure. */
09051 static void free_vm_users(void) 
09052 {
09053    struct ast_vm_user *cur;
09054    AST_LIST_LOCK(&users);
09055    while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
09056       ast_set_flag(cur, VM_ALLOCED);
09057       free_user(cur);
09058    }
09059    AST_LIST_UNLOCK(&users);
09060 }
09061 
09062 /*! \brief Free the zones structure. */
09063 static void free_vm_zones(void)
09064 {
09065    struct vm_zone *zcur;
09066    AST_LIST_LOCK(&zones);
09067    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
09068       free_zone(zcur);
09069    AST_LIST_UNLOCK(&zones);
09070 }
09071 
09072 static char *substitute_escapes(const char *value)
09073 {
09074    char *current, *result;
09075 
09076    /* Add 16 for fudge factor */
09077    struct ast_str *str = ast_str_create(strlen(value) + 16);
09078 
09079    /* Substitute strings \r, \n, and \t into the appropriate characters */
09080    for (current = (char *) value; *current; current++) {
09081       if (*current == '\\') {
09082          current++;
09083          if (!*current) {
09084             ast_log(LOG_NOTICE, "Incomplete escape at end of value.\n");
09085             break;
09086          }
09087          switch (*current) {
09088          case 'r':
09089             ast_str_append(&str, 0, "\r");
09090             break;
09091          case 'n':
09092 #ifdef IMAP_STORAGE
09093             if (!str->used || str->str[str->used - 1] != '\r') {
09094                ast_str_append(&str, 0, "\r");
09095             }
09096 #endif
09097             ast_str_append(&str, 0, "\n");
09098             break;
09099          case 't':
09100             ast_str_append(&str, 0, "\t");
09101             break;
09102          default:
09103             ast_log(LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
09104             break;
09105          }
09106       } else {
09107          ast_str_append(&str, 0, "%c", *current);
09108       }
09109    }
09110 
09111    result = ast_strdup(str->str);
09112    ast_free(str);
09113 
09114    return result;
09115 }
09116 
09117 static int load_config(int reload)
09118 {
09119    struct ast_vm_user *cur;
09120    struct ast_config *cfg, *ucfg;
09121    char *cat;
09122    struct ast_variable *var;
09123    const char *val;
09124    char *q, *stringp;
09125    int x;
09126    int tmpadsi[4];
09127    struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
09128 
09129    if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
09130       if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED)
09131          return 0;
09132       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
09133       cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
09134    } else {
09135       ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
09136       ucfg = ast_config_load("users.conf", config_flags);
09137    }
09138 #ifdef IMAP_STORAGE
09139    ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
09140 #endif
09141    /* set audio control prompts */
09142    strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
09143    strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
09144    strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
09145    strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
09146    strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
09147 
09148    /* Free all the users structure */  
09149    free_vm_users();
09150 
09151    /* Free all the zones structure */
09152    free_vm_zones();
09153 
09154    AST_LIST_LOCK(&users);  
09155 
09156    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
09157 
09158    if (cfg) {
09159       /* General settings */
09160 
09161       if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
09162          val = "default";
09163       ast_copy_string(userscontext, val, sizeof(userscontext));
09164       /* Attach voice message to mail message ? */
09165       if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
09166          val = "yes";
09167       ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH); 
09168 
09169       if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
09170          val = "no";
09171       ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
09172 
09173       volgain = 0.0;
09174       if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
09175          sscanf(val, "%lf", &volgain);
09176 
09177 #ifdef ODBC_STORAGE
09178       strcpy(odbc_database, "asterisk");
09179       if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
09180          ast_copy_string(odbc_database, val, sizeof(odbc_database));
09181       }
09182       strcpy(odbc_table, "voicemessages");
09183       if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
09184          ast_copy_string(odbc_table, val, sizeof(odbc_table));
09185       }
09186 #endif      
09187       /* Mail command */
09188       strcpy(mailcmd, SENDMAIL);
09189       if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
09190          ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
09191 
09192       maxsilence = 0;
09193       if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
09194          maxsilence = atoi(val);
09195          if (maxsilence > 0)
09196             maxsilence *= 1000;
09197       }
09198       
09199       if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
09200          maxmsg = MAXMSG;
09201       } else {
09202          maxmsg = atoi(val);
09203          if (maxmsg <= 0) {
09204             ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
09205             maxmsg = MAXMSG;
09206          } else if (maxmsg > MAXMSGLIMIT) {
09207             ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
09208             maxmsg = MAXMSGLIMIT;
09209          }
09210       }
09211 
09212       if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
09213          maxdeletedmsg = 0;
09214       } else {
09215          if (sscanf(val, "%d", &x) == 1)
09216             maxdeletedmsg = x;
09217          else if (ast_true(val))
09218             maxdeletedmsg = MAXMSG;
09219          else
09220             maxdeletedmsg = 0;
09221 
09222          if (maxdeletedmsg < 0) {
09223             ast_log(LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
09224             maxdeletedmsg = MAXMSG;
09225          } else if (maxdeletedmsg > MAXMSGLIMIT) {
09226             ast_log(LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
09227             maxdeletedmsg = MAXMSGLIMIT;
09228          }
09229       }
09230 
09231       /* Load date format config for voicemail mail */
09232       if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
09233          ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
09234       }
09235 
09236       /* External password changing command */
09237       if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
09238          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
09239          pwdchange = PWDCHANGE_EXTERNAL;
09240       } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
09241          ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
09242          pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
09243       }
09244 
09245 #ifdef IMAP_STORAGE
09246       /* IMAP server address */
09247       if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
09248          ast_copy_string(imapserver, val, sizeof(imapserver));
09249       } else {
09250          ast_copy_string(imapserver, "localhost", sizeof(imapserver));
09251       }
09252       /* IMAP server port */
09253       if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
09254          ast_copy_string(imapport, val, sizeof(imapport));
09255       } else {
09256          ast_copy_string(imapport, "143", sizeof(imapport));
09257       }
09258       /* IMAP server flags */
09259       if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
09260          ast_copy_string(imapflags, val, sizeof(imapflags));
09261       }
09262       /* IMAP server master username */
09263       if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
09264          ast_copy_string(authuser, val, sizeof(authuser));
09265       }
09266       /* IMAP server master password */
09267       if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
09268          ast_copy_string(authpassword, val, sizeof(authpassword));
09269       }
09270       /* Expunge on exit */
09271       if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
09272          if (ast_false(val))
09273             expungeonhangup = 0;
09274          else
09275             expungeonhangup = 1;
09276       } else {
09277          expungeonhangup = 1;
09278       }
09279       /* IMAP voicemail folder */
09280       if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
09281          ast_copy_string(imapfolder, val, sizeof(imapfolder));
09282       } else {
09283          ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
09284       }
09285       if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
09286          ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
09287       }
09288       if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
09289          imapgreetings = ast_true(val);
09290       } else {
09291          imapgreetings = 0;
09292       }
09293       if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
09294          ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
09295       } else {
09296          ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
09297       }
09298 
09299       /* There is some very unorthodox casting done here. This is due
09300        * to the way c-client handles the argument passed in. It expects a 
09301        * void pointer and casts the pointer directly to a long without
09302        * first dereferencing it. */
09303       if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
09304          mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
09305       } else {
09306          mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
09307       }
09308 
09309       if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
09310          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
09311       } else {
09312          mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
09313       }
09314 
09315       if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
09316          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
09317       } else {
09318          mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
09319       }
09320 
09321       if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
09322          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
09323       } else {
09324          mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
09325       }
09326 
09327 #endif
09328       /* External voicemail notify application */
09329       if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
09330          ast_copy_string(externnotify, val, sizeof(externnotify));
09331          ast_debug(1, "found externnotify: %s\n", externnotify);
09332       } else {
09333          externnotify[0] = '\0';
09334       }
09335 
09336       /* SMDI voicemail notification */
09337       if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
09338          ast_debug(1, "Enabled SMDI voicemail notification\n");
09339          if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
09340             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find(val) : NULL;
09341          } else {
09342             ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
09343             smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find("/dev/ttyS0") : NULL;
09344          }
09345          if (!smdi_iface) {
09346             ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
09347          }
09348       }
09349 
09350       /* Silence treshold */
09351       silencethreshold = 256;
09352       if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
09353          silencethreshold = atoi(val);
09354       
09355       if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
09356          val = ASTERISK_USERNAME;
09357       ast_copy_string(serveremail, val, sizeof(serveremail));
09358       
09359       vmmaxsecs = 0;
09360       if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
09361          if (sscanf(val, "%d", &x) == 1) {
09362             vmmaxsecs = x;
09363          } else {
09364             ast_log(LOG_WARNING, "Invalid max message time length\n");
09365          }
09366       } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
09367          static int maxmessage_deprecate = 0;
09368          if (maxmessage_deprecate == 0) {
09369             maxmessage_deprecate = 1;
09370             ast_log(LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
09371          }
09372          if (sscanf(val, "%d", &x) == 1) {
09373             vmmaxsecs = x;
09374          } else {
09375             ast_log(LOG_WARNING, "Invalid max message time length\n");
09376          }
09377       }
09378 
09379       vmminsecs = 0;
09380       if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
09381          if (sscanf(val, "%d", &x) == 1) {
09382             vmminsecs = x;
09383             if (maxsilence <= vmminsecs)
09384                ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
09385          } else {
09386             ast_log(LOG_WARNING, "Invalid min message time length\n");
09387          }
09388       } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
09389          static int maxmessage_deprecate = 0;
09390          if (maxmessage_deprecate == 0) {
09391             maxmessage_deprecate = 1;
09392             ast_log(LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
09393          }
09394          if (sscanf(val, "%d", &x) == 1) {
09395             vmminsecs = x;
09396             if (maxsilence <= vmminsecs)
09397                ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
09398          } else {
09399             ast_log(LOG_WARNING, "Invalid min message time length\n");
09400          }
09401       }
09402 
09403       val = ast_variable_retrieve(cfg, "general", "format");
09404       if (!val)
09405          val = "wav";   
09406       ast_copy_string(vmfmts, val, sizeof(vmfmts));
09407 
09408       skipms = 3000;
09409       if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
09410          if (sscanf(val, "%d", &x) == 1) {
09411             maxgreet = x;
09412          } else {
09413             ast_log(LOG_WARNING, "Invalid max message greeting length\n");
09414          }
09415       }
09416 
09417       if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
09418          if (sscanf(val, "%d", &x) == 1) {
09419             skipms = x;
09420          } else {
09421             ast_log(LOG_WARNING, "Invalid skipms value\n");
09422          }
09423       }
09424 
09425       maxlogins = 3;
09426       if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
09427          if (sscanf(val, "%d", &x) == 1) {
09428             maxlogins = x;
09429          } else {
09430             ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
09431          }
09432       }
09433 
09434       /* Force new user to record name ? */
09435       if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
09436          val = "no";
09437       ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
09438 
09439       /* Force new user to record greetings ? */
09440       if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
09441          val = "no";
09442       ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
09443 
09444       if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
09445          ast_debug(1, "VM_CID Internal context string: %s\n", val);
09446          stringp = ast_strdupa(val);
09447          for (x = 0; x < MAX_NUM_CID_CONTEXTS; x++) {
09448             if (!ast_strlen_zero(stringp)) {
09449                q = strsep(&stringp, ",");
09450                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
09451                   q++;
09452                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
09453                ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
09454             } else {
09455                cidinternalcontexts[x][0] = '\0';
09456             }
09457          }
09458       }
09459       if (!(val = ast_variable_retrieve(cfg, "general", "review"))) {
09460          ast_debug(1, "VM Review Option disabled globally\n");
09461          val = "no";
09462       }
09463       ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW); 
09464 
09465       /* Temporary greeting reminder */
09466       if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
09467          ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
09468          val = "no";
09469       } else {
09470          ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
09471       }
09472       ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
09473 
09474       if (!(val = ast_variable_retrieve(cfg, "general", "operator"))) {
09475          ast_debug(1, "VM Operator break disabled globally\n");
09476          val = "no";
09477       }
09478       ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);  
09479 
09480       if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
09481          ast_debug(1, "VM CID Info before msg disabled globally\n");
09482          val = "no";
09483       } 
09484       ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID); 
09485 
09486       if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))) {
09487          ast_debug(1, "Send Voicemail msg disabled globally\n");
09488          val = "no";
09489       }
09490       ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
09491    
09492       if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
09493          ast_debug(1, "ENVELOPE before msg enabled globally\n");
09494          val = "yes";
09495       }
09496       ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);  
09497 
09498       if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
09499          ast_debug(1, "Move Heard enabled globally\n");
09500          val = "yes";
09501       }
09502       ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD); 
09503 
09504       if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
09505          ast_debug(1, "Duration info before msg enabled globally\n");
09506          val = "yes";
09507       }
09508       ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);  
09509 
09510       saydurationminfo = 2;
09511       if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
09512          if (sscanf(val, "%d", &x) == 1) {
09513             saydurationminfo = x;
09514          } else {
09515             ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
09516          }
09517       }
09518 
09519       if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
09520          ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
09521          val = "no";
09522       }
09523       ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
09524 
09525       if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
09526          ast_copy_string(dialcontext, val, sizeof(dialcontext));
09527          ast_debug(1, "found dialout context: %s\n", dialcontext);
09528       } else {
09529          dialcontext[0] = '\0';  
09530       }
09531       
09532       if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
09533          ast_copy_string(callcontext, val, sizeof(callcontext));
09534          ast_debug(1, "found callback context: %s\n", callcontext);
09535       } else {
09536          callcontext[0] = '\0';
09537       }
09538 
09539       if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
09540          ast_copy_string(exitcontext, val, sizeof(exitcontext));
09541          ast_debug(1, "found operator context: %s\n", exitcontext);
09542       } else {
09543          exitcontext[0] = '\0';
09544       }
09545       
09546       /* load password sounds configuration */
09547       if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
09548          ast_copy_string(vm_password, val, sizeof(vm_password));
09549       if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
09550          ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
09551       if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
09552          ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
09553       if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
09554          ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
09555       if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
09556          ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
09557       /* load configurable audio prompts */
09558       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
09559          ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
09560       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
09561          ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
09562       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
09563          ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
09564       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
09565          ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
09566       if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
09567          ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
09568 
09569       if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
09570          val = "no";
09571       ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD); 
09572 
09573       poll_freq = DEFAULT_POLL_FREQ;
09574       if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
09575          if (sscanf(val, "%u", &poll_freq) != 1) {
09576             poll_freq = DEFAULT_POLL_FREQ;
09577             ast_log(LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
09578          }
09579       }
09580 
09581       poll_mailboxes = 0;
09582       if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
09583          poll_mailboxes = ast_true(val);
09584 
09585       if (ucfg) { 
09586          for (cat = ast_category_browse(ucfg, NULL); cat; cat = ast_category_browse(ucfg, cat)) {
09587             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
09588                continue;
09589             if ((cur = find_or_create(userscontext, cat))) {
09590                populate_defaults(cur);
09591                apply_options_full(cur, ast_variable_browse(ucfg, cat));
09592                ast_copy_string(cur->context, userscontext, sizeof(cur->context));
09593             }
09594          }
09595          ast_config_destroy(ucfg);
09596       }
09597       cat = ast_category_browse(cfg, NULL);
09598       while (cat) {
09599          if (strcasecmp(cat, "general")) {
09600             var = ast_variable_browse(cfg, cat);
09601             if (strcasecmp(cat, "zonemessages")) {
09602                /* Process mailboxes in this context */
09603                while (var) {
09604                   append_mailbox(cat, var->name, var->value);
09605                   var = var->next;
09606                }
09607             } else {
09608                /* Timezones in this context */
09609                while (var) {
09610                   struct vm_zone *z;
09611                   if ((z = ast_malloc(sizeof(*z)))) {
09612                      char *msg_format, *timezone;
09613                      msg_format = ast_strdupa(var->value);
09614                      timezone = strsep(&msg_format, "|");
09615                      if (msg_format) {
09616                         ast_copy_string(z->name, var->name, sizeof(z->name));
09617                         ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
09618                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
09619                         AST_LIST_LOCK(&zones);
09620                         AST_LIST_INSERT_HEAD(&zones, z, list);
09621                         AST_LIST_UNLOCK(&zones);
09622                      } else {
09623                         ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
09624                         ast_free(z);
09625                      }
09626                   } else {
09627                      AST_LIST_UNLOCK(&users);
09628                      ast_config_destroy(cfg);
09629                      return -1;
09630                   }
09631                   var = var->next;
09632                }
09633             }
09634          }
09635          cat = ast_category_browse(cfg, cat);
09636       }
09637       memset(fromstring, 0, sizeof(fromstring));
09638       memset(pagerfromstring, 0, sizeof(pagerfromstring));
09639       strcpy(charset, "ISO-8859-1");
09640       if (emailbody) {
09641          ast_free(emailbody);
09642          emailbody = NULL;
09643       }
09644       if (emailsubject) {
09645          ast_free(emailsubject);
09646          emailsubject = NULL;
09647       }
09648       if (pagerbody) {
09649          ast_free(pagerbody);
09650          pagerbody = NULL;
09651       }
09652       if (pagersubject) {
09653          ast_free(pagersubject);
09654          pagersubject = NULL;
09655       }
09656       if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
09657          ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
09658       if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
09659          ast_copy_string(fromstring, val, sizeof(fromstring));
09660       if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
09661          ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
09662       if ((val = ast_variable_retrieve(cfg, "general", "charset")))
09663          ast_copy_string(charset, val, sizeof(charset));
09664       if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
09665          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
09666          for (x = 0; x < 4; x++) {
09667             memcpy(&adsifdn[x], &tmpadsi[x], 1);
09668          }
09669       }
09670       if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
09671          sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
09672          for (x = 0; x < 4; x++) {
09673             memcpy(&adsisec[x], &tmpadsi[x], 1);
09674          }
09675       }
09676       if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
09677          if (atoi(val)) {
09678             adsiver = atoi(val);
09679          }
09680       }
09681       if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
09682          ast_copy_string(zonetag, val, sizeof(zonetag));
09683       }
09684       if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
09685          emailsubject = ast_strdup(val);
09686       }
09687       if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
09688          emailbody = substitute_escapes(val);
09689       }
09690       if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
09691          pagersubject = ast_strdup(val);
09692       }
09693       if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
09694          pagerbody = substitute_escapes(val);
09695       }
09696       AST_LIST_UNLOCK(&users);
09697       ast_config_destroy(cfg);
09698 
09699       if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
09700          start_poll_thread();
09701       if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
09702          stop_poll_thread();;
09703 
09704       return 0;
09705    } else {
09706       AST_LIST_UNLOCK(&users);
09707       ast_log(LOG_WARNING, "Failed to load configuration file.\n");
09708       if (ucfg)
09709          ast_config_destroy(ucfg);
09710       return 0;
09711    }
09712 }
09713 
09714 static int reload(void)
09715 {
09716    return load_config(1);
09717 }
09718 
09719 static int unload_module(void)
09720 {
09721    int res;
09722 
09723    res = ast_unregister_application(app);
09724    res |= ast_unregister_application(app2);
09725    res |= ast_unregister_application(app3);
09726    res |= ast_unregister_application(app4);
09727    res |= ast_custom_function_unregister(&mailbox_exists_acf);
09728    res |= ast_manager_unregister("VoicemailUsersList");
09729    ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
09730    ast_uninstall_vm_functions();
09731 
09732    if (poll_thread != AST_PTHREADT_NULL)
09733       stop_poll_thread();
09734 
09735 
09736    free_vm_users();
09737    free_vm_zones();
09738    return res;
09739 }
09740 
09741 static int load_module(void)
09742 {
09743    int res;
09744    my_umask = umask(0);
09745    umask(my_umask);
09746 
09747    /* compute the location of the voicemail spool directory */
09748    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
09749 
09750    if ((res = load_config(0)))
09751       return res;
09752 
09753    res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
09754    res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
09755    res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
09756    res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
09757    res |= ast_custom_function_register(&mailbox_exists_acf);
09758    res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
09759    if (res)
09760       return res;
09761 
09762    ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
09763 
09764    ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
09765 
09766    return res;
09767 }
09768 
09769 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
09770 {
09771    int cmd = 0;
09772    char destination[80] = "";
09773    int retries = 0;
09774 
09775    if (!num) {
09776       ast_verb(3, "Destination number will be entered manually\n");
09777       while (retries < 3 && cmd != 't') {
09778          destination[1] = '\0';
09779          destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
09780          if (!cmd)
09781             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
09782          if (!cmd)
09783             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
09784          if (!cmd) {
09785             cmd = ast_waitfordigit(chan, 6000);
09786             if (cmd)
09787                destination[0] = cmd;
09788          }
09789          if (!cmd) {
09790             retries++;
09791          } else {
09792 
09793             if (cmd < 0)
09794                return 0;
09795             if (cmd == '*') {
09796                ast_verb(3, "User hit '*' to cancel outgoing call\n");
09797                return 0;
09798             }
09799             if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination)-1, 6000, 10000, "#")) < 0) 
09800                retries++;
09801             else
09802                cmd = 't';
09803          }
09804       }
09805       if (retries >= 3) {
09806          return 0;
09807       }
09808       
09809    } else {
09810       ast_verb(3, "Destination number is CID number '%s'\n", num);
09811       ast_copy_string(destination, num, sizeof(destination));
09812    }
09813 
09814    if (!ast_strlen_zero(destination)) {
09815       if (destination[strlen(destination) -1 ] == '*')
09816          return 0; 
09817       ast_verb(3, "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
09818       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
09819       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
09820       chan->priority = 0;
09821       return 9;
09822    }
09823    return 0;
09824 }
09825 
09826 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)
09827 {
09828    int res = 0;
09829    char filename[PATH_MAX];
09830    struct ast_config *msg_cfg = NULL;
09831    const char *origtime, *context;
09832    char *name, *num;
09833    int retries = 0;
09834    char *cid;
09835    struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
09836 
09837    vms->starting = 0; 
09838 
09839    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
09840 
09841    /* Retrieve info from VM attribute file */
09842 
09843    make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
09844    snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
09845    RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
09846    msg_cfg = ast_config_load(filename, config_flags);
09847    DISPOSE(vms->curdir, vms->curmsg);
09848    if (!msg_cfg) {
09849       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
09850       return 0;
09851    }
09852 
09853    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
09854       ast_config_destroy(msg_cfg);
09855       return 0;
09856    }
09857 
09858    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
09859 
09860    context = ast_variable_retrieve(msg_cfg, "message", "context");
09861    if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
09862       context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
09863    switch (option) {
09864    case 3:
09865       if (!res)
09866          res = play_message_datetime(chan, vmu, origtime, filename);
09867       if (!res)
09868          res = play_message_callerid(chan, vms, cid, context, 0);
09869 
09870       res = 't';
09871       break;
09872 
09873    case 2:  /* Call back */
09874 
09875       if (ast_strlen_zero(cid))
09876          break;
09877 
09878       ast_callerid_parse(cid, &name, &num);
09879       while ((res > -1) && (res != 't')) {
09880          switch (res) {
09881          case '1':
09882             if (num) {
09883                /* Dial the CID number */
09884                res = dialout(chan, vmu, num, vmu->callback);
09885                if (res) {
09886                   ast_config_destroy(msg_cfg);
09887                   return 9;
09888                }
09889             } else {
09890                res = '2';
09891             }
09892             break;
09893 
09894          case '2':
09895             /* Want to enter a different number, can only do this if there's a dialout context for this user */
09896             if (!ast_strlen_zero(vmu->dialout)) {
09897                res = dialout(chan, vmu, NULL, vmu->dialout);
09898                if (res) {
09899                   ast_config_destroy(msg_cfg);
09900                   return 9;
09901                }
09902             } else {
09903                ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
09904                res = ast_play_and_wait(chan, "vm-sorry");
09905             }
09906             ast_config_destroy(msg_cfg);
09907             return res;
09908          case '*':
09909             res = 't';
09910             break;
09911          case '3':
09912          case '4':
09913          case '5':
09914          case '6':
09915          case '7':
09916          case '8':
09917          case '9':
09918          case '0':
09919 
09920             res = ast_play_and_wait(chan, "vm-sorry");
09921             retries++;
09922             break;
09923          default:
09924             if (num) {
09925                ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
09926                res = ast_play_and_wait(chan, "vm-num-i-have");
09927                if (!res)
09928                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
09929                if (!res)
09930                   res = ast_play_and_wait(chan, "vm-tocallnum");
09931                /* Only prompt for a caller-specified number if there is a dialout context specified */
09932                if (!ast_strlen_zero(vmu->dialout)) {
09933                   if (!res)
09934                      res = ast_play_and_wait(chan, "vm-calldiffnum");
09935                }
09936             } else {
09937                res = ast_play_and_wait(chan, "vm-nonumber");
09938                if (!ast_strlen_zero(vmu->dialout)) {
09939                   if (!res)
09940                      res = ast_play_and_wait(chan, "vm-toenternumber");
09941                }
09942             }
09943             if (!res)
09944                res = ast_play_and_wait(chan, "vm-star-cancel");
09945             if (!res)
09946                res = ast_waitfordigit(chan, 6000);
09947             if (!res) {
09948                retries++;
09949                if (retries > 3)
09950                   res = 't';
09951             }
09952             break; 
09953             
09954          }
09955          if (res == 't')
09956             res = 0;
09957          else if (res == '*')
09958             res = -1;
09959       }
09960       break;
09961       
09962    case 1:  /* Reply */
09963       /* Send reply directly to sender */
09964       if (ast_strlen_zero(cid))
09965          break;
09966 
09967       ast_callerid_parse(cid, &name, &num);
09968       if (!num) {
09969          ast_verb(3, "No CID number available, no reply sent\n");
09970          if (!res)
09971             res = ast_play_and_wait(chan, "vm-nonumber");
09972          ast_config_destroy(msg_cfg);
09973          return res;
09974       } else {
09975          struct ast_vm_user vmu2;
09976          if (find_user(&vmu2, vmu->context, num)) {
09977             struct leave_vm_options leave_options;
09978             char mailbox[AST_MAX_EXTENSION * 2 + 2];
09979             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
09980 
09981             ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
09982             
09983             memset(&leave_options, 0, sizeof(leave_options));
09984             leave_options.record_gain = record_gain;
09985             res = leave_voicemail(chan, mailbox, &leave_options);
09986             if (!res)
09987                res = 't';
09988             ast_config_destroy(msg_cfg);
09989             return res;
09990          } else {
09991             /* Sender has no mailbox, can't reply */
09992             ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
09993             ast_play_and_wait(chan, "vm-nobox");
09994             res = 't';
09995             ast_config_destroy(msg_cfg);
09996             return res;
09997          }
09998       } 
09999       res = 0;
10000 
10001       break;
10002    }
10003 
10004 #ifndef IMAP_STORAGE
10005    ast_config_destroy(msg_cfg);
10006 
10007    if (!res) {
10008       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
10009       vms->heard[msg] = 1;
10010       res = wait_file(chan, vms, vms->fn);
10011    }
10012 #endif
10013    return res;
10014 }
10015 
10016 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
10017          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
10018          signed char record_gain, struct vm_state *vms)
10019 {
10020    /* Record message & let caller review or re-record it, or set options if applicable */
10021    int res = 0;
10022    int cmd = 0;
10023    int max_attempts = 3;
10024    int attempts = 0;
10025    int recorded = 0;
10026    int message_exists = 0;
10027    signed char zero_gain = 0;
10028    char tempfile[PATH_MAX];
10029    char *acceptdtmf = "#";
10030    char *canceldtmf = "";
10031 
10032    /* Note that urgent and private are for flagging messages as such in the future */
10033 
10034    /* barf if no pointer passed to store duration in */
10035    if (duration == NULL) {
10036       ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
10037       return -1;
10038    }
10039 
10040    if (!outsidecaller)
10041       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
10042    else
10043       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
10044 
10045    cmd = '3';  /* Want to start by recording */
10046 
10047    while ((cmd >= 0) && (cmd != 't')) {
10048       switch (cmd) {
10049       case '1':
10050          if (!message_exists) {
10051             /* In this case, 1 is to record a message */
10052             cmd = '3';
10053             break;
10054          } else {
10055             /* Otherwise 1 is to save the existing message */
10056             ast_verb(3, "Saving message as is\n");
10057             if (!outsidecaller) 
10058                ast_filerename(tempfile, recordfile, NULL);
10059             ast_stream_and_wait(chan, "vm-msgsaved", "");
10060             if (!outsidecaller) {
10061                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms);
10062                DISPOSE(recordfile, -1);
10063             }
10064             cmd = 't';
10065             return res;
10066          }
10067       case '2':
10068          /* Review */
10069          ast_verb(3, "Reviewing the message\n");
10070          cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
10071          break;
10072       case '3':
10073          message_exists = 0;
10074          /* Record */
10075          if (recorded == 1) 
10076             ast_verb(3, "Re-recording the message\n");
10077          else  
10078             ast_verb(3, "Recording the message\n");
10079          
10080          if (recorded && outsidecaller) {
10081             cmd = ast_play_and_wait(chan, INTRO);
10082             cmd = ast_play_and_wait(chan, "beep");
10083          }
10084          recorded = 1;
10085          /* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
10086          if (record_gain)
10087             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
10088          if (ast_test_flag(vmu, VM_OPERATOR))
10089             canceldtmf = "0";
10090          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
10091          if (record_gain)
10092             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
10093          if (cmd == -1) {
10094             /* User has hung up, no options to give */
10095             if (!outsidecaller) {
10096                /* user was recording a greeting and they hung up, so let's delete the recording. */
10097                ast_filedelete(tempfile, NULL);
10098             }     
10099             return cmd;
10100          }
10101          if (cmd == '0') {
10102             break;
10103          } else if (cmd == '*') {
10104             break;
10105 #if 0
10106          } else if (vmu->review && (*duration < 5)) {
10107             /* Message is too short */
10108             ast_verb(3, "Message too short\n");
10109             cmd = ast_play_and_wait(chan, "vm-tooshort");
10110             cmd = ast_filedelete(tempfile, NULL);
10111             break;
10112          } else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
10113             /* Message is all silence */
10114             ast_verb(3, "Nothing recorded\n");
10115             cmd = ast_filedelete(tempfile, NULL);
10116             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
10117             if (!cmd)
10118                cmd = ast_play_and_wait(chan, "vm-speakup");
10119             break;
10120 #endif
10121          } else {
10122             /* If all is well, a message exists */
10123             message_exists = 1;
10124             cmd = 0;
10125          }
10126          break;
10127       case '4':
10128       case '5':
10129       case '6':
10130       case '7':
10131       case '8':
10132       case '9':
10133       case '*':
10134       case '#':
10135          cmd = ast_play_and_wait(chan, "vm-sorry");
10136          break;
10137 #if 0 
10138 /*  XXX Commented out for the moment because of the dangers of deleting
10139     a message while recording (can put the message numbers out of sync) */
10140       case '*':
10141          /* Cancel recording, delete message, offer to take another message*/
10142          cmd = ast_play_and_wait(chan, "vm-deleted");
10143          cmd = ast_filedelete(tempfile, NULL);
10144          if (outsidecaller) {
10145             res = vm_exec(chan, NULL);
10146             return res;
10147          }
10148          else
10149             return 1;
10150 #endif
10151       case '0':
10152          if (!ast_test_flag(vmu, VM_OPERATOR)) {
10153             cmd = ast_play_and_wait(chan, "vm-sorry");
10154             break;
10155          }
10156          if (message_exists || recorded) {
10157             cmd = ast_play_and_wait(chan, "vm-saveoper");
10158             if (!cmd)
10159                cmd = ast_waitfordigit(chan, 3000);
10160             if (cmd == '1') {
10161                ast_play_and_wait(chan, "vm-msgsaved");
10162                cmd = '0';
10163             } else {
10164                ast_play_and_wait(chan, "vm-deleted");
10165                DELETE(recordfile, -1, recordfile, vmu);
10166                cmd = '0';
10167             }
10168          }
10169          return cmd;
10170       default:
10171          /* If the caller is an ouside caller, and the review option is enabled,
10172             allow them to review the message, but let the owner of the box review
10173             their OGM's */
10174          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
10175             return cmd;
10176          if (message_exists) {
10177             cmd = ast_play_and_wait(chan, "vm-review");
10178          } else {
10179             cmd = ast_play_and_wait(chan, "vm-torerecord");
10180             if (!cmd)
10181                cmd = ast_waitfordigit(chan, 600);
10182          }
10183          
10184          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
10185             cmd = ast_play_and_wait(chan, "vm-reachoper");
10186             if (!cmd)
10187                cmd = ast_waitfordigit(chan, 600);
10188          }
10189 #if 0
10190          if (!cmd)
10191             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
10192 #endif
10193          if (!cmd)
10194             cmd = ast_waitfordigit(chan, 6000);
10195          if (!cmd) {
10196             attempts++;
10197          }
10198          if (attempts > max_attempts) {
10199             cmd = 't';
10200          }
10201       }
10202    }
10203    if (outsidecaller)
10204       ast_play_and_wait(chan, "vm-goodbye");
10205    if (cmd == 't')
10206       cmd = 0;
10207    return cmd;
10208 }
10209 
10210 /* This is a workaround so that menuselect displays a proper description
10211  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
10212  */
10213 
10214 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
10215       .load = load_module,
10216       .unload = unload_module,
10217       .reload = reload,
10218       );

Generated on Thu Jul 9 13:40:22 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7