Fix a bug with app_voicemail when trying to use app_directory to leave messages
[asterisk/asterisk.git] / apps / app_voicemail.c
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18
19 /*! \file
20  *
21  * \brief Comedian Mail - Voicemail System
22  *
23  * \author Mark Spencer <markster@digium.com>
24  * 
25  * \par See also
26  * \arg \ref Config_vm
27  * \ingroup applications
28  * \note This module requires res_adsi to load.
29  */
30
31 /*** MAKEOPTS
32 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o">
33         <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
34                 <depend>unixodbc</depend>
35                 <defaultenabled>no</defaultenabled>
36         </member>
37         <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
38                 <depend>imap_tk</depend>
39                 <use>ssl</use>
40                 <defaultenabled>no</defaultenabled>
41         </member>
42 </category>
43  ***/
44
45 #include "asterisk.h"
46
47 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
48
49 #include <stdlib.h>
50 #include <errno.h>
51 #include <unistd.h>
52 #include <string.h>
53 #include <stdlib.h>
54 #include <stdio.h>
55 #include <sys/time.h>
56 #include <sys/stat.h>
57 #include <sys/types.h>
58 #include <sys/mman.h>
59 #include <time.h>
60 #include <dirent.h>
61 #ifdef IMAP_STORAGE
62 #include <ctype.h>
63 #include <signal.h>
64 #include <pwd.h>
65 #include "c-client.h"
66 #include "imap4r1.h"
67 #include "linkage.h"
68 #endif
69 #include "asterisk/lock.h"
70 #include "asterisk/file.h"
71 #include "asterisk/logger.h"
72 #include "asterisk/channel.h"
73 #include "asterisk/pbx.h"
74 #include "asterisk/options.h"
75 #include "asterisk/config.h"
76 #include "asterisk/say.h"
77 #include "asterisk/module.h"
78 #include "asterisk/adsi.h"
79 #include "asterisk/app.h"
80 #include "asterisk/manager.h"
81 #include "asterisk/dsp.h"
82 #include "asterisk/localtime.h"
83 #include "asterisk/cli.h"
84 #include "asterisk/utils.h"
85 #include "asterisk/stringfields.h"
86 #include "asterisk/smdi.h"
87 #ifdef ODBC_STORAGE
88 #include "asterisk/res_odbc.h"
89 #endif
90
91 #ifdef IMAP_STORAGE
92 char *curhst = NIL;           /* currently connected host */
93 char *curusr = NIL;           /* current login user */
94
95 char temp[1024];
96
97 static char imapserver[48];
98 static char imapport[8];
99 static char imapflags[128];
100 static char authuser[32];
101 static char authpassword[42];
102 static int expungeonhangup = 1;
103 static char delimiter = '\0';
104
105 struct vm_state;
106
107 int init_mailstream (struct vm_state *vms);
108 void write_file (char *filename, char *buffer, unsigned long len);
109 void status (MAILSTREAM *stream);
110 void display_body (BODY *body, char *pfx, long i);
111 static char *get_header_by_tag(char *header, char *tag);
112 static void vm_imap_delete(int msgnum, struct vm_state *vms);
113 static char *get_user_by_mailbox(char *mailbox);
114 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
115 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
116 static void vmstate_insert(struct vm_state *vms);
117 static void vmstate_delete(struct vm_state *vms);
118 static void set_update(MAILSTREAM * stream);
119 static void init_vm_state(struct vm_state *vms);
120 static void check_msgArray(struct vm_state *vms);
121 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
122 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
123 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num);
124 static void get_mailbox_delimiter(MAILSTREAM *stream);
125 void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
126 /* should define TMP in config file... */
127 #define TMP "/tmp"
128 struct vmstate {
129       struct vm_state *vms;
130       struct vmstate *next;
131 };
132 AST_MUTEX_DEFINE_STATIC(vmstate_lock);
133 static struct vmstate *vmstates = NULL;
134 #endif
135
136 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
137
138 #define COMMAND_TIMEOUT 5000
139 /* Don't modify these here; set your umask at runtime instead */
140 #define VOICEMAIL_DIR_MODE      0777
141 #define VOICEMAIL_FILE_MODE     0666
142
143 #define VOICEMAIL_CONFIG "voicemail.conf"
144 #define ASTERISK_USERNAME "asterisk"
145
146 /* Default mail command to mail voicemail. Change it with the
147     mailcmd= command in voicemail.conf */
148 #define SENDMAIL "/usr/sbin/sendmail -t"
149
150 #define INTRO "vm-intro"
151
152 #define MAXMSG 100
153 #define MAXMSGLIMIT 9999
154
155 #define BASEMAXINLINE 256
156 #define BASELINELEN 72
157 #define BASEMAXINLINE 256
158 #define eol "\r\n"
159
160 #define MAX_DATETIME_FORMAT     512
161 #define MAX_NUM_CID_CONTEXTS 10
162
163 #define VM_REVIEW        (1 << 0)
164 #define VM_OPERATOR      (1 << 1)
165 #define VM_SAYCID        (1 << 2)
166 #define VM_SVMAIL        (1 << 3)
167 #define VM_ENVELOPE      (1 << 4)
168 #define VM_SAYDURATION   (1 << 5)
169 #define VM_SKIPAFTERCMD  (1 << 6)
170 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
171 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
172 #define VM_PBXSKIP       (1 << 9)
173 #define VM_DIRECFORWARD  (1 << 10)  /*!< directory_forward */
174 #define VM_ATTACH        (1 << 11)
175 #define VM_DELETE        (1 << 12)
176 #define VM_ALLOCED       (1 << 13)
177 #define VM_SEARCH        (1 << 14)
178 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
179 #define ERROR_LOCK_PATH  -100
180
181
182 enum {
183         OPT_SILENT =           (1 << 0),
184         OPT_BUSY_GREETING =    (1 << 1),
185         OPT_UNAVAIL_GREETING = (1 << 2),
186         OPT_RECORDGAIN =       (1 << 3),
187         OPT_PREPEND_MAILBOX =  (1 << 4),
188         OPT_PRIORITY_JUMP =    (1 << 5),
189         OPT_AUTOPLAY =         (1 << 6),
190 } vm_option_flags;
191
192 enum {
193         OPT_ARG_RECORDGAIN = 0,
194         OPT_ARG_PLAYFOLDER = 1,
195         /* This *must* be the last value in this enum! */
196         OPT_ARG_ARRAY_SIZE = 2,
197 } vm_option_args;
198
199 AST_APP_OPTIONS(vm_app_options, {
200         AST_APP_OPTION('s', OPT_SILENT),
201         AST_APP_OPTION('b', OPT_BUSY_GREETING),
202         AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
203         AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
204         AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
205         AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
206         AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
207 });
208
209 static int load_config(void);
210
211 /*! \page vmlang Voicemail Language Syntaxes Supported
212
213         \par Syntaxes supported, not really language codes.
214         \arg \b en - English
215         \arg \b de - German
216         \arg \b es - Spanish
217         \arg \b fr - French
218         \arg \b it = Italian
219         \arg \b nl - Dutch
220         \arg \b pt - Polish
221         \arg \b pt - Portuguese
222         \arg \b gr - Greek
223         \arg \b no - Norwegian
224         \arg \b se - Swedish
225
226 German requires the following additional soundfile:
227 \arg \b 1F      einE (feminine)
228
229 Spanish requires the following additional soundfile:
230 \arg \b 1M      un (masculine)
231
232 Dutch, Portuguese & Spanish require the following additional soundfiles:
233 \arg \b vm-INBOXs       singular of 'new'
234 \arg \b vm-Olds         singular of 'old/heard/read'
235
236 NB these are plural:
237 \arg \b vm-INBOX        nieuwe (nl)
238 \arg \b vm-Old          oude (nl)
239
240 Polish uses:
241 \arg \b vm-new-a        'new', feminine singular accusative
242 \arg \b vm-new-e        'new', feminine plural accusative
243 \arg \b vm-new-ych      'new', feminine plural genitive
244 \arg \b vm-old-a        'old', feminine singular accusative
245 \arg \b vm-old-e        'old', feminine plural accusative
246 \arg \b vm-old-ych      'old', feminine plural genitive
247 \arg \b digits/1-a      'one', not always same as 'digits/1'
248 \arg \b digits/2-ie     'two', not always same as 'digits/2'
249
250 Swedish uses:
251 \arg \b vm-nytt         singular of 'new'
252 \arg \b vm-nya          plural of 'new'
253 \arg \b vm-gammalt      singular of 'old'
254 \arg \b vm-gamla        plural of 'old'
255 \arg \b digits/ett      'one', not always same as 'digits/1'
256
257 Norwegian uses:
258 \arg \b vm-ny           singular of 'new'
259 \arg \b vm-nye          plural of 'new'
260 \arg \b vm-gammel       singular of 'old'
261 \arg \b vm-gamle        plural of 'old'
262
263 Dutch also uses:
264 \arg \b nl-om           'at'?
265
266 Spanish also uses:
267 \arg \b vm-youhaveno
268
269 Italian requires the following additional soundfile:
270
271 For vm_intro_it:
272 \arg \b vm-nuovo        new
273 \arg \b vm-nuovi        new plural
274 \arg \b vm-vecchio      old
275 \arg \b vm-vecchi       old plural
276
277 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
278 spelled among others when you have to change folder. For the above reasons, vm-INBOX
279 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
280
281 */
282
283 struct baseio {
284         int iocp;
285         int iolen;
286         int linelength;
287         int ateof;
288         unsigned char iobuf[BASEMAXINLINE];
289 };
290
291 /*! Structure for linked list of users */
292 struct ast_vm_user {
293         char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
294         char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
295         char password[80];               /*!< Secret pin code, numbers only */
296         char fullname[80];               /*!< Full name, for directory app */
297         char email[80];                  /*!< E-mail address */
298         char pager[80];                  /*!< E-mail address to pager (no attachment) */
299         char serveremail[80];            /*!< From: Mail address */
300         char mailcmd[160];               /*!< Configurable mail command */
301         char language[MAX_LANGUAGE];     /*!< Config: Language setting */
302         char zonetag[80];                /*!< Time zone */
303         char callback[80];
304         char dialout[80];
305         char uniqueid[20];               /*!< Unique integer identifier */
306         char exit[80];
307         char attachfmt[20];              /*!< Attachment format */
308         unsigned int flags;              /*!< VM_ flags */      
309         int saydurationm;
310         int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
311 #ifdef IMAP_STORAGE
312                 char imapuser[80];      /* IMAP server login */
313 #endif
314         double volgain;         /*!< Volume gain for voicemails sent via email */
315         AST_LIST_ENTRY(ast_vm_user) list;
316 };
317
318 struct vm_zone {
319         AST_LIST_ENTRY(vm_zone) list;
320         char name[80];
321         char timezone[80];
322         char msg_format[512];
323 };
324
325 struct vm_state {
326         char curbox[80];
327         char username[80];
328         char curdir[256];
329         char vmbox[256];
330         char fn[256];
331         char fn2[256];
332         int *deleted;
333         int *heard;
334         int curmsg;
335         int lastmsg;
336         int newmessages;
337         int oldmessages;
338         int starting;
339         int repeats;
340 #ifdef IMAP_STORAGE
341         int updated; /* decremented on each mail check until 1 -allows delay */
342         long msgArray[256];
343         MAILSTREAM *mailstream;
344         int vmArrayIndex;
345         char imapuser[80]; /* IMAP server login */
346         int interactive;
347         unsigned int quota_limit;
348         unsigned int quota_usage;
349         struct vm_state *persist_vms;
350 #endif
351 };
352 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);
353 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
354 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
355                         char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
356                         signed char record_gain);
357 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
358 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
359 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
360 #ifndef ODBC_STORAGE
361 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
362 #endif
363 static void apply_options(struct ast_vm_user *vmu, const char *options);
364
365 #ifdef ODBC_STORAGE
366 static char odbc_database[80];
367 static char odbc_table[80];
368 #define RETRIEVE(a,b) retrieve_file(a,b)
369 #define DISPOSE(a,b) remove_file(a,b)
370 #define STORE(a,b,c,d) store_file(a,b,c,d)
371 #define EXISTS(a,b,c,d) (message_exists(a,b))
372 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
373 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
374 #define DELETE(a,b,c) (delete_file(a,b))
375 #else
376 #ifdef IMAP_STORAGE
377 #define RETRIEVE(a,b)
378 #define DISPOSE(a,b)
379 #define STORE(a,b,c,d)
380 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
381 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
382 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
383 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
384 #define DELETE(a,b,c) (vm_delete(c))
385 #else
386 #define RETRIEVE(a,b)
387 #define DISPOSE(a,b)
388 #define STORE(a,b,c,d)
389 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
390 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
391 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
392 #define DELETE(a,b,c) (vm_delete(c))
393 #endif
394 #endif
395
396 static char VM_SPOOL_DIR[PATH_MAX];
397
398 static char ext_pass_cmd[128];
399 #ifdef ODBC_STORAGE
400 static char *tdesc = "Comedian Mail (Voicemail System) with ODBC Storage";
401 #else
402 #ifdef IMAP_STORAGE
403 static char *tdesc = "Comedian Mail (Voicemail System) with IMAP Storage";
404 #else
405 static char *tdesc = "Comedian Mail (Voicemail System)";
406 #endif
407 #endif
408
409 static char *addesc = "Comedian Mail";
410
411 static char *synopsis_vm =
412 "Leave a Voicemail message";
413
414 static char *descrip_vm =
415 "  VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
416 "application allows the calling party to leave a message for the specified\n"
417 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
418 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
419 "specified mailbox does not exist.\n"
420 "  The Voicemail application will exit if any of the following DTMF digits are\n"
421 "received:\n"
422 "    0 - Jump to the 'o' extension in the current dialplan context.\n"
423 "    * - Jump to the 'a' extension in the current dialplan context.\n"
424 "  This application will set the following channel variable upon completion:\n"
425 "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
426 "               application. The possible values are:\n"
427 "               SUCCESS | USEREXIT | FAILED\n\n"
428 "  Options:\n"
429 "    b    - Play the 'busy' greeting to the calling party.\n"
430 "    g(#) - Use the specified amount of gain when recording the voicemail\n"
431 "           message. The units are whole-number decibels (dB).\n"
432 "    s    - Skip the playback of instructions for leaving a message to the\n"
433 "           calling party.\n"
434 "    u    - Play the 'unavailble greeting.\n"
435 "    j    - Jump to priority n+101 if the mailbox is not found or some other\n"
436 "           error occurs.\n";
437
438 static char *synopsis_vmain =
439 "Check Voicemail messages";
440
441 static char *descrip_vmain =
442 "  VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
443 "calling party to check voicemail messages. A specific mailbox, and optional\n"
444 "corresponding context, may be specified. If a mailbox is not provided, the\n"
445 "calling party will be prompted to enter one. If a context is not specified,\n"
446 "the 'default' context will be used.\n\n"
447 "  Options:\n"
448 "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
449 "           is entered by the caller.\n"
450 "    g(#) - Use the specified amount of gain when recording a voicemail\n"
451 "           message. The units are whole-number decibels (dB).\n"
452 "    s    - Skip checking the passcode for the mailbox.\n"
453 "    a(#) - Skip folder prompt and go directly to folder specified.\n"
454 "           Defaults to INBOX\n";
455
456 static char *synopsis_vm_box_exists =
457 "Check to see if Voicemail mailbox exists";
458
459 static char *descrip_vm_box_exists =
460 "  MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
461 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
462 "will be used.\n"
463 "  This application will set the following channel variable upon completion:\n"
464 "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
465 "                        MailboxExists application. Possible values include:\n"
466 "                        SUCCESS | FAILED\n\n"
467 "  Options:\n"
468 "    j - Jump to priority n+101 if the mailbox is found.\n";
469
470 static char *synopsis_vmauthenticate =
471 "Authenticate with Voicemail passwords";
472
473 static char *descrip_vmauthenticate =
474 "  VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
475 "same way as the Authenticate application, but the passwords are taken from\n"
476 "voicemail.conf.\n"
477 "  If the mailbox is specified, only that mailbox's password will be considered\n"
478 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
479 "be set with the authenticated mailbox.\n\n"
480 "  Options:\n"
481 "    s - Skip playing the initial prompts.\n";
482
483 /* Leave a message */
484 static char *app = "VoiceMail";
485
486 /* Check mail, control, etc */
487 static char *app2 = "VoiceMailMain";
488
489 static char *app3 = "MailboxExists";
490 static char *app4 = "VMAuthenticate";
491
492 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
493 static AST_LIST_HEAD_STATIC(zones, vm_zone);
494 static int maxsilence;
495 static int maxmsg;
496 static int silencethreshold = 128;
497 static char serveremail[80];
498 static char mailcmd[160];       /* Configurable mail cmd */
499 static char externnotify[160]; 
500 static struct ast_smdi_interface *smdi_iface = NULL;
501 static char vmfmts[80];
502 static double volgain;
503 static int vmminmessage;
504 static int vmmaxmessage;
505 static int maxgreet;
506 static int skipms;
507 static int maxlogins;
508
509 static struct ast_flags globalflags = {0};
510
511 static int saydurationminfo;
512
513 static char dialcontext[AST_MAX_CONTEXT];
514 static char callcontext[AST_MAX_CONTEXT];
515 static char exitcontext[AST_MAX_CONTEXT];
516
517 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
518
519
520 static char *emailbody = NULL;
521 static char *emailsubject = NULL;
522 static char *pagerbody = NULL;
523 static char *pagersubject = NULL;
524 static char fromstring[100];
525 static char pagerfromstring[100];
526 static char emailtitle[100];
527 static char charset[32] = "ISO-8859-1";
528
529 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
530 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
531 static int adsiver = 1;
532 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
533
534 LOCAL_USER_DECL;
535
536 static void populate_defaults(struct ast_vm_user *vmu)
537 {
538         ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);     
539         if (saydurationminfo)
540                 vmu->saydurationm = saydurationminfo;
541         if (callcontext)
542                 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
543         if (dialcontext)
544                 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
545         if (exitcontext)
546                 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
547         if (maxmsg)
548                 vmu->maxmsg = maxmsg;
549         vmu->volgain = volgain;
550 }
551
552 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
553 {
554         int x;
555         if (!strcasecmp(var, "attach")) {
556                 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
557         } else if (!strcasecmp(var, "attachfmt")) {
558                 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
559         } else if (!strcasecmp(var, "serveremail")) {
560                 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
561         } else if (!strcasecmp(var, "language")) {
562                 ast_copy_string(vmu->language, value, sizeof(vmu->language));
563         } else if (!strcasecmp(var, "tz")) {
564                 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
565 #ifdef IMAP_STORAGE
566         } else if (!strcasecmp(var, "imapuser")) {
567                 strncpy(vmu->imapuser, value, sizeof(vmu->imapuser) - 1);
568 #endif
569         } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
570                 ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
571         } else if (!strcasecmp(var, "saycid")){
572                 ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
573         } else if (!strcasecmp(var,"sendvoicemail")){
574                 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
575         } else if (!strcasecmp(var, "review")){
576                 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
577         } else if (!strcasecmp(var, "tempgreetwarn")){
578                 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);  
579         } else if (!strcasecmp(var, "operator")){
580                 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);       
581         } else if (!strcasecmp(var, "envelope")){
582                 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);       
583         } else if (!strcasecmp(var, "sayduration")){
584                 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);    
585         } else if (!strcasecmp(var, "saydurationm")){
586                 if (sscanf(value, "%d", &x) == 1) {
587                         vmu->saydurationm = x;
588                 } else {
589                         ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
590                 }
591         } else if (!strcasecmp(var, "forcename")){
592                 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);      
593         } else if (!strcasecmp(var, "forcegreetings")){
594                 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);     
595         } else if (!strcasecmp(var, "callback")) {
596                 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
597         } else if (!strcasecmp(var, "dialout")) {
598                 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
599         } else if (!strcasecmp(var, "exitcontext")) {
600                 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
601         } else if (!strcasecmp(var, "maxmsg")) {
602                 vmu->maxmsg = atoi(value);
603                 if (vmu->maxmsg <= 0) {
604                         ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
605                         vmu->maxmsg = MAXMSG;
606                 } else if (vmu->maxmsg > MAXMSGLIMIT) {
607                         ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
608                         vmu->maxmsg = MAXMSGLIMIT;
609                 }
610         } else if (!strcasecmp(var, "volgain")) {
611                 sscanf(value, "%lf", &vmu->volgain);
612         } else if (!strcasecmp(var, "options")) {
613                 apply_options(vmu, value);
614         }
615 }
616
617 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
618 {
619         int res;
620         if (!ast_strlen_zero(vmu->uniqueid)) {
621                 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
622                 if (res > 0) {
623                         ast_copy_string(vmu->password, password, sizeof(vmu->password));
624                         res = 0;
625                 } else if (!res) {
626                         res = -1;
627                 }
628                 return res;
629         }
630         return -1;
631 }
632
633 static void apply_options(struct ast_vm_user *vmu, const char *options)
634 {       /* Destructively Parse options and apply */
635         char *stringp;
636         char *s;
637         char *var, *value;
638         stringp = ast_strdupa(options);
639         while ((s = strsep(&stringp, "|"))) {
640                 value = s;
641                 if ((var = strsep(&value, "=")) && value) {
642                         apply_option(vmu, var, value);
643                 }
644         }       
645 }
646
647 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
648 {
649         struct ast_variable *var, *tmp;
650         struct ast_vm_user *retval;
651
652         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
653                 if (!ivm)
654                         ast_set_flag(retval, VM_ALLOCED);       
655                 else
656                         memset(retval, 0, sizeof(*retval));
657                 if (mailbox) 
658                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
659                 populate_defaults(retval);
660                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
661                         var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
662                 else
663                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
664                 if (var) {
665                         tmp = var;
666                         while (tmp) {
667                                 printf("%s => %s\n", tmp->name, tmp->value);
668                                 if (!strcasecmp(tmp->name, "password")) {
669                                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
670                                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
671                                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
672                                 } else if (!strcasecmp(tmp->name, "pager")) {
673                                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
674                                 } else if (!strcasecmp(tmp->name, "email")) {
675                                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
676                                 } else if (!strcasecmp(tmp->name, "fullname")) {
677                                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
678                                 } else if (!strcasecmp(tmp->name, "context")) {
679                                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
680                                 } else
681                                         apply_option(retval, tmp->name, tmp->value);
682                                 tmp = tmp->next;
683                         } 
684                         ast_variables_destroy(var);
685                 } else { 
686                         if (!ivm) 
687                                 free(retval);
688                         retval = NULL;
689                 }       
690         } 
691         return retval;
692 }
693
694 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
695 {
696         /* This function could be made to generate one from a database, too */
697         struct ast_vm_user *vmu=NULL, *cur;
698         AST_LIST_LOCK(&users);
699
700         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
701                 context = "default";
702
703         AST_LIST_TRAVERSE(&users, cur, list) {
704                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
705                         break;
706                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
707                         break;
708         }
709         if (cur) {
710                 /* Make a copy, so that on a reload, we have no race */
711                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
712                         memcpy(vmu, cur, sizeof(*vmu));
713                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
714                         AST_LIST_NEXT(vmu, list) = NULL;
715                 }
716         } else
717                 vmu = find_user_realtime(ivm, context, mailbox);
718         AST_LIST_UNLOCK(&users);
719         return vmu;
720 }
721
722 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
723 {
724         /* This function could be made to generate one from a database, too */
725         struct ast_vm_user *cur;
726         int res = -1;
727         AST_LIST_LOCK(&users);
728         AST_LIST_TRAVERSE(&users, cur, list) {
729                 if ((!context || !strcasecmp(context, cur->context)) &&
730                         (!strcasecmp(mailbox, cur->mailbox)))
731                                 break;
732         }
733         if (cur) {
734                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
735                 res = 0;
736         }
737         AST_LIST_UNLOCK(&users);
738         return res;
739 }
740
741 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
742 {
743         /*  There's probably a better way of doing this. */
744         /*  That's why I've put the password change in a separate function. */
745         /*  This could also be done with a database function */
746         
747         FILE *configin;
748         FILE *configout;
749         int linenum=0;
750         char inbuf[256];
751         char orig[256];
752         char currcontext[256] ="";
753         char tmpin[PATH_MAX];
754         char tmpout[PATH_MAX];
755         struct stat statbuf;
756
757         if (!change_password_realtime(vmu, newpassword))
758                 return;
759
760         snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
761         snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
762         configin = fopen(tmpin,"r");
763         if (configin)
764                 configout = fopen(tmpout,"w+");
765         else
766                 configout = NULL;
767         if (!configin || !configout) {
768                 if (configin)
769                         fclose(configin);
770                 else
771                         ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
772                 if (configout)
773                         fclose(configout);
774                 else
775                         ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
776                         return;
777         }
778
779         while (!feof(configin)) {
780                 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
781
782                 /* Read in the line */
783                 if (fgets(inbuf, sizeof(inbuf), configin) == NULL)
784                         continue;
785                 linenum++;
786
787                 /* Make a backup of it */
788                 ast_copy_string(orig, inbuf, sizeof(orig));
789
790                 /*
791                   Read the file line by line, split each line into a comment and command section
792                   only parse the command portion of the line
793                 */
794                 if (inbuf[strlen(inbuf) - 1] == '\n')
795                         inbuf[strlen(inbuf) - 1] = '\0';
796
797                 if ((comment = strchr(inbuf, ';')))
798                         *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
799
800                 if (ast_strlen_zero(inbuf)) {
801                         fprintf(configout, "%s", orig);
802                         continue;
803                 }
804
805                 /* Check for a context, first '[' to first ']' */
806                 if ((tmpctx = strchr(inbuf, '['))) {
807                         tmpctxend = strchr(tmpctx, ']');
808                         if (tmpctxend) {
809                                 /* Valid context */
810                                 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
811                                 fprintf(configout, "%s", orig);
812                                 continue;
813                         }
814                 }
815
816                 /* This isn't a context line, check for MBX => PSWD... */
817                 user = inbuf;
818                 if ((pass = strchr(user, '='))) {
819                         /* We have a line in the form of aaaaa=aaaaaa */
820                         *pass++ = '\0';
821
822                         user = ast_strip(user);
823
824                         if (*pass == '>')
825                                 *pass++ = '\0';
826
827                         pass = ast_skip_blanks(pass);
828
829                         /* 
830                            Since no whitespace allowed in fields, or more correctly white space
831                            inside the fields is there for a purpose, we can just terminate pass
832                            at the comma or EOL whichever comes first.
833                         */
834                         if ((rest = strchr(pass, ',')))
835                                 *rest++ = '\0';
836                 } else {
837                         user = NULL;
838                 }                       
839
840                 /* Compare user, pass AND context */
841                 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
842                         !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
843                         !strcasecmp(currcontext, vmu->context)) {
844                         /* This is the line */
845                         if (rest) {
846                                 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
847                         } else {
848                                 fprintf(configout, "%s => %s", user, newpassword);
849                         }
850                         /* If there was a comment on the line print it out */
851                         if (comment) {
852                                 fprintf(configout, ";%s\n", comment);
853                         } else {
854                                 fprintf(configout, "\n");
855                         }
856                 } else {
857                         /* Put it back like it was */
858                         fprintf(configout, "%s", orig);
859                 }
860         }
861         fclose(configin);
862         fclose(configout);
863
864         stat(tmpin, &statbuf);
865         chmod(tmpout, statbuf.st_mode);
866         chown(tmpout, statbuf.st_uid, statbuf.st_gid);
867         unlink(tmpin);
868         rename(tmpout, tmpin);
869         reset_user_pw(vmu->context, vmu->mailbox, newpassword);
870         ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
871 }
872
873 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
874 {
875         char buf[255];
876         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
877         if (!ast_safe_system(buf))
878                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
879 }
880
881 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
882 {
883         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
884 }
885
886 #ifdef IMAP_STORAGE
887 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
888 {
889         char gsmdir[256];
890
891         sprintf(gsmdir,"%s/%s",dir,imapuser);
892         if (mkdir(gsmdir, 01777) && (errno != EEXIST)) {
893                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", gsmdir, strerror(errno));
894                 return sprintf(dest, "%s/msg%04d", dir, num);
895         }
896         /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
897         return sprintf(dest, "%s/%s/msg%04d", dir, imapuser, num);
898 }
899
900 static void vm_imap_delete(int msgnum, struct vm_state *vms)
901 {
902         unsigned long messageNum = 0;
903         char arg[10];
904
905         /* find real message number based on msgnum */
906         /* this may be an index into vms->msgArray based on the msgnum. */
907
908         messageNum = vms->msgArray[msgnum];
909         if (messageNum == 0) {
910                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
911                 return;
912         }
913         ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
914         /* delete message */
915         sprintf (arg,"%lu",messageNum);
916         mail_setflag (vms->mailstream,arg,"\\DELETED");
917 }
918
919 #endif
920 static int make_file(char *dest, int len, char *dir, int num)
921 {
922         return snprintf(dest, len, "%s/msg%04d", dir, num);
923 }
924
925 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
926  * \param dest    String. base directory.
927  * \param context String. Ignored if is null or empty string.
928  * \param ext     String. Ignored if is null or empty string.
929  * \param folder  String. Ignored if is null or empty string. 
930  * \return 0 on failure, 1 on success.
931  */
932 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
933 {
934         mode_t  mode = VOICEMAIL_DIR_MODE;
935
936         if (!ast_strlen_zero(context)) {
937                 make_dir(dest, len, context, "", "");
938                 if (mkdir(dest, mode) && errno != EEXIST) {
939                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
940                         return 0;
941                 }
942         }
943         if (!ast_strlen_zero(ext)) {
944                 make_dir(dest, len, context, ext, "");
945                 if (mkdir(dest, mode) && errno != EEXIST) {
946                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
947                         return 0;
948                 }
949         }
950         if (!ast_strlen_zero(folder)) {
951                 make_dir(dest, len, context, ext, folder);
952                 if (mkdir(dest, mode) && errno != EEXIST) {
953                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
954                         return 0;
955                 }
956         }
957         return 1;
958 }
959
960 /* only return failure if ast_lock_path returns 'timeout',
961    not if the path does not exist or any other reason
962 */
963 static int vm_lock_path(const char *path)
964 {
965         switch (ast_lock_path(path)) {
966         case AST_LOCK_TIMEOUT:
967                 return -1;
968         default:
969                 return 0;
970         }
971 }
972
973
974 #ifdef ODBC_STORAGE
975 static int retrieve_file(char *dir, int msgnum)
976 {
977         int x = 0;
978         int res;
979         int fd=-1;
980         size_t fdlen = 0;
981         void *fdm=NULL;
982         SQLSMALLINT colcount=0;
983         SQLHSTMT stmt;
984         char sql[256];
985         char fmt[80]="";
986         char *c;
987         char coltitle[256];
988         SQLSMALLINT collen;
989         SQLSMALLINT datatype;
990         SQLSMALLINT decimaldigits;
991         SQLSMALLINT nullable;
992         SQLULEN colsize;
993         FILE *f=NULL;
994         char rowdata[80];
995         char fn[256];
996         char full_fn[256];
997         char msgnums[80];
998         
999         struct odbc_obj *obj;
1000         obj = odbc_request_obj(odbc_database, 0);
1001         if (obj) {
1002                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1003                 c = strchr(fmt, '|');
1004                 if (c)
1005                         *c = '\0';
1006                 if (!strcasecmp(fmt, "wav49"))
1007                         strcpy(fmt, "WAV");
1008                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1009                 if (msgnum > -1)
1010                         make_file(fn, sizeof(fn), dir, msgnum);
1011                 else
1012                         ast_copy_string(fn, dir, sizeof(fn));
1013                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1014                 f = fopen(full_fn, "w+");
1015                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1016                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1017                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1018                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1019                         odbc_release_obj(obj);
1020                         goto yuck;
1021                 }
1022                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1023                 res = SQLPrepare(stmt, sql, SQL_NTS);
1024                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1025                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1026                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1027                         odbc_release_obj(obj);
1028                         goto yuck;
1029                 }
1030                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1031                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1032                 res = odbc_smart_execute(obj, stmt);
1033                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1034                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1035                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1036                         odbc_release_obj(obj);
1037                         goto yuck;
1038                 }
1039                 res = SQLFetch(stmt);
1040                 if (res == SQL_NO_DATA) {
1041                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1042                         odbc_release_obj(obj);
1043                         goto yuck;
1044                 }
1045                 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1046                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1047                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1048                         odbc_release_obj(obj);
1049                         goto yuck;
1050                 }
1051                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
1052                 if (fd < 0) {
1053                         ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1054                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1055                         odbc_release_obj(obj);
1056                         goto yuck;
1057                 }
1058                 res = SQLNumResultCols(stmt, &colcount);
1059                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
1060                         ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1061                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1062                         odbc_release_obj(obj);
1063                         goto yuck;
1064                 }
1065                 if (f) 
1066                         fprintf(f, "[message]\n");
1067                 for (x=0;x<colcount;x++) {
1068                         rowdata[0] = '\0';
1069                         collen = sizeof(coltitle);
1070                         res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen, 
1071                                                 &datatype, &colsize, &decimaldigits, &nullable);
1072                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1073                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1074                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1075                                 odbc_release_obj(obj);
1076                                 goto yuck;
1077                         }
1078                         if (!strcasecmp(coltitle, "recording")) {
1079                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
1080                                 fdlen = colsize;
1081                                 if (fd > -1) {
1082                                         char tmp[1]="";
1083                                         lseek(fd, fdlen - 1, SEEK_SET);
1084                                         if (write(fd, tmp, 1) != 1) {
1085                                                 close(fd);
1086                                                 fd = -1;
1087                                                 continue;
1088                                         }
1089                                         if (fd > -1)
1090                                                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1091                                 }
1092                                 if (fdm) {
1093                                         memset(fdm, 0, fdlen);
1094                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
1095                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1096                                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1097                                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1098                                                 odbc_release_obj(obj);
1099                                                 goto yuck;
1100                                         }
1101                                 }
1102                         } else {
1103                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1104                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1105                                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1106                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1107                                         odbc_release_obj(obj);
1108                                         goto yuck;
1109                                 }
1110                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1111                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
1112                         }
1113                 }
1114                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1115                 odbc_release_obj(obj);
1116         } else
1117                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1118 yuck:   
1119         if (f)
1120                 fclose(f);
1121         if (fdm)
1122                 munmap(fdm, fdlen);
1123         if (fd > -1)
1124                 close(fd);
1125         return x - 1;
1126 }
1127
1128 static int remove_file(char *dir, int msgnum)
1129 {
1130         char fn[256];
1131         char full_fn[256];
1132         char msgnums[80];
1133         
1134         if (msgnum > -1) {
1135                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1136                 make_file(fn, sizeof(fn), dir, msgnum);
1137         } else
1138                 ast_copy_string(fn, dir, sizeof(fn));
1139         ast_filedelete(fn, NULL);       
1140         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1141         unlink(full_fn);
1142         return 0;
1143 }
1144
1145 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1146 {
1147         int x = 0;
1148         int res;
1149         SQLHSTMT stmt;
1150         char sql[256];
1151         char rowdata[20];
1152         
1153         struct odbc_obj *obj;
1154         obj = odbc_request_obj(odbc_database, 0);
1155         if (obj) {
1156                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1157                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1158                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1159                         odbc_release_obj(obj);
1160                         goto yuck;
1161                 }
1162                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1163                 res = SQLPrepare(stmt, sql, SQL_NTS);
1164                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1165                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1166                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1167                         odbc_release_obj(obj);
1168                         goto yuck;
1169                 }
1170                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1171                 res = odbc_smart_execute(obj, stmt);
1172                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1173                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1174                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1175                         odbc_release_obj(obj);
1176                         goto yuck;
1177                 }
1178                 res = SQLFetch(stmt);
1179                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1180                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1181                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1182                         odbc_release_obj(obj);
1183                         goto yuck;
1184                 }
1185                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1186                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1187                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1188                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1189                         odbc_release_obj(obj);
1190                         goto yuck;
1191                 }
1192                 if (sscanf(rowdata, "%d", &x) != 1)
1193                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1194                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1195                 odbc_release_obj(obj);
1196         } else
1197                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1198 yuck:   
1199         return x - 1;
1200 }
1201
1202 static int message_exists(char *dir, int msgnum)
1203 {
1204         int x = 0;
1205         int res;
1206         SQLHSTMT stmt;
1207         char sql[256];
1208         char rowdata[20];
1209         char msgnums[20];
1210         
1211         struct odbc_obj *obj;
1212         obj = odbc_request_obj(odbc_database, 0);
1213         if (obj) {
1214                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1215                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1216                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1217                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1218                         odbc_release_obj(obj);
1219                         goto yuck;
1220                 }
1221                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1222                 res = SQLPrepare(stmt, sql, SQL_NTS);
1223                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1224                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1225                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1226                         odbc_release_obj(obj);
1227                         goto yuck;
1228                 }
1229                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1230                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1231                 res = odbc_smart_execute(obj, stmt);
1232                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1233                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1234                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1235                         odbc_release_obj(obj);
1236                         goto yuck;
1237                 }
1238                 res = SQLFetch(stmt);
1239                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1240                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1241                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1242                         odbc_release_obj(obj);
1243                         goto yuck;
1244                 }
1245                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1246                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1247                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1248                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1249                         odbc_release_obj(obj);
1250                         goto yuck;
1251                 }
1252                 if (sscanf(rowdata, "%d", &x) != 1)
1253                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1254                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1255                 odbc_release_obj(obj);
1256         } else
1257                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1258 yuck:   
1259         return x;
1260 }
1261
1262 static int count_messages(struct ast_vm_user *vmu, char *dir)
1263 {
1264         return last_message_index(vmu, dir) + 1;
1265 }
1266
1267 static void delete_file(char *sdir, int smsg)
1268 {
1269         int res;
1270         SQLHSTMT stmt;
1271         char sql[256];
1272         char msgnums[20];
1273         
1274         struct odbc_obj *obj;
1275         obj = odbc_request_obj(odbc_database, 0);
1276         if (obj) {
1277                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1278                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1279                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1280                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1281                         odbc_release_obj(obj);
1282                         goto yuck;
1283                 }
1284                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1285                 res = SQLPrepare(stmt, sql, SQL_NTS);
1286                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1287                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1288                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1289                         odbc_release_obj(obj);
1290                         goto yuck;
1291                 }
1292                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1293                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1294                 res = odbc_smart_execute(obj, stmt);
1295                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1296                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1297                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1298                         odbc_release_obj(obj);
1299                         goto yuck;
1300                 }
1301                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1302                 odbc_release_obj(obj);
1303         } else
1304                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1305 yuck:
1306         return; 
1307 }
1308
1309 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1310 {
1311         int res;
1312         SQLHSTMT stmt;
1313         char sql[512];
1314         char msgnums[20];
1315         char msgnumd[20];
1316         struct odbc_obj *obj;
1317
1318         delete_file(ddir, dmsg);
1319         obj = odbc_request_obj(odbc_database, 0);
1320         if (obj) {
1321                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1322                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1323                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1324                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1325                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1326                         odbc_release_obj(obj);
1327                         goto yuck;
1328                 }
1329                 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); 
1330                 res = SQLPrepare(stmt, sql, SQL_NTS);
1331                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1332                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1333                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1334                         odbc_release_obj(obj);
1335                         goto yuck;
1336                 }
1337                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1338                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1339                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1340                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1341                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1342                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1343                 res = odbc_smart_execute(obj, stmt);
1344                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1345                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1346                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1347                         odbc_release_obj(obj);
1348                         goto yuck;
1349                 }
1350                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1351                 odbc_release_obj(obj);
1352         } else
1353                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1354 yuck:
1355         return; 
1356 }
1357
1358 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1359 {
1360         int x = 0;
1361         int res;
1362         int fd = -1;
1363         void *fdm=NULL;
1364         size_t fdlen = -1;
1365         SQLHSTMT stmt;
1366         SQLINTEGER len;
1367         char sql[256];
1368         char msgnums[20];
1369         char fn[256];
1370         char full_fn[256];
1371         char fmt[80]="";
1372         char *c;
1373         char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1374         char *category = "";
1375         struct ast_config *cfg=NULL;
1376         struct odbc_obj *obj;
1377
1378         delete_file(dir, msgnum);
1379         obj = odbc_request_obj(odbc_database, 0);
1380         if (obj) {
1381                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1382                 c = strchr(fmt, '|');
1383                 if (c)
1384                         *c = '\0';
1385                 if (!strcasecmp(fmt, "wav49"))
1386                         strcpy(fmt, "WAV");
1387                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1388                 if (msgnum > -1)
1389                         make_file(fn, sizeof(fn), dir, msgnum);
1390                 else
1391                         ast_copy_string(fn, dir, sizeof(fn));
1392                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1393                 cfg = ast_config_load(full_fn);
1394                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1395                 fd = open(full_fn, O_RDWR);
1396                 if (fd < 0) {
1397                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1398                         odbc_release_obj(obj);
1399                         goto yuck;
1400                 }
1401                 if (cfg) {
1402                         context = ast_variable_retrieve(cfg, "message", "context");
1403                         if (!context) context = "";
1404                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1405                         if (!macrocontext) macrocontext = "";
1406                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1407                         if (!callerid) callerid = "";
1408                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1409                         if (!origtime) origtime = "";
1410                         duration = ast_variable_retrieve(cfg, "message", "duration");
1411                         if (!duration) duration = "";
1412                         category = ast_variable_retrieve(cfg, "message", "category");
1413                         if (!category) category = "";
1414                 }
1415                 fdlen = lseek(fd, 0, SEEK_END);
1416                 lseek(fd, 0, SEEK_SET);
1417                 printf("Length is %zd\n", fdlen);
1418                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1419                 if (!fdm) {
1420                         ast_log(LOG_WARNING, "Memory map failed!\n");
1421                         odbc_release_obj(obj);
1422                         goto yuck;
1423                 } 
1424                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1425                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1426                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1427                         odbc_release_obj(obj);
1428                         goto yuck;
1429                 }
1430                 if (!ast_strlen_zero(category)) 
1431                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1432                 else
1433                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1434                 res = SQLPrepare(stmt, sql, SQL_NTS);
1435                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1436                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1437                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1438                         odbc_release_obj(obj);
1439                         goto yuck;
1440                 }
1441                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1442                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1443                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1444                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1445                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1446                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1447                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1448                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1449                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1450                 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1451                 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1452                 if (!ast_strlen_zero(category))
1453                         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1454                 res = odbc_smart_execute(obj, stmt);
1455                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1456                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1457                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1458                         odbc_release_obj(obj);
1459                         goto yuck;
1460                 }
1461                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1462                 odbc_release_obj(obj);
1463         } else
1464                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1465 yuck:   
1466         if (cfg)
1467                 ast_config_destroy(cfg);
1468         if (fdm)
1469                 munmap(fdm, fdlen);
1470         if (fd > -1)
1471                 close(fd);
1472         return x;
1473 }
1474
1475 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1476 {
1477         int res;
1478         SQLHSTMT stmt;
1479         char sql[256];
1480         char msgnums[20];
1481         char msgnumd[20];
1482         struct odbc_obj *obj;
1483
1484         delete_file(ddir, dmsg);
1485         obj = odbc_request_obj(odbc_database, 0);
1486         if (obj) {
1487                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1488                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1489                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1490                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1491                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1492                         odbc_release_obj(obj);
1493                         goto yuck;
1494                 }
1495                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1496                 res = SQLPrepare(stmt, sql, SQL_NTS);
1497                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1498                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1499                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1500                         odbc_release_obj(obj);
1501                         goto yuck;
1502                 }
1503                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1504                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1505                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1506                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1507                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1508                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1509                 res = odbc_smart_execute(obj, stmt);
1510                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1511                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1512                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1513                         odbc_release_obj(obj);
1514                         goto yuck;
1515                 }
1516                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1517                 odbc_release_obj(obj);
1518         } else
1519                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1520 yuck:
1521         return; 
1522 }
1523
1524 #else
1525 static int count_messages(struct ast_vm_user *vmu, char *dir)
1526 {
1527         /* Find all .txt files - even if they are not in sequence from 0000 */
1528
1529         int vmcount = 0;
1530         DIR *vmdir = NULL;
1531         struct dirent *vment = NULL;
1532
1533         if (vm_lock_path(dir))
1534                 return ERROR_LOCK_PATH;
1535
1536         if ((vmdir = opendir(dir))) {
1537                 while ((vment = readdir(vmdir))) {
1538                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
1539                                 vmcount++;
1540                 }
1541                 closedir(vmdir);
1542         }
1543         ast_unlock_path(dir);
1544         
1545         return vmcount;
1546 }
1547
1548 static void rename_file(char *sfn, char *dfn)
1549 {
1550         char stxt[256];
1551         char dtxt[256];
1552         ast_filerename(sfn,dfn,NULL);
1553         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1554         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1555         rename(stxt, dtxt);
1556 }
1557
1558 static int copy(char *infile, char *outfile)
1559 {
1560         int ifd;
1561         int ofd;
1562         int res;
1563         int len;
1564         char buf[4096];
1565
1566 #ifdef HARDLINK_WHEN_POSSIBLE
1567         /* Hard link if possible; saves disk space & is faster */
1568         if (link(infile, outfile)) {
1569 #endif
1570                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1571                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1572                         return -1;
1573                 }
1574                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1575                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1576                         close(ifd);
1577                         return -1;
1578                 }
1579                 do {
1580                         len = read(ifd, buf, sizeof(buf));
1581                         if (len < 0) {
1582                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1583                                 close(ifd);
1584                                 close(ofd);
1585                                 unlink(outfile);
1586                         }
1587                         if (len) {
1588                                 res = write(ofd, buf, len);
1589                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1590                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1591                                         close(ifd);
1592                                         close(ofd);
1593                                         unlink(outfile);
1594                                 }
1595                         }
1596                 } while (len);
1597                 close(ifd);
1598                 close(ofd);
1599                 return 0;
1600 #ifdef HARDLINK_WHEN_POSSIBLE
1601         } else {
1602                 /* Hard link succeeded */
1603                 return 0;
1604         }
1605 #endif
1606 }
1607
1608 static void copy_file(char *frompath, char *topath)
1609 {
1610         char frompath2[256],topath2[256];
1611         ast_filecopy(frompath, topath, NULL);
1612         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1613         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1614         copy(frompath2, topath2);
1615 }
1616
1617 /*
1618  * A negative return value indicates an error.
1619  */
1620 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1621 {
1622         int x;
1623         char fn[256];
1624
1625         if (vm_lock_path(dir))
1626                 return ERROR_LOCK_PATH;
1627
1628         for (x = 0; x < vmu->maxmsg; x++) {
1629                 make_file(fn, sizeof(fn), dir, x);
1630                 if (ast_fileexists(fn, NULL, NULL) < 1)
1631                         break;
1632         }
1633         ast_unlock_path(dir);
1634
1635         return x - 1;
1636 }
1637
1638 static int vm_delete(char *file)
1639 {
1640         char *txt;
1641         int txtsize = 0;
1642
1643         txtsize = (strlen(file) + 5)*sizeof(char);
1644         txt = alloca(txtsize);
1645         /* Sprintf here would safe because we alloca'd exactly the right length,
1646          * but trying to eliminate all sprintf's anyhow
1647          */
1648         snprintf(txt, txtsize, "%s.txt", file);
1649         unlink(txt);
1650         return ast_filedelete(file, NULL);
1651 }
1652
1653
1654 #endif
1655 static int
1656 inbuf(struct baseio *bio, FILE *fi)
1657 {
1658         int l;
1659
1660         if (bio->ateof)
1661                 return 0;
1662
1663         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1664                 if (ferror(fi))
1665                         return -1;
1666
1667                 bio->ateof = 1;
1668                 return 0;
1669         }
1670
1671         bio->iolen= l;
1672         bio->iocp= 0;
1673
1674         return 1;
1675 }
1676
1677 static int 
1678 inchar(struct baseio *bio, FILE *fi)
1679 {
1680         if (bio->iocp>=bio->iolen) {
1681                 if (!inbuf(bio, fi))
1682                         return EOF;
1683         }
1684
1685         return bio->iobuf[bio->iocp++];
1686 }
1687
1688 static int
1689 ochar(struct baseio *bio, int c, FILE *so)
1690 {
1691         if (bio->linelength>=BASELINELEN) {
1692                 if (fputs(eol,so)==EOF)
1693                         return -1;
1694
1695                 bio->linelength= 0;
1696         }
1697
1698         if (putc(((unsigned char)c),so)==EOF)
1699                 return -1;
1700
1701         bio->linelength++;
1702
1703         return 1;
1704 }
1705
1706 static int base_encode(char *filename, FILE *so)
1707 {
1708         unsigned char dtable[BASEMAXINLINE];
1709         int i,hiteof= 0;
1710         FILE *fi;
1711         struct baseio bio;
1712
1713         memset(&bio, 0, sizeof(bio));
1714         bio.iocp = BASEMAXINLINE;
1715
1716         if (!(fi = fopen(filename, "rb"))) {
1717                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1718                 return -1;
1719         }
1720
1721         for (i= 0;i<9;i++) {
1722                 dtable[i]= 'A'+i;
1723                 dtable[i+9]= 'J'+i;
1724                 dtable[26+i]= 'a'+i;
1725                 dtable[26+i+9]= 'j'+i;
1726         }
1727         for (i= 0;i<8;i++) {
1728                 dtable[i+18]= 'S'+i;
1729                 dtable[26+i+18]= 's'+i;
1730         }
1731         for (i= 0;i<10;i++) {
1732                 dtable[52+i]= '0'+i;
1733         }
1734         dtable[62]= '+';
1735         dtable[63]= '/';
1736
1737         while (!hiteof){
1738                 unsigned char igroup[3],ogroup[4];
1739                 int c,n;
1740
1741                 igroup[0]= igroup[1]= igroup[2]= 0;
1742
1743                 for (n= 0;n<3;n++) {
1744                         if ((c = inchar(&bio, fi)) == EOF) {
1745                                 hiteof= 1;
1746                                 break;
1747                         }
1748
1749                         igroup[n]= (unsigned char)c;
1750                 }
1751
1752                 if (n> 0) {
1753                         ogroup[0]= dtable[igroup[0]>>2];
1754                         ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1755                         ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1756                         ogroup[3]= dtable[igroup[2]&0x3F];
1757
1758                         if (n<3) {
1759                                 ogroup[3]= '=';
1760
1761                                 if (n<2)
1762                                         ogroup[2]= '=';
1763                         }
1764
1765                         for (i= 0;i<4;i++)
1766                                 ochar(&bio, ogroup[i], so);
1767                 }
1768         }
1769
1770         if (fputs(eol,so)==EOF)
1771                 return 0;
1772
1773         fclose(fi);
1774
1775         return 1;
1776 }
1777
1778 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)
1779 {
1780         char callerid[256];
1781         /* Prepare variables for substition in email body and subject */
1782         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1783         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1784         snprintf(passdata, passdatasize, "%d", msgnum);
1785         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1786         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1787         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1788         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1789         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1790         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1791         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1792         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1793 }
1794
1795 /*
1796  * fill in *tm for current time according to the proper timezone, if any.
1797  * Return tm so it can be used as a function argument.
1798  */
1799 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1800 {
1801         const struct vm_zone *z = NULL;
1802         time_t t = time(NULL);
1803
1804         /* Does this user have a timezone specified? */
1805         if (!ast_strlen_zero(vmu->zonetag)) {
1806                 /* Find the zone in the list */
1807                 AST_LIST_LOCK(&zones);
1808                 AST_LIST_TRAVERSE(&zones, z, list) {
1809                         if (!strcmp(z->name, vmu->zonetag))
1810                                 break;
1811                 }
1812                 AST_LIST_UNLOCK(&zones);
1813         }
1814         ast_localtime(&t, tm, z ? z->timezone : NULL);
1815         return tm;
1816 }
1817
1818 /* same as mkstemp, but return a FILE * */
1819 static FILE *vm_mkftemp(char *template)
1820 {
1821         FILE *p = NULL;
1822         int pfd = mkstemp(template);
1823         if (pfd > -1) {
1824                 p = fdopen(pfd, "w");
1825                 if (!p) {
1826                         close(pfd);
1827                         pfd = -1;
1828                 }
1829         }
1830         return p;
1831 }
1832
1833 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)
1834 {
1835         FILE *p=NULL;
1836         char date[256];
1837         char host[MAXHOSTNAMELEN] = "";
1838         char who[256];
1839         char bound[256];
1840         char fname[256];
1841         char dur[256];
1842         char tmp[80] = "/tmp/astmail-XXXXXX";
1843         char tmp2[256];
1844         char tmpcmd[256];
1845         struct tm tm;
1846
1847         if (vmu && ast_strlen_zero(vmu->email)) {
1848                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
1849                 return(0);
1850         }
1851         if (!strcmp(format, "wav49"))
1852                 format = "WAV";
1853         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));
1854         /* Make a temporary file instead of piping directly to sendmail, in case the mail
1855            command hangs */
1856         if ((p = vm_mkftemp(tmp)) == NULL) {
1857                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1858                 return -1;
1859         } else {
1860                 gethostname(host, sizeof(host)-1);
1861                 if (strchr(srcemail, '@'))
1862                         ast_copy_string(who, srcemail, sizeof(who));
1863                 else {
1864                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1865                 }
1866                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1867                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1868                 fprintf(p, "Date: %s\n", date);
1869
1870                 /* Set date format for voicemail mail */
1871                 strftime(date, sizeof(date), emaildateformat, &tm);
1872
1873                 if (*fromstring) {
1874                         struct ast_channel *ast;
1875                         if ((ast = ast_channel_alloc(0))) {
1876                                 char *passdata;
1877                                 int vmlen = strlen(fromstring)*3 + 200;
1878                                 if ((passdata = alloca(vmlen))) {
1879                                         memset(passdata, 0, vmlen);
1880                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1881                                         pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1882                                         fprintf(p, "From: %s <%s>\n",passdata,who);
1883                                 } else
1884                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1885                                 ast_channel_free(ast);
1886                         } else
1887                                 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1888                 } else
1889                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
1890                 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1891
1892                 if (emailsubject) {
1893                         struct ast_channel *ast;
1894                         if ((ast = ast_channel_alloc(0))) {
1895                                 char *passdata;
1896                                 int vmlen = strlen(emailsubject)*3 + 200;
1897                                 if ((passdata = alloca(vmlen))) {
1898                                         memset(passdata, 0, vmlen);
1899                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1900                                         pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1901                                         fprintf(p, "Subject: %s\n", passdata);
1902                                 } else
1903                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1904                                 ast_channel_free(ast);
1905                         } else
1906                                 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1907                 } else
1908                 if (*emailtitle) {
1909                         fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1910                         fprintf(p,"\n") ;
1911                 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1912                         fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1913                 else
1914                         fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1915                 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1916 #ifdef IMAP_STORAGE
1917                 /* additional information needed for IMAP searching */
1918                 fprintf(p, "X-Asterisk-VM-Message-Num: %d\n", msgnum + 1);
1919                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s\n", ext); */
1920                 fprintf(p, "X-Asterisk-VM-Server-Name: %s\n", fromstring);
1921                 fprintf(p, "X-Asterisk-VM-Context: %s\n", context);
1922                 fprintf(p, "X-Asterisk-VM-Extension: %s\n", chan->exten);
1923                 fprintf(p, "X-Asterisk-VM-Priority: %d\n", chan->priority);
1924                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s\n", chan->name);
1925                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s\n", cidnum);
1926                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s\n", cidname);
1927                 fprintf(p, "X-Asterisk-VM-Duration: %d\n", duration);
1928                 if (category != NULL)
1929                         fprintf(p, "X-Asterisk-VM-Category: %s\n", category);
1930                 fprintf(p, "X-Asterisk-VM-Orig-date: %s\n", date);
1931                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld\n", (long)time(NULL));
1932 #endif
1933                 if (!ast_strlen_zero(cidnum))
1934                         fprintf(p, "X-Asterisk-CallerID: %s\n", cidnum);
1935                 if (!ast_strlen_zero(cidname))
1936                         fprintf(p, "X-Asterisk-CallerIDName: %s\n", cidname);
1937                 fprintf(p, "MIME-Version: 1.0\n");
1938                 if (attach_user_voicemail) {
1939                         /* Something unique. */
1940                         snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1941
1942                         fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1943
1944                         fprintf(p, "--%s\n", bound);
1945                 }
1946                 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1947                 if (emailbody) {
1948                         struct ast_channel *ast;
1949                         if ((ast = ast_channel_alloc(0))) {
1950                                 char *passdata;
1951                                 int vmlen = strlen(emailbody)*3 + 200;
1952                                 if ((passdata = alloca(vmlen))) {
1953                                         memset(passdata, 0, vmlen);
1954                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1955                                         pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1956                                         fprintf(p, "%s\n", passdata);
1957                                 } else
1958                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1959                                 ast_channel_free(ast);
1960                         } else
1961                                 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1962                 } else {
1963                         fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1964
1965                         "in mailbox %s from %s, on %s so you might\n"
1966                         "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname, 
1967                         dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1968                 }
1969                 if (attach_user_voicemail) {
1970                         /* Eww. We want formats to tell us their own MIME type */
1971                         char *ctype = (!strcasecmp(format, "ogg")) ?  "application/" : "audio/x-";
1972                         char tmpdir[256], newtmp[256];
1973                         int tmpfd;
1974                 
1975                         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
1976                         snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
1977                         tmpfd = mkstemp(newtmp);
1978                         ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
1979                         if (vmu->volgain < -.001 || vmu->volgain > .001) {
1980                                 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
1981                                 ast_safe_system(tmpcmd);
1982                                 attach = newtmp;
1983                                 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
1984                         }
1985                         fprintf(p, "--%s\n", bound);
1986                         fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
1987                         fprintf(p, "Content-Transfer-Encoding: base64\n");
1988                         fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1989                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1990
1991                         snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1992                         base_encode(fname, p);
1993 #ifdef IMAP_STORAGE
1994                         /* only attach if necessary */
1995                         ast_log(LOG_DEBUG, "Before second attachment with format set to:%s\n",format);
1996                         if (strcmp(format, "gsm")) {
1997                            fprintf(p, "--%s\n", bound);
1998                            fprintf(p, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"\n", msgnum, format);
1999                            fprintf(p, "Content-Transfer-Encoding: base64\n");
2000                            fprintf(p, "Content-Description: Voicemail sound attachment.\n");
2001                            fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"\n\n", msgnum);
2002
2003                            snprintf(fname, sizeof(fname), "%s.gsm", attach);
2004                            base_encode(fname, p);
2005                         }
2006 #endif
2007                         fprintf(p, "\n\n--%s--\n.\n", bound);
2008                         if (tmpfd > -1)
2009                                 close(tmpfd);
2010                         unlink(newtmp);
2011                 }
2012                 fclose(p);
2013                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2014                 ast_safe_system(tmp2);
2015                 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2016         }
2017         return 0;
2018 }
2019
2020 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)
2021 {
2022         char date[256];
2023         char host[MAXHOSTNAMELEN]="";
2024         char who[256];
2025         char dur[256];
2026         char tmp[80] = "/tmp/astmail-XXXXXX";
2027         char tmp2[256];
2028         struct tm tm;
2029         FILE *p;
2030
2031         if ((p = vm_mkftemp(tmp)) == NULL) {
2032                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2033                 return -1;
2034         } else {
2035                 gethostname(host, sizeof(host)-1);
2036                 if (strchr(srcemail, '@'))
2037                         ast_copy_string(who, srcemail, sizeof(who));
2038                 else {
2039                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2040                 }
2041                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2042                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2043                 fprintf(p, "Date: %s\n", date);
2044
2045                 if (*pagerfromstring) {
2046                         struct ast_channel *ast;
2047                         if ((ast = ast_channel_alloc(0))) {
2048                                 char *passdata;
2049                                 int vmlen = strlen(fromstring)*3 + 200;
2050                                 if ((passdata = alloca(vmlen))) {
2051                                         memset(passdata, 0, vmlen);
2052                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2053                                         pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2054                                         fprintf(p, "From: %s <%s>\n", passdata, who);
2055                                 } else 
2056                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2057                                 ast_channel_free(ast);
2058                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2059                 } else
2060                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
2061                 fprintf(p, "To: %s\n", pager);
2062                 if (pagersubject) {
2063                         struct ast_channel *ast;
2064                         if ((ast = ast_channel_alloc(0))) {
2065                                 char *passdata;
2066                                 int vmlen = strlen(pagersubject) * 3 + 200;
2067                                 if ((passdata = alloca(vmlen))) {
2068                                         memset(passdata, 0, vmlen);
2069                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2070                                         pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2071                                         fprintf(p, "Subject: %s\n\n", passdata);
2072                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2073                                 ast_channel_free(ast);
2074                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2075                 } else
2076                         fprintf(p, "Subject: New VM\n\n");
2077                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2078                 if (pagerbody) {
2079                         struct ast_channel *ast;
2080                         if ((ast = ast_channel_alloc(0))) {
2081                                 char *passdata;
2082                                 int vmlen = strlen(pagerbody)*3 + 200;
2083                                 if ((passdata = alloca(vmlen))) {
2084                                         memset(passdata, 0, vmlen);
2085                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2086                                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2087                                         fprintf(p, "%s\n", passdata);
2088                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2089                         ast_channel_free(ast);
2090                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2091                 } else {
2092                         fprintf(p, "New %s long msg in box %s\n"
2093                                         "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2094                 }
2095                 fclose(p);
2096                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2097                 ast_safe_system(tmp2);
2098                 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2099         }
2100         return 0;
2101 }
2102
2103 #ifndef IMAP_STORAGE
2104 static int get_date(char *s, int len)
2105 {
2106         struct tm tm;
2107         time_t t;
2108         t = time(0);
2109         localtime_r(&t,&tm);
2110         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2111 }
2112 #endif
2113
2114 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2115 {
2116         int res;
2117         char fn[256];
2118         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2119         RETRIEVE(fn, -1);
2120         if (ast_fileexists(fn, NULL, NULL) > 0) {
2121                 res = ast_stream_and_wait(chan, fn, chan->language, ecodes);
2122                 if (res) {
2123                         DISPOSE(fn, -1);
2124                         return res;
2125                 }
2126         } else {
2127                 /* Dispose just in case */
2128                 DISPOSE(fn, -1);
2129                 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
2130                 if (res)
2131                         return res;
2132                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2133                 if (res)
2134                         return res;
2135         }
2136         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
2137         return res;
2138 }
2139
2140 static void free_user(struct ast_vm_user *vmu)
2141 {
2142         if (ast_test_flag(vmu, VM_ALLOCED))
2143                 free(vmu);
2144 }
2145
2146 static void free_zone(struct vm_zone *z)
2147 {
2148         free(z);
2149 }
2150
2151 static const char *mbox(int id)
2152 {
2153         static const char *msgs[] = {
2154                 "INBOX",
2155                 "Old",
2156                 "Work",   
2157                 "Family",
2158                 "Friends",
2159                 "Cust1",
2160                 "Cust2",
2161                 "Cust3",
2162                 "Cust4",  
2163                 "Cust5",
2164         };
2165         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2166 }
2167
2168 #ifdef ODBC_STORAGE
2169 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2170 {
2171         int x = -1;
2172         int res;
2173         SQLHSTMT stmt;
2174         char sql[256];
2175         char rowdata[20];
2176         char tmp[256]="";
2177         struct odbc_obj *obj;
2178         char *context;
2179
2180         if (newmsgs)
2181                 *newmsgs = 0;
2182         if (oldmsgs)
2183                 *oldmsgs = 0;
2184
2185         /* If no mailbox, return immediately */
2186         if (ast_strlen_zero(mailbox))
2187                 return 0;
2188
2189         ast_copy_string(tmp, mailbox, sizeof(tmp));
2190         
2191         context = strchr(tmp, '@');
2192         if (context) {   
2193                 *context = '\0';
2194                 context++;
2195         } else  
2196                 context = "default";
2197         
2198         obj = odbc_request_obj(odbc_database, 0);
2199         if (obj) {
2200                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2201                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2202                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2203                         odbc_release_obj(obj);
2204                         goto yuck;
2205                 }
2206                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2207                 res = SQLPrepare(stmt, sql, SQL_NTS);
2208                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2209                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2210                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2211                         odbc_release_obj(obj);
2212                         goto yuck;
2213                 }
2214                 res = odbc_smart_execute(obj, stmt);
2215                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2216                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2217                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2218                         odbc_release_obj(obj);
2219                         goto yuck;
2220                 }
2221                 res = SQLFetch(stmt);
2222                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2223                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2224                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2225                         odbc_release_obj(obj);
2226                         goto yuck;
2227                 }
2228                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2229                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2230                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2231                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2232                         odbc_release_obj(obj);
2233                         goto yuck;
2234                 }
2235                 *newmsgs = atoi(rowdata);
2236                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2237
2238                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2239                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2240                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2241                         odbc_release_obj(obj);
2242                         goto yuck;
2243                 }
2244                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2245                 res = SQLPrepare(stmt, sql, SQL_NTS);
2246                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2247                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2248                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2249                         odbc_release_obj(obj);
2250                         goto yuck;
2251                 }
2252                 res = odbc_smart_execute(obj, stmt);
2253                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2254                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2255                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2256                         odbc_release_obj(obj);
2257                         goto yuck;
2258                 }
2259                 res = SQLFetch(stmt);
2260                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2261                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2262                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2263                         odbc_release_obj(obj);
2264                         goto yuck;
2265                 }
2266                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2267                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2268                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2269                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2270                         odbc_release_obj(obj);
2271                         goto yuck;
2272                 }
2273                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2274                 odbc_release_obj(obj);
2275                 *oldmsgs = atoi(rowdata);
2276                 x = 0;
2277         } else
2278                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2279                 
2280 yuck:   
2281         return x;
2282 }
2283
2284 static int messagecount(const char *context, const char *mailbox, const char *folder)
2285 {
2286         struct odbc_obj *obj = NULL;
2287         int nummsgs = 0;
2288         int res;
2289         SQLHSTMT stmt = NULL;
2290         char sql[256];
2291         char rowdata[20];
2292         if (!folder)
2293                 folder = "INBOX";
2294         /* If no mailbox, return immediately */
2295         if (ast_strlen_zero(mailbox))
2296                 return 0;
2297
2298         obj = odbc_request_obj(odbc_database, 0);
2299         if (obj) {
2300                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2301                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2302                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2303                         goto yuck;
2304                 }
2305                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2306                 res = SQLPrepare(stmt, sql, SQL_NTS);
2307                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
2308                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2309                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2310                         goto yuck;
2311                 }
2312                 res = odbc_smart_execute(obj, stmt);
2313                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2314                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2315                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2316                         goto yuck;
2317                 }
2318                 res = SQLFetch(stmt);
2319                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2320                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2321                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2322                         goto yuck;
2323                 }
2324                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2325                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2326                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2327                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2328                         goto yuck;
2329                 }
2330                 nummsgs = atoi(rowdata);
2331                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2332         } else
2333                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2334
2335 yuck:
2336         if (obj)
2337                 odbc_release_obj(obj);
2338         return nummsgs;
2339 }
2340
2341 static int has_voicemail(const char *mailbox, const char *folder)
2342 {
2343         char *context, tmp[256];
2344         ast_copy_string(tmp, mailbox, sizeof(tmp));
2345         if ((context = strchr(tmp, '@')))
2346                 *context++ = '\0';
2347         else
2348                 context = "default";
2349
2350         if (messagecount(context, tmp, folder))
2351                 return 1;
2352         else
2353                 return 0;
2354 }
2355
2356 #else
2357 #ifdef IMAP_STORAGE
2358 static int count_messages_imap(const char *mailbox, int *newmsgs, int *oldmsgs)
2359 {
2360         SEARCHPGM *pgm;
2361         SEARCHHEADER *hdr;
2362  
2363         struct ast_vm_user *vmu;
2364         struct vm_state *vms_p;
2365         char tmp[256]="";
2366         char *mb, *cur;
2367         char *mailboxnc; 
2368         char *context;
2369         int ret = 0;
2370         if (newmsgs)
2371                 *newmsgs = 0;
2372         if (oldmsgs)
2373                 *oldmsgs = 0;
2374  
2375         ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2376         /* If no mailbox, return immediately */
2377         if (ast_strlen_zero(mailbox))
2378                 return 0;
2379         if (strchr(mailbox, ',')) {
2380                 int tmpnew, tmpold;
2381                 strncpy(tmp, mailbox, sizeof(tmp) - 1);
2382                 mb = tmp;
2383                 ret = 0;
2384                 while((cur = strsep(&mb, ", "))) {
2385                         if (!ast_strlen_zero(cur)) {
2386                                 if (count_messages_imap(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2387                                         return -1;
2388                                 else {
2389                                         if (newmsgs)
2390                                                 *newmsgs += tmpnew; 
2391                                         if (oldmsgs)
2392                                                 *oldmsgs += tmpold;
2393                                 }
2394                         }
2395                 }
2396                 return 0;
2397         }
2398  
2399         strncpy(tmp, mailbox, sizeof(tmp) - 1);
2400         context = strchr(tmp, '@');
2401         if (context) {
2402                 *context = '\0';
2403                 mailboxnc = tmp;
2404                 context++;
2405         } else {
2406                 context = "default";
2407                 mailboxnc = (char *)mailbox;
2408         }
2409  
2410         /* We have to get the user before we can open the stream! */
2411         /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2412         vmu = find_user(NULL, context, mailboxnc);
2413         if (vmu == NULL) {
2414                 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailboxnc,context);
2415                 return -1;
2416         } else {
2417                 /* No IMAP account available */
2418                 if (vmu->imapuser[0] == '\0') {
2419                         ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2420                         return -1;
2421                 }
2422         }
2423  
2424         /* check if someone is accessing this box right now... */
2425         vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2426         if (vms_p == NULL) {
2427                 vms_p = get_vm_state_by_mailbox(mailboxnc,1);
2428         }
2429         if (vms_p != NULL) {
2430                 ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2431                 *newmsgs = vms_p->newmessages;
2432                 *oldmsgs = vms_p->oldmessages;
2433                 return 0;
2434         }
2435  
2436         /* add one if not there... */
2437         vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2438         if (vms_p == NULL) {
2439                 vms_p = get_vm_state_by_mailbox(mailboxnc,0);
2440         }
2441  
2442         if (vms_p == NULL) {
2443                 ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2444                 vms_p = (struct vm_state *)malloc(sizeof(struct vm_state));
2445                 strcpy(vms_p->imapuser,vmu->imapuser);
2446                 strcpy(vms_p->username,mailboxnc); /* save for access from interactive entry point */
2447                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2448                 ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2449                 vms_p->updated = 1;
2450                 vms_p->interactive = 0;
2451                 /* set mailbox to INBOX! */
2452                 strncpy(vms_p->curbox, mbox(0), sizeof(vms_p->curbox) - 1);
2453                 init_vm_state(vms_p);
2454                 vmstate_insert(vms_p);
2455         }
2456         if (vms_p->mailstream == NULL)
2457                 ret = init_mailstream(vms_p);
2458         if (vms_p->mailstream == NULL) {
2459                 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2460                 return -1;
2461         }
2462         if (newmsgs && ret==0 && vms_p->updated==1 ) {
2463                 pgm = mail_newsearchpgm ();
2464                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2465                 pgm->header = hdr;
2466                 pgm->unseen = 1;
2467                 pgm->seen = 0;
2468                 pgm->undeleted = 1;
2469                 pgm->deleted = 0;
2470  
2471                 vms_p->vmArrayIndex = 0;
2472         
2473                 ast_log (LOG_DEBUG,"Before search of mailbox %s for NEW messages\n",vms_p->mailstream->mailbox);
2474                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2475                 ast_log (LOG_DEBUG,"After search\n");
2476                 *newmsgs = vms_p->vmArrayIndex;
2477                 vms_p->newmessages = vms_p->vmArrayIndex;
2478                 ast_log (LOG_DEBUG,"There are %d NEW messages\n",vms_p->newmessages);
2479         }
2480         if (oldmsgs && ret==0 && vms_p->updated==1 ) {
2481                 pgm = mail_newsearchpgm ();
2482                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2483                 pgm->header = hdr;
2484                 pgm->unseen = 0;
2485                 pgm->seen = 1;
2486                 pgm->deleted = 0;
2487                 pgm->undeleted = 1;
2488  
2489                 vms_p->vmArrayIndex = 0;
2490         
2491                 ast_log (LOG_DEBUG,"Before search of mailbox %s for OLD messages\n",vms_p->mailstream->mailbox);
2492                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2493                 *oldmsgs = vms_p->vmArrayIndex;
2494                 vms_p->oldmessages = vms_p->vmArrayIndex;
2495                 ast_log (LOG_DEBUG,"There are %d OLD messages\n",vms_p->oldmessages);
2496         }
2497         if (vms_p->updated == 1) {  /* changes, so we did the searches above */
2498                 vms_p->updated = 0;
2499         } else if (vms_p->updated > 1) {  /* decrement delay count */
2500                 vms_p->updated--;
2501         } else {  /* no changes, so don't search */
2502                 ast_log (LOG_DEBUG,"Calling mail_ping on mailbox %s\n",vms_p->mailstream->mailbox);
2503                 mail_ping(vms_p->mailstream);
2504                 /* Keep the old data */
2505                 *newmsgs = vms_p->newmessages;
2506                 *oldmsgs = vms_p->oldmessages;
2507         }
2508         return 0;
2509  }
2510 #endif
2511 #endif
2512
2513 /* copy message only used by file storage */
2514 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)
2515 {
2516         char fromdir[256], todir[256], frompath[256], topath[256];
2517         const char *frombox = mbox(imbox);
2518         int recipmsgnum;
2519
2520         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2521
2522         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2523   
2524         make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2525         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2526
2527         if (vm_lock_path(todir))
2528                 return ERROR_LOCK_PATH;
2529
2530         recipmsgnum = 0;
2531         do {
2532                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2533                 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2534                         break;
2535                 recipmsgnum++;
2536         } while (recipmsgnum < recip->maxmsg);
2537         if (recipmsgnum < recip->maxmsg) {
2538                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2539         } else {
2540                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2541         }
2542         ast_unlock_path(todir);
2543         notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2544         
2545         return 0;
2546 }
2547
2548 #ifndef ODBC_STORAGE
2549
2550 static int messagecount(const char *context, const char *mailbox, const char *folder)
2551 {
2552         return __has_voicemail(context, mailbox, folder, 0);
2553 }
2554
2555
2556 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2557 {
2558         DIR *dir;
2559         struct dirent *de;
2560         char fn[256];
2561         int ret = 0;
2562         if (!folder)
2563                 folder = "INBOX";
2564         /* If no mailbox, return immediately */
2565         if (ast_strlen_zero(mailbox))
2566                 return 0;
2567         if (!context)
2568                 context = "default";
2569         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2570         dir = opendir(fn);
2571         if (!dir)
2572                 return 0;
2573         while ((de = readdir(dir))) {
2574                 if (!strncasecmp(de->d_name, "msg", 3)) {
2575                         if (shortcircuit) {
2576                                 ret = 1;
2577                                 break;
2578                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2579                                 ret++;
2580                 }
2581         }
2582         closedir(dir);
2583         return ret;
2584 }
2585
2586
2587 static int has_voicemail(const char *mailbox, const char *folder)
2588 {
2589         char tmp[256], *tmp2 = tmp, *mbox, *context;
2590         ast_copy_string(tmp, mailbox, sizeof(tmp));
2591         while ((mbox = strsep(&tmp2, ","))) {
2592                 if ((context = strchr(mbox, '@')))
2593                         *context++ = '\0';
2594                 else
2595                         context = "default";
2596                 if (__has_voicemail(context, mbox, folder, 1))
2597                         return 1;
2598         }
2599         return 0;
2600 }
2601
2602
2603 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2604 {
2605         char tmp[256];
2606         char *context;
2607
2608         if (newmsgs)
2609                 *newmsgs = 0;
2610         if (oldmsgs)
2611                 *oldmsgs = 0;
2612         /* If no mailbox, return immediately */
2613         if (ast_strlen_zero(mailbox))
2614                 return 0;
2615         if (strchr(mailbox, ',')) {
2616                 int tmpnew, tmpold;
2617                 char *mb, *cur;
2618
2619                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2620                 mb = tmp;
2621                 while ((cur = strsep(&mb, ", "))) {
2622                         if (!ast_strlen_zero(cur)) {
2623                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2624                                         return -1;
2625                                 else {
2626                                         if (newmsgs)
2627                                                 *newmsgs += tmpnew; 
2628                                         if (oldmsgs)
2629                                                 *oldmsgs += tmpold;
2630                                 }
2631                         }
2632                 }
2633                 return 0;
2634         }
2635         ast_copy_string(tmp, mailbox, sizeof(tmp));
2636         context = strchr(tmp, '@');
2637         if (context) {
2638                 *context = '\0';
2639                 context++;
2640         } else
2641                 context = "default";
2642         if (newmsgs)
2643                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2644         if (oldmsgs)
2645                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2646         return 0;
2647 }
2648
2649 #endif
2650
2651 static void run_externnotify(char *context, char *extension)
2652 {
2653         char arguments[255];
2654         char ext_context[256] = "";
2655         int newvoicemails = 0, oldvoicemails = 0;
2656         struct ast_smdi_mwi_message *mwi_msg;
2657
2658         if (!ast_strlen_zero(context))
2659                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2660         else
2661                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2662
2663         if (!strcasecmp(externnotify, "smdi")) {
2664                 if (ast_app_has_voicemail(ext_context, NULL)) 
2665                         ast_smdi_mwi_set(smdi_iface, extension);
2666                 else
2667                         ast_smdi_mwi_unset(smdi_iface, extension);
2668
2669                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2670                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2671                         if (!strncmp(mwi_msg->cause, "INV", 3))
2672                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2673                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2674                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2675                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2676                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2677                 } else {
2678                         ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2679                 }
2680         } else if (!ast_strlen_zero(externnotify)) {
2681                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2682                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2683                 } else {
2684                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2685                         ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2686                         ast_safe_system(arguments);
2687                 }
2688         }
2689 }
2690
2691 struct leave_vm_options {
2692         unsigned int flags;
2693         signed char record_gain;
2694 };
2695
2696 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2697 {
2698 #ifdef IMAP_STORAGE
2699         int newmsgs, oldmsgs;
2700         struct vm_state *vms;
2701 #else
2702         char tmptxtfile[256], txtfile[256];
2703         char callerid[256];
2704         FILE *txt;
2705         char date[256];
2706         int  txtdes;
2707 #endif
2708         int res = 0;
2709         int msgnum;
2710         int duration = 0;
2711         int ausemacro = 0;
2712         int ousemacro = 0;
2713         int ouseexten = 0;
2714         char dir[256], tmpdir[260];
2715         char fn[256];
2716         char prefile[256]="";
2717         char tempfile[256]="";
2718         char ext_context[256] = "";
2719         char fmt[80];
2720         char *context;
2721         char ecodes[16] = "#";
2722         char tmp[256] = "", *tmpptr;
2723         struct ast_vm_user *vmu;
2724         struct ast_vm_user svm;
2725         const char *category = NULL;
2726
2727         ast_copy_string(tmp, ext, sizeof(tmp));
2728         ext = tmp;
2729         context = strchr(tmp, '@');
2730         if (context) {
2731                 *context++ = '\0';
2732                 tmpptr = strchr(context, '&');
2733         } else {
2734                 tmpptr = strchr(ext, '&');
2735         }
2736
2737         if (tmpptr)
2738                 *tmpptr++ = '\0';
2739
2740         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2741
2742         ast_log(LOG_DEBUG, "Before find_user\n");
2743         if (!(vmu = find_user(&svm, context, ext))) {
2744                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2745                 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2746                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2747                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2748                 return res;
2749         }
2750
2751         /* Setup pre-file if appropriate */
2752         if (strcmp(vmu->context, "default"))
2753                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2754         else
2755                 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2756         if (ast_test_flag(options, OPT_BUSY_GREETING))
2757                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2758         else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2759                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2760         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2761         RETRIEVE(tempfile, -1);
2762         if (ast_fileexists(tempfile, NULL, NULL) > 0)
2763                 ast_copy_string(prefile, tempfile, sizeof(prefile));
2764         DISPOSE(tempfile, -1);
2765         /* It's easier just to try to make it than to check for its existence */
2766         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2767         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2768
2769         /* Check current or macro-calling context for special extensions */
2770         if (ast_test_flag(vmu, VM_OPERATOR)) {
2771                 if (!ast_strlen_zero(vmu->exit)) {
2772                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2773                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2774                                 ouseexten = 1;
2775                         }
2776                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2777                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2778                         ouseexten = 1;
2779                 }
2780                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2781                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2782                 ousemacro = 1;
2783                 }
2784         }
2785
2786         if (!ast_strlen_zero(vmu->exit)) {
2787                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2788                         strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2789         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2790                 strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2791         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2792                 strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2793                 ausemacro = 1;
2794         }
2795
2796         /* Play the beginning intro if desired */
2797         if (!ast_strlen_zero(prefile)) {
2798                 RETRIEVE(prefile, -1);
2799                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2800                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
2801                                 res = ast_waitstream(chan, ecodes);
2802                 } else {
2803                         ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2804                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2805                 }
2806                 DISPOSE(prefile, -1);
2807                 if (res < 0) {
2808                         ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2809                         free_user(vmu);
2810                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2811                         return -1;
2812                 }
2813         }
2814         if (res == '#') {
2815                 /* On a '#' we skip the instructions */
2816                 ast_set_flag(options, OPT_SILENT);
2817                 res = 0;
2818         }
2819         if (!res && !ast_test_flag(options, OPT_SILENT)) {
2820                 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2821                 if (res == '#') {
2822                         ast_set_flag(options, OPT_SILENT);
2823                         res = 0;
2824                 }
2825         }
2826         if (res > 0)
2827                 ast_stopstream(chan);
2828         /* Check for a '*' here in case the caller wants to escape from voicemail to something
2829            other than the operator -- an automated attendant or mailbox login for example */
2830         if (res == '*') {
2831                 chan->exten[0] = 'a';
2832                 chan->exten[1] = '\0';
2833                 if (!ast_strlen_zero(vmu->exit)) {
2834                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2835                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2836                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2837                 }
2838                 chan->priority = 0;
2839                 free_user(vmu);
2840                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2841                 return 0;
2842         }
2843
2844         /* Check for a '0' here */
2845         if (res == '0') {
2846         transfer:
2847                 if (ouseexten || ousemacro) {
2848                         chan->exten[0] = 'o';
2849                         chan->exten[1] = '\0';
2850                         if (!ast_strlen_zero(vmu->exit)) {
2851                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2852                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2853                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2854                         }
2855                         ast_play_and_wait(chan, "transfer");
2856                         chan->priority = 0;
2857                         free_user(vmu);
2858                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2859                 }
2860                 return 0;
2861         }
2862         if (res < 0) {
2863                 free_user(vmu);
2864                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2865                 return -1;
2866         }
2867         /* The meat of recording the message...  All the announcements and beeps have been played*/
2868         ast_copy_string(fmt, vmfmts, sizeof(fmt));
2869         if (!ast_strlen_zero(fmt)) {
2870                 msgnum = 0;
2871
2872 #ifdef IMAP_STORAGE
2873                 /* Is ext a mailbox? */
2874                 /* must open stream for this user to get info! */
2875                 vms = get_vm_state_by_mailbox(ext,0);
2876                 if (vms != NULL) {
2877                         ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
2878                         newmsgs = vms->newmessages++;
2879                         oldmsgs = vms->oldmessages;
2880                 } else {
2881                         ast_log(LOG_DEBUG, "About to call messagecount.\n");
2882                         res = count_messages_imap(ext, &newmsgs, &oldmsgs);
2883                         if(res < 0) {
2884                                 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
2885                                 return -1;
2886                         }
2887                 }
2888                 /* here is a big difference! We add one to it later */
2889                 msgnum = newmsgs + oldmsgs;
2890                 ast_log(LOG_NOTICE, "Messagecount set to %d\n",msgnum);
2891                 sprintf(fn, "%s/msg%s%04d", TMP, vmu->mailbox, msgnum);
2892                 /* set variable for compatability */
2893                 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
2894
2895                 /* Check if mailbox is full */
2896                 if (vms->quota_usage >= vms->quota_limit) {
2897                         ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
2898                         ast_play_and_wait(chan, "vm-mailboxfull");
2899                         return -1;
2900                 }
2901
2902                 /* play beep */
2903                 res = ast_streamfile(chan, "beep", chan->language);
2904                 if (!res)
2905                         res = ast_waitstream(chan, "");
2906                 /* play_record_review does recording and verify */
2907                 ast_log(LOG_DEBUG, "About to record message in file %s\n",fn);
2908                 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2909                 if (res == '0') {
2910                         goto transfer;
2911                 }
2912                 if (res > 0) res = 0;
2913
2914                 if (duration < vmminmessage) {
2915                         if (option_verbose > 2) 
2916                                 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2917                         goto leave_vm_out;
2918                 }
2919                 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2920 #else
2921                 if (count_messages(vmu, dir) >= vmu->maxmsg) {
2922                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2923                         if (!res)
2924                                 res = ast_waitstream(chan, "");
2925                         ast_log(LOG_WARNING, "No more messages possible\n");
2926                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2927                         goto leave_vm_out;
2928                 }
2929
2930                 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
2931                 txtdes = mkstemp(tmptxtfile);
2932                 if (txtdes < 0) {
2933                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2934                         if (!res)
2935                                 res = ast_waitstream(chan, "");
2936                         ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
2937                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2938                         goto leave_vm_out;
2939                 }
2940
2941                 /* Now play the beep once we have the message number for our next message. */
2942                 if (res >= 0) {
2943                         /* Unless we're *really* silent, try to send the beep */
2944                         res = ast_stream_and_wait(chan, "beep", chan->language, "");
2945                 }
2946                                 
2947                 /* Store information */
2948                 txt = fdopen(txtdes, "w+");
2949                 if (txt) {
2950                         get_date(date, sizeof(date));
2951                         fprintf(txt, 
2952                                 ";\n"
2953                                 "; Message Information file\n"
2954                                 ";\n"
2955                                 "[message]\n"
2956                                 "origmailbox=%s\n"
2957                                 "context=%s\n"
2958                                 "macrocontext=%s\n"
2959                                 "exten=%s\n"
2960                                 "priority=%d\n"
2961                                 "callerchan=%s\n"
2962                                 "callerid=%s\n"
2963                                 "origdate=%s\n"
2964                                 "origtime=%ld\n"
2965                                 "category=%s\n",
2966                                 ext,
2967                                 chan->context,
2968                                 chan->macrocontext, 
2969                                 chan->exten,
2970                                 chan->priority,
2971                                 chan->name,
2972                                 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2973                                 date, (long)time(NULL),
2974                                 category ? category : ""); 
2975                 } else
2976                         ast_log(LOG_WARNING, "Error opening text file for output\n");
2977                 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain);
2978                                 
2979                 if (txt) {
2980                         if (duration < vmminmessage) {
2981                                 if (option_verbose > 2) 
2982                                         ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2983                                 ast_filedelete(tmptxtfile, NULL);
2984                                 unlink(tmptxtfile);
2985                         } else {
2986                                 fprintf(txt, "duration=%d\n", duration);
2987                                 fclose(txt);
2988                                 if (vm_lock_path(dir)) {
2989                                         ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
2990                                         /* Delete files */
2991                                         ast_filedelete(tmptxtfile, NULL);
2992                                         unlink(tmptxtfile);
2993                                 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
2994                                         if (option_debug) 
2995                                                 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
2996                                         unlink(tmptxtfile);
2997                                         ast_unlock_path(dir);
2998                                 } else {
2999                                         for (;;) {
3000                                                 make_file(fn, sizeof(fn), dir, msgnum);
3001                                                 if (!EXISTS(dir, msgnum, fn, NULL))
3002                                                         break;
3003                                                 msgnum++;
3004                                         }
3005
3006                                         /* assign a variable with the name of the voicemail file */ 
3007                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3008
3009                                         snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3010                         &nbs