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