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