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