Fri Aug 24 02:22:11 2007

Asterisk developer's documentation


app_voicemail.c

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

Generated on Fri Aug 24 02:22:12 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1