Sat Apr 12 07:12:17 2008

Asterisk developer's documentation


app_voicemail.c

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