Fri Feb 19 17:12:34 2010

Asterisk developer's documentation


app_voicemail.c

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

Generated on Fri Feb 19 17:12:35 2010 for Asterisk - the Open Source PBX by  doxygen 1.4.7