Sun Jun 12 16:37:44 2011

Asterisk developer's documentation


app_voicemail_odbc.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, Portuguese & Spanish require the following additional soundfiles:
00261 \arg \b vm-INBOXs singular of 'new'
00262 \arg \b vm-Olds      singular of 'old/heard/read'
00263 
00264 NB these are plural:
00265 \arg \b vm-INBOX  nieuwe (nl)
00266 \arg \b vm-Old    oude (nl)
00267 
00268 Polish uses:
00269 \arg \b vm-new-a  'new', feminine singular accusative
00270 \arg \b vm-new-e  'new', feminine plural accusative
00271 \arg \b vm-new-ych   'new', feminine plural genitive
00272 \arg \b vm-old-a  'old', feminine singular accusative
00273 \arg \b vm-old-e  'old', feminine plural accusative
00274 \arg \b vm-old-ych   'old', feminine plural genitive
00275 \arg \b digits/1-a   'one', not always same as 'digits/1'
00276 \arg \b digits/2-ie  'two', not always same as 'digits/2'
00277 
00278 Swedish uses:
00279 \arg \b vm-nytt      singular of 'new'
00280 \arg \b vm-nya    plural of 'new'
00281 \arg \b vm-gammalt   singular of 'old'
00282 \arg \b vm-gamla  plural of 'old'
00283 \arg \b digits/ett   'one', not always same as 'digits/1'
00284 
00285 Norwegian uses:
00286 \arg \b vm-ny     singular of 'new'
00287 \arg \b vm-nye    plural of 'new'
00288 \arg \b vm-gammel singular of 'old'
00289 \arg \b vm-gamle  plural of 'old'
00290 
00291 Dutch also uses:
00292 \arg \b nl-om     'at'?
00293 
00294 Spanish also uses:
00295 \arg \b vm-youhaveno
00296 
00297 Ukrainian requires the following additional soundfile:
00298 \arg \b vm-nove      'nove'
00299 \arg \b vm-stare  'stare'
00300 \arg \b digits/ua/1e 'odne'
00301 
00302 Italian requires the following additional soundfile:
00303 
00304 For vm_intro_it:
00305 \arg \b vm-nuovo  new
00306 \arg \b vm-nuovi  new plural
00307 \arg \b vm-vecchio   old
00308 \arg \b vm-vecchi old plural
00309 
00310 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
00311 spelled among others when you have to change folder. For the above reasons, vm-INBOX
00312 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
00313 
00314 */
00315 
00316 struct baseio {
00317    int iocp;
00318    int iolen;
00319    int linelength;
00320    int ateof;
00321    unsigned char iobuf[BASEMAXINLINE];
00322 };
00323 
00324 /*! Structure for linked list of users */
00325 struct ast_vm_user {
00326    char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
00327    char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
00328    char password[80];               /*!< Secret pin code, numbers only */
00329    char fullname[80];               /*!< Full name, for directory app */
00330    char email[80];                  /*!< E-mail address */
00331    char pager[80];                  /*!< E-mail address to pager (no attachment) */
00332    char serveremail[80];            /*!< From: Mail address */
00333    char mailcmd[160];               /*!< Configurable mail command */
00334    char language[MAX_LANGUAGE];     /*!< Config: Language setting */
00335    char zonetag[80];                /*!< Time zone */
00336    char callback[80];
00337    char dialout[80];
00338    char uniqueid[80];               /*!< Unique integer identifier */
00339    char exit[80];
00340    char attachfmt[20];              /*!< Attachment format */
00341    unsigned int flags;              /*!< VM_ flags */ 
00342    int saydurationm;
00343    int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
00344 #ifdef IMAP_STORAGE
00345    char imapuser[80];   /* IMAP server login */
00346    char imappassword[80];  /* IMAP server password if authpassword not defined */
00347 #endif
00348    double volgain;      /*!< Volume gain for voicemails sent via email */
00349    AST_LIST_ENTRY(ast_vm_user) list;
00350 };
00351 
00352 struct vm_zone {
00353    AST_LIST_ENTRY(vm_zone) list;
00354    char name[80];
00355    char timezone[80];
00356    char msg_format[512];
00357 };
00358 
00359 struct vm_state {
00360    char curbox[80];
00361    char username[80];
00362    char curdir[PATH_MAX];
00363    char vmbox[PATH_MAX];
00364    char fn[PATH_MAX];
00365    char fn2[PATH_MAX];
00366    int *deleted;
00367    int *heard;
00368    int curmsg;
00369    int lastmsg;
00370    int newmessages;
00371    int oldmessages;
00372    int starting;
00373    int repeats;
00374 #ifdef IMAP_STORAGE
00375    ast_mutex_t lock;
00376    int updated; /* decremented on each mail check until 1 -allows delay */
00377    long msgArray[256];
00378    MAILSTREAM *mailstream;
00379    int vmArrayIndex;
00380    char imapuser[80]; /* IMAP server login */
00381    int interactive;
00382    unsigned int quota_limit;
00383    unsigned int quota_usage;
00384    struct vm_state *persist_vms;
00385 #endif
00386 };
00387 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
00388 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
00389 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
00390          char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
00391          signed char record_gain, struct vm_state *vms);
00392 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
00393 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
00394 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
00395 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
00396 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
00397 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
00398 #endif
00399 static void apply_options(struct ast_vm_user *vmu, const char *options);
00400 
00401 #ifdef ODBC_STORAGE
00402 static char odbc_database[80];
00403 static char odbc_table[80];
00404 #define RETRIEVE(a,b) retrieve_file(a,b)
00405 #define DISPOSE(a,b) remove_file(a,b)
00406 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
00407 #define EXISTS(a,b,c,d) (message_exists(a,b))
00408 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
00409 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
00410 #define DELETE(a,b,c) (delete_file(a,b))
00411 #else
00412 #ifdef IMAP_STORAGE
00413 #define RETRIEVE(a,b)
00414 #define DISPOSE(a,b)
00415 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
00416 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00417 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00418 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
00419 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
00420 #define DELETE(a,b,c) (vm_delete(c))
00421 #else
00422 #define RETRIEVE(a,b)
00423 #define DISPOSE(a,b)
00424 #define STORE(a,b,c,d,e,f,g,h,i)
00425 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
00426 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
00427 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
00428 #define DELETE(a,b,c) (vm_delete(c))
00429 #endif
00430 #endif
00431 
00432 static char VM_SPOOL_DIR[PATH_MAX];
00433 
00434 static char ext_pass_cmd[128];
00435 
00436 int my_umask;
00437 
00438 #if ODBC_STORAGE
00439 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
00440 #elif IMAP_STORAGE
00441 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
00442 #else
00443 #define tdesc "Comedian Mail (Voicemail System)"
00444 #endif
00445 
00446 static char userscontext[AST_MAX_EXTENSION] = "default";
00447 
00448 static char *addesc = "Comedian Mail";
00449 
00450 static char *synopsis_vm =
00451 "Leave a Voicemail message";
00452 
00453 static char *descrip_vm =
00454 "  VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
00455 "application allows the calling party to leave a message for the specified\n"
00456 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
00457 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
00458 "specified mailbox does not exist.\n"
00459 "  The Voicemail application will exit if any of the following DTMF digits are\n"
00460 "received:\n"
00461 "    0 - Jump to the 'o' extension in the current dialplan context.\n"
00462 "    * - Jump to the 'a' extension in the current dialplan context.\n"
00463 "  This application will set the following channel variable upon completion:\n"
00464 "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
00465 "               application. The possible values are:\n"
00466 "               SUCCESS | USEREXIT | FAILED\n\n"
00467 "  Options:\n"
00468 "    b    - Play the 'busy' greeting to the calling party.\n"
00469 "    g(#) - Use the specified amount of gain when recording the voicemail\n"
00470 "           message. The units are whole-number decibels (dB).\n"
00471 "           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 (!strcasecmp(chan->language,"pl"))       /* POLISH syntax */
04342       res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
04343    else if (!strcasecmp(chan->language,"se"))       /* 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 (!strcasecmp(chan->language,"no"))       /* 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 (!strcasecmp(chan->language,"de"))       /* 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 (!strcasecmp(chan->language,"nl"))      /* 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 (!strcasecmp(chan->language,"it"))      /* 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 (!strcasecmp(chan->language,"gr"))
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 (!strcasecmp(chan->language, "pl")) {
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 (!strcasecmp(chan->language, "pl")) { 
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 (!strcasecmp(chan->language, "se")) /* 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 (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* 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 (!strcasecmp(chan->language, "gr")){
05037       return vm_play_folder_name_gr(chan, mbox);
05038    } else if (!strcasecmp(chan->language, "pl")){
05039       return vm_play_folder_name_pl(chan, mbox);
05040    } else if (!strcasecmp(chan->language, "ua")){  /* 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 (!strcasecmp(chan->language, "de")) {  /* GERMAN syntax */
05861       return vm_intro_de(chan, vms);
05862    } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
05863       return vm_intro_es(chan, vms);
05864    } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
05865       return vm_intro_it(chan, vms);
05866    } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
05867       return vm_intro_fr(chan, vms);
05868    } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
05869       return vm_intro_nl(chan, vms);
05870    } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
05871       return vm_intro_pt(chan, vms);
05872    } else if (!strcasecmp(chan->language, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
05873       return vm_intro_pt_BR(chan, vms);      
05874    } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
05875       return vm_intro_cz(chan, vms);
05876    } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
05877       return vm_intro_gr(chan, vms);
05878    } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
05879       return vm_intro_pl(chan, vms);
05880    } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
05881       return vm_intro_se(chan, vms);
05882    } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
05883       return vm_intro_no(chan, vms);
05884    } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
05885       return vm_intro_ru(chan, vms);
05886    } else if (!strcasecmp(chan->language, "ua")) { /* 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 /* GREEK SYNTAX */
06226    
06227 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
06228 {
06229    int cmd=0;
06230 
06231    if (vms->lastmsg > -1) {
06232       cmd = play_message(chan, vmu, vms);
06233    } else {
06234       cmd = ast_play_and_wait(chan, "vm-youhaveno");
06235       if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
06236          if (!cmd) {
06237             snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", 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       } else {
06243          if (!cmd)
06244             cmd = ast_play_and_wait(chan, "vm-messages");
06245          if (!cmd) {
06246             snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
06247             cmd = ast_play_and_wait(chan, vms->fn);
06248          }
06249       }
06250    } 
06251    return cmd;
06252 }
06253 
06254 /* Default English syntax */
06255 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
06256 {
06257    int cmd=0;
06258 
06259    if (vms->lastmsg > -1) {
06260       cmd = play_message(chan, vmu, vms);
06261    } else {
06262       cmd = ast_play_and_wait(chan, "vm-youhave");
06263       if (!cmd) 
06264          cmd = ast_play_and_wait(chan, "vm-no");
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       if (!cmd)
06270          cmd = ast_play_and_wait(chan, "vm-messages");
06271    }
06272    return cmd;
06273 }
06274 
06275 /* ITALIAN syntax */
06276 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
06277 {
06278    int cmd=0;
06279 
06280    if (vms->lastmsg > -1) {
06281       cmd = play_message(chan, vmu, vms);
06282    } else {
06283       cmd = ast_play_and_wait(chan, "vm-no");
06284       if (!cmd)
06285          cmd = ast_play_and_wait(chan, "vm-message");
06286       if (!cmd) {
06287          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
06288          cmd = ast_play_and_wait(chan, vms->fn);
06289       }
06290    }
06291    return cmd;
06292 }
06293 
06294 /* SPANISH syntax */
06295 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
06296 {
06297    int cmd=0;
06298 
06299    if (vms->lastmsg > -1) {
06300       cmd = play_message(chan, vmu, vms);
06301    } else {
06302       cmd = ast_play_and_wait(chan, "vm-youhaveno");
06303       if (!cmd)
06304          cmd = ast_play_and_wait(chan, "vm-messages");
06305       if (!cmd) {
06306          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
06307          cmd = ast_play_and_wait(chan, vms->fn);
06308       }
06309    }
06310    return cmd;
06311 }
06312 
06313 /* PORTUGUESE syntax */
06314 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
06315 {
06316    int cmd=0;
06317 
06318    if (vms->lastmsg > -1) {
06319       cmd = play_message(chan, vmu, vms);
06320    } else {
06321       cmd = ast_play_and_wait(chan, "vm-no");
06322       if (!cmd) {
06323          snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
06324          cmd = ast_play_and_wait(chan, vms->fn);
06325       }
06326       if (!cmd)
06327          cmd = ast_play_and_wait(chan, "vm-messages");
06328    }
06329    return cmd;
06330 }
06331 
06332 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
06333 {
06334    if (!strcasecmp(chan->language, "es")) {  /* SPANISH */
06335       return vm_browse_messages_es(chan, vms, vmu);
06336    } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
06337       return vm_browse_messages_it(chan, vms, vmu);
06338    } else if (!strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) {   /* PORTUGUESE */
06339       return vm_browse_messages_pt(chan, vms, vmu);
06340    } else if (!strcasecmp(chan->language, "gr")){
06341       return vm_browse_messages_gr(chan, vms, vmu);   /* GREEK */
06342    } else { /* Default to English syntax */
06343       return vm_browse_messages_en(chan, vms, vmu);
06344    }
06345 }
06346 
06347 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
06348          struct ast_vm_user *res_vmu, const char *context, const char *prefix,
06349          int skipuser, int maxlogins, int silent)
06350 {
06351    int useadsi=0, valid=0, logretries=0;
06352    char password[AST_MAX_EXTENSION]="", *passptr;
06353    struct ast_vm_user vmus, *vmu = NULL;
06354 
06355    /* If ADSI is supported, setup login screen */
06356    adsi_begin(chan, &useadsi);
06357    if (!skipuser && useadsi)
06358       adsi_login(chan);
06359    if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
06360       ast_log(LOG_WARNING, "Couldn't stream login file\n");
06361       return -1;
06362    }
06363    
06364    /* Authenticate them and get their mailbox/password */
06365    
06366    while (!valid && (logretries < maxlogins)) {
06367       /* Prompt for, and read in the username */
06368       if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
06369          ast_log(LOG_WARNING, "Couldn't read username\n");
06370          return -1;
06371       }
06372       if (ast_strlen_zero(mailbox)) {
06373          if (chan->cid.cid_num) {
06374             ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
06375          } else {
06376             if (option_verbose > 2)
06377                ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");  
06378             return -1;
06379          }
06380       }
06381       if (useadsi)
06382          adsi_password(chan);
06383 
06384       if (!ast_strlen_zero(prefix)) {
06385          char fullusername[80] = "";
06386          ast_copy_string(fullusername, prefix, sizeof(fullusername));
06387          strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
06388          ast_copy_string(mailbox, fullusername, mailbox_size);
06389       }
06390 
06391       if (option_debug)
06392          ast_log(LOG_DEBUG, "Before find user for mailbox %s\n",mailbox);
06393       vmu = find_user(&vmus, context, mailbox);
06394       if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
06395          /* saved password is blank, so don't bother asking */
06396          password[0] = '\0';
06397       } else {
06398          if (ast_streamfile(chan, "vm-password", chan->language)) {
06399             ast_log(LOG_WARNING, "Unable to stream password file\n");
06400             return -1;
06401          }
06402          if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
06403             ast_log(LOG_WARNING, "Unable to read password\n");
06404             return -1;
06405          }
06406       }
06407 
06408       if (vmu) {
06409          passptr = vmu->password;
06410          if (passptr[0] == '-') passptr++;
06411       }
06412       if (vmu && !strcmp(passptr, password))
06413          valid++;
06414       else {
06415          if (option_verbose > 2)
06416             ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
06417          if (!ast_strlen_zero(prefix))
06418             mailbox[0] = '\0';
06419       }
06420       logretries++;
06421       if (!valid) {
06422          if (skipuser || logretries >= maxlogins) {
06423             if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
06424                ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
06425                return -1;
06426             }
06427          } else {
06428             if (useadsi)
06429                adsi_login(chan);
06430             if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
06431                ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
06432                return -1;
06433             }
06434          }
06435          if (ast_waitstream(chan, "")) /* Channel is hung up */
06436             return -1;
06437       }
06438    }
06439    if (!valid && (logretries >= maxlogins)) {
06440       ast_stopstream(chan);
06441       ast_play_and_wait(chan, "vm-goodbye");
06442       return -1;
06443    }
06444    if (vmu && !skipuser) {
06445       memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
06446    }
06447    return 0;
06448 }
06449 
06450 static int vm_execmain(struct ast_channel *chan, void *data)
06451 {
06452    /* XXX This is, admittedly, some pretty horrendus code.  For some
06453       reason it just seemed a lot easier to do with GOTO's.  I feel
06454       like I'm back in my GWBASIC days. XXX */
06455    int res=-1;
06456    int cmd=0;
06457    int valid = 0;
06458    struct ast_module_user *u;
06459    char prefixstr[80] ="";
06460    char ext_context[256]="";
06461    int box;
06462    int useadsi = 0;
06463    int skipuser = 0;
06464    struct vm_state vms;
06465    struct ast_vm_user *vmu = NULL, vmus;
06466    char *context=NULL;
06467    int silentexit = 0;
06468    struct ast_flags flags = { 0 };
06469    signed char record_gain = 0;
06470    int play_auto = 0;
06471    int play_folder = 0;
06472 #ifdef IMAP_STORAGE
06473    int deleted = 0;
06474 #endif
06475    u = ast_module_user_add(chan);
06476 
06477    /* Add the vm_state to the active list and keep it active */
06478    memset(&vms, 0, sizeof(vms));
06479    vms.lastmsg = -1;
06480 
06481    memset(&vmus, 0, sizeof(vmus));
06482 
06483    if (chan->_state != AST_STATE_UP) {
06484       if (option_debug)
06485          ast_log(LOG_DEBUG, "Before ast_answer\n");
06486       ast_answer(chan);
06487    }
06488 
06489    if (!ast_strlen_zero(data)) {
06490       char *opts[OPT_ARG_ARRAY_SIZE];
06491       char *parse;
06492       AST_DECLARE_APP_ARGS(args,
06493          AST_APP_ARG(argv0);
06494          AST_APP_ARG(argv1);
06495       );
06496 
06497       parse = ast_strdupa(data);
06498 
06499       AST_STANDARD_APP_ARGS(args, parse);
06500 
06501       if (args.argc == 2) {
06502          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
06503             ast_module_user_remove(u);
06504             return -1;
06505          }
06506          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
06507             int gain;
06508             if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
06509                if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
06510                   ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
06511                   ast_module_user_remove(u);
06512                   return -1;
06513                } else {
06514                   record_gain = (signed char) gain;
06515                }
06516             } else {
06517                ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
06518             }
06519          }
06520          if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
06521             play_auto = 1;
06522             if (opts[OPT_ARG_PLAYFOLDER]) {
06523                if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%d", &play_folder) != 1) {
06524                   ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
06525                }
06526             } else {
06527                ast_log(LOG_WARNING, "Invalid folder set with option a\n");
06528             }  
06529             if ( play_folder > 9 || play_folder < 0) {
06530                ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
06531                play_folder = 0;
06532             }
06533          }
06534       } else {
06535          /* old style options parsing */
06536          while (*(args.argv0)) {
06537             if (*(args.argv0) == 's')
06538                ast_set_flag(&flags, OPT_SILENT);
06539             else if (*(args.argv0) == 'p')
06540                ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
06541             else 
06542                break;
06543             (args.argv0)++;
06544          }
06545 
06546       }
06547 
06548       valid = ast_test_flag(&flags, OPT_SILENT);
06549 
06550       if ((context = strchr(args.argv0, '@')))
06551          *context++ = '\0';
06552 
06553       if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
06554          ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
06555       else
06556          ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
06557 
06558       if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
06559          skipuser++;
06560       else
06561          valid = 0;
06562    }
06563 
06564    if (!valid)
06565       res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
06566 
06567    if (option_debug)
06568       ast_log(LOG_DEBUG, "After vm_authenticate\n");
06569    if (!res) {
06570       valid = 1;
06571       if (!skipuser)
06572          vmu = &vmus;
06573    } else {
06574       res = 0;
06575    }
06576 
06577    /* If ADSI is supported, setup login screen */
06578    adsi_begin(chan, &useadsi);
06579 
06580 #ifdef IMAP_STORAGE
06581    vms.interactive = 1;
06582    vms.updated = 1;
06583    vmstate_insert(&vms);
06584    init_vm_state(&vms);
06585 #endif
06586    if (!valid)
06587       goto out;
06588 
06589    if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
06590       /* TODO: Handle memory allocation failure */
06591    }
06592    if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
06593       /* TODO: Handle memory allocation failure */
06594    }
06595    
06596    /* Set language from config to override channel language */
06597    if (!ast_strlen_zero(vmu->language))
06598       ast_string_field_set(chan, language, vmu->language);
06599    create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
06600    /* Retrieve old and new message counts */
06601    if (option_debug)
06602       ast_log(LOG_DEBUG, "Before open_mailbox\n");
06603    res = open_mailbox(&vms, vmu, 1);
06604    if (res == ERROR_LOCK_PATH)
06605       goto out;
06606    vms.oldmessages = vms.lastmsg + 1;
06607    if (option_debug > 2)
06608       ast_log(LOG_DEBUG, "Number of old messages: %d\n",vms.oldmessages);
06609    /* Start in INBOX */
06610    res = open_mailbox(&vms, vmu, 0);
06611    if (res == ERROR_LOCK_PATH)
06612       goto out;
06613    vms.newmessages = vms.lastmsg + 1;
06614    if (option_debug > 2)
06615       ast_log(LOG_DEBUG, "Number of new messages: %d\n",vms.newmessages);
06616       
06617    /* Select proper mailbox FIRST!! */
06618    if (play_auto) {
06619       res = open_mailbox(&vms, vmu, play_folder);
06620       if (res == ERROR_LOCK_PATH)
06621          goto out;
06622 
06623       /* If there are no new messages, inform the user and hangup */
06624       if (vms.lastmsg == -1) {
06625          cmd = vm_browse_messages(chan, &vms, vmu);
06626          res = 0;
06627          goto out;
06628       }
06629    } else {
06630       if (!vms.newmessages && vms.oldmessages) {
06631          /* If we only have old messages start here */
06632          res = open_mailbox(&vms, vmu, 1);
06633          play_folder = 1;
06634          if (res == ERROR_LOCK_PATH)
06635             goto out;
06636       }
06637    }
06638 
06639    if (useadsi)
06640       adsi_status(chan, &vms);
06641    res = 0;
06642 
06643    /* Check to see if this is a new user */
06644    if (!strcasecmp(vmu->mailbox, vmu->password) && 
06645       (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
06646       if (ast_play_and_wait(chan, "vm-newuser") == -1)
06647          ast_log(LOG_WARNING, "Couldn't stream new user file\n");
06648       cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
06649       if ((cmd == 't') || (cmd == '#')) {
06650          /* Timeout */
06651          res = 0;
06652          goto out;
06653       } else if (cmd < 0) {
06654          /* Hangup */
06655          res = -1;
06656          goto out;
06657       }
06658    }
06659 #ifdef IMAP_STORAGE
06660       if (option_debug > 2)
06661          ast_log(LOG_DEBUG, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
06662       if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
06663          if (option_debug)
06664             ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
06665          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
06666       }
06667       if (option_debug > 2)
06668          ast_log(LOG_DEBUG, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
06669       if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
06670          ast_log(LOG_WARNING, "No more messages possible.  User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
06671          cmd = ast_play_and_wait(chan, "vm-mailboxfull");
06672       }
06673 #endif
06674    if (play_auto) {
06675       cmd = '1';
06676    } else {
06677       cmd = vm_intro(chan, vmu, &vms);
06678    }
06679 
06680    vms.repeats = 0;
06681    vms.starting = 1;
06682    while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
06683       /* Run main menu */
06684       switch (cmd) {
06685       case '1':
06686          vms.curmsg = 0;
06687          /* Fall through */
06688       case '5':
06689          cmd = vm_browse_messages(chan, &vms, vmu);
06690          break;
06691       case '2': /* Change folders */
06692          if (useadsi)
06693             adsi_folders(chan, 0, "Change to folder...");
06694          cmd = get_folder2(chan, "vm-changeto", 0);
06695          if (cmd == '#') {
06696             cmd = 0;
06697          } else if (cmd > 0) {
06698             cmd = cmd - '0';
06699             res = close_mailbox(&vms, vmu);
06700             if (res == ERROR_LOCK_PATH)
06701                goto out;
06702             res = open_mailbox(&vms, vmu, cmd);
06703             if (res == ERROR_LOCK_PATH)
06704                goto out;
06705             play_folder = cmd;
06706             cmd = 0;
06707          }
06708          if (useadsi)
06709             adsi_status2(chan, &vms);
06710             
06711          if (!cmd)
06712             cmd = vm_play_folder_name(chan, vms.vmbox);
06713 
06714          vms.starting = 1;
06715          break;
06716       case '3': /* Advanced options */
06717          cmd = 0;
06718          vms.repeats = 0;
06719          while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
06720             switch (cmd) {
06721             case '1': /* Reply */
06722                if (vms.lastmsg > -1 && !vms.starting) {
06723                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
06724                   if (cmd == ERROR_LOCK_PATH) {
06725                      res = cmd;
06726                      goto out;
06727                   }
06728                } else
06729                   cmd = ast_play_and_wait(chan, "vm-sorry");
06730                cmd = 't';
06731                break;
06732             case '2': /* Callback */
06733                if (option_verbose > 2 && !vms.starting)
06734                   ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
06735                if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
06736                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
06737                   if (cmd == 9) {
06738                      silentexit = 1;
06739                      goto out;
06740                   } else if (cmd == ERROR_LOCK_PATH) {
06741                      res = cmd;
06742                      goto out;
06743                   }
06744                }
06745                else 
06746                   cmd = ast_play_and_wait(chan, "vm-sorry");
06747                cmd = 't';
06748                break;
06749             case '3': /* Envelope */
06750                if (vms.lastmsg > -1 && !vms.starting) {
06751                   cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
06752                   if (cmd == ERROR_LOCK_PATH) {
06753                      res = cmd;
06754                      goto out;
06755                   }
06756                } else
06757                   cmd = ast_play_and_wait(chan, "vm-sorry");
06758                cmd = 't';
06759                break;
06760             case '4': /* Dialout */
06761                if (!ast_strlen_zero(vmu->dialout)) {
06762                   cmd = dialout(chan, vmu, NULL, vmu->dialout);
06763                   if (cmd == 9) {
06764                      silentexit = 1;
06765                      goto out;
06766                   }
06767                }
06768                else 
06769                   cmd = ast_play_and_wait(chan, "vm-sorry");
06770                cmd = 't';
06771                break;
06772 
06773             case '5': /* Leave VoiceMail */
06774                if (ast_test_flag(vmu, VM_SVMAIL)) {
06775                   cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
06776                   if (cmd == ERROR_LOCK_PATH) {
06777                      res = cmd;
06778                      ast_log(LOG_WARNING, "forward_message failed to lock path.\n");
06779                      goto out;
06780                   }
06781                } else
06782                   cmd = ast_play_and_wait(chan,"vm-sorry");
06783                cmd='t';
06784                break;
06785                
06786             case '*': /* Return to main menu */
06787                cmd = 't';
06788                break;
06789 
06790             default:
06791                cmd = 0;
06792                if (!vms.starting) {
06793                   cmd = ast_play_and_wait(chan, "vm-toreply");
06794                }
06795                if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
06796                   cmd = ast_play_and_wait(chan, "vm-tocallback");
06797                }
06798                if (!cmd && !vms.starting) {
06799                   cmd = ast_play_and_wait(chan, "vm-tohearenv");
06800                }
06801                if (!ast_strlen_zero(vmu->dialout) && !cmd) {
06802                   cmd = ast_play_and_wait(chan, "vm-tomakecall");
06803                }
06804                if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
06805                   cmd=ast_play_and_wait(chan, "vm-leavemsg");
06806                if (!cmd)
06807                   cmd = ast_play_and_wait(chan, "vm-starmain");
06808                if (!cmd)
06809                   cmd = ast_waitfordigit(chan,6000);
06810                if (!cmd)
06811                   vms.repeats++;
06812                if (vms.repeats > 3)
06813                   cmd = 't';
06814             }
06815          }
06816          if (cmd == 't') {
06817             cmd = 0;
06818             vms.repeats = 0;
06819          }
06820          break;
06821       case '4':
06822          if (vms.curmsg > 0) {
06823             vms.curmsg--;
06824             cmd = play_message(chan, vmu, &vms);
06825          } else {
06826             cmd = ast_play_and_wait(chan, "vm-nomore");
06827          }
06828          break;
06829       case '6':
06830          if (vms.curmsg < vms.lastmsg) {
06831             vms.curmsg++;
06832             cmd = play_message(chan, vmu, &vms);
06833          } else {
06834             cmd = ast_play_and_wait(chan, "vm-nomore");
06835          }
06836          break;
06837       case '7':
06838          if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
06839             vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
06840             if (useadsi)
06841                adsi_delete(chan, &vms);
06842             if (vms.deleted[vms.curmsg]) {
06843                if (play_folder == 0)
06844                   vms.newmessages--;
06845                else if (play_folder == 1)
06846                   vms.oldmessages--;
06847                cmd = ast_play_and_wait(chan, "vm-deleted");
06848             }
06849             else {
06850                if (play_folder == 0)
06851                   vms.newmessages++;
06852                else if (play_folder == 1)
06853                   vms.oldmessages++;
06854                cmd = ast_play_and_wait(chan, "vm-undeleted");
06855             }
06856             if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
06857                if (vms.curmsg < vms.lastmsg) {
06858                   vms.curmsg++;
06859                   cmd = play_message(chan, vmu, &vms);
06860                } else {
06861                   cmd = ast_play_and_wait(chan, "vm-nomore");
06862                }
06863             }
06864          } else /* Delete not valid if we haven't selected a message */
06865             cmd = 0;
06866 #ifdef IMAP_STORAGE
06867          deleted = 1;
06868 #endif
06869          break;
06870    
06871       case '8':
06872          if (vms.lastmsg > -1) {
06873             cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
06874             if (cmd == ERROR_LOCK_PATH) {
06875                res = cmd;
06876                goto out;
06877             }
06878          } else
06879             cmd = ast_play_and_wait(chan, "vm-nomore");
06880          break;
06881       case '9':
06882          if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
06883             /* No message selected */
06884             cmd = 0;
06885             break;
06886          }
06887          if (useadsi)
06888             adsi_folders(chan, 1, "Save to folder...");
06889          cmd = get_folder2(chan, "vm-savefolder", 1);
06890          box = 0; /* Shut up compiler */
06891          if (cmd == '#') {
06892             cmd = 0;
06893             break;
06894          } else if (cmd > 0) {
06895             box = cmd = cmd - '0';
06896             cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
06897             if (cmd == ERROR_LOCK_PATH) {
06898                res = cmd;
06899                goto out;
06900 #ifdef IMAP_STORAGE
06901             } else if (cmd == 10) {
06902                goto out;
06903 #endif
06904             } else if (!cmd) {
06905                vms.deleted[vms.curmsg] = 1;
06906             } else {
06907                vms.deleted[vms.curmsg] = 0;
06908                vms.heard[vms.curmsg] = 0;
06909             }
06910          }
06911          make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
06912          if (useadsi)
06913             adsi_message(chan, &vms);
06914          snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
06915          if (!cmd) {
06916             cmd = ast_play_and_wait(chan, "vm-message");
06917             if (!cmd)
06918                cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
06919             if (!cmd)
06920                cmd = ast_play_and_wait(chan, "vm-savedto");
06921             if (!cmd)
06922                cmd = vm_play_folder_name(chan, vms.fn);
06923          } else {
06924             cmd = ast_play_and_wait(chan, "vm-mailboxfull");
06925          }
06926          if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
06927             if (vms.curmsg < vms.lastmsg) {
06928                vms.curmsg++;
06929                cmd = play_message(chan, vmu, &vms);
06930             } else {
06931                cmd = ast_play_and_wait(chan, "vm-nomore");
06932             }
06933          }
06934          break;
06935       case '*':
06936          if (!vms.starting) {
06937             cmd = ast_play_and_wait(chan, "vm-onefor");
06938             if (!cmd)
06939                cmd = vm_play_folder_name(chan, vms.vmbox);
06940             if (!cmd)
06941                cmd = ast_play_and_wait(chan, "vm-opts");
06942             if (!cmd)
06943                cmd = vm_instructions(chan, &vms, 1);
06944          } else
06945             cmd = 0;
06946          break;
06947       case '0':
06948          cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
06949          if (useadsi)
06950             adsi_status(chan, &vms);
06951          break;
06952       default: /* Nothing */
06953          cmd = vm_instructions(chan, &vms, 0);
06954          break;
06955       }
06956    }
06957    if ((cmd == 't') || (cmd == '#')) {
06958       /* Timeout */
06959       res = 0;
06960    } else {
06961       /* Hangup */
06962       res = -1;
06963    }
06964 
06965 out:
06966    if (res > -1) {
06967       ast_stopstream(chan);
06968       adsi_goodbye(chan);
06969       if (valid) {
06970          if (silentexit)
06971             res = ast_play_and_wait(chan, "vm-dialout");
06972          else 
06973             res = ast_play_and_wait(chan, "vm-goodbye");
06974          if (res > 0)
06975             res = 0;
06976       }
06977       if (useadsi)
06978          ast_adsi_unload_session(chan);
06979    }
06980    if (vmu)
06981       close_mailbox(&vms, vmu);
06982    if (valid) {
06983       snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
06984       manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
06985       run_externnotify(vmu->context, vmu->mailbox);
06986    }
06987 #ifdef IMAP_STORAGE
06988    /* expunge message - use UID Expunge if supported on IMAP server*/
06989    if (option_debug > 2)
06990       ast_log(LOG_DEBUG, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
06991    if (vmu && deleted == 1 && expungeonhangup == 1) {
06992 #ifdef HAVE_IMAP_TK2006
06993       if (LEVELUIDPLUS (vms.mailstream)) {
06994          mail_expunge_full(vms.mailstream,NIL,EX_UID);
06995       } else 
06996 #endif
06997          mail_expunge(vms.mailstream);
06998    }
06999    /*  before we delete the state, we should copy pertinent info
07000     *  back to the persistent model */
07001    vmstate_delete(&vms);
07002 #endif
07003    if (vmu)
07004       free_user(vmu);
07005    if (vms.deleted)
07006       free(vms.deleted);
07007    if (vms.heard)
07008       free(vms.heard);
07009    ast_module_user_remove(u);
07010 
07011    return res;
07012 }
07013 
07014 static int vm_exec(struct ast_channel *chan, void *data)
07015 {
07016    int res = 0;
07017    struct ast_module_user *u;
07018    char *tmp;
07019    struct leave_vm_options leave_options;
07020    struct ast_flags flags = { 0 };
07021    static int deprecate_warning = 0;
07022    char *opts[OPT_ARG_ARRAY_SIZE];
07023    AST_DECLARE_APP_ARGS(args,
07024       AST_APP_ARG(argv0);
07025       AST_APP_ARG(argv1);
07026    );
07027 
07028    u = ast_module_user_add(chan);
07029    
07030    memset(&leave_options, 0, sizeof(leave_options));
07031 
07032    if (chan->_state != AST_STATE_UP)
07033       ast_answer(chan);
07034 
07035    if (!ast_strlen_zero(data)) {
07036       tmp = ast_strdupa(data);
07037       AST_STANDARD_APP_ARGS(args, tmp);
07038       if (args.argc == 2) {
07039          if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
07040             ast_module_user_remove(u);
07041             return -1;
07042          }
07043          ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP);
07044          if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
07045             int gain;
07046 
07047             if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
07048                ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
07049                ast_module_user_remove(u);
07050                return -1;
07051             } else {
07052                leave_options.record_gain = (signed char) gain;
07053             }
07054          }
07055       } else {
07056          /* old style options parsing */
07057          int old = 0;
07058          char *orig_argv0 = args.argv0;
07059          while (*(args.argv0)) {
07060             if (*(args.argv0) == 's') {
07061                old = 1;
07062                ast_set_flag(&leave_options, OPT_SILENT);
07063             } else if (*(args.argv0) == 'b') {
07064                old = 1;
07065                ast_set_flag(&leave_options, OPT_BUSY_GREETING);
07066             } else if (*(args.argv0) == 'u') {
07067                old = 1;
07068                ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
07069             } else if (*(args.argv0) == 'j') {
07070                old = 1;
07071                ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
07072             } else
07073                break;
07074             (args.argv0)++;
07075          }
07076          if (!deprecate_warning && old) {
07077             deprecate_warning = 1;
07078             ast_log(LOG_WARNING, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0);
07079             ast_log(LOG_WARNING, "Please move all leading options to the second argument.\n");
07080          }
07081       }
07082    } else {
07083       char tmp[256];
07084       res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
07085       if (res < 0) {
07086          ast_module_user_remove(u);
07087          return res;
07088       }
07089       if (ast_strlen_zero(tmp)) {
07090          ast_module_user_remove(u);
07091          return 0;
07092       }
07093       args.argv0 = ast_strdupa(tmp);
07094    }
07095 
07096    res = leave_voicemail(chan, args.argv0, &leave_options);
07097 
07098    if (res == ERROR_LOCK_PATH) {
07099       ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
07100       /*Send the call to n+101 priority, where n is the current priority*/
07101       if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
07102          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
07103             ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
07104       pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
07105       res = 0;
07106    }
07107    
07108    ast_module_user_remove(u);
07109 
07110    return res;
07111 }
07112 
07113 static struct ast_vm_user *find_or_create(char *context, char *mbox)
07114 {
07115    struct ast_vm_user *vmu;
07116    AST_LIST_TRAVERSE(&users, vmu, list) {
07117       if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox))
07118          break;
07119       if (context && (!strcasecmp(context, vmu->context)) && (!strcasecmp(mbox, vmu->mailbox)))
07120          break;
07121    }
07122    
07123    if (!vmu) {
07124       if ((vmu = ast_calloc(1, sizeof(*vmu)))) {
07125          ast_copy_string(vmu->context, context, sizeof(vmu->context));
07126          ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
07127          AST_LIST_INSERT_TAIL(&users, vmu, list);
07128       }
07129    }
07130    return vmu;
07131 }
07132 
07133 static int append_mailbox(char *context, char *mbox, char *data)
07134 {
07135    /* Assumes lock is already held */
07136    char *tmp;
07137    char *stringp;
07138    char *s;
07139    struct ast_vm_user *vmu;
07140 
07141    tmp = ast_strdupa(data);
07142 
07143    if ((vmu = find_or_create(context, mbox))) {
07144       populate_defaults(vmu);
07145 
07146       stringp = tmp;
07147       if ((s = strsep(&stringp, ","))) 
07148          ast_copy_string(vmu->password, s, sizeof(vmu->password));
07149       if (stringp && (s = strsep(&stringp, ","))) 
07150          ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
07151       if (stringp && (s = strsep(&stringp, ","))) 
07152          ast_copy_string(vmu->email, s, sizeof(vmu->email));
07153       if (stringp && (s = strsep(&stringp, ","))) 
07154          ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
07155       if (stringp && (s = strsep(&stringp, ","))) 
07156          apply_options(vmu, s);
07157    }
07158    return 0;
07159 }
07160 
07161 static int vm_box_exists(struct ast_channel *chan, void *data) 
07162 {
07163    struct ast_module_user *u;
07164    struct ast_vm_user svm;
07165    char *context, *box;
07166    int priority_jump = 0;
07167    AST_DECLARE_APP_ARGS(args,
07168       AST_APP_ARG(mbox);
07169       AST_APP_ARG(options);
07170    );
07171 
07172    if (ast_strlen_zero(data)) {
07173       ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
07174       return -1;
07175    }
07176 
07177    u = ast_module_user_add(chan);
07178 
07179    box = ast_strdupa(data);
07180 
07181    AST_STANDARD_APP_ARGS(args, box);
07182 
07183    if (args.options) {
07184       if (strchr(args.options, 'j'))
07185          priority_jump = 1;
07186    }
07187 
07188    if ((context = strchr(args.mbox, '@'))) {
07189       *context = '\0';
07190       context++;
07191    }
07192 
07193    if (find_user(&svm, context, args.mbox)) {
07194       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
07195       if (priority_jump || ast_opt_priority_jumping)
07196          if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101)) 
07197             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);
07198    } else
07199       pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
07200    ast_module_user_remove(u);
07201    return 0;
07202 }
07203 
07204 static int vmauthenticate(struct ast_channel *chan, void *data)
07205 {
07206    struct ast_module_user *u;
07207    char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
07208    struct ast_vm_user vmus;
07209    char *options = NULL;
07210    int silent = 0, skipuser = 0;
07211    int res = -1;
07212 
07213    u = ast_module_user_add(chan);
07214    
07215    if (s) {
07216       s = ast_strdupa(s);
07217       user = strsep(&s, "|");
07218       options = strsep(&s, "|");
07219       if (user) {
07220          s = user;
07221          user = strsep(&s, "@");
07222          context = strsep(&s, "");
07223          if (!ast_strlen_zero(user))
07224             skipuser++;
07225          ast_copy_string(mailbox, user, sizeof(mailbox));
07226       }
07227    }
07228 
07229    if (options) {
07230       silent = (strchr(options, 's')) != NULL;
07231    }
07232 
07233    if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
07234       pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
07235       pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
07236       ast_play_and_wait(chan, "auth-thankyou");
07237       res = 0;
07238    }
07239 
07240    ast_module_user_remove(u);
07241    return res;
07242 }
07243 
07244 static char voicemail_show_users_help[] =
07245 "Usage: voicemail show users [for <context>]\n"
07246 "       Lists all mailboxes currently set up\n";
07247 
07248 static char voicemail_show_zones_help[] =
07249 "Usage: voicemail show zones\n"
07250 "       Lists zone message formats\n";
07251 
07252 static int handle_voicemail_show_users(int fd, int argc, char *argv[])
07253 {
07254    struct ast_vm_user *vmu;
07255    char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
07256 
07257    if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
07258    else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
07259 
07260    AST_LIST_LOCK(&users);
07261    if (!AST_LIST_EMPTY(&users)) {
07262       if (argc == 3)
07263          ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
07264       else {
07265          int count = 0;
07266          AST_LIST_TRAVERSE(&users, vmu, list) {
07267             if (!strcmp(argv[4],vmu->context))
07268                count++;
07269          }
07270          if (count) {
07271             ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
07272          } else {
07273             ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
07274             AST_LIST_UNLOCK(&users);
07275             return RESULT_FAILURE;
07276          }
07277       }
07278       AST_LIST_TRAVERSE(&users, vmu, list) {
07279          int newmsgs = 0, oldmsgs = 0;
07280          char count[12], tmp[256] = "";
07281 
07282          if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
07283             snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
07284             inboxcount(tmp, &newmsgs, &oldmsgs);
07285             snprintf(count,sizeof(count),"%d",newmsgs);
07286             ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
07287          }
07288       }
07289    } else {
07290       ast_cli(fd, "There are no voicemail users currently defined\n");
07291       AST_LIST_UNLOCK(&users);
07292       return RESULT_FAILURE;
07293    }
07294    AST_LIST_UNLOCK(&users);
07295    return RESULT_SUCCESS;
07296 }
07297 
07298 static int handle_voicemail_show_zones(int fd, int argc, char *argv[])
07299 {
07300    struct vm_zone *zone;
07301    char *output_format = "%-15s %-20s %-45s\n";
07302    int res = RESULT_SUCCESS;
07303 
07304    if (argc != 3)
07305       return RESULT_SHOWUSAGE;
07306 
07307    AST_LIST_LOCK(&zones);
07308    if (!AST_LIST_EMPTY(&zones)) {
07309       ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
07310       AST_LIST_TRAVERSE(&zones, zone, list) {
07311          ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
07312       }
07313    } else {
07314       ast_cli(fd, "There are no voicemail zones currently defined\n");
07315       res = RESULT_FAILURE;
07316    }
07317    AST_LIST_UNLOCK(&zones);
07318 
07319    return res;
07320 }
07321 
07322 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
07323 {
07324    int which = 0;
07325    int wordlen;
07326    struct ast_vm_user *vmu;
07327    const char *context = "";
07328 
07329    /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
07330    if (pos > 4)
07331       return NULL;
07332    if (pos == 3)
07333       return (state == 0) ? ast_strdup("for") : NULL;
07334    wordlen = strlen(word);
07335    AST_LIST_TRAVERSE(&users, vmu, list) {
07336       if (!strncasecmp(word, vmu->context, wordlen)) {
07337          if (context && strcmp(context, vmu->context) && ++which > state)
07338             return ast_strdup(vmu->context);
07339          /* ignore repeated contexts ? */
07340          context = vmu->context;
07341       }
07342    }
07343    return NULL;
07344 }
07345 
07346 static struct ast_cli_entry cli_show_voicemail_users_deprecated = {
07347    { "show", "voicemail", "users", NULL },
07348    handle_voicemail_show_users, NULL,
07349    NULL, complete_voicemail_show_users };
07350 
07351 static struct ast_cli_entry cli_show_voicemail_zones_deprecated = {
07352    { "show", "voicemail", "zones", NULL },
07353    handle_voicemail_show_zones, NULL,
07354    NULL, NULL };
07355 
07356 static struct ast_cli_entry cli_voicemail[] = {
07357    { { "voicemail", "show", "users", NULL },
07358    handle_voicemail_show_users, "List defined voicemail boxes",
07359    voicemail_show_users_help, complete_voicemail_show_users, &cli_show_voicemail_users_deprecated },
07360 
07361    { { "voicemail", "show", "zones", NULL },
07362    handle_voicemail_show_zones, "List zone message formats",
07363    voicemail_show_zones_help, NULL, &cli_show_voicemail_zones_deprecated },
07364 };
07365 
07366 static int load_config(void)
07367 {
07368    struct ast_vm_user *cur;
07369    struct vm_zone *zcur;
07370    struct ast_config *cfg, *ucfg;
07371    char *cat;
07372    struct ast_variable *var;
07373    const char *notifystr = NULL;
07374    const char *smdistr = NULL;
07375    const char *astattach;
07376    const char *astsearch;
07377    const char *astsaycid;
07378    const char *send_voicemail;
07379 #ifdef IMAP_STORAGE
07380    const char *imap_server;
07381    const char *imap_port;
07382    const char *imap_flags;
07383    const char *imap_folder;
07384    const char *auth_user;
07385    const char *auth_password;
07386    const char *expunge_on_hangup;
07387 #endif
07388    const char *astcallop;
07389    const char *astreview;
07390    const char *asttempgreetwarn;
07391    const char *astskipcmd;
07392    const char *asthearenv;
07393    const char *astsaydurationinfo;
07394    const char *astsaydurationminfo;
07395    const char *silencestr;
07396    const char *maxmsgstr;
07397    const char *astdirfwd;
07398    const char *thresholdstr;
07399    const char *fmt;
07400    const char *astemail;
07401    const char *ucontext;
07402    const char *astmailcmd = SENDMAIL;
07403    const char *astforcename;
07404    const char *astforcegreet;
07405    const char *s;
07406    char *q,*stringp;
07407    const char *dialoutcxt = NULL;
07408    const char *callbackcxt = NULL;  
07409    const char *exitcxt = NULL;   
07410    const char *extpc;
07411    const char *emaildateformatstr;
07412    const char *volgainstr;
07413    int x;
07414    int tmpadsi[4];
07415 
07416    cfg = ast_config_load(VOICEMAIL_CONFIG);
07417 
07418    AST_LIST_LOCK(&users);
07419    while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
07420       ast_set_flag(cur, VM_ALLOCED);
07421       free_user(cur);
07422    }
07423 
07424    AST_LIST_LOCK(&zones);
07425    while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list))) 
07426       free_zone(zcur);
07427    AST_LIST_UNLOCK(&zones);
07428 
07429    memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
07430 
07431    if (cfg) {
07432       /* General settings */
07433 
07434       if (!(ucontext = ast_variable_retrieve(cfg, "general", "userscontext")))
07435          ucontext = "default";
07436       ast_copy_string(userscontext, ucontext, sizeof(userscontext));
07437       /* Attach voice message to mail message ? */
07438       if (!(astattach = ast_variable_retrieve(cfg, "general", "attach"))) 
07439          astattach = "yes";
07440       ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH); 
07441 
07442       if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
07443          astsearch = "no";
07444       ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
07445 
07446       volgain = 0.0;
07447       if ((volgainstr = ast_variable_retrieve(cfg, "general", "volgain")))
07448          sscanf(volgainstr, "%lf", &volgain);
07449 
07450 #ifdef ODBC_STORAGE
07451       strcpy(odbc_database, "asterisk");
07452       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
07453          ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
07454       }
07455       strcpy(odbc_table, "voicemessages");
07456       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
07457          ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
07458       }
07459 #endif      
07460       /* Mail command */
07461       strcpy(mailcmd, SENDMAIL);
07462       if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
07463          ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
07464 
07465       maxsilence = 0;
07466       if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
07467          maxsilence = atoi(silencestr);
07468          if (maxsilence > 0)
07469             maxsilence *= 1000;
07470       }
07471       
07472       if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
07473          maxmsg = MAXMSG;
07474       } else {
07475          maxmsg = atoi(maxmsgstr);
07476          if (maxmsg <= 0) {
07477             ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
07478             maxmsg = MAXMSG;
07479          } else if (maxmsg > MAXMSGLIMIT) {
07480             ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
07481             maxmsg = MAXMSGLIMIT;
07482          }
07483       }
07484 
07485       /* Load date format config for voicemail mail */
07486       if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
07487          ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
07488       }
07489 
07490       /* External password changing command */
07491       if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
07492          ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
07493       }
07494 #ifdef IMAP_STORAGE
07495       /* IMAP server address */
07496       if ((imap_server = ast_variable_retrieve(cfg, "general", "imapserver"))) {
07497          ast_copy_string(imapserver, imap_server, sizeof(imapserver));
07498       } else {
07499          ast_copy_string(imapserver,"localhost", sizeof(imapserver));
07500       }
07501       /* IMAP server port */
07502       if ((imap_port = ast_variable_retrieve(cfg, "general", "imapport"))) {
07503          ast_copy_string(imapport, imap_port, sizeof(imapport));
07504       } else {
07505          ast_copy_string(imapport,"143", sizeof(imapport));
07506       }
07507       /* IMAP server flags */
07508       if ((imap_flags = ast_variable_retrieve(cfg, "general", "imapflags"))) {
07509          ast_copy_string(imapflags, imap_flags, sizeof(imapflags));
07510       }
07511       /* IMAP server master username */
07512       if ((auth_user = ast_variable_retrieve(cfg, "general", "authuser"))) {
07513          ast_copy_string(authuser, auth_user, sizeof(authuser));
07514       }
07515       /* IMAP server master password */
07516       if ((auth_password = ast_variable_retrieve(cfg, "general", "authpassword"))) {
07517          ast_copy_string(authpassword, auth_password, sizeof(authpassword));
07518       }
07519       /* Expunge on exit */
07520       if ((expunge_on_hangup = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
07521          if (ast_false(expunge_on_hangup))
07522             expungeonhangup = 0;
07523          else
07524             expungeonhangup = 1;
07525       } else {
07526          expungeonhangup = 1;
07527       }
07528       /* IMAP voicemail folder */
07529       if ((imap_folder = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
07530          ast_copy_string(imapfolder, imap_folder, sizeof(imapfolder));
07531       } else {
07532          ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
07533       }
07534 #endif
07535       /* External voicemail notify application */
07536       
07537       if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
07538          ast_copy_string(externnotify, notifystr, sizeof(externnotify));
07539          if (option_debug > 2)
07540             ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
07541          if (!strcasecmp(externnotify, "smdi")) {
07542             if (option_debug)
07543                ast_log(LOG_DEBUG, "Using SMDI for external voicemail notification\n");
07544             if ((smdistr = ast_variable_retrieve(cfg, "general", "smdiport"))) {
07545                smdi_iface = ast_smdi_interface_find(smdistr);
07546             } else {
07547                if (option_debug)
07548                   ast_log(LOG_DEBUG, "No SMDI interface set, trying default (/dev/ttyS0)\n");
07549                smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
07550             }
07551 
07552             if (!smdi_iface) {
07553                ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n");
07554                externnotify[0] = '\0';
07555             }
07556          }
07557       } else {
07558          externnotify[0] = '\0';
07559       }
07560 
07561       /* Silence treshold */
07562       silencethreshold = 256;
07563       if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
07564          silencethreshold = atoi(thresholdstr);
07565       
07566       if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail"))) 
07567          astemail = ASTERISK_USERNAME;
07568       ast_copy_string(serveremail, astemail, sizeof(serveremail));
07569       
07570       vmmaxmessage = 0;
07571       if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
07572          if (sscanf(s, "%d", &x) == 1) {
07573             vmmaxmessage = x;
07574          } else {
07575             ast_log(LOG_WARNING, "Invalid max message time length\n");
07576          }
07577       }
07578 
07579       vmminmessage = 0;
07580       if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
07581          if (sscanf(s, "%d", &x) == 1) {
07582             vmminmessage = x;
07583             if (maxsilence <= vmminmessage)
07584                ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
07585          } else {
07586             ast_log(LOG_WARNING, "Invalid min message time length\n");
07587          }
07588       }
07589       fmt = ast_variable_retrieve(cfg, "general", "format");
07590       if (!fmt)
07591          fmt = "wav";   
07592       ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
07593 
07594       skipms = 3000;
07595       if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
07596          if (sscanf(s, "%d", &x) == 1) {
07597             maxgreet = x;
07598          } else {
07599             ast_log(LOG_WARNING, "Invalid max message greeting length\n");
07600          }
07601       }
07602 
07603       if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
07604          if (sscanf(s, "%d", &x) == 1) {
07605             skipms = x;
07606          } else {
07607             ast_log(LOG_WARNING, "Invalid skipms value\n");
07608          }
07609       }
07610 
07611       maxlogins = 3;
07612       if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
07613          if (sscanf(s, "%d", &x) == 1) {
07614             maxlogins = x;
07615          } else {
07616             ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
07617          }
07618       }
07619 
07620       /* Force new user to record name ? */
07621       if (!(astforcename = ast_variable_retrieve(cfg, "general", "forcename"))) 
07622          astforcename = "no";
07623       ast_set2_flag((&globalflags), ast_true(astforcename), VM_FORCENAME);
07624 
07625       /* Force new user to record greetings ? */
07626       if (!(astforcegreet = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
07627          astforcegreet = "no";
07628       ast_set2_flag((&globalflags), ast_true(astforcegreet), VM_FORCEGREET);
07629 
07630       if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
07631          if (option_debug > 2)
07632             ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
07633          stringp = ast_strdupa(s);
07634          for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
07635             if (!ast_strlen_zero(stringp)) {
07636                q = strsep(&stringp,",");
07637                while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
07638                   q++;
07639                ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
07640                if (option_debug > 2)
07641                   ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
07642             } else {
07643                cidinternalcontexts[x][0] = '\0';
07644             }
07645          }
07646       }
07647       if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
07648          if (option_debug)
07649             ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
07650          astreview = "no";
07651       }
07652       ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW); 
07653 
07654       /*Temperary greeting reminder */
07655       if (!(asttempgreetwarn = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
07656          if (option_debug)
07657             ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option disabled globally\n");
07658          asttempgreetwarn = "no";
07659       } else {
07660          if (option_debug)
07661             ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option enabled globally\n");
07662       }
07663       ast_set2_flag((&globalflags), ast_true(asttempgreetwarn), VM_TEMPGREETWARN);
07664 
07665       if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
07666          if (option_debug)
07667             ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
07668          astcallop = "no";
07669       }
07670       ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);  
07671 
07672       if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
07673          if (option_debug)
07674             ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
07675          astsaycid = "no";
07676       } 
07677       ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID); 
07678 
07679       if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
07680          if (option_debug)
07681             ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
07682          send_voicemail = "no";
07683       }
07684       ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
07685    
07686       if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
07687          if (option_debug)
07688             ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
07689          asthearenv = "yes";
07690       }
07691       ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE); 
07692 
07693       if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
07694          if (option_debug)
07695             ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
07696          astsaydurationinfo = "yes";
07697       }
07698       ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);  
07699 
07700       saydurationminfo = 2;
07701       if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
07702          if (sscanf(astsaydurationminfo, "%d", &x) == 1) {
07703             saydurationminfo = x;
07704          } else {
07705             ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
07706          }
07707       }
07708 
07709       if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
07710          if (option_debug)
07711             ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
07712          astskipcmd = "no";
07713       }
07714       ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
07715 
07716       if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
07717          ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
07718          if (option_debug)
07719             ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
07720       } else {
07721          dialcontext[0] = '\0';  
07722       }
07723       
07724       if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
07725          ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
07726          if (option_debug)
07727             ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
07728       } else {
07729          callcontext[0] = '\0';
07730       }
07731 
07732       if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
07733          ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
07734          if (option_debug)
07735             ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
07736       } else {
07737          exitcontext[0] = '\0';
07738       }
07739 
07740       if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
07741          astdirfwd = "no";
07742       ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD); 
07743       if ((ucfg = ast_config_load("users.conf"))) {   
07744          for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
07745             if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
07746                continue;
07747             if ((cur = find_or_create(userscontext, cat))) {
07748                populate_defaults(cur);
07749                apply_options_full(cur, ast_variable_browse(ucfg, cat));
07750                ast_copy_string(cur->context, userscontext, sizeof(cur->context));
07751             }
07752          }
07753          ast_config_destroy(ucfg);
07754       }
07755       cat = ast_category_browse(cfg, NULL);
07756       while (cat) {
07757          if (strcasecmp(cat, "general")) {
07758             var = ast_variable_browse(cfg, cat);
07759             if (strcasecmp(cat, "zonemessages")) {
07760                /* Process mailboxes in this context */
07761                while (var) {
07762                   append_mailbox(cat, var->name, var->value);
07763                   var = var->next;
07764                }
07765             } else {
07766                /* Timezones in this context */
07767                while (var) {
07768                   struct vm_zone *z;
07769                   if ((z = ast_malloc(sizeof(*z)))) {
07770                      char *msg_format, *timezone;
07771                      msg_format = ast_strdupa(var->value);
07772                      timezone = strsep(&msg_format, "|");
07773                      if (msg_format) {
07774                         ast_copy_string(z->name, var->name, sizeof(z->name));
07775                         ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
07776                         ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
07777                         AST_LIST_LOCK(&zones);
07778                         AST_LIST_INSERT_HEAD(&zones, z, list);
07779                         AST_LIST_UNLOCK(&zones);
07780                      } else {
07781                         ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
07782                         free(z);
07783                      }
07784                   } else {
07785                      free(z);
07786                      AST_LIST_UNLOCK(&users);
07787                      ast_config_destroy(cfg);
07788                      return -1;
07789                   }
07790                   var = var->next;
07791                }
07792             }
07793          }
07794          cat = ast_category_browse(cfg, cat);
07795       }
07796       memset(fromstring,0,sizeof(fromstring));
07797       memset(pagerfromstring,0,sizeof(pagerfromstring));
07798       memset(emailtitle,0,sizeof(emailtitle));
07799       strcpy(charset, "ISO-8859-1");
07800       if (emailbody) {
07801          free(emailbody);
07802          emailbody = NULL;
07803       }
07804       if (emailsubject) {
07805          free(emailsubject);
07806          emailsubject = NULL;
07807       }
07808       if (pagerbody) {
07809          free(pagerbody);
07810          pagerbody = NULL;
07811       }
07812       if (pagersubject) {
07813          free(pagersubject);
07814          pagersubject = NULL;
07815       }
07816       if ((s = ast_variable_retrieve(cfg, "general", "pbxskip")))
07817          ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
07818       if ((s = ast_variable_retrieve(cfg, "general", "fromstring")))
07819          ast_copy_string(fromstring,s,sizeof(fromstring));
07820       if ((s = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
07821          ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
07822       if ((s = ast_variable_retrieve(cfg, "general", "charset")))
07823          ast_copy_string(charset,s,sizeof(charset));
07824       if ((s = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
07825          sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
07826          for (x = 0; x < 4; x++) {
07827             memcpy(&adsifdn[x], &tmpadsi[x], 1);
07828          }
07829       }
07830       if ((s = ast_variable_retrieve(cfg, "general", "adsisec"))) {
07831          sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
07832          for (x = 0; x < 4; x++) {
07833             memcpy(&adsisec[x], &tmpadsi[x], 1);
07834          }
07835       }
07836       if ((s = ast_variable_retrieve(cfg, "general", "adsiver")))
07837          if (atoi(s)) {
07838             adsiver = atoi(s);
07839          }
07840       if ((s = ast_variable_retrieve(cfg, "general", "emailtitle"))) {
07841          ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
07842          ast_copy_string(emailtitle,s,sizeof(emailtitle));
07843       }
07844       if ((s = ast_variable_retrieve(cfg, "general", "emailsubject")))
07845          emailsubject = ast_strdup(s);
07846       if ((s = ast_variable_retrieve(cfg, "general", "emailbody"))) {
07847          char *tmpread, *tmpwrite;
07848          emailbody = ast_strdup(s);
07849 
07850          /* substitute strings \t and \n into the appropriate characters */
07851          tmpread = tmpwrite = emailbody;
07852          while ((tmpwrite = strchr(tmpread,'\\'))) {
07853             switch (tmpwrite[1]) {
07854             case 'r':
07855                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
07856                *tmpwrite = '\r';
07857                break;
07858             case 'n':
07859                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
07860                *tmpwrite = '\n';
07861                break;
07862             case 't':
07863                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
07864                *tmpwrite = '\t';
07865                break;
07866             default:
07867                ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
07868             }
07869             tmpread = tmpwrite + 1;
07870          }
07871       }
07872       if ((s = ast_variable_retrieve(cfg, "general", "pagersubject")))
07873          pagersubject = ast_strdup(s);
07874       if ((s = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
07875          char *tmpread, *tmpwrite;
07876          pagerbody = ast_strdup(s);
07877 
07878          /* substitute strings \t and \n into the appropriate characters */
07879          tmpread = tmpwrite = pagerbody;
07880          while ((tmpwrite = strchr(tmpread, '\\'))) {
07881             switch (tmpwrite[1]) {
07882             case 'r':
07883                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
07884                *tmpwrite = '\r';
07885                break;
07886             case 'n':
07887                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
07888                *tmpwrite = '\n';
07889                break;
07890             case 't':
07891                memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
07892                *tmpwrite = '\t';
07893                break;
07894             default:
07895                ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
07896             }
07897             tmpread = tmpwrite + 1;
07898          }
07899       }
07900       AST_LIST_UNLOCK(&users);
07901       ast_config_destroy(cfg);
07902       return 0;
07903    } else {
07904       AST_LIST_UNLOCK(&users);
07905       ast_log(LOG_WARNING, "Failed to load configuration file.\n");
07906       return 0;
07907    }
07908 }
07909 
07910 static int reload(void)
07911 {
07912    return(load_config());
07913 }
07914 
07915 static int unload_module(void)
07916 {
07917    int res;
07918    
07919    res = ast_unregister_application(app);
07920    res |= ast_unregister_application(app2);
07921    res |= ast_unregister_application(app3);
07922    res |= ast_unregister_application(app4);
07923    ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
07924    ast_uninstall_vm_functions();
07925    
07926    ast_module_user_hangup_all();
07927 
07928    return res;
07929 }
07930 
07931 static int load_module(void)
07932 {
07933    int res;
07934    char *adsi_loaded = ast_module_helper("", "res_adsi.so", 0, 0, 0, 0);
07935    free(adsi_loaded);
07936    if (!adsi_loaded) {
07937       /* If embedded, res_adsi may be known as "res_adsi" not "res_adsi.so" */
07938       adsi_loaded = ast_module_helper("", "res_adsi", 0, 0, 0, 0);
07939       ast_free(adsi_loaded);
07940       if (!adsi_loaded) {
07941          ast_log(LOG_ERROR, "app_voicemail.so depends upon res_adsi.so\n");
07942          return AST_MODULE_LOAD_DECLINE;
07943       }
07944    }
07945 
07946    my_umask = umask(0);
07947    umask(my_umask);
07948    res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
07949    res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
07950    res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
07951    res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
07952    if (res)
07953       return(res);
07954 
07955    if ((res=load_config())) {
07956       return(res);
07957    }
07958 
07959    ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
07960 
07961    /* compute the location of the voicemail spool directory */
07962    snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
07963 
07964    ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
07965 
07966    return res;
07967 }
07968 
07969 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
07970 {
07971    int cmd = 0;
07972    char destination[80] = "";
07973    int retries = 0;
07974 
07975    if (!num) {
07976       if (option_verbose > 2)
07977          ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
07978       while (retries < 3 && cmd != 't') {
07979          destination[1] = '\0';
07980          destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
07981          if (!cmd)
07982             destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
07983          if (!cmd)
07984             destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
07985          if (!cmd) {
07986             cmd = ast_waitfordigit(chan, 6000);
07987             if (cmd)
07988                destination[0] = cmd;
07989          }
07990          if (!cmd) {
07991             retries++;
07992          } else {
07993 
07994             if (cmd < 0)
07995                return 0;
07996             if (cmd == '*') {
07997                if (option_verbose > 2)
07998                   ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
07999                return 0;
08000             }
08001             if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
08002                retries++;
08003             else
08004                cmd = 't';
08005          }
08006       }
08007       if (retries >= 3) {
08008          return 0;
08009       }
08010       
08011    } else {
08012       if (option_verbose > 2)
08013          ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
08014       ast_copy_string(destination, num, sizeof(destination));
08015    }
08016 
08017    if (!ast_strlen_zero(destination)) {
08018       if (destination[strlen(destination) -1 ] == '*')
08019          return 0; 
08020       if (option_verbose > 2)
08021          ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
08022       ast_copy_string(chan->exten, destination, sizeof(chan->exten));
08023       ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
08024       chan->priority = 0;
08025       return 9;
08026    }
08027    return 0;
08028 }
08029 
08030 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)
08031 {
08032    int res = 0;
08033 #ifdef IMAP_STORAGE
08034    char origtimeS[256],cidS[256],contextS[256];
08035    char *header_content,*temp;
08036 #endif
08037    char filename[PATH_MAX];
08038    struct ast_config *msg_cfg = NULL;
08039    const char *origtime, *context;
08040    char *cid, *name, *num;
08041    int retries = 0;
08042 
08043    vms->starting = 0; 
08044 #ifdef IMAP_STORAGE
08045    /* START HERE */
08046    /* get the message info!! */
08047    if (option_debug > 2)
08048       ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms->curmsg, vms->msgArray[vms->curmsg]);
08049    if (vms->msgArray[vms->curmsg] == 0) {
08050       ast_log (LOG_WARNING,"Trying to access unknown message\n");
08051       return -1;
08052    }
08053 
08054    /* This will only work for new messages... */
08055    header_content = mail_fetchheader (vms->mailstream, vms->msgArray[vms->curmsg]);
08056    /* empty string means no valid header */
08057    if (ast_strlen_zero(header_content)) {
08058       ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[vms->curmsg]);
08059       return -1;
08060    }
08061 
08062    /* Get info from headers!! */
08063    temp = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
08064    
08065    if (temp)
08066       ast_copy_string(cidS,temp, sizeof(cidS));
08067    else
08068       cidS[0] = '\0';
08069    
08070    cid = &cidS[0];
08071    temp = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
08072    
08073    if (temp)
08074       ast_copy_string(contextS,temp, sizeof(contextS));
08075    else
08076       contextS[0] = '\0';
08077    
08078    context = &contextS[0];
08079    temp = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
08080    
08081    if (temp)
08082       ast_copy_string(origtimeS,temp, sizeof(origtimeS));
08083    else
08084       origtimeS[0] = '\0';
08085    
08086    origtime = &origtimeS[0];
08087    
08088    ast_copy_string(filename, "IMAP_STORAGE", sizeof(filename));
08089 #else
08090    make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
08091 
08092    /* Retrieve info from VM attribute file */
08093 
08094    make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
08095    snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
08096    RETRIEVE(vms->curdir, vms->curmsg);
08097    msg_cfg = ast_config_load(filename);
08098    DISPOSE(vms->curdir, vms->curmsg);
08099    if (!msg_cfg) {
08100       ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
08101       return 0;
08102    }
08103 
08104    if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
08105       ast_config_destroy(msg_cfg);
08106       return 0;
08107    }
08108 
08109    cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
08110 
08111    context = ast_variable_retrieve(msg_cfg, "message", "context");
08112    if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
08113       context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
08114 #endif
08115    switch (option) {
08116    case 3:
08117       if (!res)
08118          res = play_message_datetime(chan, vmu, origtime, filename);
08119       if (!res)
08120          res = play_message_callerid(chan, vms, cid, context, 0);
08121 
08122       res = 't';
08123       break;
08124 
08125    case 2:  /* Call back */
08126 
08127       if (ast_strlen_zero(cid))
08128          break;
08129 
08130       ast_callerid_parse(cid, &name, &num);
08131       while ((res > -1) && (res != 't')) {
08132          switch (res) {
08133          case '1':
08134             if (num) {
08135                /* Dial the CID number */
08136                res = dialout(chan, vmu, num, vmu->callback);
08137                if (res) {
08138                   ast_config_destroy(msg_cfg);
08139                   return 9;
08140                }
08141             } else {
08142                res = '2';
08143             }
08144             break;
08145 
08146          case '2':
08147             /* Want to enter a different number, can only do this if there's a dialout context for this user */
08148             if (!ast_strlen_zero(vmu->dialout)) {
08149                res = dialout(chan, vmu, NULL, vmu->dialout);
08150                if (res) {
08151                   ast_config_destroy(msg_cfg);
08152                   return 9;
08153                }
08154             } else {
08155                if (option_verbose > 2)
08156                   ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
08157                res = ast_play_and_wait(chan, "vm-sorry");
08158             }
08159             ast_config_destroy(msg_cfg);
08160             return res;
08161          case '*':
08162             res = 't';
08163             break;
08164          case '3':
08165          case '4':
08166          case '5':
08167          case '6':
08168          case '7':
08169          case '8':
08170          case '9':
08171          case '0':
08172 
08173             res = ast_play_and_wait(chan, "vm-sorry");
08174             retries++;
08175             break;
08176          default:
08177             if (num) {
08178                if (option_verbose > 2)
08179                   ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
08180                res = ast_play_and_wait(chan, "vm-num-i-have");
08181                if (!res)
08182                   res = play_message_callerid(chan, vms, num, vmu->context, 1);
08183                if (!res)
08184                   res = ast_play_and_wait(chan, "vm-tocallnum");
08185                /* Only prompt for a caller-specified number if there is a dialout context specified */
08186                if (!ast_strlen_zero(vmu->dialout)) {
08187                   if (!res)
08188                      res = ast_play_and_wait(chan, "vm-calldiffnum");
08189                }
08190             } else {
08191                res = ast_play_and_wait(chan, "vm-nonumber");
08192                if (!ast_strlen_zero(vmu->dialout)) {
08193                   if (!res)
08194                      res = ast_play_and_wait(chan, "vm-toenternumber");
08195                }
08196             }
08197             if (!res)
08198                res = ast_play_and_wait(chan, "vm-star-cancel");
08199             if (!res)
08200                res = ast_waitfordigit(chan, 6000);
08201             if (!res) {
08202                retries++;
08203                if (retries > 3)
08204                   res = 't';
08205             }
08206             break; 
08207             
08208          }
08209          if (res == 't')
08210             res = 0;
08211          else if (res == '*')
08212             res = -1;
08213       }
08214       break;
08215       
08216    case 1:  /* Reply */
08217       /* Send reply directly to sender */
08218       if (ast_strlen_zero(cid))
08219          break;
08220 
08221       ast_callerid_parse(cid, &name, &num);
08222       if (!num) {
08223          if (option_verbose > 2)
08224             ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
08225          if (!res)
08226             res = ast_play_and_wait(chan, "vm-nonumber");
08227          ast_config_destroy(msg_cfg);
08228          return res;
08229       } else {
08230          struct ast_vm_user vmu2;
08231          if (find_user(&vmu2, vmu->context, num)) {
08232             struct leave_vm_options leave_options;
08233             char mailbox[AST_MAX_EXTENSION * 2 + 2];
08234             snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
08235 
08236             if (option_verbose > 2)
08237                ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
08238             
08239             memset(&leave_options, 0, sizeof(leave_options));
08240             leave_options.record_gain = record_gain;
08241             res = leave_voicemail(chan, mailbox, &leave_options);
08242             if (!res)
08243                res = 't';
08244             ast_config_destroy(msg_cfg);
08245             return res;
08246          } else {
08247             /* Sender has no mailbox, can't reply */
08248             if (option_verbose > 2)
08249                ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
08250             ast_play_and_wait(chan, "vm-nobox");
08251             res = 't';
08252             ast_config_destroy(msg_cfg);
08253             return res;
08254          }
08255       } 
08256       res = 0;
08257 
08258       break;
08259    }
08260 
08261 #ifndef IMAP_STORAGE
08262    ast_config_destroy(msg_cfg);
08263 
08264    if (!res) {
08265       make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
08266       vms->heard[msg] = 1;
08267       res = wait_file(chan, vms, vms->fn);
08268    }
08269 #endif
08270    return res;
08271 }
08272 
08273 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
08274          int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
08275          signed char record_gain, struct vm_state *vms)
08276 {
08277    /* Record message & let caller review or re-record it, or set options if applicable */
08278    int res = 0;
08279    int cmd = 0;
08280    int max_attempts = 3;
08281    int attempts = 0;
08282    int recorded = 0;
08283    int message_exists = 0;
08284    signed char zero_gain = 0;
08285    char tempfile[PATH_MAX];
08286    char *acceptdtmf = "#";
08287    char *canceldtmf = "";
08288 
08289    /* Note that urgent and private are for flagging messages as such in the future */
08290 
08291    /* barf if no pointer passed to store duration in */
08292    if (duration == NULL) {
08293       ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
08294       return -1;
08295    }
08296 
08297    if (!outsidecaller)
08298       snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
08299    else
08300       ast_copy_string(tempfile, recordfile, sizeof(tempfile));
08301 
08302    cmd = '3';  /* Want to start by recording */
08303 
08304    while ((cmd >= 0) && (cmd != 't')) {
08305       switch (cmd) {
08306       case '1':
08307          if (!message_exists) {
08308             /* In this case, 1 is to record a message */
08309             cmd = '3';
08310             break;
08311          } else {
08312             /* Otherwise 1 is to save the existing message */
08313             if (option_verbose > 2)
08314                ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
08315             if (!outsidecaller)
08316                ast_filerename(tempfile, recordfile, NULL);
08317             ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
08318             if (!outsidecaller) {
08319                STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms);
08320                DISPOSE(recordfile, -1);
08321             }
08322             cmd = 't';
08323             return res;
08324          }
08325       case '2':
08326          /* Review */
08327          if (option_verbose > 2)
08328             ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
08329          cmd = ast_stream_and_wait(chan, tempfile, chan->language, AST_DIGIT_ANY);
08330          break;
08331       case '3':
08332          message_exists = 0;
08333          /* Record */
08334          if (recorded == 1) {
08335             if (option_verbose > 2)
08336                ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
08337          } else { 
08338             if (option_verbose > 2)
08339                ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
08340          }
08341          if (recorded && outsidecaller) {
08342             cmd = ast_play_and_wait(chan, INTRO);
08343             cmd = ast_play_and_wait(chan, "beep");
08344          }
08345          recorded = 1;
08346          /* 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 */
08347          if (record_gain)
08348             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
08349          if (ast_test_flag(vmu, VM_OPERATOR))
08350             canceldtmf = "0";
08351          cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
08352          if (record_gain)
08353             ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
08354          if (cmd == -1) {
08355             /* User has hung up, no options to give */
08356             if (!outsidecaller) {
08357                /* user was recording a greeting and they hung up, so let's delete the recording. */
08358                ast_filedelete(tempfile, NULL);
08359             }
08360             return cmd;
08361          }
08362          if (cmd == '0') {
08363             break;
08364          } else if (cmd == '*') {
08365             break;
08366          } 
08367 #if 0       
08368          else if (vmu->review && (*duration < 5)) {
08369             /* Message is too short */
08370             if (option_verbose > 2)
08371                ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
08372             cmd = ast_play_and_wait(chan, "vm-tooshort");
08373             cmd = ast_filedelete(tempfile, NULL);
08374             break;
08375          }
08376          else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
08377             /* Message is all silence */
08378             if (option_verbose > 2)
08379                ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
08380             cmd = ast_filedelete(tempfile, NULL);
08381             cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
08382             if (!cmd)
08383                cmd = ast_play_and_wait(chan, "vm-speakup");
08384             break;
08385          }
08386 #endif
08387          else {
08388             /* If all is well, a message exists */
08389             message_exists = 1;
08390             cmd = 0;
08391          }
08392          break;
08393       case '4':
08394       case '5':
08395       case '6':
08396       case '7':
08397       case '8':
08398       case '9':
08399       case '*':
08400       case '#':
08401          cmd = ast_play_and_wait(chan, "vm-sorry");
08402          break;
08403 #if 0 
08404 /*  XXX Commented out for the moment because of the dangers of deleting
08405     a message while recording (can put the message numbers out of sync) */
08406       case '*':
08407          /* Cancel recording, delete message, offer to take another message*/
08408          cmd = ast_play_and_wait(chan, "vm-deleted");
08409          cmd = ast_filedelete(tempfile, NULL);
08410          if (outsidecaller) {
08411             res = vm_exec(chan, NULL);
08412             return res;
08413          }
08414          else
08415             return 1;
08416 #endif
08417       case '0':
08418          if (!ast_test_flag(vmu, VM_OPERATOR)) {
08419             cmd = ast_play_and_wait(chan, "vm-sorry");
08420             break;
08421          }
08422          if (message_exists || recorded) {
08423             cmd = ast_play_and_wait(chan, "vm-saveoper");
08424             if (!cmd)
08425                cmd = ast_waitfordigit(chan, 3000);
08426             if (cmd == '1') {
08427                ast_play_and_wait(chan, "vm-msgsaved");
08428                cmd = '0';
08429             } else {
08430                ast_play_and_wait(chan, "vm-deleted");
08431                DELETE(recordfile, -1, recordfile);
08432                cmd = '0';
08433             }
08434          }
08435          return cmd;
08436       default:
08437          /* If the caller is an ouside caller, and the review option is enabled,
08438             allow them to review the message, but let the owner of the box review
08439             their OGM's */
08440          if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
08441             return cmd;
08442          if (message_exists) {
08443             cmd = ast_play_and_wait(chan, "vm-review");
08444          }
08445          else {
08446             cmd = ast_play_and_wait(chan, "vm-torerecord");
08447             if (!cmd)
08448                cmd = ast_waitfordigit(chan, 600);
08449          }
08450          
08451          if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
08452             cmd = ast_play_and_wait(chan, "vm-reachoper");
08453             if (!cmd)
08454                cmd = ast_waitfordigit(chan, 600);
08455          }
08456 #if 0
08457          if (!cmd)
08458             cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
08459 #endif
08460          if (!cmd)
08461             cmd = ast_waitfordigit(chan, 6000);
08462          if (!cmd) {
08463             attempts++;
08464          }
08465          if (attempts > max_attempts) {
08466             cmd = 't';
08467          }
08468       }
08469    }
08470    if (outsidecaller)
08471       ast_play_and_wait(chan, "vm-goodbye");
08472    if (cmd == 't')
08473       cmd = 0;
08474    return cmd;
08475 }
08476 
08477 #ifdef IMAP_STORAGE
08478 
08479 static void write_file(char *filename, char *buffer, unsigned long len)
08480 {
08481    FILE *output;
08482 
08483    output = fopen (filename, "w");
08484    fwrite (buffer, len, 1, output);
08485    fclose (output);
08486 }
08487 
08488 void mm_searched(MAILSTREAM *stream, unsigned long number)
08489 {
08490    struct vm_state *vms;
08491    char *mailbox;
08492    char *user;
08493    mailbox = stream->mailbox;
08494    user = get_user_by_mailbox(mailbox);
08495    vms = get_vm_state_by_imapuser(user,2);
08496    if (vms) {
08497       if (option_debug > 2)
08498          ast_log (LOG_DEBUG, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number,vms->vmArrayIndex,vms->interactive);
08499       vms->msgArray[vms->vmArrayIndex++] = number;
08500    } else {
08501       ast_log (LOG_ERROR, "No state found.\n");
08502    }
08503 }
08504 
08505 
08506 #if 0 /*No need for this. */
08507 /* MM status report
08508  * Accepts: MAIL stream
08509  */
08510 static void status(MAILSTREAM *stream)
08511 {
08512    unsigned long i;
08513    char *s, date[MAILTMPLEN];
08514    THREADER *thr;
08515    AUTHENTICATOR *auth;
08516    rfc822_date (date);
08517    ast_log (LOG_NOTICE,"%s\n",date);
08518    if (stream) {
08519       if (stream->mailbox)
08520          ast_log (LOG_NOTICE," %s mailbox: %s, %lu messages, %lu recent\n",
08521                stream->dtb->name, stream->mailbox, stream->nmsgs,stream->recent);
08522       else
08523          ast_log (LOG_NOTICE,"No mailbox is open on this stream\n");
08524       if (stream->user_flags[0]) {
08525          ast_log (LOG_NOTICE,"Keywords: %s\n", stream->user_flags[0]);
08526          for (i = 1; i < NUSERFLAGS && stream->user_flags[i]; ++i)
08527             ast_log (LOG_NOTICE,"   %s\n", stream->user_flags[i]);
08528       }
08529       if (!strcmp (stream->dtb->name, "imap")) {
08530          if (LEVELIMAP4rev1 (stream))
08531             s = "IMAP4rev1 (RFC 3501)";
08532          else if (LEVEL1730 (stream))
08533             s = "IMAP4 (RFC 1730)";
08534          else if (LEVELIMAP2bis (stream))
08535             s = "IMAP2bis";
08536          else if (LEVEL1176 (stream))
08537             s = "IMAP2 (RFC 1176)";
08538          else
08539             s = "IMAP2 (RFC 1064)";
08540          ast_log (LOG_NOTICE,"%s server %s\n", s, imap_host (stream));
08541          if (LEVELIMAP4 (stream)) {
08542             if ((i = (imap_cap(stream)->auth))) {
08543                s = "";
08544                ast_log (LOG_NOTICE,"Mutually-supported SASL mechanisms:\n");
08545                while ((auth = mail_lookup_auth (find_rightmost_bit (&i) + 1))) {
08546                   ast_log (LOG_NOTICE,"   %s\n", auth->name);
08547                   if (!strcmp (auth->name, "PLAIN"))
08548                      s = "\n  [LOGIN will not be listed here if PLAIN is supported]\n";
08549                }
08550                ast_log (LOG_NOTICE,s);
08551             }
08552             ast_log (LOG_NOTICE,"Supported standard extensions:\n");
08553             if (LEVELACL (stream))
08554                ast_log (LOG_NOTICE,"   Access Control lists (RFC 2086)\n");
08555             if (LEVELQUOTA (stream))
08556                ast_log (LOG_NOTICE,"   Quotas (RFC 2087)\n");
08557             if (LEVELLITERALPLUS (stream))
08558                ast_log (LOG_NOTICE,"   Non-synchronizing literals (RFC 2088)\n");
08559             if (LEVELIDLE (stream))
08560                ast_log (LOG_NOTICE,"   IDLE unsolicited update (RFC 2177)\n");
08561             if (LEVELMBX_REF (stream))
08562                ast_log (LOG_NOTICE,"   Mailbox referrals (RFC 2193)\n");
08563             if (LEVELLOG_REF (stream))
08564                ast_log (LOG_NOTICE,"   Login referrals (RFC 2221)\n");
08565             if (LEVELANONYMOUS (stream))
08566                ast_log (LOG_NOTICE,"   Anonymous access (RFC 2245)\n");
08567             if (LEVELNAMESPACE (stream))
08568                ast_log (LOG_NOTICE,"   Multiple namespaces (RFC 2342)\n");
08569             if (LEVELUIDPLUS (stream))
08570                ast_log (LOG_NOTICE,"   Extended UID behavior (RFC 2359)\n");
08571             if (LEVELSTARTTLS (stream))
08572                ast_log (LOG_NOTICE,"   Transport Layer Security (RFC 2595)\n");
08573             if (LEVELLOGINDISABLED (stream))
08574                ast_log (LOG_NOTICE,"   LOGIN command disabled (RFC 2595)\n");
08575             if (LEVELID (stream))
08576                ast_log (LOG_NOTICE,"   Implementation identity negotiation (RFC 2971)\n");
08577             if (LEVELCHILDREN (stream))
08578                ast_log (LOG_NOTICE,"   LIST children announcement (RFC 3348)\n");
08579             if (LEVELMULTIAPPEND (stream))
08580                ast_log (LOG_NOTICE,"   Atomic multiple APPEND (RFC 3502)\n");
08581             if (LEVELBINARY (stream))
08582                ast_log (LOG_NOTICE,"   Binary body content (RFC 3516)\n");
08583             ast_log (LOG_NOTICE,"Supported draft extensions:\n");
08584             if (LEVELUNSELECT (stream))
08585                ast_log (LOG_NOTICE,"   Mailbox unselect\n");
08586             if (LEVELSASLIR (stream))
08587                ast_log (LOG_NOTICE,"   SASL initial client response\n");
08588             if (LEVELSORT (stream))
08589                ast_log (LOG_NOTICE,"   Server-based sorting\n");
08590             if (LEVELTHREAD (stream)) {
08591                ast_log (LOG_NOTICE,"   Server-based threading:\n");
08592                for (thr = imap_cap(stream)->threader; thr; thr = thr->next)
08593                   ast_log (LOG_NOTICE,"      %s\n", thr->name);
08594             }
08595             if (LEVELSCAN (stream))
08596                ast_log (LOG_NOTICE,"   Mailbox text scan\n");
08597             if ((i = imap_cap(stream)->extlevel)) {
08598                ast_log (LOG_NOTICE,"Supported BODYSTRUCTURE extensions:\n");
08599                switch (i) {
08600                   case BODYEXTLOC:
08601                      ast_log (LOG_NOTICE,"   location\n");
08602                   case BODYEXTLANG:
08603                      ast_log (LOG_NOTICE,"   language\n");
08604                   case BODYEXTDSP:
08605                      ast_log (LOG_NOTICE,"   disposition\n");
08606                   case BODYEXTMD5:
08607                      ast_log (LOG_NOTICE,"   MD5\n");
08608                }
08609             }
08610          }else
08611             ast_log (LOG_NOTICE,"\n");
08612       }
08613    }
08614 }
08615 #endif
08616 
08617 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
08618 {
08619    struct ast_variable *var;
08620    struct ast_vm_user *vmu;
08621 
08622    vmu = ast_calloc(1, sizeof *vmu);
08623    if (!vmu)
08624       return NULL;
08625    ast_set_flag(vmu, VM_ALLOCED);
08626    populate_defaults(vmu);
08627 
08628    var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
08629    if (var) {
08630       apply_options_full(vmu, var);
08631       ast_variables_destroy(var);
08632       return vmu;
08633    } else {
08634       free(vmu);
08635       return NULL;
08636    }
08637 }
08638 
08639 /* Interfaces to C-client */
08640 
08641 void mm_exists(MAILSTREAM * stream, unsigned long number)
08642 {
08643    /* mail_ping will callback here if new mail! */
08644    if (option_debug > 3)
08645       ast_log (LOG_DEBUG, "Entering EXISTS callback for message %ld\n", number);
08646    if (number == 0) return;
08647    set_update(stream);
08648 }
08649 
08650 
08651 void mm_expunged(MAILSTREAM * stream, unsigned long number)
08652 {
08653    /* mail_ping will callback here if expunged mail! */
08654    if (option_debug > 3)
08655       ast_log (LOG_DEBUG, "Entering EXPUNGE callback for message %ld\n", number);
08656    if (number == 0) return;
08657    set_update(stream);
08658 }
08659 
08660 
08661 void mm_flags(MAILSTREAM * stream, unsigned long number)
08662 {
08663    /* mail_ping will callback here if read mail! */
08664    if (option_debug > 3)
08665       ast_log (LOG_DEBUG, "Entering FLAGS callback for message %ld\n", number);
08666    if (number == 0) return;
08667    set_update(stream);
08668 }
08669 
08670 
08671 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
08672 {
08673    mm_log (string, errflg);
08674 }
08675 
08676 
08677 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
08678 {
08679    if (delimiter == '\0') {
08680       delimiter = delim;
08681    }
08682    if (option_debug > 4) {
08683       ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
08684       if (attributes & LATT_NOINFERIORS)
08685          ast_log(LOG_DEBUG, "no inferiors\n");
08686       if (attributes & LATT_NOSELECT)
08687          ast_log(LOG_DEBUG, "no select\n");
08688       if (attributes & LATT_MARKED)
08689          ast_log(LOG_DEBUG, "marked\n");
08690       if (attributes & LATT_UNMARKED)
08691          ast_log(LOG_DEBUG, "unmarked\n");
08692    }
08693 }
08694 
08695 
08696 void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
08697 {
08698    if (option_debug > 4) {
08699       ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
08700       if (attributes & LATT_NOINFERIORS)
08701          ast_log(LOG_DEBUG, "no inferiors\n");
08702       if (attributes & LATT_NOSELECT)
08703          ast_log(LOG_DEBUG, "no select\n");
08704       if (attributes & LATT_MARKED)
08705          ast_log(LOG_DEBUG, "marked\n");
08706       if (attributes & LATT_UNMARKED)
08707          ast_log(LOG_DEBUG, "unmarked\n");
08708    }
08709 }
08710 
08711 
08712 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
08713 {
08714    ast_log (LOG_NOTICE," Mailbox %s", mailbox);
08715    if (status->flags & SA_MESSAGES)
08716       ast_log (LOG_NOTICE,", %lu messages", status->messages);
08717    if (status->flags & SA_RECENT)
08718       ast_log (LOG_NOTICE,", %lu recent", status->recent);
08719    if (status->flags & SA_UNSEEN)
08720       ast_log (LOG_NOTICE,", %lu unseen", status->unseen);
08721    if (status->flags & SA_UIDVALIDITY)
08722       ast_log (LOG_NOTICE,", %lu UID validity", status->uidvalidity);
08723    if (status->flags & SA_UIDNEXT)
08724       ast_log (LOG_NOTICE,", %lu next UID", status->uidnext);
08725    ast_log (LOG_NOTICE,"\n");
08726 }
08727 
08728 
08729 void mm_log(char *string, long errflg)
08730 {
08731    switch ((short) errflg) {
08732       case NIL:
08733          if (option_debug)
08734             ast_log(LOG_DEBUG,"IMAP Info: %s\n", string);
08735          break;
08736       case PARSE:
08737       case WARN:
08738          ast_log (LOG_WARNING,"IMAP Warning: %s\n", string);
08739          break;
08740       case ERROR:
08741          ast_log (LOG_ERROR,"IMAP Error: %s\n", string);
08742          break;
08743    }
08744 }
08745 
08746 
08747 void mm_dlog(char *string)
08748 {
08749    ast_log (LOG_NOTICE, "%s\n", string);
08750 }
08751 
08752 
08753 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
08754 {
08755    struct ast_vm_user *vmu;
08756 
08757    if (option_debug > 3)
08758       ast_log(LOG_DEBUG, "Entering callback mm_login\n");
08759 
08760    ast_copy_string(user, mb->user, MAILTMPLEN);
08761 
08762    /* We should only do this when necessary */
08763    if (!ast_strlen_zero(authpassword)) {
08764       ast_copy_string(pwd, authpassword, MAILTMPLEN);
08765    } else {
08766       AST_LIST_TRAVERSE(&users, vmu, list) {
08767          if (!strcasecmp(mb->user, vmu->imapuser)) {
08768             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
08769             break;
08770          }
08771       }
08772       if (!vmu) {
08773          if ((vmu = find_user_realtime_imapuser(mb->user))) {
08774             ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
08775             free_user(vmu);
08776          }
08777       }
08778    }
08779 }
08780 
08781 
08782 void mm_critical(MAILSTREAM * stream)
08783 {
08784 }
08785 
08786 
08787 void mm_nocritical(MAILSTREAM * stream)
08788 {
08789 }
08790 
08791 
08792 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
08793 {
08794    kill (getpid (), SIGSTOP);
08795    return NIL;
08796 }
08797 
08798 
08799 void mm_fatal(char *string)
08800 {
08801    ast_log(LOG_ERROR,"IMAP access FATAL error: %s\n", string);
08802 }
08803 
08804 /* C-client callback to handle quota */
08805 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
08806 {
08807    struct vm_state *vms;
08808    char *mailbox;
08809    char *user;
08810    unsigned long usage = 0;
08811    unsigned long limit = 0;
08812    
08813    while (pquota) {
08814       usage = pquota->usage;
08815       limit = pquota->limit;
08816       pquota = pquota->next;
08817    }
08818    
08819    mailbox = stream->mailbox;
08820    user = get_user_by_mailbox(mailbox);
08821    vms = get_vm_state_by_imapuser(user,2);
08822    if (vms) {
08823       if (option_debug > 2)
08824          ast_log (LOG_DEBUG, "User %s usage is %lu, limit is %lu\n",user,usage,limit);
08825       vms->quota_usage = usage;
08826       vms->quota_limit = limit;
08827    } else {
08828       ast_log (LOG_ERROR, "No state found.\n");
08829    }
08830 }
08831 
08832 static char *get_header_by_tag(char *header, char *tag)
08833 {
08834    char *start;
08835    int taglen;
08836    char *eol_pnt;
08837 
08838    if (!header || !tag)
08839       return NULL;
08840 
08841    taglen = strlen(tag) + 1;
08842    if (taglen < 1)
08843       return NULL;
08844 
08845    start = strstr(header, tag);
08846    if (!start)
08847       return NULL;
08848 
08849    ast_mutex_lock(&imaptemp_lock);
08850    ast_copy_string(imaptemp, start+taglen, sizeof(imaptemp));
08851    ast_mutex_unlock(&imaptemp_lock);
08852    if ((eol_pnt = strchr(imaptemp,'\r')) || (eol_pnt = strchr(imaptemp,'\n')))
08853       *eol_pnt = '\0';
08854    return imaptemp;
08855 }
08856 
08857 static char *get_user_by_mailbox(char *mailbox)
08858 {
08859    char *start, *quote;
08860    char *eol_pnt;
08861 
08862    if (!mailbox)
08863       return NULL;
08864 
08865    start = strstr(mailbox,"/user=");
08866    if (!start)
08867       return NULL;
08868 
08869    ast_mutex_lock(&imaptemp_lock);
08870    ast_copy_string(imaptemp, start+6, sizeof(imaptemp));
08871    ast_mutex_unlock(&imaptemp_lock);
08872 
08873    quote = strchr(imaptemp,'\"');
08874    if (!quote) {  /* if username is not in quotes */
08875       eol_pnt = strchr(imaptemp,'/');
08876       if (!eol_pnt) {
08877          eol_pnt = strchr(imaptemp,'}');
08878       }
08879       *eol_pnt = '\0';
08880       return imaptemp;
08881    } else {
08882       eol_pnt = strchr(imaptemp+1,'\"');
08883       *eol_pnt = '\0';
08884       return imaptemp+1;
08885    }
08886 }
08887 
08888 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
08889 {
08890    struct vmstate *vlist = NULL;
08891 
08892    ast_mutex_lock(&vmstate_lock);
08893    vlist = vmstates;
08894    while (vlist) {
08895       if (vlist->vms) {
08896          if (vlist->vms->imapuser) {
08897             if (!strcmp(vlist->vms->imapuser,user)) {
08898                if (interactive == 2) {
08899                   ast_mutex_unlock(&vmstate_lock);
08900                   return vlist->vms;
08901                } else if (vlist->vms->interactive == interactive) {
08902                   ast_mutex_unlock(&vmstate_lock);
08903                   return vlist->vms;
08904                }
08905             }
08906          } else {
08907             if (option_debug > 2)
08908                ast_log(LOG_DEBUG, " error: imapuser is NULL for %s\n",user);
08909          }
08910       } else {
08911          if (option_debug > 2)
08912             ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",user);
08913       }
08914       vlist = vlist->next;
08915    }
08916    ast_mutex_unlock(&vmstate_lock);
08917    if (option_debug > 2)
08918       ast_log(LOG_DEBUG, "%s not found in vmstates\n",user);
08919    return NULL;
08920 }
08921 
08922 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive)
08923 { 
08924    struct vmstate *vlist = NULL;
08925 
08926    ast_mutex_lock(&vmstate_lock);
08927    vlist = vmstates;
08928    if (option_debug > 2) 
08929       ast_log(LOG_DEBUG, "Mailbox set to %s\n",mailbox);
08930    while (vlist) {
08931       if (vlist->vms) {
08932          if (vlist->vms->username) {
08933             if (option_debug > 2)
08934                ast_log(LOG_DEBUG, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox,interactive,vlist->vms->username,vlist->vms->interactive);
08935             if (!strcmp(vlist->vms->username,mailbox) && vlist->vms->interactive == interactive) {
08936                if (option_debug > 2)
08937                   ast_log(LOG_DEBUG, " Found it!\n");
08938                ast_mutex_unlock(&vmstate_lock);
08939                return vlist->vms;
08940             }
08941          } else {
08942             if (option_debug > 2)
08943                ast_log(LOG_DEBUG, " error: username is NULL for %s\n",mailbox);
08944          }
08945       } else {
08946          if (option_debug > 2)
08947             ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",mailbox);
08948       }
08949       vlist = vlist->next;
08950    }
08951    ast_mutex_unlock(&vmstate_lock);
08952    if (option_debug > 2)
08953       ast_log(LOG_DEBUG, "%s not found in vmstates\n",mailbox);
08954    return NULL;
08955 }
08956 
08957 static void vmstate_insert(struct vm_state *vms) 
08958 {
08959    struct vmstate *v;
08960    struct vm_state *altvms;
08961 
08962    /* If interactive, it probably already exists, and we should
08963       use the one we already have since it is more up to date.
08964       We can compare the username to find the duplicate */
08965    if (vms->interactive == 1) {
08966       altvms = get_vm_state_by_mailbox(vms->username,0);
08967       if (altvms) {
08968          if (option_debug > 2)
08969             ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
08970          vms->newmessages = altvms->newmessages;
08971          vms->oldmessages = altvms->oldmessages;
08972          if (option_debug > 2)
08973             ast_log(LOG_DEBUG, "check_msgArray before memcpy\n");
08974          check_msgArray(vms);
08975          /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
08976          copy_msgArray(vms, altvms);
08977          if (option_debug > 2)
08978             ast_log(LOG_DEBUG, "check_msgArray after memcpy\n");
08979          check_msgArray(vms);
08980          vms->vmArrayIndex = altvms->vmArrayIndex;
08981          vms->lastmsg = altvms->lastmsg;
08982          vms->curmsg = altvms->curmsg;
08983          /* get a pointer to the persistent store */
08984          vms->persist_vms = altvms;
08985          /* Reuse the mailstream? */
08986          vms->mailstream = altvms->mailstream;
08987          /* vms->mailstream = NIL; */
08988       }
08989    }
08990 
08991    v = (struct vmstate *)malloc(sizeof(struct vmstate));
08992    if (!v) {
08993       ast_log(LOG_ERROR, "Out of memory\n");
08994    }
08995    if (option_debug > 2)
08996       ast_log(LOG_DEBUG, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
08997    ast_mutex_lock(&vmstate_lock);
08998    v->vms = vms;
08999    v->next = vmstates;
09000    vmstates = v;
09001    ast_mutex_unlock(&vmstate_lock);
09002 }
09003 
09004 static void vmstate_delete(struct vm_state *vms) 
09005 {
09006    struct vmstate *vc, *vf = NULL, *vl = NULL;
09007    struct vm_state *altvms;
09008 
09009    /* If interactive, we should copy pertainent info
09010       back to the persistent state (to make update immediate) */
09011    if (vms->interactive == 1) {
09012       altvms = vms->persist_vms;
09013       if (altvms) {
09014          if (option_debug > 2)
09015             ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
09016          altvms->newmessages = vms->newmessages;
09017          altvms->oldmessages = vms->oldmessages;
09018          altvms->updated = 1;
09019       }
09020    }
09021 
09022    ast_mutex_lock(&vmstate_lock);
09023    vc = vmstates;
09024    if (option_debug > 2)
09025       ast_log(LOG_DEBUG, "Removing vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
09026    while (vc) {
09027       if (vc->vms == vms) {
09028          vf = vc;
09029          if (vl)
09030             vl->next = vc->next;
09031          else
09032             vmstates = vc->next;
09033          break;
09034       }
09035       vl = vc;
09036       vc = vc->next;
09037    }
09038    if (!vf) {
09039       ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n",vms->imapuser,vms->username);
09040    } else {
09041       ast_mutex_destroy(&vf->vms->lock);
09042       free(vf);
09043    }
09044    ast_mutex_unlock(&vmstate_lock);
09045 }
09046 
09047 static void set_update(MAILSTREAM * stream) 
09048 {
09049    struct vm_state *vms;
09050    char *mailbox;
09051    char *user;
09052 
09053    mailbox = stream->mailbox;
09054    user = get_user_by_mailbox(mailbox);
09055    vms = get_vm_state_by_imapuser(user, 0);
09056    if (vms) {
09057       if (option_debug > 2)
09058          ast_log (LOG_DEBUG, "User %s mailbox set for update.\n",user);
09059       vms->updated = 1; /* set updated flag since mailbox changed */
09060    } else {
09061       if (option_debug > 2)
09062          ast_log (LOG_WARNING, "User %s mailbox not found for update.\n",user);
09063    }
09064 }
09065 
09066 static void init_vm_state(struct vm_state *vms) 
09067 {
09068    int x;
09069    vms->vmArrayIndex = 0;
09070    for (x = 0; x < 256; x++) {
09071       vms->msgArray[x] = 0;
09072    }
09073    ast_mutex_init(&vms->lock);
09074 }
09075 
09076 static void check_msgArray(struct vm_state *vms) 
09077 {
09078    int x;
09079    for (x = 0; x<256; x++) {
09080       if (vms->msgArray[x]!=0) {
09081          if (option_debug)
09082             ast_log (LOG_DEBUG, "Item %d set to %ld\n",x,vms->msgArray[x]);
09083       }
09084    }
09085 }
09086 
09087 static void copy_msgArray(struct vm_state *dst, struct vm_state *src)
09088 {
09089    int x;
09090    for (x = 0; x<256; x++) {
09091       dst->msgArray[x] = src->msgArray[x];
09092    }
09093 }
09094 
09095 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format) 
09096 {
09097    char *body_content;
09098    char *body_decoded;
09099    unsigned long len;
09100    unsigned long newlen;
09101    char filename[256];
09102    
09103    if (!body || body == NIL)
09104       return -1;
09105    body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
09106    if (body_content != NIL) {
09107       snprintf(filename, sizeof(filename), "%s.%s", vms->fn, format);
09108       /* ast_log (LOG_DEBUG,body_content); */
09109       body_decoded = rfc822_base64 ((unsigned char *)body_content, len, &newlen);
09110       write_file (filename, (char *) body_decoded, newlen);
09111    }
09112    return 0;
09113 }
09114 
09115 /* get delimiter via mm_list callback */
09116 static void get_mailbox_delimiter(MAILSTREAM *stream) {
09117    char tmp[50];
09118    snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
09119    mail_list(stream, tmp, "*");
09120 }
09121 
09122 /* Check Quota for user */
09123 static void check_quota(struct vm_state *vms, char *mailbox) {
09124    mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
09125    if (option_debug > 2)
09126       ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mailbox);
09127    if (vms && vms->mailstream != NULL) {
09128       imap_getquotaroot(vms->mailstream, mailbox);
09129    } else {
09130       ast_log(LOG_WARNING,"Mailstream not available for mailbox: %s\n",mailbox);
09131    }
09132 }
09133 
09134 #endif /* IMAP_STORAGE */
09135 
09136 /* This is a workaround so that menuselect displays a proper description
09137  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
09138  */
09139  
09140 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
09141       .load = load_module,
09142       .unload = unload_module,
09143       .reload = reload,
09144       );

Generated on Sun Jun 12 16:37:44 2011 for Asterisk - the Open Source PBX by  doxygen 1.5.6