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