Mon Nov 24 15:34:04 2008

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

Generated on Mon Nov 24 15:34:06 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7