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>
25
* \extref Unixodbc - http://www.unixodbc.org
26
* \extref A source distribution of University of Washington's IMAP
27
c-client (http://www.washington.edu/imap/
31
* \note For information about voicemail IMAP storage, read doc/imapstorage.txt
32
* \ingroup applications
33
* \note This module requires res_adsi to load. This needs to be optional
38
* \note This file is now almost impossible to work with, due to all \#ifdefs.
39
* Feels like the database code before realtime. Someone - please come up
40
* with a plan to clean this up.
44
<category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_voicemail.so apps/app_directory.o apps/app_directory.so">
45
<member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
46
<conflict>ODBC_STORAGE</conflict>
47
<conflict>IMAP_STORAGE</conflict>
48
<defaultenabled>yes</defaultenabled>
50
<member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
51
<depend>generic_odbc</depend>
53
<conflict>IMAP_STORAGE</conflict>
54
<conflict>FILE_STORAGE</conflict>
55
<defaultenabled>no</defaultenabled>
57
<member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
58
<depend>imap_tk</depend>
59
<conflict>ODBC_STORAGE</conflict>
60
<conflict>FILE_STORAGE</conflict>
62
<defaultenabled>no</defaultenabled>
73
#ifdef USE_SYSTEM_IMAP
74
#include <imap/c-client.h>
75
#include <imap/imap4r1.h>
76
#include <imap/linkage.h>
77
#elif defined (USE_SYSTEM_CCLIENT)
78
#include <c-client/c-client.h>
79
#include <c-client/imap4r1.h>
80
#include <c-client/linkage.h>
88
ASTERISK_FILE_VERSION(__FILE__, "$Revision: 236670 $")
90
#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
97
#include "asterisk/logger.h"
98
#include "asterisk/lock.h"
99
#include "asterisk/file.h"
100
#include "asterisk/channel.h"
101
#include "asterisk/pbx.h"
102
#include "asterisk/config.h"
103
#include "asterisk/say.h"
104
#include "asterisk/module.h"
105
#include "asterisk/adsi.h"
106
#include "asterisk/app.h"
107
#include "asterisk/manager.h"
108
#include "asterisk/dsp.h"
109
#include "asterisk/localtime.h"
110
#include "asterisk/cli.h"
111
#include "asterisk/utils.h"
112
#include "asterisk/stringfields.h"
113
#include "asterisk/smdi.h"
114
#include "asterisk/event.h"
115
#include "asterisk/taskprocessor.h"
118
#include "asterisk/res_odbc.h"
122
#include "asterisk/threadstorage.h"
126
<application name="VoiceMail" language="en_US">
128
Leave a Voicemail message.
131
<parameter name="mailboxs" argsep="&" required="true">
132
<argument name="mailbox1" argsep="@" required="true">
133
<argument name="mailbox" required="true" />
134
<argument name="context" />
136
<argument name="mailbox2" argsep="@" multiple="true">
137
<argument name="mailbox" required="true" />
138
<argument name="context" />
141
<parameter name="options">
144
<para>Play the <literal>busy</literal> greeting to the calling party.</para>
147
<argument name="c" />
148
<para>Accept digits for a new extension in context <replaceable>c</replaceable>,
149
if played during the greeting. Context defaults to the current context.</para>
152
<argument name="#" required="true" />
153
<para>Use the specified amount of gain when recording the voicemail
154
message. The units are whole-number decibels (dB). Only works on supported
155
technologies, which is DAHDI only.</para>
158
<para>Skip the playback of instructions for leaving a message to the
159
calling party.</para>
162
<para>Play the <literal>unavailable</literal> greeting.</para>
165
<para>Mark message as <literal>URGENT</literal>.</para>
168
<para>Mark message as <literal>PRIORITY</literal>.</para>
174
<para>This application allows the calling party to leave a message for the specified
175
list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
176
the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
178
<para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
181
<para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
184
<para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
187
<para>This application will set the following channel variable upon completion:</para>
189
<variable name="VMSTATUS">
190
<para>This indicates the status of the execution of the VoiceMail application.</para>
191
<value name="SUCCESS" />
192
<value name="USEREXIT" />
193
<value name="FAILED" />
198
<application name="VoiceMailMain" language="en_US">
200
Check Voicemail messages.
203
<parameter name="mailbox" required="true" argsep="@">
204
<argument name="mailbox" />
205
<argument name="context" />
207
<parameter name="options">
210
<para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
211
the mailbox that is entered by the caller.</para>
214
<argument name="#" required="true" />
215
<para>Use the specified amount of gain when recording a voicemail message.
216
The units are whole-number decibels (dB).</para>
219
<para>Skip checking the passcode for the mailbox.</para>
222
<argument name="folder" required="true" />
223
<para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
224
Defaults to <literal>0</literal> (INBOX).</para>
226
<enum name="0"><para>INBOX</para></enum>
227
<enum name="1"><para>Old</para></enum>
228
<enum name="2"><para>Work</para></enum>
229
<enum name="3"><para>Family</para></enum>
230
<enum name="4"><para>Friends</para></enum>
231
<enum name="5"><para>Cust1</para></enum>
232
<enum name="6"><para>Cust2</para></enum>
233
<enum name="7"><para>Cust3</para></enum>
234
<enum name="8"><para>Cust4</para></enum>
235
<enum name="9"><para>Cust5</para></enum>
242
<para>This application allows the calling party to check voicemail messages. A specific
243
<replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
244
may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
245
be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
246
<literal>default</literal> context will be used.</para>
249
<application name="MailboxExists" language="en_US">
251
Check to see if Voicemail mailbox exists.
254
<parameter name="mailbox" required="true" argsep="@">
255
<argument name="mailbox" required="true" />
256
<argument name="context" />
258
<parameter name="options">
259
<para>None options.</para>
263
<para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
264
<replaceable>context</replaceable> is specified, the <literal>default</literal> context
266
<para>This application will set the following channel variable upon completion:</para>
268
<variable name="VMBOXEXISTSSTATUS">
269
<para>This will contain the status of the execution of the MailboxExists application.
270
Possible values include:</para>
271
<value name="SUCCESS" />
272
<value name="FAILED" />
277
<application name="VMAuthenticate" language="en_US">
279
Authenticate with Voicemail passwords.
282
<parameter name="mailbox" required="true" argsep="@">
283
<argument name="mailbox" />
284
<argument name="context" />
286
<parameter name="options">
289
<para>Skip playing the initial prompts.</para>
295
<para>This application behaves the same way as the Authenticate application, but the passwords
296
are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
297
specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
298
is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
302
<function name="MAILBOX_EXISTS" language="en_US">
304
Tell if a mailbox is configured.
307
<parameter name="mailbox" required="true" />
308
<parameter name="context" />
311
<para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
312
If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
319
static char imapserver[48];
320
static char imapport[8];
321
static char imapflags[128];
322
static char imapfolder[64];
323
static char imapparentfolder[64] = "\0";
324
static char greetingfolder[64];
325
static char authuser[32];
326
static char authpassword[42];
327
static int imapversion = 1;
329
static int expungeonhangup = 1;
330
static int imapgreetings = 0;
331
static char delimiter = '\0';
336
AST_THREADSTORAGE(ts_vmstate);
338
/* Forward declarations for IMAP */
339
static int init_mailstream(struct vm_state *vms, int box);
340
static void write_file(char *filename, char *buffer, unsigned long len);
341
static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
342
static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
343
static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
344
static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
345
static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
346
static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
347
static void vmstate_insert(struct vm_state *vms);
348
static void vmstate_delete(struct vm_state *vms);
349
static void set_update(MAILSTREAM * stream);
350
static void init_vm_state(struct vm_state *vms);
351
static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
352
static void get_mailbox_delimiter(MAILSTREAM *stream);
353
static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
354
static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
355
static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag);
356
static void update_messages_by_imapuser(const char *user, unsigned long number);
357
static int vm_delete(char *file);
359
static int imap_remove_file (char *dir, int msgnum);
360
static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
361
static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
362
static void check_quota(struct vm_state *vms, char *mailbox);
363
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
365
struct vm_state *vms;
366
AST_LIST_ENTRY(vmstate) list;
369
static AST_LIST_HEAD_STATIC(vmstates, vmstate);
373
#define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
375
#define COMMAND_TIMEOUT 5000
376
/* Don't modify these here; set your umask at runtime instead */
377
#define VOICEMAIL_DIR_MODE 0777
378
#define VOICEMAIL_FILE_MODE 0666
379
#define CHUNKSIZE 65536
381
#define VOICEMAIL_CONFIG "voicemail.conf"
382
#define ASTERISK_USERNAME "asterisk"
384
/* Define fast-forward, pause, restart, and reverse keys
385
while listening to a voicemail message - these are
386
strings, not characters */
387
#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
388
#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
389
#define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
390
#define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
391
#define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
392
#define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
394
/* Default mail command to mail voicemail. Change it with the
395
mailcmd= command in voicemail.conf */
396
#define SENDMAIL "/usr/sbin/sendmail -t"
398
#define INTRO "vm-intro"
401
#define MAXMSGLIMIT 9999
403
#define MINPASSWORD 0 /*!< Default minimum mailbox password length */
405
#define BASELINELEN 72
406
#define BASEMAXINLINE 256
409
#define MAX_DATETIME_FORMAT 512
410
#define MAX_NUM_CID_CONTEXTS 10
412
#define VM_REVIEW (1 << 0) /*!< After recording, permit the caller to review the recording before saving */
413
#define VM_OPERATOR (1 << 1) /*!< Allow 0 to be pressed to go to 'o' extension */
414
#define VM_SAYCID (1 << 2) /*!< Repeat the CallerID info during envelope playback */
415
#define VM_SVMAIL (1 << 3) /*!< Allow the user to compose a new VM from within VoicemailMain */
416
#define VM_ENVELOPE (1 << 4) /*!< Play the envelope information (who-from, time received, etc.) */
417
#define VM_SAYDURATION (1 << 5) /*!< Play the length of the message during envelope playback */
418
#define VM_SKIPAFTERCMD (1 << 6) /*!< After deletion, assume caller wants to go to the next message */
419
#define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
420
#define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
421
#define VM_PBXSKIP (1 << 9) /*!< Skip the [PBX] preamble in the Subject line of emails */
422
#define VM_DIRECFORWARD (1 << 10) /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
423
#define VM_ATTACH (1 << 11) /*!< Attach message to voicemail notifications? */
424
#define VM_DELETE (1 << 12) /*!< Delete message after sending notification */
425
#define VM_ALLOCED (1 << 13) /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
426
#define VM_SEARCH (1 << 14) /*!< Search all contexts for a matching mailbox */
427
#define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
428
#define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
429
#define VM_MESSAGEWRAP (1 << 17) /*!< Wrap around from the last message to the first, and vice-versa */
430
#define VM_FWDURGAUTO (1 << 18) /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
431
#define ERROR_LOCK_PATH -100
444
OPT_SILENT = (1 << 0),
445
OPT_BUSY_GREETING = (1 << 1),
446
OPT_UNAVAIL_GREETING = (1 << 2),
447
OPT_RECORDGAIN = (1 << 3),
448
OPT_PREPEND_MAILBOX = (1 << 4),
449
OPT_AUTOPLAY = (1 << 6),
450
OPT_DTMFEXIT = (1 << 7),
451
OPT_MESSAGE_Urgent = (1 << 8),
452
OPT_MESSAGE_PRIORITY = (1 << 9)
456
OPT_ARG_RECORDGAIN = 0,
457
OPT_ARG_PLAYFOLDER = 1,
458
OPT_ARG_DTMFEXIT = 2,
459
/* This *must* be the last value in this enum! */
460
OPT_ARG_ARRAY_SIZE = 3,
463
AST_APP_OPTIONS(vm_app_options, {
464
AST_APP_OPTION('s', OPT_SILENT),
465
AST_APP_OPTION('b', OPT_BUSY_GREETING),
466
AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
467
AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
468
AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
469
AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
470
AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
471
AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
472
AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY)
475
static int load_config(int reload);
477
/*! \page vmlang Voicemail Language Syntaxes Supported
479
\par Syntaxes supported, not really language codes.
486
\arg \b pt - Portuguese
487
\arg \b pt_BR - Portuguese (Brazil)
489
\arg \b no - Norwegian
491
\arg \b tw - Chinese (Taiwan)
492
\arg \b ua - Ukrainian
494
German requires the following additional soundfile:
495
\arg \b 1F einE (feminine)
497
Spanish requires the following additional soundfile:
498
\arg \b 1M un (masculine)
500
Dutch, Portuguese & Spanish require the following additional soundfiles:
501
\arg \b vm-INBOXs singular of 'new'
502
\arg \b vm-Olds singular of 'old/heard/read'
505
\arg \b vm-INBOX nieuwe (nl)
506
\arg \b vm-Old oude (nl)
509
\arg \b vm-new-a 'new', feminine singular accusative
510
\arg \b vm-new-e 'new', feminine plural accusative
511
\arg \b vm-new-ych 'new', feminine plural genitive
512
\arg \b vm-old-a 'old', feminine singular accusative
513
\arg \b vm-old-e 'old', feminine plural accusative
514
\arg \b vm-old-ych 'old', feminine plural genitive
515
\arg \b digits/1-a 'one', not always same as 'digits/1'
516
\arg \b digits/2-ie 'two', not always same as 'digits/2'
519
\arg \b vm-nytt singular of 'new'
520
\arg \b vm-nya plural of 'new'
521
\arg \b vm-gammalt singular of 'old'
522
\arg \b vm-gamla plural of 'old'
523
\arg \b digits/ett 'one', not always same as 'digits/1'
526
\arg \b vm-ny singular of 'new'
527
\arg \b vm-nye plural of 'new'
528
\arg \b vm-gammel singular of 'old'
529
\arg \b vm-gamle plural of 'old'
537
Italian requires the following additional soundfile:
541
\arg \b vm-nuovi new plural
542
\arg \b vm-vecchio old
543
\arg \b vm-vecchi old plural
545
Chinese (Taiwan) requires the following additional soundfile:
546
\arg \b vm-tong A class-word for call (tong1)
547
\arg \b vm-ri A class-word for day (ri4)
548
\arg \b vm-you You (ni3)
549
\arg \b vm-haveno Have no (mei2 you3)
550
\arg \b vm-have Have (you3)
551
\arg \b vm-listen To listen (yao4 ting1)
554
\note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
555
spelled among others when you have to change folder. For the above reasons, vm-INBOX
556
and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
565
unsigned char iobuf[BASEMAXINLINE];
568
/*! Structure for linked list of users
569
* Use ast_vm_user_destroy() to free one of these structures. */
571
char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
572
char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
573
char password[80]; /*!< Secret pin code, numbers only */
574
char fullname[80]; /*!< Full name, for directory app */
575
char email[80]; /*!< E-mail address */
576
char *emailsubject; /*!< E-mail subject */
577
char *emailbody; /*!< E-mail body */
578
char pager[80]; /*!< E-mail address to pager (no attachment) */
579
char serveremail[80]; /*!< From: Mail address */
580
char mailcmd[160]; /*!< Configurable mail command */
581
char language[MAX_LANGUAGE]; /*!< Config: Language setting */
582
char zonetag[80]; /*!< Time zone */
585
char uniqueid[80]; /*!< Unique integer identifier */
587
char attachfmt[20]; /*!< Attachment format */
588
unsigned int flags; /*!< VM_ flags */
590
int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
591
int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
592
int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
594
char imapuser[80]; /*!< IMAP server login */
595
char imappassword[80]; /*!< IMAP server password if authpassword not defined */
596
char imapvmshareid[80]; /*!< Shared mailbox ID to use rather than the dialed one */
597
int imapversion; /*!< If configuration changes, use the new values */
599
double volgain; /*!< Volume gain for voicemails sent via email */
600
AST_LIST_ENTRY(ast_vm_user) list;
603
/*! Voicemail time zones */
605
AST_LIST_ENTRY(vm_zone) list;
608
char msg_format[512];
611
#define VMSTATE_MAX_MSG_ARRAY 256
613
/*! Voicemail mailbox state */
618
char curdir[PATH_MAX];
619
char vmbox[PATH_MAX];
621
char intro[PATH_MAX];
633
int updated; /*!< decremented on each mail check until 1 -allows delay */
634
long msgArray[VMSTATE_MAX_MSG_ARRAY];
635
MAILSTREAM *mailstream;
637
char imapuser[80]; /*!< IMAP server login */
640
char introfn[PATH_MAX]; /*!< Name of prepended file */
641
unsigned int quota_limit;
642
unsigned int quota_usage;
643
struct vm_state *persist_vms;
648
static char odbc_database[80];
649
static char odbc_table[80];
650
#define RETRIEVE(a,b,c,d) retrieve_file(a,b)
651
#define DISPOSE(a,b) remove_file(a,b)
652
#define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
653
#define EXISTS(a,b,c,d) (message_exists(a,b))
654
#define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
655
#define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
656
#define DELETE(a,b,c,d) (delete_file(a,b))
659
#define DISPOSE(a,b) (imap_remove_file(a,b))
660
#define STORE(a,b,c,d,e,f,g,h,i,j) (imap_store_file(a,b,c,d,e,f,g,h,i,j))
661
#define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
662
#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
663
#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
664
#define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
665
#define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
667
#define RETRIEVE(a,b,c,d)
669
#define STORE(a,b,c,d,e,f,g,h,i,j)
670
#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
671
#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
672
#define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
673
#define DELETE(a,b,c,d) (vm_delete(c))
677
static char VM_SPOOL_DIR[PATH_MAX];
679
static char ext_pass_cmd[128];
680
static char ext_pass_check_cmd[128];
684
#define PWDCHANGE_INTERNAL (1 << 1)
685
#define PWDCHANGE_EXTERNAL (1 << 2)
686
static int pwdchange = PWDCHANGE_INTERNAL;
689
#define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
692
# define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
694
# define tdesc "Comedian Mail (Voicemail System)"
698
static char userscontext[AST_MAX_EXTENSION] = "default";
700
static char *addesc = "Comedian Mail";
702
/* Leave a message */
703
static char *app = "VoiceMail";
705
/* Check mail, control, etc */
706
static char *app2 = "VoiceMailMain";
708
static char *app3 = "MailboxExists";
709
static char *app4 = "VMAuthenticate";
711
static AST_LIST_HEAD_STATIC(users, ast_vm_user);
712
static AST_LIST_HEAD_STATIC(zones, vm_zone);
713
static char zonetag[80];
714
static int maxsilence;
716
static int maxdeletedmsg;
717
static int silencethreshold = 128;
718
static char serveremail[80];
719
static char mailcmd[160]; /* Configurable mail cmd */
720
static char externnotify[160];
721
static struct ast_smdi_interface *smdi_iface = NULL;
722
static char vmfmts[80];
723
static double volgain;
724
static int vmminsecs;
725
static int vmmaxsecs;
728
static int maxlogins;
729
static int minpassword;
731
/*! Poll mailboxes for changes since there is something external to
732
* app_voicemail that may change them. */
733
static unsigned int poll_mailboxes;
735
/*! Polling frequency */
736
static unsigned int poll_freq;
737
/*! By default, poll every 30 seconds */
738
#define DEFAULT_POLL_FREQ 30
740
AST_MUTEX_DEFINE_STATIC(poll_lock);
741
static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
742
static pthread_t poll_thread = AST_PTHREADT_NULL;
743
static unsigned char poll_thread_run;
745
/*! Subscription to ... MWI event subscriptions */
746
static struct ast_event_sub *mwi_sub_sub;
747
/*! Subscription to ... MWI event un-subscriptions */
748
static struct ast_event_sub *mwi_unsub_sub;
751
* \brief An MWI subscription
753
* This is so we can keep track of which mailboxes are subscribed to.
754
* This way, we know which mailboxes to poll when the pollmailboxes
755
* option is being used.
758
AST_RWLIST_ENTRY(mwi_sub) entry;
766
struct mwi_sub_task {
772
static struct ast_taskprocessor *mwi_subscription_tps;
774
static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
776
/* custom audio control prompts for voicemail playback */
777
static char listen_control_forward_key[12];
778
static char listen_control_reverse_key[12];
779
static char listen_control_pause_key[12];
780
static char listen_control_restart_key[12];
781
static char listen_control_stop_key[12];
783
/* custom password sounds */
784
static char vm_password[80] = "vm-password";
785
static char vm_newpassword[80] = "vm-newpassword";
786
static char vm_passchanged[80] = "vm-passchanged";
787
static char vm_reenterpassword[80] = "vm-reenterpassword";
788
static char vm_mismatch[80] = "vm-mismatch";
789
static char vm_invalid_password[80] = "vm-invalid-password";
790
static char vm_pls_try_again[80] = "vm-pls-try-again";
792
static struct ast_flags globalflags = {0};
794
static int saydurationminfo;
796
static char dialcontext[AST_MAX_CONTEXT] = "";
797
static char callcontext[AST_MAX_CONTEXT] = "";
798
static char exitcontext[AST_MAX_CONTEXT] = "";
800
static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
803
static char *emailbody = NULL;
804
static char *emailsubject = NULL;
805
static char *pagerbody = NULL;
806
static char *pagersubject = NULL;
807
static char fromstring[100];
808
static char pagerfromstring[100];
809
static char charset[32] = "ISO-8859-1";
811
static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
812
static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
813
static int adsiver = 1;
814
static char emaildateformat[32] = "%A, %B %d, %Y at %r";
816
/* Forward declarations - generic */
817
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
818
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);
819
static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
820
static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
821
char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
822
signed char record_gain, struct vm_state *vms, char *flag);
823
static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
824
static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
825
static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
826
static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag);
827
static void apply_options(struct ast_vm_user *vmu, const char *options);
828
static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum);
829
static int is_valid_dtmf(const char *key);
831
#if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
832
static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
836
* \brief Strips control and non 7-bit clean characters from input string.
838
* \note To map control and none 7-bit characters to a 7-bit clean characters
839
* please use ast_str_encode_mine().
841
static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
844
for (; *input; input++) {
849
if (bufptr == buf + buflen - 1) {
859
* \brief Sets default voicemail system options to a voicemail user.
861
* This applies select global settings to a newly created (dynamic) instance of a voicemail user.
862
* - all the globalflags
863
* - the saydurationminfo
867
* - vmmaxsecs, vmmaxmsg, maxdeletedmsg
870
static void populate_defaults(struct ast_vm_user *vmu)
872
ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
873
if (saydurationminfo)
874
vmu->saydurationm = saydurationminfo;
875
ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
876
ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
877
ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
878
ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
880
vmu->maxsecs = vmmaxsecs;
882
vmu->maxmsg = maxmsg;
884
vmu->maxdeletedmsg = maxdeletedmsg;
885
vmu->volgain = volgain;
886
vmu->emailsubject = NULL;
887
vmu->emailbody = NULL;
891
* \brief Sets a a specific property value.
892
* \param vmu The voicemail user object to work with.
893
* \param var The name of the property to be set.
894
* \param value The value to be set to the property.
896
* The property name must be one of the understood properties. See the source for details.
898
static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
901
if (!strcasecmp(var, "attach")) {
902
ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
903
} else if (!strcasecmp(var, "attachfmt")) {
904
ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
905
} else if (!strcasecmp(var, "serveremail")) {
906
ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
907
} else if (!strcasecmp(var, "language")) {
908
ast_copy_string(vmu->language, value, sizeof(vmu->language));
909
} else if (!strcasecmp(var, "tz")) {
910
ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
912
} else if (!strcasecmp(var, "imapuser")) {
913
ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
914
vmu->imapversion = imapversion;
915
} else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
916
ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
917
vmu->imapversion = imapversion;
918
} else if (!strcasecmp(var, "imapvmshareid")) {
919
ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
920
vmu->imapversion = imapversion;
922
} else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
923
ast_set2_flag(vmu, ast_true(value), VM_DELETE);
924
} else if (!strcasecmp(var, "saycid")){
925
ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
926
} else if (!strcasecmp(var,"sendvoicemail")){
927
ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
928
} else if (!strcasecmp(var, "review")){
929
ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
930
} else if (!strcasecmp(var, "tempgreetwarn")){
931
ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
932
} else if (!strcasecmp(var, "messagewrap")){
933
ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);
934
} else if (!strcasecmp(var, "operator")) {
935
ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
936
} else if (!strcasecmp(var, "envelope")){
937
ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
938
} else if (!strcasecmp(var, "moveheard")){
939
ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
940
} else if (!strcasecmp(var, "sayduration")){
941
ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
942
} else if (!strcasecmp(var, "saydurationm")){
943
if (sscanf(value, "%30d", &x) == 1) {
944
vmu->saydurationm = x;
946
ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
948
} else if (!strcasecmp(var, "forcename")){
949
ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
950
} else if (!strcasecmp(var, "forcegreetings")){
951
ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
952
} else if (!strcasecmp(var, "callback")) {
953
ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
954
} else if (!strcasecmp(var, "dialout")) {
955
ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
956
} else if (!strcasecmp(var, "exitcontext")) {
957
ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
958
} else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
959
vmu->maxsecs = atoi(value);
960
if (vmu->maxsecs <= 0) {
961
ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
962
vmu->maxsecs = vmmaxsecs;
964
vmu->maxsecs = atoi(value);
966
if (!strcasecmp(var, "maxmessage"))
967
ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
968
} else if (!strcasecmp(var, "maxmsg")) {
969
vmu->maxmsg = atoi(value);
970
if (vmu->maxmsg <= 0) {
971
ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
972
vmu->maxmsg = MAXMSG;
973
} else if (vmu->maxmsg > MAXMSGLIMIT) {
974
ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
975
vmu->maxmsg = MAXMSGLIMIT;
977
} else if (!strcasecmp(var, "backupdeleted")) {
978
if (sscanf(value, "%30d", &x) == 1)
979
vmu->maxdeletedmsg = x;
980
else if (ast_true(value))
981
vmu->maxdeletedmsg = MAXMSG;
983
vmu->maxdeletedmsg = 0;
985
if (vmu->maxdeletedmsg < 0) {
986
ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
987
vmu->maxdeletedmsg = MAXMSG;
988
} else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
989
ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
990
vmu->maxdeletedmsg = MAXMSGLIMIT;
992
} else if (!strcasecmp(var, "volgain")) {
993
sscanf(value, "%30lf", &vmu->volgain);
994
} else if (!strcasecmp(var, "options")) {
995
apply_options(vmu, value);
999
static char *vm_check_password_shell(char *command, char *buf, size_t len)
1001
int fds[2], pid = 0;
1003
memset(buf, 0, len);
1006
snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
1009
pid = ast_safe_fork(0);
1015
snprintf(buf, len, "FAILURE: Fork failed");
1019
if (read(fds[0], buf, len) < 0) {
1020
ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
1025
AST_DECLARE_APP_ARGS(arg,
1028
char *mycmd = ast_strdupa(command);
1031
dup2(fds[1], STDOUT_FILENO);
1033
ast_close_fds_above_n(STDOUT_FILENO);
1035
AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
1037
execv(arg.v[0], arg.v);
1038
printf("FAILURE: %s", strerror(errno));
1046
* \brief Check that password meets minimum required length
1047
* \param vmu The voicemail user to change the password for.
1048
* \param password The password string to check
1050
* \return zero on ok, 1 on not ok.
1052
static int check_password(struct ast_vm_user *vmu, char *password)
1054
/* check minimum length */
1055
if (strlen(password) < minpassword)
1057
if (!ast_strlen_zero(ext_pass_check_cmd)) {
1058
char cmd[255], buf[255];
1060
ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
1062
snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
1063
if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
1064
ast_debug(5, "Result: %s\n", buf);
1065
if (!strncasecmp(buf, "VALID", 5)) {
1066
ast_debug(3, "Passed password check: '%s'\n", buf);
1068
} else if (!strncasecmp(buf, "FAILURE", 7)) {
1069
ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
1072
ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
1081
* \brief Performs a change of the voicemail passowrd in the realtime engine.
1082
* \param vmu The voicemail user to change the password for.
1083
* \param password The new value to be set to the password for this user.
1085
* This only works if there is a realtime engine configured.
1086
* This is called from the (top level) vm_change_password.
1088
* \return zero on success, -1 on error.
1090
static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
1093
if (!strcmp(vmu->password, password)) {
1094
/* No change (but an update would return 0 rows updated, so we opt out here) */
1098
if (strlen(password) > 10) {
1099
ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
1101
if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
1102
ast_copy_string(vmu->password, password, sizeof(vmu->password));
1109
* \brief Destructively Parse options and apply.
1111
static void apply_options(struct ast_vm_user *vmu, const char *options)
1116
stringp = ast_strdupa(options);
1117
while ((s = strsep(&stringp, "|"))) {
1119
if ((var = strsep(&value, "=")) && value) {
1120
apply_option(vmu, var, value);
1126
* \brief Loads the options specific to a voicemail user.
1128
* This is called when a vm_user structure is being set up, such as from load_options.
1130
static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
1132
for (; var; var = var->next) {
1133
if (!strcasecmp(var->name, "vmsecret")) {
1134
ast_copy_string(retval->password, var->value, sizeof(retval->password));
1135
} else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
1136
if (ast_strlen_zero(retval->password))
1137
ast_copy_string(retval->password, var->value, sizeof(retval->password));
1138
} else if (!strcasecmp(var->name, "uniqueid")) {
1139
ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
1140
} else if (!strcasecmp(var->name, "pager")) {
1141
ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
1142
} else if (!strcasecmp(var->name, "email")) {
1143
ast_copy_string(retval->email, var->value, sizeof(retval->email));
1144
} else if (!strcasecmp(var->name, "fullname")) {
1145
ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
1146
} else if (!strcasecmp(var->name, "context")) {
1147
ast_copy_string(retval->context, var->value, sizeof(retval->context));
1148
} else if (!strcasecmp(var->name, "emailsubject")) {
1149
retval->emailsubject = ast_strdup(var->value);
1150
} else if (!strcasecmp(var->name, "emailbody")) {
1151
retval->emailbody = ast_strdup(var->value);
1153
} else if (!strcasecmp(var->name, "imapuser")) {
1154
ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
1155
retval->imapversion = imapversion;
1156
} else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
1157
ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
1158
retval->imapversion = imapversion;
1159
} else if (!strcasecmp(var->name, "imapvmshareid")) {
1160
ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
1161
retval->imapversion = imapversion;
1164
apply_option(retval, var->name, var->value);
1169
* \brief Determines if a DTMF key entered is valid.
1170
* \param key The character to be compared. expects a single character. Though is capable of handling a string, this is internally copies using ast_strdupa.
1172
* Tests the character entered against the set of valid DTMF characters.
1173
* \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1175
static int is_valid_dtmf(const char *key)
1178
char *local_key = ast_strdupa(key);
1180
for (i = 0; i < strlen(key); ++i) {
1181
if (!strchr(VALID_DTMF, *local_key)) {
1182
ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1191
* \brief Finds a voicemail user from the realtime engine.
1196
* This is called as a fall through case when the normal find_user() was not able to find a user. That is, the default it so look in the usual voicemail users file first.
1198
* \return The ast_vm_user structure for the user that was found.
1200
static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1202
struct ast_variable *var;
1203
struct ast_vm_user *retval;
1205
if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1207
ast_set_flag(retval, VM_ALLOCED);
1209
memset(retval, 0, sizeof(*retval));
1211
ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1212
populate_defaults(retval);
1213
if (!context && ast_test_flag((&globalflags), VM_SEARCH))
1214
var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1216
var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1218
apply_options_full(retval, var);
1219
ast_variables_destroy(var);
1230
* \brief Finds a voicemail user from the users file or the realtime engine.
1235
* \return The ast_vm_user structure for the user that was found.
1237
static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1239
/* This function could be made to generate one from a database, too */
1240
struct ast_vm_user *vmu=NULL, *cur;
1241
AST_LIST_LOCK(&users);
1243
if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
1244
context = "default";
1246
AST_LIST_TRAVERSE(&users, cur, list) {
1248
if (cur->imapversion != imapversion) {
1252
if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1254
if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1258
/* Make a copy, so that on a reload, we have no race */
1259
if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
1260
memcpy(vmu, cur, sizeof(*vmu));
1261
ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1262
AST_LIST_NEXT(vmu, list) = NULL;
1265
vmu = find_user_realtime(ivm, context, mailbox);
1266
AST_LIST_UNLOCK(&users);
1271
* \brief Resets a user password to a specified password.
1276
* This does the actual change password work, called by the vm_change_password() function.
1278
* \return zero on success, -1 on error.
1280
static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
1282
/* This function could be made to generate one from a database, too */
1283
struct ast_vm_user *cur;
1285
AST_LIST_LOCK(&users);
1286
AST_LIST_TRAVERSE(&users, cur, list) {
1287
if ((!context || !strcasecmp(context, cur->context)) &&
1288
(!strcasecmp(mailbox, cur->mailbox)))
1292
ast_copy_string(cur->password, newpass, sizeof(cur->password));
1295
AST_LIST_UNLOCK(&users);
1300
* \brief The handler for the change password option.
1301
* \param vmu The voicemail user to work with.
1302
* \param newpassword The new password (that has been gathered from the appropriate prompting).
1303
* This is called when a new user logs in for the first time and the option to force them to change their password is set.
1304
* It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
1306
static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
1308
struct ast_config *cfg=NULL;
1309
struct ast_variable *var=NULL;
1310
struct ast_category *cat=NULL;
1311
char *category=NULL, *value=NULL, *new=NULL;
1312
const char *tmp=NULL;
1313
struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
1314
if (!change_password_realtime(vmu, newpassword))
1317
/* check voicemail.conf */
1318
if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
1319
while ((category = ast_category_browse(cfg, category))) {
1320
if (!strcasecmp(category, vmu->context)) {
1321
if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
1322
ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
1325
value = strstr(tmp,",");
1327
ast_log(AST_LOG_WARNING, "variable has bad format.\n");
1330
new = alloca((strlen(value)+strlen(newpassword)+1));
1331
sprintf(new,"%s%s", newpassword, value);
1332
if (!(cat = ast_category_get(cfg, category))) {
1333
ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
1336
ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
1339
/* save the results */
1340
reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1341
ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1342
ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
1346
/* check users.conf and update the password stored for the mailbox*/
1347
/* if no vmsecret entry exists create one. */
1348
if ((cfg = ast_config_load("users.conf", config_flags)) && cfg != CONFIG_STATUS_FILEINVALID) {
1349
ast_debug(4, "we are looking for %s\n", vmu->mailbox);
1350
while ((category = ast_category_browse(cfg, category))) {
1351
ast_debug(4, "users.conf: %s\n", category);
1352
if (!strcasecmp(category, vmu->mailbox)) {
1353
if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
1354
ast_debug(3, "looks like we need to make vmsecret!\n");
1355
var = ast_variable_new("vmsecret", newpassword, "");
1357
new = alloca(strlen(newpassword)+1);
1358
sprintf(new, "%s", newpassword);
1359
if (!(cat = ast_category_get(cfg, category))) {
1360
ast_debug(4, "failed to get category!\n");
1364
ast_variable_update(cat, "vmsecret", new, NULL, 0);
1366
ast_variable_append(cat, var);
1369
/* save the results and clean things up */
1370
reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1371
ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1372
ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
1376
static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
1379
snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
1380
if (!ast_safe_system(buf)) {
1381
ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1382
/* Reset the password in memory, too */
1383
reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1388
* \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1389
* \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1390
* \param len The length of the path string that was written out.
1392
* The path is constructed as
1393
* VM_SPOOL_DIRcontext/ext/folder
1395
* \return zero on success, -1 on error.
1397
static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
1399
return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
1403
* \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1404
* \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1405
* \param len The length of the path string that was written out.
1407
* The path is constructed as
1408
* VM_SPOOL_DIRcontext/ext/folder
1410
* \return zero on success, -1 on error.
1412
static int make_file(char *dest, const int len, const char *dir, const int num)
1414
return snprintf(dest, len, "%s/msg%04d", dir, num);
1417
/* same as mkstemp, but return a FILE * */
1418
static FILE *vm_mkftemp(char *template)
1421
int pfd = mkstemp(template);
1422
chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1424
p = fdopen(pfd, "w+");
1433
/*! \brief basically mkdir -p $dest/$context/$ext/$folder
1434
* \param dest String. base directory.
1435
* \param len Length of dest.
1436
* \param context String. Ignored if is null or empty string.
1437
* \param ext String. Ignored if is null or empty string.
1438
* \param folder String. Ignored if is null or empty string.
1439
* \return -1 on failure, 0 on success.
1441
static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1443
mode_t mode = VOICEMAIL_DIR_MODE;
1446
make_dir(dest, len, context, ext, folder);
1447
if ((res = ast_mkdir(dest, mode))) {
1448
ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1454
static const char *mbox(int id)
1456
static const char *msgs[] = {
1474
return (id >= 0 && id < ARRAY_LEN(msgs)) ? msgs[id] : "Unknown";
1477
static void free_user(struct ast_vm_user *vmu)
1479
if (ast_test_flag(vmu, VM_ALLOCED)) {
1480
if (vmu->emailbody != NULL) {
1481
ast_free(vmu->emailbody);
1482
vmu->emailbody = NULL;
1484
if (vmu->emailsubject != NULL) {
1485
ast_free(vmu->emailsubject);
1486
vmu->emailsubject = NULL;
1492
/* All IMAP-specific functions should go in this block. This
1493
* keeps them from being spread out all over the code */
1495
static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
1498
struct vm_state *vms;
1499
unsigned long messageNum;
1501
/* If greetings aren't stored in IMAP, just delete the file */
1502
if (msgnum < 0 && !imapgreetings) {
1503
ast_filedelete(file, NULL);
1507
if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1508
ast_log(LOG_WARNING, "Couldn't find a vm_state for mailbox %s. Unable to set \\DELETED flag for message %d\n", vmu->mailbox, msgnum);
1512
/* find real message number based on msgnum */
1513
/* this may be an index into vms->msgArray based on the msgnum. */
1514
messageNum = vms->msgArray[msgnum];
1515
if (messageNum == 0) {
1516
ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
1519
if (option_debug > 2)
1520
ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1521
/* delete message */
1522
snprintf (arg, sizeof(arg), "%lu",messageNum);
1523
ast_mutex_lock(&vms->lock);
1524
mail_setflag (vms->mailstream,arg,"\\DELETED");
1525
mail_expunge(vms->mailstream);
1526
ast_mutex_unlock(&vms->lock);
1529
static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
1531
struct vm_state *vms_p;
1532
char *file, *filename;
1537
/* This function is only used for retrieval of IMAP greetings
1538
* regular messages are not retrieved this way, nor are greetings
1539
* if they are stored locally*/
1540
if (msgnum > -1 || !imapgreetings) {
1543
file = strrchr(ast_strdupa(dir), '/');
1547
ast_debug (1, "Failed to procure file name from directory passed.\n");
1552
/* check if someone is accessing this box right now... */
1553
if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) &&
1554
!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1555
/* Unlike when retrieving a message, it is reasonable not to be able to find a
1556
* vm_state for a mailbox when trying to retrieve a greeting. Just create one,
1557
* that's all we need to do.
1559
if (!(vms_p = create_vm_state_from_user(vmu))) {
1560
ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
1565
/* Greetings will never have a prepended message */
1566
*vms_p->introfn = '\0';
1568
ast_mutex_lock(&vms_p->lock);
1569
ret = init_mailstream(vms_p, GREETINGS_FOLDER);
1570
if (!vms_p->mailstream) {
1571
ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
1572
ast_mutex_unlock(&vms_p->lock);
1576
/*XXX Yuck, this could probably be done a lot better */
1577
for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
1578
mail_fetchstructure(vms_p->mailstream, i + 1, &body);
1579
/* We have the body, now we extract the file name of the first attachment. */
1580
if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1581
attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
1583
ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
1584
ast_mutex_unlock(&vms_p->lock);
1587
filename = strsep(&attachment, ".");
1588
if (!strcmp(filename, file)) {
1589
ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
1590
vms_p->msgArray[vms_p->curmsg] = i + 1;
1591
save_body(body, vms_p, "2", attachment, 0);
1592
ast_mutex_unlock(&vms_p->lock);
1596
ast_mutex_unlock(&vms_p->lock);
1601
static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
1604
char *header_content;
1605
char *attachedfilefmt;
1607
struct vm_state *vms;
1608
char text_file[PATH_MAX];
1609
FILE *text_file_ptr;
1611
struct ast_vm_user *vmu;
1613
if (!(vmu = find_user(NULL, context, mailbox))) {
1614
ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
1619
if (imapgreetings) {
1620
res = imap_retrieve_greeting(dir, msgnum, vmu);
1628
/* Before anything can happen, we need a vm_state so that we can
1629
* actually access the imap server through the vms->mailstream
1631
if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1632
/* This should not happen. If it does, then I guess we'd
1633
* need to create the vm_state, extract which mailbox to
1634
* open, and then set up the msgArray so that the correct
1635
* IMAP message could be accessed. If I have seen correctly
1636
* though, the vms should be obtainable from the vmstates list
1637
* and should have its msgArray properly set up.
1639
ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
1644
make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
1645
snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
1647
/* Don't try to retrieve a message from IMAP if it already is on the file system */
1648
if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
1653
if (option_debug > 2)
1654
ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
1655
if (vms->msgArray[msgnum] == 0) {
1656
ast_log (LOG_WARNING,"Trying to access unknown message\n");
1661
/* This will only work for new messages... */
1662
ast_mutex_lock(&vms->lock);
1663
header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
1664
ast_mutex_unlock(&vms->lock);
1665
/* empty string means no valid header */
1666
if (ast_strlen_zero(header_content)) {
1667
ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
1672
ast_mutex_lock(&vms->lock);
1673
mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
1674
ast_mutex_unlock(&vms->lock);
1676
/* We have the body, now we extract the file name of the first attachment. */
1677
if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1678
attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
1680
ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
1685
/* Find the format of the attached file */
1687
strsep(&attachedfilefmt, ".");
1688
if (!attachedfilefmt) {
1689
ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
1694
save_body(body, vms, "2", attachedfilefmt, 0);
1695
if (save_body(body, vms, "3", attachedfilefmt, 1)) {
1696
*vms->introfn = '\0';
1699
/* Get info from headers!! */
1700
snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
1702
if (!(text_file_ptr = fopen(text_file, "w"))) {
1703
ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
1706
fprintf(text_file_ptr, "%s\n", "[message]");
1708
get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
1709
fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
1710
get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
1711
fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
1712
get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
1713
fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
1714
get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
1715
fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
1716
get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
1717
fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
1718
get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
1719
fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
1720
get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
1721
fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
1722
fclose(text_file_ptr);
1729
static int folder_int(const char *folder)
1731
/*assume a NULL folder means INBOX*/
1735
if (!strcasecmp(folder, imapfolder))
1737
if (!strcasecmp(folder, "INBOX"))
1740
else if (!strcasecmp(folder, "Old"))
1742
else if (!strcasecmp(folder, "Work"))
1744
else if (!strcasecmp(folder, "Family"))
1746
else if (!strcasecmp(folder, "Friends"))
1748
else if (!strcasecmp(folder, "Cust1"))
1750
else if (!strcasecmp(folder, "Cust2"))
1752
else if (!strcasecmp(folder, "Cust3"))
1754
else if (!strcasecmp(folder, "Cust4"))
1756
else if (!strcasecmp(folder, "Cust5"))
1758
else /*assume they meant INBOX if folder is not found otherwise*/
1763
* \brief Gets the number of messages that exist in a mailbox folder.
1768
* This method is used when IMAP backend is used.
1769
* \return The number of messages in this mailbox folder (zero or more).
1771
static int messagecount(const char *context, const char *mailbox, const char *folder)
1776
struct ast_vm_user *vmu, vmus;
1777
struct vm_state *vms_p;
1779
int fold = folder_int(folder);
1782
/* If URGENT, then look at INBOX */
1788
if (ast_strlen_zero(mailbox))
1791
/* We have to get the user before we can open the stream! */
1792
vmu = find_user(&vmus, context, mailbox);
1794
ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
1797
/* No IMAP account available */
1798
if (vmu->imapuser[0] == '\0') {
1799
ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
1804
/* No IMAP account available */
1805
if (vmu->imapuser[0] == '\0') {
1806
ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
1811
/* check if someone is accessing this box right now... */
1812
vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
1814
vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
1817
ast_debug(3, "Returning before search - user is logged in\n");
1818
if (fold == 0) { /* INBOX */
1819
return vms_p->newmessages;
1821
if (fold == 1) { /* Old messages */
1822
return vms_p->oldmessages;
1824
if (fold == 11) {/*Urgent messages*/
1825
return vms_p->urgentmessages;
1829
/* add one if not there... */
1830
vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
1832
vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
1836
vms_p = create_vm_state_from_user(vmu);
1838
ret = init_mailstream(vms_p, fold);
1839
if (!vms_p->mailstream) {
1840
ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
1844
ast_mutex_lock(&vms_p->lock);
1845
pgm = mail_newsearchpgm ();
1846
hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
1847
hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
1853
/* In the special case where fold is 1 (old messages) we have to do things a bit
1854
* differently. Old messages are stored in the INBOX but are marked as "seen"
1860
/* look for urgent messages */
1868
vms_p->vmArrayIndex = 0;
1869
mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
1870
if (fold == 0 && urgent == 0)
1871
vms_p->newmessages = vms_p->vmArrayIndex;
1873
vms_p->oldmessages = vms_p->vmArrayIndex;
1874
if (fold == 0 && urgent == 1)
1875
vms_p->urgentmessages = vms_p->vmArrayIndex;
1876
/*Freeing the searchpgm also frees the searchhdr*/
1877
mail_free_searchpgm(&pgm);
1878
ast_mutex_unlock(&vms_p->lock);
1880
return vms_p->vmArrayIndex;
1882
ast_mutex_lock(&vms_p->lock);
1883
mail_ping(vms_p->mailstream);
1884
ast_mutex_unlock(&vms_p->lock);
1889
static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag)
1891
char *myserveremail = serveremail;
1893
char introfn[PATH_MAX];
1897
char tmp[80] = "/tmp/astmail-XXXXXX";
1902
int ret; /* for better error checking */
1903
char *imap_flags = NIL;
1905
/* Back out early if this is a greeting and we don't want to store greetings in IMAP */
1906
if (msgnum < 0 && !imapgreetings) {
1910
/* Set urgent flag for IMAP message */
1911
if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
1912
ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
1913
imap_flags="\\FLAGGED";
1916
/* Attach only the first format */
1917
fmt = ast_strdupa(fmt);
1919
strsep(&stringp, "|");
1921
if (!ast_strlen_zero(vmu->serveremail))
1922
myserveremail = vmu->serveremail;
1925
make_file(fn, sizeof(fn), dir, msgnum);
1927
ast_copy_string (fn, dir, sizeof(fn));
1929
snprintf(introfn, sizeof(introfn), "%sintro", fn);
1930
if (ast_fileexists(introfn, NULL, NULL) <= 0) {
1934
if (ast_strlen_zero(vmu->email)) {
1935
/* We need the vmu->email to be set when we call make_email_file, but
1936
* if we keep it set, a duplicate e-mail will be created. So at the end
1937
* of this function, we will revert back to an empty string if tempcopy
1940
ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
1944
if (!strcmp(fmt, "wav49"))
1946
ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
1948
/* Make a temporary file instead of piping directly to sendmail, in case the mail
1950
if (!(p = vm_mkftemp(tmp))) {
1951
ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
1953
*(vmu->email) = '\0';
1957
if (msgnum < 0 && imapgreetings) {
1958
if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
1959
ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
1962
imap_delete_old_greeting(fn, vms);
1965
make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX", S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
1966
/* read mail file to memory */
1969
if (!(buf = ast_malloc(len + 1))) {
1970
ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
1973
*(vmu->email) = '\0';
1976
if (fread(buf, len, 1, p) < len) {
1978
ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
1982
((char *)buf)[len] = '\0';
1983
INIT(&str, mail_string, buf, len);
1984
ret = init_mailstream(vms, NEW_FOLDER);
1986
imap_mailbox_name(mailbox, sizeof(mailbox), vms, NEW_FOLDER, 1);
1987
ast_mutex_lock(&vms->lock);
1988
if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
1989
ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
1990
ast_mutex_unlock(&vms->lock);
1995
ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
2001
ast_debug(3, "%s stored\n", fn);
2004
*(vmu->email) = '\0';
2011
* \brief Gets the number of messages that exist in the inbox folder.
2012
* \param mailbox_context
2013
* \param newmsgs The variable that is updated with the count of new messages within this inbox.
2014
* \param oldmsgs The variable that is updated with the count of old messages within this inbox.
2015
* \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
2017
* This method is used when IMAP backend is used.
2018
* Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
2020
* \return zero on success, -1 on error.
2023
static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
2025
char tmp[PATH_MAX] = "";
2037
ast_debug(3,"Mailbox is set to %s\n",mailbox_context);
2038
/* If no mailbox, return immediately */
2039
if (ast_strlen_zero(mailbox_context))
2042
ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2043
context = strchr(tmp, '@');
2044
if (strchr(mailbox_context, ',')) {
2045
int tmpnew, tmpold, tmpurgent;
2046
ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2048
while ((cur = strsep(&mb, ", "))) {
2049
if (!ast_strlen_zero(cur)) {
2050
if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2058
*urgentmsgs += tmpurgent;
2069
context = "default";
2070
mailboxnc = (char *)mailbox_context;
2073
if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
2077
if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
2081
if((*urgentmsgs = messagecount(context, mailboxnc, "Urgent")) < 0)
2087
static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
2089
return inboxcount2(mailbox_context, NULL, newmsgs, oldmsgs);
2093
* \brief Determines if the given folder has messages.
2094
* \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
2095
* \param folder the folder to look in
2097
* This function is used when the mailbox is stored in an IMAP back end.
2098
* This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
2099
* \return 1 if the folder has one or more messages. zero otherwise.
2102
static int has_voicemail(const char *mailbox, const char *folder)
2104
char tmp[256], *tmp2, *box, *context;
2105
ast_copy_string(tmp, mailbox, sizeof(tmp));
2107
if (strchr(tmp2, ',')) {
2108
while ((box = strsep(&tmp2, ","))) {
2109
if (!ast_strlen_zero(box)) {
2110
if (has_voicemail(box, folder))
2115
if ((context= strchr(tmp, '@')))
2118
context = "default";
2119
return messagecount(context, tmp, folder) ? 1 : 0;
2123
* \brief Copies a message from one mailbox to another.
2133
* This works with IMAP storage based mailboxes.
2135
* \return zero on success, -1 on error.
2137
static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, char *flag)
2139
struct vm_state *sendvms = NULL, *destvms = NULL;
2140
char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2141
if (msgnum >= recip->maxmsg) {
2142
ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
2145
if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
2146
ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2149
if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
2150
ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2153
snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2154
ast_mutex_lock(&sendvms->lock);
2155
if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T)) {
2156
ast_mutex_unlock(&sendvms->lock);
2159
ast_mutex_unlock(&sendvms->lock);
2160
ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2164
static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
2166
char tmp[256], *t = tmp;
2167
size_t left = sizeof(tmp);
2169
if (box == OLD_FOLDER) {
2170
ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
2172
ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
2175
if (box == NEW_FOLDER) {
2176
ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
2178
snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
2181
/* Build up server information */
2182
ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
2184
/* Add authentication user if present */
2185
if (!ast_strlen_zero(authuser))
2186
ast_build_string(&t, &left, "/authuser=%s", authuser);
2188
/* Add flags if present */
2189
if (!ast_strlen_zero(imapflags))
2190
ast_build_string(&t, &left, "/%s", imapflags);
2192
/* End with username */
2194
ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
2196
ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
2198
if (box == NEW_FOLDER || box == OLD_FOLDER)
2199
snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
2200
else if (box == GREETINGS_FOLDER)
2201
snprintf(spec, len, "%s%s", tmp, greetingfolder);
2202
else { /* Other folders such as Friends, Family, etc... */
2203
if (!ast_strlen_zero(imapparentfolder)) {
2204
/* imapparentfolder would typically be set to INBOX */
2205
snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(box));
2207
snprintf(spec, len, "%s%s", tmp, mbox(box));
2212
static int init_mailstream(struct vm_state *vms, int box)
2214
MAILSTREAM *stream = NIL;
2219
ast_log (LOG_ERROR,"vm_state is NULL!\n");
2222
if (option_debug > 2)
2223
ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
2224
if (vms->mailstream == NIL || !vms->mailstream) {
2226
ast_log (LOG_DEBUG,"mailstream not set.\n");
2228
stream = vms->mailstream;
2230
/* debug = T; user wants protocol telemetry? */
2231
debug = NIL; /* NO protocol telemetry? */
2233
if (delimiter == '\0') { /* did not probe the server yet */
2235
#ifdef USE_SYSTEM_IMAP
2236
#include <imap/linkage.c>
2237
#elif defined(USE_SYSTEM_CCLIENT)
2238
#include <c-client/linkage.c>
2240
#include "linkage.c"
2242
/* Connect to INBOX first to get folders delimiter */
2243
imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
2244
ast_mutex_lock(&vms->lock);
2245
stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2246
ast_mutex_unlock(&vms->lock);
2247
if (stream == NIL) {
2248
ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
2251
get_mailbox_delimiter(stream);
2252
/* update delimiter in imapfolder */
2253
for (cp = imapfolder; *cp; cp++)
2257
/* Now connect to the target folder */
2258
imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
2259
if (option_debug > 2)
2260
ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
2261
ast_mutex_lock(&vms->lock);
2262
vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2263
ast_mutex_unlock(&vms->lock);
2264
if (vms->mailstream == NIL) {
2271
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
2275
int ret, urgent = 0;
2277
/* If Urgent, then look at INBOX */
2283
ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
2284
ast_debug(3,"Before init_mailstream, user is %s\n",vmu->imapuser);
2285
vms->imapversion = vmu->imapversion;
2287
if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
2288
ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
2292
create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2296
ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
2297
check_quota(vms,(char *)mbox(box));
2300
ast_mutex_lock(&vms->lock);
2301
pgm = mail_newsearchpgm();
2303
/* Check IMAP folder for Asterisk messages only... */
2304
hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
2305
hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
2310
/* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
2311
if (box == NEW_FOLDER && urgent == 1) {
2316
} else if (box == NEW_FOLDER && urgent == 0) {
2321
} else if (box == OLD_FOLDER) {
2326
ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
2328
vms->vmArrayIndex = 0;
2329
mail_search_full (vms->mailstream, NULL, pgm, NIL);
2330
vms->lastmsg = vms->vmArrayIndex - 1;
2331
mail_free_searchpgm(&pgm);
2333
ast_mutex_unlock(&vms->lock);
2337
static void write_file(char *filename, char *buffer, unsigned long len)
2341
output = fopen (filename, "w");
2342
if (fwrite(buffer, len, 1, output) != 1) {
2343
if (ferror(output)) {
2344
ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
2350
static void update_messages_by_imapuser(const char *user, unsigned long number)
2352
struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
2354
if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
2358
ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
2359
vms->msgArray[vms->vmArrayIndex++] = number;
2362
void mm_searched(MAILSTREAM *stream, unsigned long number)
2364
char *mailbox = stream->mailbox, buf[1024] = "", *user;
2366
if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
2369
update_messages_by_imapuser(user, number);
2372
static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
2374
struct ast_variable *var;
2375
struct ast_vm_user *vmu;
2377
vmu = ast_calloc(1, sizeof *vmu);
2380
ast_set_flag(vmu, VM_ALLOCED);
2381
populate_defaults(vmu);
2383
var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
2385
apply_options_full(vmu, var);
2386
ast_variables_destroy(var);
2394
/* Interfaces to C-client */
2396
void mm_exists(MAILSTREAM * stream, unsigned long number)
2398
/* mail_ping will callback here if new mail! */
2399
ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
2400
if (number == 0) return;
2405
void mm_expunged(MAILSTREAM * stream, unsigned long number)
2407
/* mail_ping will callback here if expunged mail! */
2408
ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
2409
if (number == 0) return;
2414
void mm_flags(MAILSTREAM * stream, unsigned long number)
2416
/* mail_ping will callback here if read mail! */
2417
ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
2418
if (number == 0) return;
2423
void mm_notify(MAILSTREAM * stream, char *string, long errflg)
2425
ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
2426
mm_log (string, errflg);
2430
void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2432
if (delimiter == '\0') {
2436
ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
2437
if (attributes & LATT_NOINFERIORS)
2438
ast_debug(5, "no inferiors\n");
2439
if (attributes & LATT_NOSELECT)
2440
ast_debug(5, "no select\n");
2441
if (attributes & LATT_MARKED)
2442
ast_debug(5, "marked\n");
2443
if (attributes & LATT_UNMARKED)
2444
ast_debug(5, "unmarked\n");
2448
void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2450
ast_debug(5, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
2451
if (attributes & LATT_NOINFERIORS)
2452
ast_debug(5, "no inferiors\n");
2453
if (attributes & LATT_NOSELECT)
2454
ast_debug(5, "no select\n");
2455
if (attributes & LATT_MARKED)
2456
ast_debug(5, "marked\n");
2457
if (attributes & LATT_UNMARKED)
2458
ast_debug(5, "unmarked\n");
2462
void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
2464
ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
2465
if (status->flags & SA_MESSAGES)
2466
ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
2467
if (status->flags & SA_RECENT)
2468
ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
2469
if (status->flags & SA_UNSEEN)
2470
ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
2471
if (status->flags & SA_UIDVALIDITY)
2472
ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
2473
if (status->flags & SA_UIDNEXT)
2474
ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
2475
ast_log(AST_LOG_NOTICE, "\n");
2479
void mm_log(char *string, long errflg)
2481
switch ((short) errflg) {
2483
ast_debug(1,"IMAP Info: %s\n", string);
2487
ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
2490
ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
2496
void mm_dlog(char *string)
2498
ast_log(AST_LOG_NOTICE, "%s\n", string);
2502
void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
2504
struct ast_vm_user *vmu;
2506
ast_debug(4, "Entering callback mm_login\n");
2508
ast_copy_string(user, mb->user, MAILTMPLEN);
2510
/* We should only do this when necessary */
2511
if (!ast_strlen_zero(authpassword)) {
2512
ast_copy_string(pwd, authpassword, MAILTMPLEN);
2514
AST_LIST_TRAVERSE(&users, vmu, list) {
2515
if (!strcasecmp(mb->user, vmu->imapuser)) {
2516
ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2521
if ((vmu = find_user_realtime_imapuser(mb->user))) {
2522
ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2530
void mm_critical(MAILSTREAM * stream)
2535
void mm_nocritical(MAILSTREAM * stream)
2540
long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
2542
kill (getpid (), SIGSTOP);
2547
void mm_fatal(char *string)
2549
ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
2552
/* C-client callback to handle quota */
2553
static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
2555
struct vm_state *vms;
2556
char *mailbox = stream->mailbox, *user;
2557
char buf[1024] = "";
2558
unsigned long usage = 0, limit = 0;
2561
usage = pquota->usage;
2562
limit = pquota->limit;
2563
pquota = pquota->next;
2566
if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || (!(vms = get_vm_state_by_imapuser(user, 2)) && !(vms = get_vm_state_by_imapuser(user, 0)))) {
2567
ast_log(AST_LOG_ERROR, "No state found.\n");
2571
ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
2573
vms->quota_usage = usage;
2574
vms->quota_limit = limit;
2577
static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
2579
char *start, *eol_pnt;
2582
if (ast_strlen_zero(header) || ast_strlen_zero(tag))
2585
taglen = strlen(tag) + 1;
2589
if (!(start = strstr(header, tag)))
2592
/* Since we can be called multiple times we should clear our buffer */
2593
memset(buf, 0, len);
2595
ast_copy_string(buf, start+taglen, len);
2596
if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
2601
static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
2603
char *start, *quote, *eol_pnt;
2605
if (ast_strlen_zero(mailbox))
2608
if (!(start = strstr(mailbox, "/user=")))
2611
ast_copy_string(buf, start+6, len);
2613
if (!(quote = strchr(buf, '\"'))) {
2614
if (!(eol_pnt = strchr(buf, '/')))
2615
eol_pnt = strchr(buf,'}');
2619
eol_pnt = strchr(buf+1,'\"');
2625
static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
2627
struct vm_state *vms_p;
2629
pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
2630
if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
2633
if (option_debug > 4)
2634
ast_log(AST_LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
2635
if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
2637
ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
2638
ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
2639
ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
2640
vms_p->mailstream = NIL; /* save for access from interactive entry point */
2641
vms_p->imapversion = vmu->imapversion;
2642
if (option_debug > 4)
2643
ast_log(AST_LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
2645
/* set mailbox to INBOX! */
2646
ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
2647
init_vm_state(vms_p);
2648
vmstate_insert(vms_p);
2652
static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
2654
struct vmstate *vlist = NULL;
2657
struct vm_state *vms;
2658
pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
2659
vms = pthread_getspecific(ts_vmstate.key);
2663
AST_LIST_LOCK(&vmstates);
2664
AST_LIST_TRAVERSE(&vmstates, vlist, list) {
2666
ast_debug(3, "error: vms is NULL for %s\n", user);
2669
if (vlist->vms->imapversion != imapversion) {
2672
if (!vlist->vms->imapuser) {
2673
ast_debug(3, "error: imapuser is NULL for %s\n", user);
2677
if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
2678
AST_LIST_UNLOCK(&vmstates);
2682
AST_LIST_UNLOCK(&vmstates);
2684
ast_debug(3, "%s not found in vmstates\n", user);
2689
static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
2692
struct vmstate *vlist = NULL;
2693
const char *local_context = S_OR(context, "default");
2696
struct vm_state *vms;
2697
pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
2698
vms = pthread_getspecific(ts_vmstate.key);
2702
AST_LIST_LOCK(&vmstates);
2703
AST_LIST_TRAVERSE(&vmstates, vlist, list) {
2705
ast_debug(3, "error: vms is NULL for %s\n", mailbox);
2708
if (vlist->vms->imapversion != imapversion) {
2711
if (!vlist->vms->username || !vlist->vms->context) {
2712
ast_debug(3, "error: username is NULL for %s\n", mailbox);
2716
ast_debug(3, "comparing mailbox %s@%s (i=%d) to vmstate mailbox %s@%s (i=%d)\n", mailbox, local_context, interactive, vlist->vms->username, vlist->vms->context, vlist->vms->interactive);
2718
if (!strcmp(vlist->vms->username,mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
2719
ast_debug(3, "Found it!\n");
2720
AST_LIST_UNLOCK(&vmstates);
2724
AST_LIST_UNLOCK(&vmstates);
2726
ast_debug(3, "%s not found in vmstates\n", mailbox);
2731
static void vmstate_insert(struct vm_state *vms)
2734
struct vm_state *altvms;
2736
/* If interactive, it probably already exists, and we should
2737
use the one we already have since it is more up to date.
2738
We can compare the username to find the duplicate */
2739
if (vms->interactive == 1) {
2740
altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
2742
ast_debug(3, "Duplicate mailbox %s, copying message info...\n",vms->username);
2743
vms->newmessages = altvms->newmessages;
2744
vms->oldmessages = altvms->oldmessages;
2745
vms->vmArrayIndex = altvms->vmArrayIndex;
2746
vms->lastmsg = altvms->lastmsg;
2747
vms->curmsg = altvms->curmsg;
2748
/* get a pointer to the persistent store */
2749
vms->persist_vms = altvms;
2750
/* Reuse the mailstream? */
2751
#ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
2752
vms->mailstream = altvms->mailstream;
2754
vms->mailstream = NIL;
2760
if (!(v = ast_calloc(1, sizeof(*v))))
2765
ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
2767
AST_LIST_LOCK(&vmstates);
2768
AST_LIST_INSERT_TAIL(&vmstates, v, list);
2769
AST_LIST_UNLOCK(&vmstates);
2772
static void vmstate_delete(struct vm_state *vms)
2774
struct vmstate *vc = NULL;
2775
struct vm_state *altvms = NULL;
2777
/* If interactive, we should copy pertinent info
2778
back to the persistent state (to make update immediate) */
2779
if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
2780
ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
2781
altvms->newmessages = vms->newmessages;
2782
altvms->oldmessages = vms->oldmessages;
2783
altvms->updated = 1;
2784
vms->mailstream = mail_close(vms->mailstream);
2786
/* Interactive states are not stored within the persistent list */
2790
ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
2792
AST_LIST_LOCK(&vmstates);
2793
AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
2794
if (vc->vms == vms) {
2795
AST_LIST_REMOVE_CURRENT(list);
2799
AST_LIST_TRAVERSE_SAFE_END
2800
AST_LIST_UNLOCK(&vmstates);
2803
ast_mutex_destroy(&vc->vms->lock);
2807
ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
2810
static void set_update(MAILSTREAM * stream)
2812
struct vm_state *vms;
2813
char *mailbox = stream->mailbox, *user;
2814
char buf[1024] = "";
2816
if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
2817
if (user && option_debug > 2)
2818
ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
2822
ast_debug(3, "User %s mailbox set for update.\n", user);
2824
vms->updated = 1; /* Set updated flag since mailbox changed */
2827
static void init_vm_state(struct vm_state *vms)
2830
vms->vmArrayIndex = 0;
2831
for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
2832
vms->msgArray[x] = 0;
2834
ast_mutex_init(&vms->lock);
2837
static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro)
2841
char *fn = is_intro ? vms->introfn : vms->fn;
2843
unsigned long newlen;
2846
if (!body || body == NIL)
2849
ast_mutex_lock(&vms->lock);
2850
body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
2851
ast_mutex_unlock(&vms->lock);
2852
if (body_content != NIL) {
2853
snprintf(filename, sizeof(filename), "%s.%s", fn, format);
2854
/* ast_debug(1,body_content); */
2855
body_decoded = rfc822_base64((unsigned char *)body_content, len, &newlen);
2856
/* If the body of the file is empty, return an error */
2860
write_file(filename, (char *) body_decoded, newlen);
2862
ast_debug(5, "Body of message is NULL.\n");
2869
* \brief Get delimiter via mm_list callback
2872
* Determines the delimiter character that is used by the underlying IMAP based mail store.
2874
/* MUTEX should already be held */
2875
static void get_mailbox_delimiter(MAILSTREAM *stream) {
2877
snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
2878
mail_list(stream, tmp, "*");
2882
* \brief Check Quota for user
2883
* \param vms a pointer to a vm_state struct, will use the mailstream property of this.
2884
* \param mailbox the mailbox to check the quota for.
2886
* Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
2888
static void check_quota(struct vm_state *vms, char *mailbox) {
2889
ast_mutex_lock(&vms->lock);
2890
mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
2891
ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
2892
if (vms && vms->mailstream != NULL) {
2893
imap_getquotaroot(vms->mailstream, mailbox);
2895
ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
2897
ast_mutex_unlock(&vms->lock);
2900
#endif /* IMAP_STORAGE */
2902
/*! \brief Lock file path
2903
only return failure if ast_lock_path returns 'timeout',
2904
not if the path does not exist or any other reason
2906
static int vm_lock_path(const char *path)
2908
switch (ast_lock_path(path)) {
2909
case AST_LOCK_TIMEOUT:
2918
struct generic_prepare_struct {
2924
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
2926
struct generic_prepare_struct *gps = data;
2930
res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2931
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2932
ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
2935
res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
2936
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2937
ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
2938
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2941
for (i = 0; i < gps->argc; i++)
2942
SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
2948
* \brief Retrieves a file from an ODBC data store.
2949
* \param dir the path to the file to be retreived.
2950
* \param msgnum the message number, such as within a mailbox folder.
2952
* This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
2953
* The purpose is to get the message from the database store to the local file system, so that the message may be played, or the information file may be read.
2955
* The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
2956
* The output is the message information file with the name msgnum and the extension .txt
2957
* and the message file with the extension of its format, in the directory with base file name of the msgnum.
2959
* \return 0 on success, -1 on error.
2961
static int retrieve_file(char *dir, int msgnum)
2967
void *fdm = MAP_FAILED;
2968
SQLSMALLINT colcount=0;
2975
SQLSMALLINT datatype;
2976
SQLSMALLINT decimaldigits;
2977
SQLSMALLINT nullable;
2983
char full_fn[PATH_MAX];
2985
char *argv[] = { dir, msgnums };
2986
struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
2988
struct odbc_obj *obj;
2989
obj = ast_odbc_request_obj(odbc_database, 0);
2991
ast_copy_string(fmt, vmfmts, sizeof(fmt));
2992
c = strchr(fmt, '|');
2995
if (!strcasecmp(fmt, "wav49"))
2997
snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
2999
make_file(fn, sizeof(fn), dir, msgnum);
3001
ast_copy_string(fn, dir, sizeof(fn));
3003
/* Create the information file */
3004
snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3006
if (!(f = fopen(full_fn, "w+"))) {
3007
ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
3011
snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
3012
snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
3013
stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3015
ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3016
ast_odbc_release_obj(obj);
3019
res = SQLFetch(stmt);
3020
if (res == SQL_NO_DATA) {
3021
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3022
ast_odbc_release_obj(obj);
3024
} else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3025
ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3026
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3027
ast_odbc_release_obj(obj);
3030
fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
3032
ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
3033
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3034
ast_odbc_release_obj(obj);
3037
res = SQLNumResultCols(stmt, &colcount);
3038
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3039
ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
3040
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3041
ast_odbc_release_obj(obj);
3045
fprintf(f, "[message]\n");
3046
for (x=0;x<colcount;x++) {
3048
collen = sizeof(coltitle);
3049
res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
3050
&datatype, &colsize, &decimaldigits, &nullable);
3051
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3052
ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
3053
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3054
ast_odbc_release_obj(obj);
3057
if (!strcasecmp(coltitle, "recording")) {
3059
res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
3063
lseek(fd, fdlen - 1, SEEK_SET);
3064
if (write(fd, tmp, 1) != 1) {
3069
/* Read out in small chunks */
3070
for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
3071
if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
3072
ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
3073
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3074
ast_odbc_release_obj(obj);
3077
res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
3078
munmap(fdm, CHUNKSIZE);
3079
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3080
ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3082
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3083
ast_odbc_release_obj(obj);
3088
if (truncate(full_fn, fdlen) < 0) {
3089
ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
3093
res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3094
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3095
ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
3096
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3097
ast_odbc_release_obj(obj);
3100
if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
3101
fprintf(f, "%s=%s\n", coltitle, rowdata);
3104
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3105
ast_odbc_release_obj(obj);
3107
ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3117
* \brief Determines the highest message number in use for a given user and mailbox folder.
3119
* \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
3121
* This method is used when mailboxes are stored in an ODBC back end.
3122
* Typical use to set the msgnum would be to take the value returned from this method and add one to it.
3124
* \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
3126
static int last_message_index(struct ast_vm_user *vmu, char *dir)
3133
char *argv[] = { dir };
3134
struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
3136
struct odbc_obj *obj;
3137
obj = ast_odbc_request_obj(odbc_database, 0);
3139
snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
3140
stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3142
ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3143
ast_odbc_release_obj(obj);
3146
res = SQLFetch(stmt);
3147
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3148
ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3149
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3150
ast_odbc_release_obj(obj);
3153
res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3154
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3155
ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3156
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3157
ast_odbc_release_obj(obj);
3160
if (sscanf(rowdata, "%30d", &x) != 1)
3161
ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
3162
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3163
ast_odbc_release_obj(obj);
3165
ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3171
* \brief Determines if the specified message exists.
3172
* \param dir the folder the mailbox folder to look for messages.
3173
* \param msgnum the message index to query for.
3175
* This method is used when mailboxes are stored in an ODBC back end.
3177
* \return greater than zero if the message exists, zero when the message does not exist or on error.
3179
static int message_exists(char *dir, int msgnum)
3187
char *argv[] = { dir, msgnums };
3188
struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3190
struct odbc_obj *obj;
3191
obj = ast_odbc_request_obj(odbc_database, 0);
3193
snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3194
snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
3195
stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3197
ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3198
ast_odbc_release_obj(obj);
3201
res = SQLFetch(stmt);
3202
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3203
ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3204
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3205
ast_odbc_release_obj(obj);
3208
res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3209
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3210
ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3211
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3212
ast_odbc_release_obj(obj);
3215
if (sscanf(rowdata, "%30d", &x) != 1)
3216
ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
3217
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3218
ast_odbc_release_obj(obj);
3220
ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3226
* \brief returns the one-based count for messages.
3228
* \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
3230
* This method is used when mailboxes are stored in an ODBC back end.
3231
* The message index is zero-based, the first message will be index 0. For convenient display it is good to have the
3232
* one-based messages.
3233
* This method just calls last_message_index and returns +1 of its value.
3235
* \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
3237
static int count_messages(struct ast_vm_user *vmu, char *dir)
3239
return last_message_index(vmu, dir) + 1;
3243
* \brief Deletes a message from the mailbox folder.
3244
* \param sdir The mailbox folder to work in.
3245
* \param smsg The message index to be deleted.
3247
* This method is used when mailboxes are stored in an ODBC back end.
3248
* The specified message is directly deleted from the database 'voicemessages' table.
3250
* \return the value greater than zero on success to indicate the number of messages, less than zero on error.
3252
static void delete_file(char *sdir, int smsg)
3257
char *argv[] = { sdir, msgnums };
3258
struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3260
struct odbc_obj *obj;
3261
obj = ast_odbc_request_obj(odbc_database, 0);
3263
snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3264
snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
3265
stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3267
ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3269
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3270
ast_odbc_release_obj(obj);
3272
ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3277
* \brief Copies a voicemail from one mailbox to another.
3278
* \param sdir the folder for which to look for the message to be copied.
3279
* \param smsg the index of the message to be copied.
3280
* \param ddir the destination folder to copy the message into.
3281
* \param dmsg the index to be used for the copied message.
3282
* \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
3283
* \param dmailboxcontext The context for the destination user.
3285
* This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
3287
static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
3293
struct odbc_obj *obj;
3294
char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
3295
struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
3297
delete_file(ddir, dmsg);
3298
obj = ast_odbc_request_obj(odbc_database, 0);
3300
snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3301
snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
3302
snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, flag, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
3303
stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3305
ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
3307
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3308
ast_odbc_release_obj(obj);
3310
ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3314
struct insert_data {
3321
const char *context;
3322
const char *macrocontext;
3323
const char *callerid;
3324
const char *origtime;
3325
const char *duration;
3327
char *mailboxcontext;
3328
const char *category;
3332
static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
3334
struct insert_data *data = vdata;
3338
res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
3339
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3340
ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
3341
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3345
SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *)data->dir, 0, NULL);
3346
SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *)data->msgnums, 0, NULL);
3347
SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *)data->data, data->datalen, &data->indlen);
3348
SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *)data->context, 0, NULL);
3349
SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *)data->macrocontext, 0, NULL);
3350
SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *)data->callerid, 0, NULL);
3351
SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *)data->origtime, 0, NULL);
3352
SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *)data->duration, 0, NULL);
3353
SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *)data->mailboxuser, 0, NULL);
3354
SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *)data->mailboxcontext, 0, NULL);
3355
SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *)data->flag, 0, NULL);
3356
if (!ast_strlen_zero(data->category)) {
3357
SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *)data->category, 0, NULL);
3359
res = SQLExecDirect(stmt, (unsigned char *)data->sql, SQL_NTS);
3360
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3361
ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
3362
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3370
* \brief Stores a voicemail into the database.
3371
* \param dir the folder the mailbox folder to store the message.
3372
* \param mailboxuser the user owning the mailbox folder.
3373
* \param mailboxcontext
3374
* \param msgnum the message index for the message to be stored.
3376
* This method is used when mailboxes are stored in an ODBC back end.
3377
* The message sound file and information file is looked up on the file system.
3378
* A SQL query is invoked to store the message into the (MySQL) database.
3380
* \return the zero on success -1 on error.
3382
static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
3386
void *fdm = MAP_FAILED;
3392
char full_fn[PATH_MAX];
3395
struct ast_config *cfg=NULL;
3396
struct odbc_obj *obj;
3397
struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
3398
.context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
3399
struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
3401
delete_file(dir, msgnum);
3402
if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
3403
ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3408
ast_copy_string(fmt, vmfmts, sizeof(fmt));
3409
c = strchr(fmt, '|');
3412
if (!strcasecmp(fmt, "wav49"))
3414
snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
3416
make_file(fn, sizeof(fn), dir, msgnum);
3418
ast_copy_string(fn, dir, sizeof(fn));
3419
snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3420
cfg = ast_config_load(full_fn, config_flags);
3421
snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
3422
fd = open(full_fn, O_RDWR);
3424
ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
3428
if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
3429
if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
3432
if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
3433
idata.macrocontext = "";
3435
if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
3436
idata.callerid = "";
3438
if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
3439
idata.origtime = "";
3441
if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
3442
idata.duration = "";
3444
if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
3445
idata.category = "";
3447
if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
3451
fdlen = lseek(fd, 0, SEEK_END);
3452
lseek(fd, 0, SEEK_SET);
3453
printf("Length is %zd\n", fdlen);
3454
fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED,fd, 0);
3455
if (fdm == MAP_FAILED) {
3456
ast_log(AST_LOG_WARNING, "Memory map failed!\n");
3461
idata.datalen = idata.indlen = fdlen;
3463
if (!ast_strlen_zero(idata.category))
3464
snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
3466
snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
3468
if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
3469
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3471
ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3476
ast_odbc_release_obj(obj);
3479
ast_config_destroy(cfg);
3480
if (fdm != MAP_FAILED)
3488
* \brief Renames a message in a mailbox folder.
3489
* \param sdir The folder of the message to be renamed.
3490
* \param smsg The index of the message to be renamed.
3491
* \param mailboxuser The user to become the owner of the message after it is renamed. Usually this will be the same as the original owner.
3492
* \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
3493
* \param ddir The destination folder for the message to be renamed into
3494
* \param dmsg The destination message for the message to be renamed.
3496
* This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
3497
* The is usually used to resequence the messages in the mailbox, such as to delete messag index 0, it would be called successively to slide all the other messages down one index.
3498
* But in theory, because the SQL query performs an update on (dir, msgnum, mailboxuser, mailboxcontext) in the database, it should be possible to have the message relocated to another mailbox or context as well.
3500
static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
3506
struct odbc_obj *obj;
3507
char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
3508
struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
3510
delete_file(ddir, dmsg);
3511
obj = ast_odbc_request_obj(odbc_database, 0);
3513
snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3514
snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
3515
snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
3516
stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3518
ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3520
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3521
ast_odbc_release_obj(obj);
3523
ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3528
* \brief Removes a voicemail message file.
3529
* \param dir the path to the message file.
3530
* \param msgnum the unique number for the message within the mailbox.
3532
* Removes the message content file and the information file.
3533
* This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
3534
* Typical use is to clean up after a RETRIEVE operation.
3535
* Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
3536
* \return zero on success, -1 on error.
3538
static int remove_file(char *dir, int msgnum)
3541
char full_fn[PATH_MAX];
3545
snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3546
make_file(fn, sizeof(fn), dir, msgnum);
3548
ast_copy_string(fn, dir, sizeof(fn));
3549
ast_filedelete(fn, NULL);
3550
snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3555
#ifndef IMAP_STORAGE
3557
* \brief Find all .txt files - even if they are not in sequence from 0000.
3561
* This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
3563
* \return the count of messages, zero or more.
3565
static int count_messages(struct ast_vm_user *vmu, char *dir)
3570
struct dirent *vment = NULL;
3572
if (vm_lock_path(dir))
3573
return ERROR_LOCK_PATH;
3575
if ((vmdir = opendir(dir))) {
3576
while ((vment = readdir(vmdir))) {
3577
if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
3583
ast_unlock_path(dir);
3589
* \brief Renames a message in a mailbox folder.
3590
* \param sfn The path to the mailbox information and data file to be renamed.
3591
* \param dfn The path for where the message data and information files will be renamed to.
3593
* This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
3595
static void rename_file(char *sfn, char *dfn)
3597
char stxt[PATH_MAX];
3598
char dtxt[PATH_MAX];
3599
ast_filerename(sfn,dfn,NULL);
3600
snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
3601
snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
3602
if (ast_check_realtime("voicemail_data")) {
3603
ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
3609
* \brief Determines the highest message number in use for a given user and mailbox folder.
3611
* \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
3613
* This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
3614
* Typical use to set the msgnum would be to take the value returned from this method and add one to it.
3616
* \note Should always be called with a lock already set on dir.
3617
* \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
3619
static int last_message_index(struct ast_vm_user *vmu, char *dir)
3622
unsigned char map[MAXMSGLIMIT] = "";
3624
struct dirent *msgdirent;
3627
/* Reading the entire directory into a file map scales better than
3628
* doing a stat repeatedly on a predicted sequence. I suspect this
3629
* is partially due to stat(2) internally doing a readdir(2) itself to
3630
* find each file. */
3631
if (!(msgdir = opendir(dir))) {
3635
while ((msgdirent = readdir(msgdir))) {
3636
if (sscanf(msgdirent->d_name, "msg%30d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
3641
for (x = 0; x < vmu->maxmsg; x++) {
3649
#endif /* #ifndef IMAP_STORAGE */
3650
#endif /* #else of #ifdef ODBC_STORAGE */
3651
#ifndef IMAP_STORAGE
3653
* \brief Utility function to copy a file.
3654
* \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
3655
* \param outfile The path for which to copy the file to. The directory permissions must allow the creation (or truncation) of the file, and allow for opening the file in write only mode.
3657
* When the compiler option HARDLINK_WHEN_POSSIBLE is set, the copy operation will attempt to use the hard link facility instead of copy the file (to save disk space). If the link operation fails, it falls back to the copy operation.
3658
* The copy operation copies up to 4096 bytes at once.
3660
* \return zero on success, -1 on error.
3662
static int copy(char *infile, char *outfile)
3670
#ifdef HARDLINK_WHEN_POSSIBLE
3671
/* Hard link if possible; saves disk space & is faster */
3672
if (link(infile, outfile)) {
3674
if ((ifd = open(infile, O_RDONLY)) < 0) {
3675
ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
3678
if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
3679
ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
3684
len = read(ifd, buf, sizeof(buf));
3686
ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
3692
res = write(ofd, buf, len);
3693
if (errno == ENOMEM || errno == ENOSPC || res != len) {
3694
ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
3704
#ifdef HARDLINK_WHEN_POSSIBLE
3706
/* Hard link succeeded */
3713
* \brief Copies a voicemail information (envelope) file.
3717
* Every voicemail has the data (.wav) file, and the information file.
3718
* This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
3719
* This is used by the COPY macro when not using IMAP storage.
3721
static void copy_plain_file(char *frompath, char *topath)
3723
char frompath2[PATH_MAX], topath2[PATH_MAX];
3724
struct ast_variable *tmp,*var = NULL;
3725
const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
3726
ast_filecopy(frompath, topath, NULL);
3727
snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
3728
snprintf(topath2, sizeof(topath2), "%s.txt", topath);
3729
if (ast_check_realtime("voicemail_data")) {
3730
var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
3731
/* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
3732
for (tmp = var; tmp; tmp = tmp->next) {
3733
if (!strcasecmp(tmp->name, "origmailbox")) {
3734
origmailbox = tmp->value;
3735
} else if (!strcasecmp(tmp->name, "context")) {
3736
context = tmp->value;
3737
} else if (!strcasecmp(tmp->name, "macrocontext")) {
3738
macrocontext = tmp->value;
3739
} else if (!strcasecmp(tmp->name, "exten")) {
3741
} else if (!strcasecmp(tmp->name, "priority")) {
3742
priority = tmp->value;
3743
} else if (!strcasecmp(tmp->name, "callerchan")) {
3744
callerchan = tmp->value;
3745
} else if (!strcasecmp(tmp->name, "callerid")) {
3746
callerid = tmp->value;
3747
} else if (!strcasecmp(tmp->name, "origdate")) {
3748
origdate = tmp->value;
3749
} else if (!strcasecmp(tmp->name, "origtime")) {
3750
origtime = tmp->value;
3751
} else if (!strcasecmp(tmp->name, "category")) {
3752
category = tmp->value;
3753
} else if (!strcasecmp(tmp->name, "duration")) {
3754
duration = tmp->value;
3757
ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, SENTINEL);
3759
copy(frompath2, topath2);
3760
ast_variables_destroy(var);
3765
* \brief Removes the voicemail sound and information file.
3766
* \param file The path to the sound file. This will be the the folder and message index, without the extension.
3768
* This is used by the DELETE macro when voicemails are stored on the file system.
3770
* \return zero on success, -1 on error.
3772
static int vm_delete(char *file)
3777
txtsize = (strlen(file) + 5)*sizeof(char);
3778
txt = alloca(txtsize);
3779
/* Sprintf here would safe because we alloca'd exactly the right length,
3780
* but trying to eliminate all sprintf's anyhow
3782
if (ast_check_realtime("voicemail_data")) {
3783
ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
3785
snprintf(txt, txtsize, "%s.txt", file);
3787
return ast_filedelete(file, NULL);
3791
* \brief utility used by inchar(), for base_encode()
3793
static int inbuf(struct baseio *bio, FILE *fi)
3800
if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
3815
* \brief utility used by base_encode()
3817
static int inchar(struct baseio *bio, FILE *fi)
3819
if (bio->iocp>=bio->iolen) {
3820
if (!inbuf(bio, fi))
3824
return bio->iobuf[bio->iocp++];
3828
* \brief utility used by base_encode()
3830
static int ochar(struct baseio *bio, int c, FILE *so)
3832
if (bio->linelength >= BASELINELEN) {
3833
if (fputs(eol,so) == EOF)
3839
if (putc(((unsigned char)c),so) == EOF)
3848
* \brief Performs a base 64 encode algorithm on the contents of a File
3849
* \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
3850
* \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
3852
* TODO: shouldn't this (and the above 3 support functions) be put into some kind of external utility location, such as funcs/func_base64.c ?
3854
* \return zero on success, -1 on error.
3856
static int base_encode(char *filename, FILE *so)
3858
static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
3859
'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
3860
'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
3861
'1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
3866
memset(&bio, 0, sizeof(bio));
3867
bio.iocp = BASEMAXINLINE;
3869
if (!(fi = fopen(filename, "rb"))) {
3870
ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
3875
unsigned char igroup[3], ogroup[4];
3878
igroup[0]= igroup[1]= igroup[2]= 0;
3880
for (n= 0;n<3;n++) {
3881
if ((c = inchar(&bio, fi)) == EOF) {
3886
igroup[n]= (unsigned char)c;
3890
ogroup[0]= dtable[igroup[0]>>2];
3891
ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
3892
ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
3893
ogroup[3]= dtable[igroup[2]&0x3F];
3903
ochar(&bio, ogroup[i], so);
3909
if (fputs(eol,so)==EOF)
3915
static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category, const char *flag)
3918
char fromdir[256], fromfile[256];
3919
struct ast_config *msg_cfg;
3920
const char *origcallerid, *origtime;
3921
char origcidname[80], origcidnum[80], origdate[80];
3923
struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
3925
/* Prepare variables for substitution in email body and subject */
3926
pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
3927
pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
3928
snprintf(passdata, passdatasize, "%d", msgnum);
3929
pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
3930
pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
3931
pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
3932
pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
3933
ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
3934
pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
3935
pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
3936
pbx_builtin_setvar_helper(ast, "VM_DATE", date);
3937
pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
3938
pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
3940
/* Retrieve info from VM attribute file */
3941
make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
3942
make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
3943
if (strlen(fromfile) < sizeof(fromfile) - 5) {
3944
strcat(fromfile, ".txt");
3946
if (!(msg_cfg = ast_config_load(fromfile, config_flags))) {
3947
if (option_debug > 0) {
3948
ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
3953
if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
3954
pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
3955
ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
3956
pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
3957
pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
3960
if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
3961
struct timeval tv = { inttime, };
3963
ast_localtime(&tv, &tm, NULL);
3964
ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
3965
pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
3967
ast_config_destroy(msg_cfg);
3971
* \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
3972
* \param from The string to work with.
3973
* \param to The string to write the modified quoted string. This buffer should be sufficiently larger than the from string, so as to allow it to be expanded by the surrounding quotes and escaping of internal quotes.
3975
* \return The destination string with quotes wrapped on it (the to field).
3977
static char *quote(const char *from, char *to, size_t len)
3981
for (; ptr < to + len - 1; from++) {
3984
else if (*from == '\0')
3988
if (ptr < to + len - 1)
3995
* fill in *tm for current time according to the proper timezone, if any.
3996
* Return tm so it can be used as a function argument.
3998
static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
4000
const struct vm_zone *z = NULL;
4001
struct timeval t = ast_tvnow();
4003
/* Does this user have a timezone specified? */
4004
if (!ast_strlen_zero(vmu->zonetag)) {
4005
/* Find the zone in the list */
4006
AST_LIST_LOCK(&zones);
4007
AST_LIST_TRAVERSE(&zones, z, list) {
4008
if (!strcmp(z->name, vmu->zonetag))
4011
AST_LIST_UNLOCK(&zones);
4013
ast_localtime(&t, tm, z ? z->timezone : NULL);
4017
/*!\brief Check if the string would need encoding within the MIME standard, to
4018
* avoid confusing certain mail software that expects messages to be 7-bit
4021
static int check_mime(const char *str)
4023
for (; *str; str++) {
4024
if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
4031
/*!\brief Encode a string according to the MIME rules for encoding strings
4032
* that are not 7-bit clean or contain control characters.
4034
* Additionally, if the encoded string would exceed the MIME limit of 76
4035
* characters per line, then the encoding will be broken up into multiple
4036
* sections, separated by a space character, in order to facilitate
4037
* breaking up the associated header across multiple lines.
4039
* \param start A string to be encoded
4040
* \param end An expandable buffer for holding the result
4041
* \param preamble The length of the first line already used for this string,
4042
* to ensure that each line maintains a maximum length of 76 chars.
4043
* \param postamble the length of any additional characters appended to the
4044
* line, used to ensure proper field wrapping.
4045
* \retval The encoded string.
4047
static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
4050
int first_section = 1;
4051
size_t endlen = 0, tmplen = 0;
4054
tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
4055
for (; *start; start++) {
4056
int need_encoding = 0;
4057
if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
4060
if ((first_section && need_encoding && preamble + tmplen > 70) ||
4061
(first_section && !need_encoding && preamble + tmplen > 72) ||
4062
(!first_section && need_encoding && tmplen > 70) ||
4063
(!first_section && !need_encoding && tmplen > 72)) {
4064
/* Start new line */
4065
endlen += snprintf(end + endlen, endsize - endlen, "%s%s?=", first_section ? "" : " ", tmp);
4066
tmplen = snprintf(tmp, sizeof(tmp), "=?%s?Q?", charset);
4069
if (need_encoding && *start == ' ') {
4070
tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "_");
4071
} else if (need_encoding) {
4072
tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "=%hhX", *start);
4074
tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
4077
snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
4082
* \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
4083
* \param p The output file to generate the email contents into.
4084
* \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
4085
* \param vmu The voicemail user who is sending the voicemail.
4086
* \param msgnum The message index in the mailbox folder.
4088
* \param mailbox The voicemail box to read the voicemail to be notified in this email.
4089
* \param cidnum The caller ID number.
4090
* \param cidname The caller ID name.
4091
* \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
4092
* \param format The message sound file format. i.e. .wav
4093
* \param duration The time of the message content, in seconds.
4094
* \param attach_user_voicemail if 1, the sound file is attached to the email.
4097
* \param imap if == 1, indicates the target folder for the email notification to be sent to will be an IMAP mailstore. This causes additional mailbox headers to be set, which would facilitate searching for the email in the destination IMAP folder.
4099
* The email body, and base 64 encoded attachement (if any) are stored to the file identified by *p. This method does not actually send the email. That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function.
4101
static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag)
4104
char host[MAXHOSTNAMELEN] = "";
4109
char enc_cidnum[256] = "", enc_cidname[256] = "";
4110
char *passdata = NULL, *passdata2;
4111
size_t len_passdata = 0, len_passdata2, tmplen;
4112
char *greeting_attachment;
4121
/* One alloca for multiple fields */
4122
len_passdata2 = strlen(vmu->fullname);
4123
if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
4124
len_passdata2 = tmplen;
4126
if ((tmplen = strlen(fromstring)) > len_passdata2) {
4127
len_passdata2 = tmplen;
4129
len_passdata2 = len_passdata2 * 3 + 200;
4130
passdata2 = alloca(len_passdata2);
4133
strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
4136
strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
4138
gethostname(host, sizeof(host) - 1);
4140
if (strchr(srcemail, '@'))
4141
ast_copy_string(who, srcemail, sizeof(who));
4143
snprintf(who, sizeof(who), "%s@%s", srcemail, host);
4145
greeting_attachment = strrchr(ast_strdupa(attach), '/');
4146
if (greeting_attachment)
4147
*greeting_attachment++ = '\0';
4149
snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
4150
ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
4151
fprintf(p, "Date: %s" ENDL, date);
4153
/* Set date format for voicemail mail */
4154
ast_strftime(date, sizeof(date), emaildateformat, &tm);
4156
if (!ast_strlen_zero(fromstring)) {
4157
struct ast_channel *ast;
4158
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
4160
memset(passdata2, 0, len_passdata2);
4161
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, passdata2, len_passdata2, category, flag);
4162
pbx_substitute_variables_helper(ast, fromstring, passdata2, len_passdata2);
4163
len_passdata = strlen(passdata2) * 3 + 300;
4164
passdata = alloca(len_passdata);
4165
if (check_mime(passdata2)) {
4167
encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
4168
while ((ptr = strchr(passdata, ' '))) {
4170
fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
4174
fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
4176
fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
4178
ast_channel_free(ast);
4180
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4183
fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
4186
if (check_mime(vmu->fullname)) {
4189
encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
4190
while ((ptr = strchr(passdata2, ' '))) {
4192
fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
4194
passdata2 = ptr + 1;
4196
fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
4198
fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
4200
if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
4201
char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
4202
struct ast_channel *ast;
4203
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
4204
int vmlen = strlen(e_subj) * 3 + 200;
4205
/* Only allocate more space if the previous was not large enough */
4206
if (vmlen > len_passdata) {
4207
passdata = alloca(vmlen);
4208
len_passdata = vmlen;
4211
memset(passdata, 0, len_passdata);
4212
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, len_passdata, category, flag);
4213
pbx_substitute_variables_helper(ast, e_subj, passdata, len_passdata);
4214
if (check_mime(passdata)) {
4217
encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
4218
while ((ptr = strchr(passdata2, ' '))) {
4220
fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
4222
passdata2 = ptr + 1;
4224
fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
4226
fprintf(p, "Subject: %s" ENDL, passdata);
4228
ast_channel_free(ast);
4230
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4232
} else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
4233
if (ast_strlen_zero(flag)) {
4234
fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
4236
fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
4239
if (ast_strlen_zero(flag)) {
4240
fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
4242
fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
4246
fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
4248
/* additional information needed for IMAP searching */
4249
fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
4250
/* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
4251
fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
4252
fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
4254
fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
4256
fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
4258
/* flag added for Urgent */
4259
fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
4260
fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
4261
fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
4262
fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
4263
fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
4264
fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
4265
if (!ast_strlen_zero(category)) {
4266
fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
4268
fprintf(p, "X-Asterisk-VM-Category: " ENDL);
4270
fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
4271
fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
4272
fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
4274
if (!ast_strlen_zero(cidnum)) {
4275
fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
4277
if (!ast_strlen_zero(cidname)) {
4278
fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
4280
fprintf(p, "MIME-Version: 1.0" ENDL);
4281
if (attach_user_voicemail) {
4282
/* Something unique. */
4283
snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
4285
fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
4286
fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
4287
fprintf(p, "--%s" ENDL, bound);
4289
fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
4290
if (emailbody || vmu->emailbody) {
4291
char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
4292
struct ast_channel *ast;
4293
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
4295
int vmlen = strlen(e_body) * 3 + 200;
4296
passdata = alloca(vmlen);
4297
memset(passdata, 0, vmlen);
4298
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
4299
pbx_substitute_variables_helper(ast, e_body, passdata, vmlen);
4300
fprintf(p, "%s" ENDL, passdata);
4301
ast_channel_free(ast);
4303
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4304
} else if (msgnum > -1) {
4305
if (strcmp(vmu->mailbox, mailbox)) {
4306
/* Forwarded type */
4307
struct ast_config *msg_cfg;
4310
char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
4311
struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
4312
/* Retrieve info from VM attribute file */
4313
make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
4314
make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
4315
if (strlen(fromfile) < sizeof(fromfile) - 5) {
4316
strcat(fromfile, ".txt");
4318
if ((msg_cfg = ast_config_load(fromfile, config_flags))) {
4319
if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
4320
ast_copy_string(origcallerid, v, sizeof(origcallerid));
4323
/* You might be tempted to do origdate, except that a) it's in the wrong
4324
* format, and b) it's missing for IMAP recordings. */
4325
if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
4326
struct timeval tv = { inttime, };
4328
ast_localtime(&tv, &tm, NULL);
4329
ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
4331
fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
4332
" a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
4333
"(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
4334
" chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
4335
msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
4336
date, origcallerid, origdate);
4337
ast_config_destroy(msg_cfg);
4343
fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
4344
"%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
4345
"want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
4346
ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
4347
(cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
4350
fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
4351
"Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
4354
if (imap || attach_user_voicemail) {
4355
if (!ast_strlen_zero(attach2)) {
4356
snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
4357
ast_debug(5, "creating second attachment filename %s\n", filename);
4358
add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
4359
snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
4360
ast_debug(5, "creating attachment filename %s\n", filename);
4361
add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
4363
snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
4364
ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
4365
add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
4370
static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
4372
char tmpdir[256], newtmp[256];
4377
/* Eww. We want formats to tell us their own MIME type */
4378
char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
4380
if (vmu->volgain < -.001 || vmu->volgain > .001) {
4381
create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
4382
snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
4383
tmpfd = mkstemp(newtmp);
4384
chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
4385
ast_debug(3, "newtmp: %s\n", newtmp);
4388
snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
4389
if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
4391
ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
4393
ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
4394
soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
4395
ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
4399
fprintf(p, "--%s" ENDL, bound);
4401
fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
4403
fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
4404
fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
4405
fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
4407
fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
4409
fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
4410
snprintf(fname, sizeof(fname), "%s.%s", attach, format);
4411
base_encode(fname, p);
4413
fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
4423
static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag)
4426
char tmp[80] = "/tmp/astmail-XXXXXX";
4429
if (vmu && ast_strlen_zero(vmu->email)) {
4430
ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
4433
if (!strcmp(format, "wav49"))
4435
ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
4436
/* Make a temporary file instead of piping directly to sendmail, in case the mail
4438
if ((p = vm_mkftemp(tmp)) == NULL) {
4439
ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
4442
make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
4444
snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
4445
ast_safe_system(tmp2);
4446
ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
4451
static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category, const char *flag)
4454
char host[MAXHOSTNAMELEN] = "";
4457
char tmp[80] = "/tmp/astmail-XXXXXX";
4458
char tmp2[PATH_MAX];
4462
if ((p = vm_mkftemp(tmp)) == NULL) {
4463
ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
4466
gethostname(host, sizeof(host)-1);
4467
if (strchr(srcemail, '@'))
4468
ast_copy_string(who, srcemail, sizeof(who));
4470
snprintf(who, sizeof(who), "%s@%s", srcemail, host);
4471
snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
4472
ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
4473
fprintf(p, "Date: %s\n", date);
4475
if (*pagerfromstring) {
4476
struct ast_channel *ast;
4477
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
4479
int vmlen = strlen(fromstring)*3 + 200;
4480
passdata = alloca(vmlen);
4481
memset(passdata, 0, vmlen);
4482
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
4483
pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
4484
fprintf(p, "From: %s <%s>\n", passdata, who);
4485
ast_channel_free(ast);
4487
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4489
fprintf(p, "From: Asterisk PBX <%s>\n", who);
4490
fprintf(p, "To: %s\n", pager);
4492
struct ast_channel *ast;
4493
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
4495
int vmlen = strlen(pagersubject) * 3 + 200;
4496
passdata = alloca(vmlen);
4497
memset(passdata, 0, vmlen);
4498
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
4499
pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
4500
fprintf(p, "Subject: %s\n\n", passdata);
4501
ast_channel_free(ast);
4503
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4505
if (ast_strlen_zero(flag)) {
4506
fprintf(p, "Subject: New VM\n\n");
4508
fprintf(p, "Subject: New %s VM\n\n", flag);
4512
ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
4514
struct ast_channel *ast;
4515
if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
4517
int vmlen = strlen(pagerbody) * 3 + 200;
4518
passdata = alloca(vmlen);
4519
memset(passdata, 0, vmlen);
4520
prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, passdata, vmlen, category, flag);
4521
pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
4522
fprintf(p, "%s\n", passdata);
4523
ast_channel_free(ast);
4525
ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4527
fprintf(p, "New %s long %s msg in box %s\n"
4528
"from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
4531
snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
4532
ast_safe_system(tmp2);
4533
ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
4538
* \brief Gets the current date and time, as formatted string.
4539
* \param s The buffer to hold the output formatted date.
4540
* \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
4542
* The date format string used is "%a %b %e %r UTC %Y".
4544
* \return zero on success, -1 on error.
4546
static int get_date(char *s, int len)
4549
struct timeval t = ast_tvnow();
4551
ast_localtime(&t, &tm, "UTC");
4553
return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
4556
static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
4560
char dest[PATH_MAX];
4562
snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
4564
if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
4565
ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
4569
RETRIEVE(fn, -1, ext, context);
4570
if (ast_fileexists(fn, NULL, NULL) > 0) {
4571
res = ast_stream_and_wait(chan, fn, ecodes);
4577
/* Dispose just in case */
4579
res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
4582
res = ast_say_digit_str(chan, ext, ecodes, chan->language);
4586
res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
4590
static void free_zone(struct vm_zone *z)
4596
static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
4600
SQLHSTMT stmt = NULL;
4603
char tmp[PATH_MAX] = "";
4604
struct odbc_obj *obj = NULL;
4606
struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
4615
/* If no mailbox, return immediately */
4616
if (ast_strlen_zero(mailbox))
4619
ast_copy_string(tmp, mailbox, sizeof(tmp));
4621
if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
4623
char *next, *remaining = tmp;
4624
while ((next = strsep(&remaining, " ,"))) {
4625
if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
4641
context = strchr(tmp, '@');
4646
context = "default";
4648
if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
4651
snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
4652
if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
4653
ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4656
res = SQLFetch(stmt);
4657
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
4658
ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4661
res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4662
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
4663
ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4666
*newmsgs = atoi(rowdata);
4667
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
4671
snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
4672
if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
4673
ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4676
res = SQLFetch(stmt);
4677
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
4678
ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4681
res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4682
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
4683
ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4686
SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4687
*oldmsgs = atoi(rowdata);
4691
snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
4692
if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
4693
ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4696
res = SQLFetch(stmt);
4697
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
4698
ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4701
res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4702
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
4703
ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4706
*urgentmsgs = atoi(rowdata);
4712
ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4716
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
4719
ast_odbc_release_obj(obj);
4725
static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
4727
return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
4731
* \brief Gets the number of messages that exist in a mailbox folder.
4736
* This method is used when ODBC backend is used.
4737
* \return The number of messages in this mailbox folder (zero or more).
4739
static int messagecount(const char *context, const char *mailbox, const char *folder)
4741
struct odbc_obj *obj = NULL;
4744
SQLHSTMT stmt = NULL;
4747
struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
4750
/* If no mailbox, return immediately */
4751
if (ast_strlen_zero(mailbox))
4754
obj = ast_odbc_request_obj(odbc_database, 0);
4756
snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
4757
stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4759
ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4762
res = SQLFetch(stmt);
4763
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
4764
ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4765
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
4768
res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4769
if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
4770
ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4771
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
4774
nummsgs = atoi(rowdata);
4775
SQLFreeHandle (SQL_HANDLE_STMT, stmt);
4777
ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4781
ast_odbc_release_obj(obj);
4786
* \brief Determines if the given folder has messages.
4787
* \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
4789
* This function is used when the mailbox is stored in an ODBC back end.
4790
* This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
4791
* \return 1 if the folder has one or more messages. zero otherwise.
4793
static int has_voicemail(const char *mailbox, const char *folder)
4795
char tmp[256], *tmp2 = tmp, *box, *context;
4796
ast_copy_string(tmp, mailbox, sizeof(tmp));
4797
while ((context = box = strsep(&tmp2, ","))) {
4798
strsep(&context, "@");
4799
if (ast_strlen_zero(context))
4800
context = "default";
4801
if (messagecount(context, box, folder))
4807
#ifndef IMAP_STORAGE
4809
* \brief Copies a message from one mailbox to another.
4819
* This is only used by file storage based mailboxes.
4821
* \return zero on success, -1 on error.
4823
static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag)
4825
char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
4826
const char *frombox = mbox(imbox);
4829
ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
4831
if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
4832
create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "Urgent");
4834
create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
4838
make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
4840
ast_copy_string(fromdir, dir, sizeof(fromdir));
4842
make_file(frompath, sizeof(frompath), fromdir, msgnum);
4843
make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
4845
if (vm_lock_path(todir))
4846
return ERROR_LOCK_PATH;
4848
recipmsgnum = last_message_index(recip, todir) + 1;
4849
if (recipmsgnum < recip->maxmsg) {
4850
make_file(topath, sizeof(topath), todir, recipmsgnum);
4851
if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
4852
COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
4854
/* For ODBC storage, if the file we want to copy isn't yet in the database, then the SQL
4855
* copy will fail. Instead, we need to create a local copy, store it, and delete the local
4856
* copy. We don't have to #ifdef this because if file storage reaches this point, there's a
4857
* much worse problem happening and IMAP storage doesn't call this function
4859
copy_plain_file(frompath, topath);
4860
STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
4864
ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
4866
ast_unlock_path(todir);
4867
notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
4872
#if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
4874
static int messagecount(const char *context, const char *mailbox, const char *folder)
4876
return __has_voicemail(context, mailbox, folder, 0);
4879
static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
4886
/* If no mailbox, return immediately */
4887
if (ast_strlen_zero(mailbox))
4890
if (ast_strlen_zero(folder))
4892
if (ast_strlen_zero(context))
4893
context = "default";
4895
snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
4897
if (!(dir = opendir(fn)))
4900
while ((de = readdir(dir))) {
4901
if (!strncasecmp(de->d_name, "msg", 3)) {
4905
} else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
4906
if (shortcircuit) return 1;
4914
/* If we are checking INBOX, we should check Urgent as well */
4915
if (strcmp(folder, "INBOX") == 0) {
4916
return (ret + __has_voicemail(context, mailbox, "Urgent", shortcircuit));
4923
* \brief Determines if the given folder has messages.
4924
* \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
4925
* \param folder the folder to look in
4927
* This function is used when the mailbox is stored in a filesystem back end.
4928
* This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
4929
* \return 1 if the folder has one or more messages. zero otherwise.
4931
static int has_voicemail(const char *mailbox, const char *folder)
4933
char tmp[256], *tmp2 = tmp, *box, *context;
4934
ast_copy_string(tmp, mailbox, sizeof(tmp));
4935
while ((box = strsep(&tmp2, ","))) {
4936
if ((context = strchr(box, '@')))
4939
context = "default";
4940
if (__has_voicemail(context, box, folder, 1))
4947
static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
4952
/* If no mailbox, return immediately */
4953
if (ast_strlen_zero(mailbox))
4963
if (strchr(mailbox, ',')) {
4964
int tmpnew, tmpold, tmpurgent;
4967
ast_copy_string(tmp, mailbox, sizeof(tmp));
4969
while ((cur = strsep(&mb, ", "))) {
4970
if (!ast_strlen_zero(cur)) {
4971
if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
4979
*urgentmsgs += tmpurgent;
4986
ast_copy_string(tmp, mailbox, sizeof(tmp));
4988
if ((context = strchr(tmp, '@')))
4991
context = "default";
4994
*newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
4996
*oldmsgs = __has_voicemail(context, tmp, "Old", 0);
4998
*urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
5003
static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
5005
return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
5010
static void run_externnotify(char *context, char *extension, const char *flag)
5012
char arguments[255];
5013
char ext_context[256] = "";
5014
int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
5015
struct ast_smdi_mwi_message *mwi_msg;
5017
if (!ast_strlen_zero(context))
5018
snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
5020
ast_copy_string(ext_context, extension, sizeof(ext_context));
5023
if (ast_app_has_voicemail(ext_context, NULL))
5024
ast_smdi_mwi_set(smdi_iface, extension);
5026
ast_smdi_mwi_unset(smdi_iface, extension);
5028
if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
5029
ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
5030
if (!strncmp(mwi_msg->cause, "INV", 3))
5031
ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
5032
else if (!strncmp(mwi_msg->cause, "BLK", 3))
5033
ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
5034
ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
5035
ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
5037
ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
5041
if (!ast_strlen_zero(externnotify)) {
5042
if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
5043
ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
5045
snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &", externnotify, context, extension, newvoicemails, oldvoicemails, urgentvoicemails);
5046
ast_debug(1, "Executing %s\n", arguments);
5047
ast_safe_system(arguments);
5053
* \brief Variables used for saving a voicemail.
5055
* This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
5057
struct leave_vm_options {
5059
signed char record_gain;
5064
* \brief Prompts the user and records a voicemail to a mailbox.
5067
* \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
5071
* \return zero on success, -1 on error.
5073
static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
5076
int newmsgs, oldmsgs;
5078
char urgdir[PATH_MAX];
5080
char txtfile[PATH_MAX];
5081
char tmptxtfile[PATH_MAX];
5082
struct vm_state *vms = NULL;
5097
char tmpdir[PATH_MAX];
5099
char prefile[PATH_MAX] = "";
5100
char tempfile[PATH_MAX] = "";
5101
char ext_context[256] = "";
5104
char ecodes[17] = "#";
5105
struct ast_str *tmp = ast_str_create(16);
5107
struct ast_vm_user *vmu;
5108
struct ast_vm_user svm;
5109
const char *category = NULL;
5111
const char *alldtmf = "0123456789ABCD*#";
5114
ast_str_set(&tmp, 0, "%s", ext);
5115
ext = ast_str_buffer(tmp);
5116
if ((context = strchr(ext, '@'))) {
5118
tmpptr = strchr(context, '&');
5120
tmpptr = strchr(ext, '&');
5126
ast_channel_lock(chan);
5127
if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
5128
category = ast_strdupa(category);
5130
ast_channel_unlock(chan);
5132
if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
5133
ast_copy_string(flag, "Urgent", sizeof(flag));
5134
} else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
5135
ast_copy_string(flag, "PRIORITY", sizeof(flag));
5140
ast_debug(3, "Before find_user\n");
5141
if (!(vmu = find_user(&svm, context, ext))) {
5142
ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
5143
pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5147
/* Setup pre-file if appropriate */
5148
if (strcmp(vmu->context, "default"))
5149
snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
5151
ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
5153
/* Set the path to the prefile. Will be one of
5154
VM_SPOOL_DIRcontext/ext/busy
5155
VM_SPOOL_DIRcontext/ext/unavail
5156
Depending on the flag set in options.
5158
if (ast_test_flag(options, OPT_BUSY_GREETING)) {
5159
snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
5160
} else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
5161
snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
5163
/* Set the path to the tmpfile as
5164
VM_SPOOL_DIR/context/ext/temp
5165
and attempt to create the folder structure.
5167
snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
5168
if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
5169
ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
5173
RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
5174
if (ast_fileexists(tempfile, NULL, NULL) > 0)
5175
ast_copy_string(prefile, tempfile, sizeof(prefile));
5177
DISPOSE(tempfile, -1);
5178
/* It's easier just to try to make it than to check for its existence */
5179
create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
5181
/* Check current or macro-calling context for special extensions */
5182
if (ast_test_flag(vmu, VM_OPERATOR)) {
5183
if (!ast_strlen_zero(vmu->exit)) {
5184
if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
5185
strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
5188
} else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
5189
strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
5191
} else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
5192
strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
5197
if (!ast_strlen_zero(vmu->exit)) {
5198
if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
5199
strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
5200
} else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
5201
strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
5202
else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
5203
strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
5207
if (ast_test_flag(options, OPT_DTMFEXIT)) {
5208
for (code = alldtmf; *code; code++) {
5211
if (strchr(ecodes, e[0]) == NULL && ast_canmatch_extension(chan, chan->context, e, 1, chan->cid.cid_num))
5212
strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
5216
/* Play the beginning intro if desired */
5217
if (!ast_strlen_zero(prefile)) {
5221
RETRIEVE(prefile, -1, ext, context);
5222
if (ast_fileexists(prefile, NULL, NULL) > 0) {
5223
if (ast_streamfile(chan, prefile, chan->language) > -1)
5224
res = ast_waitstream(chan, ecodes);
5226
if (success == -1) {
5227
/* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
5228
ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
5229
store_file(prefile, vmu->mailbox, vmu->context, -1);
5233
ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
5234
res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
5236
DISPOSE(prefile, -1);
5238
ast_debug(1, "Hang up during prefile playback\n");
5240
pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5246
/* On a '#' we skip the instructions */
5247
ast_set_flag(options, OPT_SILENT);
5250
if (!res && !ast_test_flag(options, OPT_SILENT)) {
5251
res = ast_stream_and_wait(chan, INTRO, ecodes);
5253
ast_set_flag(options, OPT_SILENT);
5258
ast_stopstream(chan);
5259
/* Check for a '*' here in case the caller wants to escape from voicemail to something
5260
other than the operator -- an automated attendant or mailbox login for example */
5262
chan->exten[0] = 'a';
5263
chan->exten[1] = '\0';
5264
if (!ast_strlen_zero(vmu->exit)) {
5265
ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
5266
} else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
5267
ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
5271
pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
5276
/* Check for a '0' here */
5277
if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
5279
if (ouseexten || ousemacro) {
5280
chan->exten[0] = 'o';
5281
chan->exten[1] = '\0';
5282
if (!ast_strlen_zero(vmu->exit)) {
5283
ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
5284
} else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
5285
ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
5287
ast_play_and_wait(chan, "transfer");
5290
pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
5296
/* Allow all other digits to exit Voicemail and return to the dialplan */
5297
if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
5298
if (!ast_strlen_zero(options->exitcontext))
5299
ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
5301
pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
5308
pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5312
/* The meat of recording the message... All the announcements and beeps have been played*/
5313
ast_copy_string(fmt, vmfmts, sizeof(fmt));
5314
if (!ast_strlen_zero(fmt)) {
5318
/* Is ext a mailbox? */
5319
/* must open stream for this user to get info! */
5320
res = inboxcount(ext_context, &newmsgs, &oldmsgs);
5322
ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
5326
if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
5327
/* It is possible under certain circumstances that inboxcount did not
5328
* create a vm_state when it was needed. This is a catchall which will
5331
if (!(vms = create_vm_state_from_user(vmu))) {
5332
ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
5339
/* here is a big difference! We add one to it later */
5340
msgnum = newmsgs + oldmsgs;
5341
ast_debug(3, "Messagecount set to %d\n",msgnum);
5342
snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
5343
/* set variable for compatibility */
5344
pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
5346
/* Check if mailbox is full */
5347
check_quota(vms, imapfolder);
5348
if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
5349
ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
5350
ast_play_and_wait(chan, "vm-mailboxfull");
5355
/* Check if we have exceeded maxmsg */
5356
if (msgnum >= vmu->maxmsg) {
5357
ast_log(AST_LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
5358
ast_play_and_wait(chan, "vm-mailboxfull");
5363
if (count_messages(vmu, dir) >= vmu->maxmsg) {
5364
res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
5366
res = ast_waitstream(chan, "");
5367
ast_log(AST_LOG_WARNING, "No more messages possible\n");
5368
pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5373
snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
5374
txtdes = mkstemp(tmptxtfile);
5375
chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
5377
res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
5379
res = ast_waitstream(chan, "");
5380
ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
5381
pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5385
/* Now play the beep once we have the message number for our next message. */
5387
/* Unless we're *really* silent, try to send the beep */
5388
res = ast_stream_and_wait(chan, "beep", "");
5391
/* Store information in real-time storage */
5392
if (ast_check_realtime("voicemail_data")) {
5393
snprintf(priority, sizeof(priority), "%d", chan->priority);
5394
snprintf(origtime, sizeof(origtime), "%ld", (long)time(NULL));
5395
get_date(date, sizeof(date));
5396
ast_store_realtime("voicemail_data", "origmailbox", ext, "context", chan->context, "macrocontext", chan->macrocontext, "exten", chan->exten, "priority", priority, "callerchan", chan->name, "callerid", ast_callerid_merge(callerid, sizeof(callerid), chan->cid.cid_name, chan->cid.cid_num, "Unknown"), "origdate", date, "origtime", origtime, "category", S_OR(category,""), "filename", tmptxtfile, SENTINEL);
5399
/* Store information */
5400
txt = fdopen(txtdes, "w+");
5402
get_date(date, sizeof(date));
5405
"; Message Information file\n"
5424
ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
5425
date, (long)time(NULL),
5426
category ? category : "");
5428
ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
5429
res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, NULL, options->record_gain, vms, flag);
5432
fprintf(txt, "flag=%s\n", flag);
5433
if (duration < vmminsecs) {
5435
if (option_verbose > 2)
5436
ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminsecs);
5437
ast_filedelete(tmptxtfile, NULL);
5439
if (ast_check_realtime("voicemail_data")) {
5440
ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
5443
fprintf(txt, "duration=%d\n", duration);
5445
if (vm_lock_path(dir)) {
5446
ast_log(AST_LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
5448
ast_filedelete(tmptxtfile, NULL);
5450
} else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
5451
ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
5453
ast_unlock_path(dir);
5454
if (ast_check_realtime("voicemail_data")) {
5455
ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
5458
#ifndef IMAP_STORAGE
5459
msgnum = last_message_index(vmu, dir) + 1;
5461
make_file(fn, sizeof(fn), dir, msgnum);
5463
/* assign a variable with the name of the voicemail file */
5464
#ifndef IMAP_STORAGE
5465
pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
5467
pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
5470
snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
5471
ast_filerename(tmptxtfile, fn, NULL);
5472
rename(tmptxtfile, txtfile);
5474
/* Properly set permissions on voicemail text descriptor file.
5475
Unfortunately mkstemp() makes this file 0600 on most unix systems. */
5476
if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
5477
ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
5479
ast_unlock_path(dir);
5480
if (ast_check_realtime("voicemail_data")) {
5481
snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
5482
ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
5484
/* We must store the file first, before copying the message, because
5485
* ODBC storage does the entire copy with SQL.
5487
if (ast_fileexists(fn, NULL, NULL) > 0) {
5488
STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
5491
/* Are there to be more recipients of this message? */
5493
struct ast_vm_user recipu, *recip;
5496
exten = strsep(&tmpptr, "&");
5497
cntx = strchr(exten, '@');
5502
if ((recip = find_user(&recipu, cntx, exten))) {
5503
copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
5507
#ifndef IMAP_STORAGE
5508
if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
5509
/* Move the message from INBOX to Urgent folder if this is urgent! */
5513
/* It's easier just to try to make it than to check for its existence */
5514
create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
5515
ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n",sfn,dfn);
5516
x = last_message_index(vmu, urgdir) + 1;
5517
make_file(sfn, sizeof(sfn), dir, msgnum);
5518
make_file(dfn, sizeof(dfn), urgdir, x);
5519
RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
5522
/* Notification needs to happen after the copy, though. */
5523
if (ast_fileexists(fn, NULL, NULL)) {
5525
notify_new_message(chan, vmu, vms, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
5527
notify_new_message(chan, vmu, NULL, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), flag);
5531
/* Disposal needs to happen after the optional move and copy */
5532
if (ast_fileexists(fn, NULL, NULL)) {
5533
DISPOSE(dir, msgnum);
5543
if (duration < vmminsecs)
5544
/* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
5545
pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5547
pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
5549
ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
5554
/* expunge message - use UID Expunge if supported on IMAP server*/
5555
ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n",expungeonhangup);
5556
if (expungeonhangup == 1) {
5557
ast_mutex_lock(&vms->lock);
5558
#ifdef HAVE_IMAP_TK2006
5559
if (LEVELUIDPLUS (vms->mailstream)) {
5560
mail_expunge_full(vms->mailstream,NIL,EX_UID);
5563
mail_expunge(vms->mailstream);
5564
ast_mutex_unlock(&vms->lock);
5572
static int say_and_wait(struct ast_channel *chan, int num, const char *language)
5575
d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
5579
static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
5582
/* we must use mbox(x) folder names, and copy the message there */
5588
/* get the real IMAP message number for this message */
5589
snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
5591
ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(box));
5592
ast_mutex_lock(&vms->lock);
5593
/* if save to Old folder, put in INBOX as read */
5594
if (box == OLD_FOLDER) {
5595
mail_setflag(vms->mailstream, sequence, "\\Seen");
5596
mail_clearflag(vms->mailstream, sequence, "\\Unseen");
5597
} else if (box == NEW_FOLDER) {
5598
mail_setflag(vms->mailstream, sequence, "\\Unseen");
5599
mail_clearflag(vms->mailstream, sequence, "\\Seen");
5601
if (!strcasecmp(mbox(NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
5602
ast_mutex_unlock(&vms->lock);
5605
/* Create the folder if it don't exist */
5606
imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
5607
ast_debug(5, "Checking if folder exists: %s\n",mailbox);
5608
if (mail_create(vms->mailstream, mailbox) == NIL)
5609
ast_debug(5, "Folder exists.\n");
5611
ast_log(AST_LOG_NOTICE, "Folder %s created!\n",mbox(box));
5612
res = !mail_copy(vms->mailstream, sequence, (char *)mbox(box));
5613
ast_mutex_unlock(&vms->lock);
5616
char *dir = vms->curdir;
5617
char *username = vms->username;
5618
char *context = vmu->context;
5621
char ddir[PATH_MAX];
5622
const char *dbox = mbox(box);
5624
create_dirpath(ddir, sizeof(ddir), context, username, dbox);
5626
if (vm_lock_path(ddir))
5627
return ERROR_LOCK_PATH;
5629
x = last_message_index(vmu, ddir) + 1;
5631
if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
5633
for (i = 1; i <= x; i++) {
5634
/* Push files down a "slot". The oldest file (msg0000) will be deleted. */
5635
make_file(sfn, sizeof(sfn), ddir, i);
5636
make_file(dfn, sizeof(dfn), ddir, i - 1);
5637
if (EXISTS(ddir, i, sfn, NULL)) {
5638
RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
5643
if (x >= vmu->maxmsg) {
5644
ast_unlock_path(ddir);
5648
make_file(sfn, sizeof(sfn), dir, msg);
5649
make_file(dfn, sizeof(dfn), ddir, x);
5650
if (strcmp(sfn, dfn)) {
5651
COPY(dir, msg, ddir, x, username, context, sfn, dfn);
5653
ast_unlock_path(ddir);
5658
static int adsi_logo(unsigned char *buf)
5661
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
5662
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
5666
static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
5668
unsigned char buf[256];
5674
bytes += ast_adsi_data_mode(buf + bytes);
5675
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5678
bytes += adsi_logo(buf);
5679
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
5681
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
5683
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5684
bytes += ast_adsi_data_mode(buf + bytes);
5685
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5687
if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
5689
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
5690
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
5691
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5692
bytes += ast_adsi_voice_mode(buf + bytes, 0);
5693
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5700
bytes += ast_adsi_logo(buf);
5701
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
5702
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
5703
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5704
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5707
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
5708
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
5709
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
5710
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
5711
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
5712
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
5713
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
5716
/* Add another dot */
5718
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
5719
bytes += ast_adsi_voice_mode(buf + bytes, 0);
5721
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5722
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5726
/* These buttons we load but don't use yet */
5727
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
5728
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
5729
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
5730
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
5731
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
5732
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
5733
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
5736
/* Add another dot */
5738
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
5739
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5740
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5745
snprintf(num, sizeof(num), "%d", x);
5746
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
5748
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
5749
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
5752
/* Add another dot */
5754
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
5755
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5756
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5759
if (ast_adsi_end_download(chan)) {
5761
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
5762
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
5763
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5764
bytes += ast_adsi_voice_mode(buf + bytes, 0);
5765
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5769
bytes += ast_adsi_download_disconnect(buf + bytes);
5770
bytes += ast_adsi_voice_mode(buf + bytes, 0);
5771
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
5773
ast_debug(1, "Done downloading scripts...\n");
5778
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
5779
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5781
ast_debug(1, "Restarting session...\n");
5784
/* Load the session now */
5785
if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
5787
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
5789
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
5791
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5795
static void adsi_begin(struct ast_channel *chan, int *useadsi)
5798
if (!ast_adsi_available(chan))
5800
x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
5804
if (adsi_load_vmail(chan, useadsi)) {
5805
ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
5812
static void adsi_login(struct ast_channel *chan)
5814
unsigned char buf[256];
5816
unsigned char keys[8];
5818
if (!ast_adsi_available(chan))
5823
/* Set one key for next */
5824
keys[3] = ADSI_KEY_APPS + 3;
5826
bytes += adsi_logo(buf + bytes);
5827
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
5828
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
5829
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5830
bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
5831
bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
5832
bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
5833
bytes += ast_adsi_set_keys(buf + bytes, keys);
5834
bytes += ast_adsi_voice_mode(buf + bytes, 0);
5835
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5838
static void adsi_password(struct ast_channel *chan)
5840
unsigned char buf[256];
5842
unsigned char keys[8];
5844
if (!ast_adsi_available(chan))
5849
/* Set one key for next */
5850
keys[3] = ADSI_KEY_APPS + 3;
5852
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5853
bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
5854
bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
5855
bytes += ast_adsi_set_keys(buf + bytes, keys);
5856
bytes += ast_adsi_voice_mode(buf + bytes, 0);
5857
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5860
static void adsi_folders(struct ast_channel *chan, int start, char *label)
5862
unsigned char buf[256];
5864
unsigned char keys[8];
5867
if (!ast_adsi_available(chan))
5871
y = ADSI_KEY_APPS + 12 + start + x;
5872
if (y > ADSI_KEY_APPS + 12 + 4)
5874
keys[x] = ADSI_KEY_SKT | y;
5876
keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
5880
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
5881
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
5882
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5883
bytes += ast_adsi_set_keys(buf + bytes, keys);
5884
bytes += ast_adsi_voice_mode(buf + bytes, 0);
5886
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5889
static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
5892
unsigned char buf[256];
5893
char buf1[256], buf2[256];
5899
char datetime[21]="";
5902
unsigned char keys[8];
5906
if (!ast_adsi_available(chan))
5909
/* Retrieve important info */
5910
snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
5911
f = fopen(fn2, "r");
5914
if (!fgets((char *)buf, sizeof(buf), f)) {
5919
stringp = (char *)buf;
5920
strsep(&stringp, "=");
5921
val = strsep(&stringp, "=");
5922
if (!ast_strlen_zero(val)) {
5923
if (!strcmp((char *)buf, "callerid"))
5924
ast_copy_string(cid, val, sizeof(cid));
5925
if (!strcmp((char *)buf, "origdate"))
5926
ast_copy_string(datetime, val, sizeof(datetime));
5932
/* New meaning for keys */
5934
keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
5939
/* No prev key, provide "Folder" instead */
5940
keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
5942
if (vms->curmsg >= vms->lastmsg) {
5943
/* If last message ... */
5945
/* but not only message, provide "Folder" instead */
5946
keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
5947
bytes += ast_adsi_voice_mode(buf + bytes, 0);
5950
/* Otherwise if only message, leave blank */
5955
if (!ast_strlen_zero(cid)) {
5956
ast_callerid_parse(cid, &name, &num);
5960
name = "Unknown Caller";
5962
/* If deleted, show "undeleted" */
5964
if (vms->deleted[vms->curmsg])
5965
keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
5968
keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
5969
snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
5970
strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
5971
snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
5973
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
5974
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
5975
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
5976
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
5977
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
5978
bytes += ast_adsi_set_keys(buf + bytes, keys);
5979
bytes += ast_adsi_voice_mode(buf + bytes, 0);
5981
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
5984
static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
5987
unsigned char buf[256];
5988
unsigned char keys[8];
5992
if (!ast_adsi_available(chan))
5995
/* New meaning for keys */
5997
keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
6003
/* No prev key, provide "Folder" instead */
6004
keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6006
if (vms->curmsg >= vms->lastmsg) {
6007
/* If last message ... */
6009
/* but not only message, provide "Folder" instead */
6010
keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6012
/* Otherwise if only message, leave blank */
6017
/* If deleted, show "undeleted" */
6018
if (vms->deleted[vms->curmsg])
6019
keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
6022
keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
6023
bytes += ast_adsi_set_keys(buf + bytes, keys);
6024
bytes += ast_adsi_voice_mode(buf + bytes, 0);
6026
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6029
static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
6031
unsigned char buf[256] = "";
6032
char buf1[256] = "", buf2[256] = "";
6034
unsigned char keys[8];
6037
char *newm = (vms->newmessages == 1) ? "message" : "messages";
6038
char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
6039
if (!ast_adsi_available(chan))
6041
if (vms->newmessages) {
6042
snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
6043
if (vms->oldmessages) {
6044
strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
6045
snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
6047
snprintf(buf2, sizeof(buf2), "%s.", newm);
6049
} else if (vms->oldmessages) {
6050
snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
6051
snprintf(buf2, sizeof(buf2), "%s.", oldm);
6053
strcpy(buf1, "You have no messages.");
6057
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
6058
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
6059
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6062
keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
6066
/* Don't let them listen if there are none */
6067
if (vms->lastmsg < 0)
6069
bytes += ast_adsi_set_keys(buf + bytes, keys);
6071
bytes += ast_adsi_voice_mode(buf + bytes, 0);
6073
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6076
static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
6078
unsigned char buf[256] = "";
6079
char buf1[256] = "", buf2[256] = "";
6081
unsigned char keys[8];
6084
char *mess = (vms->lastmsg == 0) ? "message" : "messages";
6086
if (!ast_adsi_available(chan))
6089
/* Original command keys */
6091
keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
6096
if ((vms->lastmsg + 1) < 1)
6099
snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
6100
strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
6102
if (vms->lastmsg + 1)
6103
snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
6105
strcpy(buf2, "no messages.");
6106
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
6107
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
6108
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
6109
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6110
bytes += ast_adsi_set_keys(buf + bytes, keys);
6112
bytes += ast_adsi_voice_mode(buf + bytes, 0);
6114
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6119
static void adsi_clear(struct ast_channel *chan)
6123
if (!ast_adsi_available(chan))
6125
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6126
bytes += ast_adsi_voice_mode(buf + bytes, 0);
6128
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6132
static void adsi_goodbye(struct ast_channel *chan)
6134
unsigned char buf[256];
6137
if (!ast_adsi_available(chan))
6139
bytes += adsi_logo(buf + bytes);
6140
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
6141
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
6142
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6143
bytes += ast_adsi_voice_mode(buf + bytes, 0);
6145
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6148
/*!\brief get_folder: Folder menu
6149
* Plays "press 1 for INBOX messages" etc.
6150
* Should possibly be internationalized
6152
static int get_folder(struct ast_channel *chan, int start)
6157
d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
6160
for (x = start; x< 5; x++) { /* For all folders */
6161
if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
6163
d = ast_play_and_wait(chan, "vm-for"); /* "for" */
6166
snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
6167
d = vm_play_folder_name(chan, fn);
6170
d = ast_waitfordigit(chan, 500);
6174
d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
6177
d = ast_waitfordigit(chan, 4000);
6182
* \brief plays a prompt and waits for a keypress.
6184
* \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
6185
* \param start Does not appear to be used at this time.
6187
* This is used by the main menu option to move a message to a folder or to save a message into a folder.
6188
* After playing the message identified by the fn parameter value, it calls get_folder(), which plays the
6189
* prompting for the number inputs that correspond to the available folders.
6191
* \return zero on success, or -1 on error.
6193
static int get_folder2(struct ast_channel *chan, char *fn, int start)
6196
res = ast_play_and_wait(chan, fn); /* Folder name */
6197
while (((res < '0') || (res > '9')) &&
6198
(res != '#') && (res >= 0)) {
6199
res = get_folder(chan, 0);
6205
* \brief presents the option to prepend to an existing message when forwarding it.
6212
* \param record_gain
6216
* Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
6218
* This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
6219
* \return zero on success, -1 on error.
6221
static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
6222
char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
6228
int retries = 0, prepend_duration = 0, already_recorded = 0;
6229
char msgfile[PATH_MAX], backup[PATH_MAX];
6230
char textfile[PATH_MAX];
6231
struct ast_config *msg_cfg;
6232
struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
6233
#ifndef IMAP_STORAGE
6234
signed char zero_gain = 0;
6236
const char *duration_str;
6238
/* Must always populate duration correctly */
6239
make_file(msgfile, sizeof(msgfile), curdir, curmsg);
6240
strcpy(textfile, msgfile);
6241
strcpy(backup, msgfile);
6242
strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
6243
strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
6245
if ((msg_cfg = ast_config_load(textfile, config_flags)) && msg_cfg != CONFIG_STATUS_FILEINVALID && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
6246
*duration = atoi(duration_str);
6251
while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
6258
/* Record new intro file */
6259
make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
6260
strncat(vms->introfn, "intro", sizeof(vms->introfn));
6261
res = ast_play_and_wait(chan, INTRO);
6262
res = ast_play_and_wait(chan, "beep");
6263
res = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *)duration, NULL, record_gain, vms, flag);
6267
/* prepend a message to the current message, update the metadata and return */
6269
make_file(msgfile, sizeof(msgfile), curdir, curmsg);
6270
strcpy(textfile, msgfile);
6271
strncat(textfile, ".txt", sizeof(textfile) - 1);
6274
/* if we can't read the message metadata, stop now */
6280
/* Back up the original file, so we can retry the prepend */
6281
if (already_recorded)
6282
ast_filecopy(backup, msgfile, NULL);
6284
ast_filecopy(msgfile, backup, NULL);
6285
already_recorded = 1;
6288
ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
6290
cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
6292
ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
6295
if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
6296
*duration = atoi(duration_str);
6298
if (prepend_duration) {
6299
struct ast_category *msg_cat;
6300
/* need enough space for a maximum-length message duration */
6301
char duration_buf[12];
6303
*duration += prepend_duration;
6304
msg_cat = ast_category_get(msg_cfg, "message");
6305
snprintf(duration_buf, 11, "%ld", *duration);
6306
if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
6307
ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
6314
/* NULL out introfile so we know there is no intro! */
6316
*vms->introfn = '\0';
6324
cmd = ast_play_and_wait(chan,"vm-forwardoptions");
6325
/* "Press 1 to prepend a message or 2 to forward the message without prepending" */
6327
cmd = ast_play_and_wait(chan,"vm-starmain");
6328
/* "press star to return to the main menu" */
6330
cmd = ast_waitfordigit(chan,6000);
6339
ast_config_destroy(msg_cfg);
6340
if (already_recorded)
6341
ast_filedelete(backup, NULL);
6342
if (prepend_duration)
6343
*duration = prepend_duration;
6345
if (cmd == 't' || cmd == 'S')
6350
static void queue_mwi_event(const char *box, int urgent, int new, int old)
6352
struct ast_event *event;
6353
char *mailbox, *context;
6355
/* Strip off @default */
6356
context = mailbox = ast_strdupa(box);
6357
strsep(&context, "@");
6358
if (ast_strlen_zero(context))
6359
context = "default";
6361
if (!(event = ast_event_new(AST_EVENT_MWI,
6362
AST_EVENT_IE_MAILBOX, AST_EVENT_IE_PLTYPE_STR, mailbox,
6363
AST_EVENT_IE_CONTEXT, AST_EVENT_IE_PLTYPE_STR, context,
6364
AST_EVENT_IE_NEWMSGS, AST_EVENT_IE_PLTYPE_UINT, (new+urgent),
6365
AST_EVENT_IE_OLDMSGS, AST_EVENT_IE_PLTYPE_UINT, old,
6366
AST_EVENT_IE_END))) {
6370
ast_event_queue_and_cache(event);
6374
* \brief Sends email notification that a user has a new voicemail waiting for them.
6381
* \param cidnum The Caller ID phone number value.
6382
* \param cidname The Caller ID name value.
6384
* \return zero on success, -1 on error.
6386
static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag)
6388
char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
6389
int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
6390
const char *category;
6391
char *myserveremail = serveremail;
6393
ast_channel_lock(chan);
6394
if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
6395
category = ast_strdupa(category);
6397
ast_channel_unlock(chan);
6399
make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
6400
make_file(fn, sizeof(fn), todir, msgnum);
6401
snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
6403
if (!ast_strlen_zero(vmu->attachfmt)) {
6404
if (strstr(fmt, vmu->attachfmt))
6405
fmt = vmu->attachfmt;
6407
ast_log(AST_LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->attachfmt, fmt, vmu->mailbox, vmu->context);
6410
/* Attach only the first format */
6411
fmt = ast_strdupa(fmt);
6413
strsep(&stringp, "|");
6415
if (!ast_strlen_zero(vmu->serveremail))
6416
myserveremail = vmu->serveremail;
6418
if (!ast_strlen_zero(vmu->email)) {
6419
int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
6420
if (!attach_user_voicemail)
6421
attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
6423
if (attach_user_voicemail)
6424
RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
6426
/* XXX possible imap issue, should category be NULL XXX */
6427
sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
6429
if (attach_user_voicemail)
6430
DISPOSE(todir, msgnum);
6433
if (!ast_strlen_zero(vmu->pager)) {
6434
sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(0), cidnum, cidname, duration, vmu, category, flag);
6437
if (ast_test_flag(vmu, VM_DELETE))
6438
DELETE(todir, msgnum, fn, vmu);
6440
/* Leave voicemail for someone */
6441
if (ast_app_has_voicemail(ext_context, NULL))
6442
ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
6444
queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
6446
manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
6447
run_externnotify(vmu->context, vmu->mailbox, flag);
6450
vm_delete(fn); /* Delete the file, but not the IMAP message */
6451
if (ast_test_flag(vmu, VM_DELETE)) { /* Delete the IMAP message if delete = yes */
6452
vm_imap_delete(NULL, vms->curmsg, vmu);
6453
vms->newmessages--; /* Fix new message count */
6461
* \brief Sends a voicemail message to a mailbox recipient.
6462
* \param ast_channel
6467
* \param is_new_message Used to indicate the mode for which this method was invoked.
6468
* Will be 0 when called to forward an existing message (option 8)
6469
* Will be 1 when called to leave a message (option 3->5)
6470
* \param record_gain
6472
* Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
6474
* When in the leave message mode (is_new_message == 1):
6475
* - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
6476
* - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
6478
* When in the forward message mode (is_new_message == 0):
6479
* - retreives the current message to be forwarded
6480
* - copies the original message to a temporary file, so updates to the envelope can be done.
6481
* - determines the target mailbox and folders
6482
* - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
6484
* \return zero on success, -1 on error.
6486
static int forward_message(struct ast_channel *chan, char *context, struct vm_state *vms, struct ast_vm_user *sender, char *fmt, int is_new_message, signed char record_gain, int urgent)
6490
struct vm_state *dstvms;
6492
char username[70]="";
6493
char fn[PATH_MAX]; /* for playback of name greeting */
6494
char ecodes[16] = "#";
6495
int res = 0, cmd = 0;
6496
struct ast_vm_user *receiver = NULL, *vmtmp;
6497
AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
6500
int saved_messages = 0, found = 0;
6501
int valid_extensions = 0;
6504
char urgent_str[7] = "";
6505
char tmptxtfile[PATH_MAX];
6507
if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
6508
ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
6511
if (vms == NULL) return -1;
6513
curmsg = vms->curmsg;
6515
tmptxtfile[0] = '\0';
6516
while (!res && !valid_extensions) {
6517
int use_directory = 0;
6518
if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
6522
while ((cmd >= 0) && !done ){
6539
/* Press 1 to enter an extension press 2 to use the directory */
6540
cmd = ast_play_and_wait(chan,"vm-forward");
6542
cmd = ast_waitfordigit(chan,3000);
6552
if (cmd < 0 || cmd == 't')
6556
if (use_directory) {
6557
/* use app_directory */
6559
char old_context[sizeof(chan->context)];
6560
char old_exten[sizeof(chan->exten)];
6562
struct ast_app* directory_app;
6564
directory_app = pbx_findapp("Directory");
6565
if (directory_app) {
6566
char vmcontext[256];
6567
/* make backup copies */
6568
memcpy(old_context, chan->context, sizeof(chan->context));
6569
memcpy(old_exten, chan->exten, sizeof(chan->exten));
6570
old_priority = chan->priority;
6572
/* call the the Directory, changes the channel */
6573
snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
6574
res = pbx_exec(chan, directory_app, vmcontext);
6576
ast_copy_string(username, chan->exten, sizeof(username));
6578
/* restore the old context, exten, and priority */
6579
memcpy(chan->context, old_context, sizeof(chan->context));
6580
memcpy(chan->exten, old_exten, sizeof(chan->exten));
6581
chan->priority = old_priority;
6583
ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
6584
ast_clear_flag((&globalflags), VM_DIRECFORWARD);
6587
/* Ask for an extension */
6588
res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
6591
if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
6595
/* start all over if no username */
6596
if (ast_strlen_zero(username))
6599
s = strsep(&stringp, "*");
6600
/* start optimistic */
6601
valid_extensions = 1;
6603
if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
6604
AST_LIST_INSERT_HEAD(&extensions, receiver, list);
6607
/* XXX Optimization for the future. When we encounter a single bad extension,
6608
* bailing out on all of the extensions may not be the way to go. We should
6609
* probably just bail on that single extension, then allow the user to enter
6612
while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
6613
free_user(receiver);
6615
ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
6616
valid_extensions = 0;
6620
/* play name if available, else play extension number */
6621
snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
6622
RETRIEVE(fn, -1, s, receiver->context);
6623
if (ast_fileexists(fn, NULL, NULL) > 0) {
6624
res = ast_stream_and_wait(chan, fn, ecodes);
6630
res = ast_say_digit_str(chan, s, ecodes, chan->language);
6634
s = strsep(&stringp, "*");
6636
/* break from the loop of reading the extensions */
6637
if (valid_extensions)
6639
/* "I am sorry, that's not a valid extension. Please try again." */
6640
res = ast_play_and_wait(chan, "pbx-invalid");
6642
/* check if we're clear to proceed */
6643
if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
6645
if (is_new_message == 1) {
6646
struct leave_vm_options leave_options;
6647
char mailbox[AST_MAX_EXTENSION * 2 + 2];
6648
snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
6650
/* Send VoiceMail */
6651
memset(&leave_options, 0, sizeof(leave_options));
6652
leave_options.record_gain = record_gain;
6653
cmd = leave_voicemail(chan, mailbox, &leave_options);
6655
/* Forward VoiceMail */
6657
struct vm_state vmstmp;
6658
memcpy(&vmstmp, vms, sizeof(vmstmp));
6660
RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
6662
cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
6664
AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
6666
int attach_user_voicemail;
6667
char *myserveremail = serveremail;
6669
/* get destination mailbox */
6670
dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
6672
dstvms = create_vm_state_from_user(vmtmp);
6675
init_mailstream(dstvms, 0);
6676
if (!dstvms->mailstream) {
6677
ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
6679
STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
6680
run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str);
6683
ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
6685
if (!ast_strlen_zero(vmtmp->serveremail))
6686
myserveremail = vmtmp->serveremail;
6687
attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
6688
/* NULL category for IMAP storage */
6689
sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, dstvms->curbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan, NULL, urgent_str);
6691
copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
6694
AST_LIST_REMOVE_CURRENT(list);
6699
AST_LIST_TRAVERSE_SAFE_END;
6700
if (saved_messages > 0) {
6701
/* give confirmation that the message was saved */
6702
/* commented out since we can't forward batches yet
6703
if (saved_messages == 1)
6704
res = ast_play_and_wait(chan, "vm-message");
6706
res = ast_play_and_wait(chan, "vm-messages");
6708
res = ast_play_and_wait(chan, "vm-saved"); */
6710
/* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
6711
if (ast_strlen_zero(vmstmp.introfn))
6713
res = ast_play_and_wait(chan, "vm-msgsaved");
6716
DISPOSE(dir, curmsg);
6719
/* If anything failed above, we still have this list to free */
6720
while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
6722
return res ? res : cmd;
6725
static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
6728
if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
6729
ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file);
6733
static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
6735
return ast_control_streamfile(chan, file, listen_control_forward_key, listen_control_reverse_key, listen_control_stop_key, listen_control_pause_key, listen_control_restart_key, skipms, NULL);
6738
static int play_message_category(struct ast_channel *chan, const char *category)
6742
if (!ast_strlen_zero(category))
6743
res = ast_play_and_wait(chan, category);
6746
ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
6753
static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
6756
struct vm_zone *the_zone = NULL;
6759
if (ast_get_time_t(origtime, &t, 0, NULL)) {
6760
ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
6764
/* Does this user have a timezone specified? */
6765
if (!ast_strlen_zero(vmu->zonetag)) {
6766
/* Find the zone in the list */
6768
AST_LIST_LOCK(&zones);
6769
AST_LIST_TRAVERSE(&zones, z, list) {
6770
if (!strcmp(z->name, vmu->zonetag)) {
6775
AST_LIST_UNLOCK(&zones);
6778
/* No internal variable parsing for now, so we'll comment it out for the time being */
6780
/* Set the DIFF_* variables */
6781
ast_localtime(&t, &time_now, NULL);
6782
tv_now = ast_tvnow();
6783
ast_localtime(&tv_now, &time_then, NULL);
6785
/* Day difference */
6786
if (time_now.tm_year == time_then.tm_year)
6787
snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
6789
snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
6790
pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
6792
/* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
6795
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
6796
} else if (!strncasecmp(chan->language, "de", 2)) { /* GERMAN syntax */
6797
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
6798
} else if (!strncasecmp(chan->language, "gr", 2)) { /* GREEK syntax */
6799
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
6800
} else if (!strncasecmp(chan->language, "it", 2)) { /* ITALIAN syntax */
6801
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
6802
} else if (!strncasecmp(chan->language, "nl", 2)) { /* DUTCH syntax */
6803
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
6804
} else if (!strncasecmp(chan->language, "no", 2)) { /* NORWEGIAN syntax */
6805
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
6806
} else if (!strncasecmp(chan->language, "pl", 2)) { /* POLISH syntax */
6807
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
6808
} else if (!strncasecmp(chan->language, "pt_BR", 5)) { /* Brazillian PORTUGUESE syntax */
6809
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
6810
} else if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
6811
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
6812
} else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
6813
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
6815
res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
6818
pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
6825
static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
6829
char *callerid, *name;
6830
char prefile[PATH_MAX] = "";
6833
/* If voicemail cid is not enabled, or we didn't get cid or context from
6834
* the attribute file, leave now.
6836
* TODO Still need to change this so that if this function is called by the
6837
* message envelope (and someone is explicitly requesting to hear the CID),
6838
* it does not check to see if CID is enabled in the config file.
6840
if ((cid == NULL)||(context == NULL))
6843
/* Strip off caller ID number from name */
6844
ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
6845
ast_callerid_parse(cid, &name, &callerid);
6846
if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
6847
/* Check for internal contexts and only */
6848
/* say extension when the call didn't come from an internal context in the list */
6849
for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
6850
ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
6851
if ((strcmp(cidinternalcontexts[i], context) == 0))
6854
if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
6856
snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
6857
if (!ast_strlen_zero(prefile)) {
6858
/* See if we can find a recorded name for this person instead of their extension number */
6859
if (ast_fileexists(prefile, NULL, NULL) > 0) {
6860
ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
6862
res = wait_file2(chan, vms, "vm-from");
6863
res = ast_stream_and_wait(chan, prefile, "");
6865
ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
6866
/* Say "from extension" as one saying to sound smoother */
6868
res = wait_file2(chan, vms, "vm-from-extension");
6869
res = ast_say_digit_str(chan, callerid, "", chan->language);
6874
ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
6875
/* Since this is all nicely figured out, why not say "from phone number" in this case? */
6877
res = wait_file2(chan, vms, "vm-from-phonenumber");
6878
res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
6881
/* Number unknown */
6882
ast_debug(1, "VM-CID: From an unknown number\n");
6883
/* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
6884
res = wait_file2(chan, vms, "vm-unknown-caller");
6889
static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
6894
/* Verify that we have a duration for the message */
6895
if (duration == NULL)
6898
/* Convert from seconds to minutes */
6899
durations=atoi(duration);
6900
durationm=(durations / 60);
6902
ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
6904
if ((!res) && (durationm >= minduration)) {
6905
res = wait_file2(chan, vms, "vm-duration");
6908
if (!strncasecmp(chan->language, "pl", 2)) {
6909
div_t num = div(durationm, 10);
6911
if (durationm == 1) {
6912
res = ast_play_and_wait(chan, "digits/1z");
6913
res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
6914
} else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
6917
res = ast_play_and_wait(chan, "digits/2-ie");
6919
res = say_and_wait(chan, durationm - 2 , chan->language);
6920
res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
6923
res = say_and_wait(chan, durationm, chan->language);
6925
res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
6927
res = say_and_wait(chan, durationm, chan->language);
6928
res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
6930
/* DEFAULT syntax */
6932
res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
6933
res = wait_file2(chan, vms, "vm-minutes");
6939
static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
6942
char filename[256], *cid;
6943
const char *origtime, *context, *category, *duration, *flag;
6944
struct ast_config *msg_cfg;
6945
struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
6948
make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
6949
adsi_message(chan, vms);
6951
res = wait_file2(chan, vms, "vm-first"); /* "First" */
6952
else if (vms->curmsg == vms->lastmsg)
6953
res = wait_file2(chan, vms, "vm-last"); /* "last" */
6955
snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
6956
RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
6957
msg_cfg = ast_config_load(filename, config_flags);
6958
if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
6959
ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
6962
flag = ast_variable_retrieve(msg_cfg, "message", "flag");
6964
/* Play the word urgent if we are listening to urgent messages */
6965
if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
6966
res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
6971
if (!strncasecmp(chan->language, "pl", 2)) {
6972
if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
6975
ten = (vms->curmsg + 1) / 10;
6976
one = (vms->curmsg + 1) % 10;
6978
if (vms->curmsg < 20) {
6979
snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
6980
res = wait_file2(chan, vms, nextmsg);
6982
snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
6983
res = wait_file2(chan, vms, nextmsg);
6986
snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
6987
res = wait_file2(chan, vms, nextmsg);
6993
res = wait_file2(chan, vms, "vm-message");
6995
} else if (!strncasecmp(chan->language, "he", 2)) {
6997
res = wait_file2(chan, vms, "vm-message");
6998
res = wait_file2(chan, vms, "vm-first");
6999
} else if (vms->curmsg == vms->lastmsg) {
7000
res = wait_file2(chan, vms, "vm-message");
7001
res = wait_file2(chan, vms, "vm-last");
7003
res = wait_file2(chan, vms, "vm-message");
7004
res = wait_file2(chan, vms, "vm-number");
7005
res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
7008
if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
7009
res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
7010
} else { /* DEFAULT syntax */
7011
res = wait_file2(chan, vms, "vm-message");
7013
if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
7015
res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
7022
ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
7026
if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
7027
ast_log(AST_LOG_WARNING, "No origtime?!\n");
7028
DISPOSE(vms->curdir, vms->curmsg);
7029
ast_config_destroy(msg_cfg);
7033
cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
7034
duration = ast_variable_retrieve(msg_cfg, "message", "duration");
7035
category = ast_variable_retrieve(msg_cfg, "message", "category");
7037
context = ast_variable_retrieve(msg_cfg, "message", "context");
7038
if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
7039
context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
7041
res = play_message_category(chan, category);
7043
if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
7044
res = play_message_datetime(chan, vmu, origtime, filename);
7045
if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
7046
res = play_message_callerid(chan, vms, cid, context, 0);
7047
if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
7048
res = play_message_duration(chan, vms, duration, vmu->saydurationm);
7049
/* Allow pressing '1' to skip envelope / callerid */
7052
ast_config_destroy(msg_cfg);
7055
make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
7056
vms->heard[vms->curmsg] = 1;
7058
/*IMAP storage stores any prepended message from a forward
7059
* as a separate file from the rest of the message
7061
if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
7062
wait_file(chan, vms, vms->introfn);
7065
if ((res = wait_file(chan, vms, vms->fn)) < 0) {
7066
ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
7070
DISPOSE(vms->curdir, vms->curmsg);
7075
static int imap_remove_file(char *dir, int msgnum)
7078
char full_fn[PATH_MAX];
7079
char intro[PATH_MAX] = {0,};
7082
make_file(fn, sizeof(fn), dir, msgnum);
7083
snprintf(intro, sizeof(intro), "%sintro", fn);
7085
ast_copy_string(fn, dir, sizeof(fn));
7087
if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
7088
ast_filedelete(fn, NULL);
7089
if (!ast_strlen_zero(intro)) {
7090
ast_filedelete(intro, NULL);
7092
snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
7100
static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
7102
char *file, *filename;
7109
file = strrchr(ast_strdupa(dir), '/');
7113
ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
7117
ast_mutex_lock(&vms->lock);
7118
for (i = 0; i < vms->mailstream->nmsgs; i++) {
7119
mail_fetchstructure(vms->mailstream, i + 1, &body);
7120
/* We have the body, now we extract the file name of the first attachment. */
7121
if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
7122
attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
7124
ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
7125
ast_mutex_unlock(&vms->lock);
7128
filename = strsep(&attachment, ".");
7129
if (!strcmp(filename, file)) {
7130
sprintf (arg,"%d", i+1);
7131
mail_setflag (vms->mailstream,arg,"\\DELETED");
7134
mail_expunge(vms->mailstream);
7135
ast_mutex_unlock(&vms->lock);
7140
#ifndef IMAP_STORAGE
7141
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
7143
int count_msg, last_msg;
7145
ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
7147
/* Rename the member vmbox HERE so that we don't try to return before
7148
* we know what's going on.
7150
snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
7152
/* Faster to make the directory than to check if it exists. */
7153
create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
7155
count_msg = count_messages(vmu, vms->curdir);
7159
vms->lastmsg = count_msg - 1;
7162
The following test is needed in case sequencing gets messed up.
7163
There appears to be more than one way to mess up sequence, so
7164
we will not try to find all of the root causes--just fix it when
7168
if (vm_lock_path(vms->curdir)) {
7169
ast_log(AST_LOG_ERROR, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
7173
last_msg = last_message_index(vmu, vms->curdir);
7174
ast_unlock_path(vms->curdir);
7184
static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
7187
#ifndef IMAP_STORAGE
7188
int res = 0, nummsg;
7192
if (vms->lastmsg <= -1)
7196
#ifndef IMAP_STORAGE
7197
/* Get the deleted messages fixed */
7198
if (vm_lock_path(vms->curdir))
7199
return ERROR_LOCK_PATH;
7201
for (x = 0; x < vmu->maxmsg; x++) {
7202
if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
7203
/* Save this message. It's not in INBOX or hasn't been heard */
7204
make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
7205
if (!EXISTS(vms->curdir, x, vms->fn, NULL))
7208
make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
7209
if (strcmp(vms->fn, fn2)) {
7210
RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
7212
} else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
7213
/* Move to old folder before deleting */
7214
res = save_to_folder(vmu, vms, x, 1);
7215
if (res == ERROR_LOCK_PATH) {
7216
/* If save failed do not delete the message */
7217
ast_log(AST_LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
7218
vms->deleted[x] = 0;
7222
} else if (vms->deleted[x] && vmu->maxdeletedmsg) {
7223
/* Move to deleted folder */
7224
res = save_to_folder(vmu, vms, x, 10);
7225
if (res == ERROR_LOCK_PATH) {
7226
/* If save failed do not delete the message */
7227
vms->deleted[x] = 0;
7231
} else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
7232
/* If realtime storage enabled - we should explicitly delete this message,
7233
cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
7234
make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
7235
if (EXISTS(vms->curdir, x, vms->fn, NULL))
7236
DELETE(vms->curdir, x, vms->fn, vmu);
7240
/* Delete ALL remaining messages */
7242
for (x = vms->curmsg + 1; x <= nummsg; x++) {
7243
make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
7244
if (EXISTS(vms->curdir, x, vms->fn, NULL))
7245
DELETE(vms->curdir, x, vms->fn, vmu);
7247
ast_unlock_path(vms->curdir);
7250
for (x=0;x < vmu->maxmsg;x++) {
7251
if (vms->deleted[x]) {
7252
ast_debug(3,"IMAP delete of %d\n",x);
7253
DELETE(vms->curdir, x, vms->fn, vmu);
7261
memset(vms->deleted, 0, vmu->maxmsg * sizeof(int));
7263
memset(vms->heard, 0, vmu->maxmsg * sizeof(int));
7268
/* In Greek even though we CAN use a syntax like "friends messages"
7269
* ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
7270
* ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
7271
* syntax for the above three categories which is more elegant.
7274
static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
7279
buf = alloca(strlen(box)+2);
7283
if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
7284
cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
7285
return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
7287
cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
7288
return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
7292
static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
7296
if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
7297
if (!strcasecmp(box, "vm-INBOX"))
7298
cmd = ast_play_and_wait(chan, "vm-new-e");
7300
cmd = ast_play_and_wait(chan, "vm-old-e");
7301
return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
7303
cmd = ast_play_and_wait(chan, "vm-messages");
7304
return cmd ? cmd : ast_play_and_wait(chan, box);
7308
static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
7312
if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
7313
cmd = ast_play_and_wait(chan, "vm-messages");
7314
return cmd ? cmd : ast_play_and_wait(chan, box);
7316
cmd = ast_play_and_wait(chan, box);
7317
return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
7321
static int vm_play_folder_name(struct ast_channel *chan, char *box)
7325
if ( !strncasecmp(chan->language, "it", 2) ||
7326
!strncasecmp(chan->language, "es", 2) ||
7327
!strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
7328
cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
7329
return cmd ? cmd : ast_play_and_wait(chan, box);
7330
} else if (!strncasecmp(chan->language, "gr", 2)) {
7331
return vm_play_folder_name_gr(chan, box);
7332
} else if (!strncasecmp(chan->language, "he", 2)) { /* Hebrew syntax */
7333
return ast_play_and_wait(chan, box);
7334
} else if (!strncasecmp(chan->language, "pl", 2)) {
7335
return vm_play_folder_name_pl(chan, box);
7336
} else if (!strncasecmp(chan->language, "ua", 2)) { /* Ukrainian syntax */
7337
return vm_play_folder_name_ua(chan, box);
7338
} else { /* Default English */
7339
cmd = ast_play_and_wait(chan, box);
7340
return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
7345
In greek the plural for old/new is
7346
different so we need the following files
7347
We also need vm-denExeteMynhmata because
7348
this syntax is different.
7350
-> vm-Olds.wav : "Palia"
7351
-> vm-INBOXs.wav : "Nea"
7352
-> vm-denExeteMynhmata : "den exete mynhmata"
7356
static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
7360
if (vms->newmessages) {
7361
res = ast_play_and_wait(chan, "vm-youhave");
7363
res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
7365
if ((vms->newmessages == 1)) {
7366
res = ast_play_and_wait(chan, "vm-INBOX");
7368
res = ast_play_and_wait(chan, "vm-message");
7370
res = ast_play_and_wait(chan, "vm-INBOXs");
7372
res = ast_play_and_wait(chan, "vm-messages");
7375
} else if (vms->oldmessages){
7376
res = ast_play_and_wait(chan, "vm-youhave");
7378
res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
7379
if ((vms->oldmessages == 1)){
7380
res = ast_play_and_wait(chan, "vm-Old");
7382
res = ast_play_and_wait(chan, "vm-message");
7384
res = ast_play_and_wait(chan, "vm-Olds");
7386
res = ast_play_and_wait(chan, "vm-messages");
7388
} else if (!vms->oldmessages && !vms->newmessages)
7389
res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
7393
/* Version of vm_intro() designed to work for many languages.
7395
* It is hoped that this function can prevent the proliferation of
7396
* language-specific vm_intro() functions and in time replace the language-
7397
* specific functions which already exist. An examination of the language-
7398
* specific functions revealed that they all corrected the same deficiencies
7399
* in vm_intro_en() (which was the default function). Namely:
7401
* 1) The vm-Old and vm-INBOX sound files were overloaded. The English
7402
* wording of the voicemail greeting hides this problem. For example,
7403
* vm-INBOX contains only the word "new". This means that both of these
7404
* sequences produce valid utterances:
7405
* * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
7406
* * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
7407
* However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
7408
* in many languages) the first utterance becomes "you have 1 the new message".
7409
* 2) The function contains hardcoded rules for pluralizing the word "message".
7410
* These rules are correct for English, but not for many other languages.
7411
* 3) No attempt is made to pluralize the adjectives ("old" and "new") as
7412
* required in many languages.
7413
* 4) The gender of the word for "message" is not specified. This is a problem
7414
* because in many languages the gender of the number in phrases such
7415
* as "you have one new message" must match the gender of the word
7416
* meaning "message".
7418
* Fixing these problems for each new language has meant duplication of effort.
7419
* This new function solves the problems in the following general ways:
7420
* 1) Add new sound files vm-new and vm-old. These can be linked to vm-INBOX
7421
* and vm-Old respectively for those languages where it makes sense.
7422
* 2) Call ast_say_counted_noun() to put the proper gender and number prefix
7424
* 3) Call ast_say_counted_adjective() to put the proper gender and number
7425
* prefix on vm-new and vm-old (none for English).
7426
* 4) Pass the gender of the language's word for "message" as an agument to
7427
* this function which is can in turn pass on to the functions which
7428
* say numbers and put endings on nounds and adjectives.
7430
* All languages require these messages:
7431
* vm-youhave "You have..."
7433
* vm-no "no" (in the sense of "none", as in "you have no messages")
7435
* To use it for English, you will need these additional sound files:
7437
* vm-message "message", singular
7438
* vm-messages "messages", plural
7440
* If you use it for Russian and other slavic languages, you will need these additional sound files:
7442
* vm-newn "novoye" (singular, neuter)
7443
* vm-newx "novikh" (counting plural form, genative plural)
7444
* vm-message "sobsheniye" (singular form)
7445
* vm-messagex1 "sobsheniya" (first counting plural form, genative singular)
7446
* vm-messagex2 "sobsheniy" (second counting plural form, genative plural)
7447
* digits/1n "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
7448
* digits/2n "dva" (neuter singular)
7450
static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
7455
res = ast_play_and_wait(chan, "vm-youhave");
7457
if (!res && vms->newmessages) {
7458
lastnum = vms->newmessages;
7460
if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
7461
res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
7464
if (!res && vms->oldmessages) {
7465
res = ast_play_and_wait(chan, "vm-and");
7469
if (!res && vms->oldmessages) {
7470
lastnum = vms->oldmessages;
7472
if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
7473
res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
7479
res = ast_play_and_wait(chan, "vm-no");
7482
res = ast_say_counted_noun(chan, lastnum, "vm-message");
7489
/* Default Hebrew syntax */
7490
static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
7494
/* Introduce messages they have */
7496
if ((vms->newmessages) || (vms->oldmessages)) {
7497
res = ast_play_and_wait(chan, "vm-youhave");
7500
* The word "shtei" refers to the number 2 in hebrew when performing a count
7501
* of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
7502
* an element, this is one of them.
7504
if (vms->newmessages) {
7506
if (vms->newmessages == 1) {
7507
res = ast_play_and_wait(chan, "vm-INBOX1");
7509
if (vms->newmessages == 2) {
7510
res = ast_play_and_wait(chan, "vm-shtei");
7512
res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
7514
res = ast_play_and_wait(chan, "vm-INBOX");
7517
if (vms->oldmessages && !res) {
7518
res = ast_play_and_wait(chan, "vm-and");
7519
if (vms->oldmessages == 1) {
7520
res = ast_play_and_wait(chan, "vm-Old1");
7522
if (vms->oldmessages == 2) {
7523
res = ast_play_and_wait(chan, "vm-shtei");
7525
res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
7527
res = ast_play_and_wait(chan, "vm-Old");
7531
if (!res && vms->oldmessages && !vms->newmessages) {
7533
if (vms->oldmessages == 1) {
7534
res = ast_play_and_wait(chan, "vm-Old1");
7536
if (vms->oldmessages == 2) {
7537
res = ast_play_and_wait(chan, "vm-shtei");
7539
res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
7541
res = ast_play_and_wait(chan, "vm-Old");
7546
if (!vms->oldmessages && !vms->newmessages) {
7548
res = ast_play_and_wait(chan, "vm-nomessages");
7556
/* Default English syntax */
7557
static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
7561
/* Introduce messages they have */
7562
res = ast_play_and_wait(chan, "vm-youhave");
7564
if (vms->urgentmessages) {
7565
res = say_and_wait(chan, vms->urgentmessages, chan->language);
7567
res = ast_play_and_wait(chan, "vm-Urgent");
7568
if ((vms->oldmessages || vms->newmessages) && !res) {
7569
res = ast_play_and_wait(chan, "vm-and");
7571
if ((vms->urgentmessages == 1))
7572
res = ast_play_and_wait(chan, "vm-message");
7574
res = ast_play_and_wait(chan, "vm-messages");
7577
if (vms->newmessages) {
7578
res = say_and_wait(chan, vms->newmessages, chan->language);
7580
res = ast_play_and_wait(chan, "vm-INBOX");
7581
if (vms->oldmessages && !res)
7582
res = ast_play_and_wait(chan, "vm-and");
7584
if ((vms->newmessages == 1))
7585
res = ast_play_and_wait(chan, "vm-message");
7587
res = ast_play_and_wait(chan, "vm-messages");
7591
if (!res && vms->oldmessages) {
7592
res = say_and_wait(chan, vms->oldmessages, chan->language);
7594
res = ast_play_and_wait(chan, "vm-Old");
7596
if (vms->oldmessages == 1)
7597
res = ast_play_and_wait(chan, "vm-message");
7599
res = ast_play_and_wait(chan, "vm-messages");
7603
if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
7604
res = ast_play_and_wait(chan, "vm-no");
7606
res = ast_play_and_wait(chan, "vm-messages");
7613
/* ITALIAN syntax */
7614
static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
7616
/* Introduce messages they have */
7618
if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
7619
res = ast_play_and_wait(chan, "vm-no") ||
7620
ast_play_and_wait(chan, "vm-message");
7622
res = ast_play_and_wait(chan, "vm-youhave");
7623
if (!res && vms->newmessages) {
7624
res = (vms->newmessages == 1) ?
7625
ast_play_and_wait(chan, "digits/un") ||
7626
ast_play_and_wait(chan, "vm-nuovo") ||
7627
ast_play_and_wait(chan, "vm-message") :
7628
/* 2 or more new messages */
7629
say_and_wait(chan, vms->newmessages, chan->language) ||
7630
ast_play_and_wait(chan, "vm-nuovi") ||
7631
ast_play_and_wait(chan, "vm-messages");
7632
if (!res && vms->oldmessages)
7633
res = ast_play_and_wait(chan, "vm-and");
7635
if (!res && vms->oldmessages) {
7636
res = (vms->oldmessages == 1) ?
7637
ast_play_and_wait(chan, "digits/un") ||
7638
ast_play_and_wait(chan, "vm-vecchio") ||
7639
ast_play_and_wait(chan, "vm-message") :
7640
/* 2 or more old messages */
7641
say_and_wait(chan, vms->oldmessages, chan->language) ||
7642
ast_play_and_wait(chan, "vm-vecchi") ||
7643
ast_play_and_wait(chan, "vm-messages");
7649
static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
7651
/* Introduce messages they have */
7655
if (!vms->oldmessages && !vms->newmessages) {
7656
res = ast_play_and_wait(chan, "vm-no");
7657
res = res ? res : ast_play_and_wait(chan, "vm-messages");
7660
res = ast_play_and_wait(chan, "vm-youhave");
7663
if (vms->newmessages) {
7664
num = div(vms->newmessages, 10);
7665
if (vms->newmessages == 1) {
7666
res = ast_play_and_wait(chan, "digits/1-a");
7667
res = res ? res : ast_play_and_wait(chan, "vm-new-a");
7668
res = res ? res : ast_play_and_wait(chan, "vm-message");
7669
} else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
7672
res = ast_play_and_wait(chan, "digits/2-ie");
7674
res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
7675
res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
7678
res = say_and_wait(chan, vms->newmessages, chan->language);
7680
res = res ? res : ast_play_and_wait(chan, "vm-new-e");
7681
res = res ? res : ast_play_and_wait(chan, "vm-messages");
7683
res = say_and_wait(chan, vms->newmessages, chan->language);
7684
res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
7685
res = res ? res : ast_play_and_wait(chan, "vm-messages");
7687
if (!res && vms->oldmessages)
7688
res = ast_play_and_wait(chan, "vm-and");
7690
if (!res && vms->oldmessages) {
7691
num = div(vms->oldmessages, 10);
7692
if (vms->oldmessages == 1) {
7693
res = ast_play_and_wait(chan, "digits/1-a");
7694
res = res ? res : ast_play_and_wait(chan, "vm-old-a");
7695
res = res ? res : ast_play_and_wait(chan, "vm-message");
7696
} else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
7699
res = ast_play_and_wait(chan, "digits/2-ie");
7701
res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
7702
res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
7705
res = say_and_wait(chan, vms->oldmessages, chan->language);
7707
res = res ? res : ast_play_and_wait(chan, "vm-old-e");
7708
res = res ? res : ast_play_and_wait(chan, "vm-messages");
7710
res = say_and_wait(chan, vms->oldmessages, chan->language);
7711
res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
7712
res = res ? res : ast_play_and_wait(chan, "vm-messages");
7719
/* SWEDISH syntax */
7720
static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
7722
/* Introduce messages they have */
7725
res = ast_play_and_wait(chan, "vm-youhave");
7729
if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
7730
res = ast_play_and_wait(chan, "vm-no");
7731
res = res ? res : ast_play_and_wait(chan, "vm-messages");
7735
if (vms->newmessages) {
7736
if ((vms->newmessages == 1)) {
7737
res = ast_play_and_wait(chan, "digits/ett");
7738
res = res ? res : ast_play_and_wait(chan, "vm-nytt");
7739
res = res ? res : ast_play_and_wait(chan, "vm-message");
7741
res = say_and_wait(chan, vms->newmessages, chan->language);
7742
res = res ? res : ast_play_and_wait(chan, "vm-nya");
7743
res = res ? res : ast_play_and_wait(chan, "vm-messages");
7745
if (!res && vms->oldmessages)
7746
res = ast_play_and_wait(chan, "vm-and");
7748
if (!res && vms->oldmessages) {
7749
if (vms->oldmessages == 1) {
7750
res = ast_play_and_wait(chan, "digits/ett");
7751
res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
7752
res = res ? res : ast_play_and_wait(chan, "vm-message");
7754
res = say_and_wait(chan, vms->oldmessages, chan->language);
7755
res = res ? res : ast_play_and_wait(chan, "vm-gamla");
7756
res = res ? res : ast_play_and_wait(chan, "vm-messages");
7763
/* NORWEGIAN syntax */
7764
static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
7766
/* Introduce messages they have */
7769
res = ast_play_and_wait(chan, "vm-youhave");
7773
if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
7774
res = ast_play_and_wait(chan, "vm-no");
7775
res = res ? res : ast_play_and_wait(chan, "vm-messages");
7779
if (vms->newmessages) {
7780
if ((vms->newmessages == 1)) {
7781
res = ast_play_and_wait(chan, "digits/1");
7782
res = res ? res : ast_play_and_wait(chan, "vm-ny");
7783
res = res ? res : ast_play_and_wait(chan, "vm-message");
7785
res = say_and_wait(chan, vms->newmessages, chan->language);
7786
res = res ? res : ast_play_and_wait(chan, "vm-nye");
7787
res = res ? res : ast_play_and_wait(chan, "vm-messages");
7789
if (!res && vms->oldmessages)
7790
res = ast_play_and_wait(chan, "vm-and");
7792
if (!res && vms->oldmessages) {
7793
if (vms->oldmessages == 1) {
7794
res = ast_play_and_wait(chan, "digits/1");
7795
res = res ? res : ast_play_and_wait(chan, "vm-gamel");
7796
res = res ? res : ast_play_and_wait(chan, "vm-message");
7798
res = say_and_wait(chan, vms->oldmessages, chan->language);
7799
res = res ? res : ast_play_and_wait(chan, "vm-gamle");
7800
res = res ? res : ast_play_and_wait(chan, "vm-messages");
7808
static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
7810
/* Introduce messages they have */
7812
res = ast_play_and_wait(chan, "vm-youhave");
7814
if (vms->newmessages) {
7815
if ((vms->newmessages == 1))
7816
res = ast_play_and_wait(chan, "digits/1F");
7818
res = say_and_wait(chan, vms->newmessages, chan->language);
7820
res = ast_play_and_wait(chan, "vm-INBOX");
7821
if (vms->oldmessages && !res)
7822
res = ast_play_and_wait(chan, "vm-and");
7824
if ((vms->newmessages == 1))
7825
res = ast_play_and_wait(chan, "vm-message");
7827
res = ast_play_and_wait(chan, "vm-messages");
7831
if (!res && vms->oldmessages) {
7832
if (vms->oldmessages == 1)
7833
res = ast_play_and_wait(chan, "digits/1F");
7835
res = say_and_wait(chan, vms->oldmessages, chan->language);
7837
res = ast_play_and_wait(chan, "vm-Old");
7839
if (vms->oldmessages == 1)
7840
res = ast_play_and_wait(chan, "vm-message");
7842
res = ast_play_and_wait(chan, "vm-messages");
7846
if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
7847
res = ast_play_and_wait(chan, "vm-no");
7849
res = ast_play_and_wait(chan, "vm-messages");
7856
/* SPANISH syntax */
7857
static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
7859
/* Introduce messages they have */
7861
if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
7862
res = ast_play_and_wait(chan, "vm-youhaveno");
7864
res = ast_play_and_wait(chan, "vm-messages");
7866
res = ast_play_and_wait(chan, "vm-youhave");
7869
if (vms->newmessages) {
7871
if ((vms->newmessages == 1)) {
7872
res = ast_play_and_wait(chan, "digits/1M");
7874
res = ast_play_and_wait(chan, "vm-message");
7876
res = ast_play_and_wait(chan, "vm-INBOXs");
7878
res = say_and_wait(chan, vms->newmessages, chan->language);
7880
res = ast_play_and_wait(chan, "vm-messages");
7882
res = ast_play_and_wait(chan, "vm-INBOX");
7885
if (vms->oldmessages && !res)
7886
res = ast_play_and_wait(chan, "vm-and");
7888
if (vms->oldmessages) {
7890
if (vms->oldmessages == 1) {
7891
res = ast_play_and_wait(chan, "digits/1M");
7893
res = ast_play_and_wait(chan, "vm-message");
7895
res = ast_play_and_wait(chan, "vm-Olds");
7897
res = say_and_wait(chan, vms->oldmessages, chan->language);
7899
res = ast_play_and_wait(chan, "vm-messages");
7901
res = ast_play_and_wait(chan, "vm-Old");
7909
/* BRAZILIAN PORTUGUESE syntax */
7910
static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
7911
/* Introduce messages they have */
7913
if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
7914
res = ast_play_and_wait(chan, "vm-nomessages");
7917
res = ast_play_and_wait(chan, "vm-youhave");
7919
if (vms->newmessages) {
7921
res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
7922
if ((vms->newmessages == 1)) {
7924
res = ast_play_and_wait(chan, "vm-message");
7926
res = ast_play_and_wait(chan, "vm-INBOXs");
7929
res = ast_play_and_wait(chan, "vm-messages");
7931
res = ast_play_and_wait(chan, "vm-INBOX");
7933
if (vms->oldmessages && !res)
7934
res = ast_play_and_wait(chan, "vm-and");
7936
if (vms->oldmessages) {
7938
res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
7939
if (vms->oldmessages == 1) {
7941
res = ast_play_and_wait(chan, "vm-message");
7943
res = ast_play_and_wait(chan, "vm-Olds");
7946
res = ast_play_and_wait(chan, "vm-messages");
7948
res = ast_play_and_wait(chan, "vm-Old");
7955
static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
7957
/* Introduce messages they have */
7959
res = ast_play_and_wait(chan, "vm-youhave");
7961
if (vms->newmessages) {
7962
res = say_and_wait(chan, vms->newmessages, chan->language);
7964
res = ast_play_and_wait(chan, "vm-INBOX");
7965
if (vms->oldmessages && !res)
7966
res = ast_play_and_wait(chan, "vm-and");
7968
if ((vms->newmessages == 1))
7969
res = ast_play_and_wait(chan, "vm-message");
7971
res = ast_play_and_wait(chan, "vm-messages");
7975
if (!res && vms->oldmessages) {
7976
res = say_and_wait(chan, vms->oldmessages, chan->language);
7978
res = ast_play_and_wait(chan, "vm-Old");
7980
if (vms->oldmessages == 1)
7981
res = ast_play_and_wait(chan, "vm-message");
7983
res = ast_play_and_wait(chan, "vm-messages");
7987
if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
7988
res = ast_play_and_wait(chan, "vm-no");
7990
res = ast_play_and_wait(chan, "vm-messages");
7998
static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
8000
/* Introduce messages they have */
8002
res = ast_play_and_wait(chan, "vm-youhave");
8004
if (vms->newmessages) {
8005
res = say_and_wait(chan, vms->newmessages, chan->language);
8007
if (vms->newmessages == 1)
8008
res = ast_play_and_wait(chan, "vm-INBOXs");
8010
res = ast_play_and_wait(chan, "vm-INBOX");
8012
if (vms->oldmessages && !res)
8013
res = ast_play_and_wait(chan, "vm-and");
8015
if ((vms->newmessages == 1))
8016
res = ast_play_and_wait(chan, "vm-message");
8018
res = ast_play_and_wait(chan, "vm-messages");
8022
if (!res && vms->oldmessages) {
8023
res = say_and_wait(chan, vms->oldmessages, chan->language);
8025
if (vms->oldmessages == 1)
8026
res = ast_play_and_wait(chan, "vm-Olds");
8028
res = ast_play_and_wait(chan, "vm-Old");
8031
if (vms->oldmessages == 1)
8032
res = ast_play_and_wait(chan, "vm-message");
8034
res = ast_play_and_wait(chan, "vm-messages");
8038
if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8039
res = ast_play_and_wait(chan, "vm-no");
8041
res = ast_play_and_wait(chan, "vm-messages");
8048
/* PORTUGUESE syntax */
8049
static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
8051
/* Introduce messages they have */
8053
res = ast_play_and_wait(chan, "vm-youhave");
8055
if (vms->newmessages) {
8056
res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
8058
if ((vms->newmessages == 1)) {
8059
res = ast_play_and_wait(chan, "vm-message");
8061
res = ast_play_and_wait(chan, "vm-INBOXs");
8063
res = ast_play_and_wait(chan, "vm-messages");
8065
res = ast_play_and_wait(chan, "vm-INBOX");
8068
if (vms->oldmessages && !res)
8069
res = ast_play_and_wait(chan, "vm-and");
8071
if (!res && vms->oldmessages) {
8072
res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
8074
if (vms->oldmessages == 1) {
8075
res = ast_play_and_wait(chan, "vm-message");
8077
res = ast_play_and_wait(chan, "vm-Olds");
8079
res = ast_play_and_wait(chan, "vm-messages");
8081
res = ast_play_and_wait(chan, "vm-Old");
8086
if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8087
res = ast_play_and_wait(chan, "vm-no");
8089
res = ast_play_and_wait(chan, "vm-messages");
8098
/* in czech there must be declension of word new and message
8099
* czech : english : czech : english
8100
* --------------------------------------------------------
8101
* vm-youhave : you have
8102
* vm-novou : one new : vm-zpravu : message
8103
* vm-nove : 2-4 new : vm-zpravy : messages
8104
* vm-novych : 5-infinite new : vm-zprav : messages
8105
* vm-starou : one old
8106
* vm-stare : 2-4 old
8107
* vm-starych : 5-infinite old
8108
* jednu : one - falling 4.
8109
* vm-no : no ( no messages )
8112
static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
8115
res = ast_play_and_wait(chan, "vm-youhave");
8117
if (vms->newmessages) {
8118
if (vms->newmessages == 1) {
8119
res = ast_play_and_wait(chan, "digits/jednu");
8121
res = say_and_wait(chan, vms->newmessages, chan->language);
8124
if ((vms->newmessages == 1))
8125
res = ast_play_and_wait(chan, "vm-novou");
8126
if ((vms->newmessages) > 1 && (vms->newmessages < 5))
8127
res = ast_play_and_wait(chan, "vm-nove");
8128
if (vms->newmessages > 4)
8129
res = ast_play_and_wait(chan, "vm-novych");
8131
if (vms->oldmessages && !res)
8132
res = ast_play_and_wait(chan, "vm-and");
8134
if ((vms->newmessages == 1))
8135
res = ast_play_and_wait(chan, "vm-zpravu");
8136
if ((vms->newmessages) > 1 && (vms->newmessages < 5))
8137
res = ast_play_and_wait(chan, "vm-zpravy");
8138
if (vms->newmessages > 4)
8139
res = ast_play_and_wait(chan, "vm-zprav");
8142
if (!res && vms->oldmessages) {
8143
res = say_and_wait(chan, vms->oldmessages, chan->language);
8145
if ((vms->oldmessages == 1))
8146
res = ast_play_and_wait(chan, "vm-starou");
8147
if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
8148
res = ast_play_and_wait(chan, "vm-stare");
8149
if (vms->oldmessages > 4)
8150
res = ast_play_and_wait(chan, "vm-starych");
8153
if ((vms->oldmessages == 1))
8154
res = ast_play_and_wait(chan, "vm-zpravu");
8155
if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
8156
res = ast_play_and_wait(chan, "vm-zpravy");
8157
if (vms->oldmessages > 4)
8158
res = ast_play_and_wait(chan, "vm-zprav");
8162
if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8163
res = ast_play_and_wait(chan, "vm-no");
8165
res = ast_play_and_wait(chan, "vm-zpravy");
8172
/* CHINESE (Taiwan) syntax */
8173
static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
8176
/* Introduce messages they have */
8177
res = ast_play_and_wait(chan, "vm-you");
8179
if (!res && vms->newmessages) {
8180
res = ast_play_and_wait(chan, "vm-have");
8182
res = say_and_wait(chan, vms->newmessages, chan->language);
8184
res = ast_play_and_wait(chan, "vm-tong");
8186
res = ast_play_and_wait(chan, "vm-INBOX");
8187
if (vms->oldmessages && !res)
8188
res = ast_play_and_wait(chan, "vm-and");
8190
res = ast_play_and_wait(chan, "vm-messages");
8192
if (!res && vms->oldmessages) {
8193
res = ast_play_and_wait(chan, "vm-have");
8195
res = say_and_wait(chan, vms->oldmessages, chan->language);
8197
res = ast_play_and_wait(chan, "vm-tong");
8199
res = ast_play_and_wait(chan, "vm-Old");
8201
res = ast_play_and_wait(chan, "vm-messages");
8203
if (!res && !vms->oldmessages && !vms->newmessages) {
8204
res = ast_play_and_wait(chan, "vm-haveno");
8206
res = ast_play_and_wait(chan, "vm-messages");
8211
static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
8215
/* Notify the user that the temp greeting is set and give them the option to remove it */
8216
snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
8217
if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
8218
RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
8219
if (ast_fileexists(prefile, NULL, NULL) > 0) {
8220
ast_play_and_wait(chan, "vm-tempgreetactive");
8222
DISPOSE(prefile, -1);
8225
/* Play voicemail intro - syntax is different for different languages */
8227
} else if (!strncasecmp(chan->language, "cs", 2)) { /* CZECH syntax */
8228
return vm_intro_cs(chan, vms);
8229
} else if (!strncasecmp(chan->language, "cz", 2)) { /* deprecated CZECH syntax */
8230
static int deprecation_warning = 0;
8231
if (deprecation_warning++ % 10 == 0) {
8232
ast_log(LOG_WARNING, "cz is not a standard language code. Please switch to using cs instead.\n");
8234
return vm_intro_cs(chan, vms);
8235
} else if (!strncasecmp(chan->language, "de", 2)) { /* GERMAN syntax */
8236
return vm_intro_de(chan, vms);
8237
} else if (!strncasecmp(chan->language, "es", 2)) { /* SPANISH syntax */
8238
return vm_intro_es(chan, vms);
8239
} else if (!strncasecmp(chan->language, "fr", 2)) { /* FRENCH syntax */
8240
return vm_intro_fr(chan, vms);
8241
} else if (!strncasecmp(chan->language, "gr", 2)) { /* GREEK syntax */
8242
return vm_intro_gr(chan, vms);
8243
} else if (!strncasecmp(chan->language, "he", 2)) { /* HEBREW syntax */
8244
return vm_intro_he(chan, vms);
8245
} else if (!strncasecmp(chan->language, "it", 2)) { /* ITALIAN syntax */
8246
return vm_intro_it(chan, vms);
8247
} else if (!strncasecmp(chan->language, "nl", 2)) { /* DUTCH syntax */
8248
return vm_intro_nl(chan, vms);
8249
} else if (!strncasecmp(chan->language, "no", 2)) { /* NORWEGIAN syntax */
8250
return vm_intro_no(chan, vms);
8251
} else if (!strncasecmp(chan->language, "pl", 2)) { /* POLISH syntax */
8252
return vm_intro_pl(chan, vms);
8253
} else if (!strncasecmp(chan->language, "pt_BR", 5)) { /* BRAZILIAN PORTUGUESE syntax */
8254
return vm_intro_pt_BR(chan, vms);
8255
} else if (!strncasecmp(chan->language, "pt", 2)) { /* PORTUGUESE syntax */
8256
return vm_intro_pt(chan, vms);
8257
} else if (!strncasecmp(chan->language, "ru", 2)) { /* RUSSIAN syntax */
8258
return vm_intro_multilang(chan, vms, "n");
8259
} else if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
8260
return vm_intro_se(chan, vms);
8261
} else if (!strncasecmp(chan->language, "ua", 2)) { /* UKRAINIAN syntax */
8262
return vm_intro_multilang(chan, vms, "n");
8263
} else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
8264
return vm_intro_zh(chan, vms);
8265
} else { /* Default to ENGLISH */
8266
return vm_intro_en(chan, vms);
8270
static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
8273
/* Play instructions and wait for new command */
8275
if (vms->starting) {
8276
if (vms->lastmsg > -1) {
8278
res = ast_play_and_wait(chan, "vm-onefor-full");
8280
res = ast_play_and_wait(chan, "vm-onefor");
8282
res = vm_play_folder_name(chan, vms->vmbox);
8286
res = ast_play_and_wait(chan, "vm-opts-full");
8288
res = ast_play_and_wait(chan, "vm-opts");
8291
/* Added for additional help */
8293
res = ast_play_and_wait(chan, "vm-onefor-full");
8295
res = vm_play_folder_name(chan, vms->vmbox);
8296
res = ast_play_and_wait(chan, "vm-opts-full");
8299
* If the current message is not the first OR
8300
* if we're listening to the first new message and there are
8301
* also urgent messages, then prompt for navigation to the
8304
if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
8305
res = ast_play_and_wait(chan, "vm-prev");
8307
if (!res && !skipadvanced)
8308
res = ast_play_and_wait(chan, "vm-advopts");
8310
res = ast_play_and_wait(chan, "vm-repeat");
8312
* If we're not listening to the last message OR
8313
* we're listening to the last urgent message and there are
8314
* also new non-urgent messages, then prompt for navigation
8315
* to the next message
8317
if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
8318
(ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
8319
res = ast_play_and_wait(chan, "vm-next");
8322
if (!vms->deleted[vms->curmsg])
8323
res = ast_play_and_wait(chan, "vm-delete");
8325
res = ast_play_and_wait(chan, "vm-undelete");
8327
res = ast_play_and_wait(chan, "vm-toforward");
8329
res = ast_play_and_wait(chan, "vm-savemessage");
8333
res = ast_play_and_wait(chan, "vm-helpexit");
8336
res = ast_waitfordigit(chan, 6000);
8339
if (vms->repeats > 2) {
8347
static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
8350
/* Play instructions and wait for new command */
8352
if (vms->lastmsg > -1) {
8353
res = ast_play_and_wait(chan, "vm-listen");
8355
res = vm_play_folder_name(chan, vms->vmbox);
8357
res = ast_play_and_wait(chan, "press");
8359
res = ast_play_and_wait(chan, "digits/1");
8362
res = ast_play_and_wait(chan, "vm-opts");
8365
return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
8371
static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
8373
if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
8374
return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
8375
} else { /* Default to ENGLISH */
8376
return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
8381
static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
8386
char newpassword[80] = "";
8387
char newpassword2[80] = "";
8388
char prefile[PATH_MAX] = "";
8389
unsigned char buf[256];
8392
if (ast_adsi_available(chan)) {
8393
bytes += adsi_logo(buf + bytes);
8394
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
8395
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
8396
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8397
bytes += ast_adsi_voice_mode(buf + bytes, 0);
8398
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
8401
/* First, have the user change their password
8402
so they won't get here again */
8404
newpassword[1] = '\0';
8405
newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
8407
newpassword[0] = '\0';
8408
if (cmd < 0 || cmd == 't' || cmd == '#')
8410
cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
8411
if (cmd < 0 || cmd == 't' || cmd == '#')
8413
cmd = check_password(vmu, newpassword); /* perform password validation */
8415
ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
8416
cmd = ast_play_and_wait(chan, vm_invalid_password);
8418
newpassword2[1] = '\0';
8419
newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
8421
newpassword2[0] = '\0';
8422
if (cmd < 0 || cmd == 't' || cmd == '#')
8424
cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
8425
if (cmd < 0 || cmd == 't' || cmd == '#')
8427
if (!strcmp(newpassword, newpassword2))
8429
ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
8430
cmd = ast_play_and_wait(chan, vm_mismatch);
8435
cmd = ast_play_and_wait(chan, vm_pls_try_again);
8438
if (pwdchange & PWDCHANGE_INTERNAL)
8439
vm_change_password(vmu, newpassword);
8440
if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
8441
vm_change_password_shell(vmu, newpassword);
8443
ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
8444
cmd = ast_play_and_wait(chan, vm_passchanged);
8446
/* If forcename is set, have the user record their name */
8447
if (ast_test_flag(vmu, VM_FORCENAME)) {
8448
snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
8449
if (ast_fileexists(prefile, NULL, NULL) < 1) {
8450
cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
8451
if (cmd < 0 || cmd == 't' || cmd == '#')
8456
/* If forcegreetings is set, have the user record their greetings */
8457
if (ast_test_flag(vmu, VM_FORCEGREET)) {
8458
snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
8459
if (ast_fileexists(prefile, NULL, NULL) < 1) {
8460
cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
8461
if (cmd < 0 || cmd == 't' || cmd == '#')
8465
snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
8466
if (ast_fileexists(prefile, NULL, NULL) < 1) {
8467
cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
8468
if (cmd < 0 || cmd == 't' || cmd == '#')
8476
static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
8481
char newpassword[80] = "";
8482
char newpassword2[80] = "";
8483
char prefile[PATH_MAX] = "";
8484
unsigned char buf[256];
8487
if (ast_adsi_available(chan)) {
8488
bytes += adsi_logo(buf + bytes);
8489
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
8490
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
8491
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8492
bytes += ast_adsi_voice_mode(buf + bytes, 0);
8493
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
8495
while ((cmd >= 0) && (cmd != 't')) {
8499
case '1': /* Record your unavailable message */
8500
snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
8501
cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
8503
case '2': /* Record your busy message */
8504
snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
8505
cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
8507
case '3': /* Record greeting */
8508
snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
8509
cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
8511
case '4': /* manage the temporary greeting */
8512
cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
8514
case '5': /* change password */
8515
if (vmu->password[0] == '-') {
8516
cmd = ast_play_and_wait(chan, "vm-no");
8519
newpassword[1] = '\0';
8520
newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
8522
newpassword[0] = '\0';
8526
if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
8530
cmd = check_password(vmu, newpassword); /* perform password validation */
8532
ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
8533
cmd = ast_play_and_wait(chan, vm_invalid_password);
8535
cmd = ast_play_and_wait(chan, vm_pls_try_again);
8539
newpassword2[1] = '\0';
8540
newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
8542
newpassword2[0] = '\0';
8547
if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
8551
if (strcmp(newpassword, newpassword2)) {
8552
ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
8553
cmd = ast_play_and_wait(chan, vm_mismatch);
8555
cmd = ast_play_and_wait(chan, vm_pls_try_again);
8559
if (pwdchange & PWDCHANGE_INTERNAL)
8560
vm_change_password(vmu, newpassword);
8561
if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
8562
vm_change_password_shell(vmu, newpassword);
8564
ast_debug(1,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
8565
cmd = ast_play_and_wait(chan, vm_passchanged);
8572
snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
8573
RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
8574
if (ast_fileexists(prefile, NULL, NULL)) {
8575
cmd = ast_play_and_wait(chan, "vm-tmpexists");
8577
DISPOSE(prefile, -1);
8579
cmd = ast_play_and_wait(chan, "vm-options");
8582
cmd = ast_waitfordigit(chan,6000);
8598
* \brief The handler for 'record a temporary greeting'.
8603
* \param record_gain
8605
* This is option 4 from the mailbox options menu.
8606
* This function manages the following promptings:
8607
* 1: play / record / review the temporary greeting. : invokes play_record_review().
8608
* 2: remove (delete) the temporary greeting.
8609
* *: return to the main menu.
8611
* \return zero on success, -1 on error.
8613
static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
8618
char prefile[PATH_MAX] = "";
8619
unsigned char buf[256];
8622
if (ast_adsi_available(chan)) {
8623
bytes += adsi_logo(buf + bytes);
8624
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
8625
bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
8626
bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8627
bytes += ast_adsi_voice_mode(buf + bytes, 0);
8628
ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
8631
snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
8632
while ((cmd >= 0) && (cmd != 't')) {
8635
RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
8636
if (ast_fileexists(prefile, NULL, NULL) <= 0) {
8637
play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
8642
cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
8645
DELETE(prefile, -1, prefile, vmu);
8646
ast_play_and_wait(chan, "vm-tempremoved");
8653
cmd = ast_play_and_wait(chan,
8654
ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
8655
"vm-tempgreeting2" : "vm-tempgreeting");
8657
cmd = ast_waitfordigit(chan,6000);
8664
DISPOSE(prefile, -1);
8672
* \brief Greek syntax for 'You have N messages' greeting.
8677
* \return zero on success, -1 on error.
8679
static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
8683
if (vms->lastmsg > -1) {
8684
cmd = play_message(chan, vmu, vms);
8686
cmd = ast_play_and_wait(chan, "vm-youhaveno");
8687
if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
8689
snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
8690
cmd = ast_play_and_wait(chan, vms->fn);
8693
cmd = ast_play_and_wait(chan, "vm-messages");
8696
cmd = ast_play_and_wait(chan, "vm-messages");
8698
snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
8699
cmd = ast_play_and_wait(chan, vms->fn);
8707
static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
8711
if (vms->lastmsg > -1) {
8712
cmd = play_message(chan, vmu, vms);
8714
if (!strcasecmp(vms->fn, "INBOX")) {
8715
cmd = ast_play_and_wait(chan, "vm-nonewmessages");
8717
cmd = ast_play_and_wait(chan, "vm-nomessages");
8724
* \brief Default English syntax for 'You have N messages' greeting.
8729
* \return zero on success, -1 on error.
8731
static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
8735
if (vms->lastmsg > -1) {
8736
cmd = play_message(chan, vmu, vms);
8738
cmd = ast_play_and_wait(chan, "vm-youhave");
8740
cmd = ast_play_and_wait(chan, "vm-no");
8742
snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
8743
cmd = ast_play_and_wait(chan, vms->fn);
8746
cmd = ast_play_and_wait(chan, "vm-messages");
8752
*\brief Italian syntax for 'You have N messages' greeting.
8757
* \return zero on success, -1 on error.
8759
static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
8763
if (vms->lastmsg > -1) {
8764
cmd = play_message(chan, vmu, vms);
8766
cmd = ast_play_and_wait(chan, "vm-no");
8768
cmd = ast_play_and_wait(chan, "vm-message");
8770
snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
8771
cmd = ast_play_and_wait(chan, vms->fn);
8778
* \brief Spanish syntax for 'You have N messages' greeting.
8783
* \return zero on success, -1 on error.
8785
static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
8789
if (vms->lastmsg > -1) {
8790
cmd = play_message(chan, vmu, vms);
8792
cmd = ast_play_and_wait(chan, "vm-youhaveno");
8794
cmd = ast_play_and_wait(chan, "vm-messages");
8796
snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
8797
cmd = ast_play_and_wait(chan, vms->fn);
8804
* \brief Portuguese syntax for 'You have N messages' greeting.
8809
* \return zero on success, -1 on error.
8811
static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
8815
if (vms->lastmsg > -1) {
8816
cmd = play_message(chan, vmu, vms);
8818
cmd = ast_play_and_wait(chan, "vm-no");
8820
snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
8821
cmd = ast_play_and_wait(chan, vms->fn);
8824
cmd = ast_play_and_wait(chan, "vm-messages");
8830
* \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
8835
* \return zero on success, -1 on error.
8837
static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
8841
if (vms->lastmsg > -1) {
8842
cmd = play_message(chan, vmu, vms);
8844
cmd = ast_play_and_wait(chan, "vm-you");
8846
cmd = ast_play_and_wait(chan, "vm-haveno");
8848
cmd = ast_play_and_wait(chan, "vm-messages");
8850
snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
8851
cmd = ast_play_and_wait(chan, vms->fn);
8858
* \brief Top level method to invoke the language variant vm_browse_messages_XX function.
8859
* \param chan The channel for the current user. We read the language property from this.
8860
* \param vms passed into the language-specific vm_browse_messages function.
8861
* \param vmu passed into the language-specific vm_browse_messages function.
8863
* The method to be invoked is determined by the value of language code property in the user's channel.
8864
* The default (when unable to match) is to use english.
8866
* \return zero on success, -1 on error.
8868
static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
8870
if (!strncasecmp(chan->language, "es", 2)) { /* SPANISH */
8871
return vm_browse_messages_es(chan, vms, vmu);
8872
} else if (!strncasecmp(chan->language, "gr", 2)) { /* GREEK */
8873
return vm_browse_messages_gr(chan, vms, vmu);
8874
} else if (!strncasecmp(chan->language, "he", 2)) { /* HEBREW */
8875
return vm_browse_messages_he(chan, vms, vmu);
8876
} else if (!strncasecmp(chan->language, "it", 2)) { /* ITALIAN */
8877
return vm_browse_messages_it(chan, vms, vmu);
8878
} else if (!strncasecmp(chan->language, "pt", 2)) { /* PORTUGUESE */
8879
return vm_browse_messages_pt(chan, vms, vmu);
8880
} else if (!strncasecmp(chan->language, "zh", 2)) {
8881
return vm_browse_messages_zh(chan, vms, vmu); /* CHINESE (Taiwan) */
8882
} else { /* Default to English syntax */
8883
return vm_browse_messages_en(chan, vms, vmu);
8887
static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
8888
struct ast_vm_user *res_vmu, const char *context, const char *prefix,
8889
int skipuser, int max_logins, int silent)
8891
int useadsi=0, valid=0, logretries=0;
8892
char password[AST_MAX_EXTENSION]="", *passptr;
8893
struct ast_vm_user vmus, *vmu = NULL;
8895
/* If ADSI is supported, setup login screen */
8896
adsi_begin(chan, &useadsi);
8897
if (!skipuser && useadsi)
8899
if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
8900
ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
8904
/* Authenticate them and get their mailbox/password */
8906
while (!valid && (logretries < max_logins)) {
8907
/* Prompt for, and read in the username */
8908
if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
8909
ast_log(AST_LOG_WARNING, "Couldn't read username\n");
8912
if (ast_strlen_zero(mailbox)) {
8913
if (chan->cid.cid_num) {
8914
ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
8916
ast_verb(3,"Username not entered\n");
8921
adsi_password(chan);
8923
if (!ast_strlen_zero(prefix)) {
8924
char fullusername[80] = "";
8925
ast_copy_string(fullusername, prefix, sizeof(fullusername));
8926
strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
8927
ast_copy_string(mailbox, fullusername, mailbox_size);
8930
ast_debug(1, "Before find user for mailbox %s\n",mailbox);
8931
vmu = find_user(&vmus, context, mailbox);
8932
if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
8933
/* saved password is blank, so don't bother asking */
8936
if (ast_streamfile(chan, vm_password, chan->language)) {
8937
ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
8940
if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
8941
ast_log(AST_LOG_WARNING, "Unable to read password\n");
8947
passptr = vmu->password;
8948
if (passptr[0] == '-') passptr++;
8950
if (vmu && !strcmp(passptr, password))
8953
ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
8954
if (!ast_strlen_zero(prefix))
8959
if (skipuser || logretries >= max_logins) {
8960
if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
8961
ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
8967
if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
8968
ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
8972
if (ast_waitstream(chan, "")) /* Channel is hung up */
8976
if (!valid && (logretries >= max_logins)) {
8977
ast_stopstream(chan);
8978
ast_play_and_wait(chan, "vm-goodbye");
8981
if (vmu && !skipuser) {
8982
memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
8987
static int vm_execmain(struct ast_channel *chan, void *data)
8989
/* XXX This is, admittedly, some pretty horrendous code. For some
8990
reason it just seemed a lot easier to do with GOTO's. I feel
8991
like I'm back in my GWBASIC days. XXX */
8995
char prefixstr[80] ="";
8996
char ext_context[256]="";
9000
struct vm_state vms;
9001
struct ast_vm_user *vmu = NULL, vmus;
9004
struct ast_flags flags = { 0 };
9005
signed char record_gain = 0;
9007
int play_folder = 0;
9013
/* Add the vm_state to the active list and keep it active */
9014
memset(&vms, 0, sizeof(vms));
9018
memset(&vmus, 0, sizeof(vmus));
9020
if (chan->_state != AST_STATE_UP) {
9021
ast_debug(1, "Before ast_answer\n");
9025
if (!ast_strlen_zero(data)) {
9026
char *opts[OPT_ARG_ARRAY_SIZE];
9028
AST_DECLARE_APP_ARGS(args,
9033
parse = ast_strdupa(data);
9035
AST_STANDARD_APP_ARGS(args, parse);
9037
if (args.argc == 2) {
9038
if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
9040
if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
9042
if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
9043
if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
9044
ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
9047
record_gain = (signed char) gain;
9050
ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
9053
if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
9055
if (opts[OPT_ARG_PLAYFOLDER]) {
9056
if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
9057
ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
9060
ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
9062
if ( play_folder > 9 || play_folder < 0) {
9063
ast_log(AST_LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
9068
/* old style options parsing */
9069
while (*(args.argv0)) {
9070
if (*(args.argv0) == 's')
9071
ast_set_flag(&flags, OPT_SILENT);
9072
else if (*(args.argv0) == 'p')
9073
ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
9081
valid = ast_test_flag(&flags, OPT_SILENT);
9083
if ((context = strchr(args.argv0, '@')))
9086
if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
9087
ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
9089
ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
9091
if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
9098
res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
9100
ast_debug(1, "After vm_authenticate\n");
9109
/* If ADSI is supported, setup login screen */
9110
adsi_begin(chan, &useadsi);
9117
pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
9118
pthread_setspecific(ts_vmstate.key, &vms);
9120
vms.interactive = 1;
9123
ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
9124
vmstate_insert(&vms);
9125
init_vm_state(&vms);
9127
if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
9128
ast_log(AST_LOG_ERROR, "Could not allocate memory for deleted message storage!\n");
9129
cmd = ast_play_and_wait(chan, "an-error-has-occured");
9132
if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
9133
ast_log(AST_LOG_ERROR, "Could not allocate memory for heard message storage!\n");
9134
cmd = ast_play_and_wait(chan, "an-error-has-occured");
9138
/* Set language from config to override channel language */
9139
if (!ast_strlen_zero(vmu->language))
9140
ast_string_field_set(chan, language, vmu->language);
9142
/* Retrieve urgent, old and new message counts */
9143
ast_debug(1, "Before open_mailbox\n");
9144
res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
9145
if (res == ERROR_LOCK_PATH)
9147
vms.oldmessages = vms.lastmsg + 1;
9148
ast_debug(1, "Number of old messages: %d\n",vms.oldmessages);
9150
res = open_mailbox(&vms, vmu, NEW_FOLDER);
9151
if (res == ERROR_LOCK_PATH)
9153
vms.newmessages = vms.lastmsg + 1;
9154
ast_debug(1, "Number of new messages: %d\n",vms.newmessages);
9155
/* Start in Urgent */
9157
res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
9158
if (res == ERROR_LOCK_PATH)
9160
vms.urgentmessages = vms.lastmsg + 1;
9161
ast_debug(1, "Number of urgent messages: %d\n",vms.urgentmessages);
9163
/* Select proper mailbox FIRST!! */
9165
if (vms.urgentmessages) {
9167
res = open_mailbox(&vms, vmu, 11);
9170
res = open_mailbox(&vms, vmu, play_folder);
9172
if (res == ERROR_LOCK_PATH)
9175
/* If there are no new messages, inform the user and hangup */
9176
if (vms.lastmsg == -1) {
9178
cmd = vm_browse_messages(chan, &vms, vmu);
9183
if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
9184
/* If we only have old messages start here */
9185
res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
9188
if (res == ERROR_LOCK_PATH)
9190
} else if (!vms.urgentmessages && vms.newmessages) {
9191
/* If we have new messages but none are urgent */
9193
res = open_mailbox(&vms, vmu, NEW_FOLDER);
9194
if (res == ERROR_LOCK_PATH)
9200
adsi_status(chan, &vms);
9203
/* Check to see if this is a new user */
9204
if (!strcasecmp(vmu->mailbox, vmu->password) &&
9205
(ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
9206
if (ast_play_and_wait(chan, "vm-newuser") == -1)
9207
ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
9208
cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
9209
if ((cmd == 't') || (cmd == '#')) {
9213
} else if (cmd < 0) {
9220
ast_debug(3, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
9221
if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
9222
ast_debug(1, "*** QUOTA EXCEEDED!!\n");
9223
cmd = ast_play_and_wait(chan, "vm-mailboxfull");
9225
ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
9226
if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
9227
ast_log(AST_LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
9228
cmd = ast_play_and_wait(chan, "vm-mailboxfull");
9234
cmd = vm_intro(chan, vmu, &vms);
9239
while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
9242
case '1': /* First message */
9245
case '5': /* Play current message */
9246
cmd = vm_browse_messages(chan, &vms, vmu);
9248
case '2': /* Change folders */
9250
adsi_folders(chan, 0, "Change to folder...");
9251
cmd = get_folder2(chan, "vm-changeto", 0);
9254
} else if (cmd > 0) {
9256
res = close_mailbox(&vms, vmu);
9257
if (res == ERROR_LOCK_PATH)
9259
/* If folder is not urgent, set in_urgent to zero! */
9260
if (cmd != 11) in_urgent = 0;
9261
res = open_mailbox(&vms, vmu, cmd);
9262
if (res == ERROR_LOCK_PATH)
9268
adsi_status2(chan, &vms);
9271
cmd = vm_play_folder_name(chan, vms.vmbox);
9275
case '3': /* Advanced options */
9278
while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
9280
case '1': /* Reply */
9281
if (vms.lastmsg > -1 && !vms.starting) {
9282
cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
9283
if (cmd == ERROR_LOCK_PATH) {
9288
cmd = ast_play_and_wait(chan, "vm-sorry");
9291
case '2': /* Callback */
9293
ast_verb(3, "Callback Requested\n");
9294
if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
9295
cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
9299
} else if (cmd == ERROR_LOCK_PATH) {
9304
cmd = ast_play_and_wait(chan, "vm-sorry");
9307
case '3': /* Envelope */
9308
if (vms.lastmsg > -1 && !vms.starting) {
9309
cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
9310
if (cmd == ERROR_LOCK_PATH) {
9315
cmd = ast_play_and_wait(chan, "vm-sorry");
9318
case '4': /* Dialout */
9319
if (!ast_strlen_zero(vmu->dialout)) {
9320
cmd = dialout(chan, vmu, NULL, vmu->dialout);
9326
cmd = ast_play_and_wait(chan, "vm-sorry");
9330
case '5': /* Leave VoiceMail */
9331
if (ast_test_flag(vmu, VM_SVMAIL)) {
9332
cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
9333
if (cmd == ERROR_LOCK_PATH) {
9335
ast_log(AST_LOG_WARNING, "forward_message failed to lock path.\n");
9339
cmd = ast_play_and_wait(chan,"vm-sorry");
9343
case '*': /* Return to main menu */
9349
if (!vms.starting) {
9350
cmd = ast_play_and_wait(chan, "vm-toreply");
9352
if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
9353
cmd = ast_play_and_wait(chan, "vm-tocallback");
9355
if (!cmd && !vms.starting) {
9356
cmd = ast_play_and_wait(chan, "vm-tohearenv");
9358
if (!ast_strlen_zero(vmu->dialout) && !cmd) {
9359
cmd = ast_play_and_wait(chan, "vm-tomakecall");
9361
if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
9362
cmd=ast_play_and_wait(chan, "vm-leavemsg");
9364
cmd = ast_play_and_wait(chan, "vm-starmain");
9366
cmd = ast_waitfordigit(chan,6000);
9369
if (vms.repeats > 3)
9378
case '4': /* Go to the previous message */
9379
if (vms.curmsg > 0) {
9381
cmd = play_message(chan, vmu, &vms);
9383
/* Check if we were listening to new
9384
messages. If so, go to Urgent messages
9385
instead of saying "no more messages"
9387
if (in_urgent == 0 && vms.urgentmessages > 0) {
9388
/* Check for Urgent messages */
9390
res = close_mailbox(&vms, vmu);
9391
if (res == ERROR_LOCK_PATH)
9393
res = open_mailbox(&vms, vmu, 11); /* Open Urgent folder */
9394
if (res == ERROR_LOCK_PATH)
9396
ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n",vms.lastmsg + 1);
9397
vms.curmsg = vms.lastmsg;
9398
if (vms.lastmsg < 0)
9399
cmd = ast_play_and_wait(chan, "vm-nomore");
9400
} else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
9401
vms.curmsg = vms.lastmsg;
9402
cmd = play_message(chan, vmu, &vms);
9404
cmd = ast_play_and_wait(chan, "vm-nomore");
9408
case '6': /* Go to the next message */
9409
if (vms.curmsg < vms.lastmsg) {
9411
cmd = play_message(chan, vmu, &vms);
9413
if (in_urgent && vms.newmessages > 0) {
9414
/* Check if we were listening to urgent
9415
* messages. If so, go to regular new messages
9416
* instead of saying "no more messages"
9419
res = close_mailbox(&vms, vmu);
9420
if (res == ERROR_LOCK_PATH)
9422
res = open_mailbox(&vms, vmu, NEW_FOLDER);
9423
if (res == ERROR_LOCK_PATH)
9425
ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
9427
if (vms.lastmsg < 0) {
9428
cmd = ast_play_and_wait(chan, "vm-nomore");
9430
} else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
9432
cmd = play_message(chan, vmu, &vms);
9434
cmd = ast_play_and_wait(chan, "vm-nomore");
9438
case '7': /* Delete the current message */
9439
if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
9440
vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
9442
adsi_delete(chan, &vms);
9443
if (vms.deleted[vms.curmsg]) {
9444
if (play_folder == 0) {
9446
vms.urgentmessages--;
9451
else if (play_folder == 1)
9453
cmd = ast_play_and_wait(chan, "vm-deleted");
9455
if (play_folder == 0) {
9457
vms.urgentmessages++;
9462
else if (play_folder == 1)
9464
cmd = ast_play_and_wait(chan, "vm-undeleted");
9466
if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
9467
if (vms.curmsg < vms.lastmsg) {
9469
cmd = play_message(chan, vmu, &vms);
9470
} else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
9472
cmd = play_message(chan, vmu, &vms);
9474
/* Check if we were listening to urgent
9475
messages. If so, go to regular new messages
9476
instead of saying "no more messages"
9478
if (in_urgent == 1) {
9479
/* Check for new messages */
9481
res = close_mailbox(&vms, vmu);
9482
if (res == ERROR_LOCK_PATH)
9484
res = open_mailbox(&vms, vmu, NEW_FOLDER);
9485
if (res == ERROR_LOCK_PATH)
9487
ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
9489
if (vms.lastmsg < 0)
9490
cmd = ast_play_and_wait(chan, "vm-nomore");
9492
cmd = ast_play_and_wait(chan, "vm-nomore");
9496
} else /* Delete not valid if we haven't selected a message */
9503
case '8': /* Forward the current messgae */
9504
if (vms.lastmsg > -1) {
9505
cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
9506
if (cmd == ERROR_LOCK_PATH) {
9511
/* Check if we were listening to urgent
9512
messages. If so, go to regular new messages
9513
instead of saying "no more messages"
9515
if (in_urgent == 1 && vms.newmessages > 0) {
9516
/* Check for new messages */
9518
res = close_mailbox(&vms, vmu);
9519
if (res == ERROR_LOCK_PATH)
9521
res = open_mailbox(&vms, vmu, NEW_FOLDER);
9522
if (res == ERROR_LOCK_PATH)
9524
ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
9526
if (vms.lastmsg < 0)
9527
cmd = ast_play_and_wait(chan, "vm-nomore");
9529
cmd = ast_play_and_wait(chan, "vm-nomore");
9533
case '9': /* Save message to folder */
9534
if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
9535
/* No message selected */
9540
adsi_folders(chan, 1, "Save to folder...");
9541
cmd = get_folder2(chan, "vm-savefolder", 1);
9542
box = 0; /* Shut up compiler */
9546
} else if (cmd > 0) {
9547
box = cmd = cmd - '0';
9548
cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
9549
if (cmd == ERROR_LOCK_PATH) {
9552
#ifndef IMAP_STORAGE
9554
vms.deleted[vms.curmsg] = 1;
9557
vms.deleted[vms.curmsg] = 0;
9558
vms.heard[vms.curmsg] = 0;
9561
make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
9563
adsi_message(chan, &vms);
9564
snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
9566
cmd = ast_play_and_wait(chan, "vm-message");
9568
cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
9570
cmd = ast_play_and_wait(chan, "vm-savedto");
9572
cmd = vm_play_folder_name(chan, vms.fn);
9574
cmd = ast_play_and_wait(chan, "vm-mailboxfull");
9576
if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
9577
if (vms.curmsg < vms.lastmsg) {
9579
cmd = play_message(chan, vmu, &vms);
9580
} else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
9582
cmd = play_message(chan, vmu, &vms);
9584
/* Check if we were listening to urgent
9585
messages. If so, go to regular new messages
9586
instead of saying "no more messages"
9588
if (in_urgent == 1 && vms.newmessages > 0) {
9589
/* Check for new messages */
9591
res = close_mailbox(&vms, vmu);
9592
if (res == ERROR_LOCK_PATH)
9594
res = open_mailbox(&vms, vmu, NEW_FOLDER);
9595
if (res == ERROR_LOCK_PATH)
9597
ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
9599
if (vms.lastmsg < 0)
9600
cmd = ast_play_and_wait(chan, "vm-nomore");
9602
cmd = ast_play_and_wait(chan, "vm-nomore");
9607
case '*': /* Help */
9608
if (!vms.starting) {
9609
cmd = ast_play_and_wait(chan, "vm-onefor");
9610
if (!strncasecmp(chan->language, "he", 2)) {
9611
cmd = ast_play_and_wait(chan, "vm-for");
9614
cmd = vm_play_folder_name(chan, vms.vmbox);
9616
cmd = ast_play_and_wait(chan, "vm-opts");
9618
cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
9622
case '0': /* Mailbox options */
9623
cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
9625
adsi_status(chan, &vms);
9627
default: /* Nothing */
9628
cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
9632
if ((cmd == 't') || (cmd == '#')) {
9642
ast_stopstream(chan);
9646
res = ast_play_and_wait(chan, "vm-dialout");
9648
res = ast_play_and_wait(chan, "vm-goodbye");
9653
ast_adsi_unload_session(chan);
9656
close_mailbox(&vms, vmu);
9658
int new = 0, old = 0, urgent = 0;
9659
snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
9660
manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
9661
/* Urgent flag not passwd to externnotify here */
9662
run_externnotify(vmu->context, vmu->mailbox, NULL);
9663
ast_app_inboxcount2(ext_context, &urgent, &new, &old);
9664
queue_mwi_event(ext_context, urgent, new, old);
9667
/* expunge message - use UID Expunge if supported on IMAP server*/
9668
ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
9669
if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
9670
ast_mutex_lock(&vms.lock);
9671
#ifdef HAVE_IMAP_TK2006
9672
if (LEVELUIDPLUS (vms.mailstream)) {
9673
mail_expunge_full(vms.mailstream,NIL,EX_UID);
9676
mail_expunge(vms.mailstream);
9677
ast_mutex_unlock(&vms.lock);
9679
/* before we delete the state, we should copy pertinent info
9680
* back to the persistent model */
9682
vmstate_delete(&vms);
9688
ast_free(vms.deleted);
9690
ast_free(vms.heard);
9693
pthread_setspecific(ts_vmstate.key, NULL);
9698
static int vm_exec(struct ast_channel *chan, void *data)
9702
struct leave_vm_options leave_options;
9703
struct ast_flags flags = { 0 };
9704
char *opts[OPT_ARG_ARRAY_SIZE];
9705
AST_DECLARE_APP_ARGS(args,
9710
memset(&leave_options, 0, sizeof(leave_options));
9712
if (chan->_state != AST_STATE_UP)
9715
if (!ast_strlen_zero(data)) {
9716
tmp = ast_strdupa(data);
9717
AST_STANDARD_APP_ARGS(args, tmp);
9718
if (args.argc == 2) {
9719
if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
9721
ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
9722
if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
9725
if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
9726
ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
9729
leave_options.record_gain = (signed char) gain;
9732
if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
9733
if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
9734
leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
9739
res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
9742
if (ast_strlen_zero(temp))
9744
args.argv0 = ast_strdupa(temp);
9747
res = leave_voicemail(chan, args.argv0, &leave_options);
9749
if (res == ERROR_LOCK_PATH) {
9750
ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
9751
pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
9758
static struct ast_vm_user *find_or_create(const char *context, const char *box)
9760
struct ast_vm_user *vmu;
9762
AST_LIST_TRAVERSE(&users, vmu, list) {
9763
if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
9764
if (strcasecmp(vmu->context, context)) {
9765
ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
9766
\n\tcontexts and that you have the 'searchcontexts' option on. This type of\
9767
\n\tconfiguration creates an ambiguity that you likely do not want. Please\
9768
\n\tamend your voicemail.conf file to avoid this situation.\n", box);
9770
ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
9773
if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
9774
ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
9779
if (!(vmu = ast_calloc(1, sizeof(*vmu))))
9782
ast_copy_string(vmu->context, context, sizeof(vmu->context));
9783
ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
9785
AST_LIST_INSERT_TAIL(&users, vmu, list);
9790
static int append_mailbox(const char *context, const char *box, const char *data)
9792
/* Assumes lock is already held */
9796
struct ast_vm_user *vmu;
9798
int new = 0, old = 0, urgent = 0;
9800
tmp = ast_strdupa(data);
9802
if (!(vmu = find_or_create(context, box)))
9805
populate_defaults(vmu);
9808
if ((s = strsep(&stringp, ",")))
9809
ast_copy_string(vmu->password, s, sizeof(vmu->password));
9810
if (stringp && (s = strsep(&stringp, ",")))
9811
ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
9812
if (stringp && (s = strsep(&stringp, ",")))
9813
ast_copy_string(vmu->email, s, sizeof(vmu->email));
9814
if (stringp && (s = strsep(&stringp, ",")))
9815
ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
9816
if (stringp && (s = strsep(&stringp, ",")))
9817
apply_options(vmu, s);
9819
mailbox_full = alloca(strlen(box) + strlen(context) + 1);
9820
strcpy(mailbox_full, box);
9821
strcat(mailbox_full, "@");
9822
strcat(mailbox_full, context);
9824
inboxcount2(mailbox_full, &urgent, &new, &old);
9825
queue_mwi_event(mailbox_full, urgent, new, old);
9830
static int vm_box_exists(struct ast_channel *chan, void *data)
9832
struct ast_vm_user svm;
9833
char *context, *box;
9834
AST_DECLARE_APP_ARGS(args,
9836
AST_APP_ARG(options);
9838
static int dep_warning = 0;
9840
if (ast_strlen_zero(data)) {
9841
ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
9847
ast_log(AST_LOG_WARNING, "MailboxExists is deprecated. Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
9850
box = ast_strdupa(data);
9852
AST_STANDARD_APP_ARGS(args, box);
9857
if ((context = strchr(args.mbox, '@'))) {
9862
if (find_user(&svm, context, args.mbox)) {
9863
pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
9865
pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
9870
static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
9872
struct ast_vm_user svm;
9873
AST_DECLARE_APP_ARGS(arg,
9875
AST_APP_ARG(context);
9878
AST_NONSTANDARD_APP_ARGS(arg, args, '@');
9880
if (ast_strlen_zero(arg.mbox)) {
9881
ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
9885
ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
9889
static struct ast_custom_function mailbox_exists_acf = {
9890
.name = "MAILBOX_EXISTS",
9891
.read = acf_mailbox_exists,
9894
static int vmauthenticate(struct ast_channel *chan, void *data)
9896
char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
9897
struct ast_vm_user vmus;
9898
char *options = NULL;
9899
int silent = 0, skipuser = 0;
9904
user = strsep(&s, ",");
9905
options = strsep(&s, ",");
9908
user = strsep(&s, "@");
9909
context = strsep(&s, "");
9910
if (!ast_strlen_zero(user))
9912
ast_copy_string(mailbox, user, sizeof(mailbox));
9917
silent = (strchr(options, 's')) != NULL;
9920
if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
9921
pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
9922
pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
9923
ast_play_and_wait(chan, "auth-thankyou");
9930
static char *show_users_realtime(int fd, const char *context)
9932
struct ast_config *cfg;
9933
const char *cat = NULL;
9935
if (!(cfg = ast_load_realtime_multientry("voicemail",
9936
"context", context, SENTINEL))) {
9942
"=============================================================\n"
9943
"=== Configured Voicemail Users ==============================\n"
9944
"=============================================================\n"
9947
while ((cat = ast_category_browse(cfg, cat))) {
9948
struct ast_variable *var = NULL;
9952
for (var = ast_variable_browse(cfg, cat); var; var = var->next)
9953
ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
9956
"=== ---------------------------------------------------------\n"
9961
"=============================================================\n"
9964
ast_config_destroy(cfg);
9969
static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
9973
struct ast_vm_user *vmu;
9974
const char *context = "";
9976
/* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
9980
return (state == 0) ? ast_strdup("for") : NULL;
9981
wordlen = strlen(word);
9982
AST_LIST_TRAVERSE(&users, vmu, list) {
9983
if (!strncasecmp(word, vmu->context, wordlen)) {
9984
if (context && strcmp(context, vmu->context) && ++which > state)
9985
return ast_strdup(vmu->context);
9986
/* ignore repeated contexts ? */
9987
context = vmu->context;
9993
/*! \brief Show a list of voicemail users in the CLI */
9994
static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
9996
struct ast_vm_user *vmu;
9997
#define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
9998
const char *context = NULL;
9999
int users_counter = 0;
10003
e->command = "voicemail show users";
10005
"Usage: voicemail show users [for <context>]\n"
10006
" Lists all mailboxes currently set up\n";
10009
return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
10012
if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
10013
return CLI_SHOWUSAGE;
10014
if (a->argc == 5) {
10015
if (strcmp(a->argv[3],"for"))
10016
return CLI_SHOWUSAGE;
10017
context = a->argv[4];
10020
if (ast_check_realtime("voicemail")) {
10022
ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
10023
return CLI_SHOWUSAGE;
10025
return show_users_realtime(a->fd, context);
10028
AST_LIST_LOCK(&users);
10029
if (AST_LIST_EMPTY(&users)) {
10030
ast_cli(a->fd, "There are no voicemail users currently defined\n");
10031
AST_LIST_UNLOCK(&users);
10032
return CLI_FAILURE;
10035
ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10038
AST_LIST_TRAVERSE(&users, vmu, list) {
10039
if (!strcmp(context, vmu->context))
10043
ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
10045
ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
10046
AST_LIST_UNLOCK(&users);
10047
return CLI_FAILURE;
10050
AST_LIST_TRAVERSE(&users, vmu, list) {
10051
int newmsgs = 0, oldmsgs = 0;
10052
char count[12], tmp[256] = "";
10054
if ((a->argc == 3) || ((a->argc == 5) && !strcmp(context, vmu->context))) {
10055
snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
10056
inboxcount(tmp, &newmsgs, &oldmsgs);
10057
snprintf(count, sizeof(count), "%d", newmsgs);
10058
ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
10062
AST_LIST_UNLOCK(&users);
10063
ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
10064
return CLI_SUCCESS;
10067
/*! \brief Show a list of voicemail zones in the CLI */
10068
static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10070
struct vm_zone *zone;
10071
#define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
10072
char *res = CLI_SUCCESS;
10076
e->command = "voicemail show zones";
10078
"Usage: voicemail show zones\n"
10079
" Lists zone message formats\n";
10086
return CLI_SHOWUSAGE;
10088
AST_LIST_LOCK(&zones);
10089
if (!AST_LIST_EMPTY(&zones)) {
10090
ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
10091
AST_LIST_TRAVERSE(&zones, zone, list) {
10092
ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
10095
ast_cli(a->fd, "There are no voicemail zones currently defined\n");
10098
AST_LIST_UNLOCK(&zones);
10103
/*! \brief Reload voicemail configuration from the CLI */
10104
static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
10108
e->command = "voicemail reload";
10110
"Usage: voicemail reload\n"
10111
" Reload voicemail configuration\n";
10118
return CLI_SHOWUSAGE;
10120
ast_cli(a->fd, "Reloading voicemail configuration...\n");
10123
return CLI_SUCCESS;
10126
static struct ast_cli_entry cli_voicemail[] = {
10127
AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
10128
AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
10129
AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
10132
static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
10134
int new = 0, old = 0, urgent = 0;
10136
inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
10138
if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
10139
mwi_sub->old_urgent = urgent;
10140
mwi_sub->old_new = new;
10141
mwi_sub->old_old = old;
10142
queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
10146
static void poll_subscribed_mailboxes(void)
10148
struct mwi_sub *mwi_sub;
10150
AST_RWLIST_RDLOCK(&mwi_subs);
10151
AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
10152
if (!ast_strlen_zero(mwi_sub->mailbox)) {
10153
poll_subscribed_mailbox(mwi_sub);
10156
AST_RWLIST_UNLOCK(&mwi_subs);
10159
static void *mb_poll_thread(void *data)
10161
while (poll_thread_run) {
10162
struct timespec ts = { 0, };
10163
struct timeval wait;
10165
wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
10166
ts.tv_sec = wait.tv_sec;
10167
ts.tv_nsec = wait.tv_usec * 1000;
10169
ast_mutex_lock(&poll_lock);
10170
ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
10171
ast_mutex_unlock(&poll_lock);
10173
if (!poll_thread_run)
10176
poll_subscribed_mailboxes();
10182
static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
10187
static int handle_unsubscribe(void *datap)
10189
struct mwi_sub *mwi_sub;
10190
uint32_t *uniqueid = datap;
10192
AST_RWLIST_WRLOCK(&mwi_subs);
10193
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
10194
if (mwi_sub->uniqueid == *uniqueid) {
10195
AST_LIST_REMOVE_CURRENT(entry);
10199
AST_RWLIST_TRAVERSE_SAFE_END
10200
AST_RWLIST_UNLOCK(&mwi_subs);
10203
mwi_sub_destroy(mwi_sub);
10205
ast_free(uniqueid);
10209
static int handle_subscribe(void *datap)
10212
struct mwi_sub *mwi_sub;
10213
struct mwi_sub_task *p = datap;
10215
len = sizeof(*mwi_sub);
10216
if (!ast_strlen_zero(p->mailbox))
10217
len += strlen(p->mailbox);
10219
if (!ast_strlen_zero(p->context))
10220
len += strlen(p->context) + 1; /* Allow for seperator */
10222
if (!(mwi_sub = ast_calloc(1, len)))
10225
mwi_sub->uniqueid = p->uniqueid;
10226
if (!ast_strlen_zero(p->mailbox))
10227
strcpy(mwi_sub->mailbox, p->mailbox);
10229
if (!ast_strlen_zero(p->context)) {
10230
strcat(mwi_sub->mailbox, "@");
10231
strcat(mwi_sub->mailbox, p->context);
10234
AST_RWLIST_WRLOCK(&mwi_subs);
10235
AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
10236
AST_RWLIST_UNLOCK(&mwi_subs);
10237
ast_free((void *) p->mailbox);
10238
ast_free((void *) p->context);
10240
poll_subscribed_mailbox(mwi_sub);
10244
static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
10246
uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
10247
if (ast_event_get_type(event) != AST_EVENT_UNSUB)
10250
if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10253
u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10255
if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
10256
ast_free(uniqueid);
10260
static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
10262
struct mwi_sub_task *mwist;
10264
if (ast_event_get_type(event) != AST_EVENT_SUB)
10267
if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
10270
if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
10271
ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
10274
mwist->mailbox = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_MAILBOX));
10275
mwist->context = ast_strdup(ast_event_get_ie_str(event, AST_EVENT_IE_CONTEXT));
10276
mwist->uniqueid = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
10278
if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
10283
static void start_poll_thread(void)
10285
mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, NULL,
10286
AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10289
mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, NULL,
10290
AST_EVENT_IE_EVENTTYPE, AST_EVENT_IE_PLTYPE_UINT, AST_EVENT_MWI,
10294
ast_event_report_subs(mwi_sub_sub);
10296
poll_thread_run = 1;
10298
ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
10301
static void stop_poll_thread(void)
10303
poll_thread_run = 0;
10306
ast_event_unsubscribe(mwi_sub_sub);
10307
mwi_sub_sub = NULL;
10310
if (mwi_unsub_sub) {
10311
ast_event_unsubscribe(mwi_unsub_sub);
10312
mwi_unsub_sub = NULL;
10315
ast_mutex_lock(&poll_lock);
10316
ast_cond_signal(&poll_cond);
10317
ast_mutex_unlock(&poll_lock);
10319
pthread_join(poll_thread, NULL);
10321
poll_thread = AST_PTHREADT_NULL;
10324
/*! \brief Manager list voicemail users command */
10325
static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
10327
struct ast_vm_user *vmu = NULL;
10328
const char *id = astman_get_header(m, "ActionID");
10329
char actionid[128] = "";
10331
if (!ast_strlen_zero(id))
10332
snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
10334
AST_LIST_LOCK(&users);
10336
if (AST_LIST_EMPTY(&users)) {
10337
astman_send_ack(s, m, "There are no voicemail users currently defined.");
10338
AST_LIST_UNLOCK(&users);
10339
return RESULT_SUCCESS;
10342
astman_send_ack(s, m, "Voicemail user list will follow");
10344
AST_LIST_TRAVERSE(&users, vmu, list) {
10347
#ifdef IMAP_STORAGE
10349
inboxcount(vmu->mailbox, &new, &old);
10352
make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
10355
"Event: VoicemailUserEntry\r\n"
10356
"VMContext: %s\r\n"
10357
"VoiceMailbox: %s\r\n"
10361
"ServerEmail: %s\r\n"
10362
"MailCommand: %s\r\n"
10368
"ExitContext: %s\r\n"
10369
"SayDurationMinimum: %d\r\n"
10370
"SayEnvelope: %s\r\n"
10372
"AttachMessage: %s\r\n"
10373
"AttachmentFormat: %s\r\n"
10374
"DeleteMessage: %s\r\n"
10375
"VolumeGain: %.2f\r\n"
10376
"CanReview: %s\r\n"
10377
"CallOperator: %s\r\n"
10378
"MaxMessageCount: %d\r\n"
10379
"MaxMessageLength: %d\r\n"
10380
"NewMessageCount: %d\r\n"
10381
#ifdef IMAP_STORAGE
10382
"OldMessageCount: %d\r\n"
10401
ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
10402
ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
10403
ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
10405
ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
10407
ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
10408
ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
10411
#ifdef IMAP_STORAGE
10412
new, old, vmu->imapuser
10414
count_messages(vmu, dirname)
10418
astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
10420
AST_LIST_UNLOCK(&users);
10422
return RESULT_SUCCESS;
10425
/*! \brief Free the users structure. */
10426
static void free_vm_users(void)
10428
struct ast_vm_user *current;
10429
AST_LIST_LOCK(&users);
10430
while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
10431
ast_set_flag(current, VM_ALLOCED);
10432
free_user(current);
10434
AST_LIST_UNLOCK(&users);
10437
/*! \brief Free the zones structure. */
10438
static void free_vm_zones(void)
10440
struct vm_zone *zcur;
10441
AST_LIST_LOCK(&zones);
10442
while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
10444
AST_LIST_UNLOCK(&zones);
10447
static const char *substitute_escapes(const char *value)
10451
/* Add 16 for fudge factor */
10452
struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
10454
ast_str_reset(str);
10456
/* Substitute strings \r, \n, and \t into the appropriate characters */
10457
for (current = (char *) value; *current; current++) {
10458
if (*current == '\\') {
10461
ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
10464
switch (*current) {
10466
ast_str_append(&str, 0, "\r");
10469
#ifdef IMAP_STORAGE
10470
if (!str->used || str->str[str->used - 1] != '\r') {
10471
ast_str_append(&str, 0, "\r");
10474
ast_str_append(&str, 0, "\n");
10477
ast_str_append(&str, 0, "\t");
10480
ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
10484
ast_str_append(&str, 0, "%c", *current);
10488
return ast_str_buffer(str);
10491
static int load_config(int reload)
10493
struct ast_vm_user *current;
10494
struct ast_config *cfg, *ucfg;
10496
struct ast_variable *var;
10498
char *q, *stringp, *tmp;
10501
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
10503
ast_unload_realtime("voicemail");
10504
ast_unload_realtime("voicemail_data");
10506
if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10507
if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
10509
} else if (ucfg == CONFIG_STATUS_FILEINVALID) {
10510
ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
10513
ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10514
if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
10515
ast_config_destroy(ucfg);
10516
ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
10519
} else if (cfg == CONFIG_STATUS_FILEINVALID) {
10520
ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
10523
ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
10524
if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
10525
ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
10529
#ifdef IMAP_STORAGE
10530
ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
10532
/* set audio control prompts */
10533
strcpy(listen_control_forward_key,DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
10534
strcpy(listen_control_reverse_key,DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
10535
strcpy(listen_control_pause_key,DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
10536
strcpy(listen_control_restart_key,DEFAULT_LISTEN_CONTROL_RESTART_KEY);
10537
strcpy(listen_control_stop_key,DEFAULT_LISTEN_CONTROL_STOP_KEY);
10539
/* Free all the users structure */
10542
/* Free all the zones structure */
10545
AST_LIST_LOCK(&users);
10547
memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
10548
memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
10551
/* General settings */
10553
if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
10555
ast_copy_string(userscontext, val, sizeof(userscontext));
10556
/* Attach voice message to mail message ? */
10557
if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
10559
ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH);
10561
if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
10563
ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
10566
if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
10567
sscanf(val, "%30lf", &volgain);
10569
#ifdef ODBC_STORAGE
10570
strcpy(odbc_database, "asterisk");
10571
if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
10572
ast_copy_string(odbc_database, val, sizeof(odbc_database));
10574
strcpy(odbc_table, "voicemessages");
10575
if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
10576
ast_copy_string(odbc_table, val, sizeof(odbc_table));
10580
strcpy(mailcmd, SENDMAIL);
10581
if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
10582
ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
10585
if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
10586
maxsilence = atoi(val);
10587
if (maxsilence > 0)
10588
maxsilence *= 1000;
10591
if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
10594
maxmsg = atoi(val);
10596
ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
10598
} else if (maxmsg > MAXMSGLIMIT) {
10599
ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10600
maxmsg = MAXMSGLIMIT;
10604
if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
10607
if (sscanf(val, "%30d", &x) == 1)
10609
else if (ast_true(val))
10610
maxdeletedmsg = MAXMSG;
10614
if (maxdeletedmsg < 0) {
10615
ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
10616
maxdeletedmsg = MAXMSG;
10617
} else if (maxdeletedmsg > MAXMSGLIMIT) {
10618
ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
10619
maxdeletedmsg = MAXMSGLIMIT;
10623
/* Load date format config for voicemail mail */
10624
if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
10625
ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
10628
/* External password changing command */
10629
if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
10630
ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10631
pwdchange = PWDCHANGE_EXTERNAL;
10632
} else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
10633
ast_copy_string(ext_pass_cmd,val,sizeof(ext_pass_cmd));
10634
pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
10637
/* External password validation command */
10638
if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
10639
ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
10640
ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
10643
#ifdef IMAP_STORAGE
10644
/* IMAP server address */
10645
if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
10646
ast_copy_string(imapserver, val, sizeof(imapserver));
10648
ast_copy_string(imapserver,"localhost", sizeof(imapserver));
10650
/* IMAP server port */
10651
if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
10652
ast_copy_string(imapport, val, sizeof(imapport));
10654
ast_copy_string(imapport,"143", sizeof(imapport));
10656
/* IMAP server flags */
10657
if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
10658
ast_copy_string(imapflags, val, sizeof(imapflags));
10660
/* IMAP server master username */
10661
if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
10662
ast_copy_string(authuser, val, sizeof(authuser));
10664
/* IMAP server master password */
10665
if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
10666
ast_copy_string(authpassword, val, sizeof(authpassword));
10668
/* Expunge on exit */
10669
if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
10670
if (ast_false(val))
10671
expungeonhangup = 0;
10673
expungeonhangup = 1;
10675
expungeonhangup = 1;
10677
/* IMAP voicemail folder */
10678
if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
10679
ast_copy_string(imapfolder, val, sizeof(imapfolder));
10681
ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
10683
if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
10684
ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
10686
if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
10687
imapgreetings = ast_true(val);
10691
if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
10692
ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
10694
ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
10697
/* There is some very unorthodox casting done here. This is due
10698
* to the way c-client handles the argument passed in. It expects a
10699
* void pointer and casts the pointer directly to a long without
10700
* first dereferencing it. */
10701
if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
10702
mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
10704
mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
10707
if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
10708
mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
10710
mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
10713
if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
10714
mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
10716
mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
10719
if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
10720
mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
10722
mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
10725
/* Increment configuration version */
10728
/* External voicemail notify application */
10729
if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
10730
ast_copy_string(externnotify, val, sizeof(externnotify));
10731
ast_debug(1, "found externnotify: %s\n", externnotify);
10733
externnotify[0] = '\0';
10736
/* SMDI voicemail notification */
10737
if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
10738
ast_debug(1, "Enabled SMDI voicemail notification\n");
10739
if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
10740
smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find(val) : NULL;
10742
ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
10743
smdi_iface = ast_smdi_interface_find ? ast_smdi_interface_find("/dev/ttyS0") : NULL;
10746
ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
10750
/* Silence treshold */
10751
silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
10752
if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
10753
silencethreshold = atoi(val);
10755
if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
10756
val = ASTERISK_USERNAME;
10757
ast_copy_string(serveremail, val, sizeof(serveremail));
10760
if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
10761
if (sscanf(val, "%30d", &x) == 1) {
10764
ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10766
} else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
10767
static int maxmessage_deprecate = 0;
10768
if (maxmessage_deprecate == 0) {
10769
maxmessage_deprecate = 1;
10770
ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
10772
if (sscanf(val, "%30d", &x) == 1) {
10775
ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
10780
if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
10781
if (sscanf(val, "%30d", &x) == 1) {
10783
if (maxsilence / 1000 >= vmminsecs) {
10784
ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
10787
ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10789
} else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
10790
static int maxmessage_deprecate = 0;
10791
if (maxmessage_deprecate == 0) {
10792
maxmessage_deprecate = 1;
10793
ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
10795
if (sscanf(val, "%30d", &x) == 1) {
10797
if (maxsilence / 1000 >= vmminsecs) {
10798
ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
10801
ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
10805
val = ast_variable_retrieve(cfg, "general", "format");
10809
tmp = ast_strdupa(val);
10810
val = ast_format_str_reduce(tmp);
10812
ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
10816
ast_copy_string(vmfmts, val, sizeof(vmfmts));
10819
if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
10820
if (sscanf(val, "%30d", &x) == 1) {
10823
ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
10827
if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
10828
if (sscanf(val, "%30d", &x) == 1) {
10831
ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
10836
if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
10837
if (sscanf(val, "%30d", &x) == 1) {
10840
ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
10844
minpassword = MINPASSWORD;
10845
if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
10846
if (sscanf(val, "%30d", &x) == 1) {
10849
ast_log(AST_LOG_WARNING, "Invalid minimum password length. Default to %d\n", minpassword);
10853
/* Force new user to record name ? */
10854
if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
10856
ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
10858
/* Force new user to record greetings ? */
10859
if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
10861
ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
10863
if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
10864
ast_debug(1, "VM_CID Internal context string: %s\n", val);
10865
stringp = ast_strdupa(val);
10866
for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
10867
if (!ast_strlen_zero(stringp)) {
10868
q = strsep(&stringp, ",");
10869
while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
10871
ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
10872
ast_debug(1,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
10874
cidinternalcontexts[x][0] = '\0';
10878
if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
10879
ast_debug(1,"VM Review Option disabled globally\n");
10882
ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW);
10884
/* Temporary greeting reminder */
10885
if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
10886
ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
10889
ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
10891
ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
10892
if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
10893
ast_debug(1, "VM next message wrap disabled globally\n");
10896
ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);
10898
if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
10899
ast_debug(1,"VM Operator break disabled globally\n");
10902
ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);
10904
if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
10905
ast_debug(1,"VM CID Info before msg disabled globally\n");
10908
ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID);
10910
if (!(val = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
10911
ast_debug(1,"Send Voicemail msg disabled globally\n");
10914
ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
10916
if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
10917
ast_debug(1,"ENVELOPE before msg enabled globally\n");
10920
ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);
10922
if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
10923
ast_debug(1,"Move Heard enabled globally\n");
10926
ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD);
10928
if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
10929
ast_debug(1,"Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
10932
ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);
10934
if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
10935
ast_debug(1,"Duration info before msg enabled globally\n");
10938
ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);
10940
saydurationminfo = 2;
10941
if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
10942
if (sscanf(val, "%30d", &x) == 1) {
10943
saydurationminfo = x;
10945
ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
10949
if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
10950
ast_debug(1,"We are not going to skip to the next msg after save/delete\n");
10953
ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
10955
if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
10956
ast_copy_string(dialcontext, val, sizeof(dialcontext));
10957
ast_debug(1, "found dialout context: %s\n", dialcontext);
10959
dialcontext[0] = '\0';
10962
if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
10963
ast_copy_string(callcontext, val, sizeof(callcontext));
10964
ast_debug(1, "found callback context: %s\n", callcontext);
10966
callcontext[0] = '\0';
10969
if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
10970
ast_copy_string(exitcontext, val, sizeof(exitcontext));
10971
ast_debug(1, "found operator context: %s\n", exitcontext);
10973
exitcontext[0] = '\0';
10976
/* load password sounds configuration */
10977
if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
10978
ast_copy_string(vm_password, val, sizeof(vm_password));
10979
if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
10980
ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
10981
if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
10982
ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
10983
if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
10984
ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
10985
if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
10986
ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
10987
if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
10988
ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
10989
if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
10990
ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
10992
/* load configurable audio prompts */
10993
if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
10994
ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
10995
if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
10996
ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
10997
if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
10998
ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
10999
if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
11000
ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
11001
if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
11002
ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
11004
if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
11006
ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD);
11008
poll_freq = DEFAULT_POLL_FREQ;
11009
if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
11010
if (sscanf(val, "%30u", &poll_freq) != 1) {
11011
poll_freq = DEFAULT_POLL_FREQ;
11012
ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
11016
poll_mailboxes = 0;
11017
if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
11018
poll_mailboxes = ast_true(val);
11021
for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
11022
if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
11024
if ((current = find_or_create(userscontext, cat))) {
11025
populate_defaults(current);
11026
apply_options_full(current, ast_variable_browse(ucfg, cat));
11027
ast_copy_string(current->context, userscontext, sizeof(current->context));
11030
ast_config_destroy(ucfg);
11032
cat = ast_category_browse(cfg, NULL);
11034
if (strcasecmp(cat, "general")) {
11035
var = ast_variable_browse(cfg, cat);
11036
if (strcasecmp(cat, "zonemessages")) {
11037
/* Process mailboxes in this context */
11039
append_mailbox(cat, var->name, var->value);
11043
/* Timezones in this context */
11046
if ((z = ast_malloc(sizeof(*z)))) {
11047
char *msg_format, *tzone;
11048
msg_format = ast_strdupa(var->value);
11049
tzone = strsep(&msg_format, "|");
11051
ast_copy_string(z->name, var->name, sizeof(z->name));
11052
ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
11053
ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
11054
AST_LIST_LOCK(&zones);
11055
AST_LIST_INSERT_HEAD(&zones, z, list);
11056
AST_LIST_UNLOCK(&zones);
11058
ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
11062
AST_LIST_UNLOCK(&users);
11063
ast_config_destroy(cfg);
11070
cat = ast_category_browse(cfg, cat);
11072
memset(fromstring, 0, sizeof(fromstring));
11073
memset(pagerfromstring, 0, sizeof(pagerfromstring));
11074
strcpy(charset, "ISO-8859-1");
11076
ast_free(emailbody);
11079
if (emailsubject) {
11080
ast_free(emailsubject);
11081
emailsubject = NULL;
11084
ast_free(pagerbody);
11087
if (pagersubject) {
11088
ast_free(pagersubject);
11089
pagersubject = NULL;
11091
if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
11092
ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
11093
if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
11094
ast_copy_string(fromstring, val, sizeof(fromstring));
11095
if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
11096
ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
11097
if ((val = ast_variable_retrieve(cfg, "general", "charset")))
11098
ast_copy_string(charset, val, sizeof(charset));
11099
if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
11100
sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11101
for (x = 0; x < 4; x++) {
11102
memcpy(&adsifdn[x], &tmpadsi[x], 1);
11105
if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
11106
sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
11107
for (x = 0; x < 4; x++) {
11108
memcpy(&adsisec[x], &tmpadsi[x], 1);
11111
if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
11113
adsiver = atoi(val);
11116
if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
11117
ast_copy_string(zonetag, val, sizeof(zonetag));
11119
if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
11120
emailsubject = ast_strdup(val);
11122
if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
11123
emailbody = ast_strdup(substitute_escapes(val));
11125
if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
11126
pagersubject = ast_strdup(val);
11128
if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
11129
pagerbody = ast_strdup(substitute_escapes(val));
11131
AST_LIST_UNLOCK(&users);
11132
ast_config_destroy(cfg);
11134
if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
11135
start_poll_thread();
11136
if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
11137
stop_poll_thread();;
11141
AST_LIST_UNLOCK(&users);
11142
ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
11144
ast_config_destroy(ucfg);
11149
static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
11152
char dir[PATH_MAX];
11153
snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
11154
ast_debug(2, "About to try retrieving name file %s\n", dir);
11155
RETRIEVE(dir, -1, mailbox, context);
11156
if (ast_fileexists(dir, NULL, NULL)) {
11157
res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
11163
static int reload(void)
11165
return load_config(1);
11168
static int unload_module(void)
11172
res = ast_unregister_application(app);
11173
res |= ast_unregister_application(app2);
11174
res |= ast_unregister_application(app3);
11175
res |= ast_unregister_application(app4);
11176
res |= ast_custom_function_unregister(&mailbox_exists_acf);
11177
res |= ast_manager_unregister("VoicemailUsersList");
11178
ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
11179
ast_uninstall_vm_functions();
11181
if (poll_thread != AST_PTHREADT_NULL)
11182
stop_poll_thread();
11184
mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
11185
ast_unload_realtime("voicemail");
11186
ast_unload_realtime("voicemail_data");
11193
static int load_module(void)
11196
my_umask = umask(0);
11199
/* compute the location of the voicemail spool directory */
11200
snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
11202
if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
11203
ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor. MWI will not work\n");
11206
if ((res = load_config(0)))
11209
res = ast_register_application_xml(app, vm_exec);
11210
res |= ast_register_application_xml(app2, vm_execmain);
11211
res |= ast_register_application_xml(app3, vm_box_exists);
11212
res |= ast_register_application_xml(app4, vmauthenticate);
11213
res |= ast_custom_function_register(&mailbox_exists_acf);
11214
res |= ast_manager_register("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users, "List All Voicemail User Information");
11218
ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
11220
ast_install_vm_functions(has_voicemail, inboxcount, inboxcount2, messagecount, sayname);
11221
ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
11222
ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
11227
static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
11230
char destination[80] = "";
11234
ast_verb(3, "Destination number will be entered manually\n");
11235
while (retries < 3 && cmd != 't') {
11236
destination[1] = '\0';
11237
destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
11239
destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
11241
destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
11243
cmd = ast_waitfordigit(chan, 6000);
11245
destination[0] = cmd;
11254
ast_verb(3, "User hit '*' to cancel outgoing call\n");
11257
if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0)
11263
if (retries >= 3) {
11268
if (option_verbose > 2)
11269
ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
11270
ast_copy_string(destination, num, sizeof(destination));
11273
if (!ast_strlen_zero(destination)) {
11274
if (destination[strlen(destination) -1 ] == '*')
11276
if (option_verbose > 2)
11277
ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
11278
ast_copy_string(chan->exten, destination, sizeof(chan->exten));
11279
ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
11280
chan->priority = 0;
11287
* \brief The advanced options within a message.
11293
* \param record_gain
11295
* Provides handling for the play message envelope, call the person back, or reply to message.
11297
* \return zero on success, -1 on error.
11299
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)
11302
char filename[PATH_MAX];
11303
struct ast_config *msg_cfg = NULL;
11304
const char *origtime, *context;
11308
struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
11312
make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11314
/* Retrieve info from VM attribute file */
11315
snprintf(filename,sizeof(filename), "%s.txt", vms->fn);
11316
RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
11317
msg_cfg = ast_config_load(filename, config_flags);
11318
DISPOSE(vms->curdir, vms->curmsg);
11319
if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
11320
ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
11324
if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
11325
ast_config_destroy(msg_cfg);
11329
cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
11331
context = ast_variable_retrieve(msg_cfg, "message", "context");
11332
if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
11333
context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
11335
case 3: /* Play message envelope */
11337
res = play_message_datetime(chan, vmu, origtime, filename);
11339
res = play_message_callerid(chan, vms, cid, context, 0);
11344
case 2: /* Call back */
11346
if (ast_strlen_zero(cid))
11349
ast_callerid_parse(cid, &name, &num);
11350
while ((res > -1) && (res != 't')) {
11354
/* Dial the CID number */
11355
res = dialout(chan, vmu, num, vmu->callback);
11357
ast_config_destroy(msg_cfg);
11366
/* Want to enter a different number, can only do this if there's a dialout context for this user */
11367
if (!ast_strlen_zero(vmu->dialout)) {
11368
res = dialout(chan, vmu, NULL, vmu->dialout);
11370
ast_config_destroy(msg_cfg);
11374
ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
11375
res = ast_play_and_wait(chan, "vm-sorry");
11377
ast_config_destroy(msg_cfg);
11391
res = ast_play_and_wait(chan, "vm-sorry");
11396
ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
11397
res = ast_play_and_wait(chan, "vm-num-i-have");
11399
res = play_message_callerid(chan, vms, num, vmu->context, 1);
11401
res = ast_play_and_wait(chan, "vm-tocallnum");
11402
/* Only prompt for a caller-specified number if there is a dialout context specified */
11403
if (!ast_strlen_zero(vmu->dialout)) {
11405
res = ast_play_and_wait(chan, "vm-calldiffnum");
11408
res = ast_play_and_wait(chan, "vm-nonumber");
11409
if (!ast_strlen_zero(vmu->dialout)) {
11411
res = ast_play_and_wait(chan, "vm-toenternumber");
11415
res = ast_play_and_wait(chan, "vm-star-cancel");
11417
res = ast_waitfordigit(chan, 6000);
11428
else if (res == '*')
11433
case 1: /* Reply */
11434
/* Send reply directly to sender */
11435
if (ast_strlen_zero(cid))
11438
ast_callerid_parse(cid, &name, &num);
11440
ast_verb(3, "No CID number available, no reply sent\n");
11442
res = ast_play_and_wait(chan, "vm-nonumber");
11443
ast_config_destroy(msg_cfg);
11446
struct ast_vm_user vmu2;
11447
if (find_user(&vmu2, vmu->context, num)) {
11448
struct leave_vm_options leave_options;
11449
char mailbox[AST_MAX_EXTENSION * 2 + 2];
11450
snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
11452
ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
11454
memset(&leave_options, 0, sizeof(leave_options));
11455
leave_options.record_gain = record_gain;
11456
res = leave_voicemail(chan, mailbox, &leave_options);
11459
ast_config_destroy(msg_cfg);
11462
/* Sender has no mailbox, can't reply */
11463
ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
11464
ast_play_and_wait(chan, "vm-nobox");
11466
ast_config_destroy(msg_cfg);
11475
#ifndef IMAP_STORAGE
11476
ast_config_destroy(msg_cfg);
11479
make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
11480
vms->heard[msg] = 1;
11481
res = wait_file(chan, vms, vms->fn);
11487
static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
11488
int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
11489
signed char record_gain, struct vm_state *vms, char *flag)
11491
/* Record message & let caller review or re-record it, or set options if applicable */
11494
int max_attempts = 3;
11497
int msg_exists = 0;
11498
signed char zero_gain = 0;
11499
char tempfile[PATH_MAX];
11500
char *acceptdtmf = "#";
11501
char *canceldtmf = "";
11503
/* Note that urgent and private are for flagging messages as such in the future */
11505
/* barf if no pointer passed to store duration in */
11506
if (duration == NULL) {
11507
ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
11511
if (!outsidecaller)
11512
snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
11514
ast_copy_string(tempfile, recordfile, sizeof(tempfile));
11516
cmd = '3'; /* Want to start by recording */
11518
while ((cmd >= 0) && (cmd != 't')) {
11522
/* In this case, 1 is to record a message */
11526
/* Otherwise 1 is to save the existing message */
11527
ast_verb(3, "Saving message as is\n");
11528
if (!outsidecaller)
11529
ast_filerename(tempfile, recordfile, NULL);
11530
ast_stream_and_wait(chan, "vm-msgsaved", "");
11531
if (!outsidecaller) {
11532
/* Saves to IMAP server only if imapgreeting=yes */
11533
STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
11534
DISPOSE(recordfile, -1);
11541
ast_verb(3, "Reviewing the message\n");
11542
cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
11548
ast_verb(3, "Re-recording the message\n");
11550
ast_verb(3, "Recording the message\n");
11552
if (recorded && outsidecaller) {
11553
cmd = ast_play_and_wait(chan, INTRO);
11554
cmd = ast_play_and_wait(chan, "beep");
11557
/* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
11559
ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
11560
if (ast_test_flag(vmu, VM_OPERATOR))
11562
cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
11564
ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
11566
/* User has hung up, no options to give */
11567
if (!outsidecaller) {
11568
/* user was recording a greeting and they hung up, so let's delete the recording. */
11569
ast_filedelete(tempfile, NULL);
11575
} else if (cmd == '*') {
11578
} else if (vmu->review && (*duration < 5)) {
11579
/* Message is too short */
11580
ast_verb(3, "Message too short\n");
11581
cmd = ast_play_and_wait(chan, "vm-tooshort");
11582
cmd = ast_filedelete(tempfile, NULL);
11584
} else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
11585
/* Message is all silence */
11586
ast_verb(3, "Nothing recorded\n");
11587
cmd = ast_filedelete(tempfile, NULL);
11588
cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
11590
cmd = ast_play_and_wait(chan, "vm-speakup");
11594
/* If all is well, a message exists */
11600
if (outsidecaller) { /* only mark vm messages */
11602
if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11603
ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
11604
ast_debug(1000, "This message is too urgent!\n");
11605
res = ast_play_and_wait(chan, "vm-marked-urgent");
11606
strcpy(flag, "Urgent");
11608
ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
11609
res = ast_play_and_wait(chan, "vm-urgent-removed");
11612
ast_play_and_wait(chan, "vm-sorry");
11616
cmd = ast_play_and_wait(chan, "vm-sorry");
11626
cmd = ast_play_and_wait(chan, "vm-sorry");
11629
/* XXX Commented out for the moment because of the dangers of deleting
11630
a message while recording (can put the message numbers out of sync) */
11632
/* Cancel recording, delete message, offer to take another message*/
11633
cmd = ast_play_and_wait(chan, "vm-deleted");
11634
cmd = ast_filedelete(tempfile, NULL);
11635
if (outsidecaller) {
11636
res = vm_exec(chan, NULL);
11643
if (!ast_test_flag(vmu, VM_OPERATOR)) {
11644
cmd = ast_play_and_wait(chan, "vm-sorry");
11647
if (msg_exists || recorded) {
11648
cmd = ast_play_and_wait(chan, "vm-saveoper");
11650
cmd = ast_waitfordigit(chan, 3000);
11652
ast_play_and_wait(chan, "vm-msgsaved");
11654
} else if (cmd == '4') {
11656
ast_play_and_wait(chan, "vm-marked-urgent");
11657
strcpy(flag, "Urgent");
11659
ast_play_and_wait(chan, "vm-msgsaved");
11662
ast_play_and_wait(chan, "vm-deleted");
11663
DELETE(recordfile, -1, recordfile, vmu);
11669
/* If the caller is an ouside caller, and the review option is enabled,
11670
allow them to review the message, but let the owner of the box review
11672
if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
11675
cmd = ast_play_and_wait(chan, "vm-review");
11676
if (!cmd && outsidecaller) {
11677
if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
11678
cmd = ast_play_and_wait(chan, "vm-review-urgent");
11680
cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
11684
cmd = ast_play_and_wait(chan, "vm-torerecord");
11686
cmd = ast_waitfordigit(chan, 600);
11689
if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
11690
cmd = ast_play_and_wait(chan, "vm-reachoper");
11692
cmd = ast_waitfordigit(chan, 600);
11696
cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
11699
cmd = ast_waitfordigit(chan, 6000);
11703
if (attempts > max_attempts) {
11709
ast_play_and_wait(chan, "vm-goodbye");
11715
/* This is a workaround so that menuselect displays a proper description
11716
* AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
11719
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
11720
.load = load_module,
11721
.unload = unload_module,