2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
21 * \brief Comedian Mail - Voicemail System
23 * \author Mark Spencer <markster@digium.com>
27 * \ingroup applications
28 * \note This module requires res_adsi to load.
32 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o">
33 <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
34 <depend>unixodbc</depend>
35 <defaultenabled>no</defaultenabled>
37 <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
38 <depend>imap_tk</depend>
40 <defaultenabled>no</defaultenabled>
47 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
57 #include <sys/types.h>
69 #include "asterisk/lock.h"
70 #include "asterisk/file.h"
71 #include "asterisk/logger.h"
72 #include "asterisk/channel.h"
73 #include "asterisk/pbx.h"
74 #include "asterisk/options.h"
75 #include "asterisk/config.h"
76 #include "asterisk/say.h"
77 #include "asterisk/module.h"
78 #include "asterisk/adsi.h"
79 #include "asterisk/app.h"
80 #include "asterisk/manager.h"
81 #include "asterisk/dsp.h"
82 #include "asterisk/localtime.h"
83 #include "asterisk/cli.h"
84 #include "asterisk/utils.h"
85 #include "asterisk/stringfields.h"
86 #include "asterisk/smdi.h"
88 #include "asterisk/res_odbc.h"
92 AST_MUTEX_DEFINE_STATIC(curhstusr_lock);
93 static char *curhst = NULL; /* currently connected host */
94 static char *curusr = NULL; /* current login user */
96 static char temp[1024];
98 static char imapserver[48];
99 static char imapport[8];
100 static char imapflags[128];
101 static char authuser[32];
102 static char authpassword[42];
103 static int expungeonhangup = 1;
104 static char delimiter = '\0';
108 static int init_mailstream (struct vm_state *vms);
109 static void write_file (char *filename, char *buffer, unsigned long len);
110 static void status (MAILSTREAM *stream);
111 static void display_body (BODY *body, char *pfx, long i);
112 static char *get_header_by_tag(char *header, char *tag);
113 static void vm_imap_delete(int msgnum, struct vm_state *vms);
114 static char *get_user_by_mailbox(char *mailbox);
115 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
116 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, int interactive);
117 static void vmstate_insert(struct vm_state *vms);
118 static void vmstate_delete(struct vm_state *vms);
119 static void set_update(MAILSTREAM * stream);
120 static void init_vm_state(struct vm_state *vms);
121 static void check_msgArray(struct vm_state *vms);
122 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
123 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
124 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num);
125 static void get_mailbox_delimiter(MAILSTREAM *stream);
126 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
128 struct vm_state *vms;
129 struct vmstate *next;
131 AST_MUTEX_DEFINE_STATIC(vmstate_lock);
132 static struct vmstate *vmstates = NULL;
135 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
137 #define COMMAND_TIMEOUT 5000
138 /* Don't modify these here; set your umask at runtime instead */
139 #define VOICEMAIL_DIR_MODE 0777
140 #define VOICEMAIL_FILE_MODE 0666
142 #define VOICEMAIL_CONFIG "voicemail.conf"
143 #define ASTERISK_USERNAME "asterisk"
145 /* Default mail command to mail voicemail. Change it with the
146 mailcmd= command in voicemail.conf */
147 #define SENDMAIL "/usr/sbin/sendmail -t"
149 #define INTRO "vm-intro"
152 #define MAXMSGLIMIT 9999
154 #define BASEMAXINLINE 256
155 #define BASELINELEN 72
156 #define BASEMAXINLINE 256
159 #define MAX_DATETIME_FORMAT 512
160 #define MAX_NUM_CID_CONTEXTS 10
162 #define VM_REVIEW (1 << 0)
163 #define VM_OPERATOR (1 << 1)
164 #define VM_SAYCID (1 << 2)
165 #define VM_SVMAIL (1 << 3)
166 #define VM_ENVELOPE (1 << 4)
167 #define VM_SAYDURATION (1 << 5)
168 #define VM_SKIPAFTERCMD (1 << 6)
169 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
170 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
171 #define VM_PBXSKIP (1 << 9)
172 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
173 #define VM_ATTACH (1 << 11)
174 #define VM_DELETE (1 << 12)
175 #define VM_ALLOCED (1 << 13)
176 #define VM_SEARCH (1 << 14)
177 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
178 #define ERROR_LOCK_PATH -100
182 OPT_SILENT = (1 << 0),
183 OPT_BUSY_GREETING = (1 << 1),
184 OPT_UNAVAIL_GREETING = (1 << 2),
185 OPT_RECORDGAIN = (1 << 3),
186 OPT_PREPEND_MAILBOX = (1 << 4),
187 OPT_PRIORITY_JUMP = (1 << 5),
188 OPT_AUTOPLAY = (1 << 6),
192 OPT_ARG_RECORDGAIN = 0,
193 OPT_ARG_PLAYFOLDER = 1,
194 /* This *must* be the last value in this enum! */
195 OPT_ARG_ARRAY_SIZE = 2,
198 AST_APP_OPTIONS(vm_app_options, {
199 AST_APP_OPTION('s', OPT_SILENT),
200 AST_APP_OPTION('b', OPT_BUSY_GREETING),
201 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
202 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
203 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
204 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
205 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
208 static int load_config(void);
210 /*! \page vmlang Voicemail Language Syntaxes Supported
212 \par Syntaxes supported, not really language codes.
220 \arg \b pt - Portuguese
222 \arg \b no - Norwegian
225 German requires the following additional soundfile:
226 \arg \b 1F einE (feminine)
228 Spanish requires the following additional soundfile:
229 \arg \b 1M un (masculine)
231 Dutch, Portuguese & Spanish require the following additional soundfiles:
232 \arg \b vm-INBOXs singular of 'new'
233 \arg \b vm-Olds singular of 'old/heard/read'
236 \arg \b vm-INBOX nieuwe (nl)
237 \arg \b vm-Old oude (nl)
240 \arg \b vm-new-a 'new', feminine singular accusative
241 \arg \b vm-new-e 'new', feminine plural accusative
242 \arg \b vm-new-ych 'new', feminine plural genitive
243 \arg \b vm-old-a 'old', feminine singular accusative
244 \arg \b vm-old-e 'old', feminine plural accusative
245 \arg \b vm-old-ych 'old', feminine plural genitive
246 \arg \b digits/1-a 'one', not always same as 'digits/1'
247 \arg \b digits/2-ie 'two', not always same as 'digits/2'
250 \arg \b vm-nytt singular of 'new'
251 \arg \b vm-nya plural of 'new'
252 \arg \b vm-gammalt singular of 'old'
253 \arg \b vm-gamla plural of 'old'
254 \arg \b digits/ett 'one', not always same as 'digits/1'
257 \arg \b vm-ny singular of 'new'
258 \arg \b vm-nye plural of 'new'
259 \arg \b vm-gammel singular of 'old'
260 \arg \b vm-gamle plural of 'old'
268 Italian requires the following additional soundfile:
272 \arg \b vm-nuovi new plural
273 \arg \b vm-vecchio old
274 \arg \b vm-vecchi old plural
276 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
277 spelled among others when you have to change folder. For the above reasons, vm-INBOX
278 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
287 unsigned char iobuf[BASEMAXINLINE];
290 /*! Structure for linked list of users */
292 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
293 char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
294 char password[80]; /*!< Secret pin code, numbers only */
295 char fullname[80]; /*!< Full name, for directory app */
296 char email[80]; /*!< E-mail address */
297 char pager[80]; /*!< E-mail address to pager (no attachment) */
298 char serveremail[80]; /*!< From: Mail address */
299 char mailcmd[160]; /*!< Configurable mail command */
300 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
301 char zonetag[80]; /*!< Time zone */
304 char uniqueid[20]; /*!< Unique integer identifier */
306 char attachfmt[20]; /*!< Attachment format */
307 unsigned int flags; /*!< VM_ flags */
309 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
311 char imapuser[80]; /* IMAP server login */
313 double volgain; /*!< Volume gain for voicemails sent via email */
314 AST_LIST_ENTRY(ast_vm_user) list;
318 AST_LIST_ENTRY(vm_zone) list;
321 char msg_format[512];
340 int updated; /* decremented on each mail check until 1 -allows delay */
342 MAILSTREAM *mailstream;
344 char imapuser[80]; /* IMAP server login */
346 unsigned int quota_limit;
347 unsigned int quota_usage;
348 struct vm_state *persist_vms;
351 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
352 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
353 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
354 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
355 signed char record_gain);
356 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
357 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
358 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
360 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
362 static void apply_options(struct ast_vm_user *vmu, const char *options);
365 static char odbc_database[80];
366 static char odbc_table[80];
367 #define RETRIEVE(a,b) retrieve_file(a,b)
368 #define DISPOSE(a,b) remove_file(a,b)
369 #define STORE(a,b,c,d) store_file(a,b,c,d)
370 #define EXISTS(a,b,c,d) (message_exists(a,b))
371 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
372 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
373 #define DELETE(a,b,c) (delete_file(a,b))
376 #define RETRIEVE(a,b)
378 #define STORE(a,b,c,d)
379 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
380 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
381 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
382 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
383 #define DELETE(a,b,c) (vm_delete(c))
385 #define RETRIEVE(a,b)
387 #define STORE(a,b,c,d)
388 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
389 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
390 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
391 #define DELETE(a,b,c) (vm_delete(c))
395 static char VM_SPOOL_DIR[PATH_MAX];
397 static char ext_pass_cmd[128];
400 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
402 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
404 #define tdesc "Comedian Mail (Voicemail System)"
407 static char userscontext[AST_MAX_EXTENSION] = "default";
409 static char *addesc = "Comedian Mail";
411 static char *synopsis_vm =
412 "Leave a Voicemail message";
414 static char *descrip_vm =
415 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
416 "application allows the calling party to leave a message for the specified\n"
417 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
418 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
419 "specified mailbox does not exist.\n"
420 " The Voicemail application will exit if any of the following DTMF digits are\n"
422 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
423 " * - Jump to the 'a' extension in the current dialplan context.\n"
424 " This application will set the following channel variable upon completion:\n"
425 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
426 " application. The possible values are:\n"
427 " SUCCESS | USEREXIT | FAILED\n\n"
429 " b - Play the 'busy' greeting to the calling party.\n"
430 " g(#) - Use the specified amount of gain when recording the voicemail\n"
431 " message. The units are whole-number decibels (dB).\n"
432 " s - Skip the playback of instructions for leaving a message to the\n"
434 " u - Play the 'unavailble greeting.\n"
435 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
438 static char *synopsis_vmain =
439 "Check Voicemail messages";
441 static char *descrip_vmain =
442 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
443 "calling party to check voicemail messages. A specific mailbox, and optional\n"
444 "corresponding context, may be specified. If a mailbox is not provided, the\n"
445 "calling party will be prompted to enter one. If a context is not specified,\n"
446 "the 'default' context will be used.\n\n"
448 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
449 " is entered by the caller.\n"
450 " g(#) - Use the specified amount of gain when recording a voicemail\n"
451 " message. The units are whole-number decibels (dB).\n"
452 " s - Skip checking the passcode for the mailbox.\n"
453 " a(#) - Skip folder prompt and go directly to folder specified.\n"
454 " Defaults to INBOX\n";
456 static char *synopsis_vm_box_exists =
457 "Check to see if Voicemail mailbox exists";
459 static char *descrip_vm_box_exists =
460 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
461 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
463 " This application will set the following channel variable upon completion:\n"
464 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
465 " MailboxExists application. Possible values include:\n"
466 " SUCCESS | FAILED\n\n"
468 " j - Jump to priority n+101 if the mailbox is found.\n";
470 static char *synopsis_vmauthenticate =
471 "Authenticate with Voicemail passwords";
473 static char *descrip_vmauthenticate =
474 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
475 "same way as the Authenticate application, but the passwords are taken from\n"
477 " If the mailbox is specified, only that mailbox's password will be considered\n"
478 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
479 "be set with the authenticated mailbox.\n\n"
481 " s - Skip playing the initial prompts.\n";
483 /* Leave a message */
484 static char *app = "VoiceMail";
486 /* Check mail, control, etc */
487 static char *app2 = "VoiceMailMain";
489 static char *app3 = "MailboxExists";
490 static char *app4 = "VMAuthenticate";
492 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
493 static AST_LIST_HEAD_STATIC(zones, vm_zone);
494 static int maxsilence;
496 static int silencethreshold = 128;
497 static char serveremail[80];
498 static char mailcmd[160]; /* Configurable mail cmd */
499 static char externnotify[160];
500 static struct ast_smdi_interface *smdi_iface = NULL;
501 static char vmfmts[80];
502 static double volgain;
503 static int vmminmessage;
504 static int vmmaxmessage;
507 static int maxlogins;
509 static struct ast_flags globalflags = {0};
511 static int saydurationminfo;
513 static char dialcontext[AST_MAX_CONTEXT];
514 static char callcontext[AST_MAX_CONTEXT];
515 static char exitcontext[AST_MAX_CONTEXT];
517 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
520 static char *emailbody = NULL;
521 static char *emailsubject = NULL;
522 static char *pagerbody = NULL;
523 static char *pagersubject = NULL;
524 static char fromstring[100];
525 static char pagerfromstring[100];
526 static char emailtitle[100];
527 static char charset[32] = "ISO-8859-1";
529 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
530 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
531 static int adsiver = 1;
532 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
535 static void populate_defaults(struct ast_vm_user *vmu)
537 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
538 if (saydurationminfo)
539 vmu->saydurationm = saydurationminfo;
541 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
543 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
545 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
547 vmu->maxmsg = maxmsg;
548 vmu->volgain = volgain;
551 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
554 if (!strcasecmp(var, "attach")) {
555 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
556 } else if (!strcasecmp(var, "attachfmt")) {
557 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
558 } else if (!strcasecmp(var, "serveremail")) {
559 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
560 } else if (!strcasecmp(var, "language")) {
561 ast_copy_string(vmu->language, value, sizeof(vmu->language));
562 } else if (!strcasecmp(var, "tz")) {
563 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
565 } else if (!strcasecmp(var, "imapuser")) {
566 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
568 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
569 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
570 } else if (!strcasecmp(var, "saycid")){
571 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
572 } else if (!strcasecmp(var,"sendvoicemail")){
573 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
574 } else if (!strcasecmp(var, "review")){
575 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
576 } else if (!strcasecmp(var, "tempgreetwarn")){
577 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
578 } else if (!strcasecmp(var, "operator")){
579 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
580 } else if (!strcasecmp(var, "envelope")){
581 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
582 } else if (!strcasecmp(var, "sayduration")){
583 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
584 } else if (!strcasecmp(var, "saydurationm")){
585 if (sscanf(value, "%d", &x) == 1) {
586 vmu->saydurationm = x;
588 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
590 } else if (!strcasecmp(var, "forcename")){
591 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
592 } else if (!strcasecmp(var, "forcegreetings")){
593 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
594 } else if (!strcasecmp(var, "callback")) {
595 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
596 } else if (!strcasecmp(var, "dialout")) {
597 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
598 } else if (!strcasecmp(var, "exitcontext")) {
599 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
600 } else if (!strcasecmp(var, "maxmsg")) {
601 vmu->maxmsg = atoi(value);
602 if (vmu->maxmsg <= 0) {
603 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
604 vmu->maxmsg = MAXMSG;
605 } else if (vmu->maxmsg > MAXMSGLIMIT) {
606 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
607 vmu->maxmsg = MAXMSGLIMIT;
609 } else if (!strcasecmp(var, "volgain")) {
610 sscanf(value, "%lf", &vmu->volgain);
611 } else if (!strcasecmp(var, "options")) {
612 apply_options(vmu, value);
616 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
619 if (!ast_strlen_zero(vmu->uniqueid)) {
620 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
622 ast_copy_string(vmu->password, password, sizeof(vmu->password));
632 static void apply_options(struct ast_vm_user *vmu, const char *options)
633 { /* Destructively Parse options and apply */
637 stringp = ast_strdupa(options);
638 while ((s = strsep(&stringp, "|"))) {
640 if ((var = strsep(&value, "=")) && value) {
641 apply_option(vmu, var, value);
646 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
648 struct ast_variable *tmp;
651 if (!strcasecmp(tmp->name, "password")) {
652 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
653 } else if (!strcasecmp(tmp->name, "uniqueid")) {
654 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
655 } else if (!strcasecmp(tmp->name, "pager")) {
656 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
657 } else if (!strcasecmp(tmp->name, "email")) {
658 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
659 } else if (!strcasecmp(tmp->name, "fullname")) {
660 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
661 } else if (!strcasecmp(tmp->name, "context")) {
662 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
664 apply_option(retval, tmp->name, tmp->value);
669 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
671 struct ast_variable *var;
672 struct ast_vm_user *retval;
674 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
676 ast_set_flag(retval, VM_ALLOCED);
678 memset(retval, 0, sizeof(*retval));
680 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
681 populate_defaults(retval);
682 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
683 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
685 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
687 apply_options_full(retval, var);
688 ast_variables_destroy(var);
698 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
700 /* This function could be made to generate one from a database, too */
701 struct ast_vm_user *vmu=NULL, *cur;
702 AST_LIST_LOCK(&users);
704 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
707 AST_LIST_TRAVERSE(&users, cur, list) {
708 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
710 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
714 /* Make a copy, so that on a reload, we have no race */
715 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
716 memcpy(vmu, cur, sizeof(*vmu));
717 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
718 AST_LIST_NEXT(vmu, list) = NULL;
721 vmu = find_user_realtime(ivm, context, mailbox);
722 AST_LIST_UNLOCK(&users);
726 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
728 /* This function could be made to generate one from a database, too */
729 struct ast_vm_user *cur;
731 AST_LIST_LOCK(&users);
732 AST_LIST_TRAVERSE(&users, cur, list) {
733 if ((!context || !strcasecmp(context, cur->context)) &&
734 (!strcasecmp(mailbox, cur->mailbox)))
738 ast_copy_string(cur->password, newpass, sizeof(cur->password));
741 AST_LIST_UNLOCK(&users);
745 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
747 /* There's probably a better way of doing this. */
748 /* That's why I've put the password change in a separate function. */
749 /* This could also be done with a database function */
756 char currcontext[256] ="";
757 char tmpin[PATH_MAX];
758 char tmpout[PATH_MAX];
761 if (!change_password_realtime(vmu, newpassword))
764 snprintf(tmpin, sizeof(tmpin), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR);
765 snprintf(tmpout, sizeof(tmpout), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR);
766 configin = fopen(tmpin,"r");
768 configout = fopen(tmpout,"w+");
771 if (!configin || !configout) {
775 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for reading: %s\n", tmpin, strerror(errno));
779 ast_log(LOG_WARNING, "Warning: Unable to open '%s' for writing: %s\n", tmpout, strerror(errno));
783 while (!feof(configin)) {
784 char *user = NULL, *pass = NULL, *rest = NULL, *comment = NULL, *tmpctx = NULL, *tmpctxend = NULL;
786 /* Read in the line */
787 if (fgets(inbuf, sizeof(inbuf), configin) == NULL)
791 /* Make a backup of it */
792 ast_copy_string(orig, inbuf, sizeof(orig));
795 Read the file line by line, split each line into a comment and command section
796 only parse the command portion of the line
798 if (inbuf[strlen(inbuf) - 1] == '\n')
799 inbuf[strlen(inbuf) - 1] = '\0';
801 if ((comment = strchr(inbuf, ';')))
802 *comment++ = '\0'; /* Now inbuf is terminated just before the comment */
804 if (ast_strlen_zero(inbuf)) {
805 fprintf(configout, "%s", orig);
809 /* Check for a context, first '[' to first ']' */
810 if ((tmpctx = strchr(inbuf, '['))) {
811 tmpctxend = strchr(tmpctx, ']');
814 ast_copy_string(currcontext, tmpctx + 1, tmpctxend - tmpctx);
815 fprintf(configout, "%s", orig);
820 /* This isn't a context line, check for MBX => PSWD... */
822 if ((pass = strchr(user, '='))) {
823 /* We have a line in the form of aaaaa=aaaaaa */
826 user = ast_strip(user);
831 pass = ast_skip_blanks(pass);
834 Since no whitespace allowed in fields, or more correctly white space
835 inside the fields is there for a purpose, we can just terminate pass
836 at the comma or EOL whichever comes first.
838 if ((rest = strchr(pass, ',')))
844 /* Compare user, pass AND context */
845 if (!ast_strlen_zero(user) && !strcmp(user, vmu->mailbox) &&
846 !ast_strlen_zero(pass) && !strcmp(pass, vmu->password) &&
847 !strcasecmp(currcontext, vmu->context)) {
848 /* This is the line */
850 fprintf(configout, "%s => %s,%s", user, newpassword, rest);
852 fprintf(configout, "%s => %s", user, newpassword);
854 /* If there was a comment on the line print it out */
856 fprintf(configout, ";%s\n", comment);
858 fprintf(configout, "\n");
861 /* Put it back like it was */
862 fprintf(configout, "%s", orig);
868 stat(tmpin, &statbuf);
869 chmod(tmpout, statbuf.st_mode);
870 chown(tmpout, statbuf.st_uid, statbuf.st_gid);
872 rename(tmpout, tmpin);
873 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
874 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
877 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
880 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
881 if (!ast_safe_system(buf))
882 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
885 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
887 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
891 static int make_gsm_file(char *dest, char *imapuser, char *dir, int num)
895 sprintf(gsmdir,"%s/%s",dir,imapuser);
896 if (mkdir(gsmdir, 01777) && (errno != EEXIST)) {
897 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", gsmdir, strerror(errno));
898 return sprintf(dest, "%s/msg%04d", dir, num);
900 /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
901 return sprintf(dest, "%s/%s/msg%04d", dir, imapuser, num);
904 static void vm_imap_delete(int msgnum, struct vm_state *vms)
906 unsigned long messageNum = 0;
909 /* find real message number based on msgnum */
910 /* this may be an index into vms->msgArray based on the msgnum. */
912 messageNum = vms->msgArray[msgnum];
913 if (messageNum == 0) {
914 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
918 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
920 sprintf (arg,"%lu",messageNum);
921 mail_setflag (vms->mailstream,arg,"\\DELETED");
925 static int make_file(char *dest, int len, char *dir, int num)
927 return snprintf(dest, len, "%s/msg%04d", dir, num);
930 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
931 * \param dest String. base directory.
932 * \param context String. Ignored if is null or empty string.
933 * \param ext String. Ignored if is null or empty string.
934 * \param folder String. Ignored if is null or empty string.
935 * \return 0 on failure, 1 on success.
937 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
939 mode_t mode = VOICEMAIL_DIR_MODE;
941 if (!ast_strlen_zero(context)) {
942 make_dir(dest, len, context, "", "");
943 if (mkdir(dest, mode) && errno != EEXIST) {
944 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
948 if (!ast_strlen_zero(ext)) {
949 make_dir(dest, len, context, ext, "");
950 if (mkdir(dest, mode) && errno != EEXIST) {
951 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
955 if (!ast_strlen_zero(folder)) {
956 make_dir(dest, len, context, ext, folder);
957 if (mkdir(dest, mode) && errno != EEXIST) {
958 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
965 /* only return failure if ast_lock_path returns 'timeout',
966 not if the path does not exist or any other reason
968 static int vm_lock_path(const char *path)
970 switch (ast_lock_path(path)) {
971 case AST_LOCK_TIMEOUT:
980 static int retrieve_file(char *dir, int msgnum)
987 SQLSMALLINT colcount=0;
994 SQLSMALLINT datatype;
995 SQLSMALLINT decimaldigits;
996 SQLSMALLINT nullable;
1004 struct odbc_obj *obj;
1005 obj = odbc_request_obj(odbc_database, 0);
1007 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1008 c = strchr(fmt, '|');
1011 if (!strcasecmp(fmt, "wav49"))
1013 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1015 make_file(fn, sizeof(fn), dir, msgnum);
1017 ast_copy_string(fn, dir, sizeof(fn));
1018 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1019 f = fopen(full_fn, "w+");
1020 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1021 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1022 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1023 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1024 odbc_release_obj(obj);
1027 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1028 res = SQLPrepare(stmt, sql, SQL_NTS);
1029 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1030 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1031 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1032 odbc_release_obj(obj);
1035 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1036 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1037 res = odbc_smart_execute(obj, stmt);
1038 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1039 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1040 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1041 odbc_release_obj(obj);
1044 res = SQLFetch(stmt);
1045 if (res == SQL_NO_DATA) {
1046 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1047 odbc_release_obj(obj);
1050 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1051 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1052 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1053 odbc_release_obj(obj);
1056 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
1058 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
1059 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1060 odbc_release_obj(obj);
1063 res = SQLNumResultCols(stmt, &colcount);
1064 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1065 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
1066 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1067 odbc_release_obj(obj);
1071 fprintf(f, "[message]\n");
1072 for (x=0;x<colcount;x++) {
1074 collen = sizeof(coltitle);
1075 res = SQLDescribeCol(stmt, x + 1, coltitle, sizeof(coltitle), &collen,
1076 &datatype, &colsize, &decimaldigits, &nullable);
1077 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1078 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
1079 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1080 odbc_release_obj(obj);
1083 if (!strcasecmp(coltitle, "recording")) {
1084 res = SQLGetData(stmt, x + 1, SQL_BINARY, NULL, 0, &colsize);
1088 lseek(fd, fdlen - 1, SEEK_SET);
1089 if (write(fd, tmp, 1) != 1) {
1095 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
1098 memset(fdm, 0, fdlen);
1099 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, fdlen, &colsize);
1100 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1101 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1102 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1103 odbc_release_obj(obj);
1108 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1109 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1110 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1111 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1112 odbc_release_obj(obj);
1115 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
1116 fprintf(f, "%s=%s\n", coltitle, rowdata);
1119 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1120 odbc_release_obj(obj);
1122 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1133 static int remove_file(char *dir, int msgnum)
1140 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1141 make_file(fn, sizeof(fn), dir, msgnum);
1143 ast_copy_string(fn, dir, sizeof(fn));
1144 ast_filedelete(fn, NULL);
1145 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1150 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1158 struct odbc_obj *obj;
1159 obj = odbc_request_obj(odbc_database, 0);
1161 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1162 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1163 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1164 odbc_release_obj(obj);
1167 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
1168 res = SQLPrepare(stmt, sql, SQL_NTS);
1169 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1170 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1171 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1172 odbc_release_obj(obj);
1175 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1176 res = odbc_smart_execute(obj, stmt);
1177 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1178 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1179 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1180 odbc_release_obj(obj);
1183 res = SQLFetch(stmt);
1184 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1185 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1186 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1187 odbc_release_obj(obj);
1190 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1191 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1192 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1193 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1194 odbc_release_obj(obj);
1197 if (sscanf(rowdata, "%d", &x) != 1)
1198 ast_log(LOG_WARNING, "Failed to read message count!\n");
1199 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1200 odbc_release_obj(obj);
1202 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1207 static int message_exists(char *dir, int msgnum)
1216 struct odbc_obj *obj;
1217 obj = odbc_request_obj(odbc_database, 0);
1219 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
1220 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1221 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1222 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1223 odbc_release_obj(obj);
1226 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1227 res = SQLPrepare(stmt, sql, SQL_NTS);
1228 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1229 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1230 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1231 odbc_release_obj(obj);
1234 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1235 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1236 res = odbc_smart_execute(obj, stmt);
1237 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1238 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1239 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1240 odbc_release_obj(obj);
1243 res = SQLFetch(stmt);
1244 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1245 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
1246 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1247 odbc_release_obj(obj);
1250 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
1251 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1252 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
1253 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1254 odbc_release_obj(obj);
1257 if (sscanf(rowdata, "%d", &x) != 1)
1258 ast_log(LOG_WARNING, "Failed to read message count!\n");
1259 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1260 odbc_release_obj(obj);
1262 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1267 static int count_messages(struct ast_vm_user *vmu, char *dir)
1269 return last_message_index(vmu, dir) + 1;
1272 static void delete_file(char *sdir, int smsg)
1279 struct odbc_obj *obj;
1280 obj = odbc_request_obj(odbc_database, 0);
1282 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1283 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1284 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1285 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1286 odbc_release_obj(obj);
1289 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
1290 res = SQLPrepare(stmt, sql, SQL_NTS);
1291 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1292 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1293 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1294 odbc_release_obj(obj);
1297 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1298 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1299 res = odbc_smart_execute(obj, stmt);
1300 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1301 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1302 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1303 odbc_release_obj(obj);
1306 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1307 odbc_release_obj(obj);
1309 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1314 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
1321 struct odbc_obj *obj;
1323 delete_file(ddir, dmsg);
1324 obj = odbc_request_obj(odbc_database, 0);
1326 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1327 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1328 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1329 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1330 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1331 odbc_release_obj(obj);
1334 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);
1335 res = SQLPrepare(stmt, sql, SQL_NTS);
1336 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1337 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1338 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1339 odbc_release_obj(obj);
1342 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1343 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1344 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxuser), 0, (void *)dmailboxuser, 0, NULL);
1345 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dmailboxcontext), 0, (void *)dmailboxcontext, 0, NULL);
1346 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1347 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1348 res = odbc_smart_execute(obj, stmt);
1349 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1350 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
1351 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1352 odbc_release_obj(obj);
1355 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1356 odbc_release_obj(obj);
1358 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1363 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
1378 char *context="", *macrocontext="", *callerid="", *origtime="", *duration="";
1379 char *category = "";
1380 struct ast_config *cfg=NULL;
1381 struct odbc_obj *obj;
1383 delete_file(dir, msgnum);
1384 obj = odbc_request_obj(odbc_database, 0);
1386 ast_copy_string(fmt, vmfmts, sizeof(fmt));
1387 c = strchr(fmt, '|');
1390 if (!strcasecmp(fmt, "wav49"))
1392 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
1394 make_file(fn, sizeof(fn), dir, msgnum);
1396 ast_copy_string(fn, dir, sizeof(fn));
1397 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
1398 cfg = ast_config_load(full_fn);
1399 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
1400 fd = open(full_fn, O_RDWR);
1402 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
1403 odbc_release_obj(obj);
1407 context = ast_variable_retrieve(cfg, "message", "context");
1408 if (!context) context = "";
1409 macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
1410 if (!macrocontext) macrocontext = "";
1411 callerid = ast_variable_retrieve(cfg, "message", "callerid");
1412 if (!callerid) callerid = "";
1413 origtime = ast_variable_retrieve(cfg, "message", "origtime");
1414 if (!origtime) origtime = "";
1415 duration = ast_variable_retrieve(cfg, "message", "duration");
1416 if (!duration) duration = "";
1417 category = ast_variable_retrieve(cfg, "message", "category");
1418 if (!category) category = "";
1420 fdlen = lseek(fd, 0, SEEK_END);
1421 lseek(fd, 0, SEEK_SET);
1422 printf("Length is %zd\n", fdlen);
1423 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
1425 ast_log(LOG_WARNING, "Memory map failed!\n");
1426 odbc_release_obj(obj);
1429 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1430 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1431 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1432 odbc_release_obj(obj);
1435 if (!ast_strlen_zero(category))
1436 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
1438 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table);
1439 res = SQLPrepare(stmt, sql, SQL_NTS);
1440 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1441 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1442 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1443 odbc_release_obj(obj);
1446 len = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1447 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(dir), 0, (void *)dir, 0, NULL);
1448 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1449 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, fdlen, 0, (void *)fdm, fdlen, &len);
1450 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(context), 0, (void *)context, 0, NULL);
1451 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(macrocontext), 0, (void *)macrocontext, 0, NULL);
1452 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(callerid), 0, (void *)callerid, 0, NULL);
1453 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(origtime), 0, (void *)origtime, 0, NULL);
1454 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(duration), 0, (void *)duration, 0, NULL);
1455 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1456 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1457 if (!ast_strlen_zero(category))
1458 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(category), 0, (void *)category, 0, NULL);
1459 res = odbc_smart_execute(obj, stmt);
1460 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1461 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1462 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1463 odbc_release_obj(obj);
1466 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1467 odbc_release_obj(obj);
1469 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1472 ast_config_destroy(cfg);
1480 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
1487 struct odbc_obj *obj;
1489 delete_file(ddir, dmsg);
1490 obj = odbc_request_obj(odbc_database, 0);
1492 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
1493 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
1494 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
1495 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1496 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
1497 odbc_release_obj(obj);
1500 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
1501 res = SQLPrepare(stmt, sql, SQL_NTS);
1502 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1503 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
1504 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1505 odbc_release_obj(obj);
1508 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(ddir), 0, (void *)ddir, 0, NULL);
1509 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnumd), 0, (void *)msgnumd, 0, NULL);
1510 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxuser), 0, (void *)mailboxuser, 0, NULL);
1511 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(mailboxcontext), 0, (void *)mailboxcontext, 0, NULL);
1512 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(sdir), 0, (void *)sdir, 0, NULL);
1513 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(msgnums), 0, (void *)msgnums, 0, NULL);
1514 res = odbc_smart_execute(obj, stmt);
1515 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
1516 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
1517 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1518 odbc_release_obj(obj);
1521 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
1522 odbc_release_obj(obj);
1524 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
1530 static int count_messages(struct ast_vm_user *vmu, char *dir)
1532 /* Find all .txt files - even if they are not in sequence from 0000 */
1536 struct dirent *vment = NULL;
1538 if (vm_lock_path(dir))
1539 return ERROR_LOCK_PATH;
1541 if ((vmdir = opendir(dir))) {
1542 while ((vment = readdir(vmdir))) {
1543 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
1548 ast_unlock_path(dir);
1553 static void rename_file(char *sfn, char *dfn)
1557 ast_filerename(sfn,dfn,NULL);
1558 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
1559 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
1563 static int copy(char *infile, char *outfile)
1571 #ifdef HARDLINK_WHEN_POSSIBLE
1572 /* Hard link if possible; saves disk space & is faster */
1573 if (link(infile, outfile)) {
1575 if ((ifd = open(infile, O_RDONLY)) < 0) {
1576 ast_log(LOG_WARNING, "Unable to open %s in read-only mode\n", infile);
1579 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
1580 ast_log(LOG_WARNING, "Unable to open %s in write-only mode\n", outfile);
1585 len = read(ifd, buf, sizeof(buf));
1587 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
1593 res = write(ofd, buf, len);
1594 if (errno == ENOMEM || errno == ENOSPC || res != len) {
1595 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
1605 #ifdef HARDLINK_WHEN_POSSIBLE
1607 /* Hard link succeeded */
1613 static void copy_file(char *frompath, char *topath)
1615 char frompath2[256],topath2[256];
1616 ast_filecopy(frompath, topath, NULL);
1617 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
1618 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
1619 copy(frompath2, topath2);
1623 * A negative return value indicates an error.
1624 * \note Should always be called with a lock already set on dir.
1626 static int last_message_index(struct ast_vm_user *vmu, char *dir)
1629 unsigned char map[MAXMSGLIMIT] = "";
1631 struct dirent *msgdirent;
1634 /* Reading the entire directory into a file map scales better than
1635 * doing a stat repeatedly on a predicted sequence. I suspect this
1636 * is partially due to stat(2) internally doing a readdir(2) itself to
1637 * find each file. */
1638 msgdir = opendir(dir);
1639 while ((msgdirent = readdir(msgdir))) {
1640 if (sscanf(msgdirent->d_name, "msg%d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
1645 for (x = 0; x < vmu->maxmsg; x++) {
1653 static int vm_delete(char *file)
1658 txtsize = (strlen(file) + 5)*sizeof(char);
1659 txt = alloca(txtsize);
1660 /* Sprintf here would safe because we alloca'd exactly the right length,
1661 * but trying to eliminate all sprintf's anyhow
1663 snprintf(txt, txtsize, "%s.txt", file);
1665 return ast_filedelete(file, NULL);
1670 static int inbuf(struct baseio *bio, FILE *fi)
1677 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
1691 static int inchar(struct baseio *bio, FILE *fi)
1693 if (bio->iocp>=bio->iolen) {
1694 if (!inbuf(bio, fi))
1698 return bio->iobuf[bio->iocp++];
1701 static int ochar(struct baseio *bio, int c, FILE *so)
1703 if (bio->linelength>=BASELINELEN) {
1704 if (fputs(eol,so)==EOF)
1710 if (putc(((unsigned char)c),so)==EOF)
1718 static int base_encode(char *filename, FILE *so)
1720 unsigned char dtable[BASEMAXINLINE];
1725 memset(&bio, 0, sizeof(bio));
1726 bio.iocp = BASEMAXINLINE;
1728 if (!(fi = fopen(filename, "rb"))) {
1729 ast_log(LOG_WARNING, "Failed to open log file: %s: %s\n", filename, strerror(errno));
1733 for (i= 0;i<9;i++) {
1736 dtable[26+i]= 'a'+i;
1737 dtable[26+i+9]= 'j'+i;
1739 for (i= 0;i<8;i++) {
1740 dtable[i+18]= 'S'+i;
1741 dtable[26+i+18]= 's'+i;
1743 for (i= 0;i<10;i++) {
1744 dtable[52+i]= '0'+i;
1750 unsigned char igroup[3],ogroup[4];
1753 igroup[0]= igroup[1]= igroup[2]= 0;
1755 for (n= 0;n<3;n++) {
1756 if ((c = inchar(&bio, fi)) == EOF) {
1761 igroup[n]= (unsigned char)c;
1765 ogroup[0]= dtable[igroup[0]>>2];
1766 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
1767 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
1768 ogroup[3]= dtable[igroup[2]&0x3F];
1778 ochar(&bio, ogroup[i], so);
1782 if (fputs(eol,so)==EOF)
1790 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)
1793 /* Prepare variables for substition in email body and subject */
1794 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
1795 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
1796 snprintf(passdata, passdatasize, "%d", msgnum);
1797 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
1798 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
1799 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
1800 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1801 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1802 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1803 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
1804 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
1808 * fill in *tm for current time according to the proper timezone, if any.
1809 * Return tm so it can be used as a function argument.
1811 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
1813 const struct vm_zone *z = NULL;
1814 time_t t = time(NULL);
1816 /* Does this user have a timezone specified? */
1817 if (!ast_strlen_zero(vmu->zonetag)) {
1818 /* Find the zone in the list */
1819 AST_LIST_LOCK(&zones);
1820 AST_LIST_TRAVERSE(&zones, z, list) {
1821 if (!strcmp(z->name, vmu->zonetag))
1824 AST_LIST_UNLOCK(&zones);
1826 ast_localtime(&t, tm, z ? z->timezone : NULL);
1830 /* same as mkstemp, but return a FILE * */
1831 static FILE *vm_mkftemp(char *template)
1834 int pfd = mkstemp(template);
1836 p = fdopen(pfd, "w");
1845 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category)
1849 char host[MAXHOSTNAMELEN] = "";
1854 char tmp[80] = "/tmp/astmail-XXXXXX";
1859 if (vmu && ast_strlen_zero(vmu->email)) {
1860 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
1863 if (!strcmp(format, "wav49"))
1865 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));
1866 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1868 if ((p = vm_mkftemp(tmp)) == NULL) {
1869 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
1872 gethostname(host, sizeof(host)-1);
1873 if (strchr(srcemail, '@'))
1874 ast_copy_string(who, srcemail, sizeof(who));
1876 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
1878 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1879 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
1880 fprintf(p, "Date: %s\n", date);
1882 /* Set date format for voicemail mail */
1883 strftime(date, sizeof(date), emaildateformat, &tm);
1886 struct ast_channel *ast;
1887 if ((ast = ast_channel_alloc(0))) {
1889 int vmlen = strlen(fromstring)*3 + 200;
1890 if ((passdata = alloca(vmlen))) {
1891 memset(passdata, 0, vmlen);
1892 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1893 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
1894 fprintf(p, "From: %s <%s>\n",passdata,who);
1896 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1897 ast_channel_free(ast);
1899 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1901 fprintf(p, "From: Asterisk PBX <%s>\n", who);
1902 fprintf(p, "To: %s <%s>\n", vmu->fullname, vmu->email);
1905 struct ast_channel *ast;
1906 if ((ast = ast_channel_alloc(0))) {
1908 int vmlen = strlen(emailsubject)*3 + 200;
1909 if ((passdata = alloca(vmlen))) {
1910 memset(passdata, 0, vmlen);
1911 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1912 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
1913 fprintf(p, "Subject: %s\n", passdata);
1915 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1916 ast_channel_free(ast);
1918 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1921 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
1923 } else if (ast_test_flag((&globalflags), VM_PBXSKIP))
1924 fprintf(p, "Subject: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1926 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s\n", msgnum + 1, mailbox);
1927 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\n", msgnum, (unsigned int)ast_random(), mailbox, getpid(), host);
1929 /* additional information needed for IMAP searching */
1930 fprintf(p, "X-Asterisk-VM-Message-Num: %d\n", msgnum + 1);
1931 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s\n", ext); */
1932 fprintf(p, "X-Asterisk-VM-Server-Name: %s\n", fromstring);
1933 fprintf(p, "X-Asterisk-VM-Context: %s\n", context);
1934 fprintf(p, "X-Asterisk-VM-Extension: %s\n", chan->exten);
1935 fprintf(p, "X-Asterisk-VM-Priority: %d\n", chan->priority);
1936 fprintf(p, "X-Asterisk-VM-Caller-channel: %s\n", chan->name);
1937 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s\n", cidnum);
1938 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s\n", cidname);
1939 fprintf(p, "X-Asterisk-VM-Duration: %d\n", duration);
1940 if (!ast_strlen_zero(category))
1941 fprintf(p, "X-Asterisk-VM-Category: %s\n", category);
1942 fprintf(p, "X-Asterisk-VM-Orig-date: %s\n", date);
1943 fprintf(p, "X-Asterisk-VM-Orig-time: %ld\n", (long)time(NULL));
1945 if (!ast_strlen_zero(cidnum))
1946 fprintf(p, "X-Asterisk-CallerID: %s\n", cidnum);
1947 if (!ast_strlen_zero(cidname))
1948 fprintf(p, "X-Asterisk-CallerIDName: %s\n", cidname);
1949 fprintf(p, "MIME-Version: 1.0\n");
1950 if (attach_user_voicemail) {
1951 /* Something unique. */
1952 snprintf(bound, sizeof(bound), "voicemail_%d%s%d%d", msgnum, mailbox, getpid(), (unsigned int)ast_random());
1954 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1956 fprintf(p, "--%s\n", bound);
1958 fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", charset);
1960 struct ast_channel *ast;
1961 if ((ast = ast_channel_alloc(0))) {
1963 int vmlen = strlen(emailbody)*3 + 200;
1964 if ((passdata = alloca(vmlen))) {
1965 memset(passdata, 0, vmlen);
1966 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
1967 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
1968 fprintf(p, "%s\n", passdata);
1970 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
1971 ast_channel_free(ast);
1973 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
1975 fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message (number %d)\n"
1977 "in mailbox %s from %s, on %s so you might\n"
1978 "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
1979 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1981 if (attach_user_voicemail) {
1982 /* Eww. We want formats to tell us their own MIME type */
1983 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
1984 char tmpdir[256], newtmp[256];
1987 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
1988 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
1989 tmpfd = mkstemp(newtmp);
1990 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
1991 if (vmu->volgain < -.001 || vmu->volgain > .001) {
1992 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
1993 ast_safe_system(tmpcmd);
1995 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
1997 fprintf(p, "--%s\n", bound);
1998 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"\n", ctype, format, msgnum, format);
1999 fprintf(p, "Content-Transfer-Encoding: base64\n");
2000 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
2001 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\n\n", msgnum, format);
2003 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
2004 base_encode(fname, p);
2006 /* only attach if necessary */
2007 if (strcmp(format, "gsm")) {
2008 fprintf(p, "--%s\n", bound);
2009 fprintf(p, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"\n", msgnum, format);
2010 fprintf(p, "Content-Transfer-Encoding: base64\n");
2011 fprintf(p, "Content-Description: Voicemail sound attachment.\n");
2012 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"\n\n", msgnum);
2013 snprintf(fname, sizeof(fname), "%s.gsm", attach);
2014 base_encode(fname, p);
2017 fprintf(p, "\n\n--%s--\n.\n", bound);
2023 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2024 ast_safe_system(tmp2);
2025 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
2030 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)
2033 char host[MAXHOSTNAMELEN]="";
2036 char tmp[80] = "/tmp/astmail-XXXXXX";
2041 if ((p = vm_mkftemp(tmp)) == NULL) {
2042 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
2045 gethostname(host, sizeof(host)-1);
2046 if (strchr(srcemail, '@'))
2047 ast_copy_string(who, srcemail, sizeof(who));
2049 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2051 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2052 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2053 fprintf(p, "Date: %s\n", date);
2055 if (*pagerfromstring) {
2056 struct ast_channel *ast;
2057 if ((ast = ast_channel_alloc(0))) {
2059 int vmlen = strlen(fromstring)*3 + 200;
2060 if ((passdata = alloca(vmlen))) {
2061 memset(passdata, 0, vmlen);
2062 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2063 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
2064 fprintf(p, "From: %s <%s>\n", passdata, who);
2066 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2067 ast_channel_free(ast);
2068 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2070 fprintf(p, "From: Asterisk PBX <%s>\n", who);
2071 fprintf(p, "To: %s\n", pager);
2073 struct ast_channel *ast;
2074 if ((ast = ast_channel_alloc(0))) {
2076 int vmlen = strlen(pagersubject) * 3 + 200;
2077 if ((passdata = alloca(vmlen))) {
2078 memset(passdata, 0, vmlen);
2079 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2080 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
2081 fprintf(p, "Subject: %s\n\n", passdata);
2082 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2083 ast_channel_free(ast);
2084 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2086 fprintf(p, "Subject: New VM\n\n");
2087 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
2089 struct ast_channel *ast;
2090 if ((ast = ast_channel_alloc(0))) {
2092 int vmlen = strlen(pagerbody)*3 + 200;
2093 if ((passdata = alloca(vmlen))) {
2094 memset(passdata, 0, vmlen);
2095 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
2096 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
2097 fprintf(p, "%s\n", passdata);
2098 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
2099 ast_channel_free(ast);
2100 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
2102 fprintf(p, "New %s long msg in box %s\n"
2103 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
2106 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
2107 ast_safe_system(tmp2);
2108 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
2113 #ifndef IMAP_STORAGE
2114 static int get_date(char *s, int len)
2119 localtime_r(&t,&tm);
2120 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
2124 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
2128 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
2130 if (ast_fileexists(fn, NULL, NULL) > 0) {
2131 res = ast_stream_and_wait(chan, fn, chan->language, ecodes);
2137 /* Dispose just in case */
2139 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
2142 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
2146 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
2150 static void free_user(struct ast_vm_user *vmu)
2152 if (ast_test_flag(vmu, VM_ALLOCED))
2156 static void free_zone(struct vm_zone *z)
2161 static const char *mbox(int id)
2163 static const char *msgs[] = {
2175 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "Unknown";
2179 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2187 struct odbc_obj *obj;
2195 /* If no mailbox, return immediately */
2196 if (ast_strlen_zero(mailbox))
2199 ast_copy_string(tmp, mailbox, sizeof(tmp));
2201 context = strchr(tmp, '@');
2206 context = "default";
2208 obj = odbc_request_obj(odbc_database, 0);
2210 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2211 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2212 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2213 odbc_release_obj(obj);
2216 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
2217 res = SQLPrepare(stmt, sql, SQL_NTS);
2218 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2219 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2220 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2221 odbc_release_obj(obj);
2224 res = odbc_smart_execute(obj, stmt);
2225 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2226 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2227 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2228 odbc_release_obj(obj);
2231 res = SQLFetch(stmt);
2232 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2233 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2234 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2235 odbc_release_obj(obj);
2238 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2239 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2240 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2241 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2242 odbc_release_obj(obj);
2245 *newmsgs = atoi(rowdata);
2246 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2248 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2249 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2250 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2251 odbc_release_obj(obj);
2254 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
2255 res = SQLPrepare(stmt, sql, SQL_NTS);
2256 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2257 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2258 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2259 odbc_release_obj(obj);
2262 res = odbc_smart_execute(obj, stmt);
2263 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2264 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2265 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2266 odbc_release_obj(obj);
2269 res = SQLFetch(stmt);
2270 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2271 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2272 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2273 odbc_release_obj(obj);
2276 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2277 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2278 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2279 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2280 odbc_release_obj(obj);
2283 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2284 odbc_release_obj(obj);
2285 *oldmsgs = atoi(rowdata);
2288 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2294 static int messagecount(const char *context, const char *mailbox, const char *folder)
2296 struct odbc_obj *obj = NULL;
2299 SQLHSTMT stmt = NULL;
2304 /* If no mailbox, return immediately */
2305 if (ast_strlen_zero(mailbox))
2308 obj = odbc_request_obj(odbc_database, 0);
2310 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2311 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2312 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2315 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
2316 res = SQLPrepare(stmt, sql, SQL_NTS);
2317 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2318 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", sql);
2319 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2322 res = odbc_smart_execute(obj, stmt);
2323 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2324 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2325 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2328 res = SQLFetch(stmt);
2329 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2330 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2331 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2334 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2335 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2336 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2337 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2340 nummsgs = atoi(rowdata);
2341 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2343 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2347 odbc_release_obj(obj);
2351 static int has_voicemail(const char *mailbox, const char *folder)
2353 char *context, tmp[256];
2354 ast_copy_string(tmp, mailbox, sizeof(tmp));
2355 if ((context = strchr(tmp, '@')))
2358 context = "default";
2360 if (messagecount(context, tmp, folder))
2368 static int count_messages_imap(const char *mailbox, int *newmsgs, int *oldmsgs)
2373 struct ast_vm_user *vmu;
2374 struct vm_state *vms_p;
2385 if(option_debug > 2)
2386 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox);
2387 /* If no mailbox, return immediately */
2388 if (ast_strlen_zero(mailbox))
2390 if (strchr(mailbox, ',')) {
2392 ast_copy_string(tmp, mailbox, sizeof(tmp));
2395 while((cur = strsep(&mb, ", "))) {
2396 if (!ast_strlen_zero(cur)) {
2397 if (count_messages_imap(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2409 ast_copy_string(tmp, mailbox, sizeof(tmp));
2410 context = strchr(tmp, '@');
2416 context = "default";
2417 mailboxnc = (char *)mailbox;
2420 /* We have to get the user before we can open the stream! */
2421 /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2422 vmu = find_user(NULL, context, mailboxnc);
2424 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailboxnc,context);
2427 /* No IMAP account available */
2428 if (vmu->imapuser[0] == '\0') {
2429 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
2434 /* check if someone is accessing this box right now... */
2435 vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
2437 vms_p = get_vm_state_by_mailbox(mailboxnc,1);
2440 if(option_debug > 2)
2441 ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
2442 *newmsgs = vms_p->newmessages;
2443 *oldmsgs = vms_p->oldmessages;
2447 /* add one if not there... */
2448 vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
2450 vms_p = get_vm_state_by_mailbox(mailboxnc,0);
2454 if(option_debug > 2)
2455 ast_log (LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2456 vms_p = (struct vm_state *)malloc(sizeof(struct vm_state));
2457 strcpy(vms_p->imapuser,vmu->imapuser);
2458 ast_copy_string(vms_p->username, mailboxnc, sizeof(vms_p->username)); /* save for access from interactive entry point */
2459 vms_p->mailstream = NIL; /* save for access from interactive entry point */
2460 if(option_debug > 2)
2461 ast_log (LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2463 vms_p->interactive = 0;
2464 /* set mailbox to INBOX! */
2465 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2466 init_vm_state(vms_p);
2467 vmstate_insert(vms_p);
2469 if (!vms_p->mailstream)
2470 ret = init_mailstream(vms_p);
2471 if (!vms_p->mailstream) {
2472 ast_log (LOG_ERROR,"Houston we have a problem - IMAP mailstream is NULL\n");
2475 if (newmsgs && ret==0 && vms_p->updated==1 ) {
2476 pgm = mail_newsearchpgm ();
2477 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2484 vms_p->vmArrayIndex = 0;
2486 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2487 *newmsgs = vms_p->vmArrayIndex;
2488 vms_p->newmessages = vms_p->vmArrayIndex;
2490 if (oldmsgs && ret==0 && vms_p->updated==1 ) {
2491 pgm = mail_newsearchpgm ();
2492 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc);
2499 vms_p->vmArrayIndex = 0;
2501 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2502 *oldmsgs = vms_p->vmArrayIndex;
2503 vms_p->oldmessages = vms_p->vmArrayIndex;
2505 if (vms_p->updated == 1) { /* changes, so we did the searches above */
2507 } else if (vms_p->updated > 1) { /* decrement delay count */
2509 } else { /* no changes, so don't search */
2510 mail_ping(vms_p->mailstream);
2511 /* Keep the old data */
2512 *newmsgs = vms_p->newmessages;
2513 *oldmsgs = vms_p->oldmessages;
2520 /* copy message only used by file storage */
2521 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)
2524 char fromdir[256], todir[256], frompath[256], topath[256];
2525 const char *frombox = mbox(imbox);
2528 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
2530 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
2532 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
2533 make_file(frompath, sizeof(frompath), fromdir, msgnum);
2535 if (vm_lock_path(todir))
2536 return ERROR_LOCK_PATH;
2538 recipmsgnum = last_message_index(recip, todir) + 1;
2539 if (recipmsgnum < recip->maxmsg) {
2540 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
2542 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
2544 ast_unlock_path(todir);
2545 notify_new_message(chan, recip, recipmsgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2550 #ifndef ODBC_STORAGE
2552 static int messagecount(const char *context, const char *mailbox, const char *folder)
2554 return __has_voicemail(context, mailbox, folder, 0);
2558 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
2566 /* If no mailbox, return immediately */
2567 if (ast_strlen_zero(mailbox))
2570 context = "default";
2571 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
2575 while ((de = readdir(dir))) {
2576 if (!strncasecmp(de->d_name, "msg", 3)) {
2580 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
2589 static int has_voicemail(const char *mailbox, const char *folder)
2591 char tmp[256], *tmp2 = tmp, *mbox, *context;
2592 ast_copy_string(tmp, mailbox, sizeof(tmp));
2593 while ((mbox = strsep(&tmp2, ","))) {
2594 if ((context = strchr(mbox, '@')))
2597 context = "default";
2598 if (__has_voicemail(context, mbox, folder, 1))
2605 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
2614 /* If no mailbox, return immediately */
2615 if (ast_strlen_zero(mailbox))
2617 if (strchr(mailbox, ',')) {
2621 ast_copy_string(tmp, mailbox, sizeof(tmp));
2623 while ((cur = strsep(&mb, ", "))) {
2624 if (!ast_strlen_zero(cur)) {
2625 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2637 ast_copy_string(tmp, mailbox, sizeof(tmp));
2638 context = strchr(tmp, '@');
2643 context = "default";
2645 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
2647 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
2653 static void run_externnotify(char *context, char *extension)
2655 char arguments[255];
2656 char ext_context[256] = "";
2657 int newvoicemails = 0, oldvoicemails = 0;
2658 struct ast_smdi_mwi_message *mwi_msg;
2660 if (!ast_strlen_zero(context))
2661 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
2663 ast_copy_string(ext_context, extension, sizeof(ext_context));
2665 if (!strcasecmp(externnotify, "smdi")) {
2666 if (ast_app_has_voicemail(ext_context, NULL))
2667 ast_smdi_mwi_set(smdi_iface, extension);
2669 ast_smdi_mwi_unset(smdi_iface, extension);
2671 if ((mwi_msg = ast_smdi_mwi_message_wait(smdi_iface, SMDI_MWI_WAIT_TIMEOUT))) {
2672 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2673 if (!strncmp(mwi_msg->cause, "INV", 3))
2674 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
2675 else if (!strncmp(mwi_msg->cause, "BLK", 3))
2676 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
2677 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
2678 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
2680 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s on %s\n", extension, smdi_iface->name);
2682 } else if (!ast_strlen_zero(externnotify)) {
2683 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
2684 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
2686 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
2687 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
2688 ast_safe_system(arguments);
2693 struct leave_vm_options {
2695 signed char record_gain;
2698 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
2701 int newmsgs, oldmsgs;
2702 struct vm_state *vms;
2704 char tmptxtfile[256], txtfile[256];
2716 char dir[256], tmpdir[260];
2718 char prefile[256]="";
2719 char tempfile[256]="";
2720 char ext_context[256] = "";
2723 char ecodes[16] = "#";
2724 char tmp[256] = "", *tmpptr;
2725 struct ast_vm_user *vmu;
2726 struct ast_vm_user svm;
2727 const char *category = NULL;
2729 ast_copy_string(tmp, ext, sizeof(tmp));
2731 context = strchr(tmp, '@');
2734 tmpptr = strchr(context, '&');
2736 tmpptr = strchr(ext, '&');
2742 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
2744 if(option_debug > 2)
2745 ast_log(LOG_DEBUG, "Before find_user\n");
2746 if (!(vmu = find_user(&svm, context, ext))) {
2747 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
2748 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
2749 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
2750 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2754 /* Setup pre-file if appropriate */
2755 if (strcmp(vmu->context, "default"))
2756 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
2758 ast_copy_string(ext_context, vmu->context, sizeof(ext_context));
2759 if (ast_test_flag(options, OPT_BUSY_GREETING))
2760 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
2761 else if (ast_test_flag(options, OPT_UNAVAIL_GREETING))
2762 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
2763 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
2764 RETRIEVE(tempfile, -1);
2765 if (ast_fileexists(tempfile, NULL, NULL) > 0)
2766 ast_copy_string(prefile, tempfile, sizeof(prefile));
2767 DISPOSE(tempfile, -1);
2768 /* It's easier just to try to make it than to check for its existence */
2769 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
2770 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
2772 /* Check current or macro-calling context for special extensions */
2773 if (ast_test_flag(vmu, VM_OPERATOR)) {
2774 if (!ast_strlen_zero(vmu->exit)) {
2775 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
2776 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2779 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
2780 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2783 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
2784 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2789 if (!ast_strlen_zero(vmu->exit)) {
2790 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
2791 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2792 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
2793 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2794 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
2795 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2799 /* Play the beginning intro if desired */
2800 if (!ast_strlen_zero(prefile)) {
2801 RETRIEVE(prefile, -1);
2802 if (ast_fileexists(prefile, NULL, NULL) > 0) {
2803 if (ast_streamfile(chan, prefile, chan->language) > -1)
2804 res = ast_waitstream(chan, ecodes);
2806 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
2807 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
2809 DISPOSE(prefile, -1);
2811 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
2813 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2818 /* On a '#' we skip the instructions */
2819 ast_set_flag(options, OPT_SILENT);
2822 if (!res && !ast_test_flag(options, OPT_SILENT)) {
2823 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
2825 ast_set_flag(options, OPT_SILENT);
2830 ast_stopstream(chan);
2831 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2832 other than the operator -- an automated attendant or mailbox login for example */
2834 chan->exten[0] = 'a';
2835 chan->exten[1] = '\0';
2836 if (!ast_strlen_zero(vmu->exit)) {
2837 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2838 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
2839 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2843 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2847 /* Check for a '0' here */
2850 if (ouseexten || ousemacro) {
2851 chan->exten[0] = 'o';
2852 chan->exten[1] = '\0';
2853 if (!ast_strlen_zero(vmu->exit)) {
2854 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
2855 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
2856 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
2858 ast_play_and_wait(chan, "transfer");
2861 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
2867 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2870 /* The meat of recording the message... All the announcements and beeps have been played*/
2871 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2872 if (!ast_strlen_zero(fmt)) {
2876 /* Is ext a mailbox? */
2877 /* must open stream for this user to get info! */
2878 vms = get_vm_state_by_mailbox(ext,0);
2880 if(option_debug > 2)
2881 ast_log(LOG_DEBUG, "Using vm_state, interactive set to %d.\n",vms->interactive);
2882 newmsgs = vms->newmessages++;
2883 oldmsgs = vms->oldmessages;
2885 res = count_messages_imap(ext, &newmsgs, &oldmsgs);
2887 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
2891 /* here is a big difference! We add one to it later */
2892 msgnum = newmsgs + oldmsgs;
2893 ast_log(LOG_NOTICE, "Messagecount set to %d\n",msgnum);
2894 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
2895 /* set variable for compatability */
2896 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
2898 /* Check if mailbox is full */
2899 if (vms->quota_usage >= vms->quota_limit) {
2900 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
2901 ast_play_and_wait(chan, "vm-mailboxfull");
2906 res = ast_streamfile(chan, "beep", chan->language);
2908 res = ast_waitstream(chan, "");
2909 /* play_record_review does recording and verify */
2910 ast_log(LOG_DEBUG, "About to record message in file %s\n",fn);
2911 res = play_record_review(chan, NULL, fn, vmmaxmessage, fmt, 1, vmu, &duration, dir, options->record_gain);
2915 if (res > 0) res = 0;
2917 if (duration < vmminmessage) {
2918 if (option_verbose > 2)
2919 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2922 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
2924 if (count_messages(vmu, dir) >= vmu->maxmsg) {
2925 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2927 res = ast_waitstream(chan, "");
2928 ast_log(LOG_WARNING, "No more messages possible\n");
2929 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2933 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
2934 txtdes = mkstemp(tmptxtfile);
2936 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
2938 res = ast_waitstream(chan, "");
2939 ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
2940 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2944 /* Now play the beep once we have the message number for our next message. */
2946 /* Unless we're *really* silent, try to send the beep */
2947 res = ast_stream_and_wait(chan, "beep", chan->language, "");
2950 /* Store information */
2951 txt = fdopen(txtdes, "w+");
2953 get_date(date, sizeof(date));
2956 "; Message Information file\n"
2975 ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"),
2976 date, (long)time(NULL),
2977 category ? category : "");
2979 ast_log(LOG_WARNING, "Error opening text file for output\n");
2980 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain);
2983 if (duration < vmminmessage) {
2984 if (option_verbose > 2)
2985 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
2986 ast_filedelete(tmptxtfile, NULL);
2989 fprintf(txt, "duration=%d\n", duration);
2991 if (vm_lock_path(dir)) {
2992 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
2994 ast_filedelete(tmptxtfile, NULL);
2996 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
2998 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3000 ast_unlock_path(dir);
3002 msgnum = last_message_index(vmu, dir) + 1;
3003 make_file(fn, sizeof(fn), dir, msgnum);
3005 /* assign a variable with the name of the voicemail file */
3006 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
3008 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
3009 ast_filerename(tmptxtfile, fn, NULL);
3010 rename(tmptxtfile, txtfile);
3012 ast_unlock_path(dir);
3013 /* Are there to be more recipients of this message? */
3015 struct ast_vm_user recipu, *recip;
3016 char *exten, *context;
3018 exten = strsep(&tmpptr, "&");
3019 context = strchr(exten, '@');
3024 if ((recip = find_user(&recipu, context, exten))) {
3025 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt);
3029 if (ast_fileexists(fn, NULL, NULL)) {
3030 STORE(dir, vmu->mailbox, vmu->context, msgnum);
3031 notify_new_message(chan, vmu, msgnum, duration, fmt, chan->cid.cid_num, chan->cid.cid_name);
3032 DISPOSE(dir, msgnum);
3043 if (duration < vmminmessage)
3044 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3045 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3047 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
3049 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
3056 #ifndef IMAP_STORAGE
3057 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
3059 /* we know max messages, so stop process when number is hit */
3065 if (vm_lock_path(dir))
3066 return ERROR_LOCK_PATH;
3068 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
3069 make_file(sfn, sizeof(sfn), dir, x);
3070 if (EXISTS(dir, x, sfn, NULL)) {
3073 make_file(dfn, sizeof(dfn), dir, dest);
3074 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
3080 ast_unlock_path(dir);
3086 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
3089 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
3093 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
3096 /* we must use mbox(x) folder names, and copy the message there */
3098 char *dbox = mbox(box);
3102 /* if save to Old folder, just leave in INBOX */
3103 if (box == 1) return 10;
3104 /* get the real IMAP message number for this message */
3105 sprintf(sequence,"%ld",vms->msgArray[msg]);
3106 if(option_debug > 2)
3107 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,dbox);
3108 res = mail_copy(vms->mailstream,sequence,dbox);
3109 if (res == 1) return 0;
3112 char *dir = vms->curdir;
3113 char *username = vms->username;
3114 char *context = vmu->context;
3118 const char *dbox = mbox(box);
3120 make_file(sfn, sizeof(sfn), dir, msg);
3121 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
3123 if (vm_lock_path(ddir))
3124 return ERROR_LOCK_PATH;
3126 x = last_message_index(vmu, ddir) + 1;
3127 make_file(dfn, sizeof(dfn), ddir, x);
3129 if (x >= vmu->maxmsg) {
3130 ast_unlock_path(ddir);
3133 if (strcmp(sfn, dfn)) {
3134 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
3136 ast_unlock_path(ddir);
3141 static int adsi_logo(unsigned char *buf)
3144 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
3145 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002 LSS, Inc.", "");
3149 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
3151 unsigned char buf[256];
3157 bytes += ast_adsi_data_mode(buf + bytes);
3158 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3161 bytes += adsi_logo(buf);
3162 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3164 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
3166 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3167 bytes += ast_adsi_data_mode(buf + bytes);
3168 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3170 if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
3172 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
3173 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3174 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3175 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3176 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3183 bytes += ast_adsi_logo(buf);
3184 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
3185 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
3186 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3187 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3190 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
3191 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
3192 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
3193 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
3194 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
3195 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
3196 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3199 /* Add another dot */
3201 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
3202 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3204 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3205 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3209 /* These buttons we load but don't use yet */
3210 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
3211 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
3212 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
3213 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
3214 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
3215 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
3216 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3219 /* Add another dot */
3221 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
3222 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3223 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3228 snprintf(num, sizeof(num), "%d", x);
3229 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
3231 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
3232 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3235 /* Add another dot */
3237 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
3238 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3239 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3242 if (ast_adsi_end_download(chan)) {
3244 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
3245 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
3246 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3247 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3248 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3252 bytes += ast_adsi_download_disconnect(buf + bytes);
3253 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3254 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
3256 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
3261 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
3262 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3264 ast_log(LOG_DEBUG, "Restarting session...\n");
3267 /* Load the session now */
3268 if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
3270 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
3272 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
3274 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3278 static void adsi_begin(struct ast_channel *chan, int *useadsi)
3281 if (!ast_adsi_available(chan))
3283 x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
3287 if (adsi_load_vmail(chan, useadsi)) {
3288 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
3295 static void adsi_login(struct ast_channel *chan)
3297 unsigned char buf[256];
3299 unsigned char keys[8];
3301 if (!ast_adsi_available(chan))
3306 /* Set one key for next */
3307 keys[3] = ADSI_KEY_APPS + 3;
3309 bytes += adsi_logo(buf + bytes);
3310 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
3311 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
3312 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3313 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
3314 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
3315 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
3316 bytes += ast_adsi_set_keys(buf + bytes, keys);
3317 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3318 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3321 static void adsi_password(struct ast_channel *chan)
3323 unsigned char buf[256];
3325 unsigned char keys[8];
3327 if (!ast_adsi_available(chan))
3332 /* Set one key for next */
3333 keys[3] = ADSI_KEY_APPS + 3;
3335 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3336 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
3337 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
3338 bytes += ast_adsi_set_keys(buf + bytes, keys);
3339 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3340 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3343 static void adsi_folders(struct ast_channel *chan, int start, char *label)
3345 unsigned char buf[256];
3347 unsigned char keys[8];
3350 if (!ast_adsi_available(chan))
3354 y = ADSI_KEY_APPS + 12 + start + x;
3355 if (y > ADSI_KEY_APPS + 12 + 4)
3357 keys[x] = ADSI_KEY_SKT | y;
3359 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
3363 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
3364 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
3365 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3366 bytes += ast_adsi_set_keys(buf + bytes, keys);
3367 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3369 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3372 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
3375 unsigned char buf[256];
3376 char buf1[256], buf2[256];
3382 char datetime[21]="";
3385 unsigned char keys[8];
3389 if (!ast_adsi_available(chan))
3392 /* Retrieve important info */
3393 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
3394 f = fopen(fn2, "r");
3397 fgets((char *)buf, sizeof(buf), f);
3400 stringp = (char *)buf;
3401 strsep(&stringp, "=");
3402 val = strsep(&stringp, "=");
3403 if (!ast_strlen_zero(val)) {
3404 if (!strcmp((char *)buf, "callerid"))
3405 ast_copy_string(cid, val, sizeof(cid));
3406 if (!strcmp((char *)buf, "origdate"))
3407 ast_copy_string(datetime, val, sizeof(datetime));
3413 /* New meaning for keys */
3415 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
3420 /* No prev key, provide "Folder" instead */
3421 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3423 if (vms->curmsg >= vms->lastmsg) {
3424 /* If last message ... */
3426 /* but not only message, provide "Folder" instead */
3427 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
3428 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3431 /* Otherwise if only message, leave blank */
3436 if (!ast_strlen_zero(cid)) {
3437 ast_callerid_parse(cid, &name, &num);
3441 name = "Unknown Caller";
3443 /* If deleted, show "undeleted" */
3445 if (vms->deleted[vms->curmsg])
3446 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
3449 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
3450 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
3451 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
3452 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
3454 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
3455 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
3456 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
3457 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
3458 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3459 bytes += ast_adsi_set_keys(buf + bytes, keys);
3460 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3462 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3465 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
3468 unsigned char buf[256];
3469 unsigned char keys[8];
3473 if (!ast_adsi_available(chan))
3476 /* New meaning for keys */