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