Fri Apr 24 16:25:48 2009

Asterisk developer's documentation


app_voicemail.c

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

Generated on Fri Apr 24 16:25:49 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7