Merged revisions 46200 via svnmerge from
[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 = NULL;     /* currently connected host */
94 static char *curusr = NULL;     /* 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  * \note Should always be called with a lock already set on dir.
1625  */
1626 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1627 {
1628         int x;
1629         unsigned char map[MAXMSGLIMIT] = "";
1630         DIR *msgdir;
1631         struct dirent *msgdirent;
1632         int msgdirint;
1633
1634         /* Reading the entire directory into a file map scales better than
1635          * doing a stat repeatedly on a predicted sequence.  I suspect this
1636          * is partially due to stat(2) internally doing a readdir(2) itself to
1637          * find each file. */
1638         msgdir = opendir(dir);
1639         while ((msgdirent = readdir(msgdir))) {
1640                 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1641                         map[msgdirint] = 1;
1642         }
1643         closedir(msgdir);
1644
1645         for (x = 0; x < vmu->maxmsg; x++) {
1646                 if (map[x] == 0)
1647                         break;
1648         }
1649
1650         return x - 1;
1651 }
1652
1653 static int vm_delete(char *file)
1654 {
1655         char *txt;
1656         int txtsize = 0;
1657
1658         txtsize = (strlen(file) + 5)*sizeof(char);
1659         txt = alloca(txtsize);
1660         /* Sprintf here would safe because we alloca'd exactly the right length,
1661          * but trying to eliminate all sprintf's anyhow
1662          */
1663         snprintf(txt, txtsize, "%s.txt", file);
1664         unlink(txt);
1665         return ast_filedelete(file, NULL);
1666 }
1667
1668
1669 #endif
1670 static int inbuf(struct baseio *bio, FILE *fi)
1671 {
1672         int l;
1673
1674         if (bio->ateof)
1675                 return 0;
1676
1677         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1678                 if (ferror(fi))
1679                         return -1;
1680
1681                 bio->ateof = 1;
1682                 return 0;
1683         }
1684
1685         bio->iolen= l;
1686         bio->iocp= 0;
1687
1688         return 1;
1689 }
1690
1691 static int inchar(struct baseio *bio, FILE *fi)
1692 {
1693         if (bio->iocp>=bio->iolen) {
1694                 if (!inbuf(bio, fi))
1695                         return EOF;
1696         }
1697
1698         return bio->iobuf[bio->iocp++];
1699 }
1700
1701 static int ochar(struct baseio *bio, int c, FILE *so)
1702 {
1703         if (bio->linelength>=BASELINELEN) {
1704                 if (fputs(eol,so)==EOF)
1705                         return -1;
1706
1707                 bio->linelength= 0;
1708         }
1709
1710         if (putc(((unsigned char)c),so)==EOF)
1711                 return -1;
1712
1713         bio->linelength++;
1714
1715         return 1;
1716 }
1717
1718 static int base_encode(char *filename, FILE *so)
1719 {
1720         unsigned char dtable[BASEMAXINLINE];
1721         int i,hiteof= 0;
1722         FILE *fi;
1723         struct baseio bio;
1724
1725         memset(&bio, 0, sizeof(bio));
1726         bio.iocp = BASEMAXINLINE;
1727
1728         if (!(fi = fopen(filename, "rb"))) {
1729                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1730                 return -1;
1731         }
1732
1733         for (i= 0;i<9;i++) {
1734                 dtable[i]= 'A'+i;
1735                 dtable[i+9]= 'J'+i;
1736                 dtable[26+i]= 'a'+i;
1737                 dtable[26+i+9]= 'j'+i;
1738         }
1739         for (i= 0;i<8;i++) {
1740                 dtable[i+18]= 'S'+i;
1741                 dtable[26+i+18]= 's'+i;
1742         }
1743         for (i= 0;i<10;i++) {
1744                 dtable[52+i]= '0'+i;
1745         }
1746         dtable[62]= '+';
1747         dtable[63]= '/';
1748
1749         while (!hiteof){
1750                 unsigned char igroup[3],ogroup[4];
1751                 int c,n;
1752
1753                 igroup[0]= igroup[1]= igroup[2]= 0;
1754
1755                 for (n= 0;n<3;n++) {
1756                         if ((c = inchar(&bio, fi)) == EOF) {
1757                                 hiteof= 1;
1758                                 break;
1759                         }
1760
1761                         igroup[n]= (unsigned char)c;
1762                 }
1763
1764                 if (n> 0) {
1765                         ogroup[0]= dtable[igroup[0]>>2];
1766                         ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1767                         ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1768                         ogroup[3]= dtable[igroup[2]&0x3F];
1769
1770                         if (n<3) {
1771                                 ogroup[3]= '=';
1772
1773                                 if (n<2)
1774                                         ogroup[2]= '=';
1775                         }
1776
1777                         for (i= 0;i<4;i++)
1778                                 ochar(&bio, ogroup[i], so);
1779                 }
1780         }
1781
1782         if (fputs(eol,so)==EOF)
1783                 return 0;
1784
1785         fclose(fi);
1786
1787         return 1;
1788 }
1789
1790 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)
1791 {
1792         char callerid[256];
1793         /* Prepare variables for substition in email body and subject */
1794         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1795         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1796         snprintf(passdata, passdatasize, "%d", msgnum);
1797         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1798         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1799         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1800         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1801         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1802         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1803         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1804         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1805 }
1806
1807 /*
1808  * fill in *tm for current time according to the proper timezone, if any.
1809  * Return tm so it can be used as a function argument.
1810  */
1811 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1812 {
1813         const struct vm_zone *z = NULL;
1814         time_t t = time(NULL);
1815
1816         /* Does this user have a timezone specified? */
1817         if (!ast_strlen_zero(vmu->zonetag)) {
1818                 /* Find the zone in the list */
1819                 AST_LIST_LOCK(&zones);
1820                 AST_LIST_TRAVERSE(&zones, z, list) {
1821                         if (!strcmp(z->name, vmu->zonetag))
1822                                 break;
1823                 }
1824                 AST_LIST_UNLOCK(&zones);
1825         }
1826         ast_localtime(&t, tm, z ? z->timezone : NULL);
1827         return tm;
1828 }
1829
1830 /* same as mkstemp, but return a FILE * */
1831 static FILE *vm_mkftemp(char *template)
1832 {
1833         FILE *p = NULL;
1834         int pfd = mkstemp(template);
1835         if (pfd > -1) {
1836                 p = fdopen(pfd, "w");
1837                 if (!p) {
1838                         close(pfd);
1839                         pfd = -1;
1840                 }
1841         }
1842         return p;
1843 }
1844
1845 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)
1846 {
1847         FILE *p=NULL;
1848         char date[256];
1849         char host[MAXHOSTNAMELEN] = "";
1850         char who[256];
1851         char bound[256];
1852         char fname[256];
1853         char dur[256];
1854         char tmp[80] = "/tmp/astmail-XXXXXX";
1855         char tmp2[256];
1856         char tmpcmd[256];
1857         struct tm tm;
1858
1859         if (vmu && ast_strlen_zero(vmu->email)) {
1860                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
1861                 return(0);
1862         }
1863         if (!strcmp(format, "wav49"))
1864                 format = "WAV";
1865         if (option_debug)
1866                 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));
1867         /* Make a temporary file instead of piping directly to sendmail, in case the mail
1868            command hangs */
1869         if ((p = vm_mkftemp(tmp)) == NULL) {
1870                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1871                 return -1;
1872         } else {
1873                 gethostname(host, sizeof(host)-1);
1874                 if (strchr(srcemail, '@'))
1875                         ast_copy_string(who, srcemail, sizeof(who));
1876                 else {
1877                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1878                 }
1879                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1880                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1881                 fprintf(p, "Date: %s\n", date);
1882
1883                 /* Set date format for voicemail mail */
1884                 strftime(date, sizeof(date), emaildateformat, &tm);
1885
1886                 if (*fromstring) {
1887                         struct ast_channel *ast;
1888                         if ((ast = ast_channel_alloc(0))) {
1889                                 char *passdata;
1890                                 int vmlen = strlen(fromstring)*3 + 200;
1891                                 if ((passdata = alloca(vmlen))) {
1892                                         memset(passdata, 0, vmlen);
1893                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1894                                         pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1895                                         fprintf(p, "From: %s <%s>\n",passdata,who);
1896                                 } else
1897                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1898                                 ast_channel_free(ast);
1899                         } else
1900                                 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1901                 } else
1902                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
1903                 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1904
1905                 if (emailsubject) {
1906                         struct ast_channel *ast;
1907                         if ((ast = ast_channel_alloc(0))) {
1908                                 char *passdata;
1909                                 int vmlen = strlen(emailsubject)*3 + 200;
1910                                 if ((passdata = alloca(vmlen))) {
1911                                         memset(passdata, 0, vmlen);
1912                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1913                                         pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1914                                         fprintf(p, "Subject: %s\n", passdata);
1915                                 } else
1916                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1917                                 ast_channel_free(ast);
1918                         } else
1919                                 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1920                 } else
1921                 if (*emailtitle) {
1922                         fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1923                         fprintf(p,"\n") ;
1924                 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1925                         fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1926                 else
1927                         fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1928                 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1929 #ifdef IMAP_STORAGE
1930                 /* additional information needed for IMAP searching */
1931                 fprintf(p, "X-Asterisk-VM-Message-Num: %d\n", msgnum + 1);
1932                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s\n", ext); */
1933                 fprintf(p, "X-Asterisk-VM-Server-Name: %s\n", fromstring);
1934                 fprintf(p, "X-Asterisk-VM-Context: %s\n", context);
1935                 fprintf(p, "X-Asterisk-VM-Extension: %s\n", chan->exten);
1936                 fprintf(p, "X-Asterisk-VM-Priority: %d\n", chan->priority);
1937                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s\n", chan->name);
1938                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s\n", cidnum);
1939                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s\n", cidname);
1940                 fprintf(p, "X-Asterisk-VM-Duration: %d\n", duration);
1941                 if (!ast_strlen_zero(category))
1942                         fprintf(p, "X-Asterisk-VM-Category: %s\n", category);
1943                 fprintf(p, "X-Asterisk-VM-Orig-date: %s\n", date);
1944                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld\n", (long)time(NULL));
1945 #endif
1946                 if (!ast_strlen_zero(cidnum))
1947                         fprintf(p, "X-Asterisk-CallerID: %s\n", cidnum);
1948                 if (!ast_strlen_zero(cidname))
1949                         fprintf(p, "X-Asterisk-CallerIDName: %s\n", cidname);
1950                 fprintf(p, "MIME-Version: 1.0\n");
1951                 if (attach_user_voicemail) {
1952                         /* Something unique. */
1953                         snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1954
1955                         fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1956
1957                         fprintf(p, "--%s\n", bound);
1958                 }
1959                 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1960                 if (emailbody) {
1961                         struct ast_channel *ast;
1962                         if ((ast = ast_channel_alloc(0))) {
1963                                 char *passdata;
1964                                 int vmlen = strlen(emailbody)*3 + 200;
1965                                 if ((passdata = alloca(vmlen))) {
1966                                         memset(passdata, 0, vmlen);
1967                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1968                                         pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1969                                         fprintf(p, "%s\n", passdata);
1970                                 } else
1971                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1972                                 ast_channel_free(ast);
1973                         } else
1974                                 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1975                 } else {
1976                         fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1977
1978                         "in mailbox %s from %s, on %s so you might\n"
1979                         "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname, 
1980                         dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1981                 }
1982                 if (attach_user_voicemail) {
1983                         /* Eww. We want formats to tell us their own MIME type */
1984                         char *ctype = (!strcasecmp(format, "ogg")) ?  "application/" : "audio/x-";
1985                         char tmpdir[256], newtmp[256];
1986                         int tmpfd;
1987                 
1988                         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
1989                         snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
1990                         tmpfd = mkstemp(newtmp);
1991                         if (option_debug)
1992                                 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
1993                         if (vmu->volgain < -.001 || vmu->volgain > .001) {
1994                                 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
1995                                 ast_safe_system(tmpcmd);
1996                                 attach = newtmp;
1997                                 if (option_debug)
1998                                         ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
1999                         }
2000                         fprintf(p, "--%s\n", bound);
2001                         fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
2002                         fprintf(p, "Content-Transfer-Encoding: base64\n");
2003                         fprintf(p, "Content-Description: Voicemail sound attachment.\n");
2004                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
2005
2006                         snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2007                         base_encode(fname, p);
2008 #ifdef IMAP_STORAGE
2009                         /* only attach if necessary */
2010                         if (strcmp(format, "gsm")) {
2011                                 fprintf(p, "--%s\n", bound);
2012                                 fprintf(p, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"\n", msgnum, format);
2013                                 fprintf(p, "Content-Transfer-Encoding: base64\n");
2014                                 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
2015                                 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"\n\n", msgnum);
2016                                 snprintf(fname, sizeof(fname), "%s.gsm", attach);
2017                                 base_encode(fname, p);
2018                         }
2019 #endif
2020                         fprintf(p, "\n\n--%s--\n.\n", bound);
2021                         if (tmpfd > -1)
2022                                 close(tmpfd);
2023                         unlink(newtmp);
2024                 }
2025                 fclose(p);
2026                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2027                 ast_safe_system(tmp2);
2028                 if (option_debug)
2029                         ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2030         }
2031         return 0;
2032 }
2033
2034 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)
2035 {
2036         char date[256];
2037         char host[MAXHOSTNAMELEN]="";
2038         char who[256];
2039         char dur[256];
2040         char tmp[80] = "/tmp/astmail-XXXXXX";
2041         char tmp2[256];
2042         struct tm tm;
2043         FILE *p;
2044
2045         if ((p = vm_mkftemp(tmp)) == NULL) {
2046                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2047                 return -1;
2048         } else {
2049                 gethostname(host, sizeof(host)-1);
2050                 if (strchr(srcemail, '@'))
2051                         ast_copy_string(who, srcemail, sizeof(who));
2052                 else {
2053                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2054                 }
2055                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2056                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2057                 fprintf(p, "Date: %s\n", date);
2058
2059                 if (*pagerfromstring) {
2060                         struct ast_channel *ast;
2061                         if ((ast = ast_channel_alloc(0))) {
2062                                 char *passdata;
2063                                 int vmlen = strlen(fromstring)*3 + 200;
2064                                 if ((passdata = alloca(vmlen))) {
2065                                         memset(passdata, 0, vmlen);
2066                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2067                                         pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2068                                         fprintf(p, "From: %s <%s>\n", passdata, who);
2069                                 } else 
2070                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2071                                 ast_channel_free(ast);
2072                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2073                 } else
2074                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
2075                 fprintf(p, "To: %s\n", pager);
2076                 if (pagersubject) {
2077                         struct ast_channel *ast;
2078                         if ((ast = ast_channel_alloc(0))) {
2079                                 char *passdata;
2080                                 int vmlen = strlen(pagersubject) * 3 + 200;
2081                                 if ((passdata = alloca(vmlen))) {
2082                                         memset(passdata, 0, vmlen);
2083                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2084                                         pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2085                                         fprintf(p, "Subject: %s\n\n", passdata);
2086                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2087                                 ast_channel_free(ast);
2088                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2089                 } else
2090                         fprintf(p, "Subject: New VM\n\n");
2091                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2092                 if (pagerbody) {
2093                         struct ast_channel *ast;
2094                         if ((ast = ast_channel_alloc(0))) {
2095                                 char *passdata;
2096                                 int vmlen = strlen(pagerbody)*3 + 200;
2097                                 if ((passdata = alloca(vmlen))) {
2098                                         memset(passdata, 0, vmlen);
2099                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2100                                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2101                                         fprintf(p, "%s\n", passdata);
2102                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2103                         ast_channel_free(ast);
2104                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2105                 } else {
2106                         fprintf(p, "New %s long msg in box %s\n"
2107                                         "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2108                 }
2109                 fclose(p);
2110                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2111                 ast_safe_system(tmp2);
2112                 if (option_debug)
2113                         ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2114         }
2115         return 0;
2116 }
2117
2118 #ifndef IMAP_STORAGE
2119 static int get_date(char *s, int len)
2120 {
2121         struct tm tm;
2122         time_t t;
2123         t = time(0);
2124         localtime_r(&t,&tm);
2125         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2126 }
2127 #endif
2128
2129 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2130 {
2131         int res;
2132         char fn[256];
2133         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2134         RETRIEVE(fn, -1);
2135         if (ast_fileexists(fn, NULL, NULL) > 0) {
2136                 res = ast_stream_and_wait(chan, fn, chan->language, ecodes);
2137                 if (res) {
2138                         DISPOSE(fn, -1);
2139                         return res;
2140                 }
2141         } else {
2142                 /* Dispose just in case */
2143                 DISPOSE(fn, -1);
2144                 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
2145                 if (res)
2146                         return res;
2147                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2148                 if (res)
2149                         return res;
2150         }
2151         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
2152         return res;
2153 }
2154
2155 static void free_user(struct ast_vm_user *vmu)
2156 {
2157         if (ast_test_flag(vmu, VM_ALLOCED))
2158                 free(vmu);
2159 }
2160
2161 static void free_zone(struct vm_zone *z)
2162 {
2163         free(z);
2164 }
2165
2166 static const char *mbox(int id)
2167 {
2168         static const char *msgs[] = {
2169                 "INBOX",
2170                 "Old",
2171                 "Work",
2172                 "Family",
2173                 "Friends",
2174                 "Cust1",
2175                 "Cust2",
2176                 "Cust3",
2177                 "Cust4",
2178                 "Cust5",
2179         };
2180         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2181 }
2182
2183 #ifdef ODBC_STORAGE
2184 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2185 {
2186         int x = -1;
2187         int res;
2188         SQLHSTMT stmt;
2189         char sql[256];
2190         char rowdata[20];
2191         char tmp[256]="";
2192         struct odbc_obj *obj;
2193         char *context;
2194
2195         if (newmsgs)
2196                 *newmsgs = 0;
2197         if (oldmsgs)
2198                 *oldmsgs = 0;
2199
2200         /* If no mailbox, return immediately */
2201         if (ast_strlen_zero(mailbox))
2202                 return 0;
2203
2204         ast_copy_string(tmp, mailbox, sizeof(tmp));
2205         
2206         context = strchr(tmp, '@');
2207         if (context) {
2208                 *context = '\0';
2209                 context++;
2210         } else
2211                 context = "default";
2212         
2213         obj = odbc_request_obj(odbc_database, 0);
2214         if (obj) {
2215                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2216                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2217                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2218                         odbc_release_obj(obj);
2219                         goto yuck;
2220                 }
2221                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2222                 res = SQLPrepare(stmt, sql, SQL_NTS);
2223                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2224                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2225                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2226                         odbc_release_obj(obj);
2227                         goto yuck;
2228                 }
2229                 res = odbc_smart_execute(obj, stmt);
2230                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2231                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2232                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2233                         odbc_release_obj(obj);
2234                         goto yuck;
2235                 }
2236                 res = SQLFetch(stmt);
2237                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2238                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2239                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2240                         odbc_release_obj(obj);
2241                         goto yuck;
2242                 }
2243                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2244                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2245                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2246                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2247                         odbc_release_obj(obj);
2248                         goto yuck;
2249                 }
2250                 *newmsgs = atoi(rowdata);
2251                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2252
2253                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2254                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2255                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2256                         odbc_release_obj(obj);
2257                         goto yuck;
2258                 }
2259                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2260                 res = SQLPrepare(stmt, sql, SQL_NTS);
2261                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2262                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2263                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2264                         odbc_release_obj(obj);
2265                         goto yuck;
2266                 }
2267                 res = odbc_smart_execute(obj, stmt);
2268                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2269                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2270                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2271                         odbc_release_obj(obj);
2272                         goto yuck;
2273                 }
2274                 res = SQLFetch(stmt);
2275                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2276                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2277                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2278                         odbc_release_obj(obj);
2279                         goto yuck;
2280                 }
2281                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2282                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2283                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2284                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2285                         odbc_release_obj(obj);
2286                         goto yuck;
2287                 }
2288                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2289                 odbc_release_obj(obj);
2290                 *oldmsgs = atoi(rowdata);
2291                 x = 0;
2292         } else
2293                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2294                 
2295 yuck:   
2296         return x;
2297 }
2298
2299 static int messagecount(const char *context, const char *mailbox, const char *folder)
2300 {
2301         struct odbc_obj *obj = NULL;
2302         int nummsgs = 0;
2303         int res;
2304         SQLHSTMT stmt = NULL;
2305         char sql[256];
2306         char rowdata[20];
2307         if (!folder)
2308                 folder = "INBOX";
2309         /* If no mailbox, return immediately */
2310         if (ast_strlen_zero(mailbox))
2311                 return 0;
2312
2313         obj = odbc_request_obj(odbc_database, 0);
2314         if (obj) {
2315                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2316                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2317                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2318                         goto yuck;
2319                 }
2320                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2321                 res = SQLPrepare(stmt, sql, SQL_NTS);
2322                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2323                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2324                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2325                         goto yuck;
2326                 }
2327                 res = odbc_smart_execute(obj, stmt);
2328                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2329                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2330                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2331                         goto yuck;
2332                 }
2333                 res = SQLFetch(stmt);
2334                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2335                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2336                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2337                         goto yuck;
2338                 }
2339                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2340                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2341                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2342                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2343                         goto yuck;
2344                 }
2345                 nummsgs = atoi(rowdata);
2346                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2347         } else
2348                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2349
2350 yuck:
2351         if (obj)
2352                 odbc_release_obj(obj);
2353         return nummsgs;
2354 }
2355
2356 static int has_voicemail(const char *mailbox, const char *folder)
2357 {
2358         char *context, tmp[256];
2359         ast_copy_string(tmp, mailbox, sizeof(tmp));
2360         if ((context = strchr(tmp, '@')))
2361                 *context++ = '\0';
2362         else
2363                 context = "default";
2364
2365         if (messagecount(context, tmp, folder))
2366                 return 1;
2367         else
2368                 return 0;
2369 }
2370
2371 #else
2372 #ifdef IMAP_STORAGE
2373 static int count_messages_imap(const char *mailbox, int *newmsgs, int *oldmsgs)
2374 {
2375         SEARCHPGM *pgm;
2376         SEARCHHEADER *hdr;
2377  
2378         struct ast_vm_user *vmu;
2379         struct vm_state *vms_p;
2380         char tmp[256]="";
2381         char *mb, *cur;
2382         char *mailboxnc; 
2383         char *context;
2384         int ret = 0;
2385         if (newmsgs)
2386                 *newmsgs = 0;
2387         if (oldmsgs)
2388                 *oldmsgs = 0;
2389  
2390         if(option_debug > 2)
2391                 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2392         /* If no mailbox, return immediately */
2393         if (ast_strlen_zero(mailbox))
2394                 return 0;
2395         if (strchr(mailbox, ',')) {
2396                 int tmpnew, tmpold;
2397                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2398                 mb = tmp;
2399                 ret = 0;
2400                 while((cur = strsep(&mb, ", "))) {
2401                         if (!ast_strlen_zero(cur)) {
2402                                 if (count_messages_imap(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2403                                         return -1;
2404                                 else {
2405                                         if (newmsgs)
2406                                                 *newmsgs += tmpnew; 
2407                                         if (oldmsgs)
2408                                                 *oldmsgs += tmpold;
2409                                 }
2410                         }
2411                 }
2412                 return 0;
2413         }
2414         ast_copy_string(tmp, mailbox, sizeof(tmp));
2415         context = strchr(tmp, '@');
2416         if (context) {
2417                 *context = '\0';
2418                 mailboxnc = tmp;
2419                 context++;
2420         } else {
2421                 context = "default";
2422                 mailboxnc = (char *)mailbox;
2423         }
2424  
2425         /* We have to get the user before we can open the stream! */
2426         /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2427         vmu = find_user(NULL, context, mailboxnc);
2428         if (!vmu) {
2429                 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailboxnc,context);
2430                 return -1;
2431         } else {
2432                 /* No IMAP account available */
2433                 if (vmu->imapuser[0] == '\0') {
2434                         ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2435                         return -1;
2436                 }
2437         }
2438  
2439         /* check if someone is accessing this box right now... */
2440         vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2441         if (!vms_p) {
2442                 vms_p = get_vm_state_by_mailbox(mailboxnc,1);
2443         }
2444         if (vms_p) {
2445                 if(option_debug > 2)
2446                         ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2447                 *newmsgs = vms_p->newmessages;
2448                 *oldmsgs = vms_p->oldmessages;
2449                 return 0;
2450         }
2451  
2452         /* add one if not there... */
2453         vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2454         if (!vms_p) {
2455                 vms_p = get_vm_state_by_mailbox(mailboxnc,0);
2456         }
2457  
2458         if (!vms_p) {
2459                 if(option_debug > 2)
2460                         ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2461                 vms_p = (struct vm_state *)malloc(sizeof(struct vm_state));
2462                 strcpy(vms_p->imapuser,vmu->imapuser);
2463                 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2464                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2465                 if(option_debug > 2)
2466                         ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2467                 vms_p->updated = 1;
2468                 vms_p->interactive = 0;
2469                 /* set mailbox to INBOX! */
2470                 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2471                 init_vm_state(vms_p);
2472                 vmstate_insert(vms_p);
2473         }
2474         if (!vms_p->mailstream)
2475                 ret = init_mailstream(vms_p);
2476         if (!vms_p->mailstream) {
2477                 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2478                 return -1;
2479         }
2480         if (newmsgs && 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 = 1;
2485                 pgm->seen = 0;
2486                 pgm->undeleted = 1;
2487                 pgm->deleted = 0;
2488  
2489                 vms_p->vmArrayIndex = 0;
2490         
2491                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2492                 *newmsgs = vms_p->vmArrayIndex;
2493                 vms_p->newmessages = vms_p->vmArrayIndex;
2494         }
2495         if (oldmsgs && ret==0 && vms_p->updated==1 ) {
2496                 pgm = mail_newsearchpgm ();
2497                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2498                 pgm->header = hdr;
2499                 pgm->unseen = 0;
2500                 pgm->seen = 1;
2501                 pgm->deleted = 0;
2502                 pgm->undeleted = 1;
2503  
2504                 vms_p->vmArrayIndex = 0;
2505         
2506                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2507                 *oldmsgs = vms_p->vmArrayIndex;
2508                 vms_p->oldmessages = vms_p->vmArrayIndex;
2509         }
2510         if (vms_p->updated == 1) {  /* changes, so we did the searches above */
2511                 vms_p->updated = 0;
2512         } else if (vms_p->updated > 1) {  /* decrement delay count */
2513                 vms_p->updated--;
2514         } else {  /* no changes, so don't search */
2515                 mail_ping(vms_p->mailstream);
2516                 /* Keep the old data */
2517                 *newmsgs = vms_p->newmessages;
2518                 *oldmsgs = vms_p->oldmessages;
2519         }
2520         return 0;
2521  }
2522 #endif
2523 #endif
2524
2525 /* copy message only used by file storage */
2526 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)
2527 {
2528         
2529         char fromdir[256], todir[256], frompath[256], topath[256];
2530         const char *frombox = mbox(imbox);
2531         int recipmsgnum;
2532
2533         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2534
2535         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2536
2537         make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2538         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2539
2540         if (vm_lock_path(todir))
2541                 return ERROR_LOCK_PATH;
2542
2543         recipmsgnum = last_message_index(recip, todir) + 1;
2544         if (recipmsgnum < recip->maxmsg) {
2545                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2546         } else {
2547                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2548         }
2549         ast_unlock_path(todir);
2550         notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2551         
2552         return 0;
2553 }
2554
2555 #ifndef ODBC_STORAGE
2556
2557 static int messagecount(const char *context, const char *mailbox, const char *folder)
2558 {
2559         return __has_voicemail(context, mailbox, folder, 0);
2560 }
2561
2562
2563 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2564 {
2565         DIR *dir;
2566         struct dirent *de;
2567         char fn[256];
2568         int ret = 0;
2569         if (!folder)
2570                 folder = "INBOX";
2571         /* If no mailbox, return immediately */
2572         if (ast_strlen_zero(mailbox))
2573                 return 0;
2574         if (!context)
2575                 context = "default";
2576         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2577         dir = opendir(fn);
2578         if (!dir)
2579                 return 0;
2580         while ((de = readdir(dir))) {
2581                 if (!strncasecmp(de->d_name, "msg", 3)) {
2582                         if (shortcircuit) {
2583                                 ret = 1;
2584                                 break;
2585                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2586                                 ret++;
2587                 }
2588         }
2589         closedir(dir);
2590         return ret;
2591 }
2592
2593
2594 static int has_voicemail(const char *mailbox, const char *folder)
2595 {
2596         char tmp[256], *tmp2 = tmp, *mbox, *context;
2597         ast_copy_string(tmp, mailbox, sizeof(tmp));
2598         while ((mbox = strsep(&tmp2, ","))) {
2599                 if ((context = strchr(mbox, '@')))
2600                         *context++ = '\0';
2601                 else
2602                         context = "default";
2603                 if (__has_voicemail(context, mbox, folder, 1))
2604                         return 1;
2605         }
2606         return 0;
2607 }
2608
2609
2610 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2611 {
2612         char tmp[256];
2613         char *context;
2614
2615         if (newmsgs)
2616                 *newmsgs = 0;
2617         if (oldmsgs)
2618                 *oldmsgs = 0;
2619         /* If no mailbox, return immediately */
2620         if (ast_strlen_zero(mailbox))
2621                 return 0;
2622         if (strchr(mailbox, ',')) {
2623                 int tmpnew, tmpold;
2624                 char *mb, *cur;
2625
2626                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2627                 mb = tmp;
2628                 while ((cur = strsep(&mb, ", "))) {
2629                         if (!ast_strlen_zero(cur)) {
2630                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2631                                         return -1;
2632                                 else {
2633                                         if (newmsgs)
2634                                                 *newmsgs += tmpnew; 
2635                                         if (oldmsgs)
2636                                                 *oldmsgs += tmpold;
2637                                 }
2638                         }
2639                 }
2640                 return 0;
2641         }
2642         ast_copy_string(tmp, mailbox, sizeof(tmp));
2643         context = strchr(tmp, '@');
2644         if (context) {
2645                 *context = '\0';
2646                 context++;
2647         } else
2648                 context = "default";
2649         if (newmsgs)
2650                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2651         if (oldmsgs)
2652                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2653         return 0;
2654 }
2655
2656 #endif
2657
2658 static void run_externnotify(char *context, char *extension)
2659 {
2660         char arguments[255];
2661         char ext_context[256] = "";
2662         int newvoicemails = 0, oldvoicemails = 0;
2663         struct ast_smdi_mwi_message *mwi_msg;
2664
2665         if (!ast_strlen_zero(context))
2666                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2667         else
2668                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2669
2670         if (!strcasecmp(externnotify, "smdi")) {
2671                 if (ast_app_has_voicemail(ext_context, NULL)) 
2672                         ast_smdi_mwi_set(smdi_iface, extension);
2673                 else
2674                         ast_smdi_mwi_unset(smdi_iface, extension);
2675
2676                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2677                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2678                         if (!strncmp(mwi_msg->cause, "INV", 3))
2679                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2680                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2681                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2682                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2683                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2684                 } else {
2685                         if (option_debug)
2686                                 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2687                 }
2688         } else if (!ast_strlen_zero(externnotify)) {
2689                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2690                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2691                 } else {
2692                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2693                         if (option_debug)
2694                                 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2695                         ast_safe_system(arguments);
2696                 }
2697         }
2698 }
2699
2700 struct leave_vm_options {
2701         unsigned int flags;
2702         signed char record_gain;
2703 };
2704
2705 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2706 {
2707 #ifdef IMAP_STORAGE
2708         int newmsgs, oldmsgs;
2709         struct vm_state *vms;
2710 #else
2711         char tmptxtfile[256], txtfile[256];
2712         char callerid[256];
2713         FILE *txt;
2714         char date[256];
2715         int txtdes;
2716 #endif
2717         int res = 0;
2718         int msgnum;
2719         int duration = 0;
2720         int ausemacro = 0;
2721         int ousemacro = 0;
2722         int ouseexten = 0;
2723         char dir[256], tmpdir[260];
2724         char fn[256];
2725         char prefile[256]="";
2726         char tempfile[256]="";
2727         char ext_context[256] = "";
2728         char fmt[80];
2729         char *context;
2730         char ecodes[16] = "#";
2731         char tmp[256] = "", *tmpptr;
2732         struct ast_vm_user *vmu;
2733         struct ast_vm_user svm;
2734         const char *category = NULL;
2735
2736         ast_copy_string(tmp, ext, sizeof(tmp));
2737         ext = tmp;
2738         context = strchr(tmp, '@');
2739         if (context) {
2740                 *context++ = '\0';
2741                 tmpptr = strchr(context, '&');
2742         } else {
2743                 tmpptr = strchr(ext, '&');
2744         }
2745
2746         if (tmpptr)
2747                 *tmpptr++ = '\0';
2748
2749         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2750
2751         if(option_debug > 2)
2752                 ast_log(LOG_DEBUG, "Before find_user\n");
2753         if (!(vmu = find_user(&svm, context, ext))) {
2754                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2755                 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2756                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2757                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2758                 return res;
2759         }
2760
2761         /* Setup pre-file if appropriate */
2762         if (strcmp(vmu->context, "default"))
2763                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2764         else
2765                 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2766         if (ast_test_flag(options, OPT_BUSY_GREETING))
2767                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2768         else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2769                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2770         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2771         RETRIEVE(tempfile, -1);
2772         if (ast_fileexists(tempfile, NULL, NULL) > 0)
2773                 ast_copy_string(prefile, tempfile, sizeof(prefile));
2774         DISPOSE(tempfile, -1);
2775         /* It's easier just to try to make it than to check for its existence */
2776         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2777         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2778
2779         /* Check current or macro-calling context for special extensions */
2780         if (ast_test_flag(vmu, VM_OPERATOR)) {
2781                 if (!ast_strlen_zero(vmu->exit)) {
2782                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2783                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2784                                 ouseexten = 1;
2785                         }
2786                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2787                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2788                         ouseexten = 1;
2789                 }
2790                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2791                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2792                 ousemacro = 1;
2793                 }
2794         }
2795
2796         if (!ast_strlen_zero(vmu->exit)) {
2797                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2798                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2799         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2800                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2801         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2802                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2803                 ausemacro = 1;
2804         }
2805
2806         /* Play the beginning intro if desired */
2807         if (!ast_strlen_zero(prefile)) {
2808                 RETRIEVE(prefile, -1);
2809                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2810                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
2811                                 res = ast_waitstream(chan, ecodes);
2812                 } else {
2813                         if (option_debug)
2814                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2815                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2816                 }
2817                 DISPOSE(prefile, -1);
2818                 if (res < 0) {
2819                         if (option_debug)
2820                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2821                         free_user(vmu);
2822                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2823                         return -1;
2824                 }
2825         }
2826         if (res == '#') {
2827                 /* On a '#' we skip the instructions */
2828                 ast_set_flag(options, OPT_SILENT);
2829                 res = 0;
2830         }
2831         if (!res && !ast_test_flag(options, OPT_SILENT)) {
2832                 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2833                 if (res == '#') {
2834                         ast_set_flag(options, OPT_SILENT);
2835                         res = 0;
2836                 }
2837         }
2838         if (res > 0)
2839                 ast_stopstream(chan);
2840         /* Check for a '*' here in case the caller wants to escape from voicemail to something
2841          other than the operator -- an automated attendant or mailbox login for example */
2842         if (res == '*') {
2843                 chan->exten[0] = 'a';
2844                 chan->exten[1] = '\0';
2845                 if (!ast_strlen_zero(vmu->exit)) {
2846                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2847                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2848                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2849                 }
2850                 chan->priority = 0;
2851                 free_user(vmu);
2852                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2853                 return 0;
2854         }
2855
2856         /* Check for a '0' here */
2857         if (res == '0') {
2858         transfer:
2859                 if (ouseexten || ousemacro) {
2860                         chan->exten[0] = 'o';
2861                         chan->exten[1] = '\0';
2862                         if (!ast_strlen_zero(vmu->exit)) {
2863                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2864                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2865                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2866                         }
2867                         ast_play_and_wait(chan, "transfer");
2868                         chan->priority = 0;
2869                         free_user(vmu);
2870                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2871                 }
2872                 return 0;
2873         }
2874         if (res < 0) {
2875                 free_user(vmu);
2876                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2877                 return -1;
2878         }
2879         /* The meat of recording the message...  All the announcements and beeps have been played*/
2880         ast_copy_string(fmt, vmfmts, sizeof(fmt));
2881         if (!ast_strlen_zero(fmt)) {
2882                 msgnum = 0;
2883
2884 #ifdef IMAP_STORAGE
2885                 /* Is ext a mailbox? */
2886                 /* must open stream for this user to get info! */
2887                 vms = get_vm_state_by_mailbox(ext,0);
2888                 if (vms) {
2889                         if(option_debug > 2)
2890                                 ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
2891                         newmsgs = vms->newmessages++;
2892                         oldmsgs = vms->oldmessages;
2893                 } else {
2894                         res = count_messages_imap(ext, &newmsgs, &oldmsgs);
2895                         if(res < 0) {
2896                                 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
2897                                 return -1;
2898                         }
2899                 }
2900                 /* here is a big difference! We add one to it later */
2901                 msgnum = newmsgs + oldmsgs;
2902                 ast_log(LOG_NOTICE, "Messagecount set to %d\n",msgnum);
2903                 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
2904                 /* set variable for compatability */
2905                 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
2906
2907                 /* Check if mailbox is full */
2908                 if (vms->quota_usage >= vms->quota_limit) {
2909                         if (option_debug)
2910                                 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
2911                         ast_play_and_wait(chan, "vm-mailboxfull");
2912                         return -1;
2913                 }
2914
2915                 /* play beep */
2916                 res = ast_streamfile(chan, "beep", chan->language);
2917                 if (!res)
2918                         res = ast_waitstream(chan, "");
2919                 /* play_record_review does recording and verify */
2920                 if (option_debug)
2921                         ast_log(LOG_DEBUG, "About to record message in file %s\n",fn);
2922                 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2923                 if (res == '0') {
2924                         goto transfer;
2925                 }
2926                 if (res > 0) res = 0;
2927
2928                 if (duration < vmminmessage) {
2929                         if (option_verbose > 2) 
2930                                 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2931                         goto leave_vm_out;
2932                 }
2933                 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2934 #else
2935                 if (count_messages(vmu, dir) >= vmu->maxmsg) {
2936                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2937                         if (!res)
2938                                 res = ast_waitstream(chan, "");
2939                         ast_log(LOG_WARNING, "No more messages possible\n");
2940                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2941                         goto leave_vm_out;
2942                 }
2943
2944                 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
2945                 txtdes = mkstemp(tmptxtfile);
2946                 if (txtdes < 0) {
2947                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2948                         if (!res)
2949                                 res = ast_waitstream(chan, "");
2950                         ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
2951                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2952                         goto leave_vm_out;
2953                 }
2954
2955                 /* Now play the beep once we have the message number for our next message. */
2956                 if (res >= 0) {
2957                         /* Unless we're *really* silent, try to send the beep */
2958                         res = ast_stream_and_wait(chan, "beep", chan->language, "");
2959                 }
2960                                 
2961                 /* Store information */
2962                 txt = fdopen(txtdes, "w+");
2963                 if (txt) {
2964                         get_date(date, sizeof(date));
2965                         fprintf(txt, 
2966                                 ";\n"
2967                                 "; Message Information file\n"
2968                                 ";\n"
2969                                 "[message]\n"
2970                                 "origmailbox=%s\n"
2971                                 "context=%s\n"
2972                                 "macrocontext=%s\n"
2973                                 "exten=%s\n"
2974                                 "priority=%d\n"
2975                                 "callerchan=%s\n"
2976                                 "callerid=%s\n"
2977                                 "origdate=%s\n"
2978                                 "origtime=%ld\n"
2979                                 "category=%s\n",
2980                                 ext,
2981                                 chan->context,
2982                                 chan->macrocontext, 
2983                                 chan->exten,
2984                                 chan->priority,
2985                                 chan->name,
2986                                 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2987                                 date, (long)time(NULL),
2988                                 category ? category : ""); 
2989                 } else
2990                         ast_log(LOG_WARNING, "Error opening text file for output\n");
2991                 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain);
2992                                 
2993                 if (txt) {
2994                         if (duration < vmminmessage) {
2995                                 if (option_verbose > 2) 
2996                                         ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2997                                 ast_filedelete(tmptxtfile, NULL);
2998                                 unlink(tmptxtfile);
2999                         } else {
3000                                 fprintf(txt, "duration=%d\n", duration);
3001                                 fclose(txt);
3002                                 if (vm_lock_path(dir)) {
3003                                         ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
3004                                         /* Delete files */
3005                                         ast_filedelete(tmptxtfile, NULL);
3006                                         unlink(tmptxtfile);
3007                                 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3008                                         if (option_debug) 
3009                                                 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3010                                         unlink(tmptxtfile);
3011                                         ast_unlock_path(dir);
3012                                 } else {
3013                                         msgnum = last_message_index(vmu, dir) + 1;
3014                                         make_file(fn, sizeof(fn), dir, msgnum);
3015
3016                                         /* assign a variable with the name of the voicemail file */ 
301