Mon May 14 04:42:53 2007

Asterisk developer's documentation


app_voicemail.c

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

Generated on Mon May 14 04:42:54 2007 for Asterisk - the Open Source PBX by  doxygen 1.5.1