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