~davewalker/ubuntu/maverick/asterisk/lp_705014

« back to all changes in this revision

Viewing changes to .pc/hack-multiple-app-voicemail/apps/app_voicemail.c

  • Committer: Bazaar Package Importer
  • Author(s): Steve Beattie
  • Date: 2010-03-02 10:00:03 UTC
  • Revision ID: james.westby@ubuntu.com-20100302100003-qaycyl89gy31bz03
Tags: 1:1.6.2.2-1ubuntu2
debian/{control,rules}: re-enable hardened options to gain PIE build
(Debian bug 542741, LP: #527538)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Asterisk -- An open source telephony toolkit.
 
3
 *
 
4
 * Copyright (C) 1999 - 2006, Digium, Inc.
 
5
 *
 
6
 * Mark Spencer <markster@digium.com>
 
7
 *
 
8
 * See http://www.asterisk.org for more information about
 
9
 * the Asterisk project. Please do not directly contact
 
10
 * any of the maintainers of this project for assistance;
 
11
 * the project provides a web site, mailing lists and IRC
 
12
 * channels for your use.
 
13
 *
 
14
 * This program is free software, distributed under the terms of
 
15
 * the GNU General Public License Version 2. See the LICENSE file
 
16
 * at the top of the source tree.
 
17
 */
 
18
 
 
19
/*! \file
 
20
 *
 
21
 * \brief Comedian Mail - Voicemail System
 
22
 *
 
23
 * \author Mark Spencer <markster@digium.com>
 
24
 *
 
25
 * \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/
 
28
 * 
 
29
 * \par See also
 
30
 * \arg \ref Config_vm
 
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
 
34
 * during compilation.
 
35
 *
 
36
 *
 
37
 *
 
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.
 
41
 */
 
42
 
 
43
/*** MAKEOPTS
 
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>
 
49
        </member>
 
50
        <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
 
51
                <depend>generic_odbc</depend>
 
52
                <depend>ltdl</depend>
 
53
                <conflict>IMAP_STORAGE</conflict>
 
54
                <conflict>FILE_STORAGE</conflict>
 
55
                <defaultenabled>no</defaultenabled>
 
56
        </member>
 
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>
 
61
                <use>openssl</use>
 
62
                <defaultenabled>no</defaultenabled>
 
63
        </member>
 
64
</category>
 
65
 ***/
 
66
 
 
67
#include "asterisk.h"
 
68
 
 
69
#ifdef IMAP_STORAGE
 
70
#include <ctype.h>
 
71
#include <signal.h>
 
72
#include <pwd.h>
 
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>
 
81
#else
 
82
#include "c-client.h"
 
83
#include "imap4r1.h"
 
84
#include "linkage.h"
 
85
#endif
 
86
#endif
 
87
 
 
88
ASTERISK_FILE_VERSION(__FILE__, "$Revision: 236670 $")
 
89
 
 
90
#include "asterisk/paths.h"     /* use ast_config_AST_SPOOL_DIR */
 
91
#include <sys/time.h>
 
92
#include <sys/stat.h>
 
93
#include <sys/mman.h>
 
94
#include <time.h>
 
95
#include <dirent.h>
 
96
 
 
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"
 
116
 
 
117
#ifdef ODBC_STORAGE
 
118
#include "asterisk/res_odbc.h"
 
119
#endif
 
120
 
 
121
#ifdef IMAP_STORAGE
 
122
#include "asterisk/threadstorage.h"
 
123
#endif
 
124
 
 
125
/*** DOCUMENTATION
 
126
        <application name="VoiceMail" language="en_US">
 
127
                <synopsis>
 
128
                        Leave a Voicemail message.
 
129
                </synopsis>
 
130
                <syntax>
 
131
                        <parameter name="mailboxs" argsep="&amp;" required="true">
 
132
                                <argument name="mailbox1" argsep="@" required="true">
 
133
                                        <argument name="mailbox" required="true" />
 
134
                                        <argument name="context" />
 
135
                                </argument>
 
136
                                <argument name="mailbox2" argsep="@" multiple="true">
 
137
                                        <argument name="mailbox" required="true" />
 
138
                                        <argument name="context" />
 
139
                                </argument>
 
140
                        </parameter>
 
141
                        <parameter name="options">
 
142
                                <optionlist>
 
143
                                        <option name="b">
 
144
                                                <para>Play the <literal>busy</literal> greeting to the calling party.</para>
 
145
                                        </option>
 
146
                                        <option name="d">
 
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>
 
150
                                        </option>
 
151
                                        <option name="g">
 
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>
 
156
                                        </option>
 
157
                                        <option name="s">
 
158
                                                <para>Skip the playback of instructions for leaving a message to the
 
159
                                                calling party.</para>
 
160
                                        </option>
 
161
                                        <option name="u">
 
162
                                                <para>Play the <literal>unavailable</literal> greeting.</para>
 
163
                                        </option>
 
164
                                        <option name="U">
 
165
                                                <para>Mark message as <literal>URGENT</literal>.</para>
 
166
                                        </option>
 
167
                                        <option name="P">
 
168
                                                <para>Mark message as <literal>PRIORITY</literal>.</para>
 
169
                                        </option>
 
170
                                </optionlist>
 
171
                        </parameter>
 
172
                </syntax>
 
173
                <description>
 
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
 
177
                        exist.</para>
 
178
                        <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
 
179
                        <enumlist>
 
180
                                <enum name="0">
 
181
                                        <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
 
182
                                </enum>
 
183
                                <enum name="*">
 
184
                                        <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
 
185
                                </enum>
 
186
                        </enumlist>
 
187
                        <para>This application will set the following channel variable upon completion:</para>
 
188
                        <variablelist>
 
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" />
 
194
                                </variable>
 
195
                        </variablelist>
 
196
                </description>
 
197
        </application>
 
198
        <application name="VoiceMailMain" language="en_US">
 
199
                <synopsis>
 
200
                        Check Voicemail messages.
 
201
                </synopsis>
 
202
                <syntax>
 
203
                        <parameter name="mailbox" required="true" argsep="@">
 
204
                                <argument name="mailbox" />
 
205
                                <argument name="context" />
 
206
                        </parameter>
 
207
                        <parameter name="options">
 
208
                                <optionlist>
 
209
                                        <option name="p">
 
210
                                                <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
 
211
                                                the mailbox that is entered by the caller.</para>
 
212
                                        </option>
 
213
                                        <option name="g">
 
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>
 
217
                                        </option>
 
218
                                        <option name="s">
 
219
                                                <para>Skip checking the passcode for the mailbox.</para>
 
220
                                        </option>
 
221
                                        <option name="a">
 
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>
 
225
                                                <enumlist>
 
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>
 
236
                                                </enumlist>
 
237
                                        </option>
 
238
                                </optionlist>
 
239
                        </parameter>
 
240
                </syntax>
 
241
                <description>
 
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>
 
247
                </description>
 
248
        </application>
 
249
        <application name="MailboxExists" language="en_US">
 
250
                <synopsis>
 
251
                        Check to see if Voicemail mailbox exists.
 
252
                </synopsis>
 
253
                <syntax>
 
254
                        <parameter name="mailbox" required="true" argsep="@">
 
255
                                <argument name="mailbox" required="true" />
 
256
                                <argument name="context" />
 
257
                        </parameter>
 
258
                        <parameter name="options">
 
259
                                <para>None options.</para>
 
260
                        </parameter>
 
261
                </syntax>
 
262
                <description>
 
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
 
265
                        will be used.</para>
 
266
                        <para>This application will set the following channel variable upon completion:</para>
 
267
                        <variablelist>
 
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" />
 
273
                                </variable>
 
274
                        </variablelist>
 
275
                </description>
 
276
        </application>
 
277
        <application name="VMAuthenticate" language="en_US">
 
278
                <synopsis>
 
279
                        Authenticate with Voicemail passwords.
 
280
                </synopsis>
 
281
                <syntax>
 
282
                        <parameter name="mailbox" required="true" argsep="@">
 
283
                                <argument name="mailbox" />
 
284
                                <argument name="context" />
 
285
                        </parameter>
 
286
                        <parameter name="options">
 
287
                                <optionlist>
 
288
                                        <option name="s">
 
289
                                                <para>Skip playing the initial prompts.</para>
 
290
                                        </option>
 
291
                                </optionlist>
 
292
                        </parameter>
 
293
                </syntax>
 
294
                <description>
 
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
 
299
                        mailbox.</para>
 
300
                </description>
 
301
        </application>
 
302
        <function name="MAILBOX_EXISTS" language="en_US">
 
303
                <synopsis>
 
304
                        Tell if a mailbox is configured.
 
305
                </synopsis>
 
306
                <syntax argsep="@">
 
307
                        <parameter name="mailbox" required="true" />
 
308
                        <parameter name="context" />
 
309
                </syntax>
 
310
                <description>
 
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>
 
313
                        context.</para>
 
314
                </description>
 
315
        </function>
 
316
 ***/
 
317
 
 
318
#ifdef IMAP_STORAGE
 
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;
 
328
 
 
329
static int expungeonhangup = 1;
 
330
static int imapgreetings = 0;
 
331
static char delimiter = '\0';
 
332
 
 
333
struct vm_state;
 
334
struct ast_vm_user;
 
335
 
 
336
AST_THREADSTORAGE(ts_vmstate);
 
337
 
 
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);
 
358
 
 
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);
 
364
struct vmstate {
 
365
        struct vm_state *vms;
 
366
        AST_LIST_ENTRY(vmstate) list;
 
367
};
 
368
 
 
369
static AST_LIST_HEAD_STATIC(vmstates, vmstate);
 
370
 
 
371
#endif
 
372
 
 
373
#define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
 
374
 
 
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
 
380
 
 
381
#define VOICEMAIL_CONFIG "voicemail.conf"
 
382
#define ASTERISK_USERNAME "asterisk"
 
383
 
 
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? */
 
393
 
 
394
/* Default mail command to mail voicemail. Change it with the
 
395
    mailcmd= command in voicemail.conf */
 
396
#define SENDMAIL "/usr/sbin/sendmail -t"
 
397
 
 
398
#define INTRO "vm-intro"
 
399
 
 
400
#define MAXMSG 100
 
401
#define MAXMSGLIMIT 9999
 
402
 
 
403
#define MINPASSWORD 0 /*!< Default minimum mailbox password length */
 
404
 
 
405
#define BASELINELEN 72
 
406
#define BASEMAXINLINE 256
 
407
#define eol "\r\n"
 
408
 
 
409
#define MAX_DATETIME_FORMAT     512
 
410
#define MAX_NUM_CID_CONTEXTS 10
 
411
 
 
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
 
432
 
 
433
 
 
434
enum {
 
435
        NEW_FOLDER,
 
436
        OLD_FOLDER,
 
437
        WORK_FOLDER,
 
438
        FAMILY_FOLDER,
 
439
        FRIENDS_FOLDER,
 
440
        GREETINGS_FOLDER
 
441
} vm_box;
 
442
 
 
443
enum {
 
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)
 
453
} vm_option_flags;
 
454
 
 
455
enum {
 
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,
 
461
} vm_option_args;
 
462
 
 
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)
 
473
});
 
474
 
 
475
static int load_config(int reload);
 
476
 
 
477
/*! \page vmlang Voicemail Language Syntaxes Supported
 
478
 
 
479
        \par Syntaxes supported, not really language codes.
 
480
        \arg \b en    - English
 
481
        \arg \b de    - German
 
482
        \arg \b es    - Spanish
 
483
        \arg \b fr    - French
 
484
        \arg \b it    - Italian
 
485
        \arg \b nl    - Dutch
 
486
        \arg \b pt    - Portuguese
 
487
        \arg \b pt_BR - Portuguese (Brazil)
 
488
        \arg \b gr    - Greek
 
489
        \arg \b no    - Norwegian
 
490
        \arg \b se    - Swedish
 
491
        \arg \b tw    - Chinese (Taiwan)
 
492
        \arg \b ua - Ukrainian
 
493
 
 
494
German requires the following additional soundfile:
 
495
\arg \b 1F      einE (feminine)
 
496
 
 
497
Spanish requires the following additional soundfile:
 
498
\arg \b 1M      un (masculine)
 
499
 
 
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'
 
503
 
 
504
NB these are plural:
 
505
\arg \b vm-INBOX        nieuwe (nl)
 
506
\arg \b vm-Old          oude (nl)
 
507
 
 
508
Polish uses:
 
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'
 
517
 
 
518
Swedish uses:
 
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'
 
524
 
 
525
Norwegian uses:
 
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'
 
530
 
 
531
Dutch also uses:
 
532
\arg \b nl-om           'at'?
 
533
 
 
534
Spanish also uses:
 
535
\arg \b vm-youhaveno
 
536
 
 
537
Italian requires the following additional soundfile:
 
538
 
 
539
For vm_intro_it:
 
540
\arg \b vm-nuovo        new
 
541
\arg \b vm-nuovi        new plural
 
542
\arg \b vm-vecchio      old
 
543
\arg \b vm-vecchi       old plural
 
544
 
 
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)
 
552
 
 
553
 
 
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.
 
557
 
 
558
*/
 
559
 
 
560
struct baseio {
 
561
        int iocp;
 
562
        int iolen;
 
563
        int linelength;
 
564
        int ateof;
 
565
        unsigned char iobuf[BASEMAXINLINE];
 
566
};
 
567
 
 
568
/*! Structure for linked list of users 
 
569
 * Use ast_vm_user_destroy() to free one of these structures. */
 
570
struct ast_vm_user {
 
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 */
 
583
        char callback[80];
 
584
        char dialout[80];
 
585
        char uniqueid[80];               /*!< Unique integer identifier */
 
586
        char exit[80];
 
587
        char attachfmt[20];              /*!< Attachment format */
 
588
        unsigned int flags;              /*!< VM_ flags */      
 
589
        int saydurationm;
 
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 */
 
593
#ifdef IMAP_STORAGE
 
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 */
 
598
#endif
 
599
        double volgain;                  /*!< Volume gain for voicemails sent via email */
 
600
        AST_LIST_ENTRY(ast_vm_user) list;
 
601
};
 
602
 
 
603
/*! Voicemail time zones */
 
604
struct vm_zone {
 
605
        AST_LIST_ENTRY(vm_zone) list;
 
606
        char name[80];
 
607
        char timezone[80];
 
608
        char msg_format[512];
 
609
};
 
610
 
 
611
#define VMSTATE_MAX_MSG_ARRAY 256
 
612
 
 
613
/*! Voicemail mailbox state */
 
614
struct vm_state {
 
615
        char curbox[80];
 
616
        char username[80];
 
617
        char context[80];
 
618
        char curdir[PATH_MAX];
 
619
        char vmbox[PATH_MAX];
 
620
        char fn[PATH_MAX];
 
621
        char intro[PATH_MAX];
 
622
        int *deleted;
 
623
        int *heard;
 
624
        int curmsg;
 
625
        int lastmsg;
 
626
        int newmessages;
 
627
        int oldmessages;
 
628
        int urgentmessages;
 
629
        int starting;
 
630
        int repeats;
 
631
#ifdef IMAP_STORAGE
 
632
        ast_mutex_t lock;
 
633
        int updated;                         /*!< decremented on each mail check until 1 -allows delay */
 
634
        long msgArray[VMSTATE_MAX_MSG_ARRAY];
 
635
        MAILSTREAM *mailstream;
 
636
        int vmArrayIndex;
 
637
        char imapuser[80];                   /*!< IMAP server login */
 
638
        int imapversion;
 
639
        int interactive;
 
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;
 
644
#endif
 
645
};
 
646
 
 
647
#ifdef ODBC_STORAGE
 
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))
 
657
#else
 
658
#ifdef IMAP_STORAGE
 
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))
 
666
#else
 
667
#define RETRIEVE(a,b,c,d)
 
668
#define DISPOSE(a,b)
 
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))
 
674
#endif
 
675
#endif
 
676
 
 
677
static char VM_SPOOL_DIR[PATH_MAX];
 
678
 
 
679
static char ext_pass_cmd[128];
 
680
static char ext_pass_check_cmd[128];
 
681
 
 
682
static int my_umask;
 
683
 
 
684
#define PWDCHANGE_INTERNAL (1 << 1)
 
685
#define PWDCHANGE_EXTERNAL (1 << 2)
 
686
static int pwdchange = PWDCHANGE_INTERNAL;
 
687
 
 
688
#ifdef ODBC_STORAGE
 
689
#define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
 
690
#else
 
691
# ifdef IMAP_STORAGE
 
692
# define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
 
693
# else
 
694
# define tdesc "Comedian Mail (Voicemail System)"
 
695
# endif
 
696
#endif
 
697
 
 
698
static char userscontext[AST_MAX_EXTENSION] = "default";
 
699
 
 
700
static char *addesc = "Comedian Mail";
 
701
 
 
702
/* Leave a message */
 
703
static char *app = "VoiceMail";
 
704
 
 
705
/* Check mail, control, etc */
 
706
static char *app2 = "VoiceMailMain";
 
707
 
 
708
static char *app3 = "MailboxExists";
 
709
static char *app4 = "VMAuthenticate";
 
710
 
 
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;
 
715
static int maxmsg;
 
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;
 
726
static int maxgreet;
 
727
static int skipms;
 
728
static int maxlogins;
 
729
static int minpassword;
 
730
 
 
731
/*! Poll mailboxes for changes since there is something external to
 
732
 *  app_voicemail that may change them. */
 
733
static unsigned int poll_mailboxes;
 
734
 
 
735
/*! Polling frequency */
 
736
static unsigned int poll_freq;
 
737
/*! By default, poll every 30 seconds */
 
738
#define DEFAULT_POLL_FREQ 30
 
739
 
 
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;
 
744
 
 
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;
 
749
 
 
750
/*!
 
751
 * \brief An MWI subscription
 
752
 *
 
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.
 
756
 */
 
757
struct mwi_sub {
 
758
        AST_RWLIST_ENTRY(mwi_sub) entry;
 
759
        int old_urgent;
 
760
        int old_new;
 
761
        int old_old;
 
762
        uint32_t uniqueid;
 
763
        char mailbox[1];
 
764
};
 
765
 
 
766
struct mwi_sub_task {
 
767
        const char *mailbox;
 
768
        const char *context;
 
769
        uint32_t uniqueid;
 
770
};
 
771
 
 
772
static struct ast_taskprocessor *mwi_subscription_tps;
 
773
 
 
774
static AST_RWLIST_HEAD_STATIC(mwi_subs, mwi_sub);
 
775
 
 
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];
 
782
 
 
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";
 
791
 
 
792
static struct ast_flags globalflags = {0};
 
793
 
 
794
static int saydurationminfo;
 
795
 
 
796
static char dialcontext[AST_MAX_CONTEXT] = "";
 
797
static char callcontext[AST_MAX_CONTEXT] = "";
 
798
static char exitcontext[AST_MAX_CONTEXT] = "";
 
799
 
 
800
static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
 
801
 
 
802
 
 
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";
 
810
 
 
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";
 
815
 
 
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);
 
830
 
 
831
#if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
 
832
static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
 
833
#endif
 
834
 
 
835
/*!
 
836
 * \brief Strips control and non 7-bit clean characters from input string.
 
837
 *
 
838
 * \note To map control and none 7-bit characters to a 7-bit clean characters
 
839
 *  please use ast_str_encode_mine().
 
840
 */
 
841
static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
 
842
{
 
843
        char *bufptr = buf;
 
844
        for (; *input; input++) {
 
845
                if (*input < 32) {
 
846
                        continue;
 
847
                }
 
848
                *bufptr++ = *input;
 
849
                if (bufptr == buf + buflen - 1) {
 
850
                        break;
 
851
                }
 
852
        }
 
853
        *bufptr = '\0';
 
854
        return buf;
 
855
}
 
856
 
 
857
 
 
858
/*!
 
859
 * \brief Sets default voicemail system options to a voicemail user.
 
860
 *
 
861
 * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
 
862
 * - all the globalflags
 
863
 * - the saydurationminfo
 
864
 * - the callcontext
 
865
 * - the dialcontext
 
866
 * - the exitcontext
 
867
 * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
 
868
 * - volume gain.
 
869
 */
 
870
static void populate_defaults(struct ast_vm_user *vmu)
 
871
{
 
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));
 
879
        if (vmmaxsecs)
 
880
                vmu->maxsecs = vmmaxsecs;
 
881
        if (maxmsg)
 
882
                vmu->maxmsg = maxmsg;
 
883
        if (maxdeletedmsg)
 
884
                vmu->maxdeletedmsg = maxdeletedmsg;
 
885
        vmu->volgain = volgain;
 
886
        vmu->emailsubject = NULL;
 
887
        vmu->emailbody = NULL;
 
888
}
 
889
 
 
890
/*!
 
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.
 
895
 * 
 
896
 * The property name must be one of the understood properties. See the source for details.
 
897
 */
 
898
static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
 
899
{
 
900
        int x;
 
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));
 
911
#ifdef IMAP_STORAGE
 
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;
 
921
#endif
 
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;
 
945
                } else {
 
946
                        ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
 
947
                }
 
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;
 
963
                } else {
 
964
                        vmu->maxsecs = atoi(value);
 
965
                }
 
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;
 
976
                }
 
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;
 
982
                else
 
983
                        vmu->maxdeletedmsg = 0;
 
984
 
 
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;
 
991
                }
 
992
        } else if (!strcasecmp(var, "volgain")) {
 
993
                sscanf(value, "%30lf", &vmu->volgain);
 
994
        } else if (!strcasecmp(var, "options")) {
 
995
                apply_options(vmu, value);
 
996
        }
 
997
}
 
998
 
 
999
static char *vm_check_password_shell(char *command, char *buf, size_t len) 
 
1000
{
 
1001
        int fds[2], pid = 0;
 
1002
 
 
1003
        memset(buf, 0, len);
 
1004
 
 
1005
        if (pipe(fds)) {
 
1006
                snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
 
1007
        } else {
 
1008
                /* good to go*/
 
1009
                pid = ast_safe_fork(0);
 
1010
 
 
1011
                if (pid < 0) {
 
1012
                        /* ok maybe not */
 
1013
                        close(fds[0]);
 
1014
                        close(fds[1]);
 
1015
                        snprintf(buf, len, "FAILURE: Fork failed");
 
1016
                } else if (pid) {
 
1017
                        /* parent */
 
1018
                        close(fds[1]);
 
1019
                        if (read(fds[0], buf, len) < 0) {
 
1020
                                ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
 
1021
                        }
 
1022
                        close(fds[0]);
 
1023
                } else {
 
1024
                        /*  child */
 
1025
                        AST_DECLARE_APP_ARGS(arg,
 
1026
                                AST_APP_ARG(v)[20];
 
1027
                        );
 
1028
                        char *mycmd = ast_strdupa(command);
 
1029
 
 
1030
                        close(fds[0]);
 
1031
                        dup2(fds[1], STDOUT_FILENO);
 
1032
                        close(fds[1]);
 
1033
                        ast_close_fds_above_n(STDOUT_FILENO);
 
1034
 
 
1035
                        AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
 
1036
 
 
1037
                        execv(arg.v[0], arg.v); 
 
1038
                        printf("FAILURE: %s", strerror(errno));
 
1039
                        _exit(0);
 
1040
                }
 
1041
        }
 
1042
        return buf;
 
1043
}
 
1044
 
 
1045
/*!
 
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
 
1049
 *
 
1050
 * \return zero on ok, 1 on not ok.
 
1051
 */
 
1052
static int check_password(struct ast_vm_user *vmu, char *password)
 
1053
{
 
1054
        /* check minimum length */
 
1055
        if (strlen(password) < minpassword)
 
1056
                return 1;
 
1057
        if (!ast_strlen_zero(ext_pass_check_cmd)) {
 
1058
                char cmd[255], buf[255];
 
1059
 
 
1060
                ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
 
1061
 
 
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);
 
1067
                                return 0;
 
1068
                        } else if (!strncasecmp(buf, "FAILURE", 7)) {
 
1069
                                ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
 
1070
                                return 0;
 
1071
                        } else {
 
1072
                                ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
 
1073
                                return 1;
 
1074
                        }
 
1075
                }
 
1076
        }
 
1077
        return 0;
 
1078
}
 
1079
 
 
1080
/*! 
 
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.
 
1084
 * 
 
1085
 * This only works if there is a realtime engine configured.
 
1086
 * This is called from the (top level) vm_change_password.
 
1087
 *
 
1088
 * \return zero on success, -1 on error.
 
1089
 */
 
1090
static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
 
1091
{
 
1092
        int res = -1;
 
1093
        if (!strcmp(vmu->password, password)) {
 
1094
                /* No change (but an update would return 0 rows updated, so we opt out here) */
 
1095
                return 0;
 
1096
        }
 
1097
 
 
1098
        if (strlen(password) > 10) {
 
1099
                ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
 
1100
        }
 
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));
 
1103
                res = 0;
 
1104
        }
 
1105
        return res;
 
1106
}
 
1107
 
 
1108
/*!
 
1109
 * \brief Destructively Parse options and apply.
 
1110
 */
 
1111
static void apply_options(struct ast_vm_user *vmu, const char *options)
 
1112
{       
 
1113
        char *stringp;
 
1114
        char *s;
 
1115
        char *var, *value;
 
1116
        stringp = ast_strdupa(options);
 
1117
        while ((s = strsep(&stringp, "|"))) {
 
1118
                value = s;
 
1119
                if ((var = strsep(&value, "=")) && value) {
 
1120
                        apply_option(vmu, var, value);
 
1121
                }
 
1122
        }       
 
1123
}
 
1124
 
 
1125
/*!
 
1126
 * \brief Loads the options specific to a voicemail user.
 
1127
 * 
 
1128
 * This is called when a vm_user structure is being set up, such as from load_options.
 
1129
 */
 
1130
static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
 
1131
{
 
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);
 
1152
#ifdef IMAP_STORAGE
 
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;
 
1162
#endif
 
1163
                } else
 
1164
                        apply_option(retval, var->name, var->value);
 
1165
        }
 
1166
}
 
1167
 
 
1168
/*!
 
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.
 
1171
 *
 
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.
 
1174
 */
 
1175
static int is_valid_dtmf(const char *key)
 
1176
{
 
1177
        int i;
 
1178
        char *local_key = ast_strdupa(key);
 
1179
 
 
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);
 
1183
                        return 0;
 
1184
                }
 
1185
                local_key++;
 
1186
        }
 
1187
        return 1;
 
1188
}
 
1189
 
 
1190
/*!
 
1191
 * \brief Finds a voicemail user from the realtime engine.
 
1192
 * \param ivm
 
1193
 * \param context
 
1194
 * \param mailbox
 
1195
 *
 
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.
 
1197
 *
 
1198
 * \return The ast_vm_user structure for the user that was found.
 
1199
 */
 
1200
static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
 
1201
{
 
1202
        struct ast_variable *var;
 
1203
        struct ast_vm_user *retval;
 
1204
 
 
1205
        if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
 
1206
                if (!ivm)
 
1207
                        ast_set_flag(retval, VM_ALLOCED);       
 
1208
                else
 
1209
                        memset(retval, 0, sizeof(*retval));
 
1210
                if (mailbox) 
 
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);
 
1215
                else
 
1216
                        var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
 
1217
                if (var) {
 
1218
                        apply_options_full(retval, var);
 
1219
                        ast_variables_destroy(var);
 
1220
                } else { 
 
1221
                        if (!ivm) 
 
1222
                                ast_free(retval);
 
1223
                        retval = NULL;
 
1224
                }       
 
1225
        } 
 
1226
        return retval;
 
1227
}
 
1228
 
 
1229
/*!
 
1230
 * \brief Finds a voicemail user from the users file or the realtime engine.
 
1231
 * \param ivm
 
1232
 * \param context
 
1233
 * \param mailbox
 
1234
 * 
 
1235
 * \return The ast_vm_user structure for the user that was found.
 
1236
 */
 
1237
static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
 
1238
{
 
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);
 
1242
 
 
1243
        if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
 
1244
                context = "default";
 
1245
 
 
1246
        AST_LIST_TRAVERSE(&users, cur, list) {
 
1247
#ifdef IMAP_STORAGE
 
1248
                if (cur->imapversion != imapversion) {
 
1249
                        continue;
 
1250
                }
 
1251
#endif
 
1252
                if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
 
1253
                        break;
 
1254
                if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
 
1255
                        break;
 
1256
        }
 
1257
        if (cur) {
 
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;
 
1263
                }
 
1264
        } else
 
1265
                vmu = find_user_realtime(ivm, context, mailbox);
 
1266
        AST_LIST_UNLOCK(&users);
 
1267
        return vmu;
 
1268
}
 
1269
 
 
1270
/*!
 
1271
 * \brief Resets a user password to a specified password.
 
1272
 * \param context
 
1273
 * \param mailbox
 
1274
 * \param newpass
 
1275
 *
 
1276
 * This does the actual change password work, called by the vm_change_password() function.
 
1277
 *
 
1278
 * \return zero on success, -1 on error.
 
1279
 */
 
1280
static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
 
1281
{
 
1282
        /* This function could be made to generate one from a database, too */
 
1283
        struct ast_vm_user *cur;
 
1284
        int res = -1;
 
1285
        AST_LIST_LOCK(&users);
 
1286
        AST_LIST_TRAVERSE(&users, cur, list) {
 
1287
                if ((!context || !strcasecmp(context, cur->context)) &&
 
1288
                        (!strcasecmp(mailbox, cur->mailbox)))
 
1289
                                break;
 
1290
        }
 
1291
        if (cur) {
 
1292
                ast_copy_string(cur->password, newpass, sizeof(cur->password));
 
1293
                res = 0;
 
1294
        }
 
1295
        AST_LIST_UNLOCK(&users);
 
1296
        return res;
 
1297
}
 
1298
 
 
1299
/*! 
 
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.
 
1305
 */
 
1306
static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
 
1307
{
 
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))
 
1315
                return;
 
1316
 
 
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");
 
1323
                                        break;
 
1324
                                }
 
1325
                                value = strstr(tmp,",");
 
1326
                                if (!value) {
 
1327
                                        ast_log(AST_LOG_WARNING, "variable has bad format.\n");
 
1328
                                        break;
 
1329
                                }
 
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");
 
1334
                                        break;
 
1335
                                }
 
1336
                                ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
 
1337
                        }
 
1338
                }
 
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");
 
1343
        }
 
1344
        category = NULL;
 
1345
        var = NULL;
 
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, "");
 
1356
                                } 
 
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");
 
1361
                                        break;
 
1362
                                }
 
1363
                                if (!var)               
 
1364
                                        ast_variable_update(cat, "vmsecret", new, NULL, 0);
 
1365
                                else
 
1366
                                        ast_variable_append(cat, var);
 
1367
                        }
 
1368
                }
 
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");
 
1373
        }
 
1374
}
 
1375
 
 
1376
static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
 
1377
{
 
1378
        char buf[255];
 
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);
 
1384
        }
 
1385
}
 
1386
 
 
1387
/*! 
 
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.
 
1391
 * 
 
1392
 * The path is constructed as 
 
1393
 *      VM_SPOOL_DIRcontext/ext/folder
 
1394
 *
 
1395
 * \return zero on success, -1 on error.
 
1396
 */
 
1397
static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
 
1398
{
 
1399
        return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
 
1400
}
 
1401
 
 
1402
/*! 
 
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.
 
1406
 * 
 
1407
 * The path is constructed as 
 
1408
 *      VM_SPOOL_DIRcontext/ext/folder
 
1409
 *
 
1410
 * \return zero on success, -1 on error.
 
1411
 */
 
1412
static int make_file(char *dest, const int len, const char *dir, const int num)
 
1413
{
 
1414
        return snprintf(dest, len, "%s/msg%04d", dir, num);
 
1415
}
 
1416
 
 
1417
/* same as mkstemp, but return a FILE * */
 
1418
static FILE *vm_mkftemp(char *template)
 
1419
{
 
1420
        FILE *p = NULL;
 
1421
        int pfd = mkstemp(template);
 
1422
        chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
 
1423
        if (pfd > -1) {
 
1424
                p = fdopen(pfd, "w+");
 
1425
                if (!p) {
 
1426
                        close(pfd);
 
1427
                        pfd = -1;
 
1428
                }
 
1429
        }
 
1430
        return p;
 
1431
}
 
1432
 
 
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.
 
1440
 */
 
1441
static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
 
1442
{
 
1443
        mode_t  mode = VOICEMAIL_DIR_MODE;
 
1444
        int res;
 
1445
 
 
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));
 
1449
                return -1;
 
1450
        }
 
1451
        return 0;
 
1452
}
 
1453
 
 
1454
static const char *mbox(int id)
 
1455
{
 
1456
        static const char *msgs[] = {
 
1457
#ifdef IMAP_STORAGE
 
1458
                imapfolder,
 
1459
#else
 
1460
                "INBOX",
 
1461
#endif
 
1462
                "Old",
 
1463
                "Work",
 
1464
                "Family",
 
1465
                "Friends",
 
1466
                "Cust1",
 
1467
                "Cust2",
 
1468
                "Cust3",
 
1469
                "Cust4",
 
1470
                "Cust5",
 
1471
                "Deleted",
 
1472
                "Urgent"
 
1473
        };
 
1474
        return (id >= 0 && id < ARRAY_LEN(msgs)) ? msgs[id] : "Unknown";
 
1475
}
 
1476
 
 
1477
static void free_user(struct ast_vm_user *vmu)
 
1478
{
 
1479
        if (ast_test_flag(vmu, VM_ALLOCED)) {
 
1480
                if (vmu->emailbody != NULL) {
 
1481
                        ast_free(vmu->emailbody);
 
1482
                        vmu->emailbody = NULL;
 
1483
                }
 
1484
                if (vmu->emailsubject != NULL) {
 
1485
                        ast_free(vmu->emailsubject);
 
1486
                        vmu->emailsubject = NULL;
 
1487
                }
 
1488
                ast_free(vmu);
 
1489
        }
 
1490
}
 
1491
 
 
1492
/* All IMAP-specific functions should go in this block. This
 
1493
 * keeps them from being spread out all over the code */
 
1494
#ifdef IMAP_STORAGE
 
1495
static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
 
1496
{
 
1497
        char arg[10];
 
1498
        struct vm_state *vms;
 
1499
        unsigned long messageNum;
 
1500
 
 
1501
        /* If greetings aren't stored in IMAP, just delete the file */
 
1502
        if (msgnum < 0 && !imapgreetings) {
 
1503
                ast_filedelete(file, NULL);
 
1504
                return;
 
1505
        }
 
1506
 
 
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);
 
1509
                return;
 
1510
        }
 
1511
 
 
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);
 
1517
                return;
 
1518
        }
 
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);
 
1527
}
 
1528
 
 
1529
static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
 
1530
{
 
1531
        struct vm_state *vms_p;
 
1532
        char *file, *filename;
 
1533
        char *attachment;
 
1534
        int ret = 0, i;
 
1535
        BODY *body;
 
1536
 
 
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) {
 
1541
                return 0;
 
1542
        } else {
 
1543
                file = strrchr(ast_strdupa(dir), '/');
 
1544
                if (file)
 
1545
                        *file++ = '\0';
 
1546
                else {
 
1547
                        ast_debug (1, "Failed to procure file name from directory passed.\n");
 
1548
                        return -1;
 
1549
                }
 
1550
        }
 
1551
 
 
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.
 
1558
                */
 
1559
                if (!(vms_p = create_vm_state_from_user(vmu))) {
 
1560
                        ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
 
1561
                        return -1;
 
1562
                }
 
1563
        }
 
1564
        
 
1565
        /* Greetings will never have a prepended message */
 
1566
        *vms_p->introfn = '\0';
 
1567
 
 
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);
 
1573
                return -1;
 
1574
        }
 
1575
 
 
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);
 
1582
                } else {
 
1583
                        ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
 
1584
                        ast_mutex_unlock(&vms_p->lock);
 
1585
                        return -1;
 
1586
                }
 
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);
 
1593
                        return 0;
 
1594
                }
 
1595
        }
 
1596
        ast_mutex_unlock(&vms_p->lock);
 
1597
 
 
1598
        return -1;
 
1599
}
 
1600
 
 
1601
static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
 
1602
{
 
1603
        BODY *body;
 
1604
        char *header_content;
 
1605
        char *attachedfilefmt;
 
1606
        char buf[80];
 
1607
        struct vm_state *vms;
 
1608
        char text_file[PATH_MAX];
 
1609
        FILE *text_file_ptr;
 
1610
        int res = 0;
 
1611
        struct ast_vm_user *vmu;
 
1612
 
 
1613
        if (!(vmu = find_user(NULL, context, mailbox))) {
 
1614
                ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
 
1615
                return -1;
 
1616
        }
 
1617
        
 
1618
        if (msgnum < 0) {
 
1619
                if (imapgreetings) {
 
1620
                        res = imap_retrieve_greeting(dir, msgnum, vmu);
 
1621
                        goto exit;
 
1622
                } else {
 
1623
                        res = 0;
 
1624
                        goto exit;
 
1625
                }
 
1626
        }
 
1627
 
 
1628
        /* Before anything can happen, we need a vm_state so that we can
 
1629
         * actually access the imap server through the vms->mailstream
 
1630
         */
 
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.
 
1638
                 */
 
1639
                ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
 
1640
                res = -1;
 
1641
                goto exit;
 
1642
        }
 
1643
        
 
1644
        make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
 
1645
        snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
 
1646
 
 
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) {
 
1649
                res = 0;
 
1650
                goto exit;
 
1651
        }
 
1652
 
 
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");
 
1657
                res = -1;
 
1658
                goto exit;
 
1659
        }
 
1660
 
 
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]);
 
1668
                res = -1;
 
1669
                goto exit;
 
1670
        }
 
1671
 
 
1672
        ast_mutex_lock(&vms->lock);
 
1673
        mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
 
1674
        ast_mutex_unlock(&vms->lock);
 
1675
 
 
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);
 
1679
        } else {
 
1680
                ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
 
1681
                res = -1;
 
1682
                goto exit;
 
1683
        }
 
1684
        
 
1685
        /* Find the format of the attached file */
 
1686
 
 
1687
        strsep(&attachedfilefmt, ".");
 
1688
        if (!attachedfilefmt) {
 
1689
                ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
 
1690
                res = -1;
 
1691
                goto exit;
 
1692
        }
 
1693
        
 
1694
        save_body(body, vms, "2", attachedfilefmt, 0);
 
1695
        if (save_body(body, vms, "3", attachedfilefmt, 1)) {
 
1696
                *vms->introfn = '\0';
 
1697
        }
 
1698
 
 
1699
        /* Get info from headers!! */
 
1700
        snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
 
1701
 
 
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));
 
1704
        }
 
1705
 
 
1706
        fprintf(text_file_ptr, "%s\n", "[message]");
 
1707
 
 
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);
 
1723
 
 
1724
exit:
 
1725
        free_user(vmu);
 
1726
        return res;
 
1727
}
 
1728
 
 
1729
static int folder_int(const char *folder)
 
1730
{
 
1731
        /*assume a NULL folder means INBOX*/
 
1732
        if (!folder)
 
1733
                return 0;
 
1734
#ifdef IMAP_STORAGE
 
1735
        if (!strcasecmp(folder, imapfolder))
 
1736
#else
 
1737
        if (!strcasecmp(folder, "INBOX"))
 
1738
#endif
 
1739
                return 0;
 
1740
        else if (!strcasecmp(folder, "Old"))
 
1741
                return 1;
 
1742
        else if (!strcasecmp(folder, "Work"))
 
1743
                return 2;
 
1744
        else if (!strcasecmp(folder, "Family"))
 
1745
                return 3;
 
1746
        else if (!strcasecmp(folder, "Friends"))
 
1747
                return 4;
 
1748
        else if (!strcasecmp(folder, "Cust1"))
 
1749
                return 5;
 
1750
        else if (!strcasecmp(folder, "Cust2"))
 
1751
                return 6;
 
1752
        else if (!strcasecmp(folder, "Cust3"))
 
1753
                return 7;
 
1754
        else if (!strcasecmp(folder, "Cust4"))
 
1755
                return 8;
 
1756
        else if (!strcasecmp(folder, "Cust5"))
 
1757
                return 9;
 
1758
        else /*assume they meant INBOX if folder is not found otherwise*/
 
1759
                return 0;
 
1760
}
 
1761
 
 
1762
/*!
 
1763
 * \brief Gets the number of messages that exist in a mailbox folder.
 
1764
 * \param context
 
1765
 * \param mailbox
 
1766
 * \param folder
 
1767
 * 
 
1768
 * This method is used when IMAP backend is used.
 
1769
 * \return The number of messages in this mailbox folder (zero or more).
 
1770
 */
 
1771
static int messagecount(const char *context, const char *mailbox, const char *folder)
 
1772
{
 
1773
        SEARCHPGM *pgm;
 
1774
        SEARCHHEADER *hdr;
 
1775
 
 
1776
        struct ast_vm_user *vmu, vmus;
 
1777
        struct vm_state *vms_p;
 
1778
        int ret = 0;
 
1779
        int fold = folder_int(folder);
 
1780
        int urgent = 0;
 
1781
        
 
1782
        /* If URGENT, then look at INBOX */
 
1783
        if (fold == 11) {
 
1784
                fold = NEW_FOLDER;
 
1785
                urgent = 1;
 
1786
        }
 
1787
 
 
1788
        if (ast_strlen_zero(mailbox))
 
1789
                return 0;
 
1790
 
 
1791
        /* We have to get the user before we can open the stream! */
 
1792
        vmu = find_user(&vmus, context, mailbox);
 
1793
        if (!vmu) {
 
1794
                ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
 
1795
                return -1;
 
1796
        } else {
 
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);
 
1800
                        return -1;
 
1801
                }
 
1802
        }
 
1803
        
 
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);
 
1807
                free_user(vmu);
 
1808
                return -1;
 
1809
        }
 
1810
 
 
1811
        /* check if someone is accessing this box right now... */
 
1812
        vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
 
1813
        if (!vms_p) {
 
1814
                vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
 
1815
        }
 
1816
        if (vms_p) {
 
1817
                ast_debug(3, "Returning before search - user is logged in\n");
 
1818
                if (fold == 0) { /* INBOX */
 
1819
                        return vms_p->newmessages;
 
1820
                }
 
1821
                if (fold == 1) { /* Old messages */
 
1822
                        return vms_p->oldmessages;
 
1823
                }
 
1824
                if (fold == 11) {/*Urgent messages*/
 
1825
                        return vms_p->urgentmessages;
 
1826
                }
 
1827
        }
 
1828
 
 
1829
        /* add one if not there... */
 
1830
        vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
 
1831
        if (!vms_p) {
 
1832
                vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
 
1833
        }
 
1834
 
 
1835
        if (!vms_p) {
 
1836
                vms_p = create_vm_state_from_user(vmu);
 
1837
        }
 
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");
 
1841
                return -1;
 
1842
        }
 
1843
        if (ret == 0) {
 
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"));
 
1848
                pgm->header = hdr;
 
1849
                if (fold != 1) {
 
1850
                        pgm->unseen = 1;
 
1851
                        pgm->seen = 0;
 
1852
                }
 
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"
 
1855
                 */
 
1856
                else {
 
1857
                        pgm->unseen = 0;
 
1858
                        pgm->seen = 1;
 
1859
                }
 
1860
                /* look for urgent messages */
 
1861
                if (urgent) {
 
1862
                        pgm->flagged = 1;
 
1863
                        pgm->unflagged = 0;
 
1864
                }
 
1865
                pgm->undeleted = 1;
 
1866
                pgm->deleted = 0;
 
1867
 
 
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;
 
1872
                if (fold == 1)
 
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);
 
1879
                vms_p->updated = 0;
 
1880
                return vms_p->vmArrayIndex;
 
1881
        } else {
 
1882
                ast_mutex_lock(&vms_p->lock);
 
1883
                mail_ping(vms_p->mailstream);
 
1884
                ast_mutex_unlock(&vms_p->lock);
 
1885
        }
 
1886
        return 0;
 
1887
}
 
1888
 
 
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)
 
1890
{
 
1891
        char *myserveremail = serveremail;
 
1892
        char fn[PATH_MAX];
 
1893
        char introfn[PATH_MAX];
 
1894
        char mailbox[256];
 
1895
        char *stringp;
 
1896
        FILE *p=NULL;
 
1897
        char tmp[80] = "/tmp/astmail-XXXXXX";
 
1898
        long len;
 
1899
        void *buf;
 
1900
        int tempcopy = 0;
 
1901
        STRING str;
 
1902
        int ret; /* for better error checking */
 
1903
        char *imap_flags = NIL;
 
1904
 
 
1905
    /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
 
1906
    if (msgnum < 0 && !imapgreetings) {
 
1907
        return 0;
 
1908
    }
 
1909
 
 
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";
 
1914
        }
 
1915
        
 
1916
        /* Attach only the first format */
 
1917
        fmt = ast_strdupa(fmt);
 
1918
        stringp = fmt;
 
1919
        strsep(&stringp, "|");
 
1920
 
 
1921
        if (!ast_strlen_zero(vmu->serveremail))
 
1922
                myserveremail = vmu->serveremail;
 
1923
 
 
1924
        if (msgnum > -1)
 
1925
                make_file(fn, sizeof(fn), dir, msgnum);
 
1926
        else
 
1927
                ast_copy_string (fn, dir, sizeof(fn));
 
1928
 
 
1929
        snprintf(introfn, sizeof(introfn), "%sintro", fn);
 
1930
        if (ast_fileexists(introfn, NULL, NULL) <= 0) {
 
1931
                *introfn = '\0';
 
1932
        }
 
1933
        
 
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
 
1938
                 * is 1.
 
1939
                 */
 
1940
                ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
 
1941
                tempcopy = 1;
 
1942
        }
 
1943
 
 
1944
        if (!strcmp(fmt, "wav49"))
 
1945
                fmt = "WAV";
 
1946
        ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
 
1947
 
 
1948
        /* Make a temporary file instead of piping directly to sendmail, in case the mail
 
1949
           command hangs. */
 
1950
        if (!(p = vm_mkftemp(tmp))) {
 
1951
                ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
 
1952
                if (tempcopy)
 
1953
                        *(vmu->email) = '\0';
 
1954
                return -1;
 
1955
        }
 
1956
 
 
1957
        if (msgnum < 0 && imapgreetings) {
 
1958
                if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
 
1959
                        ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
 
1960
                        return -1;
 
1961
                }
 
1962
                imap_delete_old_greeting(fn, vms);
 
1963
        }
 
1964
 
 
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 */
 
1967
        len = ftell(p);
 
1968
        rewind(p);
 
1969
        if (!(buf = ast_malloc(len + 1))) {
 
1970
                ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
 
1971
                fclose(p);
 
1972
                if (tempcopy)
 
1973
                        *(vmu->email) = '\0';
 
1974
                return -1;
 
1975
        }
 
1976
        if (fread(buf, len, 1, p) < len) {
 
1977
                if (ferror(p)) {
 
1978
                        ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
 
1979
                        return -1;
 
1980
                }
 
1981
        }
 
1982
        ((char *)buf)[len] = '\0';
 
1983
        INIT(&str, mail_string, buf, len);
 
1984
        ret = init_mailstream(vms, NEW_FOLDER);
 
1985
        if (ret == 0) {
 
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);
 
1991
                fclose(p);
 
1992
                unlink(tmp);
 
1993
                ast_free(buf);
 
1994
        } else {
 
1995
                ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n",mailbox);
 
1996
                fclose(p);
 
1997
                unlink(tmp);
 
1998
                ast_free(buf);
 
1999
                return -1;
 
2000
        }
 
2001
        ast_debug(3, "%s stored\n", fn);
 
2002
        
 
2003
        if (tempcopy)
 
2004
                *(vmu->email) = '\0';
 
2005
        
 
2006
        return 0;
 
2007
 
 
2008
}
 
2009
 
 
2010
/*!
 
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.
 
2016
 * 
 
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.
 
2019
 *
 
2020
 * \return zero on success, -1 on error.
 
2021
 */
 
2022
 
 
2023
static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
 
2024
{
 
2025
        char tmp[PATH_MAX] = "";
 
2026
        char *mailboxnc;
 
2027
        char *context;
 
2028
        char *mb;
 
2029
        char *cur;
 
2030
        if (newmsgs)
 
2031
                *newmsgs = 0;
 
2032
        if (oldmsgs)
 
2033
                *oldmsgs = 0;
 
2034
        if (urgentmsgs)
 
2035
                *urgentmsgs = 0;
 
2036
 
 
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))
 
2040
                return 0;
 
2041
        
 
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));
 
2047
                mb = 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))
 
2051
                                        return -1;
 
2052
                                else {
 
2053
                                        if (newmsgs)
 
2054
                                                *newmsgs += tmpnew; 
 
2055
                                        if (oldmsgs)
 
2056
                                                *oldmsgs += tmpold;
 
2057
                                        if (urgentmsgs)
 
2058
                                                *urgentmsgs += tmpurgent;
 
2059
                                }
 
2060
                        }
 
2061
                }
 
2062
                return 0;
 
2063
        }
 
2064
        if (context) {
 
2065
                *context = '\0';
 
2066
                mailboxnc = tmp;
 
2067
                context++;
 
2068
        } else {
 
2069
                context = "default";
 
2070
                mailboxnc = (char *)mailbox_context;
 
2071
        }
 
2072
        if (newmsgs) {
 
2073
                if ((*newmsgs = messagecount(context, mailboxnc, imapfolder)) < 0)
 
2074
                        return -1;
 
2075
        }
 
2076
        if (oldmsgs) {
 
2077
                if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
 
2078
                        return -1;
 
2079
        }
 
2080
        if (urgentmsgs) {
 
2081
                if((*urgentmsgs = messagecount(context, mailboxnc, "Urgent")) < 0)
 
2082
                        return -1;
 
2083
        }
 
2084
        return 0;
 
2085
}
 
2086
 
 
2087
static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
 
2088
{
 
2089
        return inboxcount2(mailbox_context, NULL, newmsgs, oldmsgs);
 
2090
}
 
2091
 
 
2092
/** 
 
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
 
2096
 *
 
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.
 
2100
 */
 
2101
 
 
2102
static int has_voicemail(const char *mailbox, const char *folder)
 
2103
{
 
2104
        char tmp[256], *tmp2, *box, *context;
 
2105
        ast_copy_string(tmp, mailbox, sizeof(tmp));
 
2106
        tmp2 = tmp;
 
2107
        if (strchr(tmp2, ',')) {
 
2108
                while ((box = strsep(&tmp2, ","))) {
 
2109
                        if (!ast_strlen_zero(box)) {
 
2110
                                if (has_voicemail(box, folder))
 
2111
                                        return 1;
 
2112
                        }
 
2113
                }
 
2114
        }
 
2115
        if ((context= strchr(tmp, '@')))
 
2116
                *context++ = '\0';
 
2117
        else
 
2118
                context = "default";
 
2119
        return messagecount(context, tmp, folder) ? 1 : 0;
 
2120
}
 
2121
 
 
2122
/*!
 
2123
 * \brief Copies a message from one mailbox to another.
 
2124
 * \param chan
 
2125
 * \param vmu
 
2126
 * \param imbox
 
2127
 * \param msgnum
 
2128
 * \param duration
 
2129
 * \param recip
 
2130
 * \param fmt
 
2131
 * \param dir
 
2132
 *
 
2133
 * This works with IMAP storage based mailboxes.
 
2134
 *
 
2135
 * \return zero on success, -1 on error.
 
2136
 */
 
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)
 
2138
{
 
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);
 
2143
                return -1;
 
2144
        }
 
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");
 
2147
                return -1;
 
2148
        }
 
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");
 
2151
                return -1;
 
2152
        }
 
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);
 
2157
                return 0;
 
2158
        }
 
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);
 
2161
        return -1;
 
2162
}
 
2163
 
 
2164
static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
 
2165
{
 
2166
        char tmp[256], *t = tmp;
 
2167
        size_t left = sizeof(tmp);
 
2168
        
 
2169
        if (box == OLD_FOLDER) {
 
2170
                ast_copy_string(vms->curbox, mbox(NEW_FOLDER), sizeof(vms->curbox));
 
2171
        } else {
 
2172
                ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
 
2173
        }
 
2174
 
 
2175
        if (box == NEW_FOLDER) {
 
2176
                ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
 
2177
        } else {
 
2178
                snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(box));
 
2179
        }
 
2180
 
 
2181
        /* Build up server information */
 
2182
        ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
 
2183
 
 
2184
        /* Add authentication user if present */
 
2185
        if (!ast_strlen_zero(authuser))
 
2186
                ast_build_string(&t, &left, "/authuser=%s", authuser);
 
2187
 
 
2188
        /* Add flags if present */
 
2189
        if (!ast_strlen_zero(imapflags))
 
2190
                ast_build_string(&t, &left, "/%s", imapflags);
 
2191
 
 
2192
        /* End with username */
 
2193
#if 1
 
2194
        ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
 
2195
#else
 
2196
        ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
 
2197
#endif
 
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));
 
2206
                } else {
 
2207
                        snprintf(spec, len, "%s%s", tmp, mbox(box));
 
2208
                }
 
2209
        }
 
2210
}
 
2211
 
 
2212
static int init_mailstream(struct vm_state *vms, int box)
 
2213
{
 
2214
        MAILSTREAM *stream = NIL;
 
2215
        long debug;
 
2216
        char tmp[256];
 
2217
        
 
2218
        if (!vms) {
 
2219
                ast_log (LOG_ERROR,"vm_state is NULL!\n");
 
2220
                return -1;
 
2221
        }
 
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) {
 
2225
                if (option_debug)
 
2226
                        ast_log (LOG_DEBUG,"mailstream not set.\n");
 
2227
        } else {
 
2228
                stream = vms->mailstream;
 
2229
        }
 
2230
        /* debug = T;  user wants protocol telemetry? */
 
2231
        debug = NIL;  /* NO protocol telemetry? */
 
2232
 
 
2233
        if (delimiter == '\0') {                /* did not probe the server yet */
 
2234
                char *cp;
 
2235
#ifdef USE_SYSTEM_IMAP
 
2236
#include <imap/linkage.c>
 
2237
#elif defined(USE_SYSTEM_CCLIENT)
 
2238
#include <c-client/linkage.c>
 
2239
#else
 
2240
#include "linkage.c"
 
2241
#endif
 
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);
 
2249
                        return -1;
 
2250
                }
 
2251
                get_mailbox_delimiter(stream);
 
2252
                /* update delimiter in imapfolder */
 
2253
                for (cp = imapfolder; *cp; cp++)
 
2254
                        if (*cp == '/')
 
2255
                                *cp = delimiter;
 
2256
        }
 
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) {
 
2265
                return -1;
 
2266
        } else {
 
2267
                return 0;
 
2268
        }
 
2269
}
 
2270
 
 
2271
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
 
2272
{
 
2273
        SEARCHPGM *pgm;
 
2274
        SEARCHHEADER *hdr;
 
2275
        int ret, urgent = 0;
 
2276
 
 
2277
        /* If Urgent, then look at INBOX */
 
2278
        if (box == 11) {
 
2279
                box = NEW_FOLDER;
 
2280
                urgent = 1;
 
2281
        }
 
2282
 
 
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;
 
2286
 
 
2287
        if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
 
2288
                ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
 
2289
                return -1;
 
2290
        }
 
2291
        
 
2292
        create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
 
2293
        
 
2294
        /* Check Quota */
 
2295
        if  (box == 0)  {
 
2296
                ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
 
2297
                check_quota(vms,(char *)mbox(box));
 
2298
        }
 
2299
 
 
2300
        ast_mutex_lock(&vms->lock);
 
2301
        pgm = mail_newsearchpgm();
 
2302
 
 
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);
 
2306
        pgm->header = hdr;
 
2307
        pgm->deleted = 0;
 
2308
        pgm->undeleted = 1;
 
2309
 
 
2310
        /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
 
2311
        if (box == NEW_FOLDER && urgent == 1) {
 
2312
                pgm->unseen = 1;
 
2313
                pgm->seen = 0;
 
2314
                pgm->flagged = 1;
 
2315
                pgm->unflagged = 0;
 
2316
        } else if (box == NEW_FOLDER && urgent == 0) {
 
2317
                pgm->unseen = 1;
 
2318
                pgm->seen = 0;
 
2319
                pgm->flagged = 0;
 
2320
                pgm->unflagged = 1;
 
2321
        } else if (box == OLD_FOLDER) {
 
2322
                pgm->seen = 1;
 
2323
                pgm->unseen = 0;
 
2324
        }
 
2325
 
 
2326
        ast_debug(3,"Before mail_search_full, user is %s\n",vmu->imapuser);
 
2327
 
 
2328
        vms->vmArrayIndex = 0;
 
2329
        mail_search_full (vms->mailstream, NULL, pgm, NIL);
 
2330
        vms->lastmsg = vms->vmArrayIndex - 1;
 
2331
        mail_free_searchpgm(&pgm);
 
2332
 
 
2333
        ast_mutex_unlock(&vms->lock);
 
2334
        return 0;
 
2335
}
 
2336
 
 
2337
static void write_file(char *filename, char *buffer, unsigned long len)
 
2338
{
 
2339
        FILE *output;
 
2340
 
 
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));
 
2345
                }
 
2346
        }
 
2347
        fclose (output);
 
2348
}
 
2349
 
 
2350
static void update_messages_by_imapuser(const char *user, unsigned long number)
 
2351
{
 
2352
        struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
 
2353
 
 
2354
        if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
 
2355
                return;
 
2356
        }
 
2357
 
 
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;
 
2360
}
 
2361
 
 
2362
void mm_searched(MAILSTREAM *stream, unsigned long number)
 
2363
{
 
2364
        char *mailbox = stream->mailbox, buf[1024] = "", *user;
 
2365
 
 
2366
        if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
 
2367
                return;
 
2368
 
 
2369
        update_messages_by_imapuser(user, number);
 
2370
}
 
2371
 
 
2372
static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
 
2373
{
 
2374
        struct ast_variable *var;
 
2375
        struct ast_vm_user *vmu;
 
2376
 
 
2377
        vmu = ast_calloc(1, sizeof *vmu);
 
2378
        if (!vmu)
 
2379
                return NULL;
 
2380
        ast_set_flag(vmu, VM_ALLOCED);
 
2381
        populate_defaults(vmu);
 
2382
 
 
2383
        var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
 
2384
        if (var) {
 
2385
                apply_options_full(vmu, var);
 
2386
                ast_variables_destroy(var);
 
2387
                return vmu;
 
2388
        } else {
 
2389
                ast_free(vmu);
 
2390
                return NULL;
 
2391
        }
 
2392
}
 
2393
 
 
2394
/* Interfaces to C-client */
 
2395
 
 
2396
void mm_exists(MAILSTREAM * stream, unsigned long number)
 
2397
{
 
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;
 
2401
        set_update(stream);
 
2402
}
 
2403
 
 
2404
 
 
2405
void mm_expunged(MAILSTREAM * stream, unsigned long number)
 
2406
{
 
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;
 
2410
        set_update(stream);
 
2411
}
 
2412
 
 
2413
 
 
2414
void mm_flags(MAILSTREAM * stream, unsigned long number)
 
2415
{
 
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;
 
2419
        set_update(stream);
 
2420
}
 
2421
 
 
2422
 
 
2423
void mm_notify(MAILSTREAM * stream, char *string, long errflg)
 
2424
{
 
2425
        ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
 
2426
        mm_log (string, errflg);
 
2427
}
 
2428
 
 
2429
 
 
2430
void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
 
2431
{
 
2432
        if (delimiter == '\0') {
 
2433
                delimiter = delim;
 
2434
        }
 
2435
 
 
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");
 
2445
}
 
2446
 
 
2447
 
 
2448
void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
 
2449
{
 
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");
 
2459
}
 
2460
 
 
2461
 
 
2462
void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
 
2463
{
 
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");
 
2476
}
 
2477
 
 
2478
 
 
2479
void mm_log(char *string, long errflg)
 
2480
{
 
2481
        switch ((short) errflg) {
 
2482
                case NIL:
 
2483
                        ast_debug(1,"IMAP Info: %s\n", string);
 
2484
                        break;
 
2485
                case PARSE:
 
2486
                case WARN:
 
2487
                        ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
 
2488
                        break;
 
2489
                case ERROR:
 
2490
                        ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
 
2491
                        break;
 
2492
        }
 
2493
}
 
2494
 
 
2495
 
 
2496
void mm_dlog(char *string)
 
2497
{
 
2498
        ast_log(AST_LOG_NOTICE, "%s\n", string);
 
2499
}
 
2500
 
 
2501
 
 
2502
void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
 
2503
{
 
2504
        struct ast_vm_user *vmu;
 
2505
 
 
2506
        ast_debug(4, "Entering callback mm_login\n");
 
2507
 
 
2508
        ast_copy_string(user, mb->user, MAILTMPLEN);
 
2509
 
 
2510
        /* We should only do this when necessary */
 
2511
        if (!ast_strlen_zero(authpassword)) {
 
2512
                ast_copy_string(pwd, authpassword, MAILTMPLEN);
 
2513
        } else {
 
2514
                AST_LIST_TRAVERSE(&users, vmu, list) {
 
2515
                        if (!strcasecmp(mb->user, vmu->imapuser)) {
 
2516
                                ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
 
2517
                                break;
 
2518
                        }
 
2519
                }
 
2520
                if (!vmu) {
 
2521
                        if ((vmu = find_user_realtime_imapuser(mb->user))) {
 
2522
                                ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
 
2523
                                free_user(vmu);
 
2524
                        }
 
2525
                }
 
2526
        }
 
2527
}
 
2528
 
 
2529
 
 
2530
void mm_critical(MAILSTREAM * stream)
 
2531
{
 
2532
}
 
2533
 
 
2534
 
 
2535
void mm_nocritical(MAILSTREAM * stream)
 
2536
{
 
2537
}
 
2538
 
 
2539
 
 
2540
long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
 
2541
{
 
2542
        kill (getpid (), SIGSTOP);
 
2543
        return NIL;
 
2544
}
 
2545
 
 
2546
 
 
2547
void mm_fatal(char *string)
 
2548
{
 
2549
        ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
 
2550
}
 
2551
 
 
2552
/* C-client callback to handle quota */
 
2553
static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
 
2554
{
 
2555
        struct vm_state *vms;
 
2556
        char *mailbox = stream->mailbox, *user;
 
2557
        char buf[1024] = "";
 
2558
        unsigned long usage = 0, limit = 0;
 
2559
        
 
2560
        while (pquota) {
 
2561
                usage = pquota->usage;
 
2562
                limit = pquota->limit;
 
2563
                pquota = pquota->next;
 
2564
        }
 
2565
        
 
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");
 
2568
                return;
 
2569
        }
 
2570
 
 
2571
        ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
 
2572
 
 
2573
        vms->quota_usage = usage;
 
2574
        vms->quota_limit = limit;
 
2575
}
 
2576
 
 
2577
static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
 
2578
{
 
2579
        char *start, *eol_pnt;
 
2580
        int taglen;
 
2581
 
 
2582
        if (ast_strlen_zero(header) || ast_strlen_zero(tag))
 
2583
                return NULL;
 
2584
 
 
2585
        taglen = strlen(tag) + 1;
 
2586
        if (taglen < 1)
 
2587
                return NULL;
 
2588
 
 
2589
        if (!(start = strstr(header, tag)))
 
2590
                return NULL;
 
2591
 
 
2592
        /* Since we can be called multiple times we should clear our buffer */
 
2593
        memset(buf, 0, len);
 
2594
 
 
2595
        ast_copy_string(buf, start+taglen, len);
 
2596
        if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
 
2597
                *eol_pnt = '\0';
 
2598
        return buf;
 
2599
}
 
2600
 
 
2601
static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
 
2602
{
 
2603
        char *start, *quote, *eol_pnt;
 
2604
 
 
2605
        if (ast_strlen_zero(mailbox))
 
2606
                return NULL;
 
2607
 
 
2608
        if (!(start = strstr(mailbox, "/user=")))
 
2609
                return NULL;
 
2610
 
 
2611
        ast_copy_string(buf, start+6, len);
 
2612
 
 
2613
        if (!(quote = strchr(buf, '\"'))) {
 
2614
                if (!(eol_pnt = strchr(buf, '/')))
 
2615
                        eol_pnt = strchr(buf,'}');
 
2616
                *eol_pnt = '\0';
 
2617
                return buf;
 
2618
        } else {
 
2619
                eol_pnt = strchr(buf+1,'\"');
 
2620
                *eol_pnt = '\0';
 
2621
                return buf+1;
 
2622
        }
 
2623
}
 
2624
 
 
2625
static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
 
2626
{
 
2627
        struct vm_state *vms_p;
 
2628
 
 
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)) {
 
2631
                return vms_p;
 
2632
        }
 
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))))
 
2636
                return NULL;
 
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);
 
2644
        vms_p->updated = 1;
 
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);
 
2649
        return vms_p;
 
2650
}
 
2651
 
 
2652
static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
 
2653
{
 
2654
        struct vmstate *vlist = NULL;
 
2655
 
 
2656
        if (interactive) {
 
2657
                struct vm_state *vms;
 
2658
                pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
 
2659
                vms = pthread_getspecific(ts_vmstate.key);
 
2660
                return vms;
 
2661
        }
 
2662
 
 
2663
        AST_LIST_LOCK(&vmstates);
 
2664
        AST_LIST_TRAVERSE(&vmstates, vlist, list) {
 
2665
                if (!vlist->vms) {
 
2666
                        ast_debug(3, "error: vms is NULL for %s\n", user);
 
2667
                        continue;
 
2668
                }
 
2669
                if (vlist->vms->imapversion != imapversion) {
 
2670
                        continue;
 
2671
                }
 
2672
                if (!vlist->vms->imapuser) {
 
2673
                        ast_debug(3, "error: imapuser is NULL for %s\n", user);
 
2674
                        continue;
 
2675
                }
 
2676
 
 
2677
                if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
 
2678
                        AST_LIST_UNLOCK(&vmstates);
 
2679
                        return vlist->vms;
 
2680
                }
 
2681
        }
 
2682
        AST_LIST_UNLOCK(&vmstates);
 
2683
 
 
2684
        ast_debug(3, "%s not found in vmstates\n", user);
 
2685
 
 
2686
        return NULL;
 
2687
}
 
2688
 
 
2689
static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
 
2690
{
 
2691
 
 
2692
        struct vmstate *vlist = NULL;
 
2693
        const char *local_context = S_OR(context, "default");
 
2694
 
 
2695
        if (interactive) {
 
2696
                struct vm_state *vms;
 
2697
                pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
 
2698
                vms = pthread_getspecific(ts_vmstate.key);
 
2699
                return vms;
 
2700
        }
 
2701
 
 
2702
        AST_LIST_LOCK(&vmstates);
 
2703
        AST_LIST_TRAVERSE(&vmstates, vlist, list) {
 
2704
                if (!vlist->vms) {
 
2705
                        ast_debug(3, "error: vms is NULL for %s\n", mailbox);
 
2706
                        continue;
 
2707
                }
 
2708
                if (vlist->vms->imapversion != imapversion) {
 
2709
                        continue;
 
2710
                }
 
2711
                if (!vlist->vms->username || !vlist->vms->context) {
 
2712
                        ast_debug(3, "error: username is NULL for %s\n", mailbox);
 
2713
                        continue;
 
2714
                }
 
2715
 
 
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);
 
2717
                
 
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);
 
2721
                        return vlist->vms;
 
2722
                }
 
2723
        }
 
2724
        AST_LIST_UNLOCK(&vmstates);
 
2725
 
 
2726
        ast_debug(3, "%s not found in vmstates\n", mailbox);
 
2727
 
 
2728
        return NULL;
 
2729
}
 
2730
 
 
2731
static void vmstate_insert(struct vm_state *vms) 
 
2732
{
 
2733
        struct vmstate *v;
 
2734
        struct vm_state *altvms;
 
2735
 
 
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);
 
2741
                if (altvms) {   
 
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;
 
2753
#else
 
2754
                        vms->mailstream = NIL;
 
2755
#endif
 
2756
                }
 
2757
                return;
 
2758
        }
 
2759
 
 
2760
        if (!(v = ast_calloc(1, sizeof(*v))))
 
2761
                return;
 
2762
        
 
2763
        v->vms = vms;
 
2764
 
 
2765
        ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
 
2766
 
 
2767
        AST_LIST_LOCK(&vmstates);
 
2768
        AST_LIST_INSERT_TAIL(&vmstates, v, list);
 
2769
        AST_LIST_UNLOCK(&vmstates);
 
2770
}
 
2771
 
 
2772
static void vmstate_delete(struct vm_state *vms) 
 
2773
{
 
2774
        struct vmstate *vc = NULL;
 
2775
        struct vm_state *altvms = NULL;
 
2776
 
 
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);
 
2785
 
 
2786
                /* Interactive states are not stored within the persistent list */
 
2787
                return;
 
2788
        }
 
2789
        
 
2790
        ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
 
2791
        
 
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);
 
2796
                        break;
 
2797
                }
 
2798
        }
 
2799
        AST_LIST_TRAVERSE_SAFE_END
 
2800
        AST_LIST_UNLOCK(&vmstates);
 
2801
        
 
2802
        if (vc) {
 
2803
                ast_mutex_destroy(&vc->vms->lock);
 
2804
                ast_free(vc);
 
2805
        }
 
2806
        else
 
2807
                ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
 
2808
}
 
2809
 
 
2810
static void set_update(MAILSTREAM * stream) 
 
2811
{
 
2812
        struct vm_state *vms;
 
2813
        char *mailbox = stream->mailbox, *user;
 
2814
        char buf[1024] = "";
 
2815
 
 
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);
 
2819
                return;
 
2820
        }
 
2821
 
 
2822
        ast_debug(3, "User %s mailbox set for update.\n", user);
 
2823
 
 
2824
        vms->updated = 1; /* Set updated flag since mailbox changed */
 
2825
}
 
2826
 
 
2827
static void init_vm_state(struct vm_state *vms) 
 
2828
{
 
2829
        int x;
 
2830
        vms->vmArrayIndex = 0;
 
2831
        for (x = 0; x < VMSTATE_MAX_MSG_ARRAY; x++) {
 
2832
                vms->msgArray[x] = 0;
 
2833
        }
 
2834
        ast_mutex_init(&vms->lock);
 
2835
}
 
2836
 
 
2837
static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro) 
 
2838
{
 
2839
        char *body_content;
 
2840
        char *body_decoded;
 
2841
        char *fn = is_intro ? vms->introfn : vms->fn;
 
2842
        unsigned long len;
 
2843
        unsigned long newlen;
 
2844
        char filename[256];
 
2845
        
 
2846
        if (!body || body == NIL)
 
2847
                return -1;
 
2848
 
 
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 */
 
2857
                if (!newlen) {
 
2858
                        return -1;
 
2859
                }
 
2860
                write_file(filename, (char *) body_decoded, newlen);
 
2861
        } else {
 
2862
                ast_debug(5, "Body of message is NULL.\n");
 
2863
                return -1;
 
2864
        }
 
2865
        return 0;
 
2866
}
 
2867
 
 
2868
/*! 
 
2869
 * \brief Get delimiter via mm_list callback 
 
2870
 * \param stream
 
2871
 *
 
2872
 * Determines the delimiter character that is used by the underlying IMAP based mail store.
 
2873
 */
 
2874
/* MUTEX should already be held */
 
2875
static void get_mailbox_delimiter(MAILSTREAM *stream) {
 
2876
        char tmp[50];
 
2877
        snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
 
2878
        mail_list(stream, tmp, "*");
 
2879
}
 
2880
 
 
2881
/*! 
 
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.
 
2885
 *
 
2886
 * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
 
2887
 */
 
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);
 
2894
        } else {
 
2895
                ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
 
2896
        }
 
2897
        ast_mutex_unlock(&vms->lock);
 
2898
}
 
2899
 
 
2900
#endif /* IMAP_STORAGE */
 
2901
 
 
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
 
2905
*/
 
2906
static int vm_lock_path(const char *path)
 
2907
{
 
2908
        switch (ast_lock_path(path)) {
 
2909
        case AST_LOCK_TIMEOUT:
 
2910
                return -1;
 
2911
        default:
 
2912
                return 0;
 
2913
        }
 
2914
}
 
2915
 
 
2916
 
 
2917
#ifdef ODBC_STORAGE
 
2918
struct generic_prepare_struct {
 
2919
        char *sql;
 
2920
        int argc;
 
2921
        char **argv;
 
2922
};
 
2923
 
 
2924
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
 
2925
{
 
2926
        struct generic_prepare_struct *gps = data;
 
2927
        int res, i;
 
2928
        SQLHSTMT stmt;
 
2929
 
 
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");
 
2933
                return NULL;
 
2934
        }
 
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);
 
2939
                return NULL;
 
2940
        }
 
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);
 
2943
 
 
2944
        return stmt;
 
2945
}
 
2946
 
 
2947
/*!
 
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.
 
2951
 * 
 
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.
 
2954
 *
 
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.
 
2958
 * 
 
2959
 * \return 0 on success, -1 on error.
 
2960
 */
 
2961
static int retrieve_file(char *dir, int msgnum)
 
2962
{
 
2963
        int x = 0;
 
2964
        int res;
 
2965
        int fd=-1;
 
2966
        size_t fdlen = 0;
 
2967
        void *fdm = MAP_FAILED;
 
2968
        SQLSMALLINT colcount=0;
 
2969
        SQLHSTMT stmt;
 
2970
        char sql[PATH_MAX];
 
2971
        char fmt[80]="";
 
2972
        char *c;
 
2973
        char coltitle[256];
 
2974
        SQLSMALLINT collen;
 
2975
        SQLSMALLINT datatype;
 
2976
        SQLSMALLINT decimaldigits;
 
2977
        SQLSMALLINT nullable;
 
2978
        SQLULEN colsize;
 
2979
        SQLLEN colsize2;
 
2980
        FILE *f=NULL;
 
2981
        char rowdata[80];
 
2982
        char fn[PATH_MAX];
 
2983
        char full_fn[PATH_MAX];
 
2984
        char msgnums[80];
 
2985
        char *argv[] = { dir, msgnums };
 
2986
        struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
 
2987
 
 
2988
        struct odbc_obj *obj;
 
2989
        obj = ast_odbc_request_obj(odbc_database, 0);
 
2990
        if (obj) {
 
2991
                ast_copy_string(fmt, vmfmts, sizeof(fmt));
 
2992
                c = strchr(fmt, '|');
 
2993
                if (c)
 
2994
                        *c = '\0';
 
2995
                if (!strcasecmp(fmt, "wav49"))
 
2996
                        strcpy(fmt, "WAV");
 
2997
                snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
 
2998
                if (msgnum > -1)
 
2999
                        make_file(fn, sizeof(fn), dir, msgnum);
 
3000
                else
 
3001
                        ast_copy_string(fn, dir, sizeof(fn));
 
3002
 
 
3003
                /* Create the information file */
 
3004
                snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
 
3005
                
 
3006
                if (!(f = fopen(full_fn, "w+"))) {
 
3007
                        ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
 
3008
                        goto yuck;
 
3009
                }
 
3010
                
 
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);
 
3014
                if (!stmt) {
 
3015
                        ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
 
3016
                        ast_odbc_release_obj(obj);
 
3017
                        goto yuck;
 
3018
                }
 
3019
                res = SQLFetch(stmt);
 
3020
                if (res == SQL_NO_DATA) {
 
3021
                        SQLFreeHandle (SQL_HANDLE_STMT, stmt);
 
3022
                        ast_odbc_release_obj(obj);
 
3023
                        goto yuck;
 
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);
 
3028
                        goto yuck;
 
3029
                }
 
3030
                fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
 
3031
                if (fd < 0) {
 
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);
 
3035
                        goto yuck;
 
3036
                }
 
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);
 
3042
                        goto yuck;
 
3043
                }
 
3044
                if (f) 
 
3045
                        fprintf(f, "[message]\n");
 
3046
                for (x=0;x<colcount;x++) {
 
3047
                        rowdata[0] = '\0';
 
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);
 
3055
                                goto yuck;
 
3056
                        }
 
3057
                        if (!strcasecmp(coltitle, "recording")) {
 
3058
                                off_t offset;
 
3059
                                res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
 
3060
                                fdlen = colsize2;
 
3061
                                if (fd > -1) {
 
3062
                                        char tmp[1]="";
 
3063
                                        lseek(fd, fdlen - 1, SEEK_SET);
 
3064
                                        if (write(fd, tmp, 1) != 1) {
 
3065
                                                close(fd);
 
3066
                                                fd = -1;
 
3067
                                                continue;
 
3068
                                        }
 
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);
 
3075
                                                        goto yuck;
 
3076
                                                } else {
 
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);
 
3081
                                                                unlink(full_fn);
 
3082
                                                                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 
3083
                                                                ast_odbc_release_obj(obj);
 
3084
                                                                goto yuck;
 
3085
                                                        }
 
3086
                                                }
 
3087
                                        }
 
3088
                                        if (truncate(full_fn, fdlen) < 0) {
 
3089
                                                ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
 
3090
                                        }
 
3091
                                }
 
3092
                        } else {
 
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);
 
3098
                                        goto yuck;
 
3099
                                }
 
3100
                                if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
 
3101
                                        fprintf(f, "%s=%s\n", coltitle, rowdata);
 
3102
                        }
 
3103
                }
 
3104
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
 
3105
                ast_odbc_release_obj(obj);
 
3106
        } else
 
3107
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
 
3108
yuck:   
 
3109
        if (f)
 
3110
                fclose(f);
 
3111
        if (fd > -1)
 
3112
                close(fd);
 
3113
        return x - 1;
 
3114
}
 
3115
 
 
3116
/*!
 
3117
 * \brief Determines the highest message number in use for a given user and mailbox folder.
 
3118
 * \param vmu 
 
3119
 * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
 
3120
 *
 
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.
 
3123
 *
 
3124
 * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
 
3125
 */
 
3126
static int last_message_index(struct ast_vm_user *vmu, char *dir)
 
3127
{
 
3128
        int x = 0;
 
3129
        int res;
 
3130
        SQLHSTMT stmt;
 
3131
        char sql[PATH_MAX];
 
3132
        char rowdata[20];
 
3133
        char *argv[] = { dir };
 
3134
        struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
 
3135
 
 
3136
        struct odbc_obj *obj;
 
3137
        obj = ast_odbc_request_obj(odbc_database, 0);
 
3138
        if (obj) {
 
3139
                snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
 
3140
                stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
 
3141
                if (!stmt) {
 
3142
                        ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
 
3143
                        ast_odbc_release_obj(obj);
 
3144
                        goto yuck;
 
3145
                }
 
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);
 
3151
                        goto yuck;
 
3152
                }
 
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);
 
3158
                        goto yuck;
 
3159
                }
 
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);
 
3164
        } else
 
3165
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
 
3166
yuck:   
 
3167
        return x - 1;
 
3168
}
 
3169
 
 
3170
/*!
 
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.
 
3174
 *
 
3175
 * This method is used when mailboxes are stored in an ODBC back end.
 
3176
 *
 
3177
 * \return greater than zero if the message exists, zero when the message does not exist or on error.
 
3178
 */
 
3179
static int message_exists(char *dir, int msgnum)
 
3180
{
 
3181
        int x = 0;
 
3182
        int res;
 
3183
        SQLHSTMT stmt;
 
3184
        char sql[PATH_MAX];
 
3185
        char rowdata[20];
 
3186
        char msgnums[20];
 
3187
        char *argv[] = { dir, msgnums };
 
3188
        struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
 
3189
 
 
3190
        struct odbc_obj *obj;
 
3191
        obj = ast_odbc_request_obj(odbc_database, 0);
 
3192
        if (obj) {
 
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);
 
3196
                if (!stmt) {
 
3197
                        ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
 
3198
                        ast_odbc_release_obj(obj);
 
3199
                        goto yuck;
 
3200
                }
 
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);
 
3206
                        goto yuck;
 
3207
                }
 
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);
 
3213
                        goto yuck;
 
3214
                }
 
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);
 
3219
        } else
 
3220
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
 
3221
yuck:   
 
3222
        return x;
 
3223
}
 
3224
 
 
3225
/*!
 
3226
 * \brief returns the one-based count for messages.
 
3227
 * \param vmu
 
3228
 * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
 
3229
 *
 
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.
 
3234
 *
 
3235
 * \return the value greater than zero on success to indicate the one-based count of messages, less than zero on error.
 
3236
 */
 
3237
static int count_messages(struct ast_vm_user *vmu, char *dir)
 
3238
{
 
3239
        return last_message_index(vmu, dir) + 1;
 
3240
}
 
3241
 
 
3242
/*!
 
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.
 
3246
 *
 
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.
 
3249
 * 
 
3250
 * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
 
3251
 */
 
3252
static void delete_file(char *sdir, int smsg)
 
3253
{
 
3254
        SQLHSTMT stmt;
 
3255
        char sql[PATH_MAX];
 
3256
        char msgnums[20];
 
3257
        char *argv[] = { sdir, msgnums };
 
3258
        struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
 
3259
 
 
3260
        struct odbc_obj *obj;
 
3261
        obj = ast_odbc_request_obj(odbc_database, 0);
 
3262
        if (obj) {
 
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);
 
3266
                if (!stmt)
 
3267
                        ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
 
3268
                else
 
3269
                        SQLFreeHandle (SQL_HANDLE_STMT, stmt);
 
3270
                ast_odbc_release_obj(obj);
 
3271
        } else
 
3272
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
 
3273
        return; 
 
3274
}
 
3275
 
 
3276
/*!
 
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.
 
3284
 *
 
3285
 * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
 
3286
 */
 
3287
static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
 
3288
{
 
3289
        SQLHSTMT stmt;
 
3290
        char sql[512];
 
3291
        char msgnums[20];
 
3292
        char msgnumd[20];
 
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 };
 
3296
 
 
3297
        delete_file(ddir, dmsg);
 
3298
        obj = ast_odbc_request_obj(odbc_database, 0);
 
3299
        if (obj) {
 
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);
 
3304
                if (!stmt)
 
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);
 
3306
                else
 
3307
                        SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 
3308
                ast_odbc_release_obj(obj);
 
3309
        } else
 
3310
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
 
3311
        return; 
 
3312
}
 
3313
 
 
3314
struct insert_data {
 
3315
        char *sql;
 
3316
        char *dir;
 
3317
        char *msgnums;
 
3318
        void *data;
 
3319
        SQLLEN datalen;
 
3320
        SQLLEN indlen;
 
3321
        const char *context;
 
3322
        const char *macrocontext;
 
3323
        const char *callerid;
 
3324
        const char *origtime;
 
3325
        const char *duration;
 
3326
        char *mailboxuser;
 
3327
        char *mailboxcontext;
 
3328
        const char *category;
 
3329
        const char *flag;
 
3330
};
 
3331
 
 
3332
static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
 
3333
{
 
3334
        struct insert_data *data = vdata;
 
3335
        int res;
 
3336
        SQLHSTMT stmt;
 
3337
 
 
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);
 
3342
                return NULL;
 
3343
        }
 
3344
 
 
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);
 
3358
        }
 
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);
 
3363
                return NULL;
 
3364
        }
 
3365
 
 
3366
        return stmt;
 
3367
}
 
3368
 
 
3369
/*!
 
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.
 
3375
 *
 
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.
 
3379
 *
 
3380
 * \return the zero on success -1 on error.
 
3381
 */
 
3382
static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
 
3383
{
 
3384
        int res = 0;
 
3385
        int fd = -1;
 
3386
        void *fdm = MAP_FAILED;
 
3387
        size_t fdlen = -1;
 
3388
        SQLHSTMT stmt;
 
3389
        char sql[PATH_MAX];
 
3390
        char msgnums[20];
 
3391
        char fn[PATH_MAX];
 
3392
        char full_fn[PATH_MAX];
 
3393
        char fmt[80]="";
 
3394
        char *c;
 
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 };
 
3400
 
 
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);
 
3404
                return -1;
 
3405
        }
 
3406
 
 
3407
        do {
 
3408
                ast_copy_string(fmt, vmfmts, sizeof(fmt));
 
3409
                c = strchr(fmt, '|');
 
3410
                if (c)
 
3411
                        *c = '\0';
 
3412
                if (!strcasecmp(fmt, "wav49"))
 
3413
                        strcpy(fmt, "WAV");
 
3414
                snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
 
3415
                if (msgnum > -1)
 
3416
                        make_file(fn, sizeof(fn), dir, msgnum);
 
3417
                else
 
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);
 
3423
                if (fd < 0) {
 
3424
                        ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
 
3425
                        res = -1;
 
3426
                        break;
 
3427
                }
 
3428
                if (cfg && cfg != CONFIG_STATUS_FILEINVALID) {
 
3429
                        if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
 
3430
                                idata.context = "";
 
3431
                        }
 
3432
                        if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
 
3433
                                idata.macrocontext = "";
 
3434
                        }
 
3435
                        if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
 
3436
                                idata.callerid = "";
 
3437
                        }
 
3438
                        if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
 
3439
                                idata.origtime = "";
 
3440
                        }
 
3441
                        if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
 
3442
                                idata.duration = "";
 
3443
                        }
 
3444
                        if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
 
3445
                                idata.category = "";
 
3446
                        }
 
3447
                        if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
 
3448
                                idata.flag = "";
 
3449
                        }
 
3450
                }
 
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");
 
3457
                        res = -1;
 
3458
                        break;
 
3459
                } 
 
3460
                idata.data = fdm;
 
3461
                idata.datalen = idata.indlen = fdlen;
 
3462
 
 
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); 
 
3465
                else
 
3466
                        snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
 
3467
 
 
3468
                if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
 
3469
                        SQLFreeHandle (SQL_HANDLE_STMT, stmt);
 
3470
                } else {
 
3471
                        ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
 
3472
                        res = -1;
 
3473
                }
 
3474
        } while (0);
 
3475
        if (obj) {
 
3476
                ast_odbc_release_obj(obj);
 
3477
        }
 
3478
        if (cfg)
 
3479
                ast_config_destroy(cfg);
 
3480
        if (fdm != MAP_FAILED)
 
3481
                munmap(fdm, fdlen);
 
3482
        if (fd > -1)
 
3483
                close(fd);
 
3484
        return res;
 
3485
}
 
3486
 
 
3487
/*!
 
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.
 
3495
 *
 
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.
 
3499
 */
 
3500
static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
 
3501
{
 
3502
        SQLHSTMT stmt;
 
3503
        char sql[PATH_MAX];
 
3504
        char msgnums[20];
 
3505
        char msgnumd[20];
 
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 };
 
3509
 
 
3510
        delete_file(ddir, dmsg);
 
3511
        obj = ast_odbc_request_obj(odbc_database, 0);
 
3512
        if (obj) {
 
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);
 
3517
                if (!stmt)
 
3518
                        ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
 
3519
                else
 
3520
                        SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 
3521
                ast_odbc_release_obj(obj);
 
3522
        } else
 
3523
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
 
3524
        return; 
 
3525
}
 
3526
 
 
3527
/*!
 
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.
 
3531
 *
 
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.
 
3537
 */
 
3538
static int remove_file(char *dir, int msgnum)
 
3539
{
 
3540
        char fn[PATH_MAX];
 
3541
        char full_fn[PATH_MAX];
 
3542
        char msgnums[80];
 
3543
        
 
3544
        if (msgnum > -1) {
 
3545
                snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
 
3546
                make_file(fn, sizeof(fn), dir, msgnum);
 
3547
        } else
 
3548
                ast_copy_string(fn, dir, sizeof(fn));
 
3549
        ast_filedelete(fn, NULL);       
 
3550
        snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
 
3551
        unlink(full_fn);
 
3552
        return 0;
 
3553
}
 
3554
#else
 
3555
#ifndef IMAP_STORAGE
 
3556
/*!
 
3557
 * \brief Find all .txt files - even if they are not in sequence from 0000.
 
3558
 * \param vmu
 
3559
 * \param dir
 
3560
 *
 
3561
 * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
 
3562
 *
 
3563
 * \return the count of messages, zero or more.
 
3564
 */
 
3565
static int count_messages(struct ast_vm_user *vmu, char *dir)
 
3566
{
 
3567
 
 
3568
        int vmcount = 0;
 
3569
        DIR *vmdir = NULL;
 
3570
        struct dirent *vment = NULL;
 
3571
 
 
3572
        if (vm_lock_path(dir))
 
3573
                return ERROR_LOCK_PATH;
 
3574
 
 
3575
        if ((vmdir = opendir(dir))) {
 
3576
                while ((vment = readdir(vmdir))) {
 
3577
                        if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
 
3578
                                vmcount++;
 
3579
                        }
 
3580
                }
 
3581
                closedir(vmdir);
 
3582
        }
 
3583
        ast_unlock_path(dir);
 
3584
        
 
3585
        return vmcount;
 
3586
}
 
3587
 
 
3588
/*!
 
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.
 
3592
 *
 
3593
 * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
 
3594
 */
 
3595
static void rename_file(char *sfn, char *dfn)
 
3596
{
 
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);
 
3604
        }
 
3605
        rename(stxt, dtxt);
 
3606
}
 
3607
 
 
3608
/*! 
 
3609
 * \brief Determines the highest message number in use for a given user and mailbox folder.
 
3610
 * \param vmu 
 
3611
 * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
 
3612
 *
 
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.
 
3615
 *
 
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.
 
3618
 */
 
3619
static int last_message_index(struct ast_vm_user *vmu, char *dir)
 
3620
{
 
3621
        int x;
 
3622
        unsigned char map[MAXMSGLIMIT] = "";
 
3623
        DIR *msgdir;
 
3624
        struct dirent *msgdirent;
 
3625
        int msgdirint;
 
3626
 
 
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))) {
 
3632
                return -1;
 
3633
        }
 
3634
 
 
3635
        while ((msgdirent = readdir(msgdir))) {
 
3636
                if (sscanf(msgdirent->d_name, "msg%30d", &msgdirint) == 1 && msgdirint < MAXMSGLIMIT)
 
3637
                        map[msgdirint] = 1;
 
3638
        }
 
3639
        closedir(msgdir);
 
3640
 
 
3641
        for (x = 0; x < vmu->maxmsg; x++) {
 
3642
                if (map[x] == 0)
 
3643
                        break;
 
3644
        }
 
3645
 
 
3646
        return x - 1;
 
3647
}
 
3648
 
 
3649
#endif /* #ifndef IMAP_STORAGE */
 
3650
#endif /* #else of #ifdef ODBC_STORAGE */
 
3651
#ifndef IMAP_STORAGE
 
3652
/*!
 
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.
 
3656
 *
 
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.
 
3659
 *
 
3660
 * \return zero on success, -1 on error.
 
3661
 */
 
3662
static int copy(char *infile, char *outfile)
 
3663
{
 
3664
        int ifd;
 
3665
        int ofd;
 
3666
        int res;
 
3667
        int len;
 
3668
        char buf[4096];
 
3669
 
 
3670
#ifdef HARDLINK_WHEN_POSSIBLE
 
3671
        /* Hard link if possible; saves disk space & is faster */
 
3672
        if (link(infile, outfile)) {
 
3673
#endif
 
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));
 
3676
                        return -1;
 
3677
                }
 
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));
 
3680
                        close(ifd);
 
3681
                        return -1;
 
3682
                }
 
3683
                do {
 
3684
                        len = read(ifd, buf, sizeof(buf));
 
3685
                        if (len < 0) {
 
3686
                                ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
 
3687
                                close(ifd);
 
3688
                                close(ofd);
 
3689
                                unlink(outfile);
 
3690
                        }
 
3691
                        if (len) {
 
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));
 
3695
                                        close(ifd);
 
3696
                                        close(ofd);
 
3697
                                        unlink(outfile);
 
3698
                                }
 
3699
                        }
 
3700
                } while (len);
 
3701
                close(ifd);
 
3702
                close(ofd);
 
3703
                return 0;
 
3704
#ifdef HARDLINK_WHEN_POSSIBLE
 
3705
        } else {
 
3706
                /* Hard link succeeded */
 
3707
                return 0;
 
3708
        }
 
3709
#endif
 
3710
}
 
3711
 
 
3712
/*!
 
3713
 * \brief Copies a voicemail information (envelope) file.
 
3714
 * \param frompath
 
3715
 * \param topath 
 
3716
 *
 
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.
 
3720
 */
 
3721
static void copy_plain_file(char *frompath, char *topath)
 
3722
{
 
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")) {
 
3740
                                exten = tmp->value;
 
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;
 
3755
                        }
 
3756
                }
 
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);
 
3758
        }
 
3759
        copy(frompath2, topath2);
 
3760
        ast_variables_destroy(var);
 
3761
}
 
3762
#endif
 
3763
 
 
3764
/*! 
 
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.
 
3767
 *
 
3768
 * This is used by the DELETE macro when voicemails are stored on the file system.
 
3769
 *
 
3770
 * \return zero on success, -1 on error.
 
3771
 */
 
3772
static int vm_delete(char *file)
 
3773
{
 
3774
        char *txt;
 
3775
        int txtsize = 0;
 
3776
 
 
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
 
3781
         */
 
3782
        if (ast_check_realtime("voicemail_data")) {
 
3783
                ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
 
3784
        }
 
3785
        snprintf(txt, txtsize, "%s.txt", file);
 
3786
        unlink(txt);
 
3787
        return ast_filedelete(file, NULL);
 
3788
}
 
3789
 
 
3790
/*!
 
3791
 * \brief utility used by inchar(), for base_encode()
 
3792
 */
 
3793
static int inbuf(struct baseio *bio, FILE *fi)
 
3794
{
 
3795
        int l;
 
3796
 
 
3797
        if (bio->ateof)
 
3798
                return 0;
 
3799
 
 
3800
        if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
 
3801
                if (ferror(fi))
 
3802
                        return -1;
 
3803
 
 
3804
                bio->ateof = 1;
 
3805
                return 0;
 
3806
        }
 
3807
 
 
3808
        bio->iolen= l;
 
3809
        bio->iocp= 0;
 
3810
 
 
3811
        return 1;
 
3812
}
 
3813
 
 
3814
/*!
 
3815
 * \brief utility used by base_encode()
 
3816
 */
 
3817
static int inchar(struct baseio *bio, FILE *fi)
 
3818
{
 
3819
        if (bio->iocp>=bio->iolen) {
 
3820
                if (!inbuf(bio, fi))
 
3821
                        return EOF;
 
3822
        }
 
3823
 
 
3824
        return bio->iobuf[bio->iocp++];
 
3825
}
 
3826
 
 
3827
/*!
 
3828
 * \brief utility used by base_encode()
 
3829
 */
 
3830
static int ochar(struct baseio *bio, int c, FILE *so)
 
3831
{
 
3832
        if (bio->linelength >= BASELINELEN) {
 
3833
                if (fputs(eol,so) == EOF)
 
3834
                        return -1;
 
3835
 
 
3836
                bio->linelength= 0;
 
3837
        }
 
3838
 
 
3839
        if (putc(((unsigned char)c),so) == EOF)
 
3840
                return -1;
 
3841
 
 
3842
        bio->linelength++;
 
3843
 
 
3844
        return 1;
 
3845
}
 
3846
 
 
3847
/*!
 
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.
 
3851
 *
 
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 ?
 
3853
 *
 
3854
 * \return zero on success, -1 on error.
 
3855
 */
 
3856
static int base_encode(char *filename, FILE *so)
 
3857
{
 
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', '+', '/'};
 
3862
        int i,hiteof= 0;
 
3863
        FILE *fi;
 
3864
        struct baseio bio;
 
3865
 
 
3866
        memset(&bio, 0, sizeof(bio));
 
3867
        bio.iocp = BASEMAXINLINE;
 
3868
 
 
3869
        if (!(fi = fopen(filename, "rb"))) {
 
3870
                ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
 
3871
                return -1;
 
3872
        }
 
3873
 
 
3874
        while (!hiteof){
 
3875
                unsigned char igroup[3], ogroup[4];
 
3876
                int c,n;
 
3877
 
 
3878
                igroup[0]= igroup[1]= igroup[2]= 0;
 
3879
 
 
3880
                for (n= 0;n<3;n++) {
 
3881
                        if ((c = inchar(&bio, fi)) == EOF) {
 
3882
                                hiteof= 1;
 
3883
                                break;
 
3884
                        }
 
3885
 
 
3886
                        igroup[n]= (unsigned char)c;
 
3887
                }
 
3888
 
 
3889
                if (n> 0) {
 
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];
 
3894
 
 
3895
                        if (n<3) {
 
3896
                                ogroup[3]= '=';
 
3897
 
 
3898
                                if (n<2)
 
3899
                                        ogroup[2]= '=';
 
3900
                        }
 
3901
 
 
3902
                        for (i= 0;i<4;i++)
 
3903
                                ochar(&bio, ogroup[i], so);
 
3904
                }
 
3905
        }
 
3906
 
 
3907
        fclose(fi);
 
3908
        
 
3909
        if (fputs(eol,so)==EOF)
 
3910
                return 0;
 
3911
 
 
3912
        return 1;
 
3913
}
 
3914
 
 
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)
 
3916
{
 
3917
        char callerid[256];
 
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];
 
3922
        int inttime;
 
3923
        struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
 
3924
 
 
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);
 
3939
 
 
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");
 
3945
        }
 
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);
 
3949
                }
 
3950
                return;
 
3951
        }
 
3952
 
 
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);
 
3958
        }
 
3959
 
 
3960
        if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
 
3961
                struct timeval tv = { inttime, };
 
3962
                struct ast_tm tm;
 
3963
                ast_localtime(&tv, &tm, NULL);
 
3964
                ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
 
3965
                pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
 
3966
        }
 
3967
        ast_config_destroy(msg_cfg);
 
3968
}
 
3969
 
 
3970
/*!
 
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.
 
3974
 * 
 
3975
 * \return The destination string with quotes wrapped on it (the to field).
 
3976
 */
 
3977
static char *quote(const char *from, char *to, size_t len)
 
3978
{
 
3979
        char *ptr = to;
 
3980
        *ptr++ = '"';
 
3981
        for (; ptr < to + len - 1; from++) {
 
3982
                if (*from == '"')
 
3983
                        *ptr++ = '\\';
 
3984
                else if (*from == '\0')
 
3985
                        break;
 
3986
                *ptr++ = *from;
 
3987
        }
 
3988
        if (ptr < to + len - 1)
 
3989
                *ptr++ = '"';
 
3990
        *ptr = '\0';
 
3991
        return to;
 
3992
}
 
3993
 
 
3994
/*! \brief
 
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.
 
3997
 */
 
3998
static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
 
3999
{
 
4000
        const struct vm_zone *z = NULL;
 
4001
        struct timeval t = ast_tvnow();
 
4002
 
 
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))
 
4009
                                break;
 
4010
                }
 
4011
                AST_LIST_UNLOCK(&zones);
 
4012
        }
 
4013
        ast_localtime(&t, tm, z ? z->timezone : NULL);
 
4014
        return tm;
 
4015
}
 
4016
 
 
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
 
4019
 * clean.
 
4020
 */
 
4021
static int check_mime(const char *str)
 
4022
{
 
4023
        for (; *str; str++) {
 
4024
                if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
 
4025
                        return 1;
 
4026
                }
 
4027
        }
 
4028
        return 0;
 
4029
}
 
4030
 
 
4031
/*!\brief Encode a string according to the MIME rules for encoding strings
 
4032
 * that are not 7-bit clean or contain control characters.
 
4033
 *
 
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.
 
4038
 *
 
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.
 
4046
 */
 
4047
static char *encode_mime_str(const char *start, char *end, size_t endsize, size_t preamble, size_t postamble)
 
4048
{
 
4049
        char tmp[80];
 
4050
        int first_section = 1;
 
4051
        size_t endlen = 0, tmplen = 0;
 
4052
        *end = '\0';
 
4053
 
 
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)) {
 
4058
                        need_encoding = 1;
 
4059
                }
 
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);
 
4067
                        first_section = 0;
 
4068
                }
 
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);
 
4073
                } else {
 
4074
                        tmplen += snprintf(tmp + tmplen, sizeof(tmp) - tmplen, "%c", *start);
 
4075
                }
 
4076
        }
 
4077
        snprintf(end + endlen, endsize - endlen, "%s%s?=%s", first_section ? "" : " ", tmp, endlen + postamble > 74 ? " " : "");
 
4078
        return end;
 
4079
}
 
4080
 
 
4081
/*!
 
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.
 
4087
 * \param context 
 
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.
 
4095
 * \param chan
 
4096
 * \param category
 
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.
 
4098
 *
 
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.
 
4100
 */
 
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)
 
4102
{
 
4103
        char date[256];
 
4104
        char host[MAXHOSTNAMELEN] = "";
 
4105
        char who[256];
 
4106
        char bound[256];
 
4107
        char dur[256];
 
4108
        struct ast_tm tm;
 
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; 
 
4113
        char filename[256];
 
4114
 
 
4115
#ifdef IMAP_STORAGE
 
4116
#define ENDL "\r\n"
 
4117
#else
 
4118
#define ENDL "\n"
 
4119
#endif
 
4120
 
 
4121
        /* One alloca for multiple fields */
 
4122
        len_passdata2 = strlen(vmu->fullname);
 
4123
        if (emailsubject && (tmplen = strlen(emailsubject)) > len_passdata2) {
 
4124
                len_passdata2 = tmplen;
 
4125
        }
 
4126
        if ((tmplen = strlen(fromstring)) > len_passdata2) {
 
4127
                len_passdata2 = tmplen;
 
4128
        }
 
4129
        len_passdata2 = len_passdata2 * 3 + 200;
 
4130
        passdata2 = alloca(len_passdata2);
 
4131
 
 
4132
        if (cidnum) {
 
4133
                strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
 
4134
        }
 
4135
        if (cidname) {
 
4136
                strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
 
4137
        }
 
4138
        gethostname(host, sizeof(host) - 1);
 
4139
 
 
4140
        if (strchr(srcemail, '@'))
 
4141
                ast_copy_string(who, srcemail, sizeof(who));
 
4142
        else 
 
4143
                snprintf(who, sizeof(who), "%s@%s", srcemail, host);
 
4144
        
 
4145
        greeting_attachment = strrchr(ast_strdupa(attach), '/');
 
4146
        if (greeting_attachment)
 
4147
                *greeting_attachment++ = '\0';
 
4148
 
 
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);
 
4152
 
 
4153
        /* Set date format for voicemail mail */
 
4154
        ast_strftime(date, sizeof(date), emaildateformat, &tm);
 
4155
 
 
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"))) {
 
4159
                        char *ptr;
 
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)) {
 
4166
                                int first_line = 1;
 
4167
                                encode_mime_str(passdata2, passdata, len_passdata, strlen("From: "), strlen(who) + 3);
 
4168
                                while ((ptr = strchr(passdata, ' '))) {
 
4169
                                        *ptr = '\0';
 
4170
                                        fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", passdata);
 
4171
                                        first_line = 0;
 
4172
                                        passdata = ptr + 1;
 
4173
                                }
 
4174
                                fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", passdata, who);
 
4175
                        } else {
 
4176
                                fprintf(p, "From: %s <%s>" ENDL, quote(passdata2, passdata, len_passdata), who);
 
4177
                        }
 
4178
                        ast_channel_free(ast);
 
4179
                } else {
 
4180
                        ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
 
4181
                }
 
4182
        } else {
 
4183
                fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
 
4184
        }
 
4185
 
 
4186
        if (check_mime(vmu->fullname)) {
 
4187
                int first_line = 1;
 
4188
                char *ptr;
 
4189
                encode_mime_str(vmu->fullname, passdata2, len_passdata2, strlen("To: "), strlen(vmu->email) + 3);
 
4190
                while ((ptr = strchr(passdata2, ' '))) {
 
4191
                        *ptr = '\0';
 
4192
                        fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", passdata2);
 
4193
                        first_line = 0;
 
4194
                        passdata2 = ptr + 1;
 
4195
                }
 
4196
                fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", passdata2, vmu->email);
 
4197
        } else {
 
4198
                fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata2), vmu->email);
 
4199
        }
 
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;
 
4209
                        }
 
4210
 
 
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)) {
 
4215
                                int first_line = 1;
 
4216
                                char *ptr;
 
4217
                                encode_mime_str(passdata, passdata2, len_passdata2, strlen("Subject: "), 0);
 
4218
                                while ((ptr = strchr(passdata2, ' '))) {
 
4219
                                        *ptr = '\0';
 
4220
                                        fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
 
4221
                                        first_line = 0;
 
4222
                                        passdata2 = ptr + 1;
 
4223
                                }
 
4224
                                fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", passdata2);
 
4225
                        } else {
 
4226
                                fprintf(p, "Subject: %s" ENDL, passdata);
 
4227
                        }
 
4228
                        ast_channel_free(ast);
 
4229
                } else {
 
4230
                        ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
 
4231
                }
 
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);
 
4235
                } else {
 
4236
                        fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
 
4237
                }
 
4238
        } else {
 
4239
                if (ast_strlen_zero(flag)) {
 
4240
                        fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
 
4241
                } else {
 
4242
                        fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
 
4243
                }
 
4244
        }
 
4245
 
 
4246
        fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
 
4247
        if (imap) {
 
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);
 
4253
#ifdef IMAP_STORAGE
 
4254
                fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
 
4255
#else
 
4256
                fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
 
4257
#endif
 
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);
 
4267
                } else {
 
4268
                        fprintf(p, "X-Asterisk-VM-Category: " ENDL);
 
4269
                }
 
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));
 
4273
        }
 
4274
        if (!ast_strlen_zero(cidnum)) {
 
4275
                fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
 
4276
        }
 
4277
        if (!ast_strlen_zero(cidname)) {
 
4278
                fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
 
4279
        }
 
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());
 
4284
 
 
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);
 
4288
        }
 
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"))) {
 
4294
                        char *passdata;
 
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);
 
4302
                } else
 
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;
 
4308
                        const char *v;
 
4309
                        int inttime;
 
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");
 
4317
                        }
 
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));
 
4321
                                }
 
4322
 
 
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, };
 
4327
                                        struct ast_tm tm;
 
4328
                                        ast_localtime(&tv, &tm, NULL);
 
4329
                                        ast_strftime(origdate, sizeof(origdate), emaildateformat, &tm);
 
4330
                                }
 
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);
 
4338
                        } else {
 
4339
                                goto plain_message;
 
4340
                        }
 
4341
                } else {
 
4342
plain_message:
 
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);
 
4348
                }
 
4349
        } else {
 
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);
 
4352
        }
 
4353
 
 
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);
 
4362
                } else {
 
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);
 
4366
                }
 
4367
        }
 
4368
}
 
4369
 
 
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)
 
4371
{
 
4372
        char tmpdir[256], newtmp[256];
 
4373
        char fname[256];
 
4374
        char tmpcmd[256];
 
4375
        int tmpfd = -1;
 
4376
 
 
4377
        /* Eww. We want formats to tell us their own MIME type */
 
4378
        char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
 
4379
 
 
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);
 
4386
                if (tmpfd > -1) {
 
4387
                        int soxstatus;
 
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) {
 
4390
                                attach = newtmp;
 
4391
                                ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
 
4392
                        } else {
 
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");
 
4396
                        }
 
4397
                }
 
4398
        }
 
4399
        fprintf(p, "--%s" ENDL, bound);
 
4400
        if (msgnum > -1)
 
4401
                fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
 
4402
        else
 
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);
 
4406
        if (msgnum > -1)
 
4407
                fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
 
4408
        else
 
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);
 
4412
        if (last)
 
4413
                fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
 
4414
        if (tmpfd > -1) {
 
4415
                unlink(fname);
 
4416
                close(tmpfd);
 
4417
                unlink(newtmp);
 
4418
        }
 
4419
        return 0;
 
4420
}
 
4421
#undef ENDL
 
4422
 
 
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)
 
4424
{
 
4425
        FILE *p=NULL;
 
4426
        char tmp[80] = "/tmp/astmail-XXXXXX";
 
4427
        char tmp2[256];
 
4428
 
 
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);
 
4431
                return(0);
 
4432
        }
 
4433
        if (!strcmp(format, "wav49"))
 
4434
                format = "WAV";
 
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
 
4437
           command hangs */
 
4438
        if ((p = vm_mkftemp(tmp)) == NULL) {
 
4439
                ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
 
4440
                return -1;
 
4441
        } else {
 
4442
                make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
 
4443
                fclose(p);
 
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);
 
4447
        }
 
4448
        return 0;
 
4449
}
 
4450
 
 
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)
 
4452
{
 
4453
        char date[256];
 
4454
        char host[MAXHOSTNAMELEN] = "";
 
4455
        char who[256];
 
4456
        char dur[PATH_MAX];
 
4457
        char tmp[80] = "/tmp/astmail-XXXXXX";
 
4458
        char tmp2[PATH_MAX];
 
4459
        struct ast_tm tm;
 
4460
        FILE *p;
 
4461
 
 
4462
        if ((p = vm_mkftemp(tmp)) == NULL) {
 
4463
                ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
 
4464
                return -1;
 
4465
        }
 
4466
        gethostname(host, sizeof(host)-1);
 
4467
        if (strchr(srcemail, '@'))
 
4468
                ast_copy_string(who, srcemail, sizeof(who));
 
4469
        else 
 
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);
 
4474
 
 
4475
        if (*pagerfromstring) {
 
4476
                struct ast_channel *ast;
 
4477
                if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
 
4478
                        char *passdata;
 
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);
 
4486
                } else 
 
4487
                        ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
 
4488
        } else
 
4489
                fprintf(p, "From: Asterisk PBX <%s>\n", who);
 
4490
        fprintf(p, "To: %s\n", pager);
 
4491
        if (pagersubject) {
 
4492
                struct ast_channel *ast;
 
4493
                if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
 
4494
                        char *passdata;
 
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);
 
4502
                } else
 
4503
                        ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
 
4504
        } else {
 
4505
                if (ast_strlen_zero(flag)) {
 
4506
                        fprintf(p, "Subject: New VM\n\n");
 
4507
                } else {
 
4508
                        fprintf(p, "Subject: New %s VM\n\n", flag);
 
4509
                }
 
4510
        }
 
4511
 
 
4512
        ast_strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
 
4513
        if (pagerbody) {
 
4514
                struct ast_channel *ast;
 
4515
                if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, "Substitution/voicemail"))) {
 
4516
                        char *passdata;
 
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);
 
4524
                } else
 
4525
                        ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
 
4526
        } else {
 
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);
 
4529
        }
 
4530
        fclose(p);
 
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);
 
4534
        return 0;
 
4535
}
 
4536
 
 
4537
/*!
 
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.
 
4541
 * 
 
4542
 * The date format string used is "%a %b %e %r UTC %Y".
 
4543
 * 
 
4544
 * \return zero on success, -1 on error.
 
4545
 */
 
4546
static int get_date(char *s, int len)
 
4547
{
 
4548
        struct ast_tm tm;
 
4549
        struct timeval t = ast_tvnow();
 
4550
        
 
4551
        ast_localtime(&t, &tm, "UTC");
 
4552
 
 
4553
        return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
 
4554
}
 
4555
 
 
4556
static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
 
4557
{
 
4558
        int res;
 
4559
        char fn[PATH_MAX];
 
4560
        char dest[PATH_MAX];
 
4561
 
 
4562
        snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
 
4563
 
 
4564
        if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
 
4565
                ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
 
4566
                return -1;
 
4567
        }
 
4568
 
 
4569
        RETRIEVE(fn, -1, ext, context);
 
4570
        if (ast_fileexists(fn, NULL, NULL) > 0) {
 
4571
                res = ast_stream_and_wait(chan, fn, ecodes);
 
4572
                if (res) {
 
4573
                        DISPOSE(fn, -1);
 
4574
                        return res;
 
4575
                }
 
4576
        } else {
 
4577
                /* Dispose just in case */
 
4578
                DISPOSE(fn, -1);
 
4579
                res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
 
4580
                if (res)
 
4581
                        return res;
 
4582
                res = ast_say_digit_str(chan, ext, ecodes, chan->language);
 
4583
                if (res)
 
4584
                        return res;
 
4585
        }
 
4586
        res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
 
4587
        return res;
 
4588
}
 
4589
 
 
4590
static void free_zone(struct vm_zone *z)
 
4591
{
 
4592
        ast_free(z);
 
4593
}
 
4594
 
 
4595
#ifdef ODBC_STORAGE
 
4596
static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
 
4597
{
 
4598
        int x = -1;
 
4599
        int res;
 
4600
        SQLHSTMT stmt = NULL;
 
4601
        char sql[PATH_MAX];
 
4602
        char rowdata[20];
 
4603
        char tmp[PATH_MAX] = "";
 
4604
        struct odbc_obj *obj = NULL;
 
4605
        char *context;
 
4606
        struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
 
4607
 
 
4608
        if (newmsgs)
 
4609
                *newmsgs = 0;
 
4610
        if (oldmsgs)
 
4611
                *oldmsgs = 0;
 
4612
        if (urgentmsgs)
 
4613
                *urgentmsgs = 0;
 
4614
 
 
4615
        /* If no mailbox, return immediately */
 
4616
        if (ast_strlen_zero(mailbox))
 
4617
                return 0;
 
4618
 
 
4619
        ast_copy_string(tmp, mailbox, sizeof(tmp));
 
4620
 
 
4621
        if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
 
4622
                int u, n, o;
 
4623
                char *next, *remaining = tmp;
 
4624
                while ((next = strsep(&remaining, " ,"))) {
 
4625
                        if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
 
4626
                                return -1;
 
4627
                        }
 
4628
                        if (urgentmsgs) {
 
4629
                                *urgentmsgs += u;
 
4630
                        }
 
4631
                        if (newmsgs) {
 
4632
                                *newmsgs += n;
 
4633
                        }
 
4634
                        if (oldmsgs) {
 
4635
                                *oldmsgs += o;
 
4636
                        }
 
4637
                }
 
4638
                return 0;
 
4639
        }
 
4640
 
 
4641
        context = strchr(tmp, '@');
 
4642
        if (context) {
 
4643
                *context = '\0';
 
4644
                context++;
 
4645
        } else
 
4646
                context = "default";
 
4647
 
 
4648
        if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
 
4649
                do {
 
4650
                        if (newmsgs) {
 
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);
 
4654
                                        break;
 
4655
                                }
 
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);
 
4659
                                        break;
 
4660
                                }
 
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);
 
4664
                                        break;
 
4665
                                }
 
4666
                                *newmsgs = atoi(rowdata);
 
4667
                                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
 
4668
                        }
 
4669
 
 
4670
                        if (oldmsgs) {
 
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);
 
4674
                                        break;
 
4675
                                }
 
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);
 
4679
                                        break;
 
4680
                                }
 
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);
 
4684
                                        break;
 
4685
                                }
 
4686
                                SQLFreeHandle(SQL_HANDLE_STMT, stmt);
 
4687
                                *oldmsgs = atoi(rowdata);
 
4688
                        }
 
4689
 
 
4690
                        if (urgentmsgs) {
 
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);
 
4694
                                        break;
 
4695
                                }
 
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);
 
4699
                                        break;
 
4700
                                }
 
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);
 
4704
                                        break;
 
4705
                                }
 
4706
                                *urgentmsgs = atoi(rowdata);
 
4707
                        }
 
4708
 
 
4709
                        x = 0;
 
4710
                } while (0);
 
4711
        } else {
 
4712
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
 
4713
        }
 
4714
 
 
4715
        if (stmt) {
 
4716
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
 
4717
        }
 
4718
        if (obj) {
 
4719
                ast_odbc_release_obj(obj);
 
4720
        }
 
4721
 
 
4722
        return x;
 
4723
}
 
4724
 
 
4725
static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
 
4726
{
 
4727
        return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
 
4728
}
 
4729
 
 
4730
/*!
 
4731
 * \brief Gets the number of messages that exist in a mailbox folder.
 
4732
 * \param context
 
4733
 * \param mailbox
 
4734
 * \param folder
 
4735
 * 
 
4736
 * This method is used when ODBC backend is used.
 
4737
 * \return The number of messages in this mailbox folder (zero or more).
 
4738
 */
 
4739
static int messagecount(const char *context, const char *mailbox, const char *folder)
 
4740
{
 
4741
        struct odbc_obj *obj = NULL;
 
4742
        int nummsgs = 0;
 
4743
        int res;
 
4744
        SQLHSTMT stmt = NULL;
 
4745
        char sql[PATH_MAX];
 
4746
        char rowdata[20];
 
4747
        struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
 
4748
        if (!folder)
 
4749
                folder = "INBOX";
 
4750
        /* If no mailbox, return immediately */
 
4751
        if (ast_strlen_zero(mailbox))
 
4752
                return 0;
 
4753
 
 
4754
        obj = ast_odbc_request_obj(odbc_database, 0);
 
4755
        if (obj) {
 
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);
 
4758
                if (!stmt) {
 
4759
                        ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
 
4760
                        goto yuck;
 
4761
                }
 
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);
 
4766
                        goto yuck;
 
4767
                }
 
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);
 
4772
                        goto yuck;
 
4773
                }
 
4774
                nummsgs = atoi(rowdata);
 
4775
                SQLFreeHandle (SQL_HANDLE_STMT, stmt);
 
4776
        } else
 
4777
                ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
 
4778
 
 
4779
yuck:
 
4780
        if (obj)
 
4781
                ast_odbc_release_obj(obj);
 
4782
        return nummsgs;
 
4783
}
 
4784
 
 
4785
/** 
 
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.
 
4788
 * 
 
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.
 
4792
 */
 
4793
static int has_voicemail(const char *mailbox, const char *folder)
 
4794
{
 
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))
 
4802
                        return 1;
 
4803
        }
 
4804
        return 0;
 
4805
}
 
4806
#endif
 
4807
#ifndef IMAP_STORAGE
 
4808
/*! 
 
4809
 * \brief Copies a message from one mailbox to another.
 
4810
 * \param chan
 
4811
 * \param vmu
 
4812
 * \param imbox
 
4813
 * \param msgnum
 
4814
 * \param duration
 
4815
 * \param recip
 
4816
 * \param fmt
 
4817
 * \param dir
 
4818
 *
 
4819
 * This is only used by file storage based mailboxes.
 
4820
 *
 
4821
 * \return zero on success, -1 on error.
 
4822
 */
 
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)
 
4824
{
 
4825
        char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
 
4826
        const char *frombox = mbox(imbox);
 
4827
        int recipmsgnum;
 
4828
 
 
4829
        ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
 
4830
 
 
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");
 
4833
        } else {
 
4834
                create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
 
4835
        }
 
4836
        
 
4837
        if (!dir)
 
4838
                make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
 
4839
        else
 
4840
                ast_copy_string(fromdir, dir, sizeof(fromdir));
 
4841
 
 
4842
        make_file(frompath, sizeof(frompath), fromdir, msgnum);
 
4843
        make_dir(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
 
4844
 
 
4845
        if (vm_lock_path(todir))
 
4846
                return ERROR_LOCK_PATH;
 
4847
 
 
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);
 
4853
                } else {
 
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
 
4858
                         */
 
4859
                        copy_plain_file(frompath, topath);
 
4860
                        STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
 
4861
                        vm_delete(topath);
 
4862
                }
 
4863
        } else {
 
4864
                ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
 
4865
        }
 
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);
 
4868
        
 
4869
        return 0;
 
4870
}
 
4871
#endif
 
4872
#if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
 
4873
 
 
4874
static int messagecount(const char *context, const char *mailbox, const char *folder)
 
4875
{
 
4876
        return __has_voicemail(context, mailbox, folder, 0);
 
4877
}
 
4878
 
 
4879
static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
 
4880
{
 
4881
        DIR *dir;
 
4882
        struct dirent *de;
 
4883
        char fn[256];
 
4884
        int ret = 0;
 
4885
 
 
4886
        /* If no mailbox, return immediately */
 
4887
        if (ast_strlen_zero(mailbox))
 
4888
                return 0;
 
4889
 
 
4890
        if (ast_strlen_zero(folder))
 
4891
                folder = "INBOX";
 
4892
        if (ast_strlen_zero(context))
 
4893
                context = "default";
 
4894
 
 
4895
        snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
 
4896
 
 
4897
        if (!(dir = opendir(fn)))
 
4898
                return 0;
 
4899
 
 
4900
        while ((de = readdir(dir))) {
 
4901
                if (!strncasecmp(de->d_name, "msg", 3)) {
 
4902
                        if (shortcircuit) {
 
4903
                                ret = 1;
 
4904
                                break;
 
4905
                        } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
 
4906
                                if (shortcircuit) return 1;
 
4907
                                ret++;
 
4908
                        }
 
4909
                }
 
4910
        }
 
4911
 
 
4912
        closedir(dir);
 
4913
 
 
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));
 
4917
        } else {
 
4918
                return ret;
 
4919
        }
 
4920
}
 
4921
 
 
4922
/** 
 
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
 
4926
 *
 
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.
 
4930
 */
 
4931
static int has_voicemail(const char *mailbox, const char *folder)
 
4932
{
 
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, '@')))
 
4937
                        *context++ = '\0';
 
4938
                else
 
4939
                        context = "default";
 
4940
                if (__has_voicemail(context, box, folder, 1))
 
4941
                        return 1;
 
4942
        }
 
4943
        return 0;
 
4944
}
 
4945
 
 
4946
 
 
4947
static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
 
4948
{
 
4949
        char tmp[256];
 
4950
        char *context;
 
4951
 
 
4952
        /* If no mailbox, return immediately */
 
4953
        if (ast_strlen_zero(mailbox))
 
4954
                return 0;
 
4955
 
 
4956
        if (newmsgs)
 
4957
                *newmsgs = 0;
 
4958
        if (oldmsgs)
 
4959
                *oldmsgs = 0;
 
4960
        if (urgentmsgs)
 
4961
                *urgentmsgs = 0;
 
4962
 
 
4963
        if (strchr(mailbox, ',')) {
 
4964
                int tmpnew, tmpold, tmpurgent;
 
4965
                char *mb, *cur;
 
4966
 
 
4967
                ast_copy_string(tmp, mailbox, sizeof(tmp));
 
4968
                mb = 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))
 
4972
                                        return -1;
 
4973
                                else {
 
4974
                                        if (newmsgs)
 
4975
                                                *newmsgs += tmpnew; 
 
4976
                                        if (oldmsgs)
 
4977
                                                *oldmsgs += tmpold;
 
4978
                                        if (urgentmsgs)
 
4979
                                                *urgentmsgs += tmpurgent;
 
4980
                                }
 
4981
                        }
 
4982
                }
 
4983
                return 0;
 
4984
        }
 
4985
 
 
4986
        ast_copy_string(tmp, mailbox, sizeof(tmp));
 
4987
        
 
4988
        if ((context = strchr(tmp, '@')))
 
4989
                *context++ = '\0';
 
4990
        else
 
4991
                context = "default";
 
4992
 
 
4993
        if (newmsgs)
 
4994
                *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
 
4995
        if (oldmsgs)
 
4996
                *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
 
4997
        if (urgentmsgs)
 
4998
                *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
 
4999
 
 
5000
        return 0;
 
5001
}
 
5002
 
 
5003
static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
 
5004
{
 
5005
        return inboxcount2(mailbox, NULL, newmsgs, oldmsgs);
 
5006
}
 
5007
 
 
5008
#endif
 
5009
 
 
5010
static void run_externnotify(char *context, char *extension, const char *flag)
 
5011
{
 
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;
 
5016
 
 
5017
        if (!ast_strlen_zero(context))
 
5018
                snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
 
5019
        else
 
5020
                ast_copy_string(ext_context, extension, sizeof(ext_context));
 
5021
 
 
5022
        if (smdi_iface) {
 
5023
                if (ast_app_has_voicemail(ext_context, NULL)) 
 
5024
                        ast_smdi_mwi_set(smdi_iface, extension);
 
5025
                else
 
5026
                        ast_smdi_mwi_unset(smdi_iface, extension);
 
5027
 
 
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);
 
5036
                } else {
 
5037
                        ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
 
5038
                }
 
5039
        }
 
5040
 
 
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);
 
5044
                } else {
 
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);
 
5048
                }
 
5049
        }
 
5050
}
 
5051
 
 
5052
/*!
 
5053
 * \brief Variables used for saving a voicemail.
 
5054
 *
 
5055
 * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
 
5056
 */
 
5057
struct leave_vm_options {
 
5058
        unsigned int flags;
 
5059
        signed char record_gain;
 
5060
        char *exitcontext;
 
5061
};
 
5062
 
 
5063
/*!
 
5064
 * \brief Prompts the user and records a voicemail to a mailbox.
 
5065
 * \param chan
 
5066
 * \param ext
 
5067
 * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
 
5068
 * 
 
5069
 * 
 
5070
 * 
 
5071
 * \return zero on success, -1 on error.
 
5072
 */
 
5073
static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
 
5074
{
 
5075
#ifdef IMAP_STORAGE
 
5076
        int newmsgs, oldmsgs;
 
5077
#else
 
5078
        char urgdir[PATH_MAX];
 
5079
#endif
 
5080
        char txtfile[PATH_MAX];
 
5081
        char tmptxtfile[PATH_MAX];
 
5082
        struct vm_state *vms = NULL;
 
5083
        char callerid[256];
 
5084
        FILE *txt;
 
5085
        char date[256];
 
5086
        int txtdes;
 
5087
        int res = 0;
 
5088
        int msgnum;
 
5089
        int duration = 0;
 
5090
        int ausemacro = 0;
 
5091
        int ousemacro = 0;
 
5092
        int ouseexten = 0;
 
5093
        char tmpdur[16];
 
5094
        char priority[16];
 
5095
        char origtime[16];
 
5096
        char dir[PATH_MAX];
 
5097
        char tmpdir[PATH_MAX];
 
5098
        char fn[PATH_MAX];
 
5099
        char prefile[PATH_MAX] = "";
 
5100
        char tempfile[PATH_MAX] = "";
 
5101
        char ext_context[256] = "";
 
5102
        char fmt[80];
 
5103
        char *context;
 
5104
        char ecodes[17] = "#";
 
5105
        struct ast_str *tmp = ast_str_create(16);
 
5106
        char *tmpptr;
 
5107
        struct ast_vm_user *vmu;
 
5108
        struct ast_vm_user svm;
 
5109
        const char *category = NULL;
 
5110
        const char *code;
 
5111
        const char *alldtmf = "0123456789ABCD*#";
 
5112
        char flag[80];
 
5113
 
 
5114
        ast_str_set(&tmp, 0, "%s", ext);
 
5115
        ext = ast_str_buffer(tmp);
 
5116
        if ((context = strchr(ext, '@'))) {
 
5117
                *context++ = '\0';
 
5118
                tmpptr = strchr(context, '&');
 
5119
        } else {
 
5120
                tmpptr = strchr(ext, '&');
 
5121
        }
 
5122
 
 
5123
        if (tmpptr)
 
5124
                *tmpptr++ = '\0';
 
5125
 
 
5126
        ast_channel_lock(chan);
 
5127
        if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
 
5128
                category = ast_strdupa(category);
 
5129
        }
 
5130
        ast_channel_unlock(chan);
 
5131
 
 
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));
 
5136
        } else {
 
5137
                flag[0] = '\0';
 
5138
        }
 
5139
 
 
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");
 
5144
                ast_free(tmp);
 
5145
                return res;
 
5146
        }
 
5147
        /* Setup pre-file if appropriate */
 
5148
        if (strcmp(vmu->context, "default"))
 
5149
                snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
 
5150
        else
 
5151
                ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
 
5152
 
 
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.
 
5157
        */
 
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);
 
5162
        }
 
5163
        /* Set the path to the tmpfile as
 
5164
                VM_SPOOL_DIR/context/ext/temp
 
5165
           and attempt to create the folder structure.
 
5166
        */
 
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);
 
5170
                ast_free(tmp);
 
5171
                return -1;
 
5172
        }
 
5173
        RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
 
5174
        if (ast_fileexists(tempfile, NULL, NULL) > 0)
 
5175
                ast_copy_string(prefile, tempfile, sizeof(prefile));
 
5176
 
 
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");
 
5180
 
 
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);
 
5186
                                ouseexten = 1;
 
5187
                        }
 
5188
                } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
 
5189
                        strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
 
5190
                        ouseexten = 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);
 
5193
                        ousemacro = 1;
 
5194
                }
 
5195
        }
 
5196
 
 
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);
 
5204
                ausemacro = 1;
 
5205
        }
 
5206
 
 
5207
        if (ast_test_flag(options, OPT_DTMFEXIT)) {
 
5208
                for (code = alldtmf; *code; code++) {
 
5209
                        char e[2] = "";
 
5210
                        e[0] = *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);
 
5213
                }
 
5214
        }
 
5215
 
 
5216
        /* Play the beginning intro if desired */
 
5217
        if (!ast_strlen_zero(prefile)) {
 
5218
#ifdef ODBC_STORAGE
 
5219
                int success = 
 
5220
#endif
 
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);
 
5225
#ifdef ODBC_STORAGE
 
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);
 
5230
                        }
 
5231
#endif
 
5232
                } else {
 
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);
 
5235
                }
 
5236
                DISPOSE(prefile, -1);
 
5237
                if (res < 0) {
 
5238
                        ast_debug(1, "Hang up during prefile playback\n");
 
5239
                        free_user(vmu);
 
5240
                        pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
 
5241
                        ast_free(tmp);
 
5242
                        return -1;
 
5243
                }
 
5244
        }
 
5245
        if (res == '#') {
 
5246
                /* On a '#' we skip the instructions */
 
5247
                ast_set_flag(options, OPT_SILENT);
 
5248
                res = 0;
 
5249
        }
 
5250
        if (!res && !ast_test_flag(options, OPT_SILENT)) {
 
5251
                res = ast_stream_and_wait(chan, INTRO, ecodes);
 
5252
                if (res == '#') {
 
5253
                        ast_set_flag(options, OPT_SILENT);
 
5254
                        res = 0;
 
5255
                }
 
5256
        }
 
5257
        if (res > 0)
 
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 */
 
5261
        if (res == '*') {
 
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));
 
5268
                }
 
5269
                chan->priority = 0;
 
5270
                free_user(vmu);
 
5271
                pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
 
5272
                ast_free(tmp);
 
5273
                return 0;
 
5274
        }
 
5275
 
 
5276
        /* Check for a '0' here */
 
5277
        if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
 
5278
        transfer:
 
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));
 
5286
                        }
 
5287
                        ast_play_and_wait(chan, "transfer");
 
5288
                        chan->priority = 0;
 
5289
                        free_user(vmu);
 
5290
                        pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
 
5291
                }
 
5292
                ast_free(tmp);
 
5293
                return 0;
 
5294
        }
 
5295
 
 
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));
 
5300
                free_user(vmu);
 
5301
                pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
 
5302
                ast_free(tmp);
 
5303
                return res;
 
5304
        }
 
5305
 
 
5306
        if (res < 0) {
 
5307
                free_user(vmu);
 
5308
                pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
 
5309
                ast_free(tmp);
 
5310
                return -1;
 
5311
        }
 
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)) {
 
5315
                msgnum = 0;
 
5316
 
 
5317
#ifdef IMAP_STORAGE
 
5318
                /* Is ext a mailbox? */
 
5319
                /* must open stream for this user to get info! */
 
5320
                res = inboxcount(ext_context, &newmsgs, &oldmsgs);
 
5321
                if (res < 0) {
 
5322
                        ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
 
5323
                        ast_free(tmp);
 
5324
                        return -1;
 
5325
                }
 
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
 
5329
                 * rarely be used.
 
5330
                 */
 
5331
                        if (!(vms = create_vm_state_from_user(vmu))) {
 
5332
                                ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
 
5333
                                ast_free(tmp);
 
5334
                                return -1;
 
5335
                        }
 
5336
                }
 
5337
                vms->newmessages++;
 
5338
                
 
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");
 
5345
 
 
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");
 
5351
                        ast_free(tmp);
 
5352
                        return -1;
 
5353
                }
 
5354
                
 
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");
 
5359
                        ast_free(tmp);
 
5360
                        return -1;
 
5361
                }
 
5362
#else
 
5363
                if (count_messages(vmu, dir) >= vmu->maxmsg) {
 
5364
                        res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
 
5365
                        if (!res)
 
5366
                                res = ast_waitstream(chan, "");
 
5367
                        ast_log(AST_LOG_WARNING, "No more messages possible\n");
 
5368
                        pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
 
5369
                        goto leave_vm_out;
 
5370
                }
 
5371
 
 
5372
#endif
 
5373
                snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
 
5374
                txtdes = mkstemp(tmptxtfile);
 
5375
                chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
 
5376
                if (txtdes < 0) {
 
5377
                        res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
 
5378
                        if (!res)
 
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");
 
5382
                        goto leave_vm_out;
 
5383
                }
 
5384
 
 
5385
                /* Now play the beep once we have the message number for our next message. */
 
5386
                if (res >= 0) {
 
5387
                        /* Unless we're *really* silent, try to send the beep */
 
5388
                        res = ast_stream_and_wait(chan, "beep", "");
 
5389
                }
 
5390
                                
 
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);
 
5397
                }
 
5398
 
 
5399
                /* Store information */
 
5400
                txt = fdopen(txtdes, "w+");
 
5401
                if (txt) {
 
5402
                        get_date(date, sizeof(date));
 
5403
                        fprintf(txt, 
 
5404
                                ";\n"
 
5405
                                "; Message Information file\n"
 
5406
                                ";\n"
 
5407
                                "[message]\n"
 
5408
                                "origmailbox=%s\n"
 
5409
                                "context=%s\n"
 
5410
                                "macrocontext=%s\n"
 
5411
                                "exten=%s\n"
 
5412
                                "priority=%d\n"
 
5413
                                "callerchan=%s\n"
 
5414
                                "callerid=%s\n"
 
5415
                                "origdate=%s\n"
 
5416
                                "origtime=%ld\n"
 
5417
                                "category=%s\n",
 
5418
                                ext,
 
5419
                                chan->context,
 
5420
                                chan->macrocontext, 
 
5421
                                chan->exten,
 
5422
                                chan->priority,
 
5423
                                chan->name,
 
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 : "");
 
5427
                } else
 
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);
 
5430
 
 
5431
                if (txt) {
 
5432
                        fprintf(txt, "flag=%s\n", flag);
 
5433
                        if (duration < vmminsecs) {
 
5434
                                fclose(txt);
 
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);
 
5438
                                unlink(tmptxtfile);
 
5439
                                if (ast_check_realtime("voicemail_data")) {
 
5440
                                        ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
 
5441
                                }
 
5442
                        } else {
 
5443
                                fprintf(txt, "duration=%d\n", duration);
 
5444
                                fclose(txt);
 
5445
                                if (vm_lock_path(dir)) {
 
5446
                                        ast_log(AST_LOG_ERROR, "Couldn't lock directory %s.  Voicemail will be lost.\n", dir);
 
5447
                                        /* Delete files */
 
5448
                                        ast_filedelete(tmptxtfile, NULL);
 
5449
                                        unlink(tmptxtfile);
 
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");
 
5452
                                        unlink(tmptxtfile);
 
5453
                                        ast_unlock_path(dir);
 
5454
                                        if (ast_check_realtime("voicemail_data")) {
 
5455
                                                ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
 
5456
                                        }
 
5457
                                } else {
 
5458
#ifndef IMAP_STORAGE
 
5459
                                        msgnum = last_message_index(vmu, dir) + 1;
 
5460
#endif
 
5461
                                        make_file(fn, sizeof(fn), dir, msgnum);
 
5462
 
 
5463
                                        /* assign a variable with the name of the voicemail file */ 
 
5464
#ifndef IMAP_STORAGE
 
5465
                                        pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
 
5466
#else
 
5467
                                        pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
 
5468
#endif
 
5469
 
 
5470
                                        snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
 
5471
                                        ast_filerename(tmptxtfile, fn, NULL);
 
5472
                                        rename(tmptxtfile, txtfile);
 
5473
 
 
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));
 
5478
 
 
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);
 
5483
                                        }
 
5484
                                        /* We must store the file first, before copying the message, because
 
5485
                                         * ODBC storage does the entire copy with SQL.
 
5486
                                         */
 
5487
                                        if (ast_fileexists(fn, NULL, NULL) > 0) {
 
5488
                                                STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
 
5489
                                        }
 
5490
 
 
5491
                                        /* Are there to be more recipients of this message? */
 
5492
                                        while (tmpptr) {
 
5493
                                                struct ast_vm_user recipu, *recip;
 
5494
                                                char *exten, *cntx;
 
5495
                                        
 
5496
                                                exten = strsep(&tmpptr, "&");
 
5497
                                                cntx = strchr(exten, '@');
 
5498
                                                if (cntx) {
 
5499
                                                        *cntx = '\0';
 
5500
                                                        cntx++;
 
5501
                                                }
 
5502
                                                if ((recip = find_user(&recipu, cntx, exten))) {
 
5503
                                                        copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
 
5504
                                                        free_user(recip);
 
5505
                                                }
 
5506
                                        }
 
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! */
 
5510
                                                char sfn[PATH_MAX];
 
5511
                                                char dfn[PATH_MAX];
 
5512
                                                int x;
 
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);
 
5520
                                        }
 
5521
#endif
 
5522
                                        /* Notification needs to happen after the copy, though. */
 
5523
                                        if (ast_fileexists(fn, NULL, NULL)) {
 
5524
#ifdef IMAP_STORAGE
 
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);
 
5526
#else
 
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);
 
5528
#endif
 
5529
                                        }
 
5530
 
 
5531
                                        /* Disposal needs to happen after the optional move and copy */
 
5532
                                        if (ast_fileexists(fn, NULL, NULL)) {
 
5533
                                                DISPOSE(dir, msgnum);
 
5534
                                        }
 
5535
                                }
 
5536
                        }
 
5537
                }
 
5538
                if (res == '0') {
 
5539
                        goto transfer;
 
5540
                } else if (res > 0)
 
5541
                        res = 0;
 
5542
 
 
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");
 
5546
                else
 
5547
                        pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
 
5548
        } else
 
5549
                ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
 
5550
leave_vm_out:
 
5551
        free_user(vmu);
 
5552
 
 
5553
#ifdef IMAP_STORAGE
 
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);
 
5561
                } else 
 
5562
#endif
 
5563
                        mail_expunge(vms->mailstream);
 
5564
                ast_mutex_unlock(&vms->lock);
 
5565
        }
 
5566
#endif
 
5567
 
 
5568
        ast_free(tmp);
 
5569
        return res;
 
5570
}
 
5571
 
 
5572
static int say_and_wait(struct ast_channel *chan, int num, const char *language)
 
5573
{
 
5574
        int d;
 
5575
        d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
 
5576
        return d;
 
5577
}
 
5578
 
 
5579
static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
 
5580
{
 
5581
#ifdef IMAP_STORAGE
 
5582
        /* we must use mbox(x) folder names, and copy the message there */
 
5583
        /* simple. huh? */
 
5584
        char sequence[10];
 
5585
        char mailbox[256];
 
5586
        int res;
 
5587
 
 
5588
        /* get the real IMAP message number for this message */
 
5589
        snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
 
5590
        
 
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");
 
5600
        }
 
5601
        if (!strcasecmp(mbox(NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
 
5602
                ast_mutex_unlock(&vms->lock);
 
5603
                return 0;
 
5604
        }
 
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");
 
5610
        else
 
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);
 
5614
        return res;
 
5615
#else
 
5616
        char *dir = vms->curdir;
 
5617
        char *username = vms->username;
 
5618
        char *context = vmu->context;
 
5619
        char sfn[PATH_MAX];
 
5620
        char dfn[PATH_MAX];
 
5621
        char ddir[PATH_MAX];
 
5622
        const char *dbox = mbox(box);
 
5623
        int x, i;
 
5624
        create_dirpath(ddir, sizeof(ddir), context, username, dbox);
 
5625
 
 
5626
        if (vm_lock_path(ddir))
 
5627
                return ERROR_LOCK_PATH;
 
5628
 
 
5629
        x = last_message_index(vmu, ddir) + 1;
 
5630
 
 
5631
        if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
 
5632
                x--;
 
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);
 
5639
                        } else
 
5640
                                break;
 
5641
                }
 
5642
        } else {
 
5643
                if (x >= vmu->maxmsg) {
 
5644
                        ast_unlock_path(ddir);
 
5645
                        return -1;
 
5646
                }
 
5647
        }
 
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);
 
5652
        }
 
5653
        ast_unlock_path(ddir);
 
5654
#endif
 
5655
        return 0;
 
5656
}
 
5657
 
 
5658
static int adsi_logo(unsigned char *buf)
 
5659
{
 
5660
        int bytes = 0;
 
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.", "");
 
5663
        return bytes;
 
5664
}
 
5665
 
 
5666
static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
 
5667
{
 
5668
        unsigned char buf[256];
 
5669
        int bytes=0;
 
5670
        int x;
 
5671
        char num[5];
 
5672
 
 
5673
        *useadsi = 0;
 
5674
        bytes += ast_adsi_data_mode(buf + bytes);
 
5675
        ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
 
5676
 
 
5677
        bytes = 0;
 
5678
        bytes += adsi_logo(buf);
 
5679
        bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
 
5680
#ifdef DISPLAY
 
5681
        bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   .", "");
 
5682
#endif
 
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);
 
5686
 
 
5687
        if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
 
5688
                bytes = 0;
 
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);
 
5694
                return 0;
 
5695
        }
 
5696
 
 
5697
#ifdef DISPLAY
 
5698
        /* Add a dot */
 
5699
        bytes = 0;
 
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);
 
5705
#endif
 
5706
        bytes = 0;
 
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);
 
5714
 
 
5715
#ifdef DISPLAY
 
5716
        /* Add another dot */
 
5717
        bytes = 0;
 
5718
        bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, "   ...", "");
 
5719
        bytes += ast_adsi_voice_mode(buf + bytes, 0);
 
5720
 
 
5721
        bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
 
5722
        ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
 
5723
#endif
 
5724
 
 
5725
        bytes = 0;
 
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);
 
5734
 
 
5735
#ifdef DISPLAY
 
5736
        /* Add another dot */
 
5737
        bytes = 0;
 
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);
 
5741
#endif
 
5742
 
 
5743
        bytes = 0;
 
5744
        for (x=0;x<5;x++) {
 
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);
 
5747
        }
 
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);
 
5750
 
 
5751
#ifdef DISPLAY
 
5752
        /* Add another dot */
 
5753
        bytes = 0;
 
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);
 
5757
#endif
 
5758
 
 
5759
        if (ast_adsi_end_download(chan)) {
 
5760
                bytes = 0;
 
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);
 
5766
                return 0;
 
5767
        }
 
5768
        bytes = 0;
 
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);
 
5772
 
 
5773
        ast_debug(1, "Done downloading scripts...\n");
 
5774
 
 
5775
#ifdef DISPLAY
 
5776
        /* Add last dot */
 
5777
        bytes = 0;
 
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);
 
5780
#endif
 
5781
        ast_debug(1, "Restarting session...\n");
 
5782
 
 
5783
        bytes = 0;
 
5784
        /* Load the session now */
 
5785
        if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
 
5786
                *useadsi = 1;
 
5787
                bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
 
5788
        } else
 
5789
                bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
 
5790
 
 
5791
        ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
 
5792
        return 0;
 
5793
}
 
5794
 
 
5795
static void adsi_begin(struct ast_channel *chan, int *useadsi)
 
5796
{
 
5797
        int x;
 
5798
        if (!ast_adsi_available(chan))
 
5799
                return;
 
5800
        x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
 
5801
        if (x < 0)
 
5802
                return;
 
5803
        if (!x) {
 
5804
                if (adsi_load_vmail(chan, useadsi)) {
 
5805
                        ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
 
5806
                        return;
 
5807
                }
 
5808
        } else
 
5809
                *useadsi = 1;
 
5810
}
 
5811
 
 
5812
static void adsi_login(struct ast_channel *chan)
 
5813
{
 
5814
        unsigned char buf[256];
 
5815
        int bytes=0;
 
5816
        unsigned char keys[8];
 
5817
        int x;
 
5818
        if (!ast_adsi_available(chan))
 
5819
                return;
 
5820
 
 
5821
        for (x=0;x<8;x++)
 
5822
                keys[x] = 0;
 
5823
        /* Set one key for next */
 
5824
        keys[3] = ADSI_KEY_APPS + 3;
 
5825
 
 
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);
 
5836
}
 
5837
 
 
5838
static void adsi_password(struct ast_channel *chan)
 
5839
{
 
5840
        unsigned char buf[256];
 
5841
        int bytes=0;
 
5842
        unsigned char keys[8];
 
5843
        int x;
 
5844
        if (!ast_adsi_available(chan))
 
5845
                return;
 
5846
 
 
5847
        for (x=0;x<8;x++)
 
5848
                keys[x] = 0;
 
5849
        /* Set one key for next */
 
5850
        keys[3] = ADSI_KEY_APPS + 3;
 
5851
 
 
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);
 
5858
}
 
5859
 
 
5860
static void adsi_folders(struct ast_channel *chan, int start, char *label)
 
5861
{
 
5862
        unsigned char buf[256];
 
5863
        int bytes=0;
 
5864
        unsigned char keys[8];
 
5865
        int x,y;
 
5866
 
 
5867
        if (!ast_adsi_available(chan))
 
5868
                return;
 
5869
 
 
5870
        for (x=0;x<5;x++) {
 
5871
                y = ADSI_KEY_APPS + 12 + start + x;
 
5872
                if (y > ADSI_KEY_APPS + 12 + 4)
 
5873
                        y = 0;
 
5874
                keys[x] = ADSI_KEY_SKT | y;
 
5875
        }
 
5876
        keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
 
5877
        keys[6] = 0;
 
5878
        keys[7] = 0;
 
5879
 
 
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);
 
5885
 
 
5886
        ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
 
5887
}
 
5888
 
 
5889
static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
 
5890
{
 
5891
        int bytes=0;
 
5892
        unsigned char buf[256]; 
 
5893
        char buf1[256], buf2[256];
 
5894
        char fn2[PATH_MAX];
 
5895
 
 
5896
        char cid[256]="";
 
5897
        char *val;
 
5898
        char *name, *num;
 
5899
        char datetime[21]="";
 
5900
        FILE *f;
 
5901
 
 
5902
        unsigned char keys[8];
 
5903
 
 
5904
        int x;
 
5905
 
 
5906
        if (!ast_adsi_available(chan))
 
5907
                return;
 
5908
 
 
5909
        /* Retrieve important info */
 
5910
        snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
 
5911
        f = fopen(fn2, "r");
 
5912
        if (f) {
 
5913
                while (!feof(f)) {      
 
5914
                        if (!fgets((char *)buf, sizeof(buf), f)) {
 
5915
                                continue;
 
5916
                        }
 
5917
                        if (!feof(f)) {
 
5918
                                char *stringp=NULL;
 
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));
 
5927
                                }
 
5928
                        }
 
5929
                }
 
5930
                fclose(f);
 
5931
        }
 
5932
        /* New meaning for keys */
 
5933
        for (x=0;x<5;x++)
 
5934
                keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
 
5935
        keys[6] = 0x0;
 
5936
        keys[7] = 0x0;
 
5937
 
 
5938
        if (!vms->curmsg) {
 
5939
                /* No prev key, provide "Folder" instead */
 
5940
                keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
 
5941
        }
 
5942
        if (vms->curmsg >= vms->lastmsg) {
 
5943
                /* If last message ... */
 
5944
                if (vms->curmsg) {
 
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);
 
5948
 
 
5949
                } else {
 
5950
                        /* Otherwise if only message, leave blank */
 
5951
                        keys[3] = 1;
 
5952
                }
 
5953
        }
 
5954
 
 
5955
        if (!ast_strlen_zero(cid)) {
 
5956
                ast_callerid_parse(cid, &name, &num);
 
5957
                if (!name)
 
5958
                        name = num;
 
5959
        } else
 
5960
                name = "Unknown Caller";
 
5961
 
 
5962
        /* If deleted, show "undeleted" */
 
5963
 
 
5964
        if (vms->deleted[vms->curmsg])
 
5965
                keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
 
5966
 
 
5967
        /* Except "Exit" */
 
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);
 
5972
 
 
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);
 
5980
 
 
5981
        ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
 
5982
}
 
5983
 
 
5984
static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
 
5985
{
 
5986
        int bytes=0;
 
5987
        unsigned char buf[256];
 
5988
        unsigned char keys[8];
 
5989
 
 
5990
        int x;
 
5991
 
 
5992
        if (!ast_adsi_available(chan))
 
5993
                return;
 
5994
 
 
5995
        /* New meaning for keys */
 
5996
        for (x=0;x<5;x++)
 
5997
                keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
 
5998
 
 
5999
        keys[6] = 0x0;
 
6000
        keys[7] = 0x0;
 
6001
 
 
6002
        if (!vms->curmsg) {
 
6003
                /* No prev key, provide "Folder" instead */
 
6004
                keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
 
6005
        }
 
6006
        if (vms->curmsg >= vms->lastmsg) {
 
6007
                /* If last message ... */
 
6008
                if (vms->curmsg) {
 
6009
                        /* but not only message, provide "Folder" instead */
 
6010
                        keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
 
6011
                } else {
 
6012
                        /* Otherwise if only message, leave blank */
 
6013
                        keys[3] = 1;
 
6014
                }
 
6015
        }
 
6016
 
 
6017
        /* If deleted, show "undeleted" */
 
6018
        if (vms->deleted[vms->curmsg]) 
 
6019
                keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
 
6020
 
 
6021
        /* Except "Exit" */
 
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);
 
6025
 
 
6026
        ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
 
6027
}
 
6028
 
 
6029
static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
 
6030
{
 
6031
        unsigned char buf[256] = "";
 
6032
        char buf1[256] = "", buf2[256] = "";
 
6033
        int bytes=0;
 
6034
        unsigned char keys[8];
 
6035
        int x;
 
6036
 
 
6037
        char *newm = (vms->newmessages == 1) ? "message" : "messages";
 
6038
        char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
 
6039
        if (!ast_adsi_available(chan))
 
6040
                return;
 
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);
 
6046
                } else {
 
6047
                        snprintf(buf2, sizeof(buf2), "%s.", newm);
 
6048
                }
 
6049
        } else if (vms->oldmessages) {
 
6050
                snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
 
6051
                snprintf(buf2, sizeof(buf2), "%s.", oldm);
 
6052
        } else {
 
6053
                strcpy(buf1, "You have no messages.");
 
6054
                buf2[0] = ' ';
 
6055
                buf2[1] = '\0';
 
6056
        }
 
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);
 
6060
 
 
6061
        for (x=0;x<6;x++)
 
6062
                keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
 
6063
        keys[6] = 0;
 
6064
        keys[7] = 0;
 
6065
 
 
6066
        /* Don't let them listen if there are none */
 
6067
        if (vms->lastmsg < 0)
 
6068
                keys[0] = 1;
 
6069
        bytes += ast_adsi_set_keys(buf + bytes, keys);
 
6070
 
 
6071
        bytes += ast_adsi_voice_mode(buf + bytes, 0);
 
6072
 
 
6073
        ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
 
6074
}
 
6075
 
 
6076
static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
 
6077
{
 
6078
        unsigned char buf[256] = "";
 
6079
        char buf1[256] = "", buf2[256] = "";
 
6080
        int bytes=0;
 
6081
        unsigned char keys[8];
 
6082
        int x;
 
6083
 
 
6084
        char *mess = (vms->lastmsg == 0) ? "message" : "messages";
 
6085
 
 
6086
        if (!ast_adsi_available(chan))
 
6087
                return;
 
6088
 
 
6089
        /* Original command keys */
 
6090
        for (x=0;x<6;x++)
 
6091
                keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
 
6092
 
 
6093
        keys[6] = 0;
 
6094
        keys[7] = 0;
 
6095
 
 
6096
        if ((vms->lastmsg + 1) < 1)
 
6097
                keys[0] = 0;
 
6098
 
 
6099
        snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
 
6100
                strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
 
6101
 
 
6102
        if (vms->lastmsg + 1)
 
6103
                snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
 
6104
        else
 
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);
 
6111
 
 
6112
        bytes += ast_adsi_voice_mode(buf + bytes, 0);
 
6113
 
 
6114
        ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
 
6115
        
 
6116
}
 
6117
 
 
6118
/*
 
6119
static void adsi_clear(struct ast_channel *chan)
 
6120
{
 
6121
        char buf[256];
 
6122
        int bytes=0;
 
6123
        if (!ast_adsi_available(chan))
 
6124
                return;
 
6125
        bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
 
6126
        bytes += ast_adsi_voice_mode(buf + bytes, 0);
 
6127
 
 
6128
        ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
 
6129
}
 
6130
*/
 
6131
 
 
6132
static void adsi_goodbye(struct ast_channel *chan)
 
6133
{
 
6134
        unsigned char buf[256];
 
6135
        int bytes=0;
 
6136
 
 
6137
        if (!ast_adsi_available(chan))
 
6138
                return;
 
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);
 
6144
 
 
6145
        ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
 
6146
}
 
6147
 
 
6148
/*!\brief get_folder: Folder menu
 
6149
 *      Plays "press 1 for INBOX messages" etc.
 
6150
 *      Should possibly be internationalized
 
6151
 */
 
6152
static int get_folder(struct ast_channel *chan, int start)
 
6153
{
 
6154
        int x;
 
6155
        int d;
 
6156
        char fn[PATH_MAX];
 
6157
        d = ast_play_and_wait(chan, "vm-press");        /* "Press" */
 
6158
        if (d)
 
6159
                return d;
 
6160
        for (x = start; x< 5; x++) {    /* For all folders */
 
6161
                if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
 
6162
                        return d;
 
6163
                d = ast_play_and_wait(chan, "vm-for");  /* "for" */
 
6164
                if (d)
 
6165
                        return d;
 
6166
                snprintf(fn, sizeof(fn), "vm-%s", mbox(x));     /* Folder name */
 
6167
                d = vm_play_folder_name(chan, fn);
 
6168
                if (d)
 
6169
                        return d;
 
6170
                d = ast_waitfordigit(chan, 500);
 
6171
                if (d)
 
6172
                        return d;
 
6173
        }
 
6174
        d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
 
6175
        if (d)
 
6176
                return d;
 
6177
        d = ast_waitfordigit(chan, 4000);
 
6178
        return d;
 
6179
}
 
6180
 
 
6181
/*!
 
6182
 * \brief plays a prompt and waits for a keypress.
 
6183
 * \param chan
 
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.
 
6186
 *
 
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.
 
6190
 * 
 
6191
 * \return zero on success, or -1 on error.
 
6192
 */
 
6193
static int get_folder2(struct ast_channel *chan, char *fn, int start)
 
6194
{
 
6195
        int res = 0;
 
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);
 
6200
        }
 
6201
        return res;
 
6202
}
 
6203
 
 
6204
/*!
 
6205
 * \brief presents the option to prepend to an existing message when forwarding it.
 
6206
 * \param chan
 
6207
 * \param vmu
 
6208
 * \param curdir
 
6209
 * \param curmsg
 
6210
 * \param vmfmts
 
6211
 * \param context
 
6212
 * \param record_gain
 
6213
 * \param duration
 
6214
 * \param vms
 
6215
 *
 
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.
 
6217
 *
 
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.
 
6220
 */
 
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)
 
6223
{
 
6224
#ifdef IMAP_STORAGE
 
6225
        int res;
 
6226
#endif
 
6227
        int cmd = 0;
 
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;
 
6235
#endif
 
6236
        const char *duration_str;
 
6237
 
 
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);
 
6244
 
 
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);
 
6247
        } else {
 
6248
                *duration = 0;
 
6249
        }
 
6250
 
 
6251
        while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
 
6252
                if (cmd)
 
6253
                        retries = 0;
 
6254
                switch (cmd) {
 
6255
                case '1': 
 
6256
 
 
6257
#ifdef IMAP_STORAGE
 
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);
 
6264
                        cmd = 't';
 
6265
#else
 
6266
 
 
6267
                        /* prepend a message to the current message, update the metadata and return */
 
6268
 
 
6269
                        make_file(msgfile, sizeof(msgfile), curdir, curmsg);
 
6270
                        strcpy(textfile, msgfile);
 
6271
                        strncat(textfile, ".txt", sizeof(textfile) - 1);
 
6272
                        *duration = 0;
 
6273
 
 
6274
                        /* if we can't read the message metadata, stop now */
 
6275
                        if (!msg_cfg) {
 
6276
                                cmd = 0;
 
6277
                                break;
 
6278
                        }
 
6279
                        
 
6280
                        /* Back up the original file, so we can retry the prepend */
 
6281
                        if (already_recorded)
 
6282
                                ast_filecopy(backup, msgfile, NULL);
 
6283
                        else
 
6284
                                ast_filecopy(msgfile, backup, NULL);
 
6285
                        already_recorded = 1;
 
6286
 
 
6287
                        if (record_gain)
 
6288
                                ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
 
6289
 
 
6290
                        cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, 1, silencethreshold, maxsilence);
 
6291
                        if (record_gain)
 
6292
                                ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
 
6293
 
 
6294
                        
 
6295
                        if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
 
6296
                                *duration = atoi(duration_str);
 
6297
 
 
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];
 
6302
 
 
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");
 
6308
                                }
 
6309
                        }
 
6310
 
 
6311
#endif
 
6312
                        break;
 
6313
                case '2': 
 
6314
                        /* NULL out introfile so we know there is no intro! */
 
6315
#ifdef IMAP_STORAGE
 
6316
                        *vms->introfn = '\0';
 
6317
#endif
 
6318
                        cmd = 't';
 
6319
                        break;
 
6320
                case '*':
 
6321
                        cmd = '*';
 
6322
                        break;
 
6323
                default: 
 
6324
                        cmd = ast_play_and_wait(chan,"vm-forwardoptions");
 
6325
                                /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
 
6326
                        if (!cmd)
 
6327
                                cmd = ast_play_and_wait(chan,"vm-starmain");
 
6328
                                /* "press star to return to the main menu" */
 
6329
                        if (!cmd)
 
6330
                                cmd = ast_waitfordigit(chan,6000);
 
6331
                        if (!cmd)
 
6332
                                retries++;
 
6333
                        if (retries > 3)
 
6334
                                cmd = 't';
 
6335
                }
 
6336
        }
 
6337
 
 
6338
        if (msg_cfg)
 
6339
                ast_config_destroy(msg_cfg);
 
6340
        if (already_recorded)
 
6341
                ast_filedelete(backup, NULL);
 
6342
        if (prepend_duration)
 
6343
                *duration = prepend_duration;
 
6344
 
 
6345
        if (cmd == 't' || cmd == 'S')
 
6346
                cmd = 0;
 
6347
        return cmd;
 
6348
}
 
6349
 
 
6350
static void queue_mwi_event(const char *box, int urgent, int new, int old)
 
6351
{
 
6352
        struct ast_event *event;
 
6353
        char *mailbox, *context;
 
6354
 
 
6355
        /* Strip off @default */
 
6356
        context = mailbox = ast_strdupa(box);
 
6357
        strsep(&context, "@");
 
6358
        if (ast_strlen_zero(context))
 
6359
                context = "default";
 
6360
 
 
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))) {
 
6367
                return;
 
6368
        }
 
6369
 
 
6370
        ast_event_queue_and_cache(event);
 
6371
}
 
6372
 
 
6373
/*!
 
6374
 * \brief Sends email notification that a user has a new voicemail waiting for them.
 
6375
 * \param chan
 
6376
 * \param vmu
 
6377
 * \param vms
 
6378
 * \param msgnum
 
6379
 * \param duration
 
6380
 * \param fmt
 
6381
 * \param cidnum The Caller ID phone number value.
 
6382
 * \param cidname The Caller ID name value.
 
6383
 *
 
6384
 * \return zero on success, -1 on error.
 
6385
 */
 
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)
 
6387
{
 
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;
 
6392
 
 
6393
        ast_channel_lock(chan);
 
6394
        if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
 
6395
                category = ast_strdupa(category);
 
6396
        }
 
6397
        ast_channel_unlock(chan);
 
6398
 
 
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);
 
6402
 
 
6403
        if (!ast_strlen_zero(vmu->attachfmt)) {
 
6404
                if (strstr(fmt, vmu->attachfmt))
 
6405
                        fmt = vmu->attachfmt;
 
6406
                else
 
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);
 
6408
        }
 
6409
 
 
6410
        /* Attach only the first format */
 
6411
        fmt = ast_strdupa(fmt);
 
6412
        stringp = fmt;
 
6413
        strsep(&stringp, "|");
 
6414
 
 
6415
        if (!ast_strlen_zero(vmu->serveremail))
 
6416
                myserveremail = vmu->serveremail;
 
6417
 
 
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);
 
6422
 
 
6423
                if (attach_user_voicemail)
 
6424
                        RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
 
6425
 
 
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);
 
6428
 
 
6429
                if (attach_user_voicemail)
 
6430
                        DISPOSE(todir, msgnum);
 
6431
        }
 
6432
 
 
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);
 
6435
        }
 
6436
 
 
6437
        if (ast_test_flag(vmu, VM_DELETE))
 
6438
                DELETE(todir, msgnum, fn, vmu);
 
6439
 
 
6440
        /* Leave voicemail for someone */
 
6441
        if (ast_app_has_voicemail(ext_context, NULL)) 
 
6442
                ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
 
6443
 
 
6444
        queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
 
6445
 
 
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);
 
6448
 
 
6449
#ifdef IMAP_STORAGE
 
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 */
 
6454
        }
 
6455
#endif
 
6456
 
 
6457
        return 0;
 
6458
}
 
6459
 
 
6460
/*!
 
6461
 * \brief Sends a voicemail message to a mailbox recipient.
 
6462
 * \param ast_channel
 
6463
 * \param context
 
6464
 * \param vms
 
6465
 * \param sender
 
6466
 * \param fmt
 
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 
 
6471
 *
 
6472
 * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
 
6473
 * 
 
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.
 
6477
 *
 
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.
 
6483
 *
 
6484
 * \return zero on success, -1 on error.
 
6485
 */
 
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)
 
6487
{
 
6488
#ifdef IMAP_STORAGE
 
6489
        int todircount=0;
 
6490
        struct vm_state *dstvms;
 
6491
#endif
 
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);
 
6498
        char *stringp;
 
6499
        const char *s;
 
6500
        int saved_messages = 0, found = 0;
 
6501
        int valid_extensions = 0;
 
6502
        char *dir;
 
6503
        int curmsg;
 
6504
        char urgent_str[7] = "";
 
6505
        char tmptxtfile[PATH_MAX];
 
6506
 
 
6507
        if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
 
6508
                ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
 
6509
        }
 
6510
 
 
6511
        if (vms == NULL) return -1;
 
6512
        dir = vms->curdir;
 
6513
        curmsg = vms->curmsg;
 
6514
 
 
6515
        tmptxtfile[0] = '\0';
 
6516
        while (!res && !valid_extensions) {
 
6517
                int use_directory = 0;
 
6518
                if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
 
6519
                        int done = 0;
 
6520
                        int retries = 0;
 
6521
                        cmd=0;
 
6522
                        while ((cmd >= 0) && !done ){
 
6523
                                if (cmd)
 
6524
                                        retries = 0;
 
6525
                                switch (cmd) {
 
6526
                                case '1': 
 
6527
                                        use_directory = 0;
 
6528
                                        done = 1;
 
6529
                                        break;
 
6530
                                case '2': 
 
6531
                                        use_directory = 1;
 
6532
                                        done=1;
 
6533
                                        break;
 
6534
                                case '*': 
 
6535
                                        cmd = 't';
 
6536
                                        done = 1;
 
6537
                                        break;
 
6538
                                default: 
 
6539
                                        /* Press 1 to enter an extension press 2 to use the directory */
 
6540
                                        cmd = ast_play_and_wait(chan,"vm-forward");
 
6541
                                        if (!cmd)
 
6542
                                                cmd = ast_waitfordigit(chan,3000);
 
6543
                                        if (!cmd)
 
6544
                                                retries++;
 
6545
                                        if (retries > 3) {
 
6546
                                                cmd = 't';
 
6547
                                                done = 1;
 
6548
                                        }
 
6549
                                        
 
6550
                                }
 
6551
                        }
 
6552
                        if (cmd < 0 || cmd == 't')
 
6553
                                break;
 
6554
                }
 
6555
                
 
6556
                if (use_directory) {
 
6557
                        /* use app_directory */
 
6558
                        
 
6559
                        char old_context[sizeof(chan->context)];
 
6560
                        char old_exten[sizeof(chan->exten)];
 
6561
                        int old_priority;
 
6562
                        struct ast_app* directory_app;
 
6563
 
 
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;
 
6571
                                
 
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);
 
6575
                                
 
6576
                                ast_copy_string(username, chan->exten, sizeof(username));
 
6577
                                
 
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;
 
6582
                        } else {
 
6583
                                ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
 
6584
                                ast_clear_flag((&globalflags), VM_DIRECFORWARD);
 
6585
                        }
 
6586
                } else {
 
6587
                        /* Ask for an extension */
 
6588
                        res = ast_streamfile(chan, "vm-extension", chan->language);     /* "extension" */
 
6589
                        if (res)
 
6590
                                break;
 
6591
                        if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
 
6592
                                break;
 
6593
                }
 
6594
                
 
6595
                /* start all over if no username */
 
6596
                if (ast_strlen_zero(username))
 
6597
                        continue;
 
6598
                stringp = username;
 
6599
                s = strsep(&stringp, "*");
 
6600
                /* start optimistic */
 
6601
                valid_extensions = 1;
 
6602
                while (s) {
 
6603
                        if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
 
6604
                                AST_LIST_INSERT_HEAD(&extensions, receiver, list);
 
6605
                                found++;
 
6606
                        } else {
 
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
 
6610
                                 * several more. XXX
 
6611
                                 */
 
6612
                                while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
 
6613
                                        free_user(receiver);
 
6614
                                }
 
6615
                                ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
 
6616
                                valid_extensions = 0;
 
6617
                                break;
 
6618
                        }
 
6619
 
 
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);
 
6625
                                if (res) {
 
6626
                                        DISPOSE(fn, -1);
 
6627
                                        return res;
 
6628
                                }
 
6629
                        } else {
 
6630
                                res = ast_say_digit_str(chan, s, ecodes, chan->language);
 
6631
                        }
 
6632
                        DISPOSE(fn, -1);
 
6633
 
 
6634
                        s = strsep(&stringp, "*");
 
6635
                }
 
6636
                /* break from the loop of reading the extensions */
 
6637
                if (valid_extensions)
 
6638
                        break;
 
6639
                /* "I am sorry, that's not a valid extension.  Please try again." */
 
6640
                res = ast_play_and_wait(chan, "pbx-invalid");
 
6641
        }
 
6642
        /* check if we're clear to proceed */
 
6643
        if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
 
6644
                return res;
 
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);
 
6649
 
 
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);
 
6654
        } else {
 
6655
                /* Forward VoiceMail */
 
6656
                long duration = 0;
 
6657
                struct vm_state vmstmp;
 
6658
                memcpy(&vmstmp, vms, sizeof(vmstmp));
 
6659
 
 
6660
                RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
 
6661
 
 
6662
                cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
 
6663
                if (!cmd) {
 
6664
                        AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
 
6665
#ifdef IMAP_STORAGE
 
6666
                                int attach_user_voicemail;
 
6667
                                char *myserveremail = serveremail;
 
6668
                                
 
6669
                                /* get destination mailbox */
 
6670
                                dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
 
6671
                                if (!dstvms) {
 
6672
                                        dstvms = create_vm_state_from_user(vmtmp);
 
6673
                                }
 
6674
                                if (dstvms) {
 
6675
                                        init_mailstream(dstvms, 0);
 
6676
                                        if (!dstvms->mailstream) {
 
6677
                                                ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
 
6678
                                        } else {
 
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); 
 
6681
                                        }
 
6682
                                } else {
 
6683
                                        ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
 
6684
                                }
 
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);
 
6690
#else
 
6691
                                copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
 
6692
#endif
 
6693
                                saved_messages++;
 
6694
                                AST_LIST_REMOVE_CURRENT(list);
 
6695
                                free_user(vmtmp);
 
6696
                                if (res)
 
6697
                                        break;
 
6698
                        }
 
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");
 
6705
                                else
 
6706
                                        res = ast_play_and_wait(chan, "vm-messages");
 
6707
                                if (!res)
 
6708
                                        res = ast_play_and_wait(chan, "vm-saved"); */
 
6709
#ifdef IMAP_STORAGE
 
6710
                                /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
 
6711
                                if (ast_strlen_zero(vmstmp.introfn))
 
6712
#endif
 
6713
                                res = ast_play_and_wait(chan, "vm-msgsaved");
 
6714
                        }       
 
6715
                }
 
6716
                DISPOSE(dir, curmsg);
 
6717
        }
 
6718
 
 
6719
        /* If anything failed above, we still have this list to free */
 
6720
        while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
 
6721
                free_user(vmtmp);
 
6722
        return res ? res : cmd;
 
6723
}
 
6724
 
 
6725
static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
 
6726
{
 
6727
        int res;
 
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); 
 
6730
        return res;
 
6731
}
 
6732
 
 
6733
static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file) 
 
6734
{
 
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);
 
6736
}
 
6737
 
 
6738
static int play_message_category(struct ast_channel *chan, const char *category)
 
6739
{
 
6740
        int res = 0;
 
6741
 
 
6742
        if (!ast_strlen_zero(category))
 
6743
                res = ast_play_and_wait(chan, category);
 
6744
 
 
6745
        if (res) {
 
6746
                ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
 
6747
                res = 0;
 
6748
        }
 
6749
 
 
6750
        return res;
 
6751
}
 
6752
 
 
6753
static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
 
6754
{
 
6755
        int res = 0;
 
6756
        struct vm_zone *the_zone = NULL;
 
6757
        time_t t;
 
6758
 
 
6759
        if (ast_get_time_t(origtime, &t, 0, NULL)) {
 
6760
                ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
 
6761
                return 0;
 
6762
        }
 
6763
 
 
6764
        /* Does this user have a timezone specified? */
 
6765
        if (!ast_strlen_zero(vmu->zonetag)) {
 
6766
                /* Find the zone in the list */
 
6767
                struct vm_zone *z;
 
6768
                AST_LIST_LOCK(&zones);
 
6769
                AST_LIST_TRAVERSE(&zones, z, list) {
 
6770
                        if (!strcmp(z->name, vmu->zonetag)) {
 
6771
                                the_zone = z;
 
6772
                                break;
 
6773
                        }
 
6774
                }
 
6775
                AST_LIST_UNLOCK(&zones);
 
6776
        }
 
6777
 
 
6778
/* No internal variable parsing for now, so we'll comment it out for the time being */
 
6779
#if 0
 
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);
 
6784
 
 
6785
        /* Day difference */
 
6786
        if (time_now.tm_year == time_then.tm_year)
 
6787
                snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
 
6788
        else
 
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);
 
6791
 
 
6792
        /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
 
6793
#endif
 
6794
        if (the_zone) {
 
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);
 
6814
        } else {
 
6815
                res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
 
6816
        }
 
6817
#if 0
 
6818
        pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
 
6819
#endif
 
6820
        return res;
 
6821
}
 
6822
 
 
6823
 
 
6824
 
 
6825
static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
 
6826
{
 
6827
        int res = 0;
 
6828
        int i;
 
6829
        char *callerid, *name;
 
6830
        char prefile[PATH_MAX] = "";
 
6831
        
 
6832
 
 
6833
        /* If voicemail cid is not enabled, or we didn't get cid or context from
 
6834
         * the attribute file, leave now.
 
6835
         *
 
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.
 
6839
         */
 
6840
        if ((cid == NULL)||(context == NULL))
 
6841
                return res;
 
6842
 
 
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))
 
6852
                                break;
 
6853
                }
 
6854
                if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
 
6855
                        if (!res) {
 
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);
 
6861
                                                if (!callback)
 
6862
                                                        res = wait_file2(chan, vms, "vm-from");
 
6863
                                                res = ast_stream_and_wait(chan, prefile, "");
 
6864
                                        } else {
 
6865
                                                ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
 
6866
                                                /* Say "from extension" as one saying to sound smoother */
 
6867
                                                if (!callback)
 
6868
                                                        res = wait_file2(chan, vms, "vm-from-extension");
 
6869
                                                res = ast_say_digit_str(chan, callerid, "", chan->language);
 
6870
                                        }
 
6871
                                }
 
6872
                        }
 
6873
                } else if (!res) {
 
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? */
 
6876
                        if (!callback)
 
6877
                                res = wait_file2(chan, vms, "vm-from-phonenumber");
 
6878
                        res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
 
6879
                }
 
6880
        } else {
 
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");
 
6885
        }
 
6886
        return res;
 
6887
}
 
6888
 
 
6889
static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
 
6890
{
 
6891
        int res = 0;
 
6892
        int durationm;
 
6893
        int durations;
 
6894
        /* Verify that we have a duration for the message */
 
6895
        if (duration == NULL)
 
6896
                return res;
 
6897
 
 
6898
        /* Convert from seconds to minutes */
 
6899
        durations=atoi(duration);
 
6900
        durationm=(durations / 60);
 
6901
 
 
6902
        ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
 
6903
 
 
6904
        if ((!res) && (durationm >= minduration)) {
 
6905
                res = wait_file2(chan, vms, "vm-duration");
 
6906
 
 
6907
                /* POLISH syntax */
 
6908
                if (!strncasecmp(chan->language, "pl", 2)) {
 
6909
                        div_t num = div(durationm, 10);
 
6910
 
 
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) {
 
6915
                                if (num.rem == 2) {
 
6916
                                        if (!num.quot) {
 
6917
                                                res = ast_play_and_wait(chan, "digits/2-ie");
 
6918
                                        } else {
 
6919
                                                res = say_and_wait(chan, durationm - 2 , chan->language);
 
6920
                                                res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
 
6921
                                        }
 
6922
                                } else {
 
6923
                                        res = say_and_wait(chan, durationm, chan->language);
 
6924
                                }
 
6925
                                res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
 
6926
                        } else {
 
6927
                                res = say_and_wait(chan, durationm, chan->language);
 
6928
                                res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
 
6929
                        }
 
6930
                /* DEFAULT syntax */
 
6931
                } else {
 
6932
                        res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
 
6933
                        res = wait_file2(chan, vms, "vm-minutes");
 
6934
                }
 
6935
        }
 
6936
        return res;
 
6937
}
 
6938
 
 
6939
static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
 
6940
{
 
6941
        int res = 0;
 
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 };
 
6946
 
 
6947
        vms->starting = 0;
 
6948
        make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
 
6949
        adsi_message(chan, vms);
 
6950
        if (!vms->curmsg)
 
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" */
 
6954
 
 
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);
 
6960
                return 0;
 
6961
        }
 
6962
        flag = ast_variable_retrieve(msg_cfg, "message", "flag");
 
6963
 
 
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" */
 
6967
        }
 
6968
 
 
6969
        if (!res) {
 
6970
                /* POLISH syntax */
 
6971
                if (!strncasecmp(chan->language, "pl", 2)) {
 
6972
                        if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
 
6973
                                int ten, one;
 
6974
                                char nextmsg[256];
 
6975
                                ten = (vms->curmsg + 1) / 10;
 
6976
                                one = (vms->curmsg + 1) % 10;
 
6977
 
 
6978
                                if (vms->curmsg < 20) {
 
6979
                                        snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
 
6980
                                        res = wait_file2(chan, vms, nextmsg);
 
6981
                                } else {
 
6982
                                        snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
 
6983
                                        res = wait_file2(chan, vms, nextmsg);
 
6984
                                        if (one > 0) {
 
6985
                                                if (!res) {
 
6986
                                                        snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
 
6987
                                                        res = wait_file2(chan, vms, nextmsg);
 
6988
                                                }
 
6989
                                        }
 
6990
                                }
 
6991
                        }
 
6992
                        if (!res)
 
6993
                                res = wait_file2(chan, vms, "vm-message");
 
6994
                /* HEBREW syntax */
 
6995
                } else if (!strncasecmp(chan->language, "he", 2)) {
 
6996
                        if (!vms->curmsg) {
 
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");
 
7002
                        } else {
 
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");
 
7006
                        }
 
7007
                } else {
 
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");
 
7012
                        }
 
7013
                        if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
 
7014
                                if (!res) {
 
7015
                                        res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
 
7016
                                }
 
7017
                        }
 
7018
                }
 
7019
        }
 
7020
 
 
7021
        if (!msg_cfg) {
 
7022
                ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
 
7023
                return 0;
 
7024
        }
 
7025
 
 
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);
 
7030
                return 0;
 
7031
        }
 
7032
 
 
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");
 
7036
 
 
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");
 
7040
        if (!res) {
 
7041
                res = play_message_category(chan, category);
 
7042
        }
 
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 */
 
7050
        if (res == '1')
 
7051
                res = 0;
 
7052
        ast_config_destroy(msg_cfg);
 
7053
 
 
7054
        if (!res) {
 
7055
                make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
 
7056
                vms->heard[vms->curmsg] = 1;
 
7057
#ifdef IMAP_STORAGE
 
7058
                /*IMAP storage stores any prepended message from a forward
 
7059
                 * as a separate file from the rest of the message
 
7060
                 */
 
7061
                if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
 
7062
                        wait_file(chan, vms, vms->introfn);
 
7063
                }
 
7064
#endif
 
7065
                if ((res = wait_file(chan, vms, vms->fn)) < 0) {
 
7066
                        ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
 
7067
                        res = 0;
 
7068
                }
 
7069
        }
 
7070
        DISPOSE(vms->curdir, vms->curmsg);
 
7071
        return res;
 
7072
}
 
7073
 
 
7074
#ifdef IMAP_STORAGE
 
7075
static int imap_remove_file(char *dir, int msgnum)
 
7076
{
 
7077
        char fn[PATH_MAX];
 
7078
        char full_fn[PATH_MAX];
 
7079
        char intro[PATH_MAX] = {0,};
 
7080
        
 
7081
        if (msgnum > -1) {
 
7082
                make_file(fn, sizeof(fn), dir, msgnum);
 
7083
                snprintf(intro, sizeof(intro), "%sintro", fn);
 
7084
        } else
 
7085
                ast_copy_string(fn, dir, sizeof(fn));
 
7086
        
 
7087
        if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
 
7088
                ast_filedelete(fn, NULL);
 
7089
                if (!ast_strlen_zero(intro)) {
 
7090
                        ast_filedelete(intro, NULL);
 
7091
                }
 
7092
                snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
 
7093
                unlink(full_fn);
 
7094
        }
 
7095
        return 0;
 
7096
}
 
7097
 
 
7098
 
 
7099
 
 
7100
static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
 
7101
{
 
7102
        char *file, *filename;
 
7103
        char *attachment;
 
7104
        char arg[10];
 
7105
        int i;
 
7106
        BODY* body;
 
7107
 
 
7108
        
 
7109
        file = strrchr(ast_strdupa(dir), '/');
 
7110
        if (file)
 
7111
                *file++ = '\0';
 
7112
        else {
 
7113
                ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
 
7114
                return -1;
 
7115
        }
 
7116
 
 
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);
 
7123
                } else {
 
7124
                        ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
 
7125
                        ast_mutex_unlock(&vms->lock);
 
7126
                        return -1;
 
7127
                }
 
7128
                filename = strsep(&attachment, ".");
 
7129
                if (!strcmp(filename, file)) {
 
7130
                        sprintf (arg,"%d", i+1);
 
7131
                        mail_setflag (vms->mailstream,arg,"\\DELETED");
 
7132
                }
 
7133
        }
 
7134
        mail_expunge(vms->mailstream);
 
7135
        ast_mutex_unlock(&vms->lock);
 
7136
        return 0;
 
7137
}
 
7138
 
 
7139
#else
 
7140
#ifndef IMAP_STORAGE
 
7141
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
 
7142
{
 
7143
        int count_msg, last_msg;
 
7144
 
 
7145
        ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
 
7146
        
 
7147
        /* Rename the member vmbox HERE so that we don't try to return before
 
7148
         * we know what's going on.
 
7149
         */
 
7150
        snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
 
7151
        
 
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);
 
7154
 
 
7155
        count_msg = count_messages(vmu, vms->curdir);
 
7156
        if (count_msg < 0)
 
7157
                return count_msg;
 
7158
        else
 
7159
                vms->lastmsg = count_msg - 1;
 
7160
 
 
7161
        /*
 
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
 
7165
        detected.
 
7166
        */
 
7167
 
 
7168
        if (vm_lock_path(vms->curdir)) {
 
7169
                ast_log(AST_LOG_ERROR, "Could not open mailbox %s:  mailbox is locked\n", vms->curdir);
 
7170
                return -1;
 
7171
        }
 
7172
 
 
7173
        last_msg = last_message_index(vmu, vms->curdir);
 
7174
        ast_unlock_path(vms->curdir);
 
7175
 
 
7176
        if (last_msg < 0) 
 
7177
                return last_msg;
 
7178
 
 
7179
        return 0;
 
7180
}
 
7181
#endif
 
7182
#endif
 
7183
 
 
7184
static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
 
7185
{
 
7186
        int x = 0;
 
7187
#ifndef IMAP_STORAGE
 
7188
        int res = 0, nummsg;
 
7189
        char fn2[PATH_MAX];
 
7190
#endif
 
7191
 
 
7192
        if (vms->lastmsg <= -1)
 
7193
                goto done;
 
7194
 
 
7195
        vms->curmsg = -1; 
 
7196
#ifndef IMAP_STORAGE
 
7197
        /* Get the deleted messages fixed */ 
 
7198
        if (vm_lock_path(vms->curdir))
 
7199
                return ERROR_LOCK_PATH;
 
7200
 
 
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)) 
 
7206
                                break;
 
7207
                        vms->curmsg++; 
 
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);
 
7211
                        } 
 
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;
 
7219
                                vms->heard[x] = 0;
 
7220
                                --x;
 
7221
                        }
 
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;
 
7228
                                vms->heard[x] = 0;
 
7229
                                --x;
 
7230
                        }
 
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);
 
7237
                }
 
7238
        } 
 
7239
 
 
7240
        /* Delete ALL remaining messages */
 
7241
        nummsg = x - 1;
 
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);
 
7246
        }
 
7247
        ast_unlock_path(vms->curdir);
 
7248
#else
 
7249
        if (vms->deleted) {
 
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);
 
7254
                        }
 
7255
                }
 
7256
        }
 
7257
#endif
 
7258
 
 
7259
done:
 
7260
        if (vms->deleted)
 
7261
                memset(vms->deleted, 0, vmu->maxmsg * sizeof(int)); 
 
7262
        if (vms->heard)
 
7263
                memset(vms->heard, 0, vmu->maxmsg * sizeof(int)); 
 
7264
 
 
7265
        return 0;
 
7266
}
 
7267
 
 
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. 
 
7272
 */
 
7273
 
 
7274
static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
 
7275
{
 
7276
        int cmd;
 
7277
        char *buf;
 
7278
 
 
7279
        buf = alloca(strlen(box)+2); 
 
7280
        strcpy(buf, box);
 
7281
        strcat(buf,"s");
 
7282
 
 
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" */
 
7286
        } else {
 
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"*/
 
7289
        }
 
7290
}
 
7291
 
 
7292
static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
 
7293
{
 
7294
        int cmd;
 
7295
 
 
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");
 
7299
                else
 
7300
                        cmd = ast_play_and_wait(chan, "vm-old-e");
 
7301
                return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
 
7302
        } else {
 
7303
                cmd = ast_play_and_wait(chan, "vm-messages");
 
7304
                return cmd ? cmd : ast_play_and_wait(chan, box);
 
7305
        }
 
7306
}
 
7307
 
 
7308
static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
 
7309
{
 
7310
        int cmd;
 
7311
 
 
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);
 
7315
        } else {
 
7316
                cmd = ast_play_and_wait(chan, box);
 
7317
                return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
 
7318
        }
 
7319
}
 
7320
 
 
7321
static int vm_play_folder_name(struct ast_channel *chan, char *box)
 
7322
{
 
7323
        int cmd;
 
7324
 
 
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 */
 
7341
        }
 
7342
}
 
7343
 
 
7344
/* GREEK SYNTAX
 
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.
 
7349
 
 
7350
        -> vm-Olds.wav  : "Palia"
 
7351
        -> vm-INBOXs.wav : "Nea"
 
7352
        -> vm-denExeteMynhmata : "den exete mynhmata"
 
7353
*/
 
7354
 
 
7355
 
 
7356
static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
 
7357
{
 
7358
        int res = 0;
 
7359
 
 
7360
        if (vms->newmessages) {
 
7361
                res = ast_play_and_wait(chan, "vm-youhave");
 
7362
                if (!res) 
 
7363
                        res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
 
7364
                if (!res) {
 
7365
                        if ((vms->newmessages == 1)) {
 
7366
                                res = ast_play_and_wait(chan, "vm-INBOX");
 
7367
                                if (!res)
 
7368
                                        res = ast_play_and_wait(chan, "vm-message");
 
7369
                        } else {
 
7370
                                res = ast_play_and_wait(chan, "vm-INBOXs");
 
7371
                                if (!res)
 
7372
                                        res = ast_play_and_wait(chan, "vm-messages");
 
7373
                        }
 
7374
                }
 
7375
        } else if (vms->oldmessages){
 
7376
                res = ast_play_and_wait(chan, "vm-youhave");
 
7377
                if (!res)
 
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");
 
7381
                        if (!res)
 
7382
                                res = ast_play_and_wait(chan, "vm-message");
 
7383
                } else {
 
7384
                        res = ast_play_and_wait(chan, "vm-Olds");
 
7385
                        if (!res)
 
7386
                                res = ast_play_and_wait(chan, "vm-messages");
 
7387
                }
 
7388
        } else if (!vms->oldmessages && !vms->newmessages) 
 
7389
                res = ast_play_and_wait(chan, "vm-denExeteMynhmata"); 
 
7390
        return res;
 
7391
}
 
7392
 
 
7393
/* Version of vm_intro() designed to work for many languages.
 
7394
 *
 
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:
 
7400
 *
 
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".
 
7417
 *
 
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
 
7423
 *     on vm-message.
 
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.
 
7429
 *
 
7430
 * All languages require these messages:
 
7431
 *  vm-youhave          "You have..."
 
7432
 *  vm-and              "and"
 
7433
 *  vm-no               "no" (in the sense of "none", as in "you have no messages")
 
7434
 *
 
7435
 * To use it for English, you will need these additional sound files:
 
7436
 *  vm-new              "new"
 
7437
 *  vm-message          "message", singular
 
7438
 *  vm-messages         "messages", plural
 
7439
 *
 
7440
 * If you use it for Russian and other slavic languages, you will need these additional sound files:
 
7441
 *
 
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)
 
7449
 */
 
7450
static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
 
7451
{
 
7452
        int res;
 
7453
        int lastnum = 0;
 
7454
 
 
7455
        res = ast_play_and_wait(chan, "vm-youhave");
 
7456
 
 
7457
        if (!res && vms->newmessages) {
 
7458
                lastnum = vms->newmessages;
 
7459
 
 
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);
 
7462
                }
 
7463
 
 
7464
                if (!res && vms->oldmessages) {
 
7465
                        res = ast_play_and_wait(chan, "vm-and");
 
7466
                }
 
7467
        }
 
7468
 
 
7469
        if (!res && vms->oldmessages) {
 
7470
                lastnum = vms->oldmessages;
 
7471
 
 
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);
 
7474
                }
 
7475
        }
 
7476
 
 
7477
        if (!res) {
 
7478
                if (lastnum == 0) {
 
7479
                        res = ast_play_and_wait(chan, "vm-no");
 
7480
                }
 
7481
                if (!res) {
 
7482
                        res = ast_say_counted_noun(chan, lastnum, "vm-message");
 
7483
                }
 
7484
        }
 
7485
 
 
7486
        return res;
 
7487
}
 
7488
 
 
7489
/* Default Hebrew syntax */
 
7490
static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
 
7491
{
 
7492
        int res = 0;
 
7493
 
 
7494
        /* Introduce messages they have */
 
7495
        if (!res) {
 
7496
                if ((vms->newmessages) || (vms->oldmessages)) {
 
7497
                        res = ast_play_and_wait(chan, "vm-youhave");
 
7498
                }
 
7499
                /*
 
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.
 
7503
                 */
 
7504
                if (vms->newmessages) {
 
7505
                        if (!res) {
 
7506
                                if (vms->newmessages == 1) {
 
7507
                                        res = ast_play_and_wait(chan, "vm-INBOX1");
 
7508
                                } else {
 
7509
                                        if (vms->newmessages == 2) {
 
7510
                                                res = ast_play_and_wait(chan, "vm-shtei");
 
7511
                                        } else {
 
7512
                                                res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
 
7513
                                        }
 
7514
                                        res = ast_play_and_wait(chan, "vm-INBOX");
 
7515
                                }
 
7516
                        }
 
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");
 
7521
                                } else {
 
7522
                                        if (vms->oldmessages == 2) {
 
7523
                                                res = ast_play_and_wait(chan, "vm-shtei");
 
7524
                                        } else {
 
7525
                                                res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
 
7526
                                        }
 
7527
                                        res = ast_play_and_wait(chan, "vm-Old");
 
7528
                                }
 
7529
                        }
 
7530
                }
 
7531
                if (!res && vms->oldmessages && !vms->newmessages) {
 
7532
                        if (!res) {
 
7533
                                if (vms->oldmessages == 1) {
 
7534
                                        res = ast_play_and_wait(chan, "vm-Old1");
 
7535
                                } else {
 
7536
                                        if (vms->oldmessages == 2) {
 
7537
                                                res = ast_play_and_wait(chan, "vm-shtei");
 
7538
                                        } else {
 
7539
                                                res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");            
 
7540
                                        }
 
7541
                                        res = ast_play_and_wait(chan, "vm-Old");
 
7542
                                }
 
7543
                        }
 
7544
                }
 
7545
                if (!res) {
 
7546
                        if (!vms->oldmessages && !vms->newmessages) {
 
7547
                                if (!res) {
 
7548
                                        res = ast_play_and_wait(chan, "vm-nomessages");
 
7549
                                }
 
7550
                        }
 
7551
                }
 
7552
        }
 
7553
        return res;
 
7554
}
 
7555
        
 
7556
/* Default English syntax */
 
7557
static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
 
7558
{
 
7559
        int res;
 
7560
 
 
7561
        /* Introduce messages they have */
 
7562
        res = ast_play_and_wait(chan, "vm-youhave");
 
7563
        if (!res) {
 
7564
                if (vms->urgentmessages) {
 
7565
                        res = say_and_wait(chan, vms->urgentmessages, chan->language);
 
7566
                        if (!res)
 
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");
 
7570
                        } else if (!res) {
 
7571
                                if ((vms->urgentmessages == 1))
 
7572
                                        res = ast_play_and_wait(chan, "vm-message");
 
7573
                                else
 
7574
                                        res = ast_play_and_wait(chan, "vm-messages");
 
7575
                        }
 
7576
                }
 
7577
                if (vms->newmessages) {
 
7578
                        res = say_and_wait(chan, vms->newmessages, chan->language);
 
7579
                        if (!res)
 
7580
                                res = ast_play_and_wait(chan, "vm-INBOX");
 
7581
                        if (vms->oldmessages && !res)
 
7582
                                res = ast_play_and_wait(chan, "vm-and");
 
7583
                        else if (!res) {
 
7584
                                if ((vms->newmessages == 1))
 
7585
                                        res = ast_play_and_wait(chan, "vm-message");
 
7586
                                else
 
7587
                                        res = ast_play_and_wait(chan, "vm-messages");
 
7588
                        }
 
7589
                                
 
7590
                }
 
7591
                if (!res && vms->oldmessages) {
 
7592
                        res = say_and_wait(chan, vms->oldmessages, chan->language);
 
7593
                        if (!res)
 
7594
                                res = ast_play_and_wait(chan, "vm-Old");
 
7595
                        if (!res) {
 
7596
                                if (vms->oldmessages == 1)
 
7597
                                        res = ast_play_and_wait(chan, "vm-message");
 
7598
                                else
 
7599
                                        res = ast_play_and_wait(chan, "vm-messages");
 
7600
                        }
 
7601
                }
 
7602
                if (!res) {
 
7603
                        if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
 
7604
                                res = ast_play_and_wait(chan, "vm-no");
 
7605
                                if (!res)
 
7606
                                        res = ast_play_and_wait(chan, "vm-messages");
 
7607
                        }
 
7608
                }
 
7609
        }
 
7610
        return res;
 
7611
}
 
7612
 
 
7613
/* ITALIAN syntax */
 
7614
static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
 
7615
{
 
7616
        /* Introduce messages they have */
 
7617
        int res;
 
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");
 
7621
        else
 
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");
 
7634
        }
 
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");
 
7644
        }
 
7645
        return res;
 
7646
}
 
7647
 
 
7648
/* POLISH syntax */
 
7649
static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
 
7650
{
 
7651
        /* Introduce messages they have */
 
7652
        int res;
 
7653
        div_t num;
 
7654
 
 
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");
 
7658
                return res;
 
7659
        } else {
 
7660
                res = ast_play_and_wait(chan, "vm-youhave");
 
7661
        }
 
7662
 
 
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) {
 
7670
                        if (num.rem == 2) {
 
7671
                                if (!num.quot) {
 
7672
                                        res = ast_play_and_wait(chan, "digits/2-ie");
 
7673
                                } else {
 
7674
                                        res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
 
7675
                                        res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
 
7676
                                }
 
7677
                        } else {
 
7678
                                res = say_and_wait(chan, vms->newmessages, chan->language);
 
7679
                        }
 
7680
                        res = res ? res : ast_play_and_wait(chan, "vm-new-e");
 
7681
                        res = res ? res : ast_play_and_wait(chan, "vm-messages");
 
7682
                } else {
 
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");
 
7686
                }
 
7687
                if (!res && vms->oldmessages)
 
7688
                        res = ast_play_and_wait(chan, "vm-and");
 
7689
        }
 
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) {
 
7697
                        if (num.rem == 2) {
 
7698
                                if (!num.quot) {
 
7699
                                        res = ast_play_and_wait(chan, "digits/2-ie");
 
7700
                                } else {
 
7701
                                        res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
 
7702
                                        res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
 
7703
                                }
 
7704
                        } else {
 
7705
                                res = say_and_wait(chan, vms->oldmessages, chan->language);
 
7706
                        }
 
7707
                        res = res ? res : ast_play_and_wait(chan, "vm-old-e");
 
7708
                        res = res ? res : ast_play_and_wait(chan, "vm-messages");
 
7709
                } else {
 
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");
 
7713
                }
 
7714
        }
 
7715
 
 
7716
        return res;
 
7717
}
 
7718
 
 
7719
/* SWEDISH syntax */
 
7720
static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
 
7721
{
 
7722
        /* Introduce messages they have */
 
7723
        int res;
 
7724
 
 
7725
        res = ast_play_and_wait(chan, "vm-youhave");
 
7726
        if (res)
 
7727
                return res;
 
7728
 
 
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");
 
7732
                return res;
 
7733
        }
 
7734
 
 
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");
 
7740
                } else {
 
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");
 
7744
                }
 
7745
                if (!res && vms->oldmessages)
 
7746
                        res = ast_play_and_wait(chan, "vm-and");
 
7747
        }
 
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");
 
7753
                } else {
 
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");
 
7757
                }
 
7758
        }
 
7759
 
 
7760
        return res;
 
7761
}
 
7762
 
 
7763
/* NORWEGIAN syntax */
 
7764
static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
 
7765
{
 
7766
        /* Introduce messages they have */
 
7767
        int res;
 
7768
 
 
7769
        res = ast_play_and_wait(chan, "vm-youhave");
 
7770
        if (res)
 
7771
                return res;
 
7772
 
 
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");
 
7776
                return res;
 
7777
        }
 
7778
 
 
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");
 
7784
                } else {
 
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");
 
7788
                }
 
7789
                if (!res && vms->oldmessages)
 
7790
                        res = ast_play_and_wait(chan, "vm-and");
 
7791
        }
 
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");
 
7797
                } else {
 
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");
 
7801
                }
 
7802
        }
 
7803
 
 
7804
        return res;
 
7805
}
 
7806
 
 
7807
/* GERMAN syntax */
 
7808
static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
 
7809
{
 
7810
        /* Introduce messages they have */
 
7811
        int res;
 
7812
        res = ast_play_and_wait(chan, "vm-youhave");
 
7813
        if (!res) {
 
7814
                if (vms->newmessages) {
 
7815
                        if ((vms->newmessages == 1))
 
7816
                                res = ast_play_and_wait(chan, "digits/1F");
 
7817
                        else
 
7818
                                res = say_and_wait(chan, vms->newmessages, chan->language);
 
7819
                        if (!res)
 
7820
                                res = ast_play_and_wait(chan, "vm-INBOX");
 
7821
                        if (vms->oldmessages && !res)
 
7822
                                res = ast_play_and_wait(chan, "vm-and");
 
7823
                        else if (!res) {
 
7824
                                if ((vms->newmessages == 1))
 
7825
                                        res = ast_play_and_wait(chan, "vm-message");
 
7826
                                else
 
7827
                                        res = ast_play_and_wait(chan, "vm-messages");
 
7828
                        }
 
7829
                                
 
7830
                }
 
7831
                if (!res && vms->oldmessages) {
 
7832
                        if (vms->oldmessages == 1)
 
7833
                                res = ast_play_and_wait(chan, "digits/1F");
 
7834
                        else
 
7835
                                res = say_and_wait(chan, vms->oldmessages, chan->language);
 
7836
                        if (!res)
 
7837
                                res = ast_play_and_wait(chan, "vm-Old");
 
7838
                        if (!res) {
 
7839
                                if (vms->oldmessages == 1)
 
7840
                                        res = ast_play_and_wait(chan, "vm-message");
 
7841
                                else
 
7842
                                        res = ast_play_and_wait(chan, "vm-messages");
 
7843
                        }
 
7844
                }
 
7845
                if (!res) {
 
7846
                        if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
 
7847
                                res = ast_play_and_wait(chan, "vm-no");
 
7848
                                if (!res)
 
7849
                                        res = ast_play_and_wait(chan, "vm-messages");
 
7850
                        }
 
7851
                }
 
7852
        }
 
7853
        return res;
 
7854
}
 
7855
 
 
7856
/* SPANISH syntax */
 
7857
static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
 
7858
{
 
7859
        /* Introduce messages they have */
 
7860
        int res;
 
7861
        if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
 
7862
                res = ast_play_and_wait(chan, "vm-youhaveno");
 
7863
                if (!res)
 
7864
                        res = ast_play_and_wait(chan, "vm-messages");
 
7865
        } else {
 
7866
                res = ast_play_and_wait(chan, "vm-youhave");
 
7867
        }
 
7868
        if (!res) {
 
7869
                if (vms->newmessages) {
 
7870
                        if (!res) {
 
7871
                                if ((vms->newmessages == 1)) {
 
7872
                                        res = ast_play_and_wait(chan, "digits/1M");
 
7873
                                        if (!res)
 
7874
                                                res = ast_play_and_wait(chan, "vm-message");
 
7875
                                        if (!res)
 
7876
                                                res = ast_play_and_wait(chan, "vm-INBOXs");
 
7877
                                } else {
 
7878
                                        res = say_and_wait(chan, vms->newmessages, chan->language);
 
7879
                                        if (!res)
 
7880
                                                res = ast_play_and_wait(chan, "vm-messages");
 
7881
                                        if (!res)
 
7882
                                                res = ast_play_and_wait(chan, "vm-INBOX");
 
7883
                                }
 
7884
                        }
 
7885
                        if (vms->oldmessages && !res)
 
7886
                                res = ast_play_and_wait(chan, "vm-and");
 
7887
                }
 
7888
                if (vms->oldmessages) {
 
7889
                        if (!res) {
 
7890
                                if (vms->oldmessages == 1) {
 
7891
                                        res = ast_play_and_wait(chan, "digits/1M");
 
7892
                                        if (!res)
 
7893
                                                res = ast_play_and_wait(chan, "vm-message");
 
7894
                                        if (!res)
 
7895
                                                res = ast_play_and_wait(chan, "vm-Olds");
 
7896
                                } else {
 
7897
                                        res = say_and_wait(chan, vms->oldmessages, chan->language);
 
7898
                                        if (!res)
 
7899
                                                res = ast_play_and_wait(chan, "vm-messages");
 
7900
                                        if (!res)
 
7901
                                                res = ast_play_and_wait(chan, "vm-Old");
 
7902
                                }
 
7903
                        }
 
7904
                }
 
7905
        }
 
7906
return res;
 
7907
}
 
7908
 
 
7909
/* BRAZILIAN PORTUGUESE syntax */
 
7910
static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
 
7911
        /* Introduce messages they have */
 
7912
        int res;
 
7913
        if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
 
7914
                res = ast_play_and_wait(chan, "vm-nomessages");
 
7915
                return res;
 
7916
        } else {
 
7917
                res = ast_play_and_wait(chan, "vm-youhave");
 
7918
        }
 
7919
        if (vms->newmessages) {
 
7920
                if (!res)
 
7921
                        res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
 
7922
                if ((vms->newmessages == 1)) {
 
7923
                        if (!res)
 
7924
                                res = ast_play_and_wait(chan, "vm-message");
 
7925
                        if (!res)
 
7926
                                res = ast_play_and_wait(chan, "vm-INBOXs");
 
7927
                } else {
 
7928
                        if (!res)
 
7929
                                res = ast_play_and_wait(chan, "vm-messages");
 
7930
                        if (!res)
 
7931
                                res = ast_play_and_wait(chan, "vm-INBOX");
 
7932
                }
 
7933
                if (vms->oldmessages && !res)
 
7934
                        res = ast_play_and_wait(chan, "vm-and");
 
7935
        }
 
7936
        if (vms->oldmessages) {
 
7937
                if (!res)
 
7938
                        res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
 
7939
                if (vms->oldmessages == 1) {
 
7940
                        if (!res)
 
7941
                                res = ast_play_and_wait(chan, "vm-message");
 
7942
                        if (!res)
 
7943
                                res = ast_play_and_wait(chan, "vm-Olds");
 
7944
                } else {
 
7945
                        if (!res)
 
7946
                                res = ast_play_and_wait(chan, "vm-messages");
 
7947
                        if (!res)
 
7948
                                res = ast_play_and_wait(chan, "vm-Old");
 
7949
                }
 
7950
        }
 
7951
        return res;
 
7952
}
 
7953
 
 
7954
/* FRENCH syntax */
 
7955
static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
 
7956
{
 
7957
        /* Introduce messages they have */
 
7958
        int res;
 
7959
        res = ast_play_and_wait(chan, "vm-youhave");
 
7960
        if (!res) {
 
7961
                if (vms->newmessages) {
 
7962
                        res = say_and_wait(chan, vms->newmessages, chan->language);
 
7963
                        if (!res)
 
7964
                                res = ast_play_and_wait(chan, "vm-INBOX");
 
7965
                        if (vms->oldmessages && !res)
 
7966
                                res = ast_play_and_wait(chan, "vm-and");
 
7967
                        else if (!res) {
 
7968
                                if ((vms->newmessages == 1))
 
7969
                                        res = ast_play_and_wait(chan, "vm-message");
 
7970
                                else
 
7971
                                        res = ast_play_and_wait(chan, "vm-messages");
 
7972
                        }
 
7973
                                
 
7974
                }
 
7975
                if (!res && vms->oldmessages) {
 
7976
                        res = say_and_wait(chan, vms->oldmessages, chan->language);
 
7977
                        if (!res)
 
7978
                                res = ast_play_and_wait(chan, "vm-Old");
 
7979
                        if (!res) {
 
7980
                                if (vms->oldmessages == 1)
 
7981
                                        res = ast_play_and_wait(chan, "vm-message");
 
7982
                                else
 
7983
                                        res = ast_play_and_wait(chan, "vm-messages");
 
7984
                        }
 
7985
                }
 
7986
                if (!res) {
 
7987
                        if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
 
7988
                                res = ast_play_and_wait(chan, "vm-no");
 
7989
                                if (!res)
 
7990
                                        res = ast_play_and_wait(chan, "vm-messages");
 
7991
                        }
 
7992
                }
 
7993
        }
 
7994
        return res;
 
7995
}
 
7996
 
 
7997
/* DUTCH syntax */
 
7998
static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
 
7999
{
 
8000
        /* Introduce messages they have */
 
8001
        int res;
 
8002
        res = ast_play_and_wait(chan, "vm-youhave");
 
8003
        if (!res) {
 
8004
                if (vms->newmessages) {
 
8005
                        res = say_and_wait(chan, vms->newmessages, chan->language);
 
8006
                        if (!res) {
 
8007
                                if (vms->newmessages == 1)
 
8008
                                        res = ast_play_and_wait(chan, "vm-INBOXs");
 
8009
                                else
 
8010
                                        res = ast_play_and_wait(chan, "vm-INBOX");
 
8011
                        }
 
8012
                        if (vms->oldmessages && !res)
 
8013
                                res = ast_play_and_wait(chan, "vm-and");
 
8014
                        else if (!res) {
 
8015
                                if ((vms->newmessages == 1))
 
8016
                                        res = ast_play_and_wait(chan, "vm-message");
 
8017
                                else
 
8018
                                        res = ast_play_and_wait(chan, "vm-messages");
 
8019
                        }
 
8020
                                
 
8021
                }
 
8022
                if (!res && vms->oldmessages) {
 
8023
                        res = say_and_wait(chan, vms->oldmessages, chan->language);
 
8024
                        if (!res) {
 
8025
                                if (vms->oldmessages == 1)
 
8026
                                        res = ast_play_and_wait(chan, "vm-Olds");
 
8027
                                else
 
8028
                                        res = ast_play_and_wait(chan, "vm-Old");
 
8029
                        }
 
8030
                        if (!res) {
 
8031
                                if (vms->oldmessages == 1)
 
8032
                                        res = ast_play_and_wait(chan, "vm-message");
 
8033
                                else
 
8034
                                        res = ast_play_and_wait(chan, "vm-messages");
 
8035
                        }
 
8036
                }
 
8037
                if (!res) {
 
8038
                        if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
 
8039
                                res = ast_play_and_wait(chan, "vm-no");
 
8040
                                if (!res)
 
8041
                                        res = ast_play_and_wait(chan, "vm-messages");
 
8042
                        }
 
8043
                }
 
8044
        }
 
8045
        return res;
 
8046
}
 
8047
 
 
8048
/* PORTUGUESE syntax */
 
8049
static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
 
8050
{
 
8051
        /* Introduce messages they have */
 
8052
        int res;
 
8053
        res = ast_play_and_wait(chan, "vm-youhave");
 
8054
        if (!res) {
 
8055
                if (vms->newmessages) {
 
8056
                        res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
 
8057
                        if (!res) {
 
8058
                                if ((vms->newmessages == 1)) {
 
8059
                                        res = ast_play_and_wait(chan, "vm-message");
 
8060
                                        if (!res)
 
8061
                                                res = ast_play_and_wait(chan, "vm-INBOXs");
 
8062
                                } else {
 
8063
                                        res = ast_play_and_wait(chan, "vm-messages");
 
8064
                                        if (!res)
 
8065
                                                res = ast_play_and_wait(chan, "vm-INBOX");
 
8066
                                }
 
8067
                        }
 
8068
                        if (vms->oldmessages && !res)
 
8069
                                res = ast_play_and_wait(chan, "vm-and");
 
8070
                }
 
8071
                if (!res && vms->oldmessages) {
 
8072
                        res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
 
8073
                        if (!res) {
 
8074
                                if (vms->oldmessages == 1) {
 
8075
                                        res = ast_play_and_wait(chan, "vm-message");
 
8076
                                        if (!res)
 
8077
                                                res = ast_play_and_wait(chan, "vm-Olds");
 
8078
                                } else {
 
8079
                                        res = ast_play_and_wait(chan, "vm-messages");
 
8080
                                        if (!res)
 
8081
                                                res = ast_play_and_wait(chan, "vm-Old");
 
8082
                                }
 
8083
                        }
 
8084
                }
 
8085
                if (!res) {
 
8086
                        if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
 
8087
                                res = ast_play_and_wait(chan, "vm-no");
 
8088
                                if (!res)
 
8089
                                        res = ast_play_and_wait(chan, "vm-messages");
 
8090
                        }
 
8091
                }
 
8092
        }
 
8093
        return res;
 
8094
}
 
8095
 
 
8096
 
 
8097
/* CZECH syntax */
 
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 )
 
8110
 */
 
8111
 
 
8112
static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
 
8113
{
 
8114
        int res;
 
8115
        res = ast_play_and_wait(chan, "vm-youhave");
 
8116
        if (!res) {
 
8117
                if (vms->newmessages) {
 
8118
                        if (vms->newmessages == 1) {
 
8119
                                res = ast_play_and_wait(chan, "digits/jednu");
 
8120
                        } else {
 
8121
                                res = say_and_wait(chan, vms->newmessages, chan->language);
 
8122
                        }
 
8123
                        if (!res) {
 
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");
 
8130
                        }
 
8131
                        if (vms->oldmessages && !res)
 
8132
                                res = ast_play_and_wait(chan, "vm-and");
 
8133
                        else if (!res) {
 
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");
 
8140
                        }
 
8141
                }
 
8142
                if (!res && vms->oldmessages) {
 
8143
                        res = say_and_wait(chan, vms->oldmessages, chan->language);
 
8144
                        if (!res) {
 
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");
 
8151
                        }
 
8152
                        if (!res) {
 
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");
 
8159
                        }
 
8160
                }
 
8161
                if (!res) {
 
8162
                        if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
 
8163
                                res = ast_play_and_wait(chan, "vm-no");
 
8164
                                if (!res)
 
8165
                                        res = ast_play_and_wait(chan, "vm-zpravy");
 
8166
                        }
 
8167
                }
 
8168
        }
 
8169
        return res;
 
8170
}
 
8171
 
 
8172
/* CHINESE (Taiwan) syntax */
 
8173
static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
 
8174
{
 
8175
        int res;
 
8176
        /* Introduce messages they have */
 
8177
        res = ast_play_and_wait(chan, "vm-you");
 
8178
 
 
8179
        if (!res && vms->newmessages) {
 
8180
                res = ast_play_and_wait(chan, "vm-have");
 
8181
                if (!res)
 
8182
                        res = say_and_wait(chan, vms->newmessages, chan->language);
 
8183
                if (!res)
 
8184
                        res = ast_play_and_wait(chan, "vm-tong");
 
8185
                if (!res)
 
8186
                        res = ast_play_and_wait(chan, "vm-INBOX");
 
8187
                if (vms->oldmessages && !res)
 
8188
                        res = ast_play_and_wait(chan, "vm-and");
 
8189
                else if (!res) 
 
8190
                        res = ast_play_and_wait(chan, "vm-messages");
 
8191
        }
 
8192
        if (!res && vms->oldmessages) {
 
8193
                res = ast_play_and_wait(chan, "vm-have");
 
8194
                if (!res)
 
8195
                        res = say_and_wait(chan, vms->oldmessages, chan->language);
 
8196
                if (!res)
 
8197
                        res = ast_play_and_wait(chan, "vm-tong");
 
8198
                if (!res)
 
8199
                        res = ast_play_and_wait(chan, "vm-Old");
 
8200
                if (!res)
 
8201
                        res = ast_play_and_wait(chan, "vm-messages");
 
8202
        }
 
8203
        if (!res && !vms->oldmessages && !vms->newmessages) {
 
8204
                res = ast_play_and_wait(chan, "vm-haveno");
 
8205
                if (!res)
 
8206
                        res = ast_play_and_wait(chan, "vm-messages");
 
8207
        }
 
8208
        return res;
 
8209
}
 
8210
 
 
8211
static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
 
8212
{
 
8213
        char prefile[256];
 
8214
        
 
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");
 
8221
                }
 
8222
                DISPOSE(prefile, -1);
 
8223
        }
 
8224
 
 
8225
        /* Play voicemail intro - syntax is different for different languages */
 
8226
        if (0) {
 
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");
 
8233
                }
 
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);
 
8267
        }
 
8268
}
 
8269
 
 
8270
static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
 
8271
{
 
8272
        int res = 0;
 
8273
        /* Play instructions and wait for new command */
 
8274
        while (!res) {
 
8275
                if (vms->starting) {
 
8276
                        if (vms->lastmsg > -1) {
 
8277
                                if (skipadvanced)
 
8278
                                        res = ast_play_and_wait(chan, "vm-onefor-full");
 
8279
                                else
 
8280
                                        res = ast_play_and_wait(chan, "vm-onefor");
 
8281
                                if (!res)
 
8282
                                        res = vm_play_folder_name(chan, vms->vmbox);
 
8283
                        }
 
8284
                        if (!res) {
 
8285
                                if (skipadvanced)
 
8286
                                        res = ast_play_and_wait(chan, "vm-opts-full");
 
8287
                                else
 
8288
                                        res = ast_play_and_wait(chan, "vm-opts");
 
8289
                        }
 
8290
                } else {
 
8291
                        /* Added for additional help */
 
8292
                        if (skipadvanced) {
 
8293
                                res = ast_play_and_wait(chan, "vm-onefor-full");
 
8294
                                if (!res)
 
8295
                                        res = vm_play_folder_name(chan, vms->vmbox);
 
8296
                                res = ast_play_and_wait(chan, "vm-opts-full");
 
8297
                        }
 
8298
                        /* Logic:
 
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
 
8302
                         * previous message
 
8303
                         */
 
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");
 
8306
                        }
 
8307
                        if (!res && !skipadvanced)
 
8308
                                res = ast_play_and_wait(chan, "vm-advopts");
 
8309
                        if (!res)
 
8310
                                res = ast_play_and_wait(chan, "vm-repeat");
 
8311
                        /* Logic:
 
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
 
8316
                         */
 
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");
 
8320
                        }
 
8321
                        if (!res) {
 
8322
                                if (!vms->deleted[vms->curmsg])
 
8323
                                        res = ast_play_and_wait(chan, "vm-delete");
 
8324
                                else
 
8325
                                        res = ast_play_and_wait(chan, "vm-undelete");
 
8326
                                if (!res)
 
8327
                                        res = ast_play_and_wait(chan, "vm-toforward");
 
8328
                                if (!res)
 
8329
                                        res = ast_play_and_wait(chan, "vm-savemessage");
 
8330
                        }
 
8331
                }
 
8332
                if (!res) {
 
8333
                        res = ast_play_and_wait(chan, "vm-helpexit");
 
8334
                }
 
8335
                if (!res)
 
8336
                        res = ast_waitfordigit(chan, 6000);
 
8337
                if (!res) {
 
8338
                        vms->repeats++;
 
8339
                        if (vms->repeats > 2) {
 
8340
                                res = 't';
 
8341
                        }
 
8342
                }
 
8343
        }
 
8344
        return res;
 
8345
}
 
8346
 
 
8347
static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms,  int skipadvanced, int in_urgent)
 
8348
{
 
8349
        int res = 0;
 
8350
        /* Play instructions and wait for new command */
 
8351
        while (!res) {
 
8352
                if (vms->lastmsg > -1) {
 
8353
                        res = ast_play_and_wait(chan, "vm-listen");
 
8354
                        if (!res)
 
8355
                                res = vm_play_folder_name(chan, vms->vmbox);
 
8356
                        if (!res)
 
8357
                                res = ast_play_and_wait(chan, "press");
 
8358
                        if (!res)
 
8359
                                res = ast_play_and_wait(chan, "digits/1");
 
8360
                }
 
8361
                if (!res)
 
8362
                        res = ast_play_and_wait(chan, "vm-opts");
 
8363
                if (!res) {
 
8364
                        vms->starting = 0;
 
8365
                        return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
 
8366
                }
 
8367
        }
 
8368
        return res;
 
8369
}
 
8370
 
 
8371
static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
 
8372
{
 
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);
 
8377
        }
 
8378
}
 
8379
 
 
8380
 
 
8381
static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
 
8382
{
 
8383
        int cmd = 0;
 
8384
        int duration = 0;
 
8385
        int tries = 0;
 
8386
        char newpassword[80] = "";
 
8387
        char newpassword2[80] = "";
 
8388
        char prefile[PATH_MAX] = "";
 
8389
        unsigned char buf[256];
 
8390
        int bytes=0;
 
8391
 
 
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);
 
8399
        }
 
8400
 
 
8401
        /* First, have the user change their password 
 
8402
           so they won't get here again */
 
8403
        for (;;) {
 
8404
                newpassword[1] = '\0';
 
8405
                newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
 
8406
                if (cmd == '#')
 
8407
                        newpassword[0] = '\0';
 
8408
                if (cmd < 0 || cmd == 't' || cmd == '#')
 
8409
                        return cmd;
 
8410
                cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
 
8411
                if (cmd < 0 || cmd == 't' || cmd == '#')
 
8412
                        return cmd;
 
8413
                cmd = check_password(vmu, newpassword); /* perform password validation */
 
8414
                if (cmd != 0) {
 
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);
 
8417
                } else {
 
8418
                        newpassword2[1] = '\0';
 
8419
                        newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
 
8420
                        if (cmd == '#')
 
8421
                                newpassword2[0] = '\0';
 
8422
                        if (cmd < 0 || cmd == 't' || cmd == '#')
 
8423
                                return cmd;
 
8424
                        cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
 
8425
                        if (cmd < 0 || cmd == 't' || cmd == '#')
 
8426
                                return cmd;
 
8427
                        if (!strcmp(newpassword, newpassword2))
 
8428
                                break;
 
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);
 
8431
                }
 
8432
                if (++tries == 3)
 
8433
                        return -1;
 
8434
                if (cmd != 0) {
 
8435
                        cmd = ast_play_and_wait(chan, vm_pls_try_again);
 
8436
                }
 
8437
        }
 
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);
 
8442
 
 
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);
 
8445
 
 
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 == '#')
 
8452
                                return cmd;
 
8453
                }
 
8454
        }
 
8455
 
 
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 == '#')
 
8462
                                return cmd;
 
8463
                }
 
8464
 
 
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 == '#')
 
8469
                                return cmd;
 
8470
                }
 
8471
        }
 
8472
 
 
8473
        return cmd;
 
8474
}
 
8475
 
 
8476
static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
 
8477
{
 
8478
        int cmd = 0;
 
8479
        int retries = 0;
 
8480
        int duration = 0;
 
8481
        char newpassword[80] = "";
 
8482
        char newpassword2[80] = "";
 
8483
        char prefile[PATH_MAX] = "";
 
8484
        unsigned char buf[256];
 
8485
        int bytes=0;
 
8486
 
 
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);
 
8494
        }
 
8495
        while ((cmd >= 0) && (cmd != 't')) {
 
8496
                if (cmd)
 
8497
                        retries = 0;
 
8498
                switch (cmd) {
 
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);
 
8502
                        break;
 
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);
 
8506
                        break;
 
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);
 
8510
                        break;
 
8511
                case '4':  /* manage the temporary greeting */
 
8512
                        cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
 
8513
                        break;
 
8514
                case '5': /* change password */
 
8515
                        if (vmu->password[0] == '-') {
 
8516
                                cmd = ast_play_and_wait(chan, "vm-no");
 
8517
                                break;
 
8518
                        }
 
8519
                        newpassword[1] = '\0';
 
8520
                        newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
 
8521
                        if (cmd == '#')
 
8522
                                newpassword[0] = '\0';
 
8523
                        else {
 
8524
                                if (cmd < 0)
 
8525
                                        break;
 
8526
                                if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
 
8527
                                        break;
 
8528
                                }
 
8529
                        }
 
8530
                        cmd = check_password(vmu, newpassword); /* perform password validation */
 
8531
                        if (cmd != 0) {
 
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);
 
8534
                                if (!cmd) {
 
8535
                                        cmd = ast_play_and_wait(chan, vm_pls_try_again);
 
8536
                                }
 
8537
                                break;
 
8538
                        }
 
8539
                        newpassword2[1] = '\0';
 
8540
                        newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
 
8541
                        if (cmd == '#')
 
8542
                                newpassword2[0] = '\0';
 
8543
                        else {
 
8544
                                if (cmd < 0)
 
8545
                                        break;
 
8546
 
 
8547
                                if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#")) < 0) {
 
8548
                                        break;
 
8549
                                }
 
8550
                        }
 
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);
 
8554
                                if (!cmd) {
 
8555
                                        cmd = ast_play_and_wait(chan, vm_pls_try_again);
 
8556
                                }
 
8557
                                break;
 
8558
                        }
 
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);
 
8563
 
 
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);
 
8566
                        break;
 
8567
                case '*': 
 
8568
                        cmd = 't';
 
8569
                        break;
 
8570
                default: 
 
8571
                        cmd = 0;
 
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");
 
8576
                        }
 
8577
                        DISPOSE(prefile, -1);
 
8578
                        if (!cmd) {
 
8579
                                cmd = ast_play_and_wait(chan, "vm-options");
 
8580
                        }
 
8581
                        if (!cmd) {
 
8582
                                cmd = ast_waitfordigit(chan,6000);
 
8583
                        }
 
8584
                        if (!cmd) {
 
8585
                                retries++;
 
8586
                        }
 
8587
                        if (retries > 3) {
 
8588
                                cmd = 't';
 
8589
                        }
 
8590
                }
 
8591
        }
 
8592
        if (cmd == 't')
 
8593
                cmd = 0;
 
8594
        return cmd;
 
8595
}
 
8596
 
 
8597
/*!
 
8598
 * \brief The handler for 'record a temporary greeting'. 
 
8599
 * \param chan
 
8600
 * \param vmu
 
8601
 * \param vms
 
8602
 * \param fmtc
 
8603
 * \param record_gain
 
8604
 *
 
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.
 
8610
 *
 
8611
 * \return zero on success, -1 on error.
 
8612
 */
 
8613
static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
 
8614
{
 
8615
        int cmd = 0;
 
8616
        int retries = 0;
 
8617
        int duration = 0;
 
8618
        char prefile[PATH_MAX] = "";
 
8619
        unsigned char buf[256];
 
8620
        int bytes = 0;
 
8621
 
 
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);
 
8629
        }
 
8630
 
 
8631
        snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
 
8632
        while ((cmd >= 0) && (cmd != 't')) {
 
8633
                if (cmd)
 
8634
                        retries = 0;
 
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);
 
8638
                        cmd = 't';      
 
8639
                } else {
 
8640
                        switch (cmd) {
 
8641
                        case '1':
 
8642
                                cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms, NULL);
 
8643
                                break;
 
8644
                        case '2':
 
8645
                                DELETE(prefile, -1, prefile, vmu);
 
8646
                                ast_play_and_wait(chan, "vm-tempremoved");
 
8647
                                cmd = 't';      
 
8648
                                break;
 
8649
                        case '*': 
 
8650
                                cmd = 't';
 
8651
                                break;
 
8652
                        default:
 
8653
                                cmd = ast_play_and_wait(chan,
 
8654
                                        ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
 
8655
                                                "vm-tempgreeting2" : "vm-tempgreeting");
 
8656
                                if (!cmd)
 
8657
                                        cmd = ast_waitfordigit(chan,6000);
 
8658
                                if (!cmd)
 
8659
                                        retries++;
 
8660
                                if (retries > 3)
 
8661
                                        cmd = 't';
 
8662
                        }
 
8663
                }
 
8664
                DISPOSE(prefile, -1);
 
8665
        }
 
8666
        if (cmd == 't')
 
8667
                cmd = 0;
 
8668
        return cmd;
 
8669
}
 
8670
 
 
8671
/*!
 
8672
 * \brief Greek syntax for 'You have N messages' greeting.
 
8673
 * \param chan
 
8674
 * \param vms
 
8675
 * \param vmu
 
8676
 *
 
8677
 * \return zero on success, -1 on error.
 
8678
 */     
 
8679
static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
 
8680
{
 
8681
        int cmd=0;
 
8682
 
 
8683
        if (vms->lastmsg > -1) {
 
8684
                cmd = play_message(chan, vmu, vms);
 
8685
        } else {
 
8686
                cmd = ast_play_and_wait(chan, "vm-youhaveno");
 
8687
                if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
 
8688
                        if (!cmd) {
 
8689
                                snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
 
8690
                                cmd = ast_play_and_wait(chan, vms->fn);
 
8691
                        }
 
8692
                        if (!cmd)
 
8693
                                cmd = ast_play_and_wait(chan, "vm-messages");
 
8694
                } else {
 
8695
                        if (!cmd)
 
8696
                                cmd = ast_play_and_wait(chan, "vm-messages");
 
8697
                        if (!cmd) {
 
8698
                                snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
 
8699
                                cmd = ast_play_and_wait(chan, vms->fn);
 
8700
                        }
 
8701
                }
 
8702
        } 
 
8703
        return cmd;
 
8704
}
 
8705
 
 
8706
/* Hebrew Syntax */
 
8707
static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
 
8708
{
 
8709
        int cmd = 0;
 
8710
 
 
8711
        if (vms->lastmsg > -1) {
 
8712
                cmd = play_message(chan, vmu, vms);
 
8713
        } else {
 
8714
                if (!strcasecmp(vms->fn, "INBOX")) {
 
8715
                        cmd = ast_play_and_wait(chan, "vm-nonewmessages");
 
8716
                } else {
 
8717
                        cmd = ast_play_and_wait(chan, "vm-nomessages");
 
8718
                }
 
8719
        }
 
8720
        return cmd;
 
8721
}
 
8722
 
 
8723
/*! 
 
8724
 * \brief Default English syntax for 'You have N messages' greeting.
 
8725
 * \param chan
 
8726
 * \param vms
 
8727
 * \param vmu
 
8728
 *
 
8729
 * \return zero on success, -1 on error.
 
8730
 */
 
8731
static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
 
8732
{
 
8733
        int cmd=0;
 
8734
 
 
8735
        if (vms->lastmsg > -1) {
 
8736
                cmd = play_message(chan, vmu, vms);
 
8737
        } else {
 
8738
                cmd = ast_play_and_wait(chan, "vm-youhave");
 
8739
                if (!cmd) 
 
8740
                        cmd = ast_play_and_wait(chan, "vm-no");
 
8741
                if (!cmd) {
 
8742
                        snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
 
8743
                        cmd = ast_play_and_wait(chan, vms->fn);
 
8744
                }
 
8745
                if (!cmd)
 
8746
                        cmd = ast_play_and_wait(chan, "vm-messages");
 
8747
        }
 
8748
        return cmd;
 
8749
}
 
8750
 
 
8751
/*! 
 
8752
 *\brief Italian syntax for 'You have N messages' greeting.
 
8753
 * \param chan
 
8754
 * \param vms
 
8755
 * \param vmu
 
8756
 *
 
8757
 * \return zero on success, -1 on error.
 
8758
 */
 
8759
static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
 
8760
{
 
8761
        int cmd=0;
 
8762
 
 
8763
        if (vms->lastmsg > -1) {
 
8764
                cmd = play_message(chan, vmu, vms);
 
8765
        } else {
 
8766
                cmd = ast_play_and_wait(chan, "vm-no");
 
8767
                if (!cmd)
 
8768
                        cmd = ast_play_and_wait(chan, "vm-message");
 
8769
                if (!cmd) {
 
8770
                        snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
 
8771
                        cmd = ast_play_and_wait(chan, vms->fn);
 
8772
                }
 
8773
        }
 
8774
        return cmd;
 
8775
}
 
8776
 
 
8777
/*! 
 
8778
 * \brief Spanish syntax for 'You have N messages' greeting.
 
8779
 * \param chan
 
8780
 * \param vms
 
8781
 * \param vmu
 
8782
 *
 
8783
 * \return zero on success, -1 on error.
 
8784
 */
 
8785
static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
 
8786
{
 
8787
        int cmd=0;
 
8788
 
 
8789
        if (vms->lastmsg > -1) {
 
8790
                cmd = play_message(chan, vmu, vms);
 
8791
        } else {
 
8792
                cmd = ast_play_and_wait(chan, "vm-youhaveno");
 
8793
                if (!cmd)
 
8794
                        cmd = ast_play_and_wait(chan, "vm-messages");
 
8795
                if (!cmd) {
 
8796
                        snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
 
8797
                        cmd = ast_play_and_wait(chan, vms->fn);
 
8798
                }
 
8799
        }
 
8800
        return cmd;
 
8801
}
 
8802
 
 
8803
/*! 
 
8804
 * \brief Portuguese syntax for 'You have N messages' greeting.
 
8805
 * \param chan
 
8806
 * \param vms
 
8807
 * \param vmu
 
8808
 *
 
8809
 * \return zero on success, -1 on error.
 
8810
 */
 
8811
static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
 
8812
{
 
8813
        int cmd=0;
 
8814
 
 
8815
        if (vms->lastmsg > -1) {
 
8816
                cmd = play_message(chan, vmu, vms);
 
8817
        } else {
 
8818
                cmd = ast_play_and_wait(chan, "vm-no");
 
8819
                if (!cmd) {
 
8820
                        snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
 
8821
                        cmd = ast_play_and_wait(chan, vms->fn);
 
8822
                }
 
8823
                if (!cmd)
 
8824
                        cmd = ast_play_and_wait(chan, "vm-messages");
 
8825
        }
 
8826
        return cmd;
 
8827
}
 
8828
 
 
8829
/*! 
 
8830
 * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
 
8831
 * \param chan
 
8832
 * \param vms
 
8833
 * \param vmu
 
8834
 *
 
8835
 * \return zero on success, -1 on error.
 
8836
 */
 
8837
static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
 
8838
{
 
8839
        int cmd=0;
 
8840
 
 
8841
        if (vms->lastmsg > -1) {
 
8842
                cmd = play_message(chan, vmu, vms);
 
8843
        } else {
 
8844
                cmd = ast_play_and_wait(chan, "vm-you");
 
8845
                if (!cmd) 
 
8846
                        cmd = ast_play_and_wait(chan, "vm-haveno");
 
8847
                if (!cmd)
 
8848
                        cmd = ast_play_and_wait(chan, "vm-messages");
 
8849
                if (!cmd) {
 
8850
                        snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
 
8851
                        cmd = ast_play_and_wait(chan, vms->fn);
 
8852
                }
 
8853
        }
 
8854
        return cmd;
 
8855
}
 
8856
 
 
8857
/*!
 
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.
 
8862
 * 
 
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.
 
8865
 *
 
8866
 * \return zero on success, -1 on error.
 
8867
 */
 
8868
static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
 
8869
{
 
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);
 
8884
        }
 
8885
}
 
8886
 
 
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)
 
8890
{
 
8891
        int useadsi=0, valid=0, logretries=0;
 
8892
        char password[AST_MAX_EXTENSION]="", *passptr;
 
8893
        struct ast_vm_user vmus, *vmu = NULL;
 
8894
 
 
8895
        /* If ADSI is supported, setup login screen */
 
8896
        adsi_begin(chan, &useadsi);
 
8897
        if (!skipuser && useadsi)
 
8898
                adsi_login(chan);
 
8899
        if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
 
8900
                ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
 
8901
                return -1;
 
8902
        }
 
8903
        
 
8904
        /* Authenticate them and get their mailbox/password */
 
8905
        
 
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");
 
8910
                        return -1;
 
8911
                }
 
8912
                if (ast_strlen_zero(mailbox)) {
 
8913
                        if (chan->cid.cid_num) {
 
8914
                                ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
 
8915
                        } else {
 
8916
                                ast_verb(3,"Username not entered\n");   
 
8917
                                return -1;
 
8918
                        }
 
8919
                }
 
8920
                if (useadsi)
 
8921
                        adsi_password(chan);
 
8922
 
 
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);
 
8928
                }
 
8929
 
 
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 */
 
8934
                        password[0] = '\0';
 
8935
                } else {
 
8936
                        if (ast_streamfile(chan, vm_password, chan->language)) {
 
8937
                                ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
 
8938
                                return -1;
 
8939
                        }
 
8940
                        if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
 
8941
                                ast_log(AST_LOG_WARNING, "Unable to read password\n");
 
8942
                                return -1;
 
8943
                        }
 
8944
                }
 
8945
 
 
8946
                if (vmu) {
 
8947
                        passptr = vmu->password;
 
8948
                        if (passptr[0] == '-') passptr++;
 
8949
                }
 
8950
                if (vmu && !strcmp(passptr, password))
 
8951
                        valid++;
 
8952
                else {
 
8953
                        ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
 
8954
                        if (!ast_strlen_zero(prefix))
 
8955
                                mailbox[0] = '\0';
 
8956
                }
 
8957
                logretries++;
 
8958
                if (!valid) {
 
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");
 
8962
                                        return -1;
 
8963
                                }
 
8964
                        } else {
 
8965
                                if (useadsi)
 
8966
                                        adsi_login(chan);
 
8967
                                if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
 
8968
                                        ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
 
8969
                                        return -1;
 
8970
                                }
 
8971
                        }
 
8972
                        if (ast_waitstream(chan, ""))   /* Channel is hung up */
 
8973
                                return -1;
 
8974
                }
 
8975
        }
 
8976
        if (!valid && (logretries >= max_logins)) {
 
8977
                ast_stopstream(chan);
 
8978
                ast_play_and_wait(chan, "vm-goodbye");
 
8979
                return -1;
 
8980
        }
 
8981
        if (vmu && !skipuser) {
 
8982
                memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
 
8983
        }
 
8984
        return 0;
 
8985
}
 
8986
 
 
8987
static int vm_execmain(struct ast_channel *chan, void *data)
 
8988
{
 
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 */
 
8992
        int res=-1;
 
8993
        int cmd=0;
 
8994
        int valid = 0;
 
8995
        char prefixstr[80] ="";
 
8996
        char ext_context[256]="";
 
8997
        int box;
 
8998
        int useadsi = 0;
 
8999
        int skipuser = 0;
 
9000
        struct vm_state vms;
 
9001
        struct ast_vm_user *vmu = NULL, vmus;
 
9002
        char *context=NULL;
 
9003
        int silentexit = 0;
 
9004
        struct ast_flags flags = { 0 };
 
9005
        signed char record_gain = 0;
 
9006
        int play_auto = 0;
 
9007
        int play_folder = 0;
 
9008
        int in_urgent = 0;
 
9009
#ifdef IMAP_STORAGE
 
9010
        int deleted = 0;
 
9011
#endif
 
9012
 
 
9013
        /* Add the vm_state to the active list and keep it active */
 
9014
        memset(&vms, 0, sizeof(vms));
 
9015
 
 
9016
        vms.lastmsg = -1;
 
9017
 
 
9018
        memset(&vmus, 0, sizeof(vmus));
 
9019
 
 
9020
        if (chan->_state != AST_STATE_UP) {
 
9021
                ast_debug(1, "Before ast_answer\n");
 
9022
                ast_answer(chan);
 
9023
        }
 
9024
 
 
9025
        if (!ast_strlen_zero(data)) {
 
9026
                char *opts[OPT_ARG_ARRAY_SIZE];
 
9027
                char *parse;
 
9028
                AST_DECLARE_APP_ARGS(args,
 
9029
                        AST_APP_ARG(argv0);
 
9030
                        AST_APP_ARG(argv1);
 
9031
                );
 
9032
 
 
9033
                parse = ast_strdupa(data);
 
9034
 
 
9035
                AST_STANDARD_APP_ARGS(args, parse);
 
9036
 
 
9037
                if (args.argc == 2) {
 
9038
                        if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
 
9039
                                return -1;
 
9040
                        if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
 
9041
                                int gain;
 
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]);
 
9045
                                                return -1;
 
9046
                                        } else {
 
9047
                                                record_gain = (signed char) gain;
 
9048
                                        }
 
9049
                                } else {
 
9050
                                        ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
 
9051
                                }
 
9052
                        }
 
9053
                        if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
 
9054
                                play_auto = 1;
 
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]);
 
9058
                                        }
 
9059
                                } else {
 
9060
                                        ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
 
9061
                                }       
 
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);
 
9064
                                        play_folder = 0;
 
9065
                                }
 
9066
                        }
 
9067
                } else {
 
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);
 
9074
                                else 
 
9075
                                        break;
 
9076
                                (args.argv0)++;
 
9077
                        }
 
9078
 
 
9079
                }
 
9080
 
 
9081
                valid = ast_test_flag(&flags, OPT_SILENT);
 
9082
 
 
9083
                if ((context = strchr(args.argv0, '@')))
 
9084
                        *context++ = '\0';
 
9085
 
 
9086
                if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
 
9087
                        ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
 
9088
                else
 
9089
                        ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
 
9090
 
 
9091
                if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
 
9092
                        skipuser++;
 
9093
                else
 
9094
                        valid = 0;
 
9095
        }
 
9096
 
 
9097
        if (!valid)
 
9098
                res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
 
9099
 
 
9100
        ast_debug(1, "After vm_authenticate\n");
 
9101
        if (!res) {
 
9102
                valid = 1;
 
9103
                if (!skipuser)
 
9104
                        vmu = &vmus;
 
9105
        } else {
 
9106
                res = 0;
 
9107
        }
 
9108
 
 
9109
        /* If ADSI is supported, setup login screen */
 
9110
        adsi_begin(chan, &useadsi);
 
9111
 
 
9112
        if (!valid) {
 
9113
                goto out;
 
9114
        }
 
9115
 
 
9116
#ifdef IMAP_STORAGE
 
9117
        pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
 
9118
        pthread_setspecific(ts_vmstate.key, &vms);
 
9119
 
 
9120
        vms.interactive = 1;
 
9121
        vms.updated = 1;
 
9122
        if (vmu)
 
9123
                ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
 
9124
        vmstate_insert(&vms);
 
9125
        init_vm_state(&vms);
 
9126
#endif
 
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");
 
9130
                return -1;
 
9131
        }
 
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");
 
9135
                return -1;
 
9136
        }
 
9137
        
 
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);
 
9141
 
 
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)
 
9146
                goto out;
 
9147
        vms.oldmessages = vms.lastmsg + 1;
 
9148
        ast_debug(1, "Number of old messages: %d\n",vms.oldmessages);
 
9149
        /* check INBOX */
 
9150
        res = open_mailbox(&vms, vmu, NEW_FOLDER);
 
9151
        if (res == ERROR_LOCK_PATH)
 
9152
                goto out;
 
9153
        vms.newmessages = vms.lastmsg + 1;
 
9154
        ast_debug(1, "Number of new messages: %d\n",vms.newmessages);
 
9155
        /* Start in Urgent */
 
9156
        in_urgent = 1;
 
9157
        res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
 
9158
        if (res == ERROR_LOCK_PATH)
 
9159
                goto out;
 
9160
        vms.urgentmessages = vms.lastmsg + 1;
 
9161
        ast_debug(1, "Number of urgent messages: %d\n",vms.urgentmessages);
 
9162
 
 
9163
        /* Select proper mailbox FIRST!! */
 
9164
        if (play_auto) {
 
9165
                if (vms.urgentmessages) {
 
9166
                        in_urgent = 1;
 
9167
                        res = open_mailbox(&vms, vmu, 11);
 
9168
                } else {
 
9169
                        in_urgent = 0;
 
9170
                        res = open_mailbox(&vms, vmu, play_folder);
 
9171
                }
 
9172
                if (res == ERROR_LOCK_PATH)
 
9173
                        goto out;
 
9174
 
 
9175
                /* If there are no new messages, inform the user and hangup */
 
9176
                if (vms.lastmsg == -1) {
 
9177
                        in_urgent = 0;
 
9178
                        cmd = vm_browse_messages(chan, &vms, vmu);
 
9179
                        res = 0;
 
9180
                        goto out;
 
9181
                }
 
9182
        } else {
 
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 */
 
9186
                        in_urgent = 0;
 
9187
                        play_folder = 1;
 
9188
                        if (res == ERROR_LOCK_PATH)
 
9189
                                goto out;
 
9190
                } else if (!vms.urgentmessages && vms.newmessages) {
 
9191
                        /* If we have new messages but none are urgent */
 
9192
                        in_urgent = 0;
 
9193
                        res = open_mailbox(&vms, vmu, NEW_FOLDER);
 
9194
                        if (res == ERROR_LOCK_PATH)
 
9195
                                goto out;
 
9196
                }
 
9197
        }
 
9198
 
 
9199
        if (useadsi)
 
9200
                adsi_status(chan, &vms);
 
9201
        res = 0;
 
9202
 
 
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 == '#')) {
 
9210
                        /* Timeout */
 
9211
                        res = 0;
 
9212
                        goto out;
 
9213
                } else if (cmd < 0) {
 
9214
                        /* Hangup */
 
9215
                        res = -1;
 
9216
                        goto out;
 
9217
                }
 
9218
        }
 
9219
#ifdef IMAP_STORAGE
 
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");
 
9224
                }
 
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");
 
9229
                }
 
9230
#endif
 
9231
        if (play_auto) {
 
9232
                cmd = '1';
 
9233
        } else {
 
9234
                cmd = vm_intro(chan, vmu, &vms);
 
9235
        }
 
9236
 
 
9237
        vms.repeats = 0;
 
9238
        vms.starting = 1;
 
9239
        while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
 
9240
                /* Run main menu */
 
9241
                switch (cmd) {
 
9242
                case '1': /* First message */
 
9243
                        vms.curmsg = 0;
 
9244
                        /* Fall through */
 
9245
                case '5': /* Play current message */
 
9246
                        cmd = vm_browse_messages(chan, &vms, vmu);
 
9247
                        break;
 
9248
                case '2': /* Change folders */
 
9249
                        if (useadsi)
 
9250
                                adsi_folders(chan, 0, "Change to folder...");
 
9251
                        cmd = get_folder2(chan, "vm-changeto", 0);
 
9252
                        if (cmd == '#') {
 
9253
                                cmd = 0;
 
9254
                        } else if (cmd > 0) {
 
9255
                                cmd = cmd - '0';
 
9256
                                res = close_mailbox(&vms, vmu);
 
9257
                                if (res == ERROR_LOCK_PATH)
 
9258
                                        goto out;
 
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)
 
9263
                                        goto out;
 
9264
                                play_folder = cmd;
 
9265
                                cmd = 0;
 
9266
                        }
 
9267
                        if (useadsi)
 
9268
                                adsi_status2(chan, &vms);
 
9269
                                
 
9270
                        if (!cmd)
 
9271
                                cmd = vm_play_folder_name(chan, vms.vmbox);
 
9272
 
 
9273
                        vms.starting = 1;
 
9274
                        break;
 
9275
                case '3': /* Advanced options */
 
9276
                        cmd = 0;
 
9277
                        vms.repeats = 0;
 
9278
                        while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
 
9279
                                switch (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) {
 
9284
                                                        res = cmd;
 
9285
                                                        goto out;
 
9286
                                                }
 
9287
                                        } else
 
9288
                                                cmd = ast_play_and_wait(chan, "vm-sorry");
 
9289
                                        cmd = 't';
 
9290
                                        break;
 
9291
                                case '2': /* Callback */
 
9292
                                        if (!vms.starting)
 
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);
 
9296
                                                if (cmd == 9) {
 
9297
                                                        silentexit = 1;
 
9298
                                                        goto out;
 
9299
                                                } else if (cmd == ERROR_LOCK_PATH) {
 
9300
                                                        res = cmd;
 
9301
                                                        goto out;
 
9302
                                                }
 
9303
                                        } else 
 
9304
                                                cmd = ast_play_and_wait(chan, "vm-sorry");
 
9305
                                        cmd = 't';
 
9306
                                        break;
 
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) {
 
9311
                                                        res = cmd;
 
9312
                                                        goto out;
 
9313
                                                }
 
9314
                                        } else
 
9315
                                                cmd = ast_play_and_wait(chan, "vm-sorry");
 
9316
                                        cmd = 't';
 
9317
                                        break;
 
9318
                                case '4': /* Dialout */
 
9319
                                        if (!ast_strlen_zero(vmu->dialout)) {
 
9320
                                                cmd = dialout(chan, vmu, NULL, vmu->dialout);
 
9321
                                                if (cmd == 9) {
 
9322
                                                        silentexit = 1;
 
9323
                                                        goto out;
 
9324
                                                }
 
9325
                                        } else 
 
9326
                                                cmd = ast_play_and_wait(chan, "vm-sorry");
 
9327
                                        cmd = 't';
 
9328
                                        break;
 
9329
 
 
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) {
 
9334
                                                        res = cmd;
 
9335
                                                        ast_log(AST_LOG_WARNING, "forward_message failed to lock path.\n");
 
9336
                                                        goto out;
 
9337
                                                }
 
9338
                                        } else
 
9339
                                                cmd = ast_play_and_wait(chan,"vm-sorry");
 
9340
                                        cmd='t';
 
9341
                                        break;
 
9342
                                        
 
9343
                                case '*': /* Return to main menu */
 
9344
                                        cmd = 't';
 
9345
                                        break;
 
9346
 
 
9347
                                default:
 
9348
                                        cmd = 0;
 
9349
                                        if (!vms.starting) {
 
9350
                                                cmd = ast_play_and_wait(chan, "vm-toreply");
 
9351
                                        }
 
9352
                                        if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
 
9353
                                                cmd = ast_play_and_wait(chan, "vm-tocallback");
 
9354
                                        }
 
9355
                                        if (!cmd && !vms.starting) {
 
9356
                                                cmd = ast_play_and_wait(chan, "vm-tohearenv");
 
9357
                                        }
 
9358
                                        if (!ast_strlen_zero(vmu->dialout) && !cmd) {
 
9359
                                                cmd = ast_play_and_wait(chan, "vm-tomakecall");
 
9360
                                        }
 
9361
                                        if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
 
9362
                                                cmd=ast_play_and_wait(chan, "vm-leavemsg");
 
9363
                                        if (!cmd)
 
9364
                                                cmd = ast_play_and_wait(chan, "vm-starmain");
 
9365
                                        if (!cmd)
 
9366
                                                cmd = ast_waitfordigit(chan,6000);
 
9367
                                        if (!cmd)
 
9368
                                                vms.repeats++;
 
9369
                                        if (vms.repeats > 3)
 
9370
                                                cmd = 't';
 
9371
                                }
 
9372
                        }
 
9373
                        if (cmd == 't') {
 
9374
                                cmd = 0;
 
9375
                                vms.repeats = 0;
 
9376
                        }
 
9377
                        break;
 
9378
                case '4': /* Go to the previous message */
 
9379
                        if (vms.curmsg > 0) {
 
9380
                                vms.curmsg--;
 
9381
                                cmd = play_message(chan, vmu, &vms);
 
9382
                        } else {
 
9383
                                /* Check if we were listening to new
 
9384
                                   messages.  If so, go to Urgent messages
 
9385
                                   instead of saying "no more messages"
 
9386
                                */
 
9387
                                if (in_urgent == 0 && vms.urgentmessages > 0) {
 
9388
                                        /* Check for Urgent messages */
 
9389
                                        in_urgent = 1;
 
9390
                                        res = close_mailbox(&vms, vmu);
 
9391
                                        if (res == ERROR_LOCK_PATH)
 
9392
                                                goto out;
 
9393
                                        res = open_mailbox(&vms, vmu, 11);  /* Open Urgent folder */
 
9394
                                        if (res == ERROR_LOCK_PATH)
 
9395
                                                goto out;
 
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);
 
9403
                                } else {
 
9404
                                        cmd = ast_play_and_wait(chan, "vm-nomore");
 
9405
                                }
 
9406
                        }
 
9407
                        break;
 
9408
                case '6': /* Go to the next message */
 
9409
                        if (vms.curmsg < vms.lastmsg) {
 
9410
                                vms.curmsg++;
 
9411
                                cmd = play_message(chan, vmu, &vms);
 
9412
                        } else {
 
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"
 
9417
                                         */
 
9418
                                        in_urgent = 0;
 
9419
                                        res = close_mailbox(&vms, vmu);
 
9420
                                        if (res == ERROR_LOCK_PATH)
 
9421
                                                goto out;
 
9422
                                        res = open_mailbox(&vms, vmu, NEW_FOLDER);
 
9423
                                        if (res == ERROR_LOCK_PATH)
 
9424
                                                goto out;
 
9425
                                        ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
 
9426
                                        vms.curmsg = -1;
 
9427
                                        if (vms.lastmsg < 0) {
 
9428
                                                cmd = ast_play_and_wait(chan, "vm-nomore");
 
9429
                                        }
 
9430
                                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
 
9431
                                        vms.curmsg = 0;
 
9432
                                        cmd = play_message(chan, vmu, &vms);
 
9433
                                } else {
 
9434
                                        cmd = ast_play_and_wait(chan, "vm-nomore");
 
9435
                                }
 
9436
                        }
 
9437
                        break;
 
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];
 
9441
                                if (useadsi)
 
9442
                                        adsi_delete(chan, &vms);
 
9443
                                if (vms.deleted[vms.curmsg]) {
 
9444
                                        if (play_folder == 0) {
 
9445
                                                if (in_urgent) {
 
9446
                                                        vms.urgentmessages--;
 
9447
                                                } else {
 
9448
                                                        vms.newmessages--;
 
9449
                                                }
 
9450
                                        }
 
9451
                                        else if (play_folder == 1)
 
9452
                                                vms.oldmessages--;
 
9453
                                        cmd = ast_play_and_wait(chan, "vm-deleted");
 
9454
                                } else {
 
9455
                                        if (play_folder == 0) {
 
9456
                                                if (in_urgent) {
 
9457
                                                        vms.urgentmessages++;
 
9458
                                                } else {
 
9459
                                                        vms.newmessages++;
 
9460
                                                }
 
9461
                                        }
 
9462
                                        else if (play_folder == 1)
 
9463
                                                vms.oldmessages++;
 
9464
                                        cmd = ast_play_and_wait(chan, "vm-undeleted");
 
9465
                                }
 
9466
                                if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
 
9467
                                        if (vms.curmsg < vms.lastmsg) {
 
9468
                                                vms.curmsg++;
 
9469
                                                cmd = play_message(chan, vmu, &vms);
 
9470
                                        } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
 
9471
                                                vms.curmsg = 0;
 
9472
                                                cmd = play_message(chan, vmu, &vms);
 
9473
                                        } else {
 
9474
                                                /* Check if we were listening to urgent
 
9475
                                                   messages.  If so, go to regular new messages
 
9476
                                                   instead of saying "no more messages"
 
9477
                                                */
 
9478
                                                if (in_urgent == 1) {
 
9479
                                                        /* Check for new messages */
 
9480
                                                        in_urgent = 0;
 
9481
                                                        res = close_mailbox(&vms, vmu);
 
9482
                                                        if (res == ERROR_LOCK_PATH)
 
9483
                                                                goto out;
 
9484
                                                        res = open_mailbox(&vms, vmu, NEW_FOLDER);
 
9485
                                                        if (res == ERROR_LOCK_PATH)
 
9486
                                                                goto out;
 
9487
                                                        ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
 
9488
                                                        vms.curmsg = -1;
 
9489
                                                        if (vms.lastmsg < 0)
 
9490
                                                                cmd = ast_play_and_wait(chan, "vm-nomore");
 
9491
                                                } else {
 
9492
                                                        cmd = ast_play_and_wait(chan, "vm-nomore");
 
9493
                                                }
 
9494
                                        }
 
9495
                                }
 
9496
                        } else /* Delete not valid if we haven't selected a message */
 
9497
                                cmd = 0;
 
9498
#ifdef IMAP_STORAGE
 
9499
                        deleted = 1;
 
9500
#endif
 
9501
                        break;
 
9502
        
 
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) {
 
9507
                                        res = cmd;
 
9508
                                        goto out;
 
9509
                                }
 
9510
                        } else {
 
9511
                                /* Check if we were listening to urgent
 
9512
                                   messages.  If so, go to regular new messages
 
9513
                                   instead of saying "no more messages"
 
9514
                                */
 
9515
                                if (in_urgent == 1 && vms.newmessages > 0) {
 
9516
                                        /* Check for new messages */
 
9517
                                        in_urgent = 0;
 
9518
                                        res = close_mailbox(&vms, vmu);
 
9519
                                        if (res == ERROR_LOCK_PATH)
 
9520
                                                goto out;
 
9521
                                        res = open_mailbox(&vms, vmu, NEW_FOLDER);
 
9522
                                        if (res == ERROR_LOCK_PATH)
 
9523
                                                goto out;
 
9524
                                        ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
 
9525
                                        vms.curmsg = -1;
 
9526
                                        if (vms.lastmsg < 0)
 
9527
                                                cmd = ast_play_and_wait(chan, "vm-nomore");
 
9528
                                } else {
 
9529
                                        cmd = ast_play_and_wait(chan, "vm-nomore");
 
9530
                                }
 
9531
                        }
 
9532
                        break;
 
9533
                case '9': /* Save message to folder */
 
9534
                        if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
 
9535
                                /* No message selected */
 
9536
                                cmd = 0;
 
9537
                                break;
 
9538
                        }
 
9539
                        if (useadsi)
 
9540
                                adsi_folders(chan, 1, "Save to folder...");
 
9541
                        cmd = get_folder2(chan, "vm-savefolder", 1);
 
9542
                        box = 0;        /* Shut up compiler */
 
9543
                        if (cmd == '#') {
 
9544
                                cmd = 0;
 
9545
                                break;
 
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) {
 
9550
                                        res = cmd;
 
9551
                                        goto out;
 
9552
#ifndef IMAP_STORAGE
 
9553
                                } else if (!cmd) {
 
9554
                                        vms.deleted[vms.curmsg] = 1;
 
9555
#endif
 
9556
                                } else {
 
9557
                                        vms.deleted[vms.curmsg] = 0;
 
9558
                                        vms.heard[vms.curmsg] = 0;
 
9559
                                }
 
9560
                        }
 
9561
                        make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
 
9562
                        if (useadsi)
 
9563
                                adsi_message(chan, &vms);
 
9564
                        snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
 
9565
                        if (!cmd) {
 
9566
                                cmd = ast_play_and_wait(chan, "vm-message");
 
9567
                                if (!cmd) 
 
9568
                                        cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
 
9569
                                if (!cmd)
 
9570
                                        cmd = ast_play_and_wait(chan, "vm-savedto");
 
9571
                                if (!cmd)
 
9572
                                        cmd = vm_play_folder_name(chan, vms.fn);
 
9573
                        } else {
 
9574
                                cmd = ast_play_and_wait(chan, "vm-mailboxfull");
 
9575
                        }
 
9576
                        if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
 
9577
                                if (vms.curmsg < vms.lastmsg) {
 
9578
                                        vms.curmsg++;
 
9579
                                        cmd = play_message(chan, vmu, &vms);
 
9580
                                } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
 
9581
                                        vms.curmsg = 0;
 
9582
                                        cmd = play_message(chan, vmu, &vms);
 
9583
                                } else {
 
9584
                                        /* Check if we were listening to urgent
 
9585
                                           messages.  If so, go to regular new messages
 
9586
                                           instead of saying "no more messages"
 
9587
                                        */
 
9588
                                        if (in_urgent == 1 && vms.newmessages > 0) {
 
9589
                                                /* Check for new messages */
 
9590
                                                in_urgent = 0;
 
9591
                                                res = close_mailbox(&vms, vmu);
 
9592
                                                if (res == ERROR_LOCK_PATH)
 
9593
                                                        goto out;
 
9594
                                                res = open_mailbox(&vms, vmu, NEW_FOLDER);
 
9595
                                                if (res == ERROR_LOCK_PATH)
 
9596
                                                        goto out;
 
9597
                                                ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n",vms.lastmsg + 1);
 
9598
                                                vms.curmsg = -1;
 
9599
                                                if (vms.lastmsg < 0)
 
9600
                                                        cmd = ast_play_and_wait(chan, "vm-nomore");
 
9601
                                        } else {
 
9602
                                                cmd = ast_play_and_wait(chan, "vm-nomore");
 
9603
                                        }
 
9604
                                }
 
9605
                        }
 
9606
                        break;
 
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");
 
9612
                                }
 
9613
                                if (!cmd)
 
9614
                                        cmd = vm_play_folder_name(chan, vms.vmbox);
 
9615
                                if (!cmd)
 
9616
                                        cmd = ast_play_and_wait(chan, "vm-opts");
 
9617
                                if (!cmd)
 
9618
                                        cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
 
9619
                        } else
 
9620
                                cmd = 0;
 
9621
                        break;
 
9622
                case '0': /* Mailbox options */
 
9623
                        cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
 
9624
                        if (useadsi)
 
9625
                                adsi_status(chan, &vms);
 
9626
                        break;
 
9627
                default:        /* Nothing */
 
9628
                        cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
 
9629
                        break;
 
9630
                }
 
9631
        }
 
9632
        if ((cmd == 't') || (cmd == '#')) {
 
9633
                /* Timeout */
 
9634
                res = 0;
 
9635
        } else {
 
9636
                /* Hangup */
 
9637
                res = -1;
 
9638
        }
 
9639
 
 
9640
out:
 
9641
        if (res > -1) {
 
9642
                ast_stopstream(chan);
 
9643
                adsi_goodbye(chan);
 
9644
                if (valid) {
 
9645
                        if (silentexit)
 
9646
                                res = ast_play_and_wait(chan, "vm-dialout");
 
9647
                        else 
 
9648
                                res = ast_play_and_wait(chan, "vm-goodbye");
 
9649
                        if (res > 0)
 
9650
                                res = 0;
 
9651
                }
 
9652
                if (useadsi)
 
9653
                        ast_adsi_unload_session(chan);
 
9654
        }
 
9655
        if (vmu)
 
9656
                close_mailbox(&vms, vmu);
 
9657
        if (valid) {
 
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);
 
9665
        }
 
9666
#ifdef IMAP_STORAGE
 
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);
 
9674
                } else 
 
9675
#endif
 
9676
                        mail_expunge(vms.mailstream);
 
9677
                ast_mutex_unlock(&vms.lock);
 
9678
        }
 
9679
        /*  before we delete the state, we should copy pertinent info
 
9680
         *  back to the persistent model */
 
9681
        if (vmu) {
 
9682
                vmstate_delete(&vms);
 
9683
        }
 
9684
#endif
 
9685
        if (vmu)
 
9686
                free_user(vmu);
 
9687
        if (vms.deleted)
 
9688
                ast_free(vms.deleted);
 
9689
        if (vms.heard)
 
9690
                ast_free(vms.heard);
 
9691
 
 
9692
#ifdef IMAP_STORAGE
 
9693
        pthread_setspecific(ts_vmstate.key, NULL);
 
9694
#endif
 
9695
        return res;
 
9696
}
 
9697
 
 
9698
static int vm_exec(struct ast_channel *chan, void *data)
 
9699
{
 
9700
        int res = 0;
 
9701
        char *tmp;
 
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,
 
9706
                AST_APP_ARG(argv0);
 
9707
                AST_APP_ARG(argv1);
 
9708
        );
 
9709
        
 
9710
        memset(&leave_options, 0, sizeof(leave_options));
 
9711
 
 
9712
        if (chan->_state != AST_STATE_UP)
 
9713
                ast_answer(chan);
 
9714
 
 
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))
 
9720
                                return -1;
 
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)) {
 
9723
                                int gain;
 
9724
 
 
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]);
 
9727
                                        return -1;
 
9728
                                } else {
 
9729
                                        leave_options.record_gain = (signed char) gain;
 
9730
                                }
 
9731
                        }
 
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];
 
9735
                        }
 
9736
                }
 
9737
        } else {
 
9738
                char temp[256];
 
9739
                res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
 
9740
                if (res < 0)
 
9741
                        return res;
 
9742
                if (ast_strlen_zero(temp))
 
9743
                        return 0;
 
9744
                args.argv0 = ast_strdupa(temp);
 
9745
        }
 
9746
 
 
9747
        res = leave_voicemail(chan, args.argv0, &leave_options);
 
9748
 
 
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");
 
9752
                res = 0;
 
9753
        }
 
9754
 
 
9755
        return res;
 
9756
}
 
9757
 
 
9758
static struct ast_vm_user *find_or_create(const char *context, const char *box)
 
9759
{
 
9760
        struct ast_vm_user *vmu;
 
9761
 
 
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);
 
9769
                        }
 
9770
                        ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
 
9771
                        return NULL;
 
9772
                }
 
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);
 
9775
                        return NULL;
 
9776
                }
 
9777
        }
 
9778
        
 
9779
        if (!(vmu = ast_calloc(1, sizeof(*vmu))))
 
9780
                return NULL;
 
9781
        
 
9782
        ast_copy_string(vmu->context, context, sizeof(vmu->context));
 
9783
        ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
 
9784
 
 
9785
        AST_LIST_INSERT_TAIL(&users, vmu, list);
 
9786
        
 
9787
        return vmu;
 
9788
}
 
9789
 
 
9790
static int append_mailbox(const char *context, const char *box, const char *data)
 
9791
{
 
9792
        /* Assumes lock is already held */
 
9793
        char *tmp;
 
9794
        char *stringp;
 
9795
        char *s;
 
9796
        struct ast_vm_user *vmu;
 
9797
        char *mailbox_full;
 
9798
        int new = 0, old = 0, urgent = 0;
 
9799
 
 
9800
        tmp = ast_strdupa(data);
 
9801
 
 
9802
        if (!(vmu = find_or_create(context, box)))
 
9803
                return -1;
 
9804
        
 
9805
        populate_defaults(vmu);
 
9806
 
 
9807
        stringp = tmp;
 
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);
 
9818
 
 
9819
        mailbox_full = alloca(strlen(box) + strlen(context) + 1);
 
9820
        strcpy(mailbox_full, box);
 
9821
        strcat(mailbox_full, "@");
 
9822
        strcat(mailbox_full, context);
 
9823
 
 
9824
        inboxcount2(mailbox_full, &urgent, &new, &old);
 
9825
        queue_mwi_event(mailbox_full, urgent, new, old);
 
9826
 
 
9827
        return 0;
 
9828
}
 
9829
 
 
9830
static int vm_box_exists(struct ast_channel *chan, void *data) 
 
9831
{
 
9832
        struct ast_vm_user svm;
 
9833
        char *context, *box;
 
9834
        AST_DECLARE_APP_ARGS(args,
 
9835
                AST_APP_ARG(mbox);
 
9836
                AST_APP_ARG(options);
 
9837
        );
 
9838
        static int dep_warning = 0;
 
9839
 
 
9840
        if (ast_strlen_zero(data)) {
 
9841
                ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
 
9842
                return -1;
 
9843
        }
 
9844
 
 
9845
        if (!dep_warning) {
 
9846
                dep_warning = 1;
 
9847
                ast_log(AST_LOG_WARNING, "MailboxExists is deprecated.  Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *)data);
 
9848
        }
 
9849
 
 
9850
        box = ast_strdupa(data);
 
9851
 
 
9852
        AST_STANDARD_APP_ARGS(args, box);
 
9853
 
 
9854
        if (args.options) {
 
9855
        }
 
9856
 
 
9857
        if ((context = strchr(args.mbox, '@'))) {
 
9858
                *context = '\0';
 
9859
                context++;
 
9860
        }
 
9861
 
 
9862
        if (find_user(&svm, context, args.mbox)) {
 
9863
                pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
 
9864
        } else
 
9865
                pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
 
9866
 
 
9867
        return 0;
 
9868
}
 
9869
 
 
9870
static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
 
9871
{
 
9872
        struct ast_vm_user svm;
 
9873
        AST_DECLARE_APP_ARGS(arg,
 
9874
                AST_APP_ARG(mbox);
 
9875
                AST_APP_ARG(context);
 
9876
        );
 
9877
 
 
9878
        AST_NONSTANDARD_APP_ARGS(arg, args, '@');
 
9879
 
 
9880
        if (ast_strlen_zero(arg.mbox)) {
 
9881
                ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
 
9882
                return -1;
 
9883
        }
 
9884
 
 
9885
        ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
 
9886
        return 0;
 
9887
}
 
9888
 
 
9889
static struct ast_custom_function mailbox_exists_acf = {
 
9890
        .name = "MAILBOX_EXISTS",
 
9891
        .read = acf_mailbox_exists,
 
9892
};
 
9893
 
 
9894
static int vmauthenticate(struct ast_channel *chan, void *data)
 
9895
{
 
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;
 
9900
        int res = -1;
 
9901
        
 
9902
        if (s) {
 
9903
                s = ast_strdupa(s);
 
9904
                user = strsep(&s, ",");
 
9905
                options = strsep(&s, ",");
 
9906
                if (user) {
 
9907
                        s = user;
 
9908
                        user = strsep(&s, "@");
 
9909
                        context = strsep(&s, "");
 
9910
                        if (!ast_strlen_zero(user))
 
9911
                                skipuser++;
 
9912
                        ast_copy_string(mailbox, user, sizeof(mailbox));
 
9913
                }
 
9914
        }
 
9915
 
 
9916
        if (options) {
 
9917
                silent = (strchr(options, 's')) != NULL;
 
9918
        }
 
9919
 
 
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");
 
9924
                res = 0;
 
9925
        }
 
9926
 
 
9927
        return res;
 
9928
}
 
9929
 
 
9930
static char *show_users_realtime(int fd, const char *context)
 
9931
{
 
9932
        struct ast_config *cfg;
 
9933
        const char *cat = NULL;
 
9934
 
 
9935
        if (!(cfg = ast_load_realtime_multientry("voicemail", 
 
9936
                "context", context, SENTINEL))) {
 
9937
                return CLI_FAILURE;
 
9938
        }
 
9939
 
 
9940
        ast_cli(fd,
 
9941
                "\n"
 
9942
                "=============================================================\n"
 
9943
                "=== Configured Voicemail Users ==============================\n"
 
9944
                "=============================================================\n"
 
9945
                "===\n");
 
9946
 
 
9947
        while ((cat = ast_category_browse(cfg, cat))) {
 
9948
                struct ast_variable *var = NULL;
 
9949
                ast_cli(fd,
 
9950
                        "=== Mailbox ...\n"
 
9951
                        "===\n");
 
9952
                for (var = ast_variable_browse(cfg, cat); var; var = var->next)
 
9953
                        ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
 
9954
                ast_cli(fd,
 
9955
                        "===\n"
 
9956
                        "=== ---------------------------------------------------------\n"
 
9957
                        "===\n");
 
9958
        }
 
9959
 
 
9960
        ast_cli(fd,
 
9961
                "=============================================================\n"
 
9962
                "\n");
 
9963
 
 
9964
        ast_config_destroy(cfg);
 
9965
 
 
9966
        return CLI_SUCCESS;
 
9967
}
 
9968
 
 
9969
static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
 
9970
{
 
9971
        int which = 0;
 
9972
        int wordlen;
 
9973
        struct ast_vm_user *vmu;
 
9974
        const char *context = "";
 
9975
 
 
9976
        /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
 
9977
        if (pos > 4)
 
9978
                return NULL;
 
9979
        if (pos == 3)
 
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;
 
9988
                }
 
9989
        }
 
9990
        return NULL;
 
9991
}
 
9992
 
 
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)
 
9995
{
 
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;
 
10000
 
 
10001
        switch (cmd) {
 
10002
        case CLI_INIT:
 
10003
                e->command = "voicemail show users";
 
10004
                e->usage =
 
10005
                        "Usage: voicemail show users [for <context>]\n"
 
10006
                        "       Lists all mailboxes currently set up\n";
 
10007
                return NULL;
 
10008
        case CLI_GENERATE:
 
10009
                return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
 
10010
        }       
 
10011
 
 
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];
 
10018
        }
 
10019
 
 
10020
        if (ast_check_realtime("voicemail")) {
 
10021
                if (!context) {
 
10022
                        ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
 
10023
                        return CLI_SHOWUSAGE;
 
10024
                }
 
10025
                return show_users_realtime(a->fd, context);
 
10026
        }
 
10027
 
 
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;
 
10033
        }
 
10034
        if (a->argc == 3)
 
10035
                ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
 
10036
        else {
 
10037
                int count = 0;
 
10038
                AST_LIST_TRAVERSE(&users, vmu, list) {
 
10039
                        if (!strcmp(context, vmu->context))
 
10040
                                count++;
 
10041
                }
 
10042
                if (count) {
 
10043
                        ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
 
10044
                } else {
 
10045
                        ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
 
10046
                        AST_LIST_UNLOCK(&users);
 
10047
                        return CLI_FAILURE;
 
10048
                }
 
10049
        }
 
10050
        AST_LIST_TRAVERSE(&users, vmu, list) {
 
10051
                int newmsgs = 0, oldmsgs = 0;
 
10052
                char count[12], tmp[256] = "";
 
10053
 
 
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);
 
10059
                        users_counter++;
 
10060
                }
 
10061
        }
 
10062
        AST_LIST_UNLOCK(&users);
 
10063
        ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
 
10064
        return CLI_SUCCESS;
 
10065
}
 
10066
 
 
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)
 
10069
{
 
10070
        struct vm_zone *zone;
 
10071
#define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
 
10072
        char *res = CLI_SUCCESS;
 
10073
 
 
10074
        switch (cmd) {
 
10075
        case CLI_INIT:
 
10076
                e->command = "voicemail show zones";
 
10077
                e->usage =
 
10078
                        "Usage: voicemail show zones\n"
 
10079
                        "       Lists zone message formats\n";
 
10080
                return NULL;
 
10081
        case CLI_GENERATE:
 
10082
                return NULL;
 
10083
        }
 
10084
 
 
10085
        if (a->argc != 3)
 
10086
                return CLI_SHOWUSAGE;
 
10087
 
 
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);
 
10093
                }
 
10094
        } else {
 
10095
                ast_cli(a->fd, "There are no voicemail zones currently defined\n");
 
10096
                res = CLI_FAILURE;
 
10097
        }
 
10098
        AST_LIST_UNLOCK(&zones);
 
10099
 
 
10100
        return res;
 
10101
}
 
10102
 
 
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)
 
10105
{
 
10106
        switch (cmd) {
 
10107
        case CLI_INIT:
 
10108
                e->command = "voicemail reload";
 
10109
                e->usage =
 
10110
                        "Usage: voicemail reload\n"
 
10111
                        "       Reload voicemail configuration\n";
 
10112
                return NULL;
 
10113
        case CLI_GENERATE:
 
10114
                return NULL;
 
10115
        }
 
10116
 
 
10117
        if (a->argc != 2)
 
10118
                return CLI_SHOWUSAGE;
 
10119
 
 
10120
        ast_cli(a->fd, "Reloading voicemail configuration...\n");       
 
10121
        load_config(1);
 
10122
        
 
10123
        return CLI_SUCCESS;
 
10124
}
 
10125
 
 
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"),
 
10130
};
 
10131
 
 
10132
static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
 
10133
{
 
10134
        int new = 0, old = 0, urgent = 0;
 
10135
 
 
10136
        inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
 
10137
 
 
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);
 
10143
        }
 
10144
}
 
10145
 
 
10146
static void poll_subscribed_mailboxes(void)
 
10147
{
 
10148
        struct mwi_sub *mwi_sub;
 
10149
 
 
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);
 
10154
                }
 
10155
        }
 
10156
        AST_RWLIST_UNLOCK(&mwi_subs);
 
10157
}
 
10158
 
 
10159
static void *mb_poll_thread(void *data)
 
10160
{
 
10161
        while (poll_thread_run) {
 
10162
                struct timespec ts = { 0, };
 
10163
                struct timeval wait;
 
10164
 
 
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;
 
10168
 
 
10169
                ast_mutex_lock(&poll_lock);
 
10170
                ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
 
10171
                ast_mutex_unlock(&poll_lock);
 
10172
 
 
10173
                if (!poll_thread_run)
 
10174
                        break;
 
10175
 
 
10176
                poll_subscribed_mailboxes();
 
10177
        }
 
10178
 
 
10179
        return NULL;
 
10180
}
 
10181
 
 
10182
static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
 
10183
{
 
10184
        ast_free(mwi_sub);
 
10185
}
 
10186
 
 
10187
static int handle_unsubscribe(void *datap)
 
10188
{
 
10189
        struct mwi_sub *mwi_sub;
 
10190
        uint32_t *uniqueid = datap;
 
10191
        
 
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);
 
10196
                        break;
 
10197
                }
 
10198
        }
 
10199
        AST_RWLIST_TRAVERSE_SAFE_END
 
10200
        AST_RWLIST_UNLOCK(&mwi_subs);
 
10201
 
 
10202
        if (mwi_sub)
 
10203
                mwi_sub_destroy(mwi_sub);
 
10204
 
 
10205
        ast_free(uniqueid);     
 
10206
        return 0;
 
10207
}
 
10208
 
 
10209
static int handle_subscribe(void *datap)
 
10210
{
 
10211
        unsigned int len;
 
10212
        struct mwi_sub *mwi_sub;
 
10213
        struct mwi_sub_task *p = datap;
 
10214
 
 
10215
        len = sizeof(*mwi_sub);
 
10216
        if (!ast_strlen_zero(p->mailbox))
 
10217
                len += strlen(p->mailbox);
 
10218
 
 
10219
        if (!ast_strlen_zero(p->context))
 
10220
                len += strlen(p->context) + 1; /* Allow for seperator */
 
10221
 
 
10222
        if (!(mwi_sub = ast_calloc(1, len)))
 
10223
                return -1;
 
10224
 
 
10225
        mwi_sub->uniqueid = p->uniqueid;
 
10226
        if (!ast_strlen_zero(p->mailbox))
 
10227
                strcpy(mwi_sub->mailbox, p->mailbox);
 
10228
 
 
10229
        if (!ast_strlen_zero(p->context)) {
 
10230
                strcat(mwi_sub->mailbox, "@");
 
10231
                strcat(mwi_sub->mailbox, p->context);
 
10232
        }
 
10233
 
 
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);
 
10239
        ast_free(p);
 
10240
        poll_subscribed_mailbox(mwi_sub);
 
10241
        return 0;
 
10242
}
 
10243
 
 
10244
static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
 
10245
{
 
10246
        uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
 
10247
        if (ast_event_get_type(event) != AST_EVENT_UNSUB)
 
10248
                return;
 
10249
 
 
10250
        if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
 
10251
                return;
 
10252
 
 
10253
        u = ast_event_get_ie_uint(event, AST_EVENT_IE_UNIQUEID);
 
10254
        *uniqueid = u;
 
10255
        if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
 
10256
                ast_free(uniqueid);
 
10257
        }
 
10258
}
 
10259
 
 
10260
static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
 
10261
{
 
10262
        struct mwi_sub_task *mwist;
 
10263
        
 
10264
        if (ast_event_get_type(event) != AST_EVENT_SUB)
 
10265
                return;
 
10266
 
 
10267
        if (ast_event_get_ie_uint(event, AST_EVENT_IE_EVENTTYPE) != AST_EVENT_MWI)
 
10268
                return;
 
10269
 
 
10270
        if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
 
10271
                ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
 
10272
                return;
 
10273
        }
 
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);
 
10277
        
 
10278
        if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
 
10279
                ast_free(mwist);
 
10280
        }
 
10281
}
 
10282
 
 
10283
static void start_poll_thread(void)
 
10284
{
 
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,
 
10287
                AST_EVENT_IE_END);
 
10288
 
 
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,
 
10291
                AST_EVENT_IE_END);
 
10292
 
 
10293
        if (mwi_sub_sub)
 
10294
                ast_event_report_subs(mwi_sub_sub);
 
10295
 
 
10296
        poll_thread_run = 1;
 
10297
 
 
10298
        ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL);
 
10299
}
 
10300
 
 
10301
static void stop_poll_thread(void)
 
10302
{
 
10303
        poll_thread_run = 0;
 
10304
 
 
10305
        if (mwi_sub_sub) {
 
10306
                ast_event_unsubscribe(mwi_sub_sub);
 
10307
                mwi_sub_sub = NULL;
 
10308
        }
 
10309
 
 
10310
        if (mwi_unsub_sub) {
 
10311
                ast_event_unsubscribe(mwi_unsub_sub);
 
10312
                mwi_unsub_sub = NULL;
 
10313
        }
 
10314
 
 
10315
        ast_mutex_lock(&poll_lock);
 
10316
        ast_cond_signal(&poll_cond);
 
10317
        ast_mutex_unlock(&poll_lock);
 
10318
 
 
10319
        pthread_join(poll_thread, NULL);
 
10320
 
 
10321
        poll_thread = AST_PTHREADT_NULL;
 
10322
}
 
10323
 
 
10324
/*! \brief Manager list voicemail users command */
 
10325
static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
 
10326
{
 
10327
        struct ast_vm_user *vmu = NULL;
 
10328
        const char *id = astman_get_header(m, "ActionID");
 
10329
        char actionid[128] = "";
 
10330
 
 
10331
        if (!ast_strlen_zero(id))
 
10332
                snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
 
10333
 
 
10334
        AST_LIST_LOCK(&users);
 
10335
 
 
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;
 
10340
        }
 
10341
        
 
10342
        astman_send_ack(s, m, "Voicemail user list will follow");
 
10343
        
 
10344
        AST_LIST_TRAVERSE(&users, vmu, list) {
 
10345
                char dirname[256];
 
10346
 
 
10347
#ifdef IMAP_STORAGE
 
10348
                int new, old;
 
10349
                inboxcount(vmu->mailbox, &new, &old);
 
10350
#endif
 
10351
                
 
10352
                make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
 
10353
                astman_append(s,
 
10354
                        "%s"
 
10355
                        "Event: VoicemailUserEntry\r\n"
 
10356
                        "VMContext: %s\r\n"
 
10357
                        "VoiceMailbox: %s\r\n"
 
10358
                        "Fullname: %s\r\n"
 
10359
                        "Email: %s\r\n"
 
10360
                        "Pager: %s\r\n"
 
10361
                        "ServerEmail: %s\r\n"
 
10362
                        "MailCommand: %s\r\n"
 
10363
                        "Language: %s\r\n"
 
10364
                        "TimeZone: %s\r\n"
 
10365
                        "Callback: %s\r\n"
 
10366
                        "Dialout: %s\r\n"
 
10367
                        "UniqueID: %s\r\n"
 
10368
                        "ExitContext: %s\r\n"
 
10369
                        "SayDurationMinimum: %d\r\n"
 
10370
                        "SayEnvelope: %s\r\n"
 
10371
                        "SayCID: %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"
 
10383
                        "IMAPUser: %s\r\n"
 
10384
#endif
 
10385
                        "\r\n",
 
10386
                        actionid,
 
10387
                        vmu->context,
 
10388
                        vmu->mailbox,
 
10389
                        vmu->fullname,
 
10390
                        vmu->email,
 
10391
                        vmu->pager,
 
10392
                        vmu->serveremail,
 
10393
                        vmu->mailcmd,
 
10394
                        vmu->language,
 
10395
                        vmu->zonetag,
 
10396
                        vmu->callback,
 
10397
                        vmu->dialout,
 
10398
                        vmu->uniqueid,
 
10399
                        vmu->exit,
 
10400
                        vmu->saydurationm,
 
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",
 
10404
                        vmu->attachfmt,
 
10405
                        ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
 
10406
                        vmu->volgain,
 
10407
                        ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
 
10408
                        ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
 
10409
                        vmu->maxmsg,
 
10410
                        vmu->maxsecs,
 
10411
#ifdef IMAP_STORAGE
 
10412
                        new, old, vmu->imapuser
 
10413
#else
 
10414
                        count_messages(vmu, dirname)
 
10415
#endif
 
10416
                        );
 
10417
        }               
 
10418
        astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
 
10419
 
 
10420
        AST_LIST_UNLOCK(&users);
 
10421
 
 
10422
        return RESULT_SUCCESS;
 
10423
}
 
10424
 
 
10425
/*! \brief Free the users structure. */
 
10426
static void free_vm_users(void) 
 
10427
{
 
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);
 
10433
        }
 
10434
        AST_LIST_UNLOCK(&users);
 
10435
}
 
10436
 
 
10437
/*! \brief Free the zones structure. */
 
10438
static void free_vm_zones(void)
 
10439
{
 
10440
        struct vm_zone *zcur;
 
10441
        AST_LIST_LOCK(&zones);
 
10442
        while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
 
10443
                free_zone(zcur);
 
10444
        AST_LIST_UNLOCK(&zones);
 
10445
}
 
10446
 
 
10447
static const char *substitute_escapes(const char *value)
 
10448
{
 
10449
        char *current;
 
10450
 
 
10451
        /* Add 16 for fudge factor */
 
10452
        struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
 
10453
 
 
10454
        ast_str_reset(str);
 
10455
        
 
10456
        /* Substitute strings \r, \n, and \t into the appropriate characters */
 
10457
        for (current = (char *) value; *current; current++) {
 
10458
                if (*current == '\\') {
 
10459
                        current++;
 
10460
                        if (!*current) {
 
10461
                                ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
 
10462
                                break;
 
10463
                        }
 
10464
                        switch (*current) {
 
10465
                        case 'r':
 
10466
                                ast_str_append(&str, 0, "\r");
 
10467
                                break;
 
10468
                        case 'n':
 
10469
#ifdef IMAP_STORAGE
 
10470
                                if (!str->used || str->str[str->used - 1] != '\r') {
 
10471
                                        ast_str_append(&str, 0, "\r");
 
10472
                                }
 
10473
#endif
 
10474
                                ast_str_append(&str, 0, "\n");
 
10475
                                break;
 
10476
                        case 't':
 
10477
                                ast_str_append(&str, 0, "\t");
 
10478
                                break;
 
10479
                        default:
 
10480
                                ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
 
10481
                                break;
 
10482
                        }
 
10483
                } else {
 
10484
                        ast_str_append(&str, 0, "%c", *current);
 
10485
                }
 
10486
        }
 
10487
 
 
10488
        return ast_str_buffer(str);
 
10489
}
 
10490
 
 
10491
static int load_config(int reload)
 
10492
{
 
10493
        struct ast_vm_user *current;
 
10494
        struct ast_config *cfg, *ucfg;
 
10495
        char *cat;
 
10496
        struct ast_variable *var;
 
10497
        const char *val;
 
10498
        char *q, *stringp, *tmp;
 
10499
        int x;
 
10500
        int tmpadsi[4];
 
10501
        struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
 
10502
 
 
10503
        ast_unload_realtime("voicemail");
 
10504
        ast_unload_realtime("voicemail_data");
 
10505
 
 
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) {
 
10508
                        return 0;
 
10509
                } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
 
10510
                        ast_log(LOG_ERROR, "Config file users.conf is in an invalid format.  Avoiding.\n");
 
10511
                        ucfg = NULL;
 
10512
                }
 
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");
 
10517
                        return 0;
 
10518
                }
 
10519
        } else if (cfg == CONFIG_STATUS_FILEINVALID) {
 
10520
                ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format.  Aborting.\n");
 
10521
                return 0;
 
10522
        } else {
 
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");
 
10526
                        ucfg = NULL;
 
10527
                }
 
10528
        }
 
10529
#ifdef IMAP_STORAGE
 
10530
        ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
 
10531
#endif
 
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);
 
10538
 
 
10539
        /* Free all the users structure */      
 
10540
        free_vm_users();
 
10541
 
 
10542
        /* Free all the zones structure */
 
10543
        free_vm_zones();
 
10544
 
 
10545
        AST_LIST_LOCK(&users);  
 
10546
 
 
10547
        memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
 
10548
        memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
 
10549
 
 
10550
        if (cfg) {
 
10551
                /* General settings */
 
10552
 
 
10553
                if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
 
10554
                        val = "default";
 
10555
                ast_copy_string(userscontext, val, sizeof(userscontext));
 
10556
                /* Attach voice message to mail message ? */
 
10557
                if (!(val = ast_variable_retrieve(cfg, "general", "attach"))) 
 
10558
                        val = "yes";
 
10559
                ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH);        
 
10560
 
 
10561
                if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
 
10562
                        val = "no";
 
10563
                ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
 
10564
 
 
10565
                volgain = 0.0;
 
10566
                if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
 
10567
                        sscanf(val, "%30lf", &volgain);
 
10568
 
 
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));
 
10573
                }
 
10574
                strcpy(odbc_table, "voicemessages");
 
10575
                if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
 
10576
                        ast_copy_string(odbc_table, val, sizeof(odbc_table));
 
10577
                }
 
10578
#endif          
 
10579
                /* Mail command */
 
10580
                strcpy(mailcmd, SENDMAIL);
 
10581
                if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
 
10582
                        ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
 
10583
 
 
10584
                maxsilence = 0;
 
10585
                if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
 
10586
                        maxsilence = atoi(val);
 
10587
                        if (maxsilence > 0)
 
10588
                                maxsilence *= 1000;
 
10589
                }
 
10590
                
 
10591
                if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
 
10592
                        maxmsg = MAXMSG;
 
10593
                } else {
 
10594
                        maxmsg = atoi(val);
 
10595
                        if (maxmsg <= 0) {
 
10596
                                ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
 
10597
                                maxmsg = 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;
 
10601
                        }
 
10602
                }
 
10603
 
 
10604
                if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
 
10605
                        maxdeletedmsg = 0;
 
10606
                } else {
 
10607
                        if (sscanf(val, "%30d", &x) == 1)
 
10608
                                maxdeletedmsg = x;
 
10609
                        else if (ast_true(val))
 
10610
                                maxdeletedmsg = MAXMSG;
 
10611
                        else
 
10612
                                maxdeletedmsg = 0;
 
10613
 
 
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;
 
10620
                        }
 
10621
                }
 
10622
 
 
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));
 
10626
                }
 
10627
 
 
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;
 
10635
                }
 
10636
 
 
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);
 
10641
                }
 
10642
 
 
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));
 
10647
                } else {
 
10648
                        ast_copy_string(imapserver,"localhost", sizeof(imapserver));
 
10649
                }
 
10650
                /* IMAP server port */
 
10651
                if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
 
10652
                        ast_copy_string(imapport, val, sizeof(imapport));
 
10653
                } else {
 
10654
                        ast_copy_string(imapport,"143", sizeof(imapport));
 
10655
                }
 
10656
                /* IMAP server flags */
 
10657
                if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
 
10658
                        ast_copy_string(imapflags, val, sizeof(imapflags));
 
10659
                }
 
10660
                /* IMAP server master username */
 
10661
                if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
 
10662
                        ast_copy_string(authuser, val, sizeof(authuser));
 
10663
                }
 
10664
                /* IMAP server master password */
 
10665
                if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
 
10666
                        ast_copy_string(authpassword, val, sizeof(authpassword));
 
10667
                }
 
10668
                /* Expunge on exit */
 
10669
                if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
 
10670
                        if (ast_false(val))
 
10671
                                expungeonhangup = 0;
 
10672
                        else
 
10673
                                expungeonhangup = 1;
 
10674
                } else {
 
10675
                        expungeonhangup = 1;
 
10676
                }
 
10677
                /* IMAP voicemail folder */
 
10678
                if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
 
10679
                        ast_copy_string(imapfolder, val, sizeof(imapfolder));
 
10680
                } else {
 
10681
                        ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
 
10682
                }
 
10683
                if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
 
10684
                        ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
 
10685
                }
 
10686
                if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
 
10687
                        imapgreetings = ast_true(val);
 
10688
                } else {
 
10689
                        imapgreetings = 0;
 
10690
                }
 
10691
                if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
 
10692
                        ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
 
10693
                } else {
 
10694
                        ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
 
10695
                }
 
10696
 
 
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)));
 
10703
                } else {
 
10704
                        mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
 
10705
                }
 
10706
 
 
10707
                if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
 
10708
                        mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
 
10709
                } else {
 
10710
                        mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
 
10711
                }
 
10712
 
 
10713
                if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
 
10714
                        mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
 
10715
                } else {
 
10716
                        mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
 
10717
                }
 
10718
 
 
10719
                if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
 
10720
                        mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
 
10721
                } else {
 
10722
                        mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
 
10723
                }
 
10724
 
 
10725
                /* Increment configuration version */
 
10726
                imapversion++;
 
10727
#endif
 
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);
 
10732
                } else {
 
10733
                        externnotify[0] = '\0';
 
10734
                }
 
10735
 
 
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;
 
10741
                        } else {
 
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;
 
10744
                        }
 
10745
                        if (!smdi_iface) {
 
10746
                                ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
 
10747
                        } 
 
10748
                }
 
10749
 
 
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);
 
10754
                
 
10755
                if (!(val = ast_variable_retrieve(cfg, "general", "serveremail"))) 
 
10756
                        val = ASTERISK_USERNAME;
 
10757
                ast_copy_string(serveremail, val, sizeof(serveremail));
 
10758
                
 
10759
                vmmaxsecs = 0;
 
10760
                if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
 
10761
                        if (sscanf(val, "%30d", &x) == 1) {
 
10762
                                vmmaxsecs = x;
 
10763
                        } else {
 
10764
                                ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
 
10765
                        }
 
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");
 
10771
                        }
 
10772
                        if (sscanf(val, "%30d", &x) == 1) {
 
10773
                                vmmaxsecs = x;
 
10774
                        } else {
 
10775
                                ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
 
10776
                        }
 
10777
                }
 
10778
 
 
10779
                vmminsecs = 0;
 
10780
                if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
 
10781
                        if (sscanf(val, "%30d", &x) == 1) {
 
10782
                                vmminsecs = x;
 
10783
                                if (maxsilence / 1000 >= vmminsecs) {
 
10784
                                        ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
 
10785
                                }
 
10786
                        } else {
 
10787
                                ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
 
10788
                        }
 
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");
 
10794
                        }
 
10795
                        if (sscanf(val, "%30d", &x) == 1) {
 
10796
                                vmminsecs = x;
 
10797
                                if (maxsilence / 1000 >= vmminsecs) {
 
10798
                                        ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
 
10799
                                }
 
10800
                        } else {
 
10801
                                ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
 
10802
                        }
 
10803
                }
 
10804
 
 
10805
                val = ast_variable_retrieve(cfg, "general", "format");
 
10806
                if (!val) {
 
10807
                        val = "wav";    
 
10808
                } else {
 
10809
                        tmp = ast_strdupa(val);
 
10810
                        val = ast_format_str_reduce(tmp);
 
10811
                        if (!val) {
 
10812
                                ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
 
10813
                                val = "wav";
 
10814
                        }
 
10815
                }
 
10816
                ast_copy_string(vmfmts, val, sizeof(vmfmts));
 
10817
 
 
10818
                skipms = 3000;
 
10819
                if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
 
10820
                        if (sscanf(val, "%30d", &x) == 1) {
 
10821
                                maxgreet = x;
 
10822
                        } else {
 
10823
                                ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
 
10824
                        }
 
10825
                }
 
10826
 
 
10827
                if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
 
10828
                        if (sscanf(val, "%30d", &x) == 1) {
 
10829
                                skipms = x;
 
10830
                        } else {
 
10831
                                ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
 
10832
                        }
 
10833
                }
 
10834
 
 
10835
                maxlogins = 3;
 
10836
                if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
 
10837
                        if (sscanf(val, "%30d", &x) == 1) {
 
10838
                                maxlogins = x;
 
10839
                        } else {
 
10840
                                ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
 
10841
                        }
 
10842
                }
 
10843
 
 
10844
                minpassword = MINPASSWORD;
 
10845
                if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
 
10846
                        if (sscanf(val, "%30d", &x) == 1) {
 
10847
                                minpassword = x;
 
10848
                        } else {
 
10849
                                ast_log(AST_LOG_WARNING, "Invalid minimum password length.  Default to %d\n", minpassword);
 
10850
                        }
 
10851
                }
 
10852
 
 
10853
                /* Force new user to record name ? */
 
10854
                if (!(val = ast_variable_retrieve(cfg, "general", "forcename"))) 
 
10855
                        val = "no";
 
10856
                ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
 
10857
 
 
10858
                /* Force new user to record greetings ? */
 
10859
                if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings"))) 
 
10860
                        val = "no";
 
10861
                ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
 
10862
 
 
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 */
 
10870
                                                q++;
 
10871
                                        ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
 
10872
                                        ast_debug(1,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
 
10873
                                } else {
 
10874
                                        cidinternalcontexts[x][0] = '\0';
 
10875
                                }
 
10876
                        }
 
10877
                }
 
10878
                if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
 
10879
                        ast_debug(1,"VM Review Option disabled globally\n");
 
10880
                        val = "no";
 
10881
                }
 
10882
                ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW);        
 
10883
 
 
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");
 
10887
                        val = "no";
 
10888
                } else {
 
10889
                        ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
 
10890
                }
 
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");
 
10894
                        val = "no";
 
10895
                }
 
10896
                ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);   
 
10897
 
 
10898
                if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
 
10899
                        ast_debug(1,"VM Operator break disabled globally\n");
 
10900
                        val = "no";
 
10901
                }
 
10902
                ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);      
 
10903
 
 
10904
                if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
 
10905
                        ast_debug(1,"VM CID Info before msg disabled globally\n");
 
10906
                        val = "no";
 
10907
                } 
 
10908
                ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID);        
 
10909
 
 
10910
                if (!(val = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
 
10911
                        ast_debug(1,"Send Voicemail msg disabled globally\n");
 
10912
                        val = "no";
 
10913
                }
 
10914
                ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
 
10915
        
 
10916
                if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
 
10917
                        ast_debug(1,"ENVELOPE before msg enabled globally\n");
 
10918
                        val = "yes";
 
10919
                }
 
10920
                ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);      
 
10921
 
 
10922
                if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
 
10923
                        ast_debug(1,"Move Heard enabled globally\n");
 
10924
                        val = "yes";
 
10925
                }
 
10926
                ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD);     
 
10927
 
 
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");
 
10930
                        val = "no";
 
10931
                }
 
10932
                ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);    
 
10933
 
 
10934
                if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
 
10935
                        ast_debug(1,"Duration info before msg enabled globally\n");
 
10936
                        val = "yes";
 
10937
                }
 
10938
                ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);   
 
10939
 
 
10940
                saydurationminfo = 2;
 
10941
                if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
 
10942
                        if (sscanf(val, "%30d", &x) == 1) {
 
10943
                                saydurationminfo = x;
 
10944
                        } else {
 
10945
                                ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
 
10946
                        }
 
10947
                }
 
10948
 
 
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");
 
10951
                        val = "no";
 
10952
                }
 
10953
                ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
 
10954
 
 
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);
 
10958
                } else {
 
10959
                        dialcontext[0] = '\0';  
 
10960
                }
 
10961
                
 
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);
 
10965
                } else {
 
10966
                        callcontext[0] = '\0';
 
10967
                }
 
10968
 
 
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);
 
10972
                } else {
 
10973
                        exitcontext[0] = '\0';
 
10974
                }
 
10975
                
 
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));
 
10991
                }
 
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));
 
11003
 
 
11004
                if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory"))) 
 
11005
                        val = "no";
 
11006
                ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD);  
 
11007
 
 
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);
 
11013
                        }
 
11014
                }
 
11015
 
 
11016
                poll_mailboxes = 0;
 
11017
                if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
 
11018
                        poll_mailboxes = ast_true(val);
 
11019
 
 
11020
                if (ucfg) {     
 
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")))
 
11023
                                        continue;
 
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));
 
11028
                                }
 
11029
                        }
 
11030
                        ast_config_destroy(ucfg);
 
11031
                }
 
11032
                cat = ast_category_browse(cfg, NULL);
 
11033
                while (cat) {
 
11034
                        if (strcasecmp(cat, "general")) {
 
11035
                                var = ast_variable_browse(cfg, cat);
 
11036
                                if (strcasecmp(cat, "zonemessages")) {
 
11037
                                        /* Process mailboxes in this context */
 
11038
                                        while (var) {
 
11039
                                                append_mailbox(cat, var->name, var->value);
 
11040
                                                var = var->next;
 
11041
                                        }
 
11042
                                } else {
 
11043
                                        /* Timezones in this context */
 
11044
                                        while (var) {
 
11045
                                                struct vm_zone *z;
 
11046
                                                if ((z = ast_malloc(sizeof(*z)))) {
 
11047
                                                        char *msg_format, *tzone;
 
11048
                                                        msg_format = ast_strdupa(var->value);
 
11049
                                                        tzone = strsep(&msg_format, "|");
 
11050
                                                        if (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);
 
11057
                                                        } else {
 
11058
                                                                ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
 
11059
                                                                ast_free(z);
 
11060
                                                        }
 
11061
                                                } else {
 
11062
                                                        AST_LIST_UNLOCK(&users);
 
11063
                                                        ast_config_destroy(cfg);
 
11064
                                                        return -1;
 
11065
                                                }
 
11066
                                                var = var->next;
 
11067
                                        }
 
11068
                                }
 
11069
                        }
 
11070
                        cat = ast_category_browse(cfg, cat);
 
11071
                }
 
11072
                memset(fromstring, 0, sizeof(fromstring));
 
11073
                memset(pagerfromstring, 0, sizeof(pagerfromstring));
 
11074
                strcpy(charset, "ISO-8859-1");
 
11075
                if (emailbody) {
 
11076
                        ast_free(emailbody);
 
11077
                        emailbody = NULL;
 
11078
                }
 
11079
                if (emailsubject) {
 
11080
                        ast_free(emailsubject);
 
11081
                        emailsubject = NULL;
 
11082
                }
 
11083
                if (pagerbody) {
 
11084
                        ast_free(pagerbody);
 
11085
                        pagerbody = NULL;
 
11086
                }
 
11087
                if (pagersubject) {
 
11088
                        ast_free(pagersubject);
 
11089
                        pagersubject = NULL;
 
11090
                }
 
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);
 
11103
                        }
 
11104
                }
 
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);
 
11109
                        }
 
11110
                }
 
11111
                if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
 
11112
                        if (atoi(val)) {
 
11113
                                adsiver = atoi(val);
 
11114
                        }
 
11115
                }
 
11116
                if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
 
11117
                        ast_copy_string(zonetag, val, sizeof(zonetag));
 
11118
                }
 
11119
                if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
 
11120
                        emailsubject = ast_strdup(val);
 
11121
                }
 
11122
                if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
 
11123
                        emailbody = ast_strdup(substitute_escapes(val));
 
11124
                }
 
11125
                if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
 
11126
                        pagersubject = ast_strdup(val);
 
11127
                }
 
11128
                if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
 
11129
                        pagerbody = ast_strdup(substitute_escapes(val));
 
11130
                }
 
11131
                AST_LIST_UNLOCK(&users);
 
11132
                ast_config_destroy(cfg);
 
11133
 
 
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();;
 
11138
 
 
11139
                return 0;
 
11140
        } else {
 
11141
                AST_LIST_UNLOCK(&users);
 
11142
                ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
 
11143
                if (ucfg)
 
11144
                        ast_config_destroy(ucfg);
 
11145
                return 0;
 
11146
        }
 
11147
}
 
11148
 
 
11149
static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
 
11150
{
 
11151
        int res = -1;
 
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);
 
11158
        }
 
11159
        DISPOSE(dir, -1);
 
11160
        return res;
 
11161
}
 
11162
 
 
11163
static int reload(void)
 
11164
{
 
11165
        return load_config(1);
 
11166
}
 
11167
 
 
11168
static int unload_module(void)
 
11169
{
 
11170
        int res;
 
11171
 
 
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();
 
11180
 
 
11181
        if (poll_thread != AST_PTHREADT_NULL)
 
11182
                stop_poll_thread();
 
11183
 
 
11184
        mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
 
11185
        ast_unload_realtime("voicemail");
 
11186
        ast_unload_realtime("voicemail_data");
 
11187
 
 
11188
        free_vm_users();
 
11189
        free_vm_zones();
 
11190
        return res;
 
11191
}
 
11192
 
 
11193
static int load_module(void)
 
11194
{
 
11195
        int res;
 
11196
        my_umask = umask(0);
 
11197
        umask(my_umask);
 
11198
 
 
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);
 
11201
        
 
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");
 
11204
        }
 
11205
 
 
11206
        if ((res = load_config(0)))
 
11207
                return res;
 
11208
 
 
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");
 
11215
        if (res)
 
11216
                return res;
 
11217
 
 
11218
        ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
 
11219
 
 
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);
 
11223
 
 
11224
        return res;
 
11225
}
 
11226
 
 
11227
static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context) 
 
11228
{
 
11229
        int cmd = 0;
 
11230
        char destination[80] = "";
 
11231
        int retries = 0;
 
11232
 
 
11233
        if (!num) {
 
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");
 
11238
                        if (!cmd)
 
11239
                                destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
 
11240
                        if (!cmd)
 
11241
                                destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
 
11242
                        if (!cmd) {
 
11243
                                cmd = ast_waitfordigit(chan, 6000);
 
11244
                                if (cmd)
 
11245
                                        destination[0] = cmd;
 
11246
                        }
 
11247
                        if (!cmd) {
 
11248
                                retries++;
 
11249
                        } else {
 
11250
 
 
11251
                                if (cmd < 0)
 
11252
                                        return 0;
 
11253
                                if (cmd == '*') {
 
11254
                                        ast_verb(3, "User hit '*' to cancel outgoing call\n");
 
11255
                                        return 0;
 
11256
                                }
 
11257
                                if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0) 
 
11258
                                        retries++;
 
11259
                                else
 
11260
                                        cmd = 't';
 
11261
                        }
 
11262
                }
 
11263
                if (retries >= 3) {
 
11264
                        return 0;
 
11265
                }
 
11266
                
 
11267
        } else {
 
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));
 
11271
        }
 
11272
 
 
11273
        if (!ast_strlen_zero(destination)) {
 
11274
                if (destination[strlen(destination) -1 ] == '*')
 
11275
                        return 0; 
 
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;
 
11281
                return 9;
 
11282
        }
 
11283
        return 0;
 
11284
}
 
11285
 
 
11286
/*!
 
11287
 * \brief The advanced options within a message.
 
11288
 * \param chan
 
11289
 * \param vmu 
 
11290
 * \param vms
 
11291
 * \param msg
 
11292
 * \param option
 
11293
 * \param record_gain
 
11294
 *
 
11295
 * Provides handling for the play message envelope, call the person back, or reply to message. 
 
11296
 *
 
11297
 * \return zero on success, -1 on error.
 
11298
 */
 
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)
 
11300
{
 
11301
        int res = 0;
 
11302
        char filename[PATH_MAX];
 
11303
        struct ast_config *msg_cfg = NULL;
 
11304
        const char *origtime, *context;
 
11305
        char *name, *num;
 
11306
        int retries = 0;
 
11307
        char *cid;
 
11308
        struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
 
11309
 
 
11310
        vms->starting = 0; 
 
11311
 
 
11312
        make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
 
11313
 
 
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);
 
11321
                return 0;
 
11322
        }
 
11323
 
 
11324
        if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
 
11325
                ast_config_destroy(msg_cfg);
 
11326
                return 0;
 
11327
        }
 
11328
 
 
11329
        cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
 
11330
 
 
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");
 
11334
        switch (option) {
 
11335
        case 3: /* Play message envelope */
 
11336
                if (!res)
 
11337
                        res = play_message_datetime(chan, vmu, origtime, filename);
 
11338
                if (!res)
 
11339
                        res = play_message_callerid(chan, vms, cid, context, 0);
 
11340
 
 
11341
                res = 't';
 
11342
                break;
 
11343
 
 
11344
        case 2: /* Call back */
 
11345
 
 
11346
                if (ast_strlen_zero(cid))
 
11347
                        break;
 
11348
 
 
11349
                ast_callerid_parse(cid, &name, &num);
 
11350
                while ((res > -1) && (res != 't')) {
 
11351
                        switch (res) {
 
11352
                        case '1':
 
11353
                                if (num) {
 
11354
                                        /* Dial the CID number */
 
11355
                                        res = dialout(chan, vmu, num, vmu->callback);
 
11356
                                        if (res) {
 
11357
                                                ast_config_destroy(msg_cfg);
 
11358
                                                return 9;
 
11359
                                        }
 
11360
                                } else {
 
11361
                                        res = '2';
 
11362
                                }
 
11363
                                break;
 
11364
 
 
11365
                        case '2':
 
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);
 
11369
                                        if (res) {
 
11370
                                                ast_config_destroy(msg_cfg);
 
11371
                                                return 9;
 
11372
                                        }
 
11373
                                } else {
 
11374
                                        ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
 
11375
                                        res = ast_play_and_wait(chan, "vm-sorry");
 
11376
                                }
 
11377
                                ast_config_destroy(msg_cfg);
 
11378
                                return res;
 
11379
                        case '*':
 
11380
                                res = 't';
 
11381
                                break;
 
11382
                        case '3':
 
11383
                        case '4':
 
11384
                        case '5':
 
11385
                        case '6':
 
11386
                        case '7':
 
11387
                        case '8':
 
11388
                        case '9':
 
11389
                        case '0':
 
11390
 
 
11391
                                res = ast_play_and_wait(chan, "vm-sorry");
 
11392
                                retries++;
 
11393
                                break;
 
11394
                        default:
 
11395
                                if (num) {
 
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");
 
11398
                                        if (!res)
 
11399
                                                res = play_message_callerid(chan, vms, num, vmu->context, 1);
 
11400
                                        if (!res)
 
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)) {
 
11404
                                                if (!res)
 
11405
                                                        res = ast_play_and_wait(chan, "vm-calldiffnum");
 
11406
                                        }
 
11407
                                } else {
 
11408
                                        res = ast_play_and_wait(chan, "vm-nonumber");
 
11409
                                        if (!ast_strlen_zero(vmu->dialout)) {
 
11410
                                                if (!res)
 
11411
                                                        res = ast_play_and_wait(chan, "vm-toenternumber");
 
11412
                                        }
 
11413
                                }
 
11414
                                if (!res)
 
11415
                                        res = ast_play_and_wait(chan, "vm-star-cancel");
 
11416
                                if (!res)
 
11417
                                        res = ast_waitfordigit(chan, 6000);
 
11418
                                if (!res) {
 
11419
                                        retries++;
 
11420
                                        if (retries > 3)
 
11421
                                                res = 't';
 
11422
                                }
 
11423
                                break; 
 
11424
                                
 
11425
                        }
 
11426
                        if (res == 't')
 
11427
                                res = 0;
 
11428
                        else if (res == '*')
 
11429
                                res = -1;
 
11430
                }
 
11431
                break;
 
11432
                
 
11433
        case 1: /* Reply */
 
11434
                /* Send reply directly to sender */
 
11435
                if (ast_strlen_zero(cid))
 
11436
                        break;
 
11437
 
 
11438
                ast_callerid_parse(cid, &name, &num);
 
11439
                if (!num) {
 
11440
                        ast_verb(3, "No CID number available, no reply sent\n");
 
11441
                        if (!res)
 
11442
                                res = ast_play_and_wait(chan, "vm-nonumber");
 
11443
                        ast_config_destroy(msg_cfg);
 
11444
                        return res;
 
11445
                } else {
 
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);
 
11451
 
 
11452
                                ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
 
11453
                                
 
11454
                                memset(&leave_options, 0, sizeof(leave_options));
 
11455
                                leave_options.record_gain = record_gain;
 
11456
                                res = leave_voicemail(chan, mailbox, &leave_options);
 
11457
                                if (!res)
 
11458
                                        res = 't';
 
11459
                                ast_config_destroy(msg_cfg);
 
11460
                                return res;
 
11461
                        } else {
 
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");
 
11465
                                res = 't';
 
11466
                                ast_config_destroy(msg_cfg);
 
11467
                                return res;
 
11468
                        }
 
11469
                } 
 
11470
                res = 0;
 
11471
 
 
11472
                break;
 
11473
        }
 
11474
 
 
11475
#ifndef IMAP_STORAGE
 
11476
        ast_config_destroy(msg_cfg);
 
11477
 
 
11478
        if (!res) {
 
11479
                make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
 
11480
                vms->heard[msg] = 1;
 
11481
                res = wait_file(chan, vms, vms->fn);
 
11482
        }
 
11483
#endif
 
11484
        return res;
 
11485
}
 
11486
 
 
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)
 
11490
{
 
11491
        /* Record message & let caller review or re-record it, or set options if applicable */
 
11492
        int res = 0;
 
11493
        int cmd = 0;
 
11494
        int max_attempts = 3;
 
11495
        int attempts = 0;
 
11496
        int recorded = 0;
 
11497
        int msg_exists = 0;
 
11498
        signed char zero_gain = 0;
 
11499
        char tempfile[PATH_MAX];
 
11500
        char *acceptdtmf = "#";
 
11501
        char *canceldtmf = "";
 
11502
 
 
11503
        /* Note that urgent and private are for flagging messages as such in the future */
 
11504
 
 
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");
 
11508
                return -1;
 
11509
        }
 
11510
 
 
11511
        if (!outsidecaller)
 
11512
                snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
 
11513
        else
 
11514
                ast_copy_string(tempfile, recordfile, sizeof(tempfile));
 
11515
 
 
11516
        cmd = '3';  /* Want to start by recording */
 
11517
 
 
11518
        while ((cmd >= 0) && (cmd != 't')) {
 
11519
                switch (cmd) {
 
11520
                case '1':
 
11521
                        if (!msg_exists) {
 
11522
                                /* In this case, 1 is to record a message */
 
11523
                                cmd = '3';
 
11524
                                break;
 
11525
                        } else {
 
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);
 
11535
                                }
 
11536
                                cmd = 't';
 
11537
                                return res;
 
11538
                        }
 
11539
                case '2':
 
11540
                        /* Review */
 
11541
                        ast_verb(3, "Reviewing the message\n");
 
11542
                        cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
 
11543
                        break;
 
11544
                case '3':
 
11545
                        msg_exists = 0;
 
11546
                        /* Record */
 
11547
                        if (recorded == 1) 
 
11548
                                ast_verb(3, "Re-recording the message\n");
 
11549
                        else    
 
11550
                                ast_verb(3, "Recording the message\n");
 
11551
                        
 
11552
                        if (recorded && outsidecaller) {
 
11553
                                cmd = ast_play_and_wait(chan, INTRO);
 
11554
                                cmd = ast_play_and_wait(chan, "beep");
 
11555
                        }
 
11556
                        recorded = 1;
 
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 */
 
11558
                        if (record_gain)
 
11559
                                ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
 
11560
                        if (ast_test_flag(vmu, VM_OPERATOR))
 
11561
                                canceldtmf = "0";
 
11562
                        cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
 
11563
                        if (record_gain)
 
11564
                                ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
 
11565
                        if (cmd == -1) {
 
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);
 
11570
                                }               
 
11571
                                return cmd;
 
11572
                        }
 
11573
                        if (cmd == '0') {
 
11574
                                break;
 
11575
                        } else if (cmd == '*') {
 
11576
                                break;
 
11577
#if 0
 
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);
 
11583
                                break;
 
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");
 
11589
                                if (!cmd)
 
11590
                                        cmd = ast_play_and_wait(chan, "vm-speakup");
 
11591
                                break;
 
11592
#endif
 
11593
                        } else {
 
11594
                                /* If all is well, a message exists */
 
11595
                                msg_exists = 1;
 
11596
                                cmd = 0;
 
11597
                        }
 
11598
                        break;
 
11599
                case '4':
 
11600
                        if (outsidecaller) {  /* only mark vm messages */
 
11601
                                /* Mark Urgent */
 
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");
 
11607
                                } else if (flag) {
 
11608
                                        ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
 
11609
                                        res = ast_play_and_wait(chan, "vm-urgent-removed");
 
11610
                                        strcpy(flag, "");
 
11611
                                } else {
 
11612
                                        ast_play_and_wait(chan, "vm-sorry");
 
11613
                                }
 
11614
                                cmd = 0;
 
11615
                        } else {
 
11616
                                cmd = ast_play_and_wait(chan, "vm-sorry");
 
11617
                        }
 
11618
                        break;
 
11619
                case '5':
 
11620
                case '6':
 
11621
                case '7':
 
11622
                case '8':
 
11623
                case '9':
 
11624
                case '*':
 
11625
                case '#':
 
11626
                        cmd = ast_play_and_wait(chan, "vm-sorry");
 
11627
                        break;
 
11628
#if 0 
 
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) */
 
11631
                case '*':
 
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);
 
11637
                                return res;
 
11638
                        }
 
11639
                        else
 
11640
                                return 1;
 
11641
#endif
 
11642
                case '0':
 
11643
                        if (!ast_test_flag(vmu, VM_OPERATOR)) {
 
11644
                                cmd = ast_play_and_wait(chan, "vm-sorry");
 
11645
                                break;
 
11646
                        }
 
11647
                        if (msg_exists || recorded) {
 
11648
                                cmd = ast_play_and_wait(chan, "vm-saveoper");
 
11649
                                if (!cmd)
 
11650
                                        cmd = ast_waitfordigit(chan, 3000);
 
11651
                                if (cmd == '1') {
 
11652
                                        ast_play_and_wait(chan, "vm-msgsaved");
 
11653
                                        cmd = '0';
 
11654
                                } else if (cmd == '4') {
 
11655
                                        if (flag) {
 
11656
                                                ast_play_and_wait(chan, "vm-marked-urgent");
 
11657
                                                strcpy(flag, "Urgent");
 
11658
                                        }
 
11659
                                        ast_play_and_wait(chan, "vm-msgsaved");
 
11660
                                        cmd = '0';
 
11661
                                } else {
 
11662
                                        ast_play_and_wait(chan, "vm-deleted");
 
11663
                                        DELETE(recordfile, -1, recordfile, vmu);
 
11664
                                        cmd = '0';
 
11665
                                }
 
11666
                        }
 
11667
                        return cmd;
 
11668
                default:
 
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
 
11671
                           their OGM's */
 
11672
                        if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
 
11673
                                return cmd;
 
11674
                        if (msg_exists) {
 
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");
 
11679
                                        } else if (flag) {
 
11680
                                                cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
 
11681
                                        }
 
11682
                                }
 
11683
                        } else {
 
11684
                                cmd = ast_play_and_wait(chan, "vm-torerecord");
 
11685
                                if (!cmd)
 
11686
                                        cmd = ast_waitfordigit(chan, 600);
 
11687
                        }
 
11688
                        
 
11689
                        if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
 
11690
                                cmd = ast_play_and_wait(chan, "vm-reachoper");
 
11691
                                if (!cmd)
 
11692
                                        cmd = ast_waitfordigit(chan, 600);
 
11693
                        }
 
11694
#if 0
 
11695
                        if (!cmd)
 
11696
                                cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
 
11697
#endif
 
11698
                        if (!cmd)
 
11699
                                cmd = ast_waitfordigit(chan, 6000);
 
11700
                        if (!cmd) {
 
11701
                                attempts++;
 
11702
                        }
 
11703
                        if (attempts > max_attempts) {
 
11704
                                cmd = 't';
 
11705
                        }
 
11706
                }
 
11707
        }
 
11708
        if (outsidecaller)
 
11709
                ast_play_and_wait(chan, "vm-goodbye");
 
11710
        if (cmd == 't')
 
11711
                cmd = 0;
 
11712
        return cmd;
 
11713
}
 
11714
 
 
11715
/* This is a workaround so that menuselect displays a proper description
 
11716
 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
 
11717
 */
 
11718
 
 
11719
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
 
11720
                .load = load_module,
 
11721
                .unload = unload_module,
 
11722
                .reload = reload,
 
11723
                );