60df5dcead1e835a08825c1d3b301cf5e29aaaf7
[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 char *curhst = NIL;           /* currently connected host */
93 char *curusr = NIL;           /* current login user */
94
95 char temp[1024];
96
97 static char imapserver[48];
98 static char imapport[8];
99 static char imapflags[128];
100 static char authuser[32];
101 static char authpassword[42];
102 static int expungeonhangup = 1;
103 static char delimiter = '\0';
104
105 struct vm_state;
106
107 int init_mailstream (struct vm_state *vms);
108 void write_file (char *filename, char *buffer, unsigned long len);
109 void status (MAILSTREAM *stream);
110 void display_body (BODY *body, char *pfx, long i);
111 static char *get_header_by_tag(char *header, char *tag);
112 static void vm_imap_delete(int msgnum, struct vm_state *vms);
113 static char *get_user_by_mailbox(char *mailbox);
114 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
115 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
116 static void vmstate_insert(struct vm_state *vms);
117 static void vmstate_delete(struct vm_state *vms);
118 static void set_update(MAILSTREAM * stream);
119 static void init_vm_state(struct vm_state *vms);
120 static void check_msgArray(struct vm_state *vms);
121 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
122 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
123 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num);
124 static void get_mailbox_delimiter(MAILSTREAM *stream);
125 void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
126 /* should define TMP in config file... */
127 #define TMP "/tmp"
128 struct vmstate {
129       struct vm_state *vms;
130       struct vmstate *next;
131 };
132 AST_MUTEX_DEFINE_STATIC(vmstate_lock);
133 static struct vmstate *vmstates = NULL;
134 #endif
135
136 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
137
138 #define COMMAND_TIMEOUT 5000
139 /* Don't modify these here; set your umask at runtime instead */
140 #define VOICEMAIL_DIR_MODE      0777
141 #define VOICEMAIL_FILE_MODE     0666
142
143 #define VOICEMAIL_CONFIG "voicemail.conf"
144 #define ASTERISK_USERNAME "asterisk"
145
146 /* Default mail command to mail voicemail. Change it with the
147     mailcmd= command in voicemail.conf */
148 #define SENDMAIL "/usr/sbin/sendmail -t"
149
150 #define INTRO "vm-intro"
151
152 #define MAXMSG 100
153 #define MAXMSGLIMIT 9999
154
155 #define BASEMAXINLINE 256
156 #define BASELINELEN 72
157 #define BASEMAXINLINE 256
158 #define eol "\r\n"
159
160 #define MAX_DATETIME_FORMAT     512
161 #define MAX_NUM_CID_CONTEXTS 10
162
163 #define VM_REVIEW        (1 << 0)
164 #define VM_OPERATOR      (1 << 1)
165 #define VM_SAYCID        (1 << 2)
166 #define VM_SVMAIL        (1 << 3)
167 #define VM_ENVELOPE      (1 << 4)
168 #define VM_SAYDURATION   (1 << 5)
169 #define VM_SKIPAFTERCMD  (1 << 6)
170 #define VM_FORCENAME     (1 << 7)   /*!< Have new users record their name */
171 #define VM_FORCEGREET    (1 << 8)   /*!< Have new users record their greetings */
172 #define VM_PBXSKIP       (1 << 9)
173 #define VM_DIRECFORWARD  (1 << 10)  /*!< directory_forward */
174 #define VM_ATTACH        (1 << 11)
175 #define VM_DELETE        (1 << 12)
176 #define VM_ALLOCED       (1 << 13)
177 #define VM_SEARCH        (1 << 14)
178 #define VM_TEMPGREETWARN (1 << 15)  /*!< Remind user tempgreeting is set */
179 #define ERROR_LOCK_PATH  -100
180
181
182 enum {
183         OPT_SILENT =           (1 << 0),
184         OPT_BUSY_GREETING =    (1 << 1),
185         OPT_UNAVAIL_GREETING = (1 << 2),
186         OPT_RECORDGAIN =       (1 << 3),
187         OPT_PREPEND_MAILBOX =  (1 << 4),
188         OPT_PRIORITY_JUMP =    (1 << 5),
189         OPT_AUTOPLAY =         (1 << 6),
190 } vm_option_flags;
191
192 enum {
193         OPT_ARG_RECORDGAIN = 0,
194         OPT_ARG_PLAYFOLDER = 1,
195         /* This *must* be the last value in this enum! */
196         OPT_ARG_ARRAY_SIZE = 2,
197 } vm_option_args;
198
199 AST_APP_OPTIONS(vm_app_options, {
200         AST_APP_OPTION('s', OPT_SILENT),
201         AST_APP_OPTION('b', OPT_BUSY_GREETING),
202         AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
203         AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
204         AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
205         AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
206         AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
207 });
208
209 static int load_config(void);
210
211 /*! \page vmlang Voicemail Language Syntaxes Supported
212
213         \par Syntaxes supported, not really language codes.
214         \arg \b en - English
215         \arg \b de - German
216         \arg \b es - Spanish
217         \arg \b fr - French
218         \arg \b it = Italian
219         \arg \b nl - Dutch
220         \arg \b pt - Polish
221         \arg \b pt - Portuguese
222         \arg \b gr - Greek
223         \arg \b no - Norwegian
224         \arg \b se - Swedish
225
226 German requires the following additional soundfile:
227 \arg \b 1F      einE (feminine)
228
229 Spanish requires the following additional soundfile:
230 \arg \b 1M      un (masculine)
231
232 Dutch, Portuguese & Spanish require the following additional soundfiles:
233 \arg \b vm-INBOXs       singular of 'new'
234 \arg \b vm-Olds         singular of 'old/heard/read'
235
236 NB these are plural:
237 \arg \b vm-INBOX        nieuwe (nl)
238 \arg \b vm-Old          oude (nl)
239
240 Polish uses:
241 \arg \b vm-new-a        'new', feminine singular accusative
242 \arg \b vm-new-e        'new', feminine plural accusative
243 \arg \b vm-new-ych      'new', feminine plural genitive
244 \arg \b vm-old-a        'old', feminine singular accusative
245 \arg \b vm-old-e        'old', feminine plural accusative
246 \arg \b vm-old-ych      'old', feminine plural genitive
247 \arg \b digits/1-a      'one', not always same as 'digits/1'
248 \arg \b digits/2-ie     'two', not always same as 'digits/2'
249
250 Swedish uses:
251 \arg \b vm-nytt         singular of 'new'
252 \arg \b vm-nya          plural of 'new'
253 \arg \b vm-gammalt      singular of 'old'
254 \arg \b vm-gamla        plural of 'old'
255 \arg \b digits/ett      'one', not always same as 'digits/1'
256
257 Norwegian uses:
258 \arg \b vm-ny           singular of 'new'
259 \arg \b vm-nye          plural of 'new'
260 \arg \b vm-gammel       singular of 'old'
261 \arg \b vm-gamle        plural of 'old'
262
263 Dutch also uses:
264 \arg \b nl-om           'at'?
265
266 Spanish also uses:
267 \arg \b vm-youhaveno
268
269 Italian requires the following additional soundfile:
270
271 For vm_intro_it:
272 \arg \b vm-nuovo        new
273 \arg \b vm-nuovi        new plural
274 \arg \b vm-vecchio      old
275 \arg \b vm-vecchi       old plural
276
277 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
278 spelled among others when you have to change folder. For the above reasons, vm-INBOX
279 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
280
281 */
282
283 struct baseio {
284         int iocp;
285         int iolen;
286         int linelength;
287         int ateof;
288         unsigned char iobuf[BASEMAXINLINE];
289 };
290
291 /*! Structure for linked list of users */
292 struct ast_vm_user {
293         char context[AST_MAX_CONTEXT];   /*!< Voicemail context */
294         char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
295         char password[80];               /*!< Secret pin code, numbers only */
296         char fullname[80];               /*!< Full name, for directory app */
297         char email[80];                  /*!< E-mail address */
298         char pager[80];                  /*!< E-mail address to pager (no attachment) */
299         char serveremail[80];            /*!< From: Mail address */
300         char mailcmd[160];               /*!< Configurable mail command */
301         char language[MAX_LANGUAGE];     /*!< Config: Language setting */
302         char zonetag[80];                /*!< Time zone */
303         char callback[80];
304         char dialout[80];
305         char uniqueid[20];               /*!< Unique integer identifier */
306         char exit[80];
307         char attachfmt[20];              /*!< Attachment format */
308         unsigned int flags;              /*!< VM_ flags */      
309         int saydurationm;
310         int maxmsg;                      /*!< Maximum number of msgs per folder for this mailbox */
311 #ifdef IMAP_STORAGE
312                 char imapuser[80];      /* IMAP server login */
313 #endif
314         double volgain;         /*!< Volume gain for voicemails sent via email */
315         AST_LIST_ENTRY(ast_vm_user) list;
316 };
317
318 struct vm_zone {
319         AST_LIST_ENTRY(vm_zone) list;
320         char name[80];
321         char timezone[80];
322         char msg_format[512];
323 };
324
325 struct vm_state {
326         char curbox[80];
327         char username[80];
328         char curdir[256];
329         char vmbox[256];
330         char fn[256];
331         char fn2[256];
332         int *deleted;
333         int *heard;
334         int curmsg;
335         int lastmsg;
336         int newmessages;
337         int oldmessages;
338         int starting;
339         int repeats;
340 #ifdef IMAP_STORAGE
341         int updated; /* decremented on each mail check until 1 -allows delay */
342         long msgArray[256];
343         MAILSTREAM *mailstream;
344         int vmArrayIndex;
345         char imapuser[80]; /* IMAP server login */
346         int interactive;
347         unsigned int quota_limit;
348         unsigned int quota_usage;
349         struct vm_state *persist_vms;
350 #endif
351 };
352 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);
353 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
354 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
355                         char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
356                         signed char record_gain);
357 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
358 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
359 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
360 #ifndef ODBC_STORAGE
361 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
362 #endif
363 static void apply_options(struct ast_vm_user *vmu, const char *options);
364
365 #ifdef ODBC_STORAGE
366 static char odbc_database[80];
367 static char odbc_table[80];
368 #define RETRIEVE(a,b) retrieve_file(a,b)
369 #define DISPOSE(a,b) remove_file(a,b)
370 #define STORE(a,b,c,d) store_file(a,b,c,d)
371 #define EXISTS(a,b,c,d) (message_exists(a,b))
372 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
373 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
374 #define DELETE(a,b,c) (delete_file(a,b))
375 #else
376 #ifdef IMAP_STORAGE
377 #define RETRIEVE(a,b)
378 #define DISPOSE(a,b)
379 #define STORE(a,b,c,d)
380 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
381 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
382 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
383 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
384 #define DELETE(a,b,c) (vm_delete(c))
385 #else
386 #define RETRIEVE(a,b)
387 #define DISPOSE(a,b)
388 #define STORE(a,b,c,d)
389 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
390 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
391 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
392 #define DELETE(a,b,c) (vm_delete(c))
393 #endif
394 #endif
395
396 static char VM_SPOOL_DIR[PATH_MAX];
397
398 static char ext_pass_cmd[128];
399 #ifdef ODBC_STORAGE
400 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
401 #else
402 #ifdef IMAP_STORAGE
403 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
404 #else
405 #define tdesc "Comedian Mail (Voicemail System)"
406 #endif
407 #endif
408
409 static char *addesc = "Comedian Mail";
410
411 static char *synopsis_vm =
412 "Leave a Voicemail message";
413
414 static char *descrip_vm =
415 "  VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
416 "application allows the calling party to leave a message for the specified\n"
417 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
418 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
419 "specified mailbox does not exist.\n"
420 "  The Voicemail application will exit if any of the following DTMF digits are\n"
421 "received:\n"
422 "    0 - Jump to the 'o' extension in the current dialplan context.\n"
423 "    * - Jump to the 'a' extension in the current dialplan context.\n"
424 "  This application will set the following channel variable upon completion:\n"
425 "    VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
426 "               application. The possible values are:\n"
427 "               SUCCESS | USEREXIT | FAILED\n\n"
428 "  Options:\n"
429 "    b    - Play the 'busy' greeting to the calling party.\n"
430 "    g(#) - Use the specified amount of gain when recording the voicemail\n"
431 "           message. The units are whole-number decibels (dB).\n"
432 "    s    - Skip the playback of instructions for leaving a message to the\n"
433 "           calling party.\n"
434 "    u    - Play the 'unavailble greeting.\n"
435 "    j    - Jump to priority n+101 if the mailbox is not found or some other\n"
436 "           error occurs.\n";
437
438 static char *synopsis_vmain =
439 "Check Voicemail messages";
440
441 static char *descrip_vmain =
442 "  VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
443 "calling party to check voicemail messages. A specific mailbox, and optional\n"
444 "corresponding context, may be specified. If a mailbox is not provided, the\n"
445 "calling party will be prompted to enter one. If a context is not specified,\n"
446 "the 'default' context will be used.\n\n"
447 "  Options:\n"
448 "    p    - Consider the mailbox parameter as a prefix to the mailbox that\n"
449 "           is entered by the caller.\n"
450 "    g(#) - Use the specified amount of gain when recording a voicemail\n"
451 "           message. The units are whole-number decibels (dB).\n"
452 "    s    - Skip checking the passcode for the mailbox.\n"
453 "    a(#) - Skip folder prompt and go directly to folder specified.\n"
454 "           Defaults to INBOX\n";
455
456 static char *synopsis_vm_box_exists =
457 "Check to see if Voicemail mailbox exists";
458
459 static char *descrip_vm_box_exists =
460 "  MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
461 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
462 "will be used.\n"
463 "  This application will set the following channel variable upon completion:\n"
464 "    VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
465 "                        MailboxExists application. Possible values include:\n"
466 "                        SUCCESS | FAILED\n\n"
467 "  Options:\n"
468 "    j - Jump to priority n+101 if the mailbox is found.\n";
469
470 static char *synopsis_vmauthenticate =
471 "Authenticate with Voicemail passwords";
472
473 static char *descrip_vmauthenticate =
474 "  VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
475 "same way as the Authenticate application, but the passwords are taken from\n"
476 "voicemail.conf.\n"
477 "  If the mailbox is specified, only that mailbox's password will be considered\n"
478 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
479 "be set with the authenticated mailbox.\n\n"
480 "  Options:\n"
481 "    s - Skip playing the initial prompts.\n";
482
483 /* Leave a message */
484 static char *app = "VoiceMail";
485
486 /* Check mail, control, etc */
487 static char *app2 = "VoiceMailMain";
488
489 static char *app3 = "MailboxExists";
490 static char *app4 = "VMAuthenticate";
491
492 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
493 static AST_LIST_HEAD_STATIC(zones, vm_zone);
494 static int maxsilence;
495 static int maxmsg;
496 static int silencethreshold = 128;
497 static char serveremail[80];
498 static char mailcmd[160];       /* Configurable mail cmd */
499 static char externnotify[160]; 
500 static struct ast_smdi_interface *smdi_iface = NULL;
501 static char vmfmts[80];
502 static double volgain;
503 static int vmminmessage;
504 static int vmmaxmessage;
505 static int maxgreet;
506 static int skipms;
507 static int maxlogins;
508
509 static struct ast_flags globalflags = {0};
510
511 static int saydurationminfo;
512
513 static char dialcontext[AST_MAX_CONTEXT];
514 static char callcontext[AST_MAX_CONTEXT];
515 static char exitcontext[AST_MAX_CONTEXT];
516
517 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
518
519
520 static char *emailbody = NULL;
521 static char *emailsubject = NULL;
522 static char *pagerbody = NULL;
523 static char *pagersubject = NULL;
524 static char fromstring[100];
525 static char pagerfromstring[100];
526 static char emailtitle[100];
527 static char charset[32] = "ISO-8859-1";
528
529 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
530 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
531 static int adsiver = 1;
532 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
533
534
535 static void populate_defaults(struct ast_vm_user *vmu)
536 {
537         ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);     
538         if (saydurationminfo)
539                 vmu->saydurationm = saydurationminfo;
540         if (callcontext)
541                 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
542         if (dialcontext)
543                 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
544         if (exitcontext)
545                 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
546         if (maxmsg)
547                 vmu->maxmsg = maxmsg;
548         vmu->volgain = volgain;
549 }
550
551 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
552 {
553         int x;
554         if (!strcasecmp(var, "attach")) {
555                 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
556         } else if (!strcasecmp(var, "attachfmt")) {
557                 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
558         } else if (!strcasecmp(var, "serveremail")) {
559                 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
560         } else if (!strcasecmp(var, "language")) {
561                 ast_copy_string(vmu->language, value, sizeof(vmu->language));
562         } else if (!strcasecmp(var, "tz")) {
563                 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
564 #ifdef IMAP_STORAGE
565         } else if (!strcasecmp(var, "imapuser")) {
566                 strncpy(vmu->imapuser, value, sizeof(vmu->imapuser) - 1);
567 #endif
568         } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
569                 ast_set2_flag(vmu, ast_true(value), VM_DELETE); 
570         } else if (!strcasecmp(var, "saycid")){
571                 ast_set2_flag(vmu, ast_true(value), VM_SAYCID); 
572         } else if (!strcasecmp(var,"sendvoicemail")){
573                 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL); 
574         } else if (!strcasecmp(var, "review")){
575                 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
576         } else if (!strcasecmp(var, "tempgreetwarn")){
577                 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);  
578         } else if (!strcasecmp(var, "operator")){
579                 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);       
580         } else if (!strcasecmp(var, "envelope")){
581                 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);       
582         } else if (!strcasecmp(var, "sayduration")){
583                 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);    
584         } else if (!strcasecmp(var, "saydurationm")){
585                 if (sscanf(value, "%d", &x) == 1) {
586                         vmu->saydurationm = x;
587                 } else {
588                         ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
589                 }
590         } else if (!strcasecmp(var, "forcename")){
591                 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);      
592         } else if (!strcasecmp(var, "forcegreetings")){
593                 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);     
594         } else if (!strcasecmp(var, "callback")) {
595                 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
596         } else if (!strcasecmp(var, "dialout")) {
597                 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
598         } else if (!strcasecmp(var, "exitcontext")) {
599                 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
600         } else if (!strcasecmp(var, "maxmsg")) {
601                 vmu->maxmsg = atoi(value);
602                 if (vmu->maxmsg <= 0) {
603                         ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
604                         vmu->maxmsg = MAXMSG;
605                 } else if (vmu->maxmsg > MAXMSGLIMIT) {
606                         ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
607                         vmu->maxmsg = MAXMSGLIMIT;
608                 }
609         } else if (!strcasecmp(var, "volgain")) {
610                 sscanf(value, "%lf", &vmu->volgain);
611         } else if (!strcasecmp(var, "options")) {
612                 apply_options(vmu, value);
613         }
614 }
615
616 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
617 {
618         int res;
619         if (!ast_strlen_zero(vmu->uniqueid)) {
620                 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
621                 if (res > 0) {
622                         ast_copy_string(vmu->password, password, sizeof(vmu->password));
623                         res = 0;
624                 } else if (!res) {
625                         res = -1;
626                 }
627                 return res;
628         }
629         return -1;
630 }
631
632 static void apply_options(struct ast_vm_user *vmu, const char *options)
633 {       /* Destructively Parse options and apply */
634         char *stringp;
635         char *s;
636         char *var, *value;
637         stringp = ast_strdupa(options);
638         while ((s = strsep(&stringp, "|"))) {
639                 value = s;
640                 if ((var = strsep(&value, "=")) && value) {
641                         apply_option(vmu, var, value);
642                 }
643         }       
644 }
645
646 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
647 {
648         struct ast_variable *var, *tmp;
649         struct ast_vm_user *retval;
650
651         if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
652                 if (!ivm)
653                         ast_set_flag(retval, VM_ALLOCED);       
654                 else
655                         memset(retval, 0, sizeof(*retval));
656                 if (mailbox) 
657                         ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
658                 populate_defaults(retval);
659                 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
660                         var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
661                 else
662                         var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
663                 if (var) {
664                         tmp = var;
665                         while (tmp) {
666                                 printf("%s => %s\n", tmp->name, tmp->value);
667                                 if (!strcasecmp(tmp->name, "password")) {
668                                         ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
669                                 } else if (!strcasecmp(tmp->name, "uniqueid")) {
670                                         ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
671                                 } else if (!strcasecmp(tmp->name, "pager")) {
672                                         ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
673                                 } else if (!strcasecmp(tmp->name, "email")) {
674                                         ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
675                                 } else if (!strcasecmp(tmp->name, "fullname")) {
676                                         ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
677                                 } else if (!strcasecmp(tmp->name, "context")) {
678                                         ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
679                                 } else
680                                         apply_option(retval, tmp->name, tmp->value);
681                                 tmp = tmp->next;
682                         } 
683                         ast_variables_destroy(var);
684                 } else { 
685                         if (!ivm) 
686                                 free(retval);
687                         retval = NULL;
688                 }       
689         } 
690         return retval;
691 }
692
693 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
694 {
695         /* This function could be made to generate one from a database, too */
696         struct ast_vm_user *vmu=NULL, *cur;
697         AST_LIST_LOCK(&users);
698
699         if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
700                 context = "default";
701
702         AST_LIST_TRAVERSE(&users, cur, list) {
703                 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
704                         break;
705                 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
706                         break;
707         }
708         if (cur) {
709                 /* Make a copy, so that on a reload, we have no race */
710                 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
711                         memcpy(vmu, cur, sizeof(*vmu));
712                         ast_set2_flag(vmu, !ivm, VM_ALLOCED);
713                         AST_LIST_NEXT(vmu, list) = NULL;
714                 }
715         } else
716                 vmu = find_user_realtime(ivm, context, mailbox);
717         AST_LIST_UNLOCK(&users);
718         return vmu;
719 }
720
721 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
722 {
723         /* This function could be made to generate one from a database, too */
724         struct ast_vm_user *cur;
725         int res = -1;
726         AST_LIST_LOCK(&users);
727         AST_LIST_TRAVERSE(&users, cur, list) {
728                 if ((!context || !strcasecmp(context, cur->context)) &&
729                         (!strcasecmp(mailbox, cur->mailbox)))
730                                 break;
731         }
732         if (cur) {
733                 ast_copy_string(cur->password, newpass, sizeof(cur->password));
734                 res = 0;
735         }
736         AST_LIST_UNLOCK(&users);
737         return res;
738 }
739
740 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
741 {
742         /*  There's probably a better way of doing this. */
743         /*  That's why I've put the password change in a separate function. */
744         /*  This could also be done with a database function */
745         
746         FILE *configin;
747         FILE *configout;
748         int linenum=0;
749         char inbuf[256];
750         char orig[256];
751         char currcontext[256] ="";
752         char tmpin[PATH_MAX];
753         char tmpout[PATH_MAX];
754         struct stat statbuf;
755
756         if (!change_password_realtime(vmu, newpassword))
757                 return;
758
759         snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
760         snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
761         configin = fopen(tmpin,"r");
762         if (configin)
763                 configout = fopen(tmpout,"w+");
764         else
765                 configout = NULL;
766         if (!configin || !configout) {
767                 if (configin)
768                         fclose(configin);
769                 else
770                         ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
771                 if (configout)
772                         fclose(configout);
773                 else
774                         ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
775                         return;
776         }
777
778         while (!feof(configin)) {
779                 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
780
781                 /* Read in the line */
782                 if (fgets(inbuf, sizeof(inbuf), configin) == NULL)
783                         continue;
784                 linenum++;
785
786                 /* Make a backup of it */
787                 ast_copy_string(orig, inbuf, sizeof(orig));
788
789                 /*
790                   Read the file line by line, split each line into a comment and command section
791                   only parse the command portion of the line
792                 */
793                 if (inbuf[strlen(inbuf) - 1] == '\n')
794                         inbuf[strlen(inbuf) - 1] = '\0';
795
796                 if ((comment = strchr(inbuf, ';')))
797                         *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
798
799                 if (ast_strlen_zero(inbuf)) {
800                         fprintf(configout, "%s", orig);
801                         continue;
802                 }
803
804                 /* Check for a context, first '[' to first ']' */
805                 if ((tmpctx = strchr(inbuf, '['))) {
806                         tmpctxend = strchr(tmpctx, ']');
807                         if (tmpctxend) {
808                                 /* Valid context */
809                                 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
810                                 fprintf(configout, "%s", orig);
811                                 continue;
812                         }
813                 }
814
815                 /* This isn't a context line, check for MBX => PSWD... */
816                 user = inbuf;
817                 if ((pass = strchr(user, '='))) {
818                         /* We have a line in the form of aaaaa=aaaaaa */
819                         *pass++ = '\0';
820
821                         user = ast_strip(user);
822
823                         if (*pass == '>')
824                                 *pass++ = '\0';
825
826                         pass = ast_skip_blanks(pass);
827
828                         /* 
829                            Since no whitespace allowed in fields, or more correctly white space
830                            inside the fields is there for a purpose, we can just terminate pass
831                            at the comma or EOL whichever comes first.
832                         */
833                         if ((rest = strchr(pass, ',')))
834                                 *rest++ = '\0';
835                 } else {
836                         user = NULL;
837                 }                       
838
839                 /* Compare user, pass AND context */
840                 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
841                         !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
842                         !strcasecmp(currcontext, vmu->context)) {
843                         /* This is the line */
844                         if (rest) {
845                                 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
846                         } else {
847                                 fprintf(configout, "%s => %s", user, newpassword);
848                         }
849                         /* If there was a comment on the line print it out */
850                         if (comment) {
851                                 fprintf(configout, ";%s\n", comment);
852                         } else {
853                                 fprintf(configout, "\n");
854                         }
855                 } else {
856                         /* Put it back like it was */
857                         fprintf(configout, "%s", orig);
858                 }
859         }
860         fclose(configin);
861         fclose(configout);
862
863         stat(tmpin, &statbuf);
864         chmod(tmpout, statbuf.st_mode);
865         chown(tmpout, statbuf.st_uid, statbuf.st_gid);
866         unlink(tmpin);
867         rename(tmpout, tmpin);
868         reset_user_pw(vmu->context, vmu->mailbox, newpassword);
869         ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
870 }
871
872 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
873 {
874         char buf[255];
875         snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
876         if (!ast_safe_system(buf))
877                 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
878 }
879
880 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
881 {
882         return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
883 }
884
885 #ifdef IMAP_STORAGE
886 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
887 {
888         char gsmdir[256];
889
890         sprintf(gsmdir,"%s/%s",dir,imapuser);
891         if (mkdir(gsmdir, 01777) && (errno != EEXIST)) {
892                 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", gsmdir, strerror(errno));
893                 return sprintf(dest, "%s/msg%04d", dir, num);
894         }
895         /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
896         return sprintf(dest, "%s/%s/msg%04d", dir, imapuser, num);
897 }
898
899 static void vm_imap_delete(int msgnum, struct vm_state *vms)
900 {
901         unsigned long messageNum = 0;
902         char arg[10];
903
904         /* find real message number based on msgnum */
905         /* this may be an index into vms->msgArray based on the msgnum. */
906
907         messageNum = vms->msgArray[msgnum];
908         if (messageNum == 0) {
909                 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
910                 return;
911         }
912         ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
913         /* delete message */
914         sprintf (arg,"%lu",messageNum);
915         mail_setflag (vms->mailstream,arg,"\\DELETED");
916 }
917
918 #endif
919 static int make_file(char *dest, int len, char *dir, int num)
920 {
921         return snprintf(dest, len, "%s/msg%04d", dir, num);
922 }
923
924 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
925  * \param dest    String. base directory.
926  * \param context String. Ignored if is null or empty string.
927  * \param ext     String. Ignored if is null or empty string.
928  * \param folder  String. Ignored if is null or empty string. 
929  * \return 0 on failure, 1 on success.
930  */
931 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
932 {
933         mode_t  mode = VOICEMAIL_DIR_MODE;
934
935         if (!ast_strlen_zero(context)) {
936                 make_dir(dest, len, context, "", "");
937                 if (mkdir(dest, mode) && errno != EEXIST) {
938                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
939                         return 0;
940                 }
941         }
942         if (!ast_strlen_zero(ext)) {
943                 make_dir(dest, len, context, ext, "");
944                 if (mkdir(dest, mode) && errno != EEXIST) {
945                         ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
946                         return 0;
947                 }
948         }
949         if (!ast_strlen_zero(folder)) {
950                 make_dir(dest, len, context, ext, folder);
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         return 1;
957 }
958
959 /* only return failure if ast_lock_path returns 'timeout',
960    not if the path does not exist or any other reason
961 */
962 static int vm_lock_path(const char *path)
963 {
964         switch (ast_lock_path(path)) {
965         case AST_LOCK_TIMEOUT:
966                 return -1;
967         default:
968                 return 0;
969         }
970 }
971
972
973 #ifdef ODBC_STORAGE
974 static int retrieve_file(char *dir, int msgnum)
975 {
976         int x = 0;
977         int res;
978         int fd=-1;
979         size_t fdlen = 0;
980         void *fdm=NULL;
981         SQLSMALLINT colcount=0;
982         SQLHSTMT stmt;
983         char sql[256];
984         char fmt[80]="";
985         char *c;
986         char coltitle[256];
987         SQLSMALLINT collen;
988         SQLSMALLINT datatype;
989         SQLSMALLINT decimaldigits;
990         SQLSMALLINT nullable;
991         SQLULEN colsize;
992         FILE *f=NULL;
993         char rowdata[80];
994         char fn[256];
995         char full_fn[256];
996         char msgnums[80];
997         
998         struct odbc_obj *obj;
999         obj = odbc_request_obj(odbc_database, 0);
1000         if (obj) {
1001                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1002                 c = strchr(fmt, '|');
1003                 if (c)
1004                         *c = '\0';
1005                 if (!strcasecmp(fmt, "wav49"))
1006                         strcpy(fmt, "WAV");
1007                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1008                 if (msgnum > -1)
1009                         make_file(fn, sizeof(fn), dir, msgnum);
1010                 else
1011                         ast_copy_string(fn, dir, sizeof(fn));
1012                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1013                 f = fopen(full_fn, "w+");
1014                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1015                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1016                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1017                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1018                         odbc_release_obj(obj);
1019                         goto yuck;
1020                 }
1021                 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1022                 res = SQLPrepare(stmt, sql, SQL_NTS);
1023                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1024                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1025                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1026                         odbc_release_obj(obj);
1027                         goto yuck;
1028                 }
1029                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1030                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1031                 res = odbc_smart_execute(obj, stmt);
1032                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1033                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1034                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1035                         odbc_release_obj(obj);
1036                         goto yuck;
1037                 }
1038                 res = SQLFetch(stmt);
1039                 if (res == SQL_NO_DATA) {
1040                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1041                         odbc_release_obj(obj);
1042                         goto yuck;
1043                 }
1044                 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1045                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1046                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1047                         odbc_release_obj(obj);
1048                         goto yuck;
1049                 }
1050                 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
1051                 if (fd < 0) {
1052                         ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1053                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1054                         odbc_release_obj(obj);
1055                         goto yuck;
1056                 }
1057                 res = SQLNumResultCols(stmt, &colcount);
1058                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {   
1059                         ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1060                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1061                         odbc_release_obj(obj);
1062                         goto yuck;
1063                 }
1064                 if (f) 
1065                         fprintf(f, "[message]\n");
1066                 for (x=0;x<colcount;x++) {
1067                         rowdata[0] = '\0';
1068                         collen = sizeof(coltitle);
1069                         res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen, 
1070                                                 &datatype, &colsize, &decimaldigits, &nullable);
1071                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1072                                 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1073                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1074                                 odbc_release_obj(obj);
1075                                 goto yuck;
1076                         }
1077                         if (!strcasecmp(coltitle, "recording")) {
1078                                 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
1079                                 fdlen = colsize;
1080                                 if (fd > -1) {
1081                                         char tmp[1]="";
1082                                         lseek(fd, fdlen - 1, SEEK_SET);
1083                                         if (write(fd, tmp, 1) != 1) {
1084                                                 close(fd);
1085                                                 fd = -1;
1086                                                 continue;
1087                                         }
1088                                         if (fd > -1)
1089                                                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1090                                 }
1091                                 if (fdm) {
1092                                         memset(fdm, 0, fdlen);
1093                                         res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
1094                                         if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1095                                                 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1096                                                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1097                                                 odbc_release_obj(obj);
1098                                                 goto yuck;
1099                                         }
1100                                 }
1101                         } else {
1102                                 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1103                                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1104                                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1105                                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1106                                         odbc_release_obj(obj);
1107                                         goto yuck;
1108                                 }
1109                                 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1110                                         fprintf(f, "%s=%s\n", coltitle, rowdata);
1111                         }
1112                 }
1113                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1114                 odbc_release_obj(obj);
1115         } else
1116                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1117 yuck:   
1118         if (f)
1119                 fclose(f);
1120         if (fdm)
1121                 munmap(fdm, fdlen);
1122         if (fd > -1)
1123                 close(fd);
1124         return x - 1;
1125 }
1126
1127 static int remove_file(char *dir, int msgnum)
1128 {
1129         char fn[256];
1130         char full_fn[256];
1131         char msgnums[80];
1132         
1133         if (msgnum > -1) {
1134                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1135                 make_file(fn, sizeof(fn), dir, msgnum);
1136         } else
1137                 ast_copy_string(fn, dir, sizeof(fn));
1138         ast_filedelete(fn, NULL);       
1139         snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1140         unlink(full_fn);
1141         return 0;
1142 }
1143
1144 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1145 {
1146         int x = 0;
1147         int res;
1148         SQLHSTMT stmt;
1149         char sql[256];
1150         char rowdata[20];
1151         
1152         struct odbc_obj *obj;
1153         obj = odbc_request_obj(odbc_database, 0);
1154         if (obj) {
1155                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1156                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1157                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1158                         odbc_release_obj(obj);
1159                         goto yuck;
1160                 }
1161                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1162                 res = SQLPrepare(stmt, sql, SQL_NTS);
1163                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1164                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1165                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1166                         odbc_release_obj(obj);
1167                         goto yuck;
1168                 }
1169                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1170                 res = odbc_smart_execute(obj, stmt);
1171                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1172                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1173                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1174                         odbc_release_obj(obj);
1175                         goto yuck;
1176                 }
1177                 res = SQLFetch(stmt);
1178                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1179                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1180                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1181                         odbc_release_obj(obj);
1182                         goto yuck;
1183                 }
1184                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1185                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1186                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1187                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1188                         odbc_release_obj(obj);
1189                         goto yuck;
1190                 }
1191                 if (sscanf(rowdata, "%d", &x) != 1)
1192                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1193                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1194                 odbc_release_obj(obj);
1195         } else
1196                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1197 yuck:   
1198         return x - 1;
1199 }
1200
1201 static int message_exists(char *dir, int msgnum)
1202 {
1203         int x = 0;
1204         int res;
1205         SQLHSTMT stmt;
1206         char sql[256];
1207         char rowdata[20];
1208         char msgnums[20];
1209         
1210         struct odbc_obj *obj;
1211         obj = odbc_request_obj(odbc_database, 0);
1212         if (obj) {
1213                 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1214                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1215                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1216                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1217                         odbc_release_obj(obj);
1218                         goto yuck;
1219                 }
1220                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1221                 res = SQLPrepare(stmt, sql, SQL_NTS);
1222                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1223                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1224                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1225                         odbc_release_obj(obj);
1226                         goto yuck;
1227                 }
1228                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1229                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1230                 res = odbc_smart_execute(obj, stmt);
1231                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1232                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1233                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1234                         odbc_release_obj(obj);
1235                         goto yuck;
1236                 }
1237                 res = SQLFetch(stmt);
1238                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1239                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1240                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1241                         odbc_release_obj(obj);
1242                         goto yuck;
1243                 }
1244                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1245                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1246                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1247                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1248                         odbc_release_obj(obj);
1249                         goto yuck;
1250                 }
1251                 if (sscanf(rowdata, "%d", &x) != 1)
1252                         ast_log(LOG_WARNING, "Failed to read message count!\n");
1253                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1254                 odbc_release_obj(obj);
1255         } else
1256                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1257 yuck:   
1258         return x;
1259 }
1260
1261 static int count_messages(struct ast_vm_user *vmu, char *dir)
1262 {
1263         return last_message_index(vmu, dir) + 1;
1264 }
1265
1266 static void delete_file(char *sdir, int smsg)
1267 {
1268         int res;
1269         SQLHSTMT stmt;
1270         char sql[256];
1271         char msgnums[20];
1272         
1273         struct odbc_obj *obj;
1274         obj = odbc_request_obj(odbc_database, 0);
1275         if (obj) {
1276                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1277                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1278                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1279                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1280                         odbc_release_obj(obj);
1281                         goto yuck;
1282                 }
1283                 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1284                 res = SQLPrepare(stmt, sql, SQL_NTS);
1285                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1286                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1287                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1288                         odbc_release_obj(obj);
1289                         goto yuck;
1290                 }
1291                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1292                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1293                 res = odbc_smart_execute(obj, stmt);
1294                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1295                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1296                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1297                         odbc_release_obj(obj);
1298                         goto yuck;
1299                 }
1300                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1301                 odbc_release_obj(obj);
1302         } else
1303                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1304 yuck:
1305         return; 
1306 }
1307
1308 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1309 {
1310         int res;
1311         SQLHSTMT stmt;
1312         char sql[512];
1313         char msgnums[20];
1314         char msgnumd[20];
1315         struct odbc_obj *obj;
1316
1317         delete_file(ddir, dmsg);
1318         obj = odbc_request_obj(odbc_database, 0);
1319         if (obj) {
1320                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1321                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1322                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1323                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1324                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1325                         odbc_release_obj(obj);
1326                         goto yuck;
1327                 }
1328                 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); 
1329                 res = SQLPrepare(stmt, sql, SQL_NTS);
1330                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1331                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1332                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1333                         odbc_release_obj(obj);
1334                         goto yuck;
1335                 }
1336                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1337                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1338                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1339                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1340                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1341                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1342                 res = odbc_smart_execute(obj, stmt);
1343                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1344                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1345                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1346                         odbc_release_obj(obj);
1347                         goto yuck;
1348                 }
1349                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1350                 odbc_release_obj(obj);
1351         } else
1352                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1353 yuck:
1354         return; 
1355 }
1356
1357 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1358 {
1359         int x = 0;
1360         int res;
1361         int fd = -1;
1362         void *fdm=NULL;
1363         size_t fdlen = -1;
1364         SQLHSTMT stmt;
1365         SQLINTEGER len;
1366         char sql[256];
1367         char msgnums[20];
1368         char fn[256];
1369         char full_fn[256];
1370         char fmt[80]="";
1371         char *c;
1372         char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1373         char *category = "";
1374         struct ast_config *cfg=NULL;
1375         struct odbc_obj *obj;
1376
1377         delete_file(dir, msgnum);
1378         obj = odbc_request_obj(odbc_database, 0);
1379         if (obj) {
1380                 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1381                 c = strchr(fmt, '|');
1382                 if (c)
1383                         *c = '\0';
1384                 if (!strcasecmp(fmt, "wav49"))
1385                         strcpy(fmt, "WAV");
1386                 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1387                 if (msgnum > -1)
1388                         make_file(fn, sizeof(fn), dir, msgnum);
1389                 else
1390                         ast_copy_string(fn, dir, sizeof(fn));
1391                 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1392                 cfg = ast_config_load(full_fn);
1393                 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1394                 fd = open(full_fn, O_RDWR);
1395                 if (fd < 0) {
1396                         ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1397                         odbc_release_obj(obj);
1398                         goto yuck;
1399                 }
1400                 if (cfg) {
1401                         context = ast_variable_retrieve(cfg, "message", "context");
1402                         if (!context) context = "";
1403                         macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1404                         if (!macrocontext) macrocontext = "";
1405                         callerid = ast_variable_retrieve(cfg, "message", "callerid");
1406                         if (!callerid) callerid = "";
1407                         origtime = ast_variable_retrieve(cfg, "message", "origtime");
1408                         if (!origtime) origtime = "";
1409                         duration = ast_variable_retrieve(cfg, "message", "duration");
1410                         if (!duration) duration = "";
1411                         category = ast_variable_retrieve(cfg, "message", "category");
1412                         if (!category) category = "";
1413                 }
1414                 fdlen = lseek(fd, 0, SEEK_END);
1415                 lseek(fd, 0, SEEK_SET);
1416                 printf("Length is %zd\n", fdlen);
1417                 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1418                 if (!fdm) {
1419                         ast_log(LOG_WARNING, "Memory map failed!\n");
1420                         odbc_release_obj(obj);
1421                         goto yuck;
1422                 } 
1423                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1424                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1425                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1426                         odbc_release_obj(obj);
1427                         goto yuck;
1428                 }
1429                 if (!ast_strlen_zero(category)) 
1430                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table); 
1431                 else
1432                         snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1433                 res = SQLPrepare(stmt, sql, SQL_NTS);
1434                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1435                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1436                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1437                         odbc_release_obj(obj);
1438                         goto yuck;
1439                 }
1440                 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1441                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1442                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1443                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1444                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1445                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1446                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1447                 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1448                 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1449                 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1450                 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1451                 if (!ast_strlen_zero(category))
1452                         SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1453                 res = odbc_smart_execute(obj, stmt);
1454                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1455                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1456                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1457                         odbc_release_obj(obj);
1458                         goto yuck;
1459                 }
1460                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1461                 odbc_release_obj(obj);
1462         } else
1463                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1464 yuck:   
1465         if (cfg)
1466                 ast_config_destroy(cfg);
1467         if (fdm)
1468                 munmap(fdm, fdlen);
1469         if (fd > -1)
1470                 close(fd);
1471         return x;
1472 }
1473
1474 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1475 {
1476         int res;
1477         SQLHSTMT stmt;
1478         char sql[256];
1479         char msgnums[20];
1480         char msgnumd[20];
1481         struct odbc_obj *obj;
1482
1483         delete_file(ddir, dmsg);
1484         obj = odbc_request_obj(odbc_database, 0);
1485         if (obj) {
1486                 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1487                 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1488                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1489                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1490                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1491                         odbc_release_obj(obj);
1492                         goto yuck;
1493                 }
1494                 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1495                 res = SQLPrepare(stmt, sql, SQL_NTS);
1496                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1497                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1498                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1499                         odbc_release_obj(obj);
1500                         goto yuck;
1501                 }
1502                 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1503                 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1504                 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1505                 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1506                 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1507                 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1508                 res = odbc_smart_execute(obj, stmt);
1509                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1510                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1511                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1512                         odbc_release_obj(obj);
1513                         goto yuck;
1514                 }
1515                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1516                 odbc_release_obj(obj);
1517         } else
1518                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1519 yuck:
1520         return; 
1521 }
1522
1523 #else
1524 static int count_messages(struct ast_vm_user *vmu, char *dir)
1525 {
1526         /* Find all .txt files - even if they are not in sequence from 0000 */
1527
1528         int vmcount = 0;
1529         DIR *vmdir = NULL;
1530         struct dirent *vment = NULL;
1531
1532         if (vm_lock_path(dir))
1533                 return ERROR_LOCK_PATH;
1534
1535         if ((vmdir = opendir(dir))) {
1536                 while ((vment = readdir(vmdir))) {
1537                         if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) 
1538                                 vmcount++;
1539                 }
1540                 closedir(vmdir);
1541         }
1542         ast_unlock_path(dir);
1543         
1544         return vmcount;
1545 }
1546
1547 static void rename_file(char *sfn, char *dfn)
1548 {
1549         char stxt[256];
1550         char dtxt[256];
1551         ast_filerename(sfn,dfn,NULL);
1552         snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1553         snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1554         rename(stxt, dtxt);
1555 }
1556
1557 static int copy(char *infile, char *outfile)
1558 {
1559         int ifd;
1560         int ofd;
1561         int res;
1562         int len;
1563         char buf[4096];
1564
1565 #ifdef HARDLINK_WHEN_POSSIBLE
1566         /* Hard link if possible; saves disk space & is faster */
1567         if (link(infile, outfile)) {
1568 #endif
1569                 if ((ifd = open(infile, O_RDONLY)) < 0) {
1570                         ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1571                         return -1;
1572                 }
1573                 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1574                         ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1575                         close(ifd);
1576                         return -1;
1577                 }
1578                 do {
1579                         len = read(ifd, buf, sizeof(buf));
1580                         if (len < 0) {
1581                                 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1582                                 close(ifd);
1583                                 close(ofd);
1584                                 unlink(outfile);
1585                         }
1586                         if (len) {
1587                                 res = write(ofd, buf, len);
1588                                 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1589                                         ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1590                                         close(ifd);
1591                                         close(ofd);
1592                                         unlink(outfile);
1593                                 }
1594                         }
1595                 } while (len);
1596                 close(ifd);
1597                 close(ofd);
1598                 return 0;
1599 #ifdef HARDLINK_WHEN_POSSIBLE
1600         } else {
1601                 /* Hard link succeeded */
1602                 return 0;
1603         }
1604 #endif
1605 }
1606
1607 static void copy_file(char *frompath, char *topath)
1608 {
1609         char frompath2[256],topath2[256];
1610         ast_filecopy(frompath, topath, NULL);
1611         snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1612         snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1613         copy(frompath2, topath2);
1614 }
1615
1616 /*
1617  * A negative return value indicates an error.
1618  */
1619 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1620 {
1621         int x;
1622         char fn[256];
1623
1624         if (vm_lock_path(dir))
1625                 return ERROR_LOCK_PATH;
1626
1627         for (x = 0; x < vmu->maxmsg; x++) {
1628                 make_file(fn, sizeof(fn), dir, x);
1629                 if (ast_fileexists(fn, NULL, NULL) < 1)
1630                         break;
1631         }
1632         ast_unlock_path(dir);
1633
1634         return x - 1;
1635 }
1636
1637 static int vm_delete(char *file)
1638 {
1639         char *txt;
1640         int txtsize = 0;
1641
1642         txtsize = (strlen(file) + 5)*sizeof(char);
1643         txt = alloca(txtsize);
1644         /* Sprintf here would safe because we alloca'd exactly the right length,
1645          * but trying to eliminate all sprintf's anyhow
1646          */
1647         snprintf(txt, txtsize, "%s.txt", file);
1648         unlink(txt);
1649         return ast_filedelete(file, NULL);
1650 }
1651
1652
1653 #endif
1654 static int
1655 inbuf(struct baseio *bio, FILE *fi)
1656 {
1657         int l;
1658
1659         if (bio->ateof)
1660                 return 0;
1661
1662         if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1663                 if (ferror(fi))
1664                         return -1;
1665
1666                 bio->ateof = 1;
1667                 return 0;
1668         }
1669
1670         bio->iolen= l;
1671         bio->iocp= 0;
1672
1673         return 1;
1674 }
1675
1676 static int 
1677 inchar(struct baseio *bio, FILE *fi)
1678 {
1679         if (bio->iocp>=bio->iolen) {
1680                 if (!inbuf(bio, fi))
1681                         return EOF;
1682         }
1683
1684         return bio->iobuf[bio->iocp++];
1685 }
1686
1687 static int
1688 ochar(struct baseio *bio, int c, FILE *so)
1689 {
1690         if (bio->linelength>=BASELINELEN) {
1691                 if (fputs(eol,so)==EOF)
1692                         return -1;
1693
1694                 bio->linelength= 0;
1695         }
1696
1697         if (putc(((unsigned char)c),so)==EOF)
1698                 return -1;
1699
1700         bio->linelength++;
1701
1702         return 1;
1703 }
1704
1705 static int base_encode(char *filename, FILE *so)
1706 {
1707         unsigned char dtable[BASEMAXINLINE];
1708         int i,hiteof= 0;
1709         FILE *fi;
1710         struct baseio bio;
1711
1712         memset(&bio, 0, sizeof(bio));
1713         bio.iocp = BASEMAXINLINE;
1714
1715         if (!(fi = fopen(filename, "rb"))) {
1716                 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1717                 return -1;
1718         }
1719
1720         for (i= 0;i<9;i++) {
1721                 dtable[i]= 'A'+i;
1722                 dtable[i+9]= 'J'+i;
1723                 dtable[26+i]= 'a'+i;
1724                 dtable[26+i+9]= 'j'+i;
1725         }
1726         for (i= 0;i<8;i++) {
1727                 dtable[i+18]= 'S'+i;
1728                 dtable[26+i+18]= 's'+i;
1729         }
1730         for (i= 0;i<10;i++) {
1731                 dtable[52+i]= '0'+i;
1732         }
1733         dtable[62]= '+';
1734         dtable[63]= '/';
1735
1736         while (!hiteof){
1737                 unsigned char igroup[3],ogroup[4];
1738                 int c,n;
1739
1740                 igroup[0]= igroup[1]= igroup[2]= 0;
1741
1742                 for (n= 0;n<3;n++) {
1743                         if ((c = inchar(&bio, fi)) == EOF) {
1744                                 hiteof= 1;
1745                                 break;
1746                         }
1747
1748                         igroup[n]= (unsigned char)c;
1749                 }
1750
1751                 if (n> 0) {
1752                         ogroup[0]= dtable[igroup[0]>>2];
1753                         ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1754                         ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1755                         ogroup[3]= dtable[igroup[2]&0x3F];
1756
1757                         if (n<3) {
1758                                 ogroup[3]= '=';
1759
1760                                 if (n<2)
1761                                         ogroup[2]= '=';
1762                         }
1763
1764                         for (i= 0;i<4;i++)
1765                                 ochar(&bio, ogroup[i], so);
1766                 }
1767         }
1768
1769         if (fputs(eol,so)==EOF)
1770                 return 0;
1771
1772         fclose(fi);
1773
1774         return 1;
1775 }
1776
1777 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)
1778 {
1779         char callerid[256];
1780         /* Prepare variables for substition in email body and subject */
1781         pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1782         pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1783         snprintf(passdata, passdatasize, "%d", msgnum);
1784         pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1785         pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1786         pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1787         pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1788         pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1789         pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1790         pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1791         pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1792 }
1793
1794 /*
1795  * fill in *tm for current time according to the proper timezone, if any.
1796  * Return tm so it can be used as a function argument.
1797  */
1798 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1799 {
1800         const struct vm_zone *z = NULL;
1801         time_t t = time(NULL);
1802
1803         /* Does this user have a timezone specified? */
1804         if (!ast_strlen_zero(vmu->zonetag)) {
1805                 /* Find the zone in the list */
1806                 AST_LIST_LOCK(&zones);
1807                 AST_LIST_TRAVERSE(&zones, z, list) {
1808                         if (!strcmp(z->name, vmu->zonetag))
1809                                 break;
1810                 }
1811                 AST_LIST_UNLOCK(&zones);
1812         }
1813         ast_localtime(&t, tm, z ? z->timezone : NULL);
1814         return tm;
1815 }
1816
1817 /* same as mkstemp, but return a FILE * */
1818 static FILE *vm_mkftemp(char *template)
1819 {
1820         FILE *p = NULL;
1821         int pfd = mkstemp(template);
1822         if (pfd > -1) {
1823                 p = fdopen(pfd, "w");
1824                 if (!p) {
1825                         close(pfd);
1826                         pfd = -1;
1827                 }
1828         }
1829         return p;
1830 }
1831
1832 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)
1833 {
1834         FILE *p=NULL;
1835         char date[256];
1836         char host[MAXHOSTNAMELEN] = "";
1837         char who[256];
1838         char bound[256];
1839         char fname[256];
1840         char dur[256];
1841         char tmp[80] = "/tmp/astmail-XXXXXX";
1842         char tmp2[256];
1843         char tmpcmd[256];
1844         struct tm tm;
1845
1846         if (vmu && ast_strlen_zero(vmu->email)) {
1847                 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s].  E-mail will not be sent.\n", vmu->mailbox);
1848                 return(0);
1849         }
1850         if (!strcmp(format, "wav49"))
1851                 format = "WAV";
1852         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));
1853         /* Make a temporary file instead of piping directly to sendmail, in case the mail
1854            command hangs */
1855         if ((p = vm_mkftemp(tmp)) == NULL) {
1856                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1857                 return -1;
1858         } else {
1859                 gethostname(host, sizeof(host)-1);
1860                 if (strchr(srcemail, '@'))
1861                         ast_copy_string(who, srcemail, sizeof(who));
1862                 else {
1863                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1864                 }
1865                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1866                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1867                 fprintf(p, "Date: %s\n", date);
1868
1869                 /* Set date format for voicemail mail */
1870                 strftime(date, sizeof(date), emaildateformat, &tm);
1871
1872                 if (*fromstring) {
1873                         struct ast_channel *ast;
1874                         if ((ast = ast_channel_alloc(0))) {
1875                                 char *passdata;
1876                                 int vmlen = strlen(fromstring)*3 + 200;
1877                                 if ((passdata = alloca(vmlen))) {
1878                                         memset(passdata, 0, vmlen);
1879                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1880                                         pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1881                                         fprintf(p, "From: %s <%s>\n",passdata,who);
1882                                 } else
1883                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1884                                 ast_channel_free(ast);
1885                         } else
1886                                 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1887                 } else
1888                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
1889                 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1890
1891                 if (emailsubject) {
1892                         struct ast_channel *ast;
1893                         if ((ast = ast_channel_alloc(0))) {
1894                                 char *passdata;
1895                                 int vmlen = strlen(emailsubject)*3 + 200;
1896                                 if ((passdata = alloca(vmlen))) {
1897                                         memset(passdata, 0, vmlen);
1898                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1899                                         pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1900                                         fprintf(p, "Subject: %s\n", passdata);
1901                                 } else
1902                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1903                                 ast_channel_free(ast);
1904                         } else
1905                                 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1906                 } else
1907                 if (*emailtitle) {
1908                         fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1909                         fprintf(p,"\n") ;
1910                 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1911                         fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1912                 else
1913                         fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1914                 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1915 #ifdef IMAP_STORAGE
1916                 /* additional information needed for IMAP searching */
1917                 fprintf(p, "X-Asterisk-VM-Message-Num: %d\n", msgnum + 1);
1918                 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s\n", ext); */
1919                 fprintf(p, "X-Asterisk-VM-Server-Name: %s\n", fromstring);
1920                 fprintf(p, "X-Asterisk-VM-Context: %s\n", context);
1921                 fprintf(p, "X-Asterisk-VM-Extension: %s\n", chan->exten);
1922                 fprintf(p, "X-Asterisk-VM-Priority: %d\n", chan->priority);
1923                 fprintf(p, "X-Asterisk-VM-Caller-channel: %s\n", chan->name);
1924                 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s\n", cidnum);
1925                 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s\n", cidname);
1926                 fprintf(p, "X-Asterisk-VM-Duration: %d\n", duration);
1927                 if (category != NULL)
1928                         fprintf(p, "X-Asterisk-VM-Category: %s\n", category);
1929                 fprintf(p, "X-Asterisk-VM-Orig-date: %s\n", date);
1930                 fprintf(p, "X-Asterisk-VM-Orig-time: %ld\n", (long)time(NULL));
1931 #endif
1932                 if (!ast_strlen_zero(cidnum))
1933                         fprintf(p, "X-Asterisk-CallerID: %s\n", cidnum);
1934                 if (!ast_strlen_zero(cidname))
1935                         fprintf(p, "X-Asterisk-CallerIDName: %s\n", cidname);
1936                 fprintf(p, "MIME-Version: 1.0\n");
1937                 if (attach_user_voicemail) {
1938                         /* Something unique. */
1939                         snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1940
1941                         fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1942
1943                         fprintf(p, "--%s\n", bound);
1944                 }
1945                 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1946                 if (emailbody) {
1947                         struct ast_channel *ast;
1948                         if ((ast = ast_channel_alloc(0))) {
1949                                 char *passdata;
1950                                 int vmlen = strlen(emailbody)*3 + 200;
1951                                 if ((passdata = alloca(vmlen))) {
1952                                         memset(passdata, 0, vmlen);
1953                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1954                                         pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1955                                         fprintf(p, "%s\n", passdata);
1956                                 } else
1957                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1958                                 ast_channel_free(ast);
1959                         } else
1960                                 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1961                 } else {
1962                         fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1963
1964                         "in mailbox %s from %s, on %s so you might\n"
1965                         "want to check it when you get a chance.  Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname, 
1966                         dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1967                 }
1968                 if (attach_user_voicemail) {
1969                         /* Eww. We want formats to tell us their own MIME type */
1970                         char *ctype = (!strcasecmp(format, "ogg")) ?  "application/" : "audio/x-";
1971                         char tmpdir[256], newtmp[256];
1972                         int tmpfd;
1973                 
1974                         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
1975                         snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
1976                         tmpfd = mkstemp(newtmp);
1977                         ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
1978                         if (vmu->volgain < -.001 || vmu->volgain > .001) {
1979                                 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
1980                                 ast_safe_system(tmpcmd);
1981                                 attach = newtmp;
1982                                 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
1983                         }
1984                         fprintf(p, "--%s\n", bound);
1985                         fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
1986                         fprintf(p, "Content-Transfer-Encoding: base64\n");
1987                         fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1988                         fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
1989
1990                         snprintf(fname, sizeof(fname), "%s.%s", attach, format);
1991                         base_encode(fname, p);
1992 #ifdef IMAP_STORAGE
1993                         /* only attach if necessary */
1994                         ast_log(LOG_DEBUG, "Before second attachment with format set to:%s\n",format);
1995                         if (strcmp(format, "gsm")) {
1996                            fprintf(p, "--%s\n", bound);
1997                            fprintf(p, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"\n", msgnum, format);
1998                            fprintf(p, "Content-Transfer-Encoding: base64\n");
1999                            fprintf(p, "Content-Description: Voicemail sound attachment.\n");
2000                            fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"\n\n", msgnum);
2001
2002                            snprintf(fname, sizeof(fname), "%s.gsm", attach);
2003                            base_encode(fname, p);
2004                         }
2005 #endif
2006                         fprintf(p, "\n\n--%s--\n.\n", bound);
2007                         if (tmpfd > -1)
2008                                 close(tmpfd);
2009                         unlink(newtmp);
2010                 }
2011                 fclose(p);
2012                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2013                 ast_safe_system(tmp2);
2014                 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2015         }
2016         return 0;
2017 }
2018
2019 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)
2020 {
2021         char date[256];
2022         char host[MAXHOSTNAMELEN]="";
2023         char who[256];
2024         char dur[256];
2025         char tmp[80] = "/tmp/astmail-XXXXXX";
2026         char tmp2[256];
2027         struct tm tm;
2028         FILE *p;
2029
2030         if ((p = vm_mkftemp(tmp)) == NULL) {
2031                 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2032                 return -1;
2033         } else {
2034                 gethostname(host, sizeof(host)-1);
2035                 if (strchr(srcemail, '@'))
2036                         ast_copy_string(who, srcemail, sizeof(who));
2037                 else {
2038                         snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2039                 }
2040                 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2041                 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2042                 fprintf(p, "Date: %s\n", date);
2043
2044                 if (*pagerfromstring) {
2045                         struct ast_channel *ast;
2046                         if ((ast = ast_channel_alloc(0))) {
2047                                 char *passdata;
2048                                 int vmlen = strlen(fromstring)*3 + 200;
2049                                 if ((passdata = alloca(vmlen))) {
2050                                         memset(passdata, 0, vmlen);
2051                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2052                                         pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2053                                         fprintf(p, "From: %s <%s>\n", passdata, who);
2054                                 } else 
2055                                         ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2056                                 ast_channel_free(ast);
2057                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2058                 } else
2059                         fprintf(p, "From: Asterisk PBX <%s>\n", who);
2060                 fprintf(p, "To: %s\n", pager);
2061                 if (pagersubject) {
2062                         struct ast_channel *ast;
2063                         if ((ast = ast_channel_alloc(0))) {
2064                                 char *passdata;
2065                                 int vmlen = strlen(pagersubject) * 3 + 200;
2066                                 if ((passdata = alloca(vmlen))) {
2067                                         memset(passdata, 0, vmlen);
2068                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2069                                         pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2070                                         fprintf(p, "Subject: %s\n\n", passdata);
2071                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2072                                 ast_channel_free(ast);
2073                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2074                 } else
2075                         fprintf(p, "Subject: New VM\n\n");
2076                 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2077                 if (pagerbody) {
2078                         struct ast_channel *ast;
2079                         if ((ast = ast_channel_alloc(0))) {
2080                                 char *passdata;
2081                                 int vmlen = strlen(pagerbody)*3 + 200;
2082                                 if ((passdata = alloca(vmlen))) {
2083                                         memset(passdata, 0, vmlen);
2084                                         prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2085                                         pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2086                                         fprintf(p, "%s\n", passdata);
2087                                 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2088                         ast_channel_free(ast);
2089                         } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2090                 } else {
2091                         fprintf(p, "New %s long msg in box %s\n"
2092                                         "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2093                 }
2094                 fclose(p);
2095                 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2096                 ast_safe_system(tmp2);
2097                 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2098         }
2099         return 0;
2100 }
2101
2102 #ifndef IMAP_STORAGE
2103 static int get_date(char *s, int len)
2104 {
2105         struct tm tm;
2106         time_t t;
2107         t = time(0);
2108         localtime_r(&t,&tm);
2109         return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2110 }
2111 #endif
2112
2113 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2114 {
2115         int res;
2116         char fn[256];
2117         snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2118         RETRIEVE(fn, -1);
2119         if (ast_fileexists(fn, NULL, NULL) > 0) {
2120                 res = ast_stream_and_wait(chan, fn, chan->language, ecodes);
2121                 if (res) {
2122                         DISPOSE(fn, -1);
2123                         return res;
2124                 }
2125         } else {
2126                 /* Dispose just in case */
2127                 DISPOSE(fn, -1);
2128                 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
2129                 if (res)
2130                         return res;
2131                 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2132                 if (res)
2133                         return res;
2134         }
2135         res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
2136         return res;
2137 }
2138
2139 static void free_user(struct ast_vm_user *vmu)
2140 {
2141         if (ast_test_flag(vmu, VM_ALLOCED))
2142                 free(vmu);
2143 }
2144
2145 static void free_zone(struct vm_zone *z)
2146 {
2147         free(z);
2148 }
2149
2150 static const char *mbox(int id)
2151 {
2152         static const char *msgs[] = {
2153                 "INBOX",
2154                 "Old",
2155                 "Work",   
2156                 "Family",
2157                 "Friends",
2158                 "Cust1",
2159                 "Cust2",
2160                 "Cust3",
2161                 "Cust4",  
2162                 "Cust5",
2163         };
2164         return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2165 }
2166
2167 #ifdef ODBC_STORAGE
2168 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2169 {
2170         int x = -1;
2171         int res;
2172         SQLHSTMT stmt;
2173         char sql[256];
2174         char rowdata[20];
2175         char tmp[256]="";
2176         struct odbc_obj *obj;
2177         char *context;
2178
2179         if (newmsgs)
2180                 *newmsgs = 0;
2181         if (oldmsgs)
2182                 *oldmsgs = 0;
2183
2184         /* If no mailbox, return immediately */
2185         if (ast_strlen_zero(mailbox))
2186                 return 0;
2187
2188         ast_copy_string(tmp, mailbox, sizeof(tmp));
2189         
2190         context = strchr(tmp, '@');
2191         if (context) {   
2192                 *context = '\0';
2193                 context++;
2194         } else  
2195                 context = "default";
2196         
2197         obj = odbc_request_obj(odbc_database, 0);
2198         if (obj) {
2199                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2200                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2201                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2202                         odbc_release_obj(obj);
2203                         goto yuck;
2204                 }
2205                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2206                 res = SQLPrepare(stmt, sql, SQL_NTS);
2207                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2208                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2209                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2210                         odbc_release_obj(obj);
2211                         goto yuck;
2212                 }
2213                 res = odbc_smart_execute(obj, stmt);
2214                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2215                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2216                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2217                         odbc_release_obj(obj);
2218                         goto yuck;
2219                 }
2220                 res = SQLFetch(stmt);
2221                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2222                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2223                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2224                         odbc_release_obj(obj);
2225                         goto yuck;
2226                 }
2227                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2228                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2229                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2230                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2231                         odbc_release_obj(obj);
2232                         goto yuck;
2233                 }
2234                 *newmsgs = atoi(rowdata);
2235                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2236
2237                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2238                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2239                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2240                         odbc_release_obj(obj);
2241                         goto yuck;
2242                 }
2243                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2244                 res = SQLPrepare(stmt, sql, SQL_NTS);
2245                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2246                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2247                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2248                         odbc_release_obj(obj);
2249                         goto yuck;
2250                 }
2251                 res = odbc_smart_execute(obj, stmt);
2252                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2253                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2254                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2255                         odbc_release_obj(obj);
2256                         goto yuck;
2257                 }
2258                 res = SQLFetch(stmt);
2259                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2260                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2261                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2262                         odbc_release_obj(obj);
2263                         goto yuck;
2264                 }
2265                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2266                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2267                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2268                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2269                         odbc_release_obj(obj);
2270                         goto yuck;
2271                 }
2272                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2273                 odbc_release_obj(obj);
2274                 *oldmsgs = atoi(rowdata);
2275                 x = 0;
2276         } else
2277                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2278                 
2279 yuck:   
2280         return x;
2281 }
2282
2283 static int messagecount(const char *context, const char *mailbox, const char *folder)
2284 {
2285         struct odbc_obj *obj = NULL;
2286         int nummsgs = 0;
2287         int res;
2288         SQLHSTMT stmt = NULL;
2289         char sql[256];
2290         char rowdata[20];
2291         if (!folder)
2292                 folder = "INBOX";
2293         /* If no mailbox, return immediately */
2294         if (ast_strlen_zero(mailbox))
2295                 return 0;
2296
2297         obj = odbc_request_obj(odbc_database, 0);
2298         if (obj) {
2299                 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2300                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2301                         ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2302                         goto yuck;
2303                 }
2304                 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2305                 res = SQLPrepare(stmt, sql, SQL_NTS);
2306                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {  
2307                         ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2308                         SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2309                         goto yuck;
2310                 }
2311                 res = odbc_smart_execute(obj, stmt);
2312                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2313                         ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2314                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2315                         goto yuck;
2316                 }
2317                 res = SQLFetch(stmt);
2318                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2319                         ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2320                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2321                         goto yuck;
2322                 }
2323                 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2324                 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2325                         ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2326                         SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2327                         goto yuck;
2328                 }
2329                 nummsgs = atoi(rowdata);
2330                 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2331         } else
2332                 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2333
2334 yuck:
2335         if (obj)
2336                 odbc_release_obj(obj);
2337         return nummsgs;
2338 }
2339
2340 static int has_voicemail(const char *mailbox, const char *folder)
2341 {
2342         char *context, tmp[256];
2343         ast_copy_string(tmp, mailbox, sizeof(tmp));
2344         if ((context = strchr(tmp, '@')))
2345                 *context++ = '\0';
2346         else
2347                 context = "default";
2348
2349         if (messagecount(context, tmp, folder))
2350                 return 1;
2351         else
2352                 return 0;
2353 }
2354
2355 #else
2356 #ifdef IMAP_STORAGE
2357 static int count_messages_imap(const char *mailbox, int *newmsgs, int *oldmsgs)
2358 {
2359         SEARCHPGM *pgm;
2360         SEARCHHEADER *hdr;
2361  
2362         struct ast_vm_user *vmu;
2363         struct vm_state *vms_p;
2364         char tmp[256]="";
2365         char *mb, *cur;
2366         char *mailboxnc; 
2367         char *context;
2368         int ret = 0;
2369         if (newmsgs)
2370                 *newmsgs = 0;
2371         if (oldmsgs)
2372                 *oldmsgs = 0;
2373  
2374         ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2375         /* If no mailbox, return immediately */
2376         if (ast_strlen_zero(mailbox))
2377                 return 0;
2378         if (strchr(mailbox, ',')) {
2379                 int tmpnew, tmpold;
2380                 strncpy(tmp, mailbox, sizeof(tmp) - 1);
2381                 mb = tmp;
2382                 ret = 0;
2383                 while((cur = strsep(&mb, ", "))) {
2384                         if (!ast_strlen_zero(cur)) {
2385                                 if (count_messages_imap(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2386                                         return -1;
2387                                 else {
2388                                         if (newmsgs)
2389                                                 *newmsgs += tmpnew; 
2390                                         if (oldmsgs)
2391                                                 *oldmsgs += tmpold;
2392                                 }
2393                         }
2394                 }
2395                 return 0;
2396         }
2397  
2398         strncpy(tmp, mailbox, sizeof(tmp) - 1);
2399         context = strchr(tmp, '@');
2400         if (context) {
2401                 *context = '\0';
2402                 mailboxnc = tmp;
2403                 context++;
2404         } else {
2405                 context = "default";
2406                 mailboxnc = (char *)mailbox;
2407         }
2408  
2409         /* We have to get the user before we can open the stream! */
2410         /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2411         vmu = find_user(NULL, context, mailboxnc);
2412         if (vmu == NULL) {
2413                 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailboxnc,context);
2414                 return -1;
2415         } else {
2416                 /* No IMAP account available */
2417                 if (vmu->imapuser[0] == '\0') {
2418                         ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2419                         return -1;
2420                 }
2421         }
2422  
2423         /* check if someone is accessing this box right now... */
2424         vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2425         if (vms_p == NULL) {
2426                 vms_p = get_vm_state_by_mailbox(mailboxnc,1);
2427         }
2428         if (vms_p != NULL) {
2429                 ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2430                 *newmsgs = vms_p->newmessages;
2431                 *oldmsgs = vms_p->oldmessages;
2432                 return 0;
2433         }
2434  
2435         /* add one if not there... */
2436         vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2437         if (vms_p == NULL) {
2438                 vms_p = get_vm_state_by_mailbox(mailboxnc,0);
2439         }
2440  
2441         if (vms_p == NULL) {
2442                 ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2443                 vms_p = (struct vm_state *)malloc(sizeof(struct vm_state));
2444                 strcpy(vms_p->imapuser,vmu->imapuser);
2445                 strcpy(vms_p->username,mailboxnc); /* save for access from interactive entry point */
2446                 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2447                 ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2448                 vms_p->updated = 1;
2449                 vms_p->interactive = 0;
2450                 /* set mailbox to INBOX! */
2451                 strncpy(vms_p->curbox, mbox(0), sizeof(vms_p->curbox) - 1);
2452                 init_vm_state(vms_p);
2453                 vmstate_insert(vms_p);
2454         }
2455         if (vms_p->mailstream == NULL)
2456                 ret = init_mailstream(vms_p);
2457         if (vms_p->mailstream == NULL) {
2458                 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2459                 return -1;
2460         }
2461         if (newmsgs && ret==0 && vms_p->updated==1 ) {
2462                 pgm = mail_newsearchpgm ();
2463                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2464                 pgm->header = hdr;
2465                 pgm->unseen = 1;
2466                 pgm->seen = 0;
2467                 pgm->undeleted = 1;
2468                 pgm->deleted = 0;
2469  
2470                 vms_p->vmArrayIndex = 0;
2471         
2472                 ast_log (LOG_DEBUG,"Before search of mailbox %s for NEW messages\n",vms_p->mailstream->mailbox);
2473                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2474                 ast_log (LOG_DEBUG,"After search\n");
2475                 *newmsgs = vms_p->vmArrayIndex;
2476                 vms_p->newmessages = vms_p->vmArrayIndex;
2477                 ast_log (LOG_DEBUG,"There are %d NEW messages\n",vms_p->newmessages);
2478         }
2479         if (oldmsgs && ret==0 && vms_p->updated==1 ) {
2480                 pgm = mail_newsearchpgm ();
2481                 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2482                 pgm->header = hdr;
2483                 pgm->unseen = 0;
2484                 pgm->seen = 1;
2485                 pgm->deleted = 0;
2486                 pgm->undeleted = 1;
2487  
2488                 vms_p->vmArrayIndex = 0;
2489         
2490                 ast_log (LOG_DEBUG,"Before search of mailbox %s for OLD messages\n",vms_p->mailstream->mailbox);
2491                 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2492                 *oldmsgs = vms_p->vmArrayIndex;
2493                 vms_p->oldmessages = vms_p->vmArrayIndex;
2494                 ast_log (LOG_DEBUG,"There are %d OLD messages\n",vms_p->oldmessages);
2495         }
2496         if (vms_p->updated == 1) {  /* changes, so we did the searches above */
2497                 vms_p->updated = 0;
2498         } else if (vms_p->updated > 1) {  /* decrement delay count */
2499                 vms_p->updated--;
2500         } else {  /* no changes, so don't search */
2501                 ast_log (LOG_DEBUG,"Calling mail_ping on mailbox %s\n",vms_p->mailstream->mailbox);
2502                 mail_ping(vms_p->mailstream);
2503                 /* Keep the old data */
2504                 *newmsgs = vms_p->newmessages;
2505                 *oldmsgs = vms_p->oldmessages;
2506         }
2507         return 0;
2508  }
2509 #endif
2510 #endif
2511
2512 /* copy message only used by file storage */
2513 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)
2514 {
2515         char fromdir[256], todir[256], frompath[256], topath[256];
2516         const char *frombox = mbox(imbox);
2517         int recipmsgnum;
2518
2519         ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2520
2521         create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2522   
2523         make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2524         make_file(frompath, sizeof(frompath), fromdir, msgnum);
2525
2526         if (vm_lock_path(todir))
2527                 return ERROR_LOCK_PATH;
2528
2529         recipmsgnum = 0;
2530         do {
2531                 make_file(topath, sizeof(topath), todir, recipmsgnum);
2532                 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
2533                         break;
2534                 recipmsgnum++;
2535         } while (recipmsgnum < recip->maxmsg);
2536         if (recipmsgnum < recip->maxmsg) {
2537                 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2538         } else {
2539                 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2540         }
2541         ast_unlock_path(todir);
2542         notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2543         
2544         return 0;
2545 }
2546
2547 #ifndef ODBC_STORAGE
2548
2549 static int messagecount(const char *context, const char *mailbox, const char *folder)
2550 {
2551         return __has_voicemail(context, mailbox, folder, 0);
2552 }
2553
2554
2555 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2556 {
2557         DIR *dir;
2558         struct dirent *de;
2559         char fn[256];
2560         int ret = 0;
2561         if (!folder)
2562                 folder = "INBOX";
2563         /* If no mailbox, return immediately */
2564         if (ast_strlen_zero(mailbox))
2565                 return 0;
2566         if (!context)
2567                 context = "default";
2568         snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2569         dir = opendir(fn);
2570         if (!dir)
2571                 return 0;
2572         while ((de = readdir(dir))) {
2573                 if (!strncasecmp(de->d_name, "msg", 3)) {
2574                         if (shortcircuit) {
2575                                 ret = 1;
2576                                 break;
2577                         } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2578                                 ret++;
2579                 }
2580         }
2581         closedir(dir);
2582         return ret;
2583 }
2584
2585
2586 static int has_voicemail(const char *mailbox, const char *folder)
2587 {
2588         char tmp[256], *tmp2 = tmp, *mbox, *context;
2589         ast_copy_string(tmp, mailbox, sizeof(tmp));
2590         while ((mbox = strsep(&tmp2, ","))) {
2591                 if ((context = strchr(mbox, '@')))
2592                         *context++ = '\0';
2593                 else
2594                         context = "default";
2595                 if (__has_voicemail(context, mbox, folder, 1))
2596                         return 1;
2597         }
2598         return 0;
2599 }
2600
2601
2602 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2603 {
2604         char tmp[256];
2605         char *context;
2606
2607         if (newmsgs)
2608                 *newmsgs = 0;
2609         if (oldmsgs)
2610                 *oldmsgs = 0;
2611         /* If no mailbox, return immediately */
2612         if (ast_strlen_zero(mailbox))
2613                 return 0;
2614         if (strchr(mailbox, ',')) {
2615                 int tmpnew, tmpold;
2616                 char *mb, *cur;
2617
2618                 ast_copy_string(tmp, mailbox, sizeof(tmp));
2619                 mb = tmp;
2620                 while ((cur = strsep(&mb, ", "))) {
2621                         if (!ast_strlen_zero(cur)) {
2622                                 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2623                                         return -1;
2624                                 else {
2625                                         if (newmsgs)
2626                                                 *newmsgs += tmpnew; 
2627                                         if (oldmsgs)
2628                                                 *oldmsgs += tmpold;
2629                                 }
2630                         }
2631                 }
2632                 return 0;
2633         }
2634         ast_copy_string(tmp, mailbox, sizeof(tmp));
2635         context = strchr(tmp, '@');
2636         if (context) {
2637                 *context = '\0';
2638                 context++;
2639         } else
2640                 context = "default";
2641         if (newmsgs)
2642                 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2643         if (oldmsgs)
2644                 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2645         return 0;
2646 }
2647
2648 #endif
2649
2650 static void run_externnotify(char *context, char *extension)
2651 {
2652         char arguments[255];
2653         char ext_context[256] = "";
2654         int newvoicemails = 0, oldvoicemails = 0;
2655         struct ast_smdi_mwi_message *mwi_msg;
2656
2657         if (!ast_strlen_zero(context))
2658                 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2659         else
2660                 ast_copy_string(ext_context, extension, sizeof(ext_context));
2661
2662         if (!strcasecmp(externnotify, "smdi")) {
2663                 if (ast_app_has_voicemail(ext_context, NULL)) 
2664                         ast_smdi_mwi_set(smdi_iface, extension);
2665                 else
2666                         ast_smdi_mwi_unset(smdi_iface, extension);
2667
2668                 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2669                         ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2670                         if (!strncmp(mwi_msg->cause, "INV", 3))
2671                                 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2672                         else if (!strncmp(mwi_msg->cause, "BLK", 3))
2673                                 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2674                         ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2675                         ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2676                 } else {
2677                         ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2678                 }
2679         } else if (!ast_strlen_zero(externnotify)) {
2680                 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2681                         ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2682                 } else {
2683                         snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2684                         ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2685                         ast_safe_system(arguments);
2686                 }
2687         }
2688 }
2689
2690 struct leave_vm_options {
2691         unsigned int flags;
2692         signed char record_gain;
2693 };
2694
2695 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2696 {
2697 #ifdef IMAP_STORAGE
2698         int newmsgs, oldmsgs;
2699         struct vm_state *vms;
2700 #else
2701         char tmptxtfile[256], txtfile[256];
2702         char callerid[256];
2703         FILE *txt;
2704         char date[256];
2705         int  txtdes;
2706 #endif
2707         int res = 0;
2708         int msgnum;
2709         int duration = 0;
2710         int ausemacro = 0;
2711         int ousemacro = 0;
2712         int ouseexten = 0;
2713         char dir[256], tmpdir[260];
2714         char fn[256];
2715         char prefile[256]="";
2716         char tempfile[256]="";
2717         char ext_context[256] = "";
2718         char fmt[80];
2719         char *context;
2720         char ecodes[16] = "#";
2721         char tmp[256] = "", *tmpptr;
2722         struct ast_vm_user *vmu;
2723         struct ast_vm_user svm;
2724         const char *category = NULL;
2725
2726         ast_copy_string(tmp, ext, sizeof(tmp));
2727         ext = tmp;
2728         context = strchr(tmp, '@');
2729         if (context) {
2730                 *context++ = '\0';
2731                 tmpptr = strchr(context, '&');
2732         } else {
2733                 tmpptr = strchr(ext, '&');
2734         }
2735
2736         if (tmpptr)
2737                 *tmpptr++ = '\0';
2738
2739         category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2740
2741         ast_log(LOG_DEBUG, "Before find_user\n");
2742         if (!(vmu = find_user(&svm, context, ext))) {
2743                 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2744                 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2745                         ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2746                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2747                 return res;
2748         }
2749
2750         /* Setup pre-file if appropriate */
2751         if (strcmp(vmu->context, "default"))
2752                 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2753         else
2754                 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2755         if (ast_test_flag(options, OPT_BUSY_GREETING))
2756                 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2757         else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2758                 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2759         snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2760         RETRIEVE(tempfile, -1);
2761         if (ast_fileexists(tempfile, NULL, NULL) > 0)
2762                 ast_copy_string(prefile, tempfile, sizeof(prefile));
2763         DISPOSE(tempfile, -1);
2764         /* It's easier just to try to make it than to check for its existence */
2765         create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2766         create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2767
2768         /* Check current or macro-calling context for special extensions */
2769         if (ast_test_flag(vmu, VM_OPERATOR)) {
2770                 if (!ast_strlen_zero(vmu->exit)) {
2771                         if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2772                                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2773                                 ouseexten = 1;
2774                         }
2775                 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2776                         strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2777                         ouseexten = 1;
2778                 }
2779                 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2780                 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2781                 ousemacro = 1;
2782                 }
2783         }
2784
2785         if (!ast_strlen_zero(vmu->exit)) {
2786                 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2787                         strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2788         } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2789                 strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2790         else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2791                 strncat(ecodes, "*", sizeof(ecodes) -  strlen(ecodes) - 1);
2792                 ausemacro = 1;
2793         }
2794
2795         /* Play the beginning intro if desired */
2796         if (!ast_strlen_zero(prefile)) {
2797                 RETRIEVE(prefile, -1);
2798                 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2799                         if (ast_streamfile(chan, prefile, chan->language) > -1) 
2800                                 res = ast_waitstream(chan, ecodes);
2801                 } else {
2802                         ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2803                         res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2804                 }
2805                 DISPOSE(prefile, -1);
2806                 if (res < 0) {
2807                         ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2808                         free_user(vmu);
2809                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2810                         return -1;
2811                 }
2812         }
2813         if (res == '#') {
2814                 /* On a '#' we skip the instructions */
2815                 ast_set_flag(options, OPT_SILENT);
2816                 res = 0;
2817         }
2818         if (!res && !ast_test_flag(options, OPT_SILENT)) {
2819                 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2820                 if (res == '#') {
2821                         ast_set_flag(options, OPT_SILENT);
2822                         res = 0;
2823                 }
2824         }
2825         if (res > 0)
2826                 ast_stopstream(chan);
2827         /* Check for a '*' here in case the caller wants to escape from voicemail to something
2828            other than the operator -- an automated attendant or mailbox login for example */
2829         if (res == '*') {
2830                 chan->exten[0] = 'a';
2831                 chan->exten[1] = '\0';
2832                 if (!ast_strlen_zero(vmu->exit)) {
2833                         ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2834                 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2835                         ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2836                 }
2837                 chan->priority = 0;
2838                 free_user(vmu);
2839                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2840                 return 0;
2841         }
2842
2843         /* Check for a '0' here */
2844         if (res == '0') {
2845         transfer:
2846                 if (ouseexten || ousemacro) {
2847                         chan->exten[0] = 'o';
2848                         chan->exten[1] = '\0';
2849                         if (!ast_strlen_zero(vmu->exit)) {
2850                                 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2851                         } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2852                                 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2853                         }
2854                         ast_play_and_wait(chan, "transfer");
2855                         chan->priority = 0;
2856                         free_user(vmu);
2857                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2858                 }
2859                 return 0;
2860         }
2861         if (res < 0) {
2862                 free_user(vmu);
2863                 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2864                 return -1;
2865         }
2866         /* The meat of recording the message...  All the announcements and beeps have been played*/
2867         ast_copy_string(fmt, vmfmts, sizeof(fmt));
2868         if (!ast_strlen_zero(fmt)) {
2869                 msgnum = 0;
2870
2871 #ifdef IMAP_STORAGE
2872                 /* Is ext a mailbox? */
2873                 /* must open stream for this user to get info! */
2874                 vms = get_vm_state_by_mailbox(ext,0);
2875                 if (vms != NULL) {
2876                         ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
2877                         newmsgs = vms->newmessages++;
2878                         oldmsgs = vms->oldmessages;
2879                 } else {
2880                         ast_log(LOG_DEBUG, "About to call messagecount.\n");
2881                         res = count_messages_imap(ext, &newmsgs, &oldmsgs);
2882                         if(res < 0) {
2883                                 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
2884                                 return -1;
2885                         }
2886                 }
2887                 /* here is a big difference! We add one to it later */
2888                 msgnum = newmsgs + oldmsgs;
2889                 ast_log(LOG_NOTICE, "Messagecount set to %d\n",msgnum);
2890                 sprintf(fn, "%s/msg%s%04d", TMP, vmu->mailbox, msgnum);
2891                 /* set variable for compatability */
2892                 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
2893
2894                 /* Check if mailbox is full */
2895                 if (vms->quota_usage >= vms->quota_limit) {
2896                         ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
2897                         ast_play_and_wait(chan, "vm-mailboxfull");
2898                         return -1;
2899                 }
2900
2901                 /* play beep */
2902                 res = ast_streamfile(chan, "beep", chan->language);
2903                 if (!res)
2904                         res = ast_waitstream(chan, "");
2905                 /* play_record_review does recording and verify */
2906                 ast_log(LOG_DEBUG, "About to record message in file %s\n",fn);
2907                 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2908                 if (res == '0') {
2909                         goto transfer;
2910                 }
2911                 if (res > 0) res = 0;
2912
2913                 if (duration < vmminmessage) {
2914                         if (option_verbose > 2) 
2915                                 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2916                         goto leave_vm_out;
2917                 }
2918                 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2919 #else
2920                 if (count_messages(vmu, dir) >= vmu->maxmsg) {
2921                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2922                         if (!res)
2923                                 res = ast_waitstream(chan, "");
2924                         ast_log(LOG_WARNING, "No more messages possible\n");
2925                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2926                         goto leave_vm_out;
2927                 }
2928
2929                 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
2930                 txtdes = mkstemp(tmptxtfile);
2931                 if (txtdes < 0) {
2932                         res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2933                         if (!res)
2934                                 res = ast_waitstream(chan, "");
2935                         ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
2936                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2937                         goto leave_vm_out;
2938                 }
2939
2940                 /* Now play the beep once we have the message number for our next message. */
2941                 if (res >= 0) {
2942                         /* Unless we're *really* silent, try to send the beep */
2943                         res = ast_stream_and_wait(chan, "beep", chan->language, "");
2944                 }
2945                                 
2946                 /* Store information */
2947                 txt = fdopen(txtdes, "w+");
2948                 if (txt) {
2949                         get_date(date, sizeof(date));
2950                         fprintf(txt, 
2951                                 ";\n"
2952                                 "; Message Information file\n"
2953                                 ";\n"
2954                                 "[message]\n"
2955                                 "origmailbox=%s\n"
2956                                 "context=%s\n"
2957                                 "macrocontext=%s\n"
2958                                 "exten=%s\n"
2959                                 "priority=%d\n"
2960                                 "callerchan=%s\n"
2961                                 "callerid=%s\n"
2962                                 "origdate=%s\n"
2963                                 "origtime=%ld\n"
2964                                 "category=%s\n",
2965                                 ext,
2966                                 chan->context,
2967                                 chan->macrocontext, 
2968                                 chan->exten,
2969                                 chan->priority,
2970                                 chan->name,
2971                                 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2972                                 date, (long)time(NULL),
2973                                 category ? category : ""); 
2974                 } else
2975                         ast_log(LOG_WARNING, "Error opening text file for output\n");
2976                 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain);
2977                                 
2978                 if (txt) {
2979                         if (duration < vmminmessage) {
2980                                 if (option_verbose > 2) 
2981                                         ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2982                                 ast_filedelete(tmptxtfile, NULL);
2983                                 unlink(tmptxtfile);
2984                         } else {
2985                                 fprintf(txt, "duration=%d\n", duration);
2986                                 fclose(txt);
2987                                 if (vm_lock_path(dir)) {
2988                                         ast_log(LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
2989                                         /* Delete files */
2990                                         ast_filedelete(tmptxtfile, NULL);
2991                                         unlink(tmptxtfile);
2992                                 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
2993                                         if (option_debug) 
2994                                                 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
2995                                         unlink(tmptxtfile);
2996                                         ast_unlock_path(dir);
2997                                 } else {
2998                                         for (;;) {
2999                                                 make_file(fn, sizeof(fn), dir, msgnum);
3000                                                 if (!EXISTS(dir, msgnum, fn, NULL))
3001                                                         break;
3002                                                 msgnum++;
3003                                         }
3004
3005                                         /* assign a variable with the name of the voicemail file */ 
3006                                         pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3007
3008                                         snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3009                                         ast_filerename(tmptxtfile, fn, NULL);
3010                                         rename(tmptxtfile, txtfile);
3011
3012                                         ast_unlock_path(dir);
3013                                         /* Are there to be more recipients of this message? */
3014                                         while (tmpptr) {
3015                                                 struct ast_vm_user recipu, *recip;
3016                                                 char *exten, *context;
3017                                         
3018                                                 exten = strsep(&tmpptr, "&");
3019                                                 context = strchr(exten, '@');
3020                                                 if (context) {
3021                                                         *context = '\0';
3022                                                         context++;
3023                                                 }
3024                                                 if ((recip = find_user(&recipu, context, exten))) {
3025                                                         copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
3026                                                         free_user(recip);
3027                                                 }
3028                                         }
3029                                         if (ast_fileexists(fn, NULL, NULL)) {
3030                                                 STORE(dir, vmu->mailbox, vmu->context, msgnum);
3031                                                 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
3032                                                 DISPOSE(dir, msgnum);
3033                                         }
3034                                 }
3035                         }
3036                 }
3037 #endif
3038                 if (res == '0') {
3039                         goto transfer;
3040                 } else if (res > 0)
3041                         res = 0;
3042
3043                 if (duration < vmminmessage)
3044                         /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3045                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3046                 else
3047                         pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3048         } else
3049                 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3050 leave_vm_out:
3051         free_user(vmu);
3052         
3053         return res;
3054 }
3055
3056 #ifndef IMAP_STORAGE
3057 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3058 {
3059         /* we know max messages, so stop process when number is hit */
3060
3061         int x,dest;
3062         char sfn[256];
3063         char dfn[256];
3064
3065         if (vm_lock_path(dir))
3066                 return ERROR_LOCK_PATH;
3067
3068         for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3069                 make_file(sfn, sizeof(sfn), dir, x);
3070                 if (EXISTS(dir, x, sfn, NULL)) {
3071                         
3072                         if (x != dest) {
3073                                 make_file(dfn, sizeof(dfn), dir, dest);
3074                                 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3075                         }
3076                         
3077                         dest++;
3078                 }
3079         }
3080         ast_unlock_path(dir);
3081
3082         return 0;
3083 }
3084 #endif
3085
3086 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3087 {
3088         int d;
3089         d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3090         return d;
3091 }
3092
3093 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3094 {
3095 #ifdef IMAP_STORAGE
3096         /* we must use mbox(x) folder names, and copy the message there */
3097         /* simple. huh? */
3098         char *dbox = mbox(box);
3099         long res;
3100         char sequence[10];
3101
3102         /* if save to Old folder, just leave in INBOX */
3103         if (box == 1) return 10;
3104         /* get the real IMAP message number for this message */
3105         sprintf(sequence,"%ld",vms->msgArray[msg]);
3106         ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3107         res = mail_copy(vms->mailstream,sequence,dbox);
3108         if (res == 1) return 0;
3109         return 1;
3110 #else
3111         char *dir = vms->curdir;
3112         char *username = vms->username;
3113         char *context = vmu->context;
3114         char sfn[256];
3115         char dfn[256];
3116         char ddir[256];
3117         const char *dbox = mbox(box);
3118         int x;
3119         make_file(sfn, sizeof(sfn), dir, msg);
3120         create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3121
3122         if (vm_lock_path(ddir))
3123                 return ERROR_LOCK_PATH;
3124
3125         for (x = 0; x < vmu->maxmsg; x++) {
3126                 make_file(dfn, sizeof(dfn), ddir, x);
3127                 if (!EXISTS(ddir, x, dfn, NULL))
3128                         break;
3129         }
3130         if (x >= vmu->maxmsg) {
3131                 ast_unlock_path(ddir);
3132                 return -1;
3133         }
3134         if (strcmp(sfn, dfn)) {
3135                 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3136         }
3137         ast_unlock_path(ddir);
3138 #endif
3139         return 0;
3140 }
3141
3142 static int adsi_logo(unsigned char *buf)
3143 {
3144         int bytes = 0;
3145         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3146         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
3147         return bytes;
3148 }
3149
3150 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3151 {
3152         unsigned char buf[256];
3153         int bytes=0;
3154         int x;
3155         char num[5];
3156
3157         *useadsi = 0;
3158         bytes += adsi_data_mode(buf + bytes);
3159         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3160
3161         bytes = 0;
3162         bytes += adsi_logo(buf);
3163         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3164 #ifdef DISPLAY
3165         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
3166 #endif
3167         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3168         bytes += adsi_data_mode(buf + bytes);
3169         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3170
3171         if (adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3172                 bytes = 0;
3173                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3174                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3175                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3176                 bytes += adsi_voice_mode(buf + bytes, 0);
3177                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3178                 return 0;
3179         }
3180
3181 #ifdef DISPLAY
3182         /* Add a dot */
3183         bytes = 0;
3184         bytes += adsi_logo(buf);
3185         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3186         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ..", "");
3187         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3188         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3189 #endif
3190         bytes = 0;
3191         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
3192         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
3193         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
3194         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
3195         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
3196         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
3197         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3198
3199 #ifdef DISPLAY
3200         /* Add another dot */
3201         bytes = 0;
3202         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
3203         bytes += adsi_voice_mode(buf + bytes, 0);
3204
3205         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3206         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3207 #endif
3208
3209         bytes = 0;
3210         /* These buttons we load but don't use yet */
3211         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
3212         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
3213         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
3214         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
3215         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
3216         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
3217         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3218
3219 #ifdef DISPLAY
3220         /* Add another dot */
3221         bytes = 0;
3222         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ....", "");
3223         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3224         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3225 #endif
3226
3227         bytes = 0;
3228         for (x=0;x<5;x++) {
3229                 snprintf(num, sizeof(num), "%d", x);
3230                 bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
3231         }
3232         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
3233         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3234
3235 #ifdef DISPLAY
3236         /* Add another dot */
3237         bytes = 0;
3238         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .....", "");
3239         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3240         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3241 #endif
3242
3243         if (adsi_end_download(chan)) {
3244                 bytes = 0;
3245                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
3246                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3247                 bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3248                 bytes += adsi_voice_mode(buf + bytes, 0);
3249                 adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3250                 return 0;
3251         }
3252         bytes = 0;
3253         bytes += adsi_download_disconnect(buf + bytes);
3254         bytes += adsi_voice_mode(buf + bytes, 0);
3255         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3256
3257         ast_log(LOG_DEBUG, "Done downloading scripts...\n");
3258
3259 #ifdef DISPLAY
3260         /* Add last dot */
3261         bytes = 0;
3262         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "   ......", "");
3263         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3264 #endif
3265         ast_log(LOG_DEBUG, "Restarting session...\n");
3266
3267         bytes = 0;
3268         /* Load the session now */
3269         if (adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
3270                 *useadsi = 1;
3271                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
3272         } else
3273                 bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
3274
3275         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3276         return 0;
3277 }
3278
3279 static void adsi_begin(struct ast_channel *chan, int *useadsi)
3280 {
3281         int x;
3282         if (!adsi_available(chan))
3283                 return;
3284         x = adsi_load_session(chan, adsifdn, adsiver, 1);
3285         if (x < 0)
3286                 return;
3287         if (!x) {
3288                 if (adsi_load_vmail(chan, useadsi)) {
3289                         ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
3290                         return;
3291                 }
3292         } else
3293                 *useadsi = 1;
3294 }
3295
3296 static void adsi_login(struct ast_channel *chan)
3297 {
3298         unsigned char buf[256];
3299         int bytes=0;
3300         unsigned char keys[8];
3301         int x;
3302         if (!adsi_available(chan))
3303                 return;
3304
3305         for (x=0;x<8;x++)
3306                 keys[x] = 0;
3307         /* Set one key for next */
3308         keys[3] = ADSI_KEY_APPS + 3;
3309
3310         bytes += adsi_logo(buf + bytes);
3311         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
3312         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
3313         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3314         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
3315         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
3316         bytes += adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
3317         bytes += adsi_set_keys(buf + bytes, keys);
3318         bytes += adsi_voice_mode(buf + bytes, 0);
3319         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3320 }
3321
3322 static void adsi_password(struct ast_channel *chan)
3323 {
3324         unsigned char buf[256];
3325         int bytes=0;
3326         unsigned char keys[8];
3327         int x;
3328         if (!adsi_available(chan))
3329                 return;
3330
3331         for (x=0;x<8;x++)
3332                 keys[x] = 0;
3333         /* Set one key for next */
3334         keys[3] = ADSI_KEY_APPS + 3;
3335
3336         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3337         bytes += adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
3338         bytes += adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
3339         bytes += adsi_set_keys(buf + bytes, keys);
3340         bytes += adsi_voice_mode(buf + bytes, 0);
3341         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3342 }
3343
3344 static void adsi_folders(struct ast_channel *chan, int start, char *label)
3345 {
3346         unsigned char buf[256];
3347         int bytes=0;
3348         unsigned char keys[8];
3349         int x,y;
3350
3351         if (!adsi_available(chan))
3352                 return;
3353
3354         for (x=0;x<5;x++) {
3355                 y = ADSI_KEY_APPS + 12 + start + x;
3356                 if (y > ADSI_KEY_APPS + 12 + 4)
3357                         y = 0;
3358                 keys[x] = ADSI_KEY_SKT | y;
3359         }
3360         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
3361         keys[6] = 0;
3362         keys[7] = 0;
3363
3364         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
3365         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
3366         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3367         bytes += adsi_set_keys(buf + bytes, keys);
3368         bytes += adsi_voice_mode(buf + bytes, 0);
3369
3370         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3371 }
3372
3373 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
3374 {
3375         int bytes=0;
3376         unsigned char buf[256]; 
3377         char buf1[256], buf2[256];
3378         char fn2[256];
3379
3380         char cid[256]="";
3381         char *val;
3382         char *name, *num;
3383         char datetime[21]="";
3384         FILE *f;
3385
3386         unsigned char keys[8];
3387
3388         int x;
3389
3390         if (!adsi_available(chan))
3391                 return;
3392
3393         /* Retrieve important info */
3394         snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
3395         f = fopen(fn2, "r");
3396         if (f) {
3397                 while (!feof(f)) {      
3398                         fgets((char *)buf, sizeof(buf), f);
3399                         if (!feof(f)) {
3400                                 char *stringp=NULL;
3401                                 stringp = (char *)buf;
3402                                 strsep(&stringp, "=");
3403                                 val = strsep(&stringp, "=");
3404                                 if (!ast_strlen_zero(val)) {
3405                                         if (!strcmp((char *)buf, "callerid"))
3406                                                 ast_copy_string(cid, val, sizeof(cid));
3407                                         if (!strcmp((char *)buf, "origdate"))
3408                                                 ast_copy_string(datetime, val, sizeof(datetime));
3409                                 }
3410                         }
3411                 }
3412                 fclose(f);
3413         }
3414         /* New meaning for keys */
3415         for (x=0;x<5;x++)
3416                 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3417         keys[6] = 0x0;
3418         keys[7] = 0x0;
3419
3420         if (!vms->curmsg) {
3421                 /* No prev key, provide "Folder" instead */
3422                 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3423         }
3424         if (vms->curmsg >= vms->lastmsg) {
3425                 /* If last message ... */
3426                 if (vms->curmsg) {
3427                         /* but not only message, provide "Folder" instead */
3428                         keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3429                         bytes += adsi_voice_mode(buf + bytes, 0);
3430
3431                 } else {
3432                         /* Otherwise if only message, leave blank */
3433                         keys[3] = 1;
3434                 }
3435         }
3436
3437         if (!ast_strlen_zero(cid)) {
3438                 ast_callerid_parse(cid, &name, &num);
3439                 if (!name)
3440                         name = num;
3441         } else
3442                 name = "Unknown Caller";
3443
3444         /* If deleted, show "undeleted" */
3445
3446         if (vms->deleted[vms->curmsg])
3447                 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3448
3449         /* Except "Exit" */
3450         keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3451         snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
3452                 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
3453         snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
3454
3455         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3456         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3457         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
3458         bytes += adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
3459         bytes += adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3460         bytes += adsi_set_keys(buf + bytes, keys);
3461         bytes += adsi_voice_mode(buf + bytes, 0);
3462
3463         adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3464 }
3465
3466 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)