Fri Aug 13 18:20:55 2010

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

Generated on Fri Aug 13 18:20:56 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7