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

Generated on Thu Oct 1 13:08:39 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7