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