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

Generated on Mon Mar 31 07:37:56 2008 for Asterisk - the Open Source PBX by  doxygen 1.5.1