a9c7537ebfda2f1a51c37c2cdab27507f45fa6e3
[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
2548         if (vm_lock_path(todir))
2549                 return ERROR_LOCK_PATH;
2550
2551         recipmsgnum = last_message_index(recip, todir) + 1;
2552         if (recipmsgnum < recip->maxmsg) {
2553                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2554         } else {
2555                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2556         }
2557         ast_unlock_path(todir);
2558         notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2559         
2560         return 0;
2561 }
2562
2563 #ifndef ODBC_STORAGE
2564
2565 static int messagecount(const char *context, const char *mailbox, const char *folder)
2566 {
2567         return __has_voicemail(context, mailbox, folder, 0);
2568 }
2569
2570
2571 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2572 {
2573         DIR *dir;
2574         struct dirent *de;
2575         char fn[256];
2576         int ret = 0;
2577         if (!folder)
2578                 folder = "INBOX";
2579         /* If no mailbox, return immediately */
2580         if (ast_strlen_zero(mailbox))
2581                 return 0;
2582         if (!context)
2583                 context = "default";
2584         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2585         dir = opendir(fn);
2586         if (!dir)
2587                 return 0;
2588         while ((de = readdir(dir))) {
2589                 if (!strncasecmp(de->d_name, "msg", 3)) {
2590                         if (shortcircuit) {
2591                                 ret = 1;
2592                                 break;
2593                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2594                                 ret++;
2595                 }
2596         }
2597         closedir(dir);
2598         return ret;
2599 }
2600
2601
2602 static int has_voicemail(const char *mailbox, const char *folder)
2603 {
2604         char tmp[256], *tmp2 = tmp, *mbox, *context;
2605         ast_copy_string(tmp, mailbox, sizeof(tmp));
2606         while ((mbox = strsep(&tmp2, ","))) {
2607                 if ((context = strchr(mbox, '@')))
2608                         *context++ = '\0';
2609                 else
2610                         context = "default";
2611                 if (__has_voicemail(context, mbox, folder, 1))
2612                         return 1;
2613         }
2614         return 0;
2615 }
2616
2617
2618 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2619 {
2620         char tmp[256];
2621         char *context;
2622
2623         if (newmsgs)
2624                 *newmsgs = 0;
2625         if (oldmsgs)
2626                 *oldmsgs = 0;
2627         /* If no mailbox, return immediately */
2628         if (ast_strlen_zero(mailbox))
2629                 return 0;
2630         if (strchr(mailbox, ',')) {
2631                 int tmpnew, tmpold;
2632                 char *mb, *cur;
2633
2634                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2635                 mb = tmp;
2636                 while ((cur = strsep(&mb, ", "))) {
2637                         if (!ast_strlen_zero(cur)) {
2638                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2639                                         return -1;
2640                                 else {
2641                                         if (newmsgs)
2642                                                 *newmsgs += tmpnew; 
2643                                         if (oldmsgs)
2644                                                 *oldmsgs += tmpold;
2645                                 }
2646                         }
2647                 }
2648                 return 0;
2649         }
2650         ast_copy_string(tmp, mailbox, sizeof(tmp));
2651         context = strchr(tmp, '@');
2652         if (context) {
2653                 *context = '\0';
2654                 context++;
2655         } else
2656                 context = "default";
2657         if (newmsgs)
2658                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2659         if (oldmsgs)
2660                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2661         return 0;
2662 }
2663
2664 #endif
2665
2666 static void run_externnotify(char *context, char *extension)
2667 {
2668         char arguments[255];
2669         char ext_context[256] = "";
2670         int newvoicemails = 0, oldvoicemails = 0;
2671         struct ast_smdi_mwi_message *mwi_msg;
2672
2673         if (!ast_strlen_zero(context))
2674                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2675         else
2676                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2677
2678         if (!strcasecmp(externnotify, "smdi")) {
2679                 if (ast_app_has_voicemail(ext_context, NULL)) 
2680                         ast_smdi_mwi_set(smdi_iface, extension);
2681                 else
2682                         ast_smdi_mwi_unset(smdi_iface, extension);
2683
2684                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2685                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2686                         if (!strncmp(mwi_msg->cause, "INV", 3))
2687                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2688                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2689                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2690                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2691                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2692                 } else {
2693                         if (option_debug)
2694                                 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2695                 }
2696         } else if (!ast_strlen_zero(externnotify)) {
2697                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2698                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2699                 } else {
2700                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2701                         if (option_debug)
2702                                 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2703                         ast_safe_system(arguments);
2704                 }
2705         }
2706 }
2707
2708 struct leave_vm_options {
2709         unsigned int flags;
2710         signed char record_gain;
2711 };
2712
2713 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2714 {
2715 #ifdef IMAP_STORAGE
2716         int newmsgs, oldmsgs;
2717         struct vm_state *vms;
2718 #else
2719         char tmptxtfile[256], txtfile[256];
2720         char callerid[256];
2721         FILE *txt;
2722         char date[256];
2723         int txtdes;
2724 #endif
2725         int res = 0;
2726         int msgnum;
2727         int duration = 0;
2728         int ausemacro = 0;
2729         int ousemacro = 0;
2730         int ouseexten = 0;
2731         char dir[256], tmpdir[260];
2732         char fn[256];
2733         char prefile[256]="";
2734         char tempfile[256]="";
2735         char ext_context[256] = "";
2736         char fmt[80];
2737         char *context;
2738         char ecodes[16] = "#";
2739         char tmp[256] = "", *tmpptr;
2740         struct ast_vm_user *vmu;
2741         struct ast_vm_user svm;
2742         const char *category = NULL;
2743
2744         ast_copy_string(tmp, ext, sizeof(tmp));
2745         ext = tmp;
2746         context = strchr(tmp, '@');
2747         if (context) {
2748                 *context++ = '\0';
2749                 tmpptr = strchr(context, '&');
2750         } else {
2751                 tmpptr = strchr(ext, '&');
2752         }
2753
2754         if (tmpptr)
2755                 *tmpptr++ = '\0';
2756
2757         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2758
2759         if(option_debug > 2)
2760                 ast_log(LOG_DEBUG, "Before find_user\n");
2761         if (!(vmu = find_user(&svm, context, ext))) {
2762                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2763                 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2764                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2765                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2766                 return res;
2767         }
2768
2769         /* Setup pre-file if appropriate */
2770         if (strcmp(vmu->context, "default"))
2771                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2772         else
2773                 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2774         if (ast_test_flag(options, OPT_BUSY_GREETING))
2775                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2776         else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2777                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2778         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2779         RETRIEVE(tempfile, -1);
2780         if (ast_fileexists(tempfile, NULL, NULL) > 0)
2781                 ast_copy_string(prefile, tempfile, sizeof(prefile));
2782         DISPOSE(tempfile, -1);
2783         /* It's easier just to try to make it than to check for its existence */
2784         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2785         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2786
2787         /* Check current or macro-calling context for special extensions */
2788         if (ast_test_flag(vmu, VM_OPERATOR)) {
2789                 if (!ast_strlen_zero(vmu->exit)) {
2790                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2791                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2792                                 ouseexten = 1;
2793                         }
2794                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2795                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2796                         ouseexten = 1;
2797                 }
2798                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2799                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2800                 ousemacro = 1;
2801                 }
2802         }
2803
2804         if (!ast_strlen_zero(vmu->exit)) {
2805                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2806                         strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2807         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2808                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2809         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2810                 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2811                 ausemacro = 1;
2812         }
2813
2814         /* Play the beginning intro if desired */
2815         if (!ast_strlen_zero(prefile)) {
2816                 RETRIEVE(prefile, -1);
2817                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2818                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
2819                                 res = ast_waitstream(chan, ecodes);
2820                 } else {
2821                         if (option_debug)
2822                                 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2823                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2824                 }
2825                 DISPOSE(prefile, -1);
2826                 if (res < 0) {
2827                         if (option_debug)
2828                                 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2829                         free_user(vmu);
2830                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2831                         return -1;
2832                 }
2833         }
2834         if (res == '#') {
2835                 /* On a '#' we skip the instructions */
2836                 ast_set_flag(options, OPT_SILENT);
2837                 res = 0;
2838         }
2839         if (!res && !ast_test_flag(options, OPT_SILENT)) {
2840                 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2841                 if (res == '#') {
2842                         ast_set_flag(options, OPT_SILENT);
2843                         res = 0;
2844                 }
2845         }
2846         if (res > 0)
2847                 ast_stopstream(chan);
2848         /* Check for a '*' here in case the caller wants to escape from voicemail to something
2849          other than the operator -- an automated attendant or mailbox login for example */
2850         if (res == '*') {
2851                 chan->exten[0] = 'a';
2852                 chan->exten[1] = '\0';
2853                 if (!ast_strlen_zero(vmu->exit)) {
2854                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2855                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2856                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2857                 }
2858                 chan->priority = 0;
2859                 free_user(vmu);
2860                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2861                 return 0;
2862         }
2863
2864         /* Check for a '0' here */
2865         if (res == '0') {
2866         transfer:
2867                 if (ouseexten || ousemacro) {
2868                         chan->exten[0] = 'o';
2869                         chan->exten[1] = '\0';
2870                         if (!ast_strlen_zero(vmu->exit)) {
2871                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2872                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2873                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2874                         }
2875                         ast_play_and_wait(chan, "transfer");
2876                         chan->priority = 0;
2877                         free_user(vmu);
2878                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2879                 }
2880                 return 0;
2881         }
2882         if (res < 0) {
2883                 free_user(vmu);
2884                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2885                 return -1;
2886         }
2887         /* The meat of recording the message...  All the announcements and beeps have been played*/
2888         ast_copy_string(fmt, vmfmts, sizeof(fmt));
2889         if (!ast_strlen_zero(fmt)) {
2890                 msgnum = 0;
2891
2892 #ifdef IMAP_STORAGE
2893                 /* Is ext a mailbox? */
2894                 /* must open stream for this user to get info! */
2895                 vms = get_vm_state_by_mailbox(ext,0);
2896                 if (vms) {
2897                         if(option_debug > 2)
2898                                 ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
2899                         newmsgs = vms->newmessages++;
2900                         oldmsgs = vms->oldmessages;
2901                 } else {
2902                         res = count_messages_imap(ext, &newmsgs, &oldmsgs);
2903                         if(res < 0) {
2904                                 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
2905                                 return -1;
2906                         }
2907                 }
2908                 /* here is a big difference! We add one to it later */
2909                 msgnum = newmsgs + oldmsgs;
2910                 ast_log(LOG_NOTICE, "Messagecount set to %d\n",msgnum);
2911                 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
2912                 /* set variable for compatibility */
2913                 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
2914
2915                 /* Check if mailbox is full */
2916                 if (vms->quota_usage >= vms->quota_limit) {
2917                         if (option_debug)
2918                                 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
2919                         ast_play_and_wait(chan, "vm-mailboxfull");
2920                         return -1;
2921                 }
2922
2923                 /* play beep */
2924                 res = ast_streamfile(chan, "beep", chan->language);
2925                 if (!res)
2926                         res = ast_waitstream(chan, "");
2927                 /* play_record_review does recording and verify */
2928                 if (option_debug)
2929                         ast_log(LOG_DEBUG, "About to record message in file %s\n",fn);
2930                 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2931                 if (res == '0') {
2932                         goto transfer;
2933                 }
2934                 if (res > 0) res = 0;
2935
2936                 if (duration < vmminmessage) {
2937                         if (option_verbose > 2) 
2938                                 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2939                         goto leave_vm_out;
2940                 }
2941                 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2942 #else
2943                 if (count_messages(vmu, dir) >= vmu->maxmsg) {
2944                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2945                         if (!res)
2946                                 res = ast_waitstream(chan, "");
2947                         ast_log(LOG_WARNING, "No more messages possible\n");
2948                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2949                         goto leave_vm_out;
2950                 }
2951
2952                 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
2953                 txtdes = mkstemp(tmptxtfile);
2954                 if (txtdes < 0) {
2955                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2956                         if (!res)
2957                                 res = ast_waitstream(chan, "");
2958                         ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
2959                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2960                         goto leave_vm_out;
2961                 }
2962
2963                 /* Now play the beep once we have the message number for our next message. */
2964                 if (res >= 0) {
2965                         /* Unless we're *really* silent, try to send the beep */
2966                         res = ast_stream_and_wait(chan, "beep", chan->language, "");
2967                 }
2968                                 
2969                 /* Store information */
2970                 txt = fdopen(txtdes, "w+");
2971                 if (txt) {
2972                         get_date(date, sizeof(date));
2973                         fprintf(txt, 
2974                                 ";\n"
2975                                 "; Message Information file\n"
2976                                 ";\n"
2977                                 "[message]\n"
2978                                 "origmailbox=%s\n"
2979                                 "context=%s\n"
2980                                 "macrocontext=%s\n"
2981                                 "exten=%s\n"
2982                                 "priority=%d\n"
2983                                 "callerchan=%s\n"
2984                                 "callerid=%s\n"
2985                                 "origdate=%s\n"
2986                                 "origtime=%ld\n"
2987                                 "category=%s\n",
2988                                 ext,
2989                                 chan->context,
2990                                 chan->macrocontext, 
2991                                 chan->exten,
2992                                 chan->priority,
2993                                 chan->name,
2994                                 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2995                                 date, (long)time(NULL),
2996                                 category ? category : ""); 
2997                 } else
2998                         ast_log(LOG_WARNING, "Error opening text file for output\n");
2999                 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain);
3000                                 
3001                 if (txt) {
3002                         if (duration < vmminmessage) {
3003                                 if (option_verbose > 2) 
3004                                         ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
3005                                 ast_filedelete(tmptxtfile, NULL);
3006                                 unlink(tmptxtfile);
3007                         } else {
3008                                 fprintf(txt, "duration=%d\n", duration);
3009                                 fclose(txt);
3010                                 if (vm_lock_path(dir)) {
3011                                         ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
3012                                         /* Delete files */
3013                                         ast_filedelete(tmptxtfile, NULL);
3014                                         unlink(tmptxtfile);
3015                                 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3016                                         if (option_debug) 
3017                                                 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3018                                         unlink(tmptxtfile);
3019                                         ast_unlock_path(dir);
3020                                 } else {
3021                                         msgnum = last_message_index(vmu, dir) + 1;
3022                                         make_file(fn, sizeof(fn), dir, msgnum);
3023
3024                                         /* assign a variable with the name of the voicemail file */ 
3025                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3026
3027                                         snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3028                                         ast_filerename(tmptxtfile, fn, NULL);
3029                                         rename(tmptxtfile, txtfile);
3030
3031                                         ast_unlock_path(dir);
3032                                         /* Are there to be more recipients of this message? */
3033                                         while (tmpptr) {
3034                                                 struct ast_vm_user recipu, *recip;
3035                                                 char *exten, *context;
3036                                         
3037                                                 exten = strsep(&tmpptr, "&");
3038                                                 context = strchr(exten, '@');
3039                                                 if (context) {
3040                                                         *context = '\0';
3041                                                         context++;
3042                                                 }
3043                                                 if ((recip = find_user(&recipu, context, exten))) {
3044                                                         copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
3045                                                         free_user(recip);
3046                                                 }
3047                                         }
3048                                         if (ast_fileexists(fn, NULL, NULL)) {
3049                                                 STORE(dir, vmu->mailbox, vmu->context, msgnum);
3050                                                 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
3051                                                 DISPOSE(dir, msgnum);
3052                                         }
3053                                 }
3054                         }
3055                 }
3056 #endif
3057                 if (res == '0') {
3058                         goto transfer;
3059                 } else if (res > 0)
3060                         res = 0;
3061
3062                 if (duration < vmminmessage)
3063                         /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3064                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3065                 else
3066                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3067         } else
3068                 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3069 leave_vm_out:
3070         free_user(vmu);
3071         
3072         return res;
3073 }
3074
3075 #ifndef IMAP_STORAGE
3076 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3077 {
3078         /* we know max messages, so stop process when number is hit */
3079
3080         int x,dest;
3081         char sfn[256];
3082         char dfn[256];
3083
3084         if (vm_lock_path(dir))
3085                 return ERROR_LOCK_PATH;
3086
3087         for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3088                 make_file(sfn, sizeof(sfn), dir, x);
3089                 if (EXISTS(dir, x, sfn, NULL)) {
3090                         
3091                         if (x != dest) {
3092                                 make_file(dfn, sizeof(dfn), dir, dest);
3093                                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3094                         }
3095                         
3096                         dest++;
3097                 }
3098         }
3099         ast_unlock_path(dir);
3100
3101         return 0;
3102 }
3103 #endif
3104
3105 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3106 {
3107         int d;
3108         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3109         return d;
3110 }
3111
3112 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3113 {
3114 #ifdef IMAP_STORAGE
3115         /* we must use mbox(x) folder names, and copy the message there */
3116         /* simple. huh? */
3117         char *dbox = mbox(box);
3118         long res;
3119         char sequence[10];
3120
3121         /* if save to Old folder, just leave in INBOX */
3122         if (box == 1) return 10;
3123         /* get the real IMAP message number for this message */
3124         sprintf(sequence,"%ld",vms->msgArray[msg]);
3125         if (option_debug > 2)
3126                 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3127         res = mail_copy(vms->mailstream,sequence,dbox);
3128         if (res == 1) return 0;
3129         return 1;
3130 #else
3131         char *dir = vms->curdir;
3132         char *username = vms->username;
3133         char *context = vmu->context;
3134         char sfn[256];
3135         char dfn[256];
3136         char ddir[256];
3137         const char *dbox = mbox(box);
3138         int x;
3139         make_file(sfn, sizeof(sfn), dir, msg);
3140         create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3141
3142         if (vm_lock_path(ddir))
3143                 return ERROR_LOCK_PATH;
3144
3145         x = last_message_index(vmu, ddir) + 1;
3146         make_file(dfn, sizeof(dfn), ddir, x);
3147
3148         if (x >= vmu->maxmsg) {
3149                 ast_unlock_path(ddir);
3150                 return -1;
3151         }
3152         if (strcmp(sfn, dfn)) {
3153                 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3154         }
3155         ast_unlock_path(ddir);
3156 #endif
3157         return 0;
3158 }
3159
3160 static int adsi_logo(unsigned char *buf)
3161 {
3162         int bytes = 0;
3163         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3164         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
3165         return bytes;
3166 }
3167
3168 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3169 {
3170         unsigned char buf[256];
3171         int bytes=0;
3172         int x;
3173         char num[5];
3174
3175         *useadsi = 0;
3176         bytes += ast_adsi_data_mode(buf + bytes);
3177         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3178
3179         bytes = 0;
3180         bytes += adsi_logo(buf);
3181         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3182 #ifdef DISPLAY
3183         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
3184 #endif
3185         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3186         bytes += ast_adsi_data_mode(buf + bytes);
3187         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3188
3189         if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3190                 bytes = 0;
3191                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3192                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3193                 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3194                 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3195                 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3196                 return 0;
3197         }
3198
3199 #ifdef DISPLAY
3200         /* Add a dot */
3201         bytes = 0;
3202         bytes += ast_adsi_logo(buf);
3203         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3204         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
3205         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3206         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3207 #endif
3208         bytes = 0;
3209         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
3210         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
3211         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
3212         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
3213         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
3214         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
3215         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3216
3217 #ifdef DISPLAY
3218         /* Add another dot */
3219         bytes = 0;
3220         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
3221         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3222
3223         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3224         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3225 #endif
3226
3227         bytes = 0;
3228         /* These buttons we load but don't use yet */
3229         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
3230         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
3231         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
3232         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
3233         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
3234         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
3235         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3236
3237 #ifdef DISPLAY
3238         /* Add another dot */
3239         bytes = 0;
3240         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
3241         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3242         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3243 #endif
3244
3245         bytes = 0;
3246         for (x=0;x<5;x++) {
3247                 snprintf(num, sizeof(num), "%d", x);
3248                 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
3249         }
3250         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
3251         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3252
3253 #ifdef DISPLAY
3254         /* Add another dot */
3255         bytes = 0;
3256         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
3257         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3258         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3259 #endif
3260
3261         if (ast_adsi_end_download(chan)) {
3262                 bytes = 0;
3263                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
3264                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3265                 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3266                 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3267                 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3268                 return 0;
3269         }
3270         bytes = 0;
3271         bytes += ast_adsi_download_disconnect(buf + bytes);
3272         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3273         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3274
3275         if (option_debug)
3276                 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
3277
3278 #ifdef DISPLAY
3279         /* Add last dot */
3280         bytes = 0;
3281         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
3282         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3283 #endif
3284         if (option_debug)
3285                 ast_log(LOG_DEBUG, "Restarting session...\n");
3286
3287         bytes = 0;
3288         /* Load the session now */
3289         if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
3290                 *useadsi = 1;
3291                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
3292         } else
3293                 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
3294
3295         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3296         return 0;
3297 }
3298
3299 static void adsi_begin(struct ast_channel *chan, int *useadsi)
3300 {
3301         int x;
3302         if (!ast_adsi_available(chan))
3303                 return;
3304         x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
3305         if (x < 0)
3306                 return;
3307         if (!x) {
3308                 if (adsi_load_vmail(chan, useadsi)) {
3309                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
3310                         return;
3311                 }
3312         } else
3313                 *useadsi = 1;
3314 }
3315
3316 static void adsi_login(struct ast_channel *chan)
3317 {
3318         unsigned char buf[256];
3319         int bytes=0;
3320         unsigned char keys[8];
3321         int x;
3322         if (!ast_adsi_available(chan))
3323                 return;
3324
3325         for (x=0;x<8;x++)
3326                 keys[x] = 0;
3327         /* Set one key for next */
3328         keys[3] = ADSI_KEY_APPS + 3;
3329
3330         bytes += adsi_logo(buf + bytes);
3331         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
3332         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
3333         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3334         bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
3335         bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
3336         bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
3337         bytes += ast_adsi_set_keys(buf + bytes, keys);
3338         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3339         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3340 }
3341
3342 static void adsi_password(struct ast_channel *chan)
3343 {
3344         unsigned char buf[256];
3345         int bytes=0;
3346         unsigned char keys[8];
3347         int x;
3348         if (!ast_adsi_available(chan))
3349                 return;
3350
3351         for (x=0;x<8;x++)
3352                 keys[x] = 0;
3353         /* Set one key for next */
3354         keys[3] = ADSI_KEY_APPS + 3;
3355
3356         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3357         bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
3358         bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
3359         bytes += ast_adsi_set_keys(buf + bytes, keys);
3360         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3361         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3362 }
3363
3364 static void adsi_folders(struct ast_channel *chan, int start, char *label)
3365 {
3366         unsigned char buf[256];
3367         int bytes=0;
3368         unsigned char keys[8];
3369         int x,y;
3370
3371         if (!ast_adsi_available(chan))
3372                 return;
3373
3374         for (x=0;x<5;x++) {
3375                 y = ADSI_KEY_APPS + 12 + start + x;
3376                 if (y > ADSI_KEY_APPS + 12 + 4)
3377                         y = 0;
3378                 keys[x] = ADSI_KEY_SKT | y;
3379         }
3380         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
3381         keys[6] = 0;
3382         keys[7] = 0;
3383
3384         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
3385         bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
3386         bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3387         bytes += ast_adsi_set_keys(buf + bytes, keys);
3388         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3389
3390         ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3391 }
3392
3393 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
3394 {
3395         int bytes=0;
3396         unsigned char buf[256]; 
3397         char buf1[256], buf2[256];
3398         char fn2[256];
3399
3400         char cid[256]="";
3401         char *val;
3402         char *name, *num;
3403         char datetime[21]="";
3404         FILE *f;
3405
3406         unsigned char keys[8];
3407
3408         int x;
3409
3410         if (!ast_adsi_available(chan))
3411                 return;
3412
3413         /* Retrieve important info */
3414         snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
3415         f = fopen(fn2, "r");
3416         if (f) {
3417                 while (!feof(f)) {      
3418                         fgets((char *)buf, sizeof(buf), f);
3419                         if (!feof(f)) {
3420                                 char *stringp=NULL;
3421                                 stringp = (char *)buf;
3422                                 strsep(&stringp, "=");
3423                                 val = strsep(&stringp, "=");
3424                                 if (!ast_strlen_zero(val)) {
3425                                         if (!strcmp((char *)buf, "callerid"))
3426                                                 ast_copy_string(cid, val, sizeof(cid));
3427                                         if (!strcmp((char *)buf, "origdate"))
3428                                                 ast_copy_string(datetime, val, sizeof(datetime));
3429                                 }
3430                         }
3431                 }
3432                 fclose(f);
3433         }
3434         /* New meaning for keys */
3435         for (x=0;x<5;x++)
3436                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3437         keys[6] = 0x0;
3438         keys[7] = 0x0;
3439
3440         if (!vms->curmsg) {
3441                 /* No prev key, provide "Folder" instead */
3442                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3443         }
3444         if (vms->curmsg >= vms->lastmsg) {
3445                 /* If last message ... */
3446                 if (vms->curmsg) {
3447                         /* but not only message, provide "Folder" instead */
3448                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3449                         bytes += ast_adsi_voice_mode(buf + bytes, 0);
3450
3451                 } else {
3452                         /* Otherwise if only message, leave blank */
3453                         keys[3] = 1;
3454                 }
3455         }
3456
3457         if (!ast_strlen_zero(cid)) {
3458                 ast_callerid_parse(cid, &name, &num);
3459                 if (!name)
3460                         name = num;
3461         } else
3462                 name = "Unknown Caller";
3463
3464         /* If deleted, show "undeleted" */
3465
3466         if (vms->deleted[vms->curmsg])
3467                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3468
3469         /* Except "Exit" */
3470         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3471         snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
3472                 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");