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