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