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