Wed Feb 11 11:59:43 2009

Asterisk developer's documentation


app_voicemail.c

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

Generated on Wed Feb 11 11:59:44 2009 for Asterisk - the Open Source PBX by  doxygen 1.4.7