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

Generated on Tue Nov 4 13:20:14 2008 for Asterisk - the Open Source PBX by  doxygen 1.4.7