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

Generated on Thu Dec 17 15:35:33 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7