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