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