Asterisk - The Open Source Telephony Project  18.5.0
app_voicemail.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2006, Digium, Inc.
5  *
6  * Mark Spencer <[email protected]>
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 /*!
20  * \file
21  * \author Mark Spencer <[email protected]>
22  * \brief Comedian Mail - Voicemail System
23  *
24  * unixODBC (http://www.unixodbc.org/)
25  * A source distribution of University of Washington's IMAP c-client
26  * (http://www.washington.edu/imap/)
27  *
28  * \par See also
29  * \arg \ref Config_vm
30  * \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
31  * \ingroup applications
32  * \todo This module requires res_adsi to load. This needs to be optional
33  * during compilation.
34  *
35  * \todo This file is now almost impossible to work with, due to all \#ifdefs.
36  * Feels like the database code before realtime. Someone - please come up
37  * with a plan to clean this up.
38  */
39 
40 /*! \li \ref app_voicemail.c uses configuration file \ref voicemail.conf
41  * \addtogroup configuration_file Configuration Files
42  */
43 
44 /*!
45  * \page voicemail.conf voicemail.conf
46  * \verbinclude voicemail.conf.sample
47  */
48 
49 #include "asterisk.h"
50 
51 #ifdef IMAP_STORAGE
52 #include <ctype.h>
53 #include <signal.h>
54 #include <pwd.h>
55 #ifdef USE_SYSTEM_IMAP
56 #include <imap/c-client.h>
57 #include <imap/imap4r1.h>
58 #include <imap/linkage.h>
59 #elif defined (USE_SYSTEM_CCLIENT)
60 #include <c-client/c-client.h>
61 #include <c-client/imap4r1.h>
62 #include <c-client/linkage.h>
63 #else
64 #include "c-client.h"
65 #include "imap4r1.h"
66 #include "linkage.h"
67 #endif
68 #endif
69 
70 #include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
71 #include <sys/time.h>
72 #include <sys/stat.h>
73 #include <sys/mman.h>
74 #include <time.h>
75 #include <dirent.h>
76 #if defined(__FreeBSD__) || defined(__OpenBSD__)
77 #include <sys/wait.h>
78 #endif
79 
80 #include "asterisk/logger.h"
81 #include "asterisk/lock.h"
82 #include "asterisk/file.h"
83 #include "asterisk/channel.h"
84 #include "asterisk/pbx.h"
85 #include "asterisk/config.h"
86 #include "asterisk/say.h"
87 #include "asterisk/module.h"
88 #include "asterisk/adsi.h"
89 #include "asterisk/app.h"
90 #include "asterisk/mwi.h"
91 #include "asterisk/manager.h"
92 #include "asterisk/dsp.h"
93 #include "asterisk/localtime.h"
94 #include "asterisk/cli.h"
95 #include "asterisk/utils.h"
96 #include "asterisk/stringfields.h"
97 #include "asterisk/strings.h"
98 #include "asterisk/smdi.h"
99 #include "asterisk/astobj2.h"
100 #include "asterisk/taskprocessor.h"
101 #include "asterisk/test.h"
102 #include "asterisk/format_cache.h"
103 
104 #ifdef ODBC_STORAGE
105 #include "asterisk/res_odbc.h"
106 #endif
107 
108 #ifdef IMAP_STORAGE
109 #include "asterisk/threadstorage.h"
110 #endif
111 
112 /*** DOCUMENTATION
113  <application name="VoiceMail" language="en_US">
114  <synopsis>
115  Leave a Voicemail message.
116  </synopsis>
117  <syntax>
118  <parameter name="mailboxs" argsep="&amp;" required="true">
119  <argument name="mailbox1" argsep="@" required="true">
120  <argument name="mailbox" required="true" />
121  <argument name="context" />
122  </argument>
123  <argument name="mailbox2" argsep="@" multiple="true">
124  <argument name="mailbox" required="true" />
125  <argument name="context" />
126  </argument>
127  </parameter>
128  <parameter name="options">
129  <optionlist>
130  <option name="b">
131  <para>Play the <literal>busy</literal> greeting to the calling party.</para>
132  </option>
133  <option name="d">
134  <argument name="c" />
135  <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
136  if played during the greeting. Context defaults to the current context.</para>
137  </option>
138  <option name="e">
139  <para>Play greetings as early media -- only answer the channel just
140  before accepting the voice message.</para>
141  </option>
142  <option name="g">
143  <argument name="#" required="true" />
144  <para>Use the specified amount of gain when recording the voicemail
145  message. The units are whole-number decibels (dB). Only works on supported
146  technologies, which is DAHDI only.</para>
147  </option>
148  <option name="s">
149  <para>Skip the playback of instructions for leaving a message to the
150  calling party.</para>
151  </option>
152  <option name="t">
153  <argument name="x" required="false" />
154  <para>Play a custom beep tone to the caller instead of the default one.
155  If this option is used but no file is specified, the beep is suppressed.</para>
156  </option>
157  <option name="u">
158  <para>Play the <literal>unavailable</literal> greeting.</para>
159  </option>
160  <option name="U">
161  <para>Mark message as <literal>URGENT</literal>.</para>
162  </option>
163  <option name="P">
164  <para>Mark message as <literal>PRIORITY</literal>.</para>
165  </option>
166  </optionlist>
167  </parameter>
168  </syntax>
169  <description>
170  <para>This application allows the calling party to leave a message for the specified
171  list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
172  the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
173  exist.</para>
174  <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
175  <enumlist>
176  <enum name="0">
177  <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
178  </enum>
179  <enum name="*">
180  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
181  </enum>
182  </enumlist>
183  <para>This application will set the following channel variable upon completion:</para>
184  <variablelist>
185  <variable name="VMSTATUS">
186  <para>This indicates the status of the execution of the VoiceMail application.</para>
187  <value name="SUCCESS" />
188  <value name="USEREXIT" />
189  <value name="FAILED" />
190  </variable>
191  </variablelist>
192  </description>
193  <see-also>
194  <ref type="application">VoiceMailMain</ref>
195  </see-also>
196  </application>
197  <application name="VoiceMailMain" language="en_US">
198  <synopsis>
199  Check Voicemail messages.
200  </synopsis>
201  <syntax>
202  <parameter name="mailbox" required="true" argsep="@">
203  <argument name="mailbox" />
204  <argument name="context" />
205  </parameter>
206  <parameter name="options">
207  <optionlist>
208  <option name="p">
209  <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
210  the mailbox that is entered by the caller.</para>
211  </option>
212  <option name="g">
213  <argument name="#" required="true" />
214  <para>Use the specified amount of gain when recording a voicemail message.
215  The units are whole-number decibels (dB).</para>
216  </option>
217  <option name="s">
218  <para>Skip checking the passcode for the mailbox.</para>
219  </option>
220  <option name="a">
221  <argument name="folder" required="true" />
222  <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
223  Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
224  <enumlist>
225  <enum name="0"><para>INBOX</para></enum>
226  <enum name="1"><para>Old</para></enum>
227  <enum name="2"><para>Work</para></enum>
228  <enum name="3"><para>Family</para></enum>
229  <enum name="4"><para>Friends</para></enum>
230  <enum name="5"><para>Cust1</para></enum>
231  <enum name="6"><para>Cust2</para></enum>
232  <enum name="7"><para>Cust3</para></enum>
233  <enum name="8"><para>Cust4</para></enum>
234  <enum name="9"><para>Cust5</para></enum>
235  </enumlist>
236  </option>
237  </optionlist>
238  </parameter>
239  </syntax>
240  <description>
241  <para>This application allows the calling party to check voicemail messages. A specific
242  <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
243  may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
244  be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
245  <literal>default</literal> context will be used.</para>
246  <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
247  or Password, and the extension exists:</para>
248  <enumlist>
249  <enum name="*">
250  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
251  </enum>
252  </enumlist>
253  </description>
254  <see-also>
255  <ref type="application">VoiceMail</ref>
256  </see-also>
257  </application>
258  <application name="VMAuthenticate" language="en_US">
259  <synopsis>
260  Authenticate with Voicemail passwords.
261  </synopsis>
262  <syntax>
263  <parameter name="mailbox" required="true" argsep="@">
264  <argument name="mailbox" />
265  <argument name="context" />
266  </parameter>
267  <parameter name="options">
268  <optionlist>
269  <option name="s">
270  <para>Skip playing the initial prompts.</para>
271  </option>
272  </optionlist>
273  </parameter>
274  </syntax>
275  <description>
276  <para>This application behaves the same way as the Authenticate application, but the passwords
277  are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
278  specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
279  is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
280  mailbox.</para>
281  <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
282  or Password, and the extension exists:</para>
283  <enumlist>
284  <enum name="*">
285  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
286  </enum>
287  </enumlist>
288  </description>
289  </application>
290  <application name="VoiceMailPlayMsg" language="en_US">
291  <synopsis>
292  Play a single voice mail msg from a mailbox by msg id.
293  </synopsis>
294  <syntax>
295  <parameter name="mailbox" required="true" argsep="@">
296  <argument name="mailbox" />
297  <argument name="context" />
298  </parameter>
299  <parameter name="msg_id" required="true">
300  <para>The msg id of the msg to play back. </para>
301  </parameter>
302  </syntax>
303  <description>
304  <para>This application sets the following channel variable upon completion:</para>
305  <variablelist>
306  <variable name="VOICEMAIL_PLAYBACKSTATUS">
307  <para>The status of the playback attempt as a text string.</para>
308  <value name="SUCCESS"/>
309  <value name="FAILED"/>
310  </variable>
311  </variablelist>
312  </description>
313  </application>
314  <application name="VMSayName" language="en_US">
315  <synopsis>
316  Play the name of a voicemail user
317  </synopsis>
318  <syntax>
319  <parameter name="mailbox" required="true" argsep="@">
320  <argument name="mailbox" />
321  <argument name="context" />
322  </parameter>
323  </syntax>
324  <description>
325  <para>This application will say the recorded name of the voicemail user specified as the
326  argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
327  <para>Similar to the Background() application, playback of the recorded
328  name can be interrupted by entering an extension, which will be searched
329  for in the current context.</para>
330  </description>
331  </application>
332  <function name="VM_INFO" language="en_US">
333  <synopsis>
334  Returns the selected attribute from a mailbox.
335  </synopsis>
336  <syntax argsep=",">
337  <parameter name="mailbox" argsep="@" required="true">
338  <argument name="mailbox" required="true" />
339  <argument name="context" />
340  </parameter>
341  <parameter name="attribute" required="true">
342  <optionlist>
343  <option name="count">
344  <para>Count of messages in specified <replaceable>folder</replaceable>.
345  If <replaceable>folder</replaceable> is not specified, defaults to <literal>INBOX</literal>.</para>
346  </option>
347  <option name="email">
348  <para>E-mail address associated with the mailbox.</para>
349  </option>
350  <option name="exists">
351  <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.</para>
352  </option>
353  <option name="fullname">
354  <para>Full name associated with the mailbox.</para>
355  </option>
356  <option name="language">
357  <para>Mailbox language if overridden, otherwise the language of the channel.</para>
358  </option>
359  <option name="locale">
360  <para>Mailbox locale if overridden, otherwise global locale.</para>
361  </option>
362  <option name="pager">
363  <para>Pager e-mail address associated with the mailbox.</para>
364  </option>
365  <option name="password">
366  <para>Mailbox access password.</para>
367  </option>
368  <option name="tz">
369  <para>Mailbox timezone if overridden, otherwise global timezone</para>
370  </option>
371  </optionlist>
372  </parameter>
373  <parameter name="folder" required="false">
374  <para>If not specified, <literal>INBOX</literal> is assumed.</para>
375  </parameter>
376  </syntax>
377  <description>
378  <para>Returns the selected attribute from the specified <replaceable>mailbox</replaceable>.
379  If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
380  context. Where the <replaceable>folder</replaceable> can be specified, common folders
381  include <literal>INBOX</literal>, <literal>Old</literal>, <literal>Work</literal>,
382  <literal>Family</literal> and <literal>Friends</literal>.</para>
383  </description>
384  </function>
385  <manager name="VoicemailUsersList" language="en_US">
386  <synopsis>
387  List All Voicemail User Information.
388  </synopsis>
389  <syntax>
390  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
391  </syntax>
392  <description>
393  </description>
394  </manager>
395  <manager name="VoicemailUserStatus" language="en_US">
396  <synopsis>
397  Show the status of given voicemail user's info.
398  </synopsis>
399  <syntax>
400  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
401  <parameter name="Context" required="true">
402  <para>The context you want to check.</para>
403  </parameter>
404  <parameter name="Mailbox" required="true">
405  <para>The mailbox you want to check.</para>
406  </parameter>
407  </syntax>
408  <description>
409  <para>Retrieves the status of the given voicemail user.</para>
410  </description>
411  </manager>
412  <manager name="VoicemailRefresh" language="en_US">
413  <synopsis>
414  Tell Asterisk to poll mailboxes for a change
415  </synopsis>
416  <syntax>
417  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
418  <parameter name="Context" />
419  <parameter name="Mailbox" />
420  </syntax>
421  <description>
422  <para>Normally, MWI indicators are only sent when Asterisk itself
423  changes a mailbox. With external programs that modify the content
424  of a mailbox from outside the application, an option exists called
425  <literal>pollmailboxes</literal> that will cause voicemail to
426  continually scan all mailboxes on a system for changes. This can
427  cause a large amount of load on a system. This command allows
428  external applications to signal when a particular mailbox has
429  changed, thus permitting external applications to modify mailboxes
430  and MWI to work without introducing considerable CPU load.</para>
431  <para>If <replaceable>Context</replaceable> is not specified, all
432  mailboxes on the system will be polled for changes. If
433  <replaceable>Context</replaceable> is specified, but
434  <replaceable>Mailbox</replaceable> is omitted, then all mailboxes
435  within <replaceable>Context</replaceable> will be polled.
436  Otherwise, only a single mailbox will be polled for changes.</para>
437  </description>
438  </manager>
439  ***/
440 
441 #ifdef IMAP_STORAGE
442 static char imapserver[48] = "localhost";
443 static char imapport[8] = "143";
444 static char imapflags[128];
445 static char imapfolder[64] = "INBOX";
446 static char imapparentfolder[64];
447 static char greetingfolder[64] = "INBOX";
448 static char authuser[32];
449 static char authpassword[42];
450 static int imapversion = 1;
451 
452 static int expungeonhangup = 1;
453 static int imapgreetings;
454 static int imap_poll_logout;
455 static char delimiter;
456 
457 /* mail_open cannot be protected on a stream basis */
458 ast_mutex_t mail_open_lock;
459 
460 struct vm_state;
461 struct ast_vm_user;
462 
463 AST_THREADSTORAGE(ts_vmstate);
464 
465 /* Forward declarations for IMAP */
466 static int init_mailstream(struct vm_state *vms, int box);
467 static void write_file(char *filename, char *buffer, unsigned long len);
468 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
469 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
470 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
471 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
472 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
473 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
474 static void vmstate_insert(struct vm_state *vms);
475 static void vmstate_delete(struct vm_state *vms);
476 static void set_update(MAILSTREAM * stream);
477 static void init_vm_state(struct vm_state *vms);
478 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
479 static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream);
480 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
481 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
482 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag, const char *msg_id);
483 static void vm_imap_update_msg_id(char *dir, int msgnum, const char *msg_id, struct ast_vm_user *vmu, struct ast_config *msg_cfg, int folder);
484 static void update_messages_by_imapuser(const char *user, unsigned long number);
485 static int vm_delete(char *file);
486 
487 static int imap_remove_file (char *dir, int msgnum);
488 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
489 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
490 static void check_quota(struct vm_state *vms, char *mailbox);
491 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
492 static void imap_logout(const char *mailbox_id);
493 
494 struct vmstate {
495  struct vm_state *vms;
496  AST_LIST_ENTRY(vmstate) list;
497 };
498 
499 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
500 
501 #endif
502 
503 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
504 
505 #define COMMAND_TIMEOUT 5000
506 /* Don't modify these here; set your umask at runtime instead */
507 #define VOICEMAIL_DIR_MODE 0777
508 #define VOICEMAIL_FILE_MODE 0666
509 #define CHUNKSIZE 65536
510 
511 #define VOICEMAIL_CONFIG "voicemail.conf"
512 #define ASTERISK_USERNAME "asterisk"
513 
514 /* Define fast-forward, pause, restart, and reverse keys
515  * while listening to a voicemail message - these are
516  * strings, not characters */
517 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
518 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
519 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
520 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
521 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
522 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
523 
524 /* Default mail command to mail voicemail. Change it with the
525  * mailcmd= command in voicemail.conf */
526 #define SENDMAIL "/usr/sbin/sendmail -t"
527 
528 #define INTRO "vm-intro"
529 
530 #define MAX_MAIL_BODY_CONTENT_SIZE 134217728L // 128 Mbyte
531 
532 #define MAXMSG 100
533 #define MAXMSGLIMIT 9999
534 
535 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
536 
537 #define BASELINELEN 72
538 #define BASEMAXINLINE 256
539 #ifdef IMAP_STORAGE
540 #define ENDL "\r\n"
541 #else
542 #define ENDL "\n"
543 #endif
544 
545 #define MAX_DATETIME_FORMAT 512
546 #define MAX_NUM_CID_CONTEXTS 10
547 
548 #define VM_REVIEW (1 << 0) /*!< After recording, permit the caller to review the recording before saving */
549 #define VM_OPERATOR (1 << 1) /*!< Allow 0 to be pressed to go to 'o' extension */
550 #define VM_SAYCID (1 << 2) /*!< Repeat the CallerID info during envelope playback */
551 #define VM_SVMAIL (1 << 3) /*!< Allow the user to compose a new VM from within VoicemailMain */
552 #define VM_ENVELOPE (1 << 4) /*!< Play the envelope information (who-from, time received, etc.) */
553 #define VM_SAYDURATION (1 << 5) /*!< Play the length of the message during envelope playback */
554 #define VM_SKIPAFTERCMD (1 << 6) /*!< After deletion, assume caller wants to go to the next message */
555 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
556 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
557 #define VM_PBXSKIP (1 << 9) /*!< Skip the [PBX] preamble in the Subject line of emails */
558 #define VM_DIRECFORWARD (1 << 10) /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
559 #define VM_ATTACH (1 << 11) /*!< Attach message to voicemail notifications? */
560 #define VM_DELETE (1 << 12) /*!< Delete message after sending notification */
561 #define VM_ALLOCED (1 << 13) /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
562 #define VM_SEARCH (1 << 14) /*!< Search all contexts for a matching mailbox */
563 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
564 #define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
565 #define VM_MESSAGEWRAP (1 << 17) /*!< Wrap around from the last message to the first, and vice-versa */
566 #define VM_FWDURGAUTO (1 << 18) /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
567 #define ERROR_LOCK_PATH -100
568 #define ERROR_MAX_MSGS -101
569 #define OPERATOR_EXIT 300
570 
571 enum vm_box {
578 };
579 
581  OPT_SILENT = (1 << 0),
582  OPT_BUSY_GREETING = (1 << 1),
584  OPT_RECORDGAIN = (1 << 3),
586  OPT_AUTOPLAY = (1 << 6),
587  OPT_DTMFEXIT = (1 << 7),
588  OPT_MESSAGE_Urgent = (1 << 8),
590  OPT_EARLYM_GREETING = (1 << 10),
591  OPT_BEEP = (1 << 11)
592 };
593 
599  /* This *must* be the last value in this enum! */
601 };
602 
607 };
608 
621 });
622 
623 static const char * const mailbox_folders[] = {
624 #ifdef IMAP_STORAGE
625  imapfolder,
626 #else
627  "INBOX",
628 #endif
629  "Old",
630  "Work",
631  "Family",
632  "Friends",
633  "Cust1",
634  "Cust2",
635  "Cust3",
636  "Cust4",
637  "Cust5",
638  "Deleted",
639  "Urgent",
640 };
641 
642 static int load_config(int reload);
643 #ifdef TEST_FRAMEWORK
644 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
645 #endif
646 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
647 
648 /*! \page vmlang Voicemail Language Syntaxes Supported
649 
650  \par Syntaxes supported, not really language codes.
651  \arg \b en - English
652  \arg \b de - German
653  \arg \b es - Spanish
654  \arg \b fr - French
655  \arg \b it - Italian
656  \arg \b nl - Dutch
657  \arg \b pt - Portuguese
658  \arg \b pt_BR - Portuguese (Brazil)
659  \arg \b gr - Greek
660  \arg \b no - Norwegian
661  \arg \b se - Swedish
662  \arg \b tw - Chinese (Taiwan)
663  \arg \b ua - Ukrainian
664 
665 German requires the following additional soundfile:
666 \arg \b 1F einE (feminine)
667 
668 Spanish requires the following additional soundfile:
669 \arg \b 1M un (masculine)
670 
671 Dutch, Portuguese & Spanish require the following additional soundfiles:
672 \arg \b vm-INBOXs singular of 'new'
673 \arg \b vm-Olds singular of 'old/heard/read'
674 
675 NB these are plural:
676 \arg \b vm-INBOX nieuwe (nl)
677 \arg \b vm-Old oude (nl)
678 
679 Polish uses:
680 \arg \b vm-new-a 'new', feminine singular accusative
681 \arg \b vm-new-e 'new', feminine plural accusative
682 \arg \b vm-new-ych 'new', feminine plural genitive
683 \arg \b vm-old-a 'old', feminine singular accusative
684 \arg \b vm-old-e 'old', feminine plural accusative
685 \arg \b vm-old-ych 'old', feminine plural genitive
686 \arg \b digits/1-a 'one', not always same as 'digits/1'
687 \arg \b digits/2-ie 'two', not always same as 'digits/2'
688 
689 Swedish uses:
690 \arg \b vm-nytt singular of 'new'
691 \arg \b vm-nya plural of 'new'
692 \arg \b vm-gammalt singular of 'old'
693 \arg \b vm-gamla plural of 'old'
694 \arg \b digits/ett 'one', not always same as 'digits/1'
695 
696 Norwegian uses:
697 \arg \b vm-ny singular of 'new'
698 \arg \b vm-nye plural of 'new'
699 \arg \b vm-gammel singular of 'old'
700 \arg \b vm-gamle plural of 'old'
701 
702 Dutch also uses:
703 \arg \b nl-om 'at'?
704 
705 Spanish also uses:
706 \arg \b vm-youhaveno
707 
708 Italian requires the following additional soundfile:
709 
710 For vm_intro_it:
711 \arg \b vm-nuovo new
712 \arg \b vm-nuovi new plural
713 \arg \b vm-vecchio old
714 \arg \b vm-vecchi old plural
715 
716 Japanese requires the following additional soundfile:
717 \arg \b jp-arimasu there is
718 \arg \b jp-arimasen there is not
719 \arg \b jp-oshitekudasai please press
720 \arg \b jp-ni article ni
721 \arg \b jp-ga article ga
722 \arg \b jp-wa article wa
723 \arg \b jp-wo article wo
724 
725 Chinese (Taiwan) requires the following additional soundfile:
726 \arg \b vm-tong A class-word for call (tong1)
727 \arg \b vm-ri A class-word for day (ri4)
728 \arg \b vm-you You (ni3)
729 \arg \b vm-haveno Have no (mei2 you3)
730 \arg \b vm-have Have (you3)
731 \arg \b vm-listen To listen (yao4 ting1)
732 
733 
734 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
735 spelled among others when you have to change folder. For the above reasons, vm-INBOX
736 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
737 
738 */
739 
740 struct baseio {
741  int iocp;
742  int iolen;
744  int ateof;
745  unsigned char iobuf[BASEMAXINLINE];
746 };
747 
748 #define MAX_VM_MBOX_ID_LEN (AST_MAX_EXTENSION)
749 #define MAX_VM_CONTEXT_LEN (AST_MAX_CONTEXT)
750 /* MAX_VM_MAILBOX_LEN allows enough room for the '@' and NULL terminator */
751 #define MAX_VM_MAILBOX_LEN (MAX_VM_MBOX_ID_LEN + MAX_VM_CONTEXT_LEN)
752 
753 /*! Structure for linked list of users
754  * Use ast_vm_user_destroy() to free one of these structures. */
755 struct ast_vm_user {
756  char context[MAX_VM_CONTEXT_LEN];/*!< Voicemail context */
757  char mailbox[MAX_VM_MBOX_ID_LEN];/*!< Mailbox id, unique within vm context */
758  char password[80]; /*!< Secret pin code, numbers only */
759  char fullname[80]; /*!< Full name, for directory app */
760  char *email; /*!< E-mail address */
761  char *emailsubject; /*!< E-mail subject */
762  char *emailbody; /*!< E-mail body */
763  char pager[80]; /*!< E-mail address to pager (no attachment) */
764  char serveremail[80]; /*!< From: Mail address */
765  char fromstring[100]; /*!< From: Username */
766  char language[MAX_LANGUAGE]; /*!< Config: Language setting */
767  char zonetag[80]; /*!< Time zone */
768  char locale[20]; /*!< The locale (for presentation of date/time) */
769  char callback[80];
770  char dialout[80];
771  char uniqueid[80]; /*!< Unique integer identifier */
772  char exit[80];
773  char attachfmt[20]; /*!< Attachment format */
774  unsigned int flags; /*!< VM_ flags */
776  int minsecs; /*!< Minimum number of seconds per message for this mailbox */
777  int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
778  int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
779  int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
780  int passwordlocation; /*!< Storage location of the password */
781 #ifdef IMAP_STORAGE
782  char imapserver[48]; /*!< IMAP server address */
783  char imapport[8]; /*!< IMAP server port */
784  char imapflags[128]; /*!< IMAP optional flags */
785  char imapuser[80]; /*!< IMAP server login */
786  char imappassword[80]; /*!< IMAP server password if authpassword not defined */
787  char imapfolder[64]; /*!< IMAP voicemail folder */
788  char imapvmshareid[80]; /*!< Shared mailbox ID to use rather than the dialed one */
789  int imapversion; /*!< If configuration changes, use the new values */
790 #endif
791  double volgain; /*!< Volume gain for voicemails sent via email */
793 };
794 
795 /*! Voicemail time zones */
796 struct vm_zone {
798  char name[80];
799  char timezone[80];
800  char msg_format[512];
801 };
802 
803 #define VMSTATE_MAX_MSG_ARRAY 256
804 
805 /*! Voicemail mailbox state */
806 struct vm_state {
807  char curbox[80];
808  char username[80];
809  char context[80];
810  char curdir[PATH_MAX];
811  char vmbox[PATH_MAX];
812  char fn[PATH_MAX];
814  int *deleted;
815  int *heard;
816  int dh_arraysize; /* used for deleted / heard allocation */
817  int curmsg;
818  int lastmsg;
822  int starting;
823  int repeats;
824 #ifdef IMAP_STORAGE
826  int updated; /*!< decremented on each mail check until 1 -allows delay */
827  long *msgArray;
828  unsigned msg_array_max;
829  MAILSTREAM *mailstream;
830  int vmArrayIndex;
831  char imapuser[80]; /*!< IMAP server login */
832  char imapfolder[64]; /*!< IMAP voicemail folder */
833  char imapserver[48]; /*!< IMAP server address */
834  char imapport[8]; /*!< IMAP server port */
835  char imapflags[128]; /*!< IMAP optional flags */
836  int imapversion;
837  int interactive;
838  char introfn[PATH_MAX]; /*!< Name of prepended file */
839  unsigned int quota_limit;
840  unsigned int quota_usage;
841  struct vm_state *persist_vms;
842 #endif
843 };
844 
845 #ifdef ODBC_STORAGE
846 static char odbc_database[80] = "asterisk";
847 static char odbc_table[80] = "voicemessages";
848 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
849 #define DISPOSE(a,b) remove_file(a,b)
850 #define STORE(a,b,c,d,e,f,g,h,i,j,k) store_file(a,b,c,d)
851 #define EXISTS(a,b,c,d) (message_exists(a,b))
852 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
853 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
854 #define DELETE(a,b,c,d) (delete_file(a,b))
855 #define UPDATE_MSG_ID(a, b, c, d, e, f) (odbc_update_msg_id((a), (b), (c)))
856 #else
857 #ifdef IMAP_STORAGE
858 #define DISPOSE(a,b) (imap_remove_file(a,b))
859 #define STORE(a,b,c,d,e,f,g,h,i,j,k) (imap_store_file(a,b,c,d,e,f,g,h,i,j,k))
860 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
861 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
862 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
863 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
864 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
865 #define UPDATE_MSG_ID(a, b, c, d, e, f) (vm_imap_update_msg_id((a), (b), (c), (d), (e), (f)))
866 #else
867 #define RETRIEVE(a,b,c,d)
868 #define DISPOSE(a,b)
869 #define STORE(a,b,c,d,e,f,g,h,i,j,k)
870 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
871 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
872 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
873 #define DELETE(a,b,c,d) (vm_delete(c))
874 #define UPDATE_MSG_ID(a, b, c, d, e, f)
875 #endif
876 #endif
877 
878 static char VM_SPOOL_DIR[PATH_MAX];
879 
880 static char ext_pass_cmd[128];
881 static char ext_pass_check_cmd[128];
882 
883 static int my_umask;
884 
885 #define PWDCHANGE_INTERNAL (1 << 1)
886 #define PWDCHANGE_EXTERNAL (1 << 2)
888 
889 #ifdef ODBC_STORAGE
890 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
891 #else
892 # ifdef IMAP_STORAGE
893 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
894 # else
895 # define tdesc "Comedian Mail (Voicemail System)"
896 # endif
897 #endif
898 
899 static char userscontext[AST_MAX_EXTENSION] = "default";
900 
901 static char *addesc = "Comedian Mail";
902 
903 /* Leave a message */
904 static char *voicemail_app = "VoiceMail";
905 
906 /* Check mail, control, etc */
907 static char *voicemailmain_app = "VoiceMailMain";
908 
909 static char *vmauthenticate_app = "VMAuthenticate";
910 
911 static char *playmsg_app = "VoiceMailPlayMsg";
912 
913 static char *sayname_app = "VMSayName";
914 
917 static char zonetag[80];
918 static char locale[20];
919 static int maxsilence;
920 static int maxmsg = MAXMSG;
921 static int maxdeletedmsg;
922 static int silencethreshold = 128;
923 static char serveremail[80] = ASTERISK_USERNAME;
924 static char mailcmd[160] = SENDMAIL; /* Configurable mail cmd */
925 static char externnotify[160];
927 static char vmfmts[80] = "wav";
928 static double volgain;
929 static int vmminsecs;
930 static int vmmaxsecs;
931 static int maxgreet;
932 static int skipms = 3000;
933 static int maxlogins = 3;
935 static int passwordlocation;
937 
938 /*! Poll mailboxes for changes since there is something external to
939  * app_voicemail that may change them. */
940 static unsigned int poll_mailboxes;
941 
942 /*! By default, poll every 30 seconds */
943 #define DEFAULT_POLL_FREQ 30
944 /*! Polling frequency */
945 static unsigned int poll_freq = DEFAULT_POLL_FREQ;
946 
948 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
949 static pthread_t poll_thread = AST_PTHREADT_NULL;
950 static unsigned char poll_thread_run;
951 
953 
955  char *alias;
956  char *mailbox;
957  char buf[0];
958 };
959 
961  char *alias;
962  char *mailbox;
963  char buf[0];
964 };
965 
966 #define MAPPING_BUCKETS 511
970 
974 
975 /* custom audio control prompts for voicemail playback */
978 static char listen_control_pause_key[12];
980 static char listen_control_stop_key[12];
981 
982 /* custom password sounds */
983 static char vm_login[80] = "vm-login";
984 static char vm_newuser[80] = "vm-newuser";
985 static char vm_password[80] = "vm-password";
986 static char vm_newpassword[80] = "vm-newpassword";
987 static char vm_passchanged[80] = "vm-passchanged";
988 static char vm_reenterpassword[80] = "vm-reenterpassword";
989 static char vm_mismatch[80] = "vm-mismatch";
990 static char vm_invalid_password[80] = "vm-invalid-password";
991 static char vm_pls_try_again[80] = "vm-pls-try-again";
992 
993 /*
994  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
995  * 1. create a sound along the lines of "Please try again. When done, press the pound key" which could be spliced
996  * from existing sound clips. This would require some programming changes in the area of vm_forward options and also
997  * app.c's __ast_play_and_record function
998  * 2. create a sound prompt saying "Please try again. When done recording, press any key to stop and send the prepended
999  * message." At the time of this comment, I think this would require new voice work to be commissioned.
1000  * 3. Something way different like providing instructions before a time out or a post-recording menu. This would require
1001  * more effort than either of the other two.
1002  */
1003 static char vm_prepend_timeout[80] = "vm-then-pound";
1004 
1005 static struct ast_flags globalflags = {0};
1006 
1007 static int saydurationminfo = 2;
1008 
1009 static char dialcontext[AST_MAX_CONTEXT] = "";
1010 static char callcontext[AST_MAX_CONTEXT] = "";
1011 static char exitcontext[AST_MAX_CONTEXT] = "";
1012 
1014 
1015 
1016 static char *emailbody;
1017 static char *emailsubject;
1018 static char *pagerbody;
1019 static char *pagersubject;
1020 static char fromstring[100];
1021 static char pagerfromstring[100];
1022 static char charset[32] = "ISO-8859-1";
1023 
1024 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
1025 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
1026 static int adsiver = 1;
1027 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
1028 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
1029 
1030 /* Forward declarations - generic */
1031 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
1032 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu);
1033 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);
1034 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
1035 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
1036  char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
1037  signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro);
1038 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
1039 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
1040 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);
1041 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, const char *msg_id);
1042 static void apply_options(struct ast_vm_user *vmu, const char *options);
1043 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);
1044 static int is_valid_dtmf(const char *key);
1045 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
1046 static int write_password_to_file(const char *secretfn, const char *password);
1047 static const char *substitute_escapes(const char *value);
1048 static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids [], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu);
1049 static void notify_new_state(struct ast_vm_user *vmu);
1050 static int append_vmu_info_astman(struct mansession *s, struct ast_vm_user *vmu, const char* event_name, const char* actionid);
1051 
1052 
1053 /*!
1054  * Place a message in the indicated folder
1055  *
1056  * \param vmu Voicemail user
1057  * \param vms Current voicemail state for the user
1058  * \param msg The message number to save
1059  * \param box The folder into which the message should be saved
1060  * \param[out] newmsg The new message number of the saved message
1061  * \param move Tells whether to copy or to move the message
1062  *
1063  * \note the "move" parameter is only honored for IMAP voicemail presently
1064  * \retval 0 Success
1065  * \retval other Failure
1066  */
1067 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move);
1068 
1069 static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_create(const char *mailbox, const char *context, const char *folder, int descending, enum ast_vm_snapshot_sort_val sort_val, int combine_INBOX_and_OLD);
1070 static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot);
1071 
1072 static int vm_msg_forward(const char *from_mailbox, const char *from_context, const char *from_folder, const char *to_mailbox, const char *to_context, const char *to_folder, size_t num_msgs, const char *msg_ids[], int delete_old);
1073 static int vm_msg_move(const char *mailbox, const char *context, size_t num_msgs, const char *oldfolder, const char *old_msg_ids[], const char *newfolder);
1074 static int vm_msg_remove(const char *mailbox, const char *context, size_t num_msgs, const char *folder, const char *msgs[]);
1075 static int vm_msg_play(struct ast_channel *chan, const char *mailbox, const char *context, const char *folder, const char *msg_num, ast_vm_msg_play_cb cb);
1076 
1077 #ifdef TEST_FRAMEWORK
1078 static int vm_test_destroy_user(const char *context, const char *mailbox);
1079 static int vm_test_create_user(const char *context, const char *mailbox);
1080 #endif
1081 
1082 /*!
1083  * \internal
1084  * \brief Parse the given mailbox_id into mailbox and context.
1085  * \since 12.0.0
1086  *
1087  * \param mailbox_id The mailbox@context string to separate.
1088  * \param mailbox Where the mailbox part will start.
1089  * \param context Where the context part will start. ("default" if not present)
1090  *
1091  * \retval 0 on success.
1092  * \retval -1 on error.
1093  */
1094 static int separate_mailbox(char *mailbox_id, char **mailbox, char **context)
1095 {
1096  if (ast_strlen_zero(mailbox_id) || !mailbox || !context) {
1097  return -1;
1098  }
1099  *context = mailbox_id;
1100  *mailbox = strsep(context, "@");
1101  if (ast_strlen_zero(*mailbox)) {
1102  return -1;
1103  }
1104  if (ast_strlen_zero(*context)) {
1105  *context = "default";
1106  }
1107  return 0;
1108 }
1109 
1111 
1112 struct inprocess {
1113  int count;
1114  char *context;
1115  char mailbox[0];
1116 };
1117 
1118 static int inprocess_hash_fn(const void *obj, const int flags)
1119 {
1120  const struct inprocess *i = obj;
1121  return atoi(i->mailbox);
1122 }
1123 
1124 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
1125 {
1126  struct inprocess *i = obj, *j = arg;
1127  if (strcmp(i->mailbox, j->mailbox)) {
1128  return 0;
1129  }
1130  return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
1131 }
1132 
1133 static int inprocess_count(const char *context, const char *mailbox, int delta)
1134 {
1135  int context_len = strlen(context) + 1;
1136  int mailbox_len = strlen(mailbox) + 1;
1137  struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + context_len + mailbox_len);
1138  arg->context = arg->mailbox + mailbox_len;
1139  ast_copy_string(arg->mailbox, mailbox, mailbox_len); /* SAFE */
1140  ast_copy_string(arg->context, context, context_len); /* SAFE */
1141  ao2_lock(inprocess_container);
1142  if ((i = ao2_find(inprocess_container, arg, 0))) {
1143  int ret = ast_atomic_fetchadd_int(&i->count, delta);
1144  ao2_unlock(inprocess_container);
1145  ao2_ref(i, -1);
1146  return ret;
1147  }
1148  if (delta < 0) {
1149  ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
1150  }
1151  if (!(i = ao2_alloc(sizeof(*i) + context_len + mailbox_len, NULL))) {
1152  ao2_unlock(inprocess_container);
1153  return 0;
1154  }
1155  i->context = i->mailbox + mailbox_len;
1156  ast_copy_string(i->mailbox, mailbox, mailbox_len); /* SAFE */
1157  ast_copy_string(i->context, context, context_len); /* SAFE */
1158  i->count = delta;
1159  ao2_link(inprocess_container, i);
1160  ao2_unlock(inprocess_container);
1161  ao2_ref(i, -1);
1162  return 0;
1163 }
1164 
1165 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
1166 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
1167 #endif
1168 
1169 /*!
1170  * \brief Strips control and non 7-bit clean characters from input string.
1171  *
1172  * \note To map control and none 7-bit characters to a 7-bit clean characters
1173  * please use ast_str_encode_mine().
1174  */
1175 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
1176 {
1177  char *bufptr = buf;
1178  for (; *input; input++) {
1179  if (*input < 32) {
1180  continue;
1181  }
1182  *bufptr++ = *input;
1183  if (bufptr == buf + buflen - 1) {
1184  break;
1185  }
1186  }
1187  *bufptr = '\0';
1188  return buf;
1189 }
1190 
1191 
1192 /*!
1193  * \brief Sets default voicemail system options to a voicemail user.
1194  *
1195  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
1196  * - all the globalflags
1197  * - the saydurationminfo
1198  * - the callcontext
1199  * - the dialcontext
1200  * - the exitcontext
1201  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
1202  * - volume gain.
1203  * - emailsubject, emailbody set to NULL
1204  */
1205 static void populate_defaults(struct ast_vm_user *vmu)
1206 {
1207  ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
1209  if (saydurationminfo) {
1211  }
1212  ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
1213  ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
1214  ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
1215  ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
1216  ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
1217  if (vmminsecs) {
1218  vmu->minsecs = vmminsecs;
1219  }
1220  if (vmmaxsecs) {
1221  vmu->maxsecs = vmmaxsecs;
1222  }
1223  if (maxmsg) {
1224  vmu->maxmsg = maxmsg;
1225  }
1226  if (maxdeletedmsg) {
1228  }
1229  vmu->volgain = volgain;
1230  ast_free(vmu->email);
1231  vmu->email = NULL;
1232  ast_free(vmu->emailsubject);
1233  vmu->emailsubject = NULL;
1234  ast_free(vmu->emailbody);
1235  vmu->emailbody = NULL;
1236 #ifdef IMAP_STORAGE
1237  ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
1238  ast_copy_string(vmu->imapserver, imapserver, sizeof(vmu->imapserver));
1239  ast_copy_string(vmu->imapport, imapport, sizeof(vmu->imapport));
1240  ast_copy_string(vmu->imapflags, imapflags, sizeof(vmu->imapflags));
1241 #endif
1242 }
1243 
1244 /*!
1245  * \brief Sets a specific property value.
1246  * \param vmu The voicemail user object to work with.
1247  * \param var The name of the property to be set.
1248  * \param value The value to be set to the property.
1249  *
1250  * The property name must be one of the understood properties. See the source for details.
1251  */
1252 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
1253 {
1254  int x;
1255  if (!strcasecmp(var, "attach")) {
1256  ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
1257  } else if (!strcasecmp(var, "attachfmt")) {
1258  ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
1259  } else if (!strcasecmp(var, "serveremail")) {
1260  ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
1261  } else if (!strcasecmp(var, "fromstring")) {
1262  ast_copy_string(vmu->fromstring, value, sizeof(vmu->fromstring));
1263  } else if (!strcasecmp(var, "emailbody")) {
1264  ast_free(vmu->emailbody);
1265  vmu->emailbody = ast_strdup(substitute_escapes(value));
1266  } else if (!strcasecmp(var, "emailsubject")) {
1267  ast_free(vmu->emailsubject);
1269  } else if (!strcasecmp(var, "language")) {
1270  ast_copy_string(vmu->language, value, sizeof(vmu->language));
1271  } else if (!strcasecmp(var, "tz")) {
1272  ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
1273  } else if (!strcasecmp(var, "locale")) {
1274  ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
1275 #ifdef IMAP_STORAGE
1276  } else if (!strcasecmp(var, "imapuser")) {
1277  ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
1278  vmu->imapversion = imapversion;
1279  } else if (!strcasecmp(var, "imapserver")) {
1280  ast_copy_string(vmu->imapserver, value, sizeof(vmu->imapserver));
1281  vmu->imapversion = imapversion;
1282  } else if (!strcasecmp(var, "imapport")) {
1283  ast_copy_string(vmu->imapport, value, sizeof(vmu->imapport));
1284  vmu->imapversion = imapversion;
1285  } else if (!strcasecmp(var, "imapflags")) {
1286  ast_copy_string(vmu->imapflags, value, sizeof(vmu->imapflags));
1287  vmu->imapversion = imapversion;
1288  } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
1289  ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
1290  vmu->imapversion = imapversion;
1291  } else if (!strcasecmp(var, "imapfolder")) {
1292  ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
1293  vmu->imapversion = imapversion;
1294  } else if (!strcasecmp(var, "imapvmshareid")) {
1295  ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
1296  vmu->imapversion = imapversion;
1297 #endif
1298  } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
1299  ast_set2_flag(vmu, ast_true(value), VM_DELETE);
1300  } else if (!strcasecmp(var, "saycid")){
1301  ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
1302  } else if (!strcasecmp(var, "sendvoicemail")){
1303  ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
1304  } else if (!strcasecmp(var, "review")){
1305  ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
1306  } else if (!strcasecmp(var, "tempgreetwarn")){
1307  ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
1308  } else if (!strcasecmp(var, "messagewrap")){
1309  ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);
1310  } else if (!strcasecmp(var, "operator")) {
1311  ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
1312  } else if (!strcasecmp(var, "envelope")){
1313  ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
1314  } else if (!strcasecmp(var, "moveheard")){
1315  ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
1316  } else if (!strcasecmp(var, "sayduration")){
1317  ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
1318  } else if (!strcasecmp(var, "saydurationm")){
1319  if (sscanf(value, "%30d", &x) == 1) {
1320  vmu->saydurationm = x;
1321  } else {
1322  ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
1323  }
1324  } else if (!strcasecmp(var, "forcename")){
1325  ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
1326  } else if (!strcasecmp(var, "forcegreetings")){
1327  ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
1328  } else if (!strcasecmp(var, "callback")) {
1329  ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
1330  } else if (!strcasecmp(var, "dialout")) {
1331  ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
1332  } else if (!strcasecmp(var, "exitcontext")) {
1333  ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
1334  } else if (!strcasecmp(var, "minsecs")) {
1335  if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
1336  vmu->minsecs = x;
1337  } else {
1338  ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
1339  vmu->minsecs = vmminsecs;
1340  }
1341  } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
1342  vmu->maxsecs = atoi(value);
1343  if (vmu->maxsecs <= 0) {
1344  ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
1345  vmu->maxsecs = vmmaxsecs;
1346  } else {
1347  vmu->maxsecs = atoi(value);
1348  }
1349  if (!strcasecmp(var, "maxmessage"))
1350  ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
1351  } else if (!strcasecmp(var, "maxmsg")) {
1352  vmu->maxmsg = atoi(value);
1353  /* Accept maxmsg=0 (Greetings only voicemail) */
1354  if (vmu->maxmsg < 0) {
1355  ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
1356  vmu->maxmsg = MAXMSG;
1357  } else if (vmu->maxmsg > MAXMSGLIMIT) {
1358  ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
1359  vmu->maxmsg = MAXMSGLIMIT;
1360  }
1361  } else if (!strcasecmp(var, "nextaftercmd")) {
1362  ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
1363  } else if (!strcasecmp(var, "backupdeleted")) {
1364  if (sscanf(value, "%30d", &x) == 1)
1365  vmu->maxdeletedmsg = x;
1366  else if (ast_true(value))
1367  vmu->maxdeletedmsg = MAXMSG;
1368  else
1369  vmu->maxdeletedmsg = 0;
1370 
1371  if (vmu->maxdeletedmsg < 0) {
1372  ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
1373  vmu->maxdeletedmsg = MAXMSG;
1374  } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
1375  ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
1376  vmu->maxdeletedmsg = MAXMSGLIMIT;
1377  }
1378  } else if (!strcasecmp(var, "volgain")) {
1379  sscanf(value, "%30lf", &vmu->volgain);
1380  } else if (!strcasecmp(var, "passwordlocation")) {
1381  if (!strcasecmp(value, "spooldir")) {
1383  } else {
1385  }
1386  } else if (!strcasecmp(var, "options")) {
1387  apply_options(vmu, value);
1388  }
1389 }
1390 
1391 static char *vm_check_password_shell(char *command, char *buf, size_t len)
1392 {
1393  int fds[2], pid = 0;
1394 
1395  memset(buf, 0, len);
1396 
1397  if (pipe(fds)) {
1398  snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
1399  } else {
1400  /* good to go*/
1401  pid = ast_safe_fork(0);
1402 
1403  if (pid < 0) {
1404  /* ok maybe not */
1405  close(fds[0]);
1406  close(fds[1]);
1407  snprintf(buf, len, "FAILURE: Fork failed");
1408  } else if (pid) {
1409  /* parent */
1410  close(fds[1]);
1411  if (read(fds[0], buf, len) < 0) {
1412  ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
1413  }
1414  close(fds[0]);
1415  } else {
1416  /* child */
1418  AST_APP_ARG(v)[20];
1419  );
1420  char *mycmd = ast_strdupa(command);
1421 
1422  close(fds[0]);
1423  dup2(fds[1], STDOUT_FILENO);
1424  close(fds[1]);
1425  ast_close_fds_above_n(STDOUT_FILENO);
1426 
1427  AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
1428 
1429  execv(arg.v[0], arg.v);
1430  printf("FAILURE: %s", strerror(errno));
1431  _exit(0);
1432  }
1433  }
1434  return buf;
1435 }
1436 
1437 /*!
1438  * \brief Check that password meets minimum required length
1439  * \param vmu The voicemail user to change the password for.
1440  * \param password The password string to check
1441  *
1442  * \return zero on ok, 1 on not ok.
1443  */
1444 static int check_password(struct ast_vm_user *vmu, char *password)
1445 {
1446  /* check minimum length */
1447  if (strlen(password) < minpassword)
1448  return 1;
1449  /* check that password does not contain '*' character */
1450  if (!ast_strlen_zero(password) && password[0] == '*')
1451  return 1;
1452  if (!ast_strlen_zero(ext_pass_check_cmd)) {
1453  char cmd[255], buf[255];
1454 
1455  ast_debug(1, "Verify password policies for %s\n", password);
1456 
1457  snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
1458  if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
1459  ast_debug(5, "Result: %s\n", buf);
1460  if (!strncasecmp(buf, "VALID", 5)) {
1461  ast_debug(3, "Passed password check: '%s'\n", buf);
1462  return 0;
1463  } else if (!strncasecmp(buf, "FAILURE", 7)) {
1464  ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
1465  return 0;
1466  } else {
1467  ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
1468  return 1;
1469  }
1470  }
1471  }
1472  return 0;
1473 }
1474 
1475 /*!
1476  * \brief Performs a change of the voicemail passowrd in the realtime engine.
1477  * \param vmu The voicemail user to change the password for.
1478  * \param password The new value to be set to the password for this user.
1479  *
1480  * This only works if there is a realtime engine configured.
1481  * This is called from the (top level) vm_change_password.
1482  *
1483  * \return zero on success, -1 on error.
1484  */
1485 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
1486 {
1487  int res = -1;
1488  if (!strcmp(vmu->password, password)) {
1489  /* No change (but an update would return 0 rows updated, so we opt out here) */
1490  return 0;
1491  }
1492 
1493  if (strlen(password) > 10) {
1494  ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
1495  }
1496  if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
1497  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
1498  ast_copy_string(vmu->password, password, sizeof(vmu->password));
1499  res = 0;
1500  }
1501  return res;
1502 }
1503 
1504 /*!
1505  * \brief Destructively Parse options and apply.
1506  */
1507 static void apply_options(struct ast_vm_user *vmu, const char *options)
1508 {
1509  char *stringp;
1510  char *s;
1511  char *var, *value;
1512  stringp = ast_strdupa(options);
1513  while ((s = strsep(&stringp, "|"))) {
1514  value = s;
1515  if ((var = strsep(&value, "=")) && value) {
1516  apply_option(vmu, var, value);
1517  }
1518  }
1519 }
1520 
1521 /*!
1522  * \brief Loads the options specific to a voicemail user.
1523  *
1524  * This is called when a vm_user structure is being set up, such as from load_options.
1525  */
1527 {
1528  for (; var; var = var->next) {
1529  if (!strcasecmp(var->name, "vmsecret")) {
1530  ast_copy_string(retval->password, var->value, sizeof(retval->password));
1531  } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
1532  if (ast_strlen_zero(retval->password)) {
1533  if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
1534  ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
1535  "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
1536  } else {
1537  ast_copy_string(retval->password, var->value, sizeof(retval->password));
1538  }
1539  }
1540  } else if (!strcasecmp(var->name, "uniqueid")) {
1541  ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
1542  } else if (!strcasecmp(var->name, "pager")) {
1543  ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
1544  } else if (!strcasecmp(var->name, "email")) {
1545  ast_free(retval->email);
1546  retval->email = ast_strdup(var->value);
1547  } else if (!strcasecmp(var->name, "fullname")) {
1548  ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
1549  } else if (!strcasecmp(var->name, "context")) {
1550  ast_copy_string(retval->context, var->value, sizeof(retval->context));
1551  } else if (!strcasecmp(var->name, "emailsubject")) {
1552  ast_free(retval->emailsubject);
1554  } else if (!strcasecmp(var->name, "emailbody")) {
1555  ast_free(retval->emailbody);
1556  retval->emailbody = ast_strdup(substitute_escapes(var->value));
1557 #ifdef IMAP_STORAGE
1558  } else if (!strcasecmp(var->name, "imapuser")) {
1559  ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
1560  retval->imapversion = imapversion;
1561  } else if (!strcasecmp(var->name, "imapserver")) {
1562  ast_copy_string(retval->imapserver, var->value, sizeof(retval->imapserver));
1563  retval->imapversion = imapversion;
1564  } else if (!strcasecmp(var->name, "imapport")) {
1565  ast_copy_string(retval->imapport, var->value, sizeof(retval->imapport));
1566  retval->imapversion = imapversion;
1567  } else if (!strcasecmp(var->name, "imapflags")) {
1568  ast_copy_string(retval->imapflags, var->value, sizeof(retval->imapflags));
1569  retval->imapversion = imapversion;
1570  } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
1571  ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
1572  retval->imapversion = imapversion;
1573  } else if (!strcasecmp(var->name, "imapfolder")) {
1574  ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
1575  retval->imapversion = imapversion;
1576  } else if (!strcasecmp(var->name, "imapvmshareid")) {
1577  ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
1578  retval->imapversion = imapversion;
1579 #endif
1580  } else
1581  apply_option(retval, var->name, var->value);
1582  }
1583 }
1584 
1585 /*!
1586  * \brief Determines if a DTMF key entered is valid.
1587  * \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.
1588  *
1589  * Tests the character entered against the set of valid DTMF characters.
1590  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1591  */
1592 static int is_valid_dtmf(const char *key)
1593 {
1594  int i;
1595  char *local_key = ast_strdupa(key);
1596 
1597  for (i = 0; i < strlen(key); ++i) {
1598  if (!strchr(VALID_DTMF, *local_key)) {
1599  ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1600  return 0;
1601  }
1602  local_key++;
1603  }
1604  return 1;
1605 }
1606 
1607 /*!
1608  * \brief Finds a voicemail user from the realtime engine.
1609  * \param ivm
1610  * \param context
1611  * \param mailbox
1612  *
1613  * 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.
1614  *
1615  * \return The ast_vm_user structure for the user that was found.
1616  */
1617 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1618 {
1619  struct ast_variable *var;
1620  struct ast_vm_user *retval;
1621 
1622  if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1623  if (ivm) {
1624  memset(retval, 0, sizeof(*retval));
1625  }
1626  populate_defaults(retval);
1627  if (!ivm) {
1628  ast_set_flag(retval, VM_ALLOCED);
1629  }
1630  if (mailbox) {
1631  ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1632  }
1633  if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
1634  var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1635  } else {
1636  var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1637  }
1638  if (var) {
1639  apply_options_full(retval, var);
1640  ast_variables_destroy(var);
1641  } else {
1642  if (!ivm)
1643  ast_free(retval);
1644  retval = NULL;
1645  }
1646  }
1647  return retval;
1648 }
1649 
1650 /*!
1651  * \brief Finds a voicemail user from the users file or the realtime engine.
1652  * \param ivm
1653  * \param context
1654  * \param mailbox
1655  *
1656  * \return The ast_vm_user structure for the user that was found.
1657  */
1658 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1659 {
1660  /* This function could be made to generate one from a database, too */
1661  struct ast_vm_user *vmu = NULL, *cur;
1662  AST_LIST_LOCK(&users);
1663 
1664  if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
1665  context = "default";
1666 
1667  AST_LIST_TRAVERSE(&users, cur, list) {
1668 #ifdef IMAP_STORAGE
1669  if (cur->imapversion != imapversion) {
1670  continue;
1671  }
1672 #endif
1673  if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1674  break;
1675  if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1676  break;
1677  }
1678  if (cur) {
1679  /* Make a copy, so that on a reload, we have no race */
1680  if ((vmu = (ivm ? ivm : ast_calloc(1, sizeof(*vmu))))) {
1681  ast_free(vmu->email);
1682  ast_free(vmu->emailbody);
1683  ast_free(vmu->emailsubject);
1684  *vmu = *cur;
1685  vmu->email = ast_strdup(cur->email);
1686  vmu->emailbody = ast_strdup(cur->emailbody);
1687  vmu->emailsubject = ast_strdup(cur->emailsubject);
1688  ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1689  AST_LIST_NEXT(vmu, list) = NULL;
1690  }
1691  }
1693  if (!vmu) {
1694  vmu = find_user_realtime(ivm, context, mailbox);
1695  }
1696  if (!vmu && !ast_strlen_zero(aliasescontext)) {
1697  struct alias_mailbox_mapping *mapping;
1698  char *search_string = ast_alloca(MAX_VM_MAILBOX_LEN);
1699 
1700  snprintf(search_string, MAX_VM_MAILBOX_LEN, "%s%s%s",
1701  mailbox,
1702  ast_strlen_zero(context) ? "" : "@",
1703  S_OR(context, ""));
1704 
1705  mapping = ao2_find(alias_mailbox_mappings, search_string, OBJ_SEARCH_KEY);
1706  if (mapping) {
1707  char *search_mailbox = NULL;
1708  char *search_context = NULL;
1709 
1710  separate_mailbox(ast_strdupa(mapping->mailbox), &search_mailbox, &search_context);
1711  ao2_ref(mapping, -1);
1712  vmu = find_user(ivm, search_mailbox, search_context);
1713  }
1714  }
1715 
1716  return vmu;
1717 }
1718 
1719 /*!
1720  * \brief Resets a user password to a specified password.
1721  * \param context
1722  * \param mailbox
1723  * \param newpass
1724  *
1725  * This does the actual change password work, called by the vm_change_password() function.
1726  *
1727  * \return zero on success, -1 on error.
1728  */
1729 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
1730 {
1731  /* This function could be made to generate one from a database, too */
1732  struct ast_vm_user *cur;
1733  int res = -1;
1734  AST_LIST_LOCK(&users);
1735  AST_LIST_TRAVERSE(&users, cur, list) {
1736  if ((!context || !strcasecmp(context, cur->context)) &&
1737  (!strcasecmp(mailbox, cur->mailbox)))
1738  break;
1739  }
1740  if (cur) {
1741  ast_copy_string(cur->password, newpass, sizeof(cur->password));
1742  res = 0;
1743  }
1745  return res;
1746 }
1747 
1748 /*!
1749  * \brief Check if configuration file is valid
1750  */
1751 static inline int valid_config(const struct ast_config *cfg)
1752 {
1753  return cfg && cfg != CONFIG_STATUS_FILEINVALID;
1754 }
1755 
1756 /*!
1757  * \brief The handler for the change password option.
1758  * \param vmu The voicemail user to work with.
1759  * \param newpassword The new password (that has been gathered from the appropriate prompting).
1760  * 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.
1761  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
1762  */
1763 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
1764 {
1765  struct ast_config *cfg = NULL;
1766  struct ast_variable *var = NULL;
1767  struct ast_category *cat = NULL;
1768  char *category = NULL;
1769  const char *tmp = NULL;
1770  struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
1771  char secretfn[PATH_MAX] = "";
1772  int found = 0;
1773 
1774  if (!change_password_realtime(vmu, newpassword))
1775  return;
1776 
1777  /* check if we should store the secret in the spool directory next to the messages */
1778  switch (vmu->passwordlocation) {
1779  case OPT_PWLOC_SPOOLDIR:
1780  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
1781  if (write_password_to_file(secretfn, newpassword) == 0) {
1782  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
1783  ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
1784  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1785  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1786  break;
1787  } else {
1788  ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
1789  }
1790  /* Fall-through */
1792  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
1793  while ((category = ast_category_browse(cfg, category))) {
1794  if (!strcasecmp(category, vmu->context)) {
1795  char *value = NULL;
1796  char *new = NULL;
1797  if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
1798  ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
1799  break;
1800  }
1801  value = strstr(tmp, ",");
1802  if (!value) {
1803  new = ast_malloc(strlen(newpassword) + 1);
1804  sprintf(new, "%s", newpassword);
1805  } else {
1806  new = ast_malloc((strlen(value) + strlen(newpassword) + 1));
1807  sprintf(new, "%s%s", newpassword, value);
1808  }
1809  if (!(cat = ast_category_get(cfg, category, NULL))) {
1810  ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
1811  ast_free(new);
1812  break;
1813  }
1814  ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
1815  found = 1;
1816  ast_free(new);
1817  }
1818  }
1819  /* save the results */
1820  if (found) {
1821  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
1822  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1823  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1824  ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "app_voicemail");
1825  ast_config_destroy(cfg);
1826  break;
1827  }
1828 
1829  ast_config_destroy(cfg);
1830  }
1831  /* Fall-through */
1832  case OPT_PWLOC_USERSCONF:
1833  /* check users.conf and update the password stored for the mailbox */
1834  /* if no vmsecret entry exists create one. */
1835  if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
1836  ast_debug(4, "we are looking for %s\n", vmu->mailbox);
1837  for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
1838  ast_debug(4, "users.conf: %s\n", category);
1839  if (!strcasecmp(category, vmu->mailbox)) {
1840  char new[strlen(newpassword) + 1];
1841  if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
1842  ast_debug(3, "looks like we need to make vmsecret!\n");
1843  var = ast_variable_new("vmsecret", newpassword, "");
1844  } else {
1845  var = NULL;
1846  }
1847 
1848  sprintf(new, "%s", newpassword);
1849  if (!(cat = ast_category_get(cfg, category, NULL))) {
1850  ast_debug(4, "failed to get category!\n");
1851  ast_free(var);
1852  break;
1853  }
1854  if (!var) {
1855  ast_variable_update(cat, "vmsecret", new, NULL, 0);
1856  } else {
1857  ast_variable_append(cat, var);
1858  }
1859  found = 1;
1860  break;
1861  }
1862  }
1863  /* save the results and clean things up */
1864  if (found) {
1865  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
1866  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1867  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1868  ast_config_text_file_save("users.conf", cfg, "app_voicemail");
1869  }
1870 
1871  ast_config_destroy(cfg);
1872  }
1873  }
1874 }
1875 
1876 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
1877 {
1878  char buf[255];
1879  snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
1880  ast_debug(1, "External password: %s\n",buf);
1881  if (!ast_safe_system(buf)) {
1882  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
1883  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1884  /* Reset the password in memory, too */
1885  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1886  }
1887 }
1888 
1889 /*!
1890  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1891  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1892  * \param len The length of the path string that was written out.
1893  * \param context
1894  * \param ext
1895  * \param folder
1896  *
1897  * The path is constructed as
1898  * VM_SPOOL_DIRcontext/ext/folder
1899  *
1900  * \return zero on success, -1 on error.
1901  */
1902 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
1903 {
1904  return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
1905 }
1906 
1907 /*!
1908  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1909  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1910  * \param len The length of the path string that was written out.
1911  * \param dir
1912  * \param num
1913  *
1914  * The path is constructed as
1915  * VM_SPOOL_DIRcontext/ext/folder
1916  *
1917  * \return zero on success, -1 on error.
1918  */
1919 static int make_file(char *dest, const int len, const char *dir, const int num)
1920 {
1921  return snprintf(dest, len, "%s/msg%04d", dir, num);
1922 }
1923 
1924 /* same as mkstemp, but return a FILE * */
1925 static FILE *vm_mkftemp(char *template)
1926 {
1927  FILE *p = NULL;
1928  int pfd = mkstemp(template);
1929  chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1930  if (pfd > -1) {
1931  p = fdopen(pfd, "w+");
1932  if (!p) {
1933  close(pfd);
1934  pfd = -1;
1935  }
1936  }
1937  return p;
1938 }
1939 
1940 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1941  * \param dest String. base directory.
1942  * \param len Length of dest.
1943  * \param context String. Ignored if is null or empty string.
1944  * \param ext String. Ignored if is null or empty string.
1945  * \param folder String. Ignored if is null or empty string.
1946  * \return -1 on failure, 0 on success.
1947  */
1948 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1949 {
1950  mode_t mode = VOICEMAIL_DIR_MODE;
1951  int res;
1952 
1953  make_dir(dest, len, context, ext, folder);
1954  if ((res = ast_mkdir(dest, mode))) {
1955  ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1956  return -1;
1957  }
1958  return 0;
1959 }
1960 
1961 static const char *mbox(struct ast_vm_user *vmu, int id)
1962 {
1963 #ifdef IMAP_STORAGE
1964  if (vmu && id == 0) {
1965  return vmu->imapfolder;
1966  }
1967 #endif
1968  return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
1969 }
1970 
1971 static const char *vm_index_to_foldername(int id)
1972 {
1973  return mbox(NULL, id);
1974 }
1975 
1976 
1977 static int get_folder_by_name(const char *name)
1978 {
1979  size_t i;
1980 
1981  for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
1982  if (strcasecmp(name, mailbox_folders[i]) == 0) {
1983  return i;
1984  }
1985  }
1986 
1987  return -1;
1988 }
1989 
1990 static void free_user(struct ast_vm_user *vmu)
1991 {
1992  if (!vmu) {
1993  return;
1994  }
1995 
1996  ast_free(vmu->email);
1997  vmu->email = NULL;
1998  ast_free(vmu->emailbody);
1999  vmu->emailbody = NULL;
2000  ast_free(vmu->emailsubject);
2001  vmu->emailsubject = NULL;
2002 
2003  if (ast_test_flag(vmu, VM_ALLOCED)) {
2004  ast_free(vmu);
2005  }
2006 }
2007 
2008 static void free_user_final(struct ast_vm_user *vmu)
2009 {
2010  if (!vmu) {
2011  return;
2012  }
2013 
2014  if (!ast_strlen_zero(vmu->mailbox)) {
2016  }
2017 
2018  free_user(vmu);
2019 }
2020 
2021 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
2022 
2023  int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
2024 
2025  /* remove old allocation */
2026  if (vms->deleted) {
2027  ast_free(vms->deleted);
2028  vms->deleted = NULL;
2029  }
2030  if (vms->heard) {
2031  ast_free(vms->heard);
2032  vms->heard = NULL;
2033  }
2034  vms->dh_arraysize = 0;
2035 
2036  if (arraysize > 0) {
2037  if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
2038  return -1;
2039  }
2040  if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
2041  ast_free(vms->deleted);
2042  vms->deleted = NULL;
2043  return -1;
2044  }
2045  vms->dh_arraysize = arraysize;
2046  }
2047 
2048  return 0;
2049 }
2050 
2051 /* All IMAP-specific functions should go in this block. This
2052  * keeps them from being spread out all over the code */
2053 #ifdef IMAP_STORAGE
2054 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
2055 {
2056  char arg[10];
2057  struct vm_state *vms;
2058  unsigned long messageNum;
2059 
2060  /* If greetings aren't stored in IMAP, just delete the file */
2061  if (msgnum < 0 && !imapgreetings) {
2062  ast_filedelete(file, NULL);
2063  return;
2064  }
2065 
2066  if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2067  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);
2068  return;
2069  }
2070 
2071  if (msgnum < 0) {
2072  imap_delete_old_greeting(file, vms);
2073  return;
2074  }
2075 
2076  /* find real message number based on msgnum */
2077  /* this may be an index into vms->msgArray based on the msgnum. */
2078  messageNum = vms->msgArray[msgnum];
2079  if (messageNum == 0) {
2080  ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
2081  return;
2082  }
2083  ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
2084  /* delete message */
2085  snprintf (arg, sizeof(arg), "%lu", messageNum);
2086  ast_mutex_lock(&vms->lock);
2087  mail_setflag (vms->mailstream, arg, "\\DELETED");
2088  mail_expunge(vms->mailstream);
2089  ast_mutex_unlock(&vms->lock);
2090 }
2091 
2092 static void vm_imap_update_msg_id(char *dir, int msgnum, const char *msg_id, struct ast_vm_user *vmu, struct ast_config *msg_cfg, int folder)
2093 {
2094  struct ast_channel *chan;
2095  char *cid;
2096  char *cid_name;
2097  char *cid_num;
2098  struct vm_state *vms;
2099  const char *duration_str;
2100  int duration = 0;
2101 
2102  /*
2103  * First, get things initially set up. If any of this fails, then
2104  * back out before doing anything substantial
2105  */
2106  vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0);
2107  if (!vms) {
2108  return;
2109  }
2110 
2111  if (open_mailbox(vms, vmu, folder)) {
2112  return;
2113  }
2114 
2115  chan = ast_dummy_channel_alloc();
2116  if (!chan) {
2117  close_mailbox(vms, vmu);
2118  return;
2119  }
2120 
2121  /*
2122  * We need to make sure the new message we save has the same
2123  * callerid, flag, and duration as the original message
2124  */
2125  cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
2126 
2127  if (!ast_strlen_zero(cid)) {
2128  ast_callerid_parse(cid, &cid_name, &cid_num);
2130  if (!ast_strlen_zero(cid_name)) {
2131  ast_channel_caller(chan)->id.name.valid = 1;
2132  ast_channel_caller(chan)->id.name.str = ast_strdup(cid_name);
2133  }
2134  if (!ast_strlen_zero(cid_num)) {
2135  ast_channel_caller(chan)->id.number.valid = 1;
2136  ast_channel_caller(chan)->id.number.str = ast_strdup(cid_num);
2137  }
2138  }
2139 
2140  duration_str = ast_variable_retrieve(msg_cfg, "message", "duration");
2141 
2142  if (!ast_strlen_zero(duration_str)) {
2143  sscanf(duration_str, "%30d", &duration);
2144  }
2145 
2146  /*
2147  * IMAP messages cannot be altered once delivered. So we have to delete the
2148  * current message and then re-add it with the updated message ID.
2149  *
2150  * Furthermore, there currently is no atomic way to create a new message and to
2151  * store it in an arbitrary folder. So we have to save it to the INBOX and then
2152  * move to the appropriate folder.
2153  */
2154  if (!imap_store_file(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, vmfmts,
2155  duration, vms, ast_variable_retrieve(msg_cfg, "message", "flag"), msg_id)) {
2156  if (folder != NEW_FOLDER) {
2157  save_to_folder(vmu, vms, msgnum, folder, NULL, 1);
2158  }
2159  vm_imap_delete(dir, msgnum, vmu);
2160  }
2161  close_mailbox(vms, vmu);
2162  ast_channel_unref(chan);
2163 }
2164 
2165 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
2166 {
2167  struct vm_state *vms_p;
2168  char *file, *filename;
2169  char dest[PATH_MAX];
2170  int i;
2171  BODY *body;
2172  int ret = 0;
2173  int curr_mbox;
2174 
2175  /* This function is only used for retrieval of IMAP greetings
2176  * regular messages are not retrieved this way, nor are greetings
2177  * if they are stored locally*/
2178  if (msgnum > -1 || !imapgreetings) {
2179  return 0;
2180  } else {
2181  file = strrchr(ast_strdupa(dir), '/');
2182  if (file)
2183  *file++ = '\0';
2184  else {
2185  ast_debug(1, "Failed to procure file name from directory passed.\n");
2186  return -1;
2187  }
2188  }
2189 
2190  /* check if someone is accessing this box right now... */
2191  if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) &&
2192  !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2193  /* Unlike when retrieving a message, it is reasonable not to be able to find a
2194  * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
2195  * that's all we need to do.
2196  */
2197  if (!(vms_p = create_vm_state_from_user(vmu))) {
2198  ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
2199  return -1;
2200  }
2201  }
2202 
2203  /* Greetings will never have a prepended message */
2204  *vms_p->introfn = '\0';
2205 
2206  ast_mutex_lock(&vms_p->lock);
2207 
2208  /* get the current mailbox so that we can point the mailstream back to it later */
2209  curr_mbox = get_folder_by_name(vms_p->curbox);
2210 
2211  if (init_mailstream(vms_p, GREETINGS_FOLDER) || !vms_p->mailstream) {
2212  ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2213  ast_mutex_unlock(&vms_p->lock);
2214  return -1;
2215  }
2216 
2217  /*XXX Yuck, this could probably be done a lot better */
2218  for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
2219  mail_fetchstructure(vms_p->mailstream, i + 1, &body);
2220  /* We have the body, now we extract the file name of the first attachment. */
2221  if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2222  char *attachment = body->nested.part->next->body.parameter->value;
2223  char copy[strlen(attachment) + 1];
2224 
2225  strcpy(copy, attachment); /* safe */
2226  attachment = copy;
2227 
2228  filename = strsep(&attachment, ".");
2229  if (!strcmp(filename, file)) {
2230  ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
2231  vms_p->msgArray[vms_p->curmsg] = i + 1;
2232  create_dirpath(dest, sizeof(dest), vmu->context, vms_p->username, "");
2233  save_body(body, vms_p, "2", attachment, 0);
2234  ret = 0;
2235  break;
2236  }
2237  } else {
2238  ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
2239  ret = -1;
2240  break;
2241  }
2242  }
2243 
2244  if (curr_mbox != -1) {
2245  /* restore previous mbox stream */
2246  if (init_mailstream(vms_p, curr_mbox) || !vms_p->mailstream) {
2247  ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2248  ret = -1;
2249  }
2250  }
2251  ast_mutex_unlock(&vms_p->lock);
2252  return ret;
2253 }
2254 
2255 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
2256 {
2257  BODY *body;
2258  char *header_content;
2259  char *attachedfilefmt;
2260  char buf[80];
2261  struct vm_state *vms;
2262  char text_file[PATH_MAX];
2263  FILE *text_file_ptr;
2264  int res = 0;
2265  struct ast_vm_user *vmu;
2266  int curr_mbox;
2267 
2268  if (!(vmu = find_user(NULL, context, mailbox))) {
2269  ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
2270  return -1;
2271  }
2272 
2273  if (msgnum < 0) {
2274  if (imapgreetings) {
2275  res = imap_retrieve_greeting(dir, msgnum, vmu);
2276  goto exit;
2277  } else {
2278  res = 0;
2279  goto exit;
2280  }
2281  }
2282 
2283  /* Before anything can happen, we need a vm_state so that we can
2284  * actually access the imap server through the vms->mailstream
2285  */
2286  if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2287  /* This should not happen. If it does, then I guess we'd
2288  * need to create the vm_state, extract which mailbox to
2289  * open, and then set up the msgArray so that the correct
2290  * IMAP message could be accessed. If I have seen correctly
2291  * though, the vms should be obtainable from the vmstates list
2292  * and should have its msgArray properly set up.
2293  */
2294  ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
2295  res = -1;
2296  goto exit;
2297  }
2298 
2299  /* Ensure we have the correct mailbox open and have a valid mailstream for it */
2300  curr_mbox = get_folder_by_name(vms->curbox);
2301  if (curr_mbox < 0) {
2302  ast_debug(3, "Mailbox folder curbox not set, defaulting to Inbox\n");
2303  curr_mbox = 0;
2304  }
2305  init_mailstream(vms, curr_mbox);
2306  if (!vms->mailstream) {
2307  ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmu->mailbox);
2308  res = -1;
2309  goto exit;
2310  }
2311 
2312  make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
2313  snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
2314 
2315  /* Don't try to retrieve a message from IMAP if it already is on the file system */
2316  if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
2317  res = 0;
2318  goto exit;
2319  }
2320 
2321  ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
2322  if (vms->msgArray[msgnum] == 0) {
2323  ast_log(LOG_WARNING, "Trying to access unknown message\n");
2324  res = -1;
2325  goto exit;
2326  }
2327 
2328  /* This will only work for new messages... */
2329  ast_mutex_lock(&vms->lock);
2330  header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
2331  ast_mutex_unlock(&vms->lock);
2332  /* empty string means no valid header */
2333  if (ast_strlen_zero(header_content)) {
2334  ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
2335  res = -1;
2336  goto exit;
2337  }
2338 
2339  ast_mutex_lock(&vms->lock);
2340  mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
2341  ast_mutex_unlock(&vms->lock);
2342 
2343  /* We have the body, now we extract the file name of the first attachment. */
2344  if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2345  attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
2346  } else {
2347  ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
2348  res = -1;
2349  goto exit;
2350  }
2351 
2352  /* Find the format of the attached file */
2353 
2354  strsep(&attachedfilefmt, ".");
2355  if (!attachedfilefmt) {
2356  ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
2357  res = -1;
2358  goto exit;
2359  }
2360 
2361  save_body(body, vms, "2", attachedfilefmt, 0);
2362  if (save_body(body, vms, "3", attachedfilefmt, 1)) {
2363  *vms->introfn = '\0';
2364  }
2365 
2366  /* Get info from headers!! */
2367  snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
2368 
2369  if (!(text_file_ptr = fopen(text_file, "w"))) {
2370  ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
2371  goto exit;
2372  }
2373 
2374  fprintf(text_file_ptr, "%s\n", "[message]");
2375 
2376  if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))) {
2377  fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
2378  }
2379  if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))) {
2380  fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
2381  }
2382  if (get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))) {
2383  fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
2384  }
2385  if (get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))) {
2386  fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
2387  }
2388  if (get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))) {
2389  fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
2390  }
2391  if (get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))) {
2392  fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
2393  }
2394  if (get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf))) {
2395  fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
2396  }
2397  if (get_header_by_tag(header_content, "X-Asterisk-VM-Message-ID:", buf, sizeof(buf))) {
2398  fprintf(text_file_ptr, "msg_id=%s\n", S_OR(buf, ""));
2399  }
2400  fclose(text_file_ptr);
2401 
2402 exit:
2403  free_user(vmu);
2404  return res;
2405 }
2406 
2407 static int folder_int(const char *folder)
2408 {
2409  /*assume a NULL folder means INBOX*/
2410  if (!folder) {
2411  return 0;
2412  }
2413  if (!strcasecmp(folder, imapfolder)) {
2414  return 0;
2415  } else if (!strcasecmp(folder, "Old")) {
2416  return 1;
2417  } else if (!strcasecmp(folder, "Work")) {
2418  return 2;
2419  } else if (!strcasecmp(folder, "Family")) {
2420  return 3;
2421  } else if (!strcasecmp(folder, "Friends")) {
2422  return 4;
2423  } else if (!strcasecmp(folder, "Cust1")) {
2424  return 5;
2425  } else if (!strcasecmp(folder, "Cust2")) {
2426  return 6;
2427  } else if (!strcasecmp(folder, "Cust3")) {
2428  return 7;
2429  } else if (!strcasecmp(folder, "Cust4")) {
2430  return 8;
2431  } else if (!strcasecmp(folder, "Cust5")) {
2432  return 9;
2433  } else if (!strcasecmp(folder, "Urgent")) {
2434  return 11;
2435  } else { /*assume they meant INBOX if folder is not found otherwise*/
2436  return 0;
2437  }
2438 }
2439 
2440 static int __messagecount(const char *context, const char *mailbox, const char *folder)
2441 {
2442  SEARCHPGM *pgm;
2443  SEARCHHEADER *hdr;
2444 
2445  struct ast_vm_user *vmu, vmus;
2446  struct vm_state *vms_p;
2447  int ret = 0;
2448  int fold = folder_int(folder);
2449  int urgent = 0;
2450 
2451  /* If URGENT, then look at INBOX */
2452  if (fold == 11) {
2453  fold = NEW_FOLDER;
2454  urgent = 1;
2455  }
2456 
2457  if (ast_strlen_zero(mailbox))
2458  return 0;
2459 
2460  /* We have to get the user before we can open the stream! */
2461  memset(&vmus, 0, sizeof(vmus));
2462  vmu = find_user(&vmus, context, mailbox);
2463  if (!vmu) {
2464  ast_log(AST_LOG_WARNING, "Couldn't find mailbox %s in context %s\n", mailbox, context);
2465  free_user(vmu);
2466  return -1;
2467  } else {
2468  /* No IMAP account available */
2469  if (vmu->imapuser[0] == '\0') {
2470  ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2471  free_user(vmu);
2472  return -1;
2473  }
2474  }
2475 
2476  /* No IMAP account available */
2477  if (vmu->imapuser[0] == '\0') {
2478  ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2479  free_user(vmu);
2480  return -1;
2481  }
2482 
2483  /* check if someone is accessing this box right now... */
2484  vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
2485  if (!vms_p) {
2486  vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
2487  }
2488  if (vms_p) {
2489  ast_debug(3, "Returning before search - user is logged in\n");
2490  if (fold == 0) { /* INBOX */
2491  free_user(vmu);
2492  return urgent ? vms_p->urgentmessages : vms_p->newmessages;
2493  }
2494  if (fold == 1) { /* Old messages */
2495  free_user(vmu);
2496  return vms_p->oldmessages;
2497  }
2498  }
2499 
2500  /* add one if not there... */
2501  vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
2502  if (!vms_p) {
2503  vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
2504  }
2505 
2506  if (!vms_p) {
2507  vms_p = create_vm_state_from_user(vmu);
2508  }
2509  ret = init_mailstream(vms_p, fold);
2510  if (!vms_p->mailstream) {
2511  ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
2512  free_user(vmu);
2513  return -1;
2514  }
2515  if (ret == 0) {
2516  ast_mutex_lock(&vms_p->lock);
2517  pgm = mail_newsearchpgm ();
2518  hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
2519  hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
2520  pgm->header = hdr;
2521  if (fold != OLD_FOLDER) {
2522  pgm->unseen = 1;
2523  pgm->seen = 0;
2524  }
2525  /* In the special case where fold is 1 (old messages) we have to do things a bit
2526  * differently. Old messages are stored in the INBOX but are marked as "seen"
2527  */
2528  else {
2529  pgm->unseen = 0;
2530  pgm->seen = 1;
2531  }
2532  /* look for urgent messages */
2533  if (fold == NEW_FOLDER) {
2534  if (urgent) {
2535  pgm->flagged = 1;
2536  pgm->unflagged = 0;
2537  } else {
2538  pgm->flagged = 0;
2539  pgm->unflagged = 1;
2540  }
2541  }
2542  pgm->undeleted = 1;
2543  pgm->deleted = 0;
2544 
2545  vms_p->vmArrayIndex = 0;
2546  mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2547  if (fold == 0 && urgent == 0)
2548  vms_p->newmessages = vms_p->vmArrayIndex;
2549  if (fold == 1)
2550  vms_p->oldmessages = vms_p->vmArrayIndex;
2551  if (fold == 0 && urgent == 1)
2552  vms_p->urgentmessages = vms_p->vmArrayIndex;
2553  /*Freeing the searchpgm also frees the searchhdr*/
2554  mail_free_searchpgm(&pgm);
2555  ast_mutex_unlock(&vms_p->lock);
2556  free_user(vmu);
2557  vms_p->updated = 0;
2558  return vms_p->vmArrayIndex;
2559  } else {
2560  ast_mutex_lock(&vms_p->lock);
2561  mail_ping(vms_p->mailstream);
2562  ast_mutex_unlock(&vms_p->lock);
2563  }
2564  free_user(vmu);
2565  return 0;
2566 }
2567 
2568 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
2569 {
2570  /* Check if mailbox is full */
2571  check_quota(vms, vmu->imapfolder);
2572  if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
2573  ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
2574  if (chan) {
2575  ast_play_and_wait(chan, "vm-mailboxfull");
2576  }
2577  return -1;
2578  }
2579 
2580  /* Check if we have exceeded maxmsg */
2581  ast_debug(3, "Checking message number quota: mailbox has %d messages, maximum is set to %d, current messages %d\n", msgnum, vmu->maxmsg, inprocess_count(vmu->mailbox, vmu->context, 0));
2582  if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
2583  ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
2584  if (chan) {
2585  ast_play_and_wait(chan, "vm-mailboxfull");
2586  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2587  }
2588  return -1;
2589  }
2590 
2591  return 0;
2592 }
2593 
2594 /*!
2595  * \brief Gets the number of messages that exist in a mailbox folder.
2596  * \param mailbox_id
2597  * \param folder
2598  *
2599  * This method is used when IMAP backend is used.
2600  * \return The number of messages in this mailbox folder (zero or more).
2601  */
2602 static int messagecount(const char *mailbox_id, const char *folder)
2603 {
2604  char *context;
2605  char *mailbox;
2606 
2607  if (ast_strlen_zero(mailbox_id)
2608  || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
2609  return 0;
2610  }
2611 
2612  if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
2613  return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
2614  } else {
2615  return __messagecount(context, mailbox, folder);
2616  }
2617 }
2618 
2619 static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag, const char *msg_id)
2620 {
2621  char *myserveremail = serveremail;
2622  char fn[PATH_MAX];
2623  char introfn[PATH_MAX];
2624  char mailbox[256];
2625  char *stringp;
2626  FILE *p = NULL;
2627  char tmp[80] = "/tmp/astmail-XXXXXX";
2628  long len;
2629  void *buf;
2630  int tempcopy = 0;
2631  STRING str;
2632  int ret; /* for better error checking */
2633  char *imap_flags = NIL;
2634  int msgcount;
2635  int box = NEW_FOLDER;
2636 
2637  snprintf(mailbox, sizeof(mailbox), "%s@%s", vmu->mailbox, vmu->context);
2638  msgcount = messagecount(mailbox, "INBOX") + messagecount(mailbox, "Old");
2639 
2640  /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
2641  if (msgnum < 0) {
2642  if(!imapgreetings) {
2643  return 0;
2644  } else {
2645  box = GREETINGS_FOLDER;
2646  }
2647  }
2648 
2649  if (imap_check_limits(chan, vms, vmu, msgcount)) {
2650  return -1;
2651  }
2652 
2653  /* Set urgent flag for IMAP message */
2654  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
2655  ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
2656  imap_flags = "\\FLAGGED";
2657  }
2658 
2659  /* Attach only the first format */
2660  fmt = ast_strdupa(fmt);
2661  stringp = fmt;
2662  strsep(&stringp, "|");
2663 
2664  if (!ast_strlen_zero(vmu->serveremail))
2665  myserveremail = vmu->serveremail;
2666 
2667  if (msgnum > -1)
2668  make_file(fn, sizeof(fn), dir, msgnum);
2669  else
2670  ast_copy_string (fn, dir, sizeof(fn));
2671 
2672  snprintf(introfn, sizeof(introfn), "%sintro", fn);
2673  if (ast_fileexists(introfn, NULL, NULL) <= 0) {
2674  *introfn = '\0';
2675  }
2676 
2677  if (ast_strlen_zero(vmu->email)) {
2678  /* We need the vmu->email to be set when we call make_email_file, but
2679  * if we keep it set, a duplicate e-mail will be created. So at the end
2680  * of this function, we will revert back to an empty string if tempcopy
2681  * is 1.
2682  */
2683  vmu->email = ast_strdup(vmu->imapuser);
2684  tempcopy = 1;
2685  }
2686 
2687  if (!strcmp(fmt, "wav49"))
2688  fmt = "WAV";
2689  ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2690 
2691  /* Make a temporary file instead of piping directly to sendmail, in case the mail
2692  command hangs. */
2693  if (!(p = vm_mkftemp(tmp))) {
2694  ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2695  if (tempcopy) {
2696  ast_free(vmu->email);
2697  vmu->email = NULL;
2698  }
2699  return -1;
2700  }
2701 
2702  if (msgnum < 0 && imapgreetings) {
2703  if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
2704  ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
2705  return -1;
2706  }
2707  imap_delete_old_greeting(fn, vms);
2708  }
2709 
2710  make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
2711  chan ? S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL) : NULL,
2712  chan ? S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL) : NULL,
2713  fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
2714  /* read mail file to memory */
2715  len = ftell(p);
2716  rewind(p);
2717  if (!(buf = ast_malloc(len + 1))) {
2718  ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
2719  fclose(p);
2720  if (tempcopy)
2721  *(vmu->email) = '\0';
2722  return -1;
2723  }
2724  if (fread(buf, 1, len, p) != len) {
2725  if (ferror(p)) {
2726  ast_log(LOG_ERROR, "Error while reading mail file: %s\n", tmp);
2727  return -1;
2728  }
2729  }
2730  ((char *) buf)[len] = '\0';
2731  INIT(&str, mail_string, buf, len);
2732  ret = init_mailstream(vms, box);
2733  if (ret == 0) {
2734  imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
2735  ast_mutex_lock(&vms->lock);
2736  if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
2737  ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2738  ast_mutex_unlock(&vms->lock);
2739  fclose(p);
2740  unlink(tmp);
2741  ast_free(buf);
2742  } else {
2743  ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
2744  fclose(p);
2745  unlink(tmp);
2746  ast_free(buf);
2747  return -1;
2748  }
2749  ast_debug(3, "%s stored\n", fn);
2750 
2751  if (tempcopy)
2752  *(vmu->email) = '\0';
2753  inprocess_count(vmu->mailbox, vmu->context, -1);
2754  return 0;
2755 
2756 }
2757 
2758 /*!
2759  * \brief Gets the number of messages that exist in the inbox folder.
2760  * \param mailbox_context
2761  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
2762  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
2763  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
2764  *
2765  * This method is used when IMAP backend is used.
2766  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
2767  *
2768  * \return zero on success, -1 on error.
2769  */
2770 
2771 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
2772 {
2773  char tmp[PATH_MAX] = "";
2774  char *mailboxnc;
2775  char *context;
2776  char *mb;
2777  char *cur;
2778  if (newmsgs)
2779  *newmsgs = 0;
2780  if (oldmsgs)
2781  *oldmsgs = 0;
2782  if (urgentmsgs)
2783  *urgentmsgs = 0;
2784 
2785  ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
2786  /* If no mailbox, return immediately */
2787  if (ast_strlen_zero(mailbox_context))
2788  return 0;
2789 
2790  ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2791  context = strchr(tmp, '@');
2792  if (strchr(mailbox_context, ',')) {
2793  int tmpnew, tmpold, tmpurgent;
2794  ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2795  mb = tmp;
2796  while ((cur = strsep(&mb, ", "))) {
2797  if (!ast_strlen_zero(cur)) {
2798  if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2799  return -1;
2800  else {
2801  if (newmsgs)
2802  *newmsgs += tmpnew;
2803  if (oldmsgs)
2804  *oldmsgs += tmpold;
2805  if (urgentmsgs)
2806  *urgentmsgs += tmpurgent;
2807  }
2808  }
2809  }
2810  return 0;
2811  }
2812  if (context) {
2813  *context = '\0';
2814  mailboxnc = tmp;
2815  context++;
2816  } else {
2817  context = "default";
2818  mailboxnc = (char *) mailbox_context;
2819  }
2820 
2821  if (newmsgs) {
2822  struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
2823  if (!vmu) {
2824  ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2825  return -1;
2826  }
2827  if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
2828  free_user(vmu);
2829  return -1;
2830  }
2831  free_user(vmu);
2832  }
2833  if (oldmsgs) {
2834  if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
2835  return -1;
2836  }
2837  }
2838  if (urgentmsgs) {
2839  if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
2840  return -1;
2841  }
2842  }
2843  return 0;
2844 }
2845 
2846 /**
2847  * \brief Determines if the given folder has messages.
2848  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
2849  * \param folder the folder to look in
2850  *
2851  * This function is used when the mailbox is stored in an IMAP back end.
2852  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
2853  * \return 1 if the folder has one or more messages. zero otherwise.
2854  */
2855 
2856 static int has_voicemail(const char *mailbox, const char *folder)
2857 {
2858  char tmp[256], *tmp2, *box, *context;
2859  ast_copy_string(tmp, mailbox, sizeof(tmp));
2860  tmp2 = tmp;
2861  if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
2862  while ((box = strsep(&tmp2, ",&"))) {
2863  if (!ast_strlen_zero(box)) {
2864  if (has_voicemail(box, folder)) {
2865  return 1;
2866  }
2867  }
2868  }
2869  }
2870  if ((context = strchr(tmp, '@'))) {
2871  *context++ = '\0';
2872  } else {
2873  context = "default";
2874  }
2875  return __messagecount(context, tmp, folder) ? 1 : 0;
2876 }
2877 
2878 /*!
2879  * \brief Copies a message from one mailbox to another.
2880  * \param chan
2881  * \param vmu
2882  * \param imbox
2883  * \param msgnum
2884  * \param duration
2885  * \param recip
2886  * \param fmt
2887  * \param dir
2888  *
2889  * This works with IMAP storage based mailboxes.
2890  *
2891  * \return zero on success, -1 on error.
2892  */
2893 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, const char *dest_folder)
2894 {
2895  struct vm_state *sendvms = NULL;
2896  char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2897  if (msgnum >= recip->maxmsg) {
2898  ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
2899  return -1;
2900  }
2901  if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
2902  ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2903  return -1;
2904  }
2905  if (!get_vm_state_by_imapuser(recip->imapuser, 0)) {
2906  ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2907  return -1;
2908  }
2909  snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2910  ast_mutex_lock(&sendvms->lock);
2911  if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
2912  ast_mutex_unlock(&sendvms->lock);
2913  return 0;
2914  }
2915  ast_mutex_unlock(&sendvms->lock);
2916  ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2917  return -1;
2918 }
2919 
2920 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
2921 {
2922  char tmp[256], *t = tmp;
2923  size_t left = sizeof(tmp);
2924 
2925  if (box == OLD_FOLDER) {
2926  ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
2927  } else {
2928  ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
2929  }
2930 
2931  if (box == NEW_FOLDER) {
2932  ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
2933  } else {
2934  snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
2935  }
2936 
2937  /* Build up server information */
2938  ast_build_string(&t, &left, "{%s:%s/imap", S_OR(vms->imapserver, imapserver), S_OR(vms->imapport, imapport));
2939 
2940  /* Add authentication user if present */
2941  if (!ast_strlen_zero(authuser))
2942  ast_build_string(&t, &left, "/authuser=%s", authuser);
2943 
2944  /* Add flags if present */
2945  if (!ast_strlen_zero(imapflags) || !(ast_strlen_zero(vms->imapflags))) {
2946  ast_build_string(&t, &left, "/%s", S_OR(vms->imapflags, imapflags));
2947  }
2948 
2949  /* End with username */
2950 #if 1
2951  ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
2952 #else
2953  ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
2954 #endif
2955  if (box == NEW_FOLDER || box == OLD_FOLDER)
2956  snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
2957  else if (box == GREETINGS_FOLDER)
2958  snprintf(spec, len, "%s%s", tmp, greetingfolder);
2959  else { /* Other folders such as Friends, Family, etc... */
2960  if (!ast_strlen_zero(imapparentfolder)) {
2961  /* imapparentfolder would typically be set to INBOX */
2962  snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
2963  } else {
2964  snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
2965  }
2966  }
2967 }
2968 
2969 static int init_mailstream(struct vm_state *vms, int box)
2970 {
2971  MAILSTREAM *stream = NIL;
2972  long debug;
2973  char tmp[256];
2974 
2975  if (!vms) {
2976  ast_log(LOG_ERROR, "vm_state is NULL!\n");
2977  return -1;
2978  }
2979  ast_debug(3, "vm_state user is:%s\n", vms->imapuser);
2980  if (vms->mailstream == NIL || !vms->mailstream) {
2981  ast_debug(1, "mailstream not set.\n");
2982  } else {
2983  stream = vms->mailstream;
2984  }
2985  /* debug = T; user wants protocol telemetry? */
2986  debug = NIL; /* NO protocol telemetry? */
2987 
2988  if (delimiter == '\0') { /* did not probe the server yet */
2989  char *cp;
2990 #ifdef USE_SYSTEM_IMAP
2991 #include <imap/linkage.c>
2992 #elif defined(USE_SYSTEM_CCLIENT)
2993 #include <c-client/linkage.c>
2994 #else
2995 #include "linkage.c"
2996 #endif
2997  /* Connect to INBOX first to get folders delimiter */
2998  imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
2999  ast_mutex_lock(&vms->lock);
3000  ast_mutex_lock(&mail_open_lock);
3001  stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
3002  ast_mutex_unlock(&mail_open_lock);
3003  ast_mutex_unlock(&vms->lock);
3004  if (stream == NIL) {
3005  ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
3006  return -1;
3007  }
3008  get_mailbox_delimiter(vms, stream);
3009  /* update delimiter in imapfolder */
3010  for (cp = vms->imapfolder; *cp; cp++)
3011  if (*cp == '/')
3012  *cp = delimiter;
3013  }
3014  /* Now connect to the target folder */
3015  imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
3016  ast_debug(3, "Before mail_open, server: %s, box:%d\n", tmp, box);
3017  ast_mutex_lock(&vms->lock);
3018  ast_mutex_lock(&mail_open_lock);
3019  vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
3020  /* Create the folder if it dosn't exist */
3021  if (vms->mailstream && !mail_status(vms->mailstream, tmp, SA_UIDNEXT)) {
3022  mail_create(vms->mailstream, tmp);
3023  }
3024  ast_mutex_unlock(&mail_open_lock);
3025  ast_mutex_unlock(&vms->lock);
3026  if (vms->mailstream == NIL) {
3027  return -1;
3028  } else {
3029  return 0;
3030  }
3031 }
3032 
3033 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
3034 {
3035  SEARCHPGM *pgm;
3036  SEARCHHEADER *hdr;
3037  int urgent = 0;
3038 
3039  /* If Urgent, then look at INBOX */
3040  if (box == 11) {
3041  box = NEW_FOLDER;
3042  urgent = 1;
3043  }
3044 
3045  ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
3046  ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
3047  ast_copy_string(vms->imapserver, vmu->imapserver, sizeof(vms->imapserver));
3048  ast_copy_string(vms->imapport, vmu->imapport, sizeof(vms->imapport));
3049  ast_copy_string(vms->imapflags, vmu->imapflags, sizeof(vms->imapflags));
3050  vms->imapversion = vmu->imapversion;
3051  ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
3052 
3053  if (init_mailstream(vms, box) || !vms->mailstream) {
3054  ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
3055  return -1;
3056  }
3057 
3058  create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
3059 
3060  /* Check Quota */
3061  if (box == 0) {
3062  ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
3063  check_quota(vms, (char *) mbox(vmu, box));
3064  }
3065 
3066  ast_mutex_lock(&vms->lock);
3067  pgm = mail_newsearchpgm();
3068 
3069  /* Check IMAP folder for Asterisk messages only... */
3070  hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
3071  hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
3072  pgm->header = hdr;
3073  pgm->deleted = 0;
3074  pgm->undeleted = 1;
3075 
3076  /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
3077  if (box == NEW_FOLDER && urgent == 1) {
3078  pgm->unseen = 1;
3079  pgm->seen = 0;
3080  pgm->flagged = 1;
3081  pgm->unflagged = 0;
3082  } else if (box == NEW_FOLDER && urgent == 0) {
3083  pgm->unseen = 1;
3084  pgm->seen = 0;
3085  pgm->flagged = 0;
3086  pgm->unflagged = 1;
3087  } else if (box == OLD_FOLDER) {
3088  pgm->seen = 1;
3089  pgm->unseen = 0;
3090  }
3091 
3092  ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
3093 
3094  vms->vmArrayIndex = 0;
3095  mail_search_full (vms->mailstream, NULL, pgm, NIL);
3096  vms->lastmsg = vms->vmArrayIndex - 1;
3097  mail_free_searchpgm(&pgm);
3098  /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
3099  * ensure to allocate enough space to account for all of them. Warn if old messages
3100  * have not been checked first as that is required.
3101  */
3102  if (box == 0 && !vms->dh_arraysize) {
3103  ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
3104  }
3105  if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
3106  ast_mutex_unlock(&vms->lock);
3107  return -1;
3108  }
3109 
3110  ast_mutex_unlock(&vms->lock);
3111  return 0;
3112 }
3113 
3114 static void write_file(char *filename, char *buffer, unsigned long len)
3115 {
3116  FILE *output;
3117 
3118  if (!filename || !buffer) {
3119  return;
3120  }
3121 
3122  if (!(output = fopen(filename, "w"))) {
3123  ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", filename, strerror(errno));
3124  return;
3125  }
3126 
3127  if (fwrite(buffer, len, 1, output) != 1) {
3128  if (ferror(output)) {
3129  ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
3130  }
3131  }
3132  fclose (output);
3133 }
3134 
3135 static void update_messages_by_imapuser(const char *user, unsigned long number)
3136 {
3137  struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
3138 
3139  if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
3140  return;
3141  }
3142 
3143  ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
3144 
3145  /* Ensure we have room for the next message. */
3146  if (vms->vmArrayIndex >= vms->msg_array_max) {
3147  long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
3148  if (!new_mem) {
3149  return;
3150  }
3151  vms->msgArray = new_mem;
3152  vms->msg_array_max *= 2;
3153  }
3154 
3155  vms->msgArray[vms->vmArrayIndex++] = number;
3156 }
3157 
3158 void mm_searched(MAILSTREAM *stream, unsigned long number)
3159 {
3160  char *mailbox = stream->mailbox, buf[1024] = "", *user;
3161 
3162  if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
3163  return;
3164 
3165  update_messages_by_imapuser(user, number);
3166 }
3167 
3168 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
3169 {
3170  struct ast_variable *var;
3171  struct ast_vm_user *vmu;
3172 
3173  vmu = ast_calloc(1, sizeof *vmu);
3174  if (!vmu)
3175  return NULL;
3176 
3177  populate_defaults(vmu);
3178  ast_set_flag(vmu, VM_ALLOCED);
3179 
3180  var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
3181  if (var) {
3182  apply_options_full(vmu, var);
3183  ast_variables_destroy(var);
3184  return vmu;
3185  } else {
3186  ast_free(vmu);
3187  return NULL;
3188  }
3189 }
3190 
3191 /* Interfaces to C-client */
3192 
3193 void mm_exists(MAILSTREAM * stream, unsigned long number)
3194 {
3195  /* mail_ping will callback here if new mail! */
3196  ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
3197  if (number == 0) return;
3198  set_update(stream);
3199 }
3200 
3201 
3202 void mm_expunged(MAILSTREAM * stream, unsigned long number)
3203 {
3204  /* mail_ping will callback here if expunged mail! */
3205  ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
3206  if (number == 0) return;
3207  set_update(stream);
3208 }
3209 
3210 
3211 void mm_flags(MAILSTREAM * stream, unsigned long number)
3212 {
3213  /* mail_ping will callback here if read mail! */
3214  ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
3215  if (number == 0) return;
3216  set_update(stream);
3217 }
3218 
3219 
3220 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
3221 {
3222  ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
3223  mm_log (string, errflg);
3224 }
3225 
3226 
3227 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
3228 {
3229  if (delimiter == '\0') {
3230  delimiter = delim;
3231  }
3232 
3233  ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
3234  if (attributes & LATT_NOINFERIORS)
3235  ast_debug(5, "no inferiors\n");
3236  if (attributes & LATT_NOSELECT)
3237  ast_debug(5, "no select\n");
3238  if (attributes & LATT_MARKED)
3239  ast_debug(5, "marked\n");
3240  if (attributes & LATT_UNMARKED)
3241  ast_debug(5, "unmarked\n");
3242 }
3243 
3244 
3245 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
3246 {
3247  ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
3248  if (attributes & LATT_NOINFERIORS)
3249  ast_debug(5, "no inferiors\n");
3250  if (attributes & LATT_NOSELECT)
3251  ast_debug(5, "no select\n");
3252  if (attributes & LATT_MARKED)
3253  ast_debug(5, "marked\n");
3254  if (attributes & LATT_UNMARKED)
3255  ast_debug(5, "unmarked\n");
3256 }
3257 
3258 
3259 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
3260 {
3261  struct ast_str *str;
3262 
3263  if (!DEBUG_ATLEAST(5) || !(str = ast_str_create(256))) {
3264  return;
3265  }
3266 
3267  ast_str_append(&str, 0, " Mailbox %s", mailbox);
3268  if (status->flags & SA_MESSAGES) {
3269  ast_str_append(&str, 0, ", %lu messages", status->messages);
3270  }
3271  if (status->flags & SA_RECENT) {
3272  ast_str_append(&str, 0, ", %lu recent", status->recent);
3273  }
3274  if (status->flags & SA_UNSEEN) {
3275  ast_str_append(&str, 0, ", %lu unseen", status->unseen);
3276  }
3277  if (status->flags & SA_UIDVALIDITY) {
3278  ast_str_append(&str, 0, ", %lu UID validity", status->uidvalidity);
3279  }
3280  if (status->flags & SA_UIDNEXT) {
3281  ast_str_append(&str, 0, ", %lu next UID", status->uidnext);
3282  }
3283  ast_log(LOG_DEBUG, "%s\n", ast_str_buffer(str));
3284 
3285  ast_free(str);
3286 }
3287 
3288 
3289 void mm_log(char *string, long errflg)
3290 {
3291  switch ((short) errflg) {
3292  case NIL:
3293  ast_debug(1, "IMAP Info: %s\n", string);
3294  break;
3295  case PARSE:
3296  case WARN:
3297  ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
3298  break;
3299  case ERROR:
3300  ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
3301  break;
3302  }
3303 }
3304 
3305 
3306 void mm_dlog(char *string)
3307 {
3308  ast_log(AST_LOG_NOTICE, "%s\n", string);
3309 }
3310 
3311 
3312 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
3313 {
3314  struct ast_vm_user *vmu;
3315 
3316  ast_debug(4, "Entering callback mm_login\n");
3317 
3318  ast_copy_string(user, mb->user, MAILTMPLEN);
3319 
3320  /* We should only do this when necessary */
3321  if (!ast_strlen_zero(authpassword)) {
3322  ast_copy_string(pwd, authpassword, MAILTMPLEN);
3323  } else {
3324  AST_LIST_TRAVERSE(&users, vmu, list) {
3325  if (!strcasecmp(mb->user, vmu->imapuser)) {
3326  ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
3327  break;
3328  }
3329  }
3330  if (!vmu) {
3331  if ((vmu = find_user_realtime_imapuser(mb->user))) {
3332  ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
3333  free_user(vmu);
3334  }
3335  }
3336  }
3337 }
3338 
3339 
3340 void mm_critical(MAILSTREAM * stream)
3341 {
3342 }
3343 
3344 
3345 void mm_nocritical(MAILSTREAM * stream)
3346 {
3347 }
3348 
3349 
3350 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
3351 {
3352  kill (getpid (), SIGSTOP);
3353  return NIL;
3354 }
3355 
3356 
3357 void mm_fatal(char *string)
3358 {
3359  ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
3360 }
3361 
3362 /* C-client callback to handle quota */
3363 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
3364 {
3365  struct vm_state *vms;
3366  char *mailbox = stream->mailbox, *user;
3367  char buf[1024] = "";
3368  unsigned long usage = 0, limit = 0;
3369 
3370  while (pquota) {
3371  usage = pquota->usage;
3372  limit = pquota->limit;
3373  pquota = pquota->next;
3374  }
3375 
3376  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)))) {
3377  ast_log(AST_LOG_ERROR, "No state found.\n");
3378  return;
3379  }
3380 
3381  ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
3382 
3383  vms->quota_usage = usage;
3384  vms->quota_limit = limit;
3385 }
3386 
3387 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
3388 {
3389  char *start, *eol_pnt;
3390  int taglen;
3391 
3392  if (ast_strlen_zero(header) || ast_strlen_zero(tag))
3393  return NULL;
3394 
3395  taglen = strlen(tag) + 1;
3396  if (taglen < 1)
3397  return NULL;
3398 
3399  if (!(start = strcasestr(header, tag)))
3400  return NULL;
3401 
3402  /* Since we can be called multiple times we should clear our buffer */
3403  memset(buf, 0, len);
3404 
3405  ast_copy_string(buf, start+taglen, len);
3406  if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
3407  *eol_pnt = '\0';
3408  return buf;
3409 }
3410 
3411 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
3412 {
3413  char *start, *eol_pnt, *quote;
3414 
3415  if (ast_strlen_zero(mailbox))
3416  return NULL;
3417 
3418  if (!(start = strstr(mailbox, "/user=")))
3419  return NULL;
3420 
3421  ast_copy_string(buf, start+6, len);
3422 
3423  if (!(quote = strchr(buf, '"'))) {
3424  if ((eol_pnt = strchr(buf, '/')) || (eol_pnt = strchr(buf, '}'))) {
3425  *eol_pnt = '\0';
3426  }
3427  return buf;
3428  } else {
3429  if ((eol_pnt = strchr(quote + 1, '"'))) {
3430  *eol_pnt = '\0';
3431  }
3432  return quote + 1;
3433  }
3434 }
3435 
3436 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
3437 {
3438  struct vm_state *vms_p;
3439 
3440  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3441  if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
3442  return vms_p;
3443  }
3444  ast_debug(5, "Adding new vmstate for %s\n", vmu->imapuser);
3445  /* XXX: Is this correctly freed always? */
3446  if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
3447  return NULL;
3448  ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
3449  ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
3450  ast_copy_string(vms_p->imapserver, vmu->imapserver, sizeof(vms_p->imapserver));
3451  ast_copy_string(vms_p->imapport, vmu->imapport, sizeof(vms_p->imapport));
3452  ast_copy_string(vms_p->imapflags, vmu->imapflags, sizeof(vms_p->imapflags));
3453  ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
3454  ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
3455  vms_p->mailstream = NIL; /* save for access from interactive entry point */
3456  vms_p->imapversion = vmu->imapversion;
3457  ast_debug(5, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
3458  vms_p->updated = 1;
3459  /* set mailbox to INBOX! */
3460  ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
3461  init_vm_state(vms_p);
3462  vmstate_insert(vms_p);
3463  return vms_p;
3464 }
3465 
3466 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
3467 {
3468  struct vmstate *vlist = NULL;
3469 
3470  if (interactive) {
3471  struct vm_state *vms;
3472  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3473  if ((vms = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms->imapuser, user)) {
3474  return vms;
3475  }
3476  }
3477 
3478  AST_LIST_LOCK(&vmstates);
3479  AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3480  if (!vlist->vms) {
3481  ast_debug(3, "error: vms is NULL for %s\n", user);
3482  continue;
3483  }
3484  if (vlist->vms->imapversion != imapversion) {
3485  continue;
3486  }
3487 
3488  if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
3489  AST_LIST_UNLOCK(&vmstates);
3490  return vlist->vms;
3491  }
3492  }
3493  AST_LIST_UNLOCK(&vmstates);
3494 
3495  ast_debug(3, "%s not found in vmstates\n", user);
3496 
3497  return NULL;
3498 }
3499 
3500 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
3501 {
3502 
3503  struct vmstate *vlist = NULL;
3504  const char *local_context = S_OR(context, "default");
3505 
3506  if (interactive) {
3507  struct vm_state *vms;
3508  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3509  if ((vms = pthread_getspecific(ts_vmstate.key)) &&
3510  !strcmp(vms->username,mailbox) && !strcmp(vms->context, local_context)) {
3511  return vms;
3512  }
3513  }
3514 
3515  AST_LIST_LOCK(&vmstates);
3516  AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3517  if (!vlist->vms) {
3518  ast_debug(3, "error: vms is NULL for %s\n", mailbox);
3519  continue;
3520  }
3521  if (vlist->vms->imapversion != imapversion) {
3522  continue;
3523  }
3524 
3525  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);
3526 
3527  if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
3528  ast_debug(3, "Found it!\n");
3529  AST_LIST_UNLOCK(&vmstates);
3530  return vlist->vms;
3531  }
3532  }
3533  AST_LIST_UNLOCK(&vmstates);
3534 
3535  ast_debug(3, "%s not found in vmstates\n", mailbox);
3536 
3537  return NULL;
3538 }
3539 
3540 static void vmstate_insert(struct vm_state *vms)
3541 {
3542  struct vmstate *v;
3543  struct vm_state *altvms;
3544 
3545  /* If interactive, it probably already exists, and we should
3546  use the one we already have since it is more up to date.
3547  We can compare the username to find the duplicate */
3548  if (vms->interactive == 1) {
3549  altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
3550  if (altvms) {
3551  ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3552  vms->newmessages = altvms->newmessages;
3553  vms->oldmessages = altvms->oldmessages;
3554  vms->vmArrayIndex = altvms->vmArrayIndex;
3555  /* XXX: no msgArray copying? */
3556  vms->lastmsg = altvms->lastmsg;
3557  vms->curmsg = altvms->curmsg;
3558  /* get a pointer to the persistent store */
3559  vms->persist_vms = altvms;
3560  /* Reuse the mailstream? */
3561 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
3562  vms->mailstream = altvms->mailstream;
3563 #else
3564  vms->mailstream = NIL;
3565 #endif
3566  }
3567  return;
3568  }
3569 
3570  if (!(v = ast_calloc(1, sizeof(*v))))
3571  return;
3572 
3573  v->vms = vms;
3574 
3575  ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3576 
3577  AST_LIST_LOCK(&vmstates);
3578  AST_LIST_INSERT_TAIL(&vmstates, v, list);
3579  AST_LIST_UNLOCK(&vmstates);
3580 }
3581 
3582 static void vmstate_delete(struct vm_state *vms)
3583 {
3584  struct vmstate *vc = NULL;
3585  struct vm_state *altvms = NULL;
3586 
3587  /* If interactive, we should copy pertinent info
3588  back to the persistent state (to make update immediate) */
3589  if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
3590  ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3591  altvms->newmessages = vms->newmessages;
3592  altvms->oldmessages = vms->oldmessages;
3593  altvms->updated = 1;
3594  vms->mailstream = mail_close(vms->mailstream);
3595 
3596  /* Interactive states are not stored within the persistent list */
3597  return;
3598  }
3599 
3600  ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3601 
3602  AST_LIST_LOCK(&vmstates);
3603  AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
3604  if (vc->vms == vms) {
3606  break;
3607  }
3608  }
3610  AST_LIST_UNLOCK(&vmstates);
3611 
3612  if (vc) {
3613  ast_mutex_destroy(&vc->vms->lock);
3614  ast_free(vc->vms->msgArray);
3615  vc->vms->msgArray = NULL;
3616  vc->vms->msg_array_max = 0;
3617  /* XXX: is no one supposed to free vms itself? */
3618  ast_free(vc);
3619  } else {
3620  ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3621  }
3622 }
3623 
3624 static void set_update(MAILSTREAM * stream)
3625 {
3626  struct vm_state *vms;
3627  char *mailbox = stream->mailbox, *user;
3628  char buf[1024] = "";
3629 
3630  if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
3631  if (user && DEBUG_ATLEAST(3))
3632  ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
3633  return;
3634  }
3635 
3636  ast_debug(3, "User %s mailbox set for update.\n", user);
3637 
3638  vms->updated = 1; /* Set updated flag since mailbox changed */
3639 }
3640 
3641 static void init_vm_state(struct vm_state *vms)
3642 {
3643  vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
3644  vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
3645  if (!vms->msgArray) {
3646  /* Out of mem? This can't be good. */
3647  vms->msg_array_max = 0;
3648  }
3649  vms->vmArrayIndex = 0;
3650  ast_mutex_init(&vms->lock);
3651 }
3652 
3653 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro)
3654 {
3655  char *body_content;
3656  char *body_decoded;
3657  char *fn = is_intro ? vms->introfn : vms->fn;
3658  unsigned long len = 0;
3659  unsigned long newlen = 0;
3660  char filename[256];
3661 
3662  if (!body || body == NIL)
3663  return -1;
3664 
3665  ast_mutex_lock(&vms->lock);
3666  body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
3667  ast_mutex_unlock(&vms->lock);
3668  if (len > MAX_MAIL_BODY_CONTENT_SIZE) {
3670  "Msgno %ld, section %s. The body's content size %ld is huge (max %ld). User:%s, mailbox %s\n",
3671  vms->msgArray[vms->curmsg], section, len, MAX_MAIL_BODY_CONTENT_SIZE, vms->imapuser, vms->username);
3672  return -1;
3673  }
3674  if (body_content != NIL && len) {
3675  snprintf(filename, sizeof(filename), "%s.%s", fn, format);
3676  /* ast_debug(1, body_content); */
3677  body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
3678  /* If the body of the file is empty, return an error */
3679  if (!newlen || !body_decoded) {
3680  return -1;
3681  }
3682  write_file(filename, (char *) body_decoded, newlen);
3683  } else {
3684  ast_debug(5, "Body of message is NULL.\n");
3685  return -1;
3686  }
3687  return 0;
3688 }
3689 
3690 /*!
3691  * \brief Get delimiter via mm_list callback
3692  * \param vms The voicemail state object
3693  * \param stream
3694  *
3695  * Determines the delimiter character that is used by the underlying IMAP based mail store.
3696  */
3697 /* MUTEX should already be held */
3698 static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream) {
3699  char tmp[50];
3700  snprintf(tmp, sizeof(tmp), "{%s}", S_OR(vms->imapserver, imapserver));
3701  mail_list(stream, tmp, "*");
3702 }
3703 
3704 /*!
3705  * \brief Check Quota for user
3706  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
3707  * \param mailbox the mailbox to check the quota for.
3708  *
3709  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
3710  */
3711 static void check_quota(struct vm_state *vms, char *mailbox) {
3712  ast_mutex_lock(&vms->lock);
3713  mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
3714  ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
3715  if (vms && vms->mailstream != NULL) {
3716  imap_getquotaroot(vms->mailstream, mailbox);
3717  } else {
3718  ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
3719  }
3720  ast_mutex_unlock(&vms->lock);
3721 }
3722 
3723 #endif /* IMAP_STORAGE */
3724 
3725 /*! \brief Lock file path
3726  * only return failure if ast_lock_path returns 'timeout',
3727  * not if the path does not exist or any other reason
3728  */
3729 static int vm_lock_path(const char *path)
3730 {
3731  switch (ast_lock_path(path)) {
3732  case AST_LOCK_TIMEOUT:
3733  return -1;
3734  default:
3735  return 0;
3736  }
3737 }
3738 
3739 #define MSG_ID_LEN 256
3740 
3741 /* Used to attach a unique identifier to an msg_id */
3743 
3744 /*!
3745  * \brief Sets the destination string to a uniquely identifying msg_id string
3746  * \param dst pointer to a character buffer that should contain MSG_ID_LEN characters.
3747  */
3748 static void generate_msg_id(char *dst);
3749 
3750 #ifdef ODBC_STORAGE
3751 struct generic_prepare_struct {
3752  char *sql;
3753  int argc;
3754  char **argv;
3755 };
3756 
3757 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
3758 {
3759  struct generic_prepare_struct *gps = data;
3760  int res, i;
3761  SQLHSTMT stmt;
3762 
3763  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
3764  if (!SQL_SUCCEEDED(res)) {
3765  ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
3766  return NULL;
3767  }
3768  res = ast_odbc_prepare(obj, stmt, gps->sql);
3769  if (!SQL_SUCCEEDED(res)) {
3770  ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
3771  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3772  return NULL;
3773  }
3774  for (i = 0; i < gps->argc; i++)
3775  SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
3776 
3777  return stmt;
3778 }
3779 
3780 static void odbc_update_msg_id(char *dir, int msg_num, char *msg_id)
3781 {
3782  SQLHSTMT stmt;
3783  char sql[PATH_MAX];
3784  struct odbc_obj *obj;
3785  char msg_num_str[20];
3786  char *argv[] = { msg_id, dir, msg_num_str };
3787  struct generic_prepare_struct gps = { .sql = sql, .argc = 3, .argv = argv };
3788 
3789  obj = ast_odbc_request_obj(odbc_database, 0);
3790  if (!obj) {
3791  ast_log(LOG_WARNING, "Unable to update message ID for message %d in %s\n", msg_num, dir);
3792  return;
3793  }
3794 
3795  snprintf(msg_num_str, sizeof(msg_num_str), "%d", msg_num);
3796  snprintf(sql, sizeof(sql), "UPDATE %s SET msg_id=? WHERE dir=? AND msgnum=?", odbc_table);
3797  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3798  if (!stmt) {
3799  ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3800  } else {
3801  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3802  }
3803  ast_odbc_release_obj(obj);
3804  return;
3805 }
3806 
3807 /*!
3808  * \brief Retrieves a file from an ODBC data store.
3809  * \param dir the path to the file to be retrieved.
3810  * \param msgnum the message number, such as within a mailbox folder.
3811  *
3812  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
3813  * 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.
3814  *
3815  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
3816  * The output is the message information file with the name msgnum and the extension .txt
3817  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
3818  *
3819  * \return 0 on success, -1 on error.
3820  */
3821 static int retrieve_file(char *dir, int msgnum)
3822 {
3823  int x = 0;
3824  int res;
3825  int fd = -1;
3826  size_t fdlen = 0;
3827  void *fdm = MAP_FAILED;
3828  SQLSMALLINT colcount = 0;
3829  SQLHSTMT stmt;
3830  char sql[PATH_MAX];
3831  char fmt[80] = "";
3832  char *c;
3833  char coltitle[256];
3834  SQLSMALLINT collen;
3835  SQLSMALLINT datatype;
3836  SQLSMALLINT decimaldigits;
3837  SQLSMALLINT nullable;
3838  SQLULEN colsize;
3839  SQLLEN colsize2;
3840  FILE *f = NULL;
3841  char rowdata[80];
3842  char fn[PATH_MAX];
3843  char full_fn[PATH_MAX];
3844  char msgnums[80];
3845  char msg_id[MSG_ID_LEN] = "";
3846  char *argv[] = { dir, msgnums };
3847  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3848  struct odbc_obj *obj;
3849 
3850  obj = ast_odbc_request_obj(odbc_database, 0);
3851  if (!obj) {
3852  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3853  return -1;
3854  }
3855 
3856  ast_copy_string(fmt, vmfmts, sizeof(fmt));
3857  c = strchr(fmt, '|');
3858  if (c)
3859  *c = '\0';
3860  if (!strcasecmp(fmt, "wav49"))
3861  strcpy(fmt, "WAV");
3862 
3863  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3864  if (msgnum > -1)
3865  make_file(fn, sizeof(fn), dir, msgnum);
3866  else
3867  ast_copy_string(fn, dir, sizeof(fn));
3868 
3869  /* Create the information file */
3870  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3871 
3872  if (!(f = fopen(full_fn, "w+"))) {
3873  ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
3874  goto bail;
3875  }
3876 
3877  snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
3878  snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
3879 
3880  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3881  if (!stmt) {
3882  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3883  goto bail;
3884  }
3885 
3886  res = SQLFetch(stmt);
3887  if (!SQL_SUCCEEDED(res)) {
3888  if (res != SQL_NO_DATA) {
3889  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3890  }
3891  goto bail_with_handle;
3892  }
3893 
3894  fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
3895  if (fd < 0) {
3896  ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
3897  goto bail_with_handle;
3898  }
3899 
3900  res = SQLNumResultCols(stmt, &colcount);
3901  if (!SQL_SUCCEEDED(res)) {
3902  ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
3903  goto bail_with_handle;
3904  }
3905 
3906  fprintf(f, "[message]\n");
3907  for (x = 0; x < colcount; x++) {
3908  rowdata[0] = '\0';
3909  colsize = 0;
3910  collen = sizeof(coltitle);
3911  res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen,
3912  &datatype, &colsize, &decimaldigits, &nullable);
3913  if (!SQL_SUCCEEDED(res)) {
3914  ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
3915  goto bail_with_handle;
3916  }
3917  if (!strcasecmp(coltitle, "recording")) {
3918  off_t offset;
3919  res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
3920  fdlen = colsize2;
3921  if (fd > -1) {
3922  char tmp[1] = "";
3923  lseek(fd, fdlen - 1, SEEK_SET);
3924  if (write(fd, tmp, 1) != 1) {
3925  close(fd);
3926  fd = -1;
3927  continue;
3928  }
3929  /* Read out in small chunks */
3930  for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
3931  if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
3932  ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
3933  goto bail_with_handle;
3934  }
3935  res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
3936  munmap(fdm, CHUNKSIZE);
3937  if (!SQL_SUCCEEDED(res)) {
3938  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3939  unlink(full_fn);
3940  goto bail_with_handle;
3941  }
3942  }
3943  if (truncate(full_fn, fdlen) < 0) {
3944  ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
3945  }
3946  }
3947  } else {
3948  res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3949  if (res == SQL_NULL_DATA && !strcasecmp(coltitle, "msg_id")) {
3950  /* Generate msg_id now, but don't store it until we're done with this
3951  connection */
3952  generate_msg_id(msg_id);
3953  snprintf(rowdata, sizeof(rowdata), "%s", msg_id);
3954  } else if (res == SQL_NULL_DATA && !strcasecmp(coltitle, "category")) {
3955  /* Ignore null column value for category */
3956  ast_debug(3, "Ignoring null category column in ODBC voicemail retrieve_file.\n");
3957  continue;
3958  } else if (!SQL_SUCCEEDED(res)) {
3959  ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
3960  goto bail_with_handle;
3961  }
3962  if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir")) {
3963  fprintf(f, "%s=%s\n", coltitle, rowdata);
3964  }
3965  }
3966  }
3967 
3968 bail_with_handle:
3969  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3970 
3971 bail:
3972  if (f)
3973  fclose(f);
3974  if (fd > -1)
3975  close(fd);
3976 
3977  ast_odbc_release_obj(obj);
3978 
3979  /* If res_odbc is configured to only allow a single database connection, we
3980  will deadlock if we try to do this before releasing the connection we
3981  were just using. */
3982  if (!ast_strlen_zero(msg_id)) {
3983  odbc_update_msg_id(dir, msgnum, msg_id);
3984  }
3985 
3986  return x - 1;
3987 }
3988 
3989 /*!
3990  * \brief Determines the highest message number in use for a given user and mailbox folder.
3991  * \param vmu
3992  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
3993  *
3994  * This method is used when mailboxes are stored in an ODBC back end.
3995  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
3996  *
3997  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
3998 
3999  */
4000 static int last_message_index(struct ast_vm_user *vmu, char *dir)
4001 {
4002  int x = -1;
4003  int res;
4004  SQLHSTMT stmt;
4005  char sql[PATH_MAX];
4006  char rowdata[20];
4007  char *argv[] = { dir };
4008  struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
4009  struct odbc_obj *obj;
4010 
4011  obj = ast_odbc_request_obj(odbc_database, 0);
4012  if (!obj) {
4013  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4014  return -1;
4015  }
4016 
4017  snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
4018 
4019  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4020  if (!stmt) {
4021  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4022  goto bail;
4023  }
4024 
4025  res = SQLFetch(stmt);
4026  if (!SQL_SUCCEEDED(res)) {
4027  if (res == SQL_NO_DATA) {
4028  ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
4029  } else {
4030  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4031  }
4032  goto bail_with_handle;
4033  }
4034 
4035  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4036  if (!SQL_SUCCEEDED(res)) {
4037  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4038  goto bail_with_handle;
4039  }
4040 
4041  if (sscanf(rowdata, "%30d", &x) != 1) {
4042  ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
4043  }
4044 
4045 bail_with_handle:
4046  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4047 
4048 bail:
4049  ast_odbc_release_obj(obj);
4050 
4051  return x;
4052 }
4053 
4054 /*!
4055  * \brief Determines if the specified message exists.
4056  * \param dir the folder the mailbox folder to look for messages.
4057  * \param msgnum the message index to query for.
4058  *
4059  * This method is used when mailboxes are stored in an ODBC back end.
4060  *
4061  * \return greater than zero if the message exists, zero when the message does not exist or on error.
4062  */
4063 static int message_exists(char *dir, int msgnum)
4064 {
4065  int x = 0;
4066  int res;
4067  SQLHSTMT stmt;
4068  char sql[PATH_MAX];
4069  char rowdata[20];
4070  char msgnums[20];
4071  char *argv[] = { dir, msgnums };
4072  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4073  struct odbc_obj *obj;
4074 
4075  obj = ast_odbc_request_obj(odbc_database, 0);
4076  if (!obj) {
4077  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4078  return 0;
4079  }
4080 
4081  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4082  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
4083  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4084  if (!stmt) {
4085  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4086  goto bail;
4087  }
4088 
4089  res = SQLFetch(stmt);
4090  if (!SQL_SUCCEEDED(res)) {
4091  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4092  goto bail_with_handle;
4093  }
4094 
4095  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4096  if (!SQL_SUCCEEDED(res)) {
4097  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4098  goto bail_with_handle;
4099  }
4100 
4101  if (sscanf(rowdata, "%30d", &x) != 1) {
4102  ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
4103  }
4104 
4105 bail_with_handle:
4106  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4107 
4108 bail:
4109  ast_odbc_release_obj(obj);
4110  return x;
4111 }
4112 
4113 /*!
4114  * \brief returns the number of messages found.
4115  * \param vmu
4116  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
4117  *
4118  * This method is used when mailboxes are stored in an ODBC back end.
4119  *
4120  * \return The count of messages being zero or more, less than zero on error.
4121  */
4122 static int count_messages(struct ast_vm_user *vmu, char *dir)
4123 {
4124  int x = -1;
4125  int res;
4126  SQLHSTMT stmt;
4127  char sql[PATH_MAX];
4128  char rowdata[20];
4129  char *argv[] = { dir };
4130  struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
4131  struct odbc_obj *obj;
4132 
4133  obj = ast_odbc_request_obj(odbc_database, 0);
4134  if (!obj) {
4135  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4136  return -1;
4137  }
4138 
4139  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
4140  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4141  if (!stmt) {
4142  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4143  goto bail;
4144  }
4145 
4146  res = SQLFetch(stmt);
4147  if (!SQL_SUCCEEDED(res)) {
4148  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4149  goto bail_with_handle;
4150  }
4151 
4152  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4153  if (!SQL_SUCCEEDED(res)) {
4154  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4155  goto bail_with_handle;
4156  }
4157 
4158  if (sscanf(rowdata, "%30d", &x) != 1) {
4159  ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
4160  }
4161 
4162 bail_with_handle:
4163  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4164 
4165 bail:
4166  ast_odbc_release_obj(obj);
4167  return x;
4168 }
4169 
4170 /*!
4171  * \brief Deletes a message from the mailbox folder.
4172  * \param sdir The mailbox folder to work in.
4173  * \param smsg The message index to be deleted.
4174  *
4175  * This method is used when mailboxes are stored in an ODBC back end.
4176  * The specified message is directly deleted from the database 'voicemessages' table.
4177  *
4178  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
4179  */
4180 static void delete_file(const char *sdir, int smsg)
4181 {
4182  SQLHSTMT stmt;
4183  char sql[PATH_MAX];
4184  char msgnums[20];
4185  char *argv[] = { NULL, msgnums };
4186  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4187  struct odbc_obj *obj;
4188 
4189  obj = ast_odbc_request_obj(odbc_database, 0);
4190  if (!obj) {
4191  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4192  return;
4193  }
4194 
4195  argv[0] = ast_strdupa(sdir);
4196 
4197  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4198  snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
4199  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4200  if (!stmt) {
4201  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4202  } else {
4203  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4204  }
4205  ast_odbc_release_obj(obj);
4206 
4207  return;
4208 }
4209 
4210 /*!
4211  * \brief Copies a voicemail from one mailbox to another.
4212  * \param sdir the folder for which to look for the message to be copied.
4213  * \param smsg the index of the message to be copied.
4214  * \param ddir the destination folder to copy the message into.
4215  * \param dmsg the index to be used for the copied message.
4216  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
4217  * \param dmailboxcontext The context for the destination user.
4218  *
4219  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
4220  */
4221 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
4222 {
4223  SQLHSTMT stmt;
4224  char sql[512];
4225  char msgnums[20];
4226  char msgnumd[20];
4227  char msg_id[MSG_ID_LEN];
4228  struct odbc_obj *obj;
4229  char *argv[] = { ddir, msgnumd, msg_id, dmailboxuser, dmailboxcontext, sdir, msgnums };
4230  struct generic_prepare_struct gps = { .sql = sql, .argc = 7, .argv = argv };
4231 
4232  generate_msg_id(msg_id);
4233  delete_file(ddir, dmsg);
4234  obj = ast_odbc_request_obj(odbc_database, 0);
4235  if (!obj) {
4236  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4237  return;
4238  }
4239 
4240  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4241  snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
4242  snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, msg_id, 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);
4243  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4244  if (!stmt)
4245  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
4246  else
4247  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4248  ast_odbc_release_obj(obj);
4249 
4250  return;
4251 }
4252 
4253 struct insert_data {
4254  char *sql;
4255  const char *dir;
4256  const char *msgnums;
4257  void *data;
4258  SQLLEN datalen;
4259  SQLLEN indlen;
4260  const char *context;
4261  const char *macrocontext;
4262  const char *callerid;
4263  const char *origtime;
4264  const char *duration;
4265  const char *mailboxuser;
4266  const char *mailboxcontext;
4267  const char *category;
4268  const char *flag;
4269  const char *msg_id;
4270 };
4271 
4272 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
4273 {
4274  struct insert_data *data = vdata;
4275  int res;
4276  SQLHSTMT stmt;
4277 
4278  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4279  if (!SQL_SUCCEEDED(res)) {
4280  ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
4281  return NULL;
4282  }
4283 
4284  SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
4285  SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
4286  SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
4287  SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
4288  SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
4289  SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
4290  SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
4291  SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
4292  SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
4293  SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
4294  SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
4295  SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msg_id), 0, (void *) data->msg_id, 0, NULL);
4296  if (!ast_strlen_zero(data->category)) {
4297  SQLBindParameter(stmt, 13, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
4298  }
4299  res = ast_odbc_execute_sql(obj, stmt, data->sql);
4300  if (!SQL_SUCCEEDED(res)) {
4301  ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
4302  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4303  return NULL;
4304  }
4305 
4306  return stmt;
4307 }
4308 
4309 /*!
4310  * \brief Stores a voicemail into the database.
4311  * \param dir the folder the mailbox folder to store the message.
4312  * \param mailboxuser the user owning the mailbox folder.
4313  * \param mailboxcontext
4314  * \param msgnum the message index for the message to be stored.
4315  *
4316  * This method is used when mailboxes are stored in an ODBC back end.
4317  * The message sound file and information file is looked up on the file system.
4318  * A SQL query is invoked to store the message into the (MySQL) database.
4319  *
4320  * \return the zero on success -1 on error.
4321  */
4322 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
4323 {
4324  int res = 0;
4325  int fd = -1;
4326  void *fdm = MAP_FAILED;
4327  off_t fdlen = -1;
4328  SQLHSTMT stmt;
4329  char sql[PATH_MAX];
4330  char msgnums[20];
4331  char fn[PATH_MAX];
4332  char full_fn[PATH_MAX];
4333  char fmt[80]="";
4334  char *c;
4335  struct ast_config *cfg = NULL;
4336  struct odbc_obj *obj;
4337  struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
4338  .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "", .msg_id = "" };
4339  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
4340 
4341  delete_file(dir, msgnum);
4342 
4343  obj = ast_odbc_request_obj(odbc_database, 0);
4344  if (!obj) {
4345  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4346  return -1;
4347  }
4348 
4349  do {
4350  ast_copy_string(fmt, vmfmts, sizeof(fmt));
4351  c = strchr(fmt, '|');
4352  if (c)
4353  *c = '\0';
4354  if (!strcasecmp(fmt, "wav49"))
4355  strcpy(fmt, "WAV");
4356  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4357  if (msgnum > -1)
4358  make_file(fn, sizeof(fn), dir, msgnum);
4359  else
4360  ast_copy_string(fn, dir, sizeof(fn));
4361  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
4362  cfg = ast_config_load(full_fn, config_flags);
4363  snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
4364  fd = open(full_fn, O_RDWR);
4365  if (fd < 0) {
4366  ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
4367  res = -1;
4368  break;
4369  }
4370  if (valid_config(cfg)) {
4371  if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
4372  idata.context = "";
4373  }
4374  if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
4375  idata.macrocontext = "";
4376  }
4377  if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
4378  idata.callerid = "";
4379  }
4380  if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
4381  idata.origtime = "";
4382  }
4383  if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
4384  idata.duration = "";
4385  }
4386  if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
4387  idata.category = "";
4388  }
4389  if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
4390  idata.flag = "";
4391  }
4392  if (!(idata.msg_id = ast_variable_retrieve(cfg, "message", "msg_id"))) {
4393  idata.msg_id = "";
4394  }
4395  }
4396  fdlen = lseek(fd, 0, SEEK_END);
4397  if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
4398  ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
4399  res = -1;
4400  break;
4401  }
4402  fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
4403  if (fdm == MAP_FAILED) {
4404  ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
4405  res = -1;
4406  break;
4407  }
4408  idata.data = fdm;
4409  idata.datalen = idata.indlen = fdlen;
4410 
4411  if (!ast_strlen_zero(idata.category))
4412  snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
4413  else
4414  snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,msg_id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
4415 
4416  if (ast_strlen_zero(idata.origtime)) {
4417  idata.origtime = "0";
4418  }
4419 
4420  if (ast_strlen_zero(idata.duration)) {
4421  idata.duration = "0";
4422  }
4423 
4424  if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
4425  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4426  } else {
4427  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4428  res = -1;
4429  }
4430  } while (0);
4431 
4432  ast_odbc_release_obj(obj);
4433 
4434  if (valid_config(cfg))
4435  ast_config_destroy(cfg);
4436  if (fdm != MAP_FAILED)
4437  munmap(fdm, fdlen);
4438  if (fd > -1)
4439  close(fd);
4440  return res;
4441 }
4442 
4443 /*!
4444  * \brief Renames a message in a mailbox folder.
4445  * \param sdir The folder of the message to be renamed.
4446  * \param smsg The index of the message to be renamed.
4447  * \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.
4448  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
4449  * \param ddir The destination folder for the message to be renamed into
4450  * \param dmsg The destination message for the message to be renamed.
4451  *
4452  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
4453  * 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.
4454  * 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.
4455  */
4456 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
4457 {
4458  SQLHSTMT stmt;
4459  char sql[PATH_MAX];
4460  char msgnums[20];
4461  char msgnumd[20];
4462  struct odbc_obj *obj;
4463  char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
4464  struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
4465 
4466  delete_file(ddir, dmsg);
4467 
4468  obj = ast_odbc_request_obj(odbc_database, 0);
4469  if (!obj) {
4470  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4471  return;
4472  }
4473 
4474  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4475  snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
4476  snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
4477  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4478  if (!stmt)
4479  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4480  else
4481  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4482  ast_odbc_release_obj(obj);
4483  return;
4484 }
4485 
4486 /*!
4487  * \brief Removes a voicemail message file.
4488  * \param dir the path to the message file.
4489  * \param msgnum the unique number for the message within the mailbox.
4490  *
4491  * Removes the message content file and the information file.
4492  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
4493  * Typical use is to clean up after a RETRIEVE operation.
4494  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
4495  * \return zero on success, -1 on error.
4496  */
4497 static int remove_file(char *dir, int msgnum)
4498 {
4499  char fn[PATH_MAX];
4500  char full_fn[PATH_MAX];
4501  char msgnums[80];
4502 
4503  if (msgnum > -1) {
4504  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4505  make_file(fn, sizeof(fn), dir, msgnum);
4506  } else
4507  ast_copy_string(fn, dir, sizeof(fn));
4508  ast_filedelete(fn, NULL);
4509  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
4510  unlink(full_fn);
4511  return 0;
4512 }
4513 #else
4514 #ifndef IMAP_STORAGE
4515 /*!
4516  * \brief Find all .txt files - even if they are not in sequence from 0000.
4517  * \param vmu
4518  * \param dir
4519  *
4520  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
4521  *
4522  * \return the count of messages, zero or more.
4523  */
4524 static int count_messages(struct ast_vm_user *vmu, char *dir)
4525 {
4526 
4527  int vmcount = 0;
4528  DIR *vmdir = NULL;
4529  struct dirent *vment = NULL;
4530 
4531  if (vm_lock_path(dir))
4532  return ERROR_LOCK_PATH;
4533 
4534  if ((vmdir = opendir(dir))) {
4535  while ((vment = readdir(vmdir))) {
4536  if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
4537  vmcount++;
4538  }
4539  }
4540  closedir(vmdir);
4541  }
4542  ast_unlock_path(dir);
4543 
4544  return vmcount;
4545 }
4546 
4547 /*!
4548  * \brief Renames a message in a mailbox folder.
4549  * \param sfn The path to the mailbox information and data file to be renamed.
4550  * \param dfn The path for where the message data and information files will be renamed to.
4551  *
4552  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
4553  */
4554 static void rename_file(char *sfn, char *dfn)
4555 {
4556  char stxt[PATH_MAX];
4557  char dtxt[PATH_MAX];
4558  ast_filerename(sfn, dfn, NULL);
4559  snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
4560  snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
4561  if (ast_check_realtime("voicemail_data")) {
4562  ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
4563  }
4564  rename(stxt, dtxt);
4565 }
4566 
4567 /*!
4568  * \brief Determines the highest message number in use for a given user and mailbox folder.
4569  * \param vmu
4570  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
4571  *
4572  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
4573  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
4574  *
4575  * \note Should always be called with a lock already set on dir.
4576  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
4577  */
4578 static int last_message_index(struct ast_vm_user *vmu, char *dir)
4579 {
4580  int x;
4581  unsigned char map[MAXMSGLIMIT] = "";
4582  DIR *msgdir;
4583  struct dirent *msgdirent;
4584  int msgdirint;
4585  char extension[4];
4586  int stopcount = 0;
4587 
4588  /* Reading the entire directory into a file map scales better than
4589  * doing a stat repeatedly on a predicted sequence. I suspect this
4590  * is partially due to stat(2) internally doing a readdir(2) itself to
4591  * find each file. */
4592  if (!(msgdir = opendir(dir))) {
4593  return -1;
4594  }
4595 
4596  while ((msgdirent = readdir(msgdir))) {
4597  if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
4598  map[msgdirint] = 1;
4599  stopcount++;
4600  ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
4601  }
4602  }
4603  closedir(msgdir);
4604 
4605  for (x = 0; x < vmu->maxmsg; x++) {
4606  if (map[x] == 1) {
4607  stopcount--;
4608  } else if (map[x] == 0 && !stopcount) {
4609  break;
4610  }
4611  }
4612 
4613  return x - 1;
4614 }
4615 
4616 #endif /* #ifndef IMAP_STORAGE */
4617 #endif /* #else of #ifdef ODBC_STORAGE */
4618 #ifndef IMAP_STORAGE
4619 /*!
4620  * \brief Utility function to copy a file.
4621  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
4622  * \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.
4623  *
4624  * 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.
4625  * The copy operation copies up to 4096 bytes at once.
4626  *
4627  * \return zero on success, -1 on error.
4628  */
4629 static int copy(char *infile, char *outfile)
4630 {
4631  int ifd;
4632  int ofd;
4633  int res = -1;
4634  int len;
4635  char buf[4096];
4636 
4637 #ifdef HARDLINK_WHEN_POSSIBLE
4638  /* Hard link if possible; saves disk space & is faster */
4639  if (!link(infile, outfile)) {
4640  return 0;
4641  }
4642 #endif
4643 
4644  if ((ifd = open(infile, O_RDONLY)) < 0) {
4645  ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
4646  return -1;
4647  }
4648 
4649  if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
4650  ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
4651  close(ifd);
4652  return -1;
4653  }
4654 
4655  for (;;) {
4656  int wrlen;
4657 
4658  len = read(ifd, buf, sizeof(buf));
4659  if (!len) {
4660  res = 0;
4661  break;
4662  }
4663 
4664  if (len < 0) {
4665  ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
4666  break;
4667  }
4668 
4669  wrlen = write(ofd, buf, len);
4670  if (errno == ENOMEM || errno == ENOSPC || wrlen != len) {
4671  ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, wrlen, len, strerror(errno));
4672  break;
4673  }
4674  }
4675 
4676  close(ifd);
4677  close(ofd);
4678  if (res) {
4679  unlink(outfile);
4680  }
4681 
4682  return res;
4683 }
4684 
4685 /*!
4686  * \brief Copies a voicemail information (envelope) file.
4687  * \param frompath
4688  * \param topath
4689  *
4690  * Every voicemail has the data (.wav) file, and the information file.
4691  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
4692  * This is used by the COPY macro when not using IMAP storage.
4693  */
4694 static void copy_plain_file(char *frompath, char *topath)
4695 {
4696  char frompath2[PATH_MAX], topath2[PATH_MAX];
4697  struct ast_variable *tmp, *var = NULL;
4698  const char *origmailbox = "", *context = "", *macrocontext = "", *exten = "";
4699  const char *priority = "", *callerchan = "", *callerid = "", *origdate = "";
4700  const char *origtime = "", *category = "", *duration = "";
4701 
4702  ast_filecopy(frompath, topath, NULL);
4703  snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
4704  snprintf(topath2, sizeof(topath2), "%s.txt", topath);
4705 
4706  if (ast_check_realtime("voicemail_data")) {
4707  var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
4708  /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
4709  for (tmp = var; tmp; tmp = tmp->next) {
4710  if (!strcasecmp(tmp->name, "origmailbox")) {
4711  origmailbox = tmp->value;
4712  } else if (!strcasecmp(tmp->name, "context")) {
4713  context = tmp->value;
4714  } else if (!strcasecmp(tmp->name, "macrocontext")) {
4715  macrocontext = tmp->value;
4716  } else if (!strcasecmp(tmp->name, "exten")) {
4717  exten = tmp->value;
4718  } else if (!strcasecmp(tmp->name, "priority")) {
4719  priority = tmp->value;
4720  } else if (!strcasecmp(tmp->name, "callerchan")) {
4721  callerchan = tmp->value;
4722  } else if (!strcasecmp(tmp->name, "callerid")) {
4723  callerid = tmp->value;
4724  } else if (!strcasecmp(tmp->name, "origdate")) {
4725  origdate = tmp->value;
4726  } else if (!strcasecmp(tmp->name, "origtime")) {
4727  origtime = tmp->value;
4728  } else if (!strcasecmp(tmp->name, "category")) {
4729  category = tmp->value;
4730  } else if (!strcasecmp(tmp->name, "duration")) {
4731  duration = tmp->value;
4732  }
4733  }
4734  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);
4735  }
4736  copy(frompath2, topath2);
4737  ast_variables_destroy(var);
4738 }
4739 #endif
4740 
4741 /*!
4742  * \brief Removes the voicemail sound and information file.
4743  * \param file The path to the sound file. This will be the folder and message index, without the extension.
4744  *
4745  * This is used by the DELETE macro when voicemails are stored on the file system.
4746  *
4747  * \return zero on success, -1 on error.
4748  */
4749 static int vm_delete(char *file)
4750 {
4751  char *txt;
4752  int txtsize = 0;
4753 
4754  txtsize = (strlen(file) + 5)*sizeof(char);
4755  txt = ast_alloca(txtsize);
4756  /* Sprintf here would safe because we alloca'd exactly the right length,
4757  * but trying to eliminate all sprintf's anyhow
4758  */
4759  if (ast_check_realtime("voicemail_data")) {
4760  ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
4761  }
4762  snprintf(txt, txtsize, "%s.txt", file);
4763  unlink(txt);
4764  return ast_filedelete(file, NULL);
4765 }
4766 
4767 /*!
4768  * \brief utility used by inchar(), for base_encode()
4769  */
4770 static int inbuf(struct baseio *bio, FILE *fi)
4771 {
4772  int l;
4773 
4774  if (bio->ateof)
4775  return 0;
4776 
4777  if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) != BASEMAXINLINE) {
4778  bio->ateof = 1;
4779  if (l == 0) {
4780  /* Assume EOF */
4781  return 0;
4782  }
4783  }
4784 
4785  bio->iolen = l;
4786  bio->iocp = 0;
4787 
4788  return 1;
4789 }
4790 
4791 /*!
4792  * \brief utility used by base_encode()
4793  */
4794 static int inchar(struct baseio *bio, FILE *fi)
4795 {
4796  if (bio->iocp>=bio->iolen) {
4797  if (!inbuf(bio, fi))
4798  return EOF;
4799  }
4800 
4801  return bio->iobuf[bio->iocp++];
4802 }
4803 
4804 /*!
4805  * \brief utility used by base_encode()
4806  */
4807 static int ochar(struct baseio *bio, int c, FILE *so)
4808 {
4809  if (bio->linelength >= BASELINELEN) {
4810  if (fputs(ENDL, so) == EOF) {
4811  return -1;
4812  }
4813 
4814  bio->linelength = 0;
4815  }
4816 
4817  if (putc(((unsigned char) c), so) == EOF) {
4818  return -1;
4819  }
4820 
4821  bio->linelength++;
4822 
4823  return 1;
4824 }
4825 
4826 /*!
4827  * \brief Performs a base 64 encode algorithm on the contents of a File
4828  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
4829  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
4830  *
4831  * 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 ?
4832  *
4833  * \return zero on success, -1 on error.
4834  */
4835 static int base_encode(char *filename, FILE *so)
4836 {
4837  static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
4838  'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
4839  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
4840  '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
4841  int i, hiteof = 0;
4842  FILE *fi;
4843  struct baseio bio;
4844 
4845  memset(&bio, 0, sizeof(bio));
4846  bio.iocp = BASEMAXINLINE;
4847 
4848  if (!(fi = fopen(filename, "rb"))) {
4849  ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
4850  return -1;
4851  }
4852 
4853  while (!hiteof){
4854  unsigned char igroup[3], ogroup[4];
4855  int c, n;
4856 
4857  memset(igroup, 0, sizeof(igroup));
4858 
4859  for (n = 0; n < 3; n++) {
4860  if ((c = inchar(&bio, fi)) == EOF) {
4861  hiteof = 1;
4862  break;
4863  }
4864 
4865  igroup[n] = (unsigned char) c;
4866  }
4867 
4868  if (n > 0) {
4869  ogroup[0]= dtable[igroup[0] >> 2];
4870  ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
4871  ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
4872  ogroup[3]= dtable[igroup[2] & 0x3F];
4873 
4874  if (n < 3) {
4875  ogroup[3] = '=';
4876 
4877  if (n < 2)
4878  ogroup[2] = '=';
4879  }
4880 
4881  for (i = 0; i < 4; i++)
4882  ochar(&bio, ogroup[i], so);
4883  }
4884  }
4885 
4886  fclose(fi);
4887 
4888  if (fputs(ENDL, so) == EOF) {
4889  return 0;
4890  }
4891 
4892  return 1;
4893 }
4894 
4895 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, const char *category, const char *flag)
4896 {
4897  char callerid[256];
4898  char num[12];
4899  char fromdir[256], fromfile[256];
4900  struct ast_config *msg_cfg;
4901  const char *origcallerid, *origtime;
4902  char origcidname[80], origcidnum[80], origdate[80];
4903  int inttime;
4904  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
4905 
4906  /* Prepare variables for substitution in email body and subject */
4907  pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
4908  pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
4909  snprintf(num, sizeof(num), "%d", msgnum);
4910  pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
4911  pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
4912  pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
4913  pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
4914  ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
4915  pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
4916  pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
4917  pbx_builtin_setvar_helper(ast, "VM_DATE", date);
4918  pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
4919  pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
4920 
4921  /* Retrieve info from VM attribute file */
4922  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
4923  make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
4924  if (strlen(fromfile) < sizeof(fromfile) - 5) {
4925  strcat(fromfile, ".txt");
4926  }
4927  if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
4928  ast_debug(1, "Config load for message text file '%s' failed\n", fromfile);
4929  return;
4930  }
4931 
4932  if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
4933  pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
4934  ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
4935  pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
4936  pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
4937  }
4938 
4939  if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
4940  struct timeval tv = { inttime, };
4941  struct ast_tm tm;
4942  ast_localtime(&tv, &tm, NULL);
4943  ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
4944  pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
4945  }
4946  ast_config_destroy(msg_cfg);
4947 }
4948 
4949 /*!
4950  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
4951  * \param from The string to work with.
4952  * \param buf The buffer into which to write the modified quoted string.
4953  * \param maxlen Always zero, but see \see ast_str
4954  *
4955  * \return The destination string with quotes wrapped on it (the to field).
4956  */
4957 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
4958 {
4959  const char *ptr;
4960 
4961  /* We're only ever passing 0 to maxlen, so short output isn't possible */
4962  ast_str_set(buf, maxlen, "\"");
4963  for (ptr = from; *ptr; ptr++) {
4964  if (*ptr == '"' || *ptr == '\\') {
4965  ast_str_append(buf, maxlen, "\\%c", *ptr);
4966  } else {
4967  ast_str_append(buf, maxlen, "%c", *ptr);
4968  }
4969  }
4970  ast_str_append(buf, maxlen, "\"");
4971 
4972  return ast_str_buffer(*buf);
4973 }
4974 
4975 /*! \brief
4976  * fill in *tm for current time according to the proper timezone, if any.
4977  * \return tm so it can be used as a function argument.
4978  */
4979 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
4980 {
4981  const struct vm_zone *z = NULL;
4982  struct timeval t = ast_tvnow();
4983 
4984  /* Does this user have a timezone specified? */
4985  if (!ast_strlen_zero(vmu->zonetag)) {
4986  /* Find the zone in the list */
4987  AST_LIST_LOCK(&zones);
4988  AST_LIST_TRAVERSE(&zones, z, list) {
4989  if (!strcmp(z->name, vmu->zonetag))
4990  break;
4991  }
4993  }
4994  ast_localtime(&t, tm, z ? z->timezone : NULL);
4995  return tm;
4996 }
4997 
4998 /*!\brief Check if the string would need encoding within the MIME standard, to
4999  * avoid confusing certain mail software that expects messages to be 7-bit
5000  * clean.
5001  */
5002 static int check_mime(const char *str)
5003 {
5004  for (; *str; str++) {
5005  if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
5006  return 1;
5007  }
5008  }
5009  return 0;
5010 }
5011 
5012 /*!\brief Encode a string according to the MIME rules for encoding strings
5013  * that are not 7-bit clean or contain control characters.
5014  *
5015  * Additionally, if the encoded string would exceed the MIME limit of 76
5016  * characters per line, then the encoding will be broken up into multiple
5017  * sections, separated by a space character, in order to facilitate
5018  * breaking up the associated header across multiple lines.
5019  *
5020  * \param end An expandable buffer for holding the result
5021  * \param maxlen Always zero, but see \see ast_str
5022  * \param start A string to be encoded
5023  * \param preamble The length of the first line already used for this string,
5024  * to ensure that each line maintains a maximum length of 76 chars.
5025  * \param postamble the length of any additional characters appended to the
5026  * line, used to ensure proper field wrapping.
5027  * \retval The encoded string.
5028  */
5029 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
5030 {
5031  struct ast_str *tmp = ast_str_alloca(80);
5032  int first_section = 1;
5033 
5034  ast_str_reset(*end);
5035  ast_str_set(&tmp, -1, "=?%s?Q?", charset);
5036  for (; *start; start++) {
5037  int need_encoding = 0;
5038  if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
5039  need_encoding = 1;
5040  }
5041  if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
5042  (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
5043  (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
5044  (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
5045  /* Start new line */
5046  ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
5047  ast_str_set(&tmp, -1, "=?%s?Q?", charset);
5048  first_section = 0;
5049  }
5050  if (need_encoding && *start == ' ') {
5051  ast_str_append(&tmp, -1, "_");
5052  } else if (need_encoding) {
5053  ast_str_append(&tmp, -1, "=%hhX", *start);
5054  } else {
5055  ast_str_append(&tmp, -1, "%c", *start);
5056  }
5057  }
5058  ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
5059  return ast_str_buffer(*end);
5060 }
5061 
5062 /*!
5063  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
5064  * \param p The output file to generate the email contents into.
5065  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
5066  * \param vmu The voicemail user who is sending the voicemail.
5067  * \param msgnum The message index in the mailbox folder.
5068  * \param context
5069  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
5070  * \param fromfolder
5071  * \param cidnum The caller ID number.
5072  * \param cidname The caller ID name.
5073  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
5074  * \param attach2
5075  * \param format The message sound file format. i.e. .wav
5076  * \param duration The time of the message content, in seconds.
5077  * \param attach_user_voicemail if 1, the sound file is attached to the email.
5078  * \param chan
5079  * \param category
5080  * \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.
5081  * \param flag, msg_id
5082  *
5083  * 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.
5084  */
5085 static void make_email_file(FILE *p,
5086  char *srcemail,
5087  struct ast_vm_user *vmu,
5088  int msgnum,
5089  char *context,
5090  char *mailbox,
5091  const char *fromfolder,
5092  char *cidnum,
5093  char *cidname,
5094  char *attach,
5095  char *attach2,
5096  char *format,
5097  int duration,
5098  int attach_user_voicemail,
5099  struct ast_channel *chan,
5100  const char *category,
5101  int imap,
5102  const char *flag,
5103  const char *msg_id)
5104 {
5105  char date[256];
5106  char host[MAXHOSTNAMELEN] = "";
5107  char who[256];
5108  char bound[256];
5109  char dur[256];
5110  struct ast_tm tm;
5111  char enc_cidnum[256] = "", enc_cidname[256] = "";
5112  struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
5113  char *greeting_attachment;
5114  char filename[256];
5115  int first_line;
5116  char *emailsbuf;
5117  char *email;
5118 
5119  if (!str1 || !str2) {
5120  ast_free(str1);
5121  ast_free(str2);
5122  return;
5123  }
5124 
5125  if (cidnum) {
5126  strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
5127  }
5128  if (cidname) {
5129  strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
5130  }
5131  gethostname(host, sizeof(host) - 1);
5132 
5133  if (strchr(srcemail, '@')) {
5134  ast_copy_string(who, srcemail, sizeof(who));
5135  } else {
5136  snprintf(who, sizeof(who), "%s@%s", srcemail, host);
5137  }
5138 
5139  greeting_attachment = strrchr(ast_strdupa(attach), '/');
5140  if (greeting_attachment) {
5141  *greeting_attachment++ = '\0';
5142  }
5143 
5144  snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
5145  ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
5146  fprintf(p, "Date: %s" ENDL, date);
5147 
5148  /* Set date format for voicemail mail */
5149  ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5150 
5151  if (!ast_strlen_zero(fromstring) || !ast_strlen_zero(vmu->fromstring)) {
5152  struct ast_channel *ast;
5153  char *e_fromstring = !ast_strlen_zero(vmu->fromstring) ? vmu->fromstring : fromstring;
5154  if ((ast = ast_dummy_channel_alloc())) {
5155  char *ptr;
5156  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
5157  ast_str_substitute_variables(&str1, 0, ast, e_fromstring);
5158 
5159  if (check_mime(ast_str_buffer(str1))) {
5160  first_line = 1;
5161  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
5162  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5163  *ptr = '\0';
5164  fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
5165  first_line = 0;
5166  /* Substring is smaller, so this will never grow */
5167  ast_str_set(&str2, 0, "%s", ptr + 1);
5168  }
5169  fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
5170  } else {
5171  fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
5172  }
5173  ast = ast_channel_unref(ast);
5174  } else {
5175  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5176  }
5177  } else {
5178  fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
5179  }
5180 
5181  emailsbuf = ast_strdupa(vmu->email);
5182  fprintf(p, "To:");
5183  first_line = 1;
5184  while ((email = strsep(&emailsbuf, "|"))) {
5185  char *next = emailsbuf;
5186  if (check_mime(vmu->fullname)) {
5187  char *ptr;
5188  ast_str_encode_mime(&str2, 0, vmu->fullname, first_line ? strlen("To: ") : 0, strlen(email) + 3 + (next ? strlen(",") : 0));
5189  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5190  *ptr = '\0';
5191  fprintf(p, " %s" ENDL, ast_str_buffer(str2));
5192  /* Substring is smaller, so this will never grow */
5193  ast_str_set(&str2, 0, "%s", ptr + 1);
5194  }
5195  fprintf(p, " %s <%s>%s" ENDL, ast_str_buffer(str2), email, next ? "," : "");
5196  } else {
5197  fprintf(p, " %s <%s>%s" ENDL, ast_str_quote(&str2, 0, vmu->fullname), email, next ? "," : "");
5198  }
5199  first_line = 0;
5200  }
5201 
5202  if (msgnum <= -1) {
5203  fprintf(p, "Subject: New greeting '%s' on %s." ENDL, greeting_attachment, date);
5204  } else if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
5205  char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
5206  struct ast_channel *ast;
5207  if ((ast = ast_dummy_channel_alloc())) {
5208  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5209  ast_str_substitute_variables(&str1, 0, ast, e_subj);
5210  if (check_mime(ast_str_buffer(str1))) {
5211  char *ptr;
5212  first_line = 1;
5213  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
5214  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5215  *ptr = '\0';
5216  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5217  first_line = 0;
5218  /* Substring is smaller, so this will never grow */
5219  ast_str_set(&str2, 0, "%s", ptr + 1);
5220  }
5221  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5222  } else {
5223  fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
5224  }
5225  ast = ast_channel_unref(ast);
5226  } else {
5227  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5228  }
5229  } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
5230  if (ast_strlen_zero(flag)) {
5231  fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
5232  } else {
5233  fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
5234  }
5235  } else {
5236  if (ast_strlen_zero(flag)) {
5237  fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
5238  } else {
5239  fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
5240  }
5241  }
5242 
5243  fprintf(p, "Message-ID: <Asterisk-%d-%u-%s-%d@%s>" ENDL, msgnum + 1,
5244  (unsigned int) ast_random(), mailbox, (int) getpid(), host);
5245  if (imap) {
5246  /* additional information needed for IMAP searching */
5247  fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
5248  /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
5249  fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
5250  fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
5251 #ifdef IMAP_STORAGE
5252  fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
5253 #else
5254  fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
5255 #endif
5256  /* flag added for Urgent */
5257  fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, S_OR(flag, ""));
5258  fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan ? ast_channel_priority(chan) : 0);
5259  fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
5260  fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
5261  fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
5262  if (!ast_strlen_zero(category)) {
5263  fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
5264  } else {
5265  fprintf(p, "X-Asterisk-VM-Category: " ENDL);
5266  }
5267  fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
5268  fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
5269  fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
5270  fprintf(p, "X-Asterisk-VM-Message-ID: %s" ENDL, msg_id);
5271  }
5272  if (!ast_strlen_zero(cidnum)) {
5273  fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
5274  }
5275  if (!ast_strlen_zero(cidname)) {
5276  fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
5277  }
5278  fprintf(p, "MIME-Version: 1.0" ENDL);
5279  if (attach_user_voicemail) {
5280  /* Something unique. */
5281  snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%u", msgnum + 1, mailbox,
5282  (int) getpid(), (unsigned int) ast_random());
5283 
5284  fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
5285  fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
5286  fprintf(p, "--%s" ENDL, bound);
5287  }
5288  fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
5289  if (msgnum <= -1) {
5290  fprintf(p, "This message is to let you know that your greeting '%s' was changed on %s." ENDL
5291  "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL,
5292  greeting_attachment, date);
5293  } else if (emailbody || vmu->emailbody) {
5294  char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
5295  struct ast_channel *ast;
5296  if ((ast = ast_dummy_channel_alloc())) {
5297  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5298  ast_str_substitute_variables(&str1, 0, ast, e_body);
5299 #ifdef IMAP_STORAGE
5300  {
5301  /* Convert body to native line terminators for IMAP backend */
5302  char *line = ast_str_buffer(str1), *next;
5303  do {
5304  /* Terminate line before outputting it to the file */
5305  if ((next = strchr(line, '\n'))) {
5306  *next++ = '\0';
5307  }
5308  fprintf(p, "%s" ENDL, line);
5309  line = next;
5310  } while (!ast_strlen_zero(line));
5311  }
5312 #else
5313  fprintf(p, "%s" ENDL, ast_str_buffer(str1));
5314 #endif
5315  ast = ast_channel_unref(ast);
5316  } else {
5317  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5318  }
5319  } else {
5320  if (strcmp(vmu->mailbox, mailbox)) {
5321  /* Forwarded type */
5322  struct ast_config *msg_cfg;
5323  const char *v;
5324  int inttime;
5325  char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
5326  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
5327  /* Retrieve info from VM attribute file */
5328  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
5329  make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
5330  if (strlen(fromfile) < sizeof(fromfile) - 5) {
5331  strcat(fromfile, ".txt");
5332  }
5333  if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
5334  if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
5335  ast_copy_string(origcallerid, v, sizeof(origcallerid));
5336  }
5337 
5338  /* You might be tempted to do origdate, except that a) it's in the wrong
5339  * format, and b) it's missing for IMAP recordings. */
5340  if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
5341  struct timeval tv = { inttime, };
5342  struct ast_tm tm;
5343  ast_localtime(&tv, &tm, NULL);
5344  ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5345  }
5346  fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
5347  " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
5348  "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
5349  " chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
5350  msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
5351  date, origcallerid, origdate);
5352  ast_config_destroy(msg_cfg);
5353  } else {
5354  goto plain_message;
5355  }
5356  } else {
5357 plain_message:
5358  fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
5359  "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
5360  "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
5361  ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
5362  (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
5363  }
5364  }
5365 
5366  if (imap || attach_user_voicemail) {
5367  if (!ast_strlen_zero(attach2)) {
5368  snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
5369  ast_debug(5, "creating second attachment filename %s\n", filename);
5370  add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
5371  snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
5372  ast_debug(5, "creating attachment filename %s\n", filename);
5373  add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
5374  } else {
5375  snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
5376  ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
5377  add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
5378  }
5379  }
5380  ast_free(str1);
5381  ast_free(str2);
5382 }
5383 
5384 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)
5385 {
5386  char fname[PATH_MAX] = "";
5387  char sox_gain_tmpdir[PATH_MAX];
5388  char *file_to_delete = NULL, *dir_to_delete = NULL;
5389  int res;
5390  char altfname[PATH_MAX] = "";
5391  int altused = 0;
5392  char altformat[80] = "";
5393  char *c = NULL;
5394 
5395  /* Eww. We want formats to tell us their own MIME type */
5396  char *mime_type = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
5397 
5398  /* Users of multiple file formats need special attention. */
5399  snprintf(fname, sizeof(fname), "%s.%s", attach, format);
5400  if (!ast_file_is_readable(fname)) {
5401  ast_copy_string(altformat, vmfmts, sizeof(altformat));
5402  c = strchr(altformat, '|');
5403  if (c) {
5404  *c = '\0';
5405  }
5406  ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s - trying first/alternate format %s\n", fname, strerror(errno), altformat);
5407  snprintf(altfname, sizeof(altfname), "%s.%s", attach, altformat);
5408  if (!ast_file_is_readable(altfname)) {
5409  ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s - alternate format %s failure\n", altfname, strerror(errno), altformat);
5410  } else {
5411  altused = 1;
5412  }
5413  }
5414 
5415  /* This 'while' loop will only execute once. We use it so that we can 'break' */
5416  while (vmu->volgain < -.001 || vmu->volgain > .001 || altused) {
5417  char tmpdir[PATH_MAX];
5418 
5419  create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
5420 
5421  res = snprintf(sox_gain_tmpdir, sizeof(sox_gain_tmpdir), "%s/vm-gain-XXXXXX", tmpdir);
5422  if (res >= sizeof(sox_gain_tmpdir)) {
5423  ast_log(LOG_ERROR, "Failed to create temporary directory path %s: Out of buffer space\n", tmpdir);
5424  break;
5425  }
5426 
5427  if (mkdtemp(sox_gain_tmpdir)) {
5428  int soxstatus = 0;
5429  char sox_gain_cmd[PATH_MAX];
5430 
5431  ast_debug(3, "sox_gain_tmpdir: %s\n", sox_gain_tmpdir);
5432 
5433  /* Save for later */
5434  dir_to_delete = sox_gain_tmpdir;
5435 
5436  res = snprintf(fname, sizeof(fname), "%s/output.%s", sox_gain_tmpdir, format);
5437  if (res >= sizeof(fname)) {
5438  ast_log(LOG_ERROR, "Failed to create filename buffer for %s/output.%s: Too long\n", sox_gain_tmpdir, format);
5439  break;
5440  }
5441 
5442  if (!altused) {
5443  res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s",
5444  vmu->volgain, attach, format, fname);
5445  } else {
5446  if (!strcasecmp(format, "wav")) {
5447  if (vmu->volgain < -.001 || vmu->volgain > .001) {
5448  res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s -e signed-integer -b 16 %s",
5449  vmu->volgain, attach, altformat, fname);
5450  } else {
5451  res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox %s.%s -e signed-integer -b 16 %s",
5452  attach, altformat, fname);
5453  }
5454  } else {
5455  if (vmu->volgain < -.001 || vmu->volgain > .001) {
5456  res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s",
5457  vmu->volgain, attach, altformat, fname);
5458  } else {
5459  res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox %s.%s %s",
5460  attach, altformat, fname);
5461  }
5462  }
5463  }
5464 
5465  if (res >= sizeof(sox_gain_cmd)) {
5466  ast_log(LOG_ERROR, "Failed to generate sox command, out of buffer space\n");
5467  break;
5468  }
5469 
5470  soxstatus = ast_safe_system(sox_gain_cmd);
5471  if (!soxstatus) {
5472  /* Save for later */
5473  file_to_delete = fname;
5474  ast_debug(3, "VOLGAIN: Stored at: %s - Level: %.4f - Mailbox: %s\n", fname, vmu->volgain, mailbox);
5475  } else {
5476  ast_log(LOG_WARNING, "Sox failed to re-encode %s: %s (have you installed support for all sox file formats?)\n",
5477  fname,
5478  soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
5479  ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
5480  }
5481  }
5482 
5483  break;
5484  }
5485 
5486  if (!file_to_delete) {
5487  res = snprintf(fname, sizeof(fname), "%s.%s", attach, format);
5488  if (res >= sizeof(fname)) {
5489  ast_log(LOG_ERROR, "Failed to create filename buffer for %s.%s: Too long\n", attach, format);
5490  return -1;
5491  }
5492  }
5493 
5494  fprintf(p, "--%s" ENDL, bound);
5495  if (msgnum > -1)
5496  fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, mime_type, format, filename);
5497  else
5498  fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, mime_type, format, greeting_attachment, format);
5499  fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
5500  fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
5501  if (msgnum > -1)
5502  fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
5503  else
5504  fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
5505  base_encode(fname, p);
5506  if (last)
5507  fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
5508 
5509  if (file_to_delete) {
5510  unlink(file_to_delete);
5511  }
5512 
5513  if (dir_to_delete) {
5514  rmdir(dir_to_delete);
5515  }
5516 
5517  return 0;
5518 }
5519 
5520 static int sendmail(char *srcemail,
5521  struct ast_vm_user *vmu,
5522  int msgnum,
5523  char *context,
5524  char *mailbox,
5525  const char *fromfolder,
5526  char *cidnum,
5527  char *cidname,
5528  char *attach,
5529  char *attach2,
5530  char *format,
5531  int duration,
5532  int attach_user_voicemail,
5533  struct ast_channel *chan,
5534  const char *category,
5535  const char *flag,
5536  const char *msg_id)
5537 {
5538  FILE *p = NULL;
5539  char tmp[80] = "/tmp/astmail-XXXXXX";
5540  char tmp2[256];
5541  char *stringp;
5542 
5543  if (vmu && ast_strlen_zero(vmu->email)) {
5544  ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
5545  return(0);
5546  }
5547 
5548  /* Mail only the first format */
5549  format = ast_strdupa(format);
5550  stringp = format;
5551  strsep(&stringp, "|");
5552 
5553  if (!strcmp(format, "wav49"))
5554  format = "WAV";
5555  ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %u\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
5556  /* Make a temporary file instead of piping directly to sendmail, in case the mail
5557  command hangs */
5558  if ((p = vm_mkftemp(tmp)) == NULL) {
5559  ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
5560  return -1;
5561  } else {
5562  make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag, msg_id);
5563  fclose(p);
5564  snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
5565  ast_safe_system(tmp2);
5566  ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
5567  }
5568  return 0;
5569 }
5570 
5571 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)
5572 {
5573  char enc_cidnum[256], enc_cidname[256];
5574  char date[256];
5575  char host[MAXHOSTNAMELEN] = "";
5576  char who[256];
5577  char dur[PATH_MAX];
5578  char tmp[80] = "/tmp/astmail-XXXXXX";
5579  char tmp2[PATH_MAX];
5580  struct ast_tm tm;
5581  FILE *p;
5582  struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
5583 
5584  if (!str1 || !str2) {
5585  ast_free(str1);
5586  ast_free(str2);
5587  return -1;
5588  }
5589 
5590  if (cidnum) {
5591  strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
5592  }
5593  if (cidname) {
5594  strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
5595  }
5596 
5597  if ((p = vm_mkftemp(tmp)) == NULL) {
5598  ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
5599  ast_free(str1);
5600  ast_free(str2);
5601  return -1;
5602  }
5603  gethostname(host, sizeof(host)-1);
5604  if (strchr(srcemail, '@')) {
5605  ast_copy_string(who, srcemail, sizeof(who));
5606  } else {
5607  snprintf(who, sizeof(who), "%s@%s", srcemail, host);
5608  }
5609  snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
5610  ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
5611  fprintf(p, "Date: %s\n", date);
5612 
5613  /* Reformat for custom pager format */
5614  ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
5615 
5616  if (!ast_strlen_zero(pagerfromstring)) {
5617  struct ast_channel *ast;
5618  if ((ast = ast_dummy_channel_alloc())) {
5619  char *ptr;
5620  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
5621  ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
5622 
5623  if (check_mime(ast_str_buffer(str1))) {
5624  int first_line = 1;
5625  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
5626  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5627  *ptr = '\0';
5628  fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
5629  first_line = 0;
5630  /* Substring is smaller, so this will never grow */
5631  ast_str_set(&str2, 0, "%s", ptr + 1);
5632  }
5633  fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
5634  } else {
5635  fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
5636  }
5637  ast = ast_channel_unref(ast);
5638  } else {
5639  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5640  }
5641  } else {
5642  fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
5643  }
5644 
5645  if (check_mime(vmu->fullname)) {
5646  int first_line = 1;
5647  char *ptr;
5648  ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
5649  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5650  *ptr = '\0';
5651  fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
5652  first_line = 0;
5653  /* Substring is smaller, so this will never grow */
5654  ast_str_set(&str2, 0, "%s", ptr + 1);
5655  }
5656  fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
5657  } else {
5658  fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
5659  }
5660 
5661  if (!ast_strlen_zero(pagersubject)) {
5662  struct ast_channel *ast;
5663  if ((ast = ast_dummy_channel_alloc())) {
5664  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5665  ast_str_substitute_variables(&str1, 0, ast, pagersubject);
5666  if (check_mime(ast_str_buffer(str1))) {
5667  int first_line = 1;
5668  char *ptr;
5669  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
5670  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5671  *ptr = '\0';
5672  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5673  first_line = 0;
5674  /* Substring is smaller, so this will never grow */
5675  ast_str_set(&str2, 0, "%s", ptr + 1);
5676  }
5677  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5678  } else {
5679  fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
5680  }
5681  ast = ast_channel_unref(ast);
5682  } else {
5683  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5684  }
5685  } else {
5686  if (ast_strlen_zero(flag)) {
5687  fprintf(p, "Subject: New VM\n\n");
5688  } else {
5689  fprintf(p, "Subject: New %s VM\n\n", flag);
5690  }
5691  }
5692 
5693  if (pagerbody) {
5694  struct ast_channel *ast;
5695  if ((ast = ast_dummy_channel_alloc())) {
5696  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5697  ast_str_substitute_variables(&str1, 0, ast, pagerbody);
5698  fprintf(p, "%s" ENDL, ast_str_buffer(str1));
5699  ast = ast_channel_unref(ast);
5700  } else {
5701  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5702  }
5703  } else {
5704  fprintf(p, "New %s long %s msg in box %s\n"
5705  "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
5706  }
5707 
5708  fclose(p);
5709  snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
5710  ast_safe_system(tmp2);
5711  ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
5712  ast_free(str1);
5713  ast_free(str2);
5714  return 0;
5715 }
5716 
5717 /*!
5718  * \brief Gets the current date and time, as formatted string.
5719  * \param s The buffer to hold the output formatted date.
5720  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
5721  *
5722  * The date format string used is "%a %b %e %r UTC %Y".
5723  *
5724  * \return zero on success, -1 on error.
5725  */
5726 static int get_date(char *s, int len)
5727 {
5728  struct ast_tm tm;
5729  struct timeval t = ast_tvnow();
5730 
5731  ast_localtime(&t, &tm, "UTC");
5732 
5733  return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
5734 }
5735 
5736 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
5737 {
5738  int res;
5739  char fn[PATH_MAX];
5740  char dest[PATH_MAX];
5741 
5742  snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
5743 
5744  if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
5745  ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
5746  return -1;
5747  }
5748 
5749  RETRIEVE(fn, -1, ext, context);
5750  if (ast_fileexists(fn, NULL, NULL) > 0) {
5751  res = ast_stream_and_wait(chan, fn, ecodes);
5752  if (res) {
5753  DISPOSE(fn, -1);
5754  return res;
5755  }
5756  } else {
5757  /* Dispose just in case */
5758  DISPOSE(fn, -1);
5759  res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
5760  if (res)
5761  return res;
5762  res = ast_say_digit_str(chan, ext, ecodes, ast_channel_language(chan));
5763  if (res)
5764  return res;
5765  }
5766  res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
5767  return res;
5768 }
5769 
5770 static void free_zone(struct vm_zone *z)
5771 {
5772  ast_free(z);
5773 }
5774 
5775 #ifdef ODBC_STORAGE
5776 
5777 static int count_messages_in_folder(struct odbc_obj *odbc, const char *context, const char *mailbox, const char *folder, int *messages)
5778 {
5779  int res;
5780  char sql[PATH_MAX];
5781  char rowdata[20];
5782  SQLHSTMT stmt = NULL;
5783  struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
5784 
5785  if (!messages) {
5786  return 0;
5787  }
5788 
5789  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
5790  if (!(stmt = ast_odbc_prepare_and_execute(odbc, generic_prepare, &gps))) {
5791  ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5792  return 1;
5793  }
5794  res = SQLFetch(stmt);
5795  if (!SQL_SUCCEEDED(res)) {
5796  ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5797  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
5798  return 1;
5799  }
5800  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5801  if (!SQL_SUCCEEDED(res)) {
5802  ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5803  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
5804  return 1;
5805  }
5806 
5807  *messages = atoi(rowdata);
5808  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
5809 
5810  return 0;
5811 }
5812 
5813 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
5814 {
5815  char tmp[PATH_MAX] = "";
5816  struct odbc_obj *obj;
5817  char *context;
5818 
5819  if (newmsgs)
5820  *newmsgs = 0;
5821  if (oldmsgs)
5822  *oldmsgs = 0;
5823  if (urgentmsgs)
5824  *urgentmsgs = 0;
5825 
5826  /* If no mailbox, return immediately */
5827  if (ast_strlen_zero(mailbox))
5828  return 0;
5829 
5830  ast_copy_string(tmp, mailbox, sizeof(tmp));
5831 
5832  if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
5833  int u, n, o;
5834  char *next, *remaining = tmp;
5835  while ((next = strsep(&remaining, " ,"))) {
5836  if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
5837  return -1;
5838  }
5839  if (urgentmsgs) {
5840  *urgentmsgs += u;
5841  }
5842  if (newmsgs) {
5843  *newmsgs += n;
5844  }
5845  if (oldmsgs) {
5846  *oldmsgs += o;
5847  }
5848  }
5849  return 0;
5850  }
5851 
5852  context = strchr(tmp, '@');
5853  if (context) {
5854  *context = '\0';
5855  context++;
5856  } else
5857  context = "default";
5858 
5859  obj = ast_odbc_request_obj(odbc_database, 0);
5860  if (!obj) {
5861  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
5862  return -1;
5863  }
5864 
5865  if (count_messages_in_folder(obj, context, tmp, "INBOX", newmsgs)
5866  || count_messages_in_folder(obj, context, tmp, "Old", oldmsgs)
5867  || count_messages_in_folder(obj, context, tmp, "Urgent", urgentmsgs)) {
5868  ast_log(AST_LOG_WARNING, "Failed to obtain message count for mailbox %s@%s\n",
5869  tmp, context);
5870  }
5871 
5872  ast_odbc_release_obj(obj);
5873  return 0;
5874 }
5875 
5876 /*!
5877  * \brief Gets the number of messages that exist in a mailbox folder.
5878  * \param mailbox_id
5879  * \param folder
5880  *
5881  * This method is used when ODBC backend is used.
5882  * \return The number of messages in this mailbox folder (zero or more).
5883  */
5884 static int messagecount(const char *mailbox_id, const char *folder)
5885 {
5886  struct odbc_obj *obj = NULL;
5887  char *context;
5888  char *mailbox;
5889  int nummsgs = 0;
5890  int res;
5891  SQLHSTMT stmt = NULL;
5892  char sql[PATH_MAX];
5893  char rowdata[20];
5894  struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
5895 
5896  /* If no mailbox, return immediately */
5897  if (ast_strlen_zero(mailbox_id)
5898  || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
5899  return 0;
5900  }
5901 
5902  if (ast_strlen_zero(folder)) {
5903  folder = "INBOX";
5904  }
5905 
5906  obj = ast_odbc_request_obj(odbc_database, 0);
5907  if (!obj) {
5908  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
5909  return 0;
5910  }
5911 
5912  if (!strcmp(folder, "INBOX")) {
5913  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/INBOX' OR dir = '%s%s/%s/Urgent'", odbc_table, VM_SPOOL_DIR, context, mailbox, VM_SPOOL_DIR, context, mailbox);
5914  } else {
5915  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
5916  }
5917 
5918  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
5919  if (!stmt) {
5920  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5921  goto bail;
5922  }
5923  res = SQLFetch(stmt);
5924  if (!SQL_SUCCEEDED(res)) {
5925  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5926  goto bail_with_handle;
5927  }
5928  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5929  if (!SQL_SUCCEEDED(res)) {
5930  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5931  goto bail_with_handle;
5932  }
5933  nummsgs = atoi(rowdata);
5934 
5935 bail_with_handle:
5936  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
5937 
5938 bail:
5939  ast_odbc_release_obj(obj);
5940  return nummsgs;
5941 }
5942 
5943 /**
5944  * \brief Determines if the given folder has messages.
5945  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
5946  *
5947  * This function is used when the mailbox is stored in an ODBC back end.
5948  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
5949  * \return 1 if the folder has one or more messages. zero otherwise.
5950  */
5951 static int has_voicemail(const char *mailboxes, const char *folder)
5952 {
5953  char *parse;
5954  char *mailbox;
5955 
5956  parse = ast_strdupa(mailboxes);
5957  while ((mailbox = strsep(&parse, ",&"))) {
5958  if (messagecount(mailbox, folder)) {
5959  return 1;
5960  }
5961  }
5962  return 0;
5963 }
5964 #endif
5965 #ifndef IMAP_STORAGE
5966 /*!
5967  * \brief Copies a message from one mailbox to another.
5968  * \param chan
5969  * \param vmu
5970  * \param imbox
5971  * \param msgnum
5972  * \param duration
5973  * \param recip
5974  * \param fmt
5975  * \param dir
5976  * \param flag, dest_folder
5977  *
5978  * This is only used by file storage based mailboxes.
5979  *
5980  * \return zero on success, -1 on error.
5981  */
5982 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, const char *dest_folder)
5983 {
5984  char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
5985  const char *frombox = mbox(vmu, imbox);
5986  const char *userfolder;
5987  int recipmsgnum;
5988  int res = 0;
5989 
5990  ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
5991 
5992  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
5993  userfolder = "Urgent";
5994  } else if (!ast_strlen_zero(dest_folder)) {
5995  userfolder = dest_folder;
5996  } else {
5997  userfolder = "INBOX";
5998  }
5999 
6000  create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
6001 
6002  if (!dir)
6003  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
6004  else
6005  ast_copy_string(fromdir, dir, sizeof(fromdir));
6006 
6007  make_file(frompath, sizeof(frompath), fromdir, msgnum);
6008  make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
6009 
6010  if (vm_lock_path(todir))
6011  return ERROR_LOCK_PATH;
6012 
6013  recipmsgnum = last_message_index(recip, todir) + 1;
6014  if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
6015  make_file(topath, sizeof(topath), todir, recipmsgnum);
6016 #ifndef ODBC_STORAGE
6017  if (EXISTS(fromdir, msgnum, frompath, chan ? ast_channel_language(chan) : "")) {
6018  COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
6019  } else {
6020 #endif
6021  /* If we are prepending a message for ODBC, then the message already
6022  * exists in the database, but we want to force copying from the
6023  * filesystem (since only the FS contains the prepend). */
6024  copy_plain_file(frompath, topath);
6025  STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL, NULL);
6026  vm_delete(topath);
6027 #ifndef ODBC_STORAGE
6028  }
6029 #endif
6030  } else {
6031  ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
6032  res = -1;
6033  }
6034  ast_unlock_path(todir);
6035  if (chan) {
6036  struct ast_party_caller *caller = ast_channel_caller(chan);
6037  notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
6038  S_COR(caller->id.number.valid, caller->id.number.str, NULL),
6039  S_COR(caller->id.name.valid, caller->id.name.str, NULL),
6040  flag);
6041  }
6042 
6043  return res;
6044 }
6045 #endif
6046 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
6047 
6048 static int messagecount(const char *mailbox_id, const char *folder)
6049 {
6050  char *context;
6051  char *mailbox;
6052 
6053  if (ast_strlen_zero(mailbox_id)
6054  || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
6055  return 0;
6056  }
6057 
6058  return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
6059 }
6060 
6061 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
6062 {
6063  DIR *dir;
6064  struct dirent *de;
6065  char fn[256];
6066  int ret = 0;
6067  struct alias_mailbox_mapping *mapping;
6068  char *c;
6069  char *m;
6070 
6071  /* If no mailbox, return immediately */
6072  if (ast_strlen_zero(mailbox))
6073  return 0;
6074 
6075  if (ast_strlen_zero(folder))
6076  folder = "INBOX";
6077  if (ast_strlen_zero(context))
6078  context = "default";
6079 
6080  c = (char *)context;
6081  m = (char *)mailbox;
6082 
6083  if (!ast_strlen_zero(aliasescontext)) {
6084  char tmp[MAX_VM_MAILBOX_LEN];
6085 
6086  snprintf(tmp, MAX_VM_MAILBOX_LEN, "%s@%s", mailbox, context);
6087  mapping = ao2_find(alias_mailbox_mappings, tmp, OBJ_SEARCH_KEY);
6088  if (mapping) {
6089  separate_mailbox(ast_strdupa(mapping->mailbox), &m, &c);
6090  ao2_ref(mapping, -1);
6091  }
6092  }
6093 
6094  snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, c, m, folder);
6095 
6096  if (!(dir = opendir(fn)))
6097  return 0;
6098 
6099  while ((de = readdir(dir))) {
6100  if (!strncasecmp(de->d_name, "msg", 3)) {
6101  if (shortcircuit) {
6102  ret = 1;
6103  break;
6104  } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
6105  ret++;
6106  }
6107  }
6108  }
6109 
6110  closedir(dir);
6111 
6112  return ret;
6113 }
6114 
6115 /**
6116  * \brief Determines if the given folder has messages.
6117  * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
6118  * \param folder the folder to look in
6119  *
6120  * This function is used when the mailbox is stored in a filesystem back end.
6121  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
6122  * \return 1 if the folder has one or more messages. zero otherwise.
6123  */
6124 static int has_voicemail(const char *mailbox, const char *folder)
6125 {
6126  char tmp[256], *tmp2 = tmp, *box, *context;
6127  ast_copy_string(tmp, mailbox, sizeof(tmp));
6128  if (ast_strlen_zero(folder)) {
6129  folder = "INBOX";
6130  }
6131  while ((box = strsep(&tmp2, ",&"))) {
6132  if ((context = strchr(box, '@')))
6133  *context++ = '\0';
6134  else
6135  context = "default";
6136  if (__has_voicemail(context, box, folder, 1))
6137  return 1;
6138  /* If we are checking INBOX, we should check Urgent as well */
6139  if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
6140  return 1;
6141  }
6142  }
6143  return 0;
6144 }
6145 
6146 /*!
6147  * \brief Check the given mailbox's message count.
6148  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
6149  * \param urgentmsgs urgent message count.
6150  * \param newmsgs new message count.
6151  * \param oldmsgs old message count pointer
6152  * \return -1 if error occurred, 0 otherwise.
6153  */
6154 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
6155 {
6156  char tmp[256];
6157  char *context;
6158 
6159  /* If no mailbox, return immediately */
6160  if (ast_strlen_zero(mailbox)) {
6161  return 0;
6162  }
6163 
6164  if (newmsgs) {
6165  *newmsgs = 0;
6166  }
6167  if (oldmsgs) {
6168  *oldmsgs = 0;
6169  }
6170  if (urgentmsgs) {
6171  *urgentmsgs = 0;
6172  }
6173 
6174  if (strchr(mailbox, ',')) {
6175  int tmpnew, tmpold, tmpurgent;
6176  char *mb, *cur;
6177 
6178  ast_copy_string(tmp, mailbox, sizeof(tmp));
6179  mb = tmp;
6180  while ((cur = strsep(&mb, ", "))) {
6181  if (!ast_strlen_zero(cur)) {
6182  if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL)) {
6183  return -1;
6184  } else {
6185  if (newmsgs) {
6186  *newmsgs += tmpnew;
6187  }
6188  if (oldmsgs) {
6189  *oldmsgs += tmpold;
6190  }
6191  if (urgentmsgs) {
6192  *urgentmsgs += tmpurgent;
6193  }
6194  }
6195  }
6196  }
6197  return 0;
6198  }
6199 
6200  ast_copy_string(tmp, mailbox, sizeof(tmp));
6201 
6202  if ((context = strchr(tmp, '@'))) {
6203  *context++ = '\0';
6204  } else {
6205  context = "default";
6206  }
6207 
6208  if (newmsgs) {
6209  *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
6210  }
6211  if (oldmsgs) {
6212  *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
6213  }
6214  if (urgentmsgs) {
6215  *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
6216  }
6217 
6218  return 0;
6219 }
6220 
6221 #endif
6222 
6223 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
6224 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
6225 {
6226  int urgentmsgs = 0;
6227  int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
6228  if (newmsgs) {
6229  *newmsgs += urgentmsgs;
6230  }
6231  return res;
6232 }
6233 
6234 static void run_externnotify(const char *context, const char *extension, const char *flag)
6235 {
6236  char arguments[255];
6237  char ext_context[256] = "";
6238  int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
6239  struct ast_smdi_mwi_message *mwi_msg;
6240 
6241  if (!ast_strlen_zero(context))
6242  snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
6243  else
6244  ast_copy_string(ext_context, extension, sizeof(ext_context));
6245 
6246  if (smdi_iface) {
6247  if (ast_app_has_voicemail(ext_context, NULL))
6248  ast_smdi_mwi_set(smdi_iface, extension);
6249  else
6250  ast_smdi_mwi_unset(smdi_iface, extension);
6251 
6252  if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
6253  ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
6254  if (!strncmp(mwi_msg->cause, "INV", 3))
6255  ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
6256  else if (!strncmp(mwi_msg->cause, "BLK", 3))
6257  ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
6258  ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
6259  ao2_ref(mwi_msg, -1);
6260  } else {
6261  ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
6262  }
6263  }
6264 
6265  if (!ast_strlen_zero(externnotify)) {
6266  if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
6267  ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
6268  } else {
6269  snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
6270  externnotify, S_OR(context, "\"\""),
6271  extension, newvoicemails,
6272  oldvoicemails, urgentvoicemails);
6273  ast_debug(1, "Executing %s\n", arguments);
6274  ast_safe_system(arguments);
6275  }
6276  }
6277 }
6278 
6279 /*!
6280  * \brief Variables used for saving a voicemail.
6281  *
6282  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
6283  */
6284 struct leave_vm_options {
6285  unsigned int flags;
6286  signed char record_gain;
6288  char *beeptone;
6289 };
6290 
6291 static void generate_msg_id(char *dst)
6292 {
6293  /* msg id is time of msg_id generation plus an incrementing value
6294  * called each time a new msg_id is generated. This should achieve uniqueness,
6295  * but only in single system solutions.
6296  */
6297  unsigned int unique_counter = ast_atomic_fetchadd_int(&msg_id_incrementor, +1);
6298  snprintf(dst, MSG_ID_LEN, "%ld-%08x", (long) time(NULL), unique_counter);
6299 }
6300 
6301 /*!
6302  * \internal
6303  * \brief Creates a voicemail based on a specified file to a mailbox.
6304  * \param recdata A vm_recording_data containing filename and voicemail txt info.
6305  * \retval -1 failure
6306  * \retval 0 success
6307  *
6308  * This is installed to the app.h voicemail functions and accommodates all voicemail
6309  * storage methods. It should probably be broken out along with leave_voicemail at
6310  * some point in the future.
6311  *
6312  * This function currently only works for a single recipient and only uses the format
6313  * specified in recording_ext.
6314  */
6315 static int msg_create_from_file(struct ast_vm_recording_data *recdata)
6316 {
6317  /* voicemail recipient structure */
6318  struct ast_vm_user *recipient; /* points to svm once it's been created */
6319  struct ast_vm_user svm; /* struct storing the voicemail recipient */
6320 
6321  /* File paths */
6322  char tmpdir[PATH_MAX]; /* directory temp files are stored in */
6323  char tmptxtfile[PATH_MAX]; /* tmp file for voicemail txt file */
6324  char desttxtfile[PATH_MAX]; /* final destination for txt file */
6325  char tmpaudiofile[PATH_MAX]; /* tmp file where audio is stored */
6326  char dir[PATH_MAX]; /* destination for tmp files on completion */
6327  char destination[PATH_MAX]; /* destination with msgXXXX. Basically <dir>/msgXXXX */
6328 
6329  /* stuff that only seems to be needed for IMAP */
6330  #ifdef IMAP_STORAGE
6331  struct vm_state *vms = NULL;
6332  char ext_context[256] = "";
6333  char *fmt = ast_strdupa(recdata->recording_ext);
6334  int newmsgs = 0;
6335  int oldmsgs = 0;
6336  #endif
6337 
6338  /* miscellaneous operational variables */
6339  int res = 0; /* Used to store error codes from functions */
6340  int txtdes /* File descriptor for the text file used to write the voicemail info */;
6341  FILE *txt; /* FILE pointer to text file used to write the voicemail info */
6342  char date[256]; /* string used to hold date of the voicemail (only used for ODBC) */
6343  int msgnum; /* the 4 digit number designated to the voicemail */
6344  int duration = 0; /* Length of the audio being recorded in seconds */
6345  struct ast_filestream *recording_fs; /*used to read the recording to get duration data */
6346 
6347  /* We aren't currently doing anything with category, since it comes from a channel variable and
6348  * this function doesn't use channels, but this function could add that as an argument later. */
6349  const char *category = NULL; /* pointless for now */
6350  char msg_id[MSG_ID_LEN];
6351 
6352  /* Start by checking to see if the file actually exists... */
6353  if (!(ast_fileexists(recdata->recording_file, recdata->recording_ext, NULL))) {
6354  ast_log(LOG_ERROR, "File: %s not found.\n", recdata->recording_file);
6355  return -1;
6356  }
6357 
6358  memset(&svm, 0, sizeof(svm));
6359  if (!(recipient = find_user(&svm, recdata->context, recdata->mailbox))) {
6360  ast_log(LOG_ERROR, "No entry in voicemail config file for '%s@%s'\n", recdata->mailbox, recdata->context);
6361  return -1;
6362  }
6363 
6364  /* determine duration in seconds */
6365  if ((recording_fs = ast_readfile(recdata->recording_file, recdata->recording_ext, NULL, 0, 0, VOICEMAIL_DIR_MODE))) {
6366  if (!ast_seekstream(recording_fs, 0, SEEK_END)) {
6367  long framelength = ast_tellstream(recording_fs);
6368  int sample_rate = ast_ratestream(recording_fs);
6369  if (sample_rate) {
6370  duration = (int) (framelength / sample_rate);
6371  } else {
6372  ast_log(LOG_ERROR,"Unable to determine sample rate of recording %s\n", recdata->recording_file);
6373  }
6374  }
6375  ast_closestream(recording_fs);
6376  }
6377 
6378  /* If the duration was below the minimum duration for the user, let's just drop the whole thing now */
6379  if (duration < recipient->minsecs) {
6380  ast_log(LOG_NOTICE, "Copying recording to voicemail %s@%s skipped because duration was shorter than "
6381  "minmessage of recipient\n", recdata->mailbox, recdata->context);
6382  return -1;
6383  }
6384 
6385  /* Note that this number must be dropped back to a net sum of zero before returning from this function */
6386 
6387  if ((res = create_dirpath(tmpdir, sizeof(tmpdir), recipient->context, recdata->mailbox, "tmp"))) {
6388  ast_log(LOG_ERROR, "Failed to make directory.\n");
6389  }
6390 
6391  snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
6392  txtdes = mkstemp(tmptxtfile);
6393  if (txtdes < 0) {
6394  chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
6395  /* Something screwed up. Abort. */
6396  ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
6397  free_user(recipient);
6398  return -1;
6399  }
6400 
6401  /* Store information */
6402  txt = fdopen(txtdes, "w+");
6403  if (txt) {
6404  generate_msg_id(msg_id);
6405  get_date(date, sizeof(date));
6406  fprintf(txt,
6407  ";\n"
6408  "; Message Information file\n"
6409  ";\n"
6410  "[message]\n"
6411  "origmailbox=%s\n"
6412  "context=%s\n"
6413  "macrocontext=%s\n"
6414  "exten=%s\n"
6415  "rdnis=Unknown\n"
6416  "priority=%d\n"
6417  "callerchan=%s\n"
6418  "callerid=%s\n"
6419  "origdate=%s\n"
6420  "origtime=%ld\n"
6421  "category=%s\n"
6422  "msg_id=%s\n"
6423  "flag=\n" /* flags not supported in copy from file yet */
6424  "duration=%d\n", /* Don't have any reliable way to get duration of file. */
6425 
6426  recdata->mailbox,
6427  S_OR(recdata->call_context, ""),
6428  S_OR(recdata->call_macrocontext, ""),
6429  S_OR(recdata->call_extension, ""),
6430  recdata->call_priority,
6431  S_OR(recdata->call_callerchan, "Unknown"),
6432  S_OR(recdata->call_callerid, "Unknown"),
6433  date, (long) time(NULL),
6434  S_OR(category, ""),
6435  msg_id,
6436  duration);
6437 
6438  /* Since we are recording from a file, we shouldn't need to do anything else with
6439  * this txt file */
6440  fclose(txt);
6441 
6442  } else {
6443  ast_log(LOG_WARNING, "Error opening text file for output\n");
6444  if (ast_check_realtime("voicemail_data")) {
6445  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6446  }
6447  free_user(recipient);
6448  return -1;
6449  }
6450 
6451  /* At this point, the actual creation of a voicemail message should be finished.
6452  * Now we just need to copy the files being recorded into the receiving folder. */
6453 
6454  create_dirpath(dir, sizeof(dir), recipient->context, recipient->mailbox, recdata->folder);
6455 
6456 #ifdef IMAP_STORAGE
6457  /* make recipient info into an inboxcount friendly string */
6458  snprintf(ext_context, sizeof(ext_context), "%s@%s", recipient->mailbox, recipient->context);
6459 
6460  /* Is ext a mailbox? */
6461  /* must open stream for this user to get info! */
6462  res = inboxcount(ext_context, &newmsgs, &oldmsgs);
6463  if (res < 0) {
6464  ast_log(LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
6465  free_user(recipient);
6466  unlink(tmptxtfile);
6467  return -1;
6468  }
6469  if (!(vms = get_vm_state_by_mailbox(recipient->mailbox, recipient->context, 0))) {
6470  /* It is possible under certain circumstances that inboxcount did not
6471  * create a vm_state when it was needed. This is a catchall which will
6472  * rarely be used.
6473  */
6474  if (!(vms = create_vm_state_from_user(recipient))) {
6475  ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
6476  free_user(recipient);
6477  unlink(tmptxtfile);
6478  return -1;
6479  }
6480  }
6481  vms->newmessages++;
6482 
6483  /* here is a big difference! We add one to it later */
6484  msgnum = newmsgs + oldmsgs;
6485  ast_debug(3, "Messagecount set to %d\n", msgnum);
6486  snprintf(destination, sizeof(destination), "%simap/msg%s%04d", VM_SPOOL_DIR, recipient->mailbox, msgnum);
6487 
6488  /* Check to see if we have enough room in the mailbox. If not, spit out an error and end
6489  * Note that imap_check_limits raises inprocess_count if successful */
6490  if ((res = imap_check_limits(NULL, vms, recipient, msgnum))) {
6491  ast_log(LOG_NOTICE, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
6492  inprocess_count(recipient->mailbox, recipient->context, -1);
6493  free_user(recipient);
6494  unlink(tmptxtfile);
6495  return -1;
6496  }
6497 
6498 #else
6499 
6500  /* Check to see if the mailbox is full for ODBC/File storage */
6501  ast_debug(3, "mailbox = %d : inprocess = %d\n", count_messages(recipient, dir),
6502  inprocess_count(recipient->mailbox, recipient->context, 0));
6503  if (count_messages(recipient, dir) > recipient->maxmsg - inprocess_count(recipient->mailbox, recipient->context, +1)) {
6504  ast_log(AST_LOG_WARNING, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
6505  inprocess_count(recipient->mailbox, recipient->context, -1);
6506  free_user(recipient);
6507  unlink(tmptxtfile);
6508  return -1;
6509  }
6510 
6511  msgnum = last_message_index(recipient, dir) + 1;
6512 #endif
6513 
6514  /* Lock the directory receiving the voicemail since we want it to still exist when we attempt to copy the voicemail.
6515  * We need to unlock it before we return. */
6516  if (vm_lock_path(dir)) {
6517  ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
6518  /* Delete files */
6519  ast_filedelete(tmptxtfile, NULL);
6520  unlink(tmptxtfile);
6521  free_user(recipient);
6522  return -1;
6523  }
6524 
6525  make_file(destination, sizeof(destination), dir, msgnum);
6526 
6527  make_file(tmpaudiofile, sizeof(tmpaudiofile), tmpdir, msgnum);
6528 
6529  if (ast_filecopy(recdata->recording_file, tmpaudiofile, recdata->recording_ext)) {
6530  ast_log(LOG_ERROR, "Audio file failed to copy to tmp dir. Probably low disk space.\n");
6531 
6532  inprocess_count(recipient->mailbox, recipient->context, -1);
6533  ast_unlock_path(dir);
6534  free_user(recipient);
6535  unlink(tmptxtfile);
6536  return -1;
6537  }
6538 
6539  /* Alright, try to copy to the destination folder now. */
6540  if (ast_filerename(tmpaudiofile, destination, recdata->recording_ext)) {
6541  ast_log(LOG_ERROR, "Audio file failed to move to destination directory. Permissions/Overlap?\n");
6542  inprocess_count(recipient->mailbox, recipient->context, -1);
6543  ast_unlock_path(dir);
6544  free_user(recipient);
6545  unlink(tmptxtfile);
6546  return -1;
6547  }
6548 
6549  snprintf(desttxtfile, sizeof(desttxtfile), "%s.txt", destination);
6550  rename(tmptxtfile, desttxtfile);
6551 
6552  if (chmod(desttxtfile, VOICEMAIL_FILE_MODE) < 0) {
6553  ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", desttxtfile, strerror(errno));
6554  }
6555 
6556 
6557  ast_unlock_path(dir);
6558  inprocess_count(recipient->mailbox, recipient->context, -1);
6559 
6560  /* If we copied something, we should store it either to ODBC or IMAP if we are using those. The STORE macro allows us
6561  * to do both with one line and is also safe to use with file storage mode. Also, if we are using ODBC, now is a good
6562  * time to create the voicemail database entry. */
6563  if (ast_fileexists(destination, NULL, NULL) > 0) {
6564  if (ast_check_realtime("voicemail_data")) {
6565  get_date(date, sizeof(date));
6566  ast_store_realtime("voicemail_data",
6567  "origmailbox", recdata->mailbox,
6568  "context", S_OR(recdata->context, ""),
6569  "macrocontext", S_OR(recdata->call_macrocontext, ""),
6570  "exten", S_OR(recdata->call_extension, ""),
6571  "priority", recdata->call_priority,
6572  "callerchan", S_OR(recdata->call_callerchan, "Unknown"),
6573  "callerid", S_OR(recdata->call_callerid, "Unknown"),
6574  "origdate", date,
6575  "origtime", time(NULL),
6576  "category", S_OR(category, ""),
6577  "filename", tmptxtfile,
6578  "duration", duration,
6579  SENTINEL);
6580  }
6581 
6582  STORE(dir, recipient->mailbox, recipient->context, msgnum, NULL, recipient, fmt, 0, vms, "", msg_id);
6583  notify_new_state(recipient);
6584  }
6585 
6586  free_user(recipient);
6587  unlink(tmptxtfile);
6588  return 0;
6589 }
6590 
6591 /*!
6592  * \brief Prompts the user and records a voicemail to a mailbox.
6593  * \param chan
6594  * \param ext
6595  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
6596  *
6597  *
6598  *
6599  * \return zero on success, -1 on error.
6600  */
6601 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
6602 {
6603 #ifdef IMAP_STORAGE
6604  int newmsgs, oldmsgs;
6605 #endif
6606  char txtfile[PATH_MAX];
6607  char tmptxtfile[PATH_MAX];
6608  struct vm_state *vms = NULL;
6609  char callerid[256];
6610  FILE *txt;
6611  char date[256];
6612  int txtdes;
6613  int res = 0;
6614  int msgnum;
6615  int duration = 0;
6616  int sound_duration = 0;
6617  int ausemacro = 0;
6618  int ousemacro = 0;
6619  int ouseexten = 0;
6620  int greeting_only = 0;
6621  char tmpdur[16];
6622  char priority[16];
6623  char origtime[16];
6624  char dir[PATH_MAX];
6625  char tmpdir[PATH_MAX];
6626  char fn[PATH_MAX];
6627  char prefile[PATH_MAX] = "";
6628  char tempfile[PATH_MAX] = "";
6629  char ext_context[256] = "";
6630  char fmt[80];
6631  char *context;
6632  char ecodes[17] = "#";
6633  struct ast_str *tmp = ast_str_create(16);
6634  char *tmpptr;
6635  struct ast_vm_user *vmu;
6636  struct ast_vm_user svm;
6637  const char *category = NULL;
6638  const char *code;
6639  const char *alldtmf = "0123456789ABCD*#";
6640  char flag[80];
6641 
6642  if (!tmp) {
6643  return -1;
6644  }
6645 
6646  ast_str_set(&tmp, 0, "%s", ext);
6647  ext = ast_str_buffer(tmp);
6648  if ((context = strchr(ext, '@'))) {
6649  *context++ = '\0';
6650  tmpptr = strchr(context, '&');
6651  } else {
6652  tmpptr = strchr(ext, '&');
6653  }
6654 
6655  if (tmpptr)
6656  *tmpptr++ = '\0';
6657 
6658  ast_channel_lock(chan);
6659  if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
6660  category = ast_strdupa(category);
6661  }
6662  ast_channel_unlock(chan);
6663 
6664  if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
6665  ast_copy_string(flag, "Urgent", sizeof(flag));
6666  } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
6667  ast_copy_string(flag, "PRIORITY", sizeof(flag));
6668  } else {
6669  flag[0] = '\0';
6670  }
6671 
6672  ast_debug(3, "Before find_user\n");
6673  memset(&svm, 0, sizeof(svm));
6674  if (!(vmu = find_user(&svm, context, ext))) {
6675  ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
6676  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
6677  ast_free(tmp);
6678  return res;
6679  }
6680 
6681  /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
6682  if (vmu->maxmsg == 0) {
6683  greeting_only = 1;
6684  ast_set_flag(options, OPT_SILENT);
6685  }
6686 
6687  /* Setup pre-file if appropriate */
6688  if (strcmp(vmu->context, "default"))
6689  snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
6690  else
6691  ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
6692 
6693  /* Set the path to the prefile. Will be one of
6694  VM_SPOOL_DIRcontext/ext/busy
6695  VM_SPOOL_DIRcontext/ext/unavail
6696  Depending on the flag set in options.
6697  */
6698  if (ast_test_flag(options, OPT_BUSY_GREETING)) {
6699  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
6700  } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
6701  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
6702  }
6703  /* Set the path to the tmpfile as
6704  VM_SPOOL_DIR/context/ext/temp
6705  and attempt to create the folder structure.
6706  */
6707  snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
6708  if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
6709  ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
6710  free_user(vmu);
6711  ast_free(tmp);
6712  return -1;
6713  }
6714  RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
6715  if (ast_fileexists(tempfile, NULL, NULL) > 0)
6716  ast_copy_string(prefile, tempfile, sizeof(prefile));
6717 
6718  DISPOSE(tempfile, -1);
6719  /* It's easier just to try to make it than to check for its existence */
6720 #ifndef IMAP_STORAGE
6721  create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
6722 #else
6723  snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
6724  if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
6725  ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
6726  }
6727 #endif
6728 
6729  /* Check current or macro-calling context for special extensions */
6730  if (ast_test_flag(vmu, VM_OPERATOR)) {
6731  if (!ast_strlen_zero(vmu->exit)) {
6732  if (ast_exists_extension(chan, vmu->exit, "o", 1,
6733  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
6734  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
6735  ouseexten = 1;
6736  }
6737  } else if (ast_exists_extension(chan, ast_channel_context(chan), "o", 1,
6738  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
6739  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
6740  ouseexten = 1;
6741  } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
6742  && ast_exists_extension(chan, ast_channel_macrocontext(chan), "o", 1,
6743  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
6744  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
6745  ousemacro = 1;
6746  }
6747  }
6748 
6749  if (!ast_strlen_zero(vmu->exit)) {
6750  if (ast_exists_extension(chan, vmu->exit, "a", 1,
6751  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
6752  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
6753  }
6754  } else if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
6755  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
6756  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
6757  } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
6758  && ast_exists_extension(chan, ast_channel_macrocontext(chan), "a", 1,
6759  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
6760  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
6761  ausemacro = 1;
6762  }
6763 
6764  if (ast_test_flag(options, OPT_DTMFEXIT)) {
6765  for (code = alldtmf; *code; code++) {
6766  char e[2] = "";
6767  e[0] = *code;
6768  if (strchr(ecodes, e[0]) == NULL
6769  && ast_canmatch_extension(chan,
6770  (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : ast_channel_context(chan)),
6771  e, 1, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
6772  strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
6773  }
6774  }
6775  }
6776 
6777  /* Play the beginning intro if desired */
6778  if (!ast_strlen_zero(prefile)) {
6779 #ifdef ODBC_STORAGE
6780  int success =
6781 #endif
6782  RETRIEVE(prefile, -1, ext, context);
6783  if (ast_fileexists(prefile, NULL, NULL) > 0) {
6784  if (ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1)
6785  res = ast_waitstream(chan, ecodes);
6786 #ifdef ODBC_STORAGE
6787  if (success == -1) {
6788  /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
6789  ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
6790  store_file(prefile, vmu->mailbox, vmu->context, -1);
6791  }
6792 #endif
6793  } else {
6794  ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
6795  res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
6796  }
6797  DISPOSE(prefile, -1);
6798  if (res < 0) {
6799  ast_debug(1, "Hang up during prefile playback\n");
6800  free_user(vmu);
6801  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
6802  ast_free(tmp);
6803  return -1;
6804  }
6805  }
6806  if (res == '#') {
6807  /* On a '#' we skip the instructions */
6808  ast_set_flag(options, OPT_SILENT);
6809  res = 0;
6810  }
6811  if (!res && !ast_test_flag(options, OPT_SILENT)) {
6812  res = ast_stream_and_wait(chan, INTRO, ecodes);
6813  if (res == '#') {
6814  ast_set_flag(options, OPT_SILENT);
6815  res = 0;
6816  }
6817  }
6818  if (res > 0)
6819  ast_stopstream(chan);
6820  /* Check for a '*' here in case the caller wants to escape from voicemail to something
6821  other than the operator -- an automated attendant or mailbox login for example */
6822  if (res == '*') {
6823  ast_channel_exten_set(chan, "a");
6824  if (!ast_strlen_zero(vmu->exit)) {
6825  ast_channel_context_set(chan, vmu->exit);
6826  } else if (ausemacro && !ast_strlen_zero(ast_channel_macrocontext(chan))) {
6828  }
6829  ast_channel_priority_set(chan, 0);
6830  free_user(vmu);
6831  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
6832  ast_free(tmp);
6833  return 0;
6834  }
6835 
6836  /* Check for a '0' here */
6837  if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
6838  transfer:
6839  if (ouseexten || ousemacro) {
6840  ast_channel_exten_set(chan, "o");
6841  if (!ast_strlen_zero(vmu->exit)) {
6842  ast_channel_context_set(chan, vmu->exit);
6843  } else if (ousemacro && !ast_strlen_zero(ast_channel_macrocontext(chan))) {
6845  }
6846  ast_play_and_wait(chan, "transfer");
6847  ast_channel_priority_set(chan, 0);
6848  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
6849  }
6850  free_user(vmu);
6851  ast_free(tmp);
6852  return OPERATOR_EXIT;
6853  }
6854 
6855  /* Allow all other digits to exit Voicemail and return to the dialplan */
6856  if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
6857  if (!ast_strlen_zero(options->exitcontext)) {
6858  ast_channel_context_set(chan, options->exitcontext);
6859  }
6860  free_user(vmu);
6861  ast_free(tmp);
6862  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
6863  return res;
6864  }
6865 
6866  if (greeting_only) {
6867  ast_debug(3, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
6868  pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
6869  res = 0;
6870  goto leave_vm_out;
6871  }
6872 
6873  if (res < 0) {
6874  free_user(vmu);
6875  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
6876  ast_free(tmp);
6877  return -1;
6878  }
6879  /* The meat of recording the message... All the announcements and beeps have been played*/
6880  if (ast_channel_state(chan) != AST_STATE_UP) {
6881  ast_answer(chan);
6882  }
6883  ast_copy_string(fmt, vmfmts, sizeof(fmt));
6884  if (!ast_strlen_zero(fmt)) {
6885  char msg_id[MSG_ID_LEN] = "";
6886  msgnum = 0;
6887 
6888 #ifdef IMAP_STORAGE
6889  /* Is ext a mailbox? */
6890  /* must open stream for this user to get info! */
6891  res = inboxcount(ext_context, &newmsgs, &oldmsgs);
6892  if (res < 0) {
6893  ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
6894  free_user(vmu);
6895  ast_free(tmp);
6896  return -1;
6897  }
6898  if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
6899  /* It is possible under certain circumstances that inboxcount did not
6900  * create a vm_state when it was needed. This is a catchall which will
6901  * rarely be used.
6902  */
6903  if (!(vms = create_vm_state_from_user(vmu))) {
6904  ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
6905  free_user(vmu);
6906  ast_free(tmp);
6907  return -1;
6908  }
6909  }
6910  vms->newmessages++;
6911 
6912  /* here is a big difference! We add one to it later */
6913  msgnum = newmsgs + oldmsgs;
6914  ast_debug(3, "Messagecount set to %d\n", msgnum);
6915  snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
6916  /* set variable for compatibility */
6917  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
6918 
6919  if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
6920  goto leave_vm_out;
6921  }
6922 #else
6923  if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
6924  res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
6925  if (!res)
6926  res = ast_waitstream(chan, "");
6927  ast_log(AST_LOG_WARNING, "No more messages possible\n");
6928  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
6929  inprocess_count(vmu->mailbox, vmu->context, -1);
6930  goto leave_vm_out;
6931  }
6932 
6933 #endif
6934  snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
6935  txtdes = mkstemp(tmptxtfile);
6936  chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
6937  if (txtdes < 0) {
6938  res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
6939  if (!res)
6940  res = ast_waitstream(chan, "");
6941  ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
6942  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
6943  inprocess_count(vmu->mailbox, vmu->context, -1);
6944  goto leave_vm_out;
6945  }
6946 
6947  /* Now play the beep once we have the message number for our next message. */
6948  if (res >= 0) {
6949  /* Unless we're *really* silent, try to send the beep */
6950  /* Play default or custom beep, unless no beep desired */
6951  if (!ast_strlen_zero(options->beeptone)) {
6952  res = ast_stream_and_wait(chan, options->beeptone, "");
6953  }
6954  }
6955 
6956  /* Store information in real-time storage */
6957  if (ast_check_realtime("voicemail_data")) {
6958  snprintf(priority, sizeof(priority), "%d", ast_channel_priority(chan));
6959  snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
6960  get_date(date, sizeof(date));
6961  ast_callerid_merge(callerid, sizeof(callerid),
6962  S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
6963  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
6964  "Unknown");
6965  ast_store_realtime("voicemail_data",
6966  "origmailbox", ext,
6967  "context", ast_channel_context(chan),
6968  "macrocontext", ast_channel_macrocontext(chan),
6969  "exten", ast_channel_exten(chan),
6970  "priority", priority,
6971  "callerchan", ast_channel_name(chan),
6972  "callerid", callerid,
6973  "origdate", date,
6974  "origtime", origtime,
6975  "category", S_OR(category, ""),
6976  "filename", tmptxtfile,
6977  SENTINEL);
6978  }
6979 
6980  /* Store information */
6981  txt = fdopen(txtdes, "w+");
6982  if (txt) {
6983  generate_msg_id(msg_id);
6984  get_date(date, sizeof(date));
6985  ast_callerid_merge(callerid, sizeof(callerid),
6986  S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
6987  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
6988  "Unknown");
6989  fprintf(txt,
6990  ";\n"
6991  "; Message Information file\n"
6992  ";\n"
6993  "[message]\n"
6994  "origmailbox=%s\n"
6995  "context=%s\n"
6996  "macrocontext=%s\n"
6997  "exten=%s\n"
6998  "rdnis=%s\n"
6999  "priority=%d\n"
7000  "callerchan=%s\n"
7001  "callerid=%s\n"
7002  "origdate=%s\n"
7003  "origtime=%ld\n"
7004  "category=%s\n"
7005  "msg_id=%s\n",
7006  ext,
7007  ast_channel_context(chan),
7009  ast_channel_exten(chan),
7010  S_COR(ast_channel_redirecting(chan)->from.number.valid,
7011  ast_channel_redirecting(chan)->from.number.str, "unknown"),
7012  ast_channel_priority(chan),
7013  ast_channel_name(chan),
7014  callerid,
7015  date, (long) time(NULL),
7016  category ? category : "",
7017  msg_id);
7018  } else {
7019  ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
7020  inprocess_count(vmu->mailbox, vmu->context, -1);
7021  if (ast_check_realtime("voicemail_data")) {
7022  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7023  }
7024  res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
7025  goto leave_vm_out;
7026  }
7027  res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag, msg_id, 0);
7028 
7029  /* At this point, either we were instructed to make the message Urgent
7030  by arguments to VoiceMail or during the review process by the person
7031  leaving the message. So we update the directory where we want this
7032  message to go. */
7033  if (!strcmp(flag, "Urgent")) {
7034  create_dirpath(dir, sizeof(dir), vmu->context, ext, "Urgent");
7035  }
7036 
7037  if (txt) {
7038  fprintf(txt, "flag=%s\n", flag);
7039  if (sound_duration < vmu->minsecs) {
7040  fclose(txt);
7041  ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
7042  ast_filedelete(tmptxtfile, NULL);
7043  unlink(tmptxtfile);
7044  if (ast_check_realtime("voicemail_data")) {
7045  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7046  }
7047  inprocess_count(vmu->mailbox, vmu->context, -1);
7048  } else {
7049  fprintf(txt, "duration=%d\n", duration);
7050  fclose(txt);
7051  if (vm_lock_path(dir)) {
7052  ast_log(AST_LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
7053  /* Delete files */
7054  ast_filedelete(tmptxtfile, NULL);
7055  unlink(tmptxtfile);
7056  inprocess_count(vmu->mailbox, vmu->context, -1);
7057  } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
7058  ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
7059  unlink(tmptxtfile);
7060  ast_unlock_path(dir);
7061  inprocess_count(vmu->mailbox, vmu->context, -1);
7062  if (ast_check_realtime("voicemail_data")) {
7063  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7064  }
7065  } else {
7066 #ifndef IMAP_STORAGE
7067  msgnum = last_message_index(vmu, dir) + 1;
7068 #endif
7069  make_file(fn, sizeof(fn), dir, msgnum);
7070 
7071  /* assign a variable with the name of the voicemail file */
7072 #ifndef IMAP_STORAGE
7073  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
7074 #else
7075  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
7076 #endif
7077 
7078  snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
7079  ast_filerename(tmptxtfile, fn, NULL);
7080  rename(tmptxtfile, txtfile);
7081  inprocess_count(vmu->mailbox, vmu->context, -1);
7082 
7083  /* Properly set permissions on voicemail text descriptor file.
7084  Unfortunately mkstemp() makes this file 0600 on most unix systems. */
7085  if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
7086  ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
7087 
7088  ast_unlock_path(dir);
7089  if (ast_check_realtime("voicemail_data")) {
7090  snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
7091  ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
7092  }
7093  /* We must store the file first, before copying the message, because
7094  * ODBC storage does the entire copy with SQL.
7095  */
7096  if (ast_fileexists(fn, NULL, NULL) > 0) {
7097  STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag, msg_id);
7098  }
7099 
7100  /* Are there to be more recipients of this message? */
7101  while (tmpptr) {
7102  struct ast_vm_user recipu, *recip;
7103  char *exten, *cntx;
7104 
7105  exten = strsep(&tmpptr, "&");
7106  cntx = strchr(exten, '@');
7107  if (cntx) {
7108  *cntx = '\0';
7109  cntx++;
7110  }
7111  memset(&recipu, 0, sizeof(recipu));
7112  if ((recip = find_user(&recipu, cntx, exten))) {
7113  copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag, NULL);
7114  free_user(recip);
7115  }
7116  }
7117 
7118  /* Notification needs to happen after the copy, though. */
7119  if (ast_fileexists(fn, NULL, NULL)) {
7120 #ifdef IMAP_STORAGE
7121  notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
7122  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7123  S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7124  flag);
7125 #else
7126  notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
7127  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7128  S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7129  flag);
7130 #endif
7131  }
7132 
7133  /* Disposal needs to happen after the optional move and copy */
7134  if (ast_fileexists(fn, NULL, NULL)) {
7135  DISPOSE(dir, msgnum);
7136  }
7137  }
7138  }
7139  } else {
7140  inprocess_count(vmu->mailbox, vmu->context, -1);
7141  }
7142  if (res == '0') {
7143  goto transfer;
7144  } else if (res > 0 && res != 't')
7145  res = 0;
7146 
7147  if (sound_duration < vmu->minsecs)
7148  /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
7149  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7150  else
7151  pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
7152  } else
7153  ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
7154 leave_vm_out:
7155  free_user(vmu);
7156 
7157 #ifdef IMAP_STORAGE
7158  /* expunge message - use UID Expunge if supported on IMAP server*/
7159  ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
7160  if (expungeonhangup == 1 && vms->mailstream != NULL) {
7161  ast_mutex_lock(&vms->lock);
7162 #ifdef HAVE_IMAP_TK2006
7163  if (LEVELUIDPLUS (vms->mailstream)) {
7164  mail_expunge_full(vms->mailstream, NIL, EX_UID);
7165  } else
7166 #endif
7167  mail_expunge(vms->mailstream);
7168  ast_mutex_unlock(&vms->lock);
7169  }
7170 #endif
7171 
7172  ast_free(tmp);
7173  return res;
7174 }
7175 
7176 #if !defined(IMAP_STORAGE)
7177 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
7178 {
7179  /* we know the actual number of messages, so stop process when number is hit */
7180 
7181  int x, dest;
7182  char sfn[PATH_MAX];
7183  char dfn[PATH_MAX];
7184 
7185  if (vm_lock_path(dir)) {
7186  return ERROR_LOCK_PATH;
7187  }
7188 
7189  for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
7190  make_file(sfn, sizeof(sfn), dir, x);
7191  if (EXISTS(dir, x, sfn, NULL)) {
7192 
7193  if (x != dest) {
7194  make_file(dfn, sizeof(dfn), dir, dest);
7195  RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
7196  }
7197 
7198  dest++;
7199  }
7200  }
7201  ast_unlock_path(dir);
7202 
7203  return dest;
7204 }
7205 #endif
7206 
7207 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
7208 {
7209  int d;
7210  d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
7211  return d;
7212 }
7213 
7214 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move)
7215 {
7216 #ifdef IMAP_STORAGE
7217  /* we must use mbox(x) folder names, and copy the message there */
7218  /* simple. huh? */
7219  char sequence[10];
7220  char mailbox[256];
7221  int res;
7222  int curr_mbox;
7223 
7224  /* get the real IMAP message number for this message */
7225  snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
7226 
7227  ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
7228  ast_mutex_lock(&vms->lock);
7229  /* if save to Old folder, put in INBOX as read */
7230  if (box == OLD_FOLDER) {
7231  mail_setflag(vms->mailstream, sequence, "\\Seen");
7232  } else if (box == NEW_FOLDER) {
7233  mail_clearflag(vms->mailstream, sequence, "\\Seen");
7234  }
7235  if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
7236  ast_mutex_unlock(&vms->lock);
7237  return 0;
7238  }
7239 
7240  /* get the current mailbox so that we can point the mailstream back to it later */
7241  curr_mbox = get_folder_by_name(vms->curbox);
7242 
7243  /* Create the folder if it dosn't exist */
7244  imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
7245  if (vms->mailstream && !mail_status(vms->mailstream, mailbox, SA_UIDNEXT)) {
7246  if (mail_create(vms->mailstream, mailbox) != NIL) {
7247  ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
7248  }
7249  }
7250 
7251  /* restore previous mbox stream */
7252  if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
7253  ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
7254  res = -1;
7255  } else {
7256  if (move) {
7257  res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
7258  } else {
7259  res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
7260  }
7261  }
7262  ast_mutex_unlock(&vms->lock);
7263  return res;
7264 #else
7265  char *dir = vms->curdir;
7266  char *username = vms->username;
7267  char *context = vmu->context;
7268  char sfn[PATH_MAX];
7269  char dfn[PATH_MAX];
7270  char ddir[PATH_MAX];
7271  const char *dbox = mbox(vmu, box);
7272  int x, i;
7273  create_dirpath(ddir, sizeof(ddir), context, username, dbox);
7274 
7275  if (vm_lock_path(ddir))
7276  return ERROR_LOCK_PATH;
7277 
7278  x = last_message_index(vmu, ddir) + 1;
7279 
7280  if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
7281  x--;
7282  for (i = 1; i <= x; i++) {
7283  /* Push files down a "slot". The oldest file (msg0000) will be deleted. */
7284  make_file(sfn, sizeof(sfn), ddir, i);
7285  make_file(dfn, sizeof(dfn), ddir, i - 1);
7286  if (EXISTS(ddir, i, sfn, NULL)) {
7287  RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
7288  } else
7289  break;
7290  }
7291  } else {
7292  if (x >= vmu->maxmsg) {
7293  ast_unlock_path(ddir);
7294  return ERROR_MAX_MSGS;
7295  }
7296  }
7297  make_file(sfn, sizeof(sfn), dir, msg);
7298  make_file(dfn, sizeof(dfn), ddir, x);
7299  if (strcmp(sfn, dfn)) {
7300  COPY(dir, msg, ddir, x, username, context, sfn, dfn);
7301  }
7302  ast_unlock_path(ddir);
7303 
7304  if (newmsg) {
7305  *newmsg = x;
7306  }
7307  return 0;
7308 #endif
7309 }
7310 
7311 static int adsi_logo(unsigned char *buf)
7312 {
7313  int bytes = 0;
7314  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
7315  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
7316  return bytes;
7317 }
7318 
7319 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
7320 {
7321  unsigned char buf[256];
7322  int bytes = 0;
7323  int x;
7324  char num[5];
7325 
7326  *useadsi = 0;
7327  bytes += ast_adsi_data_mode(buf + bytes);
7328  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7329 
7330  bytes = 0;
7331  bytes += adsi_logo(buf);
7332  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
7333 #ifdef DISPLAY
7334  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
7335 #endif
7336  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7337  bytes += ast_adsi_data_mode(buf + bytes);
7338  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7339 
7340  if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
7341  bytes = 0;
7342  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
7343  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
7344  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7345  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7346  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7347  return 0;
7348  }
7349 
7350 #ifdef DISPLAY
7351  /* Add a dot */
7352  bytes = 0;
7353  bytes += ast_adsi_logo(buf);
7354  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
7355  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
7356  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7357  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7358 #endif
7359  bytes = 0;
7360  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
7361  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
7362  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
7363  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
7364  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
7365  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
7366  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
7367 
7368 #ifdef DISPLAY
7369  /* Add another dot */
7370  bytes = 0;
7371  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
7372  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7373 
7374  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7375  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7376 #endif
7377 
7378  bytes = 0;
7379  /* These buttons we load but don't use yet */
7380  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
7381  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
7382  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
7383  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
7384  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
7385  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
7386  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
7387 
7388 #ifdef DISPLAY
7389  /* Add another dot */
7390  bytes = 0;
7391  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
7392  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7393  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7394 #endif
7395 
7396  bytes = 0;
7397  for (x = 0; x < 5; x++) {
7398  snprintf(num, sizeof(num), "%d", x);
7399  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
7400  }
7401  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
7402  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
7403 
7404 #ifdef DISPLAY
7405  /* Add another dot */
7406  bytes = 0;
7407  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
7408  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7409  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7410 #endif
7411 
7412  if (ast_adsi_end_download(chan)) {
7413  bytes = 0;
7414  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
7415  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
7416  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7417  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7418  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7419  return 0;
7420  }
7421  bytes = 0;
7422  bytes += ast_adsi_download_disconnect(buf + bytes);
7423  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7424  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
7425 
7426  ast_debug(1, "Done downloading scripts...\n");
7427 
7428 #ifdef DISPLAY
7429  /* Add last dot */
7430  bytes = 0;
7431  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
7432  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7433 #endif
7434  ast_debug(1, "Restarting session...\n");
7435 
7436  bytes = 0;
7437  /* Load the session now */
7438  if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
7439  *useadsi = 1;
7440  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
7441  } else
7442  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
7443 
7444  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7445  return 0;
7446 }
7447 
7448 static void adsi_begin(struct ast_channel *chan, int *useadsi)
7449 {
7450  int x;
7451  if (!ast_adsi_available(chan))
7452  return;
7453  x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
7454  if (x < 0)
7455  return;
7456  if (!x) {
7457  if (adsi_load_vmail(chan, useadsi)) {
7458  ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
7459  return;
7460  }
7461  } else
7462  *useadsi = 1;
7463 }
7464 
7465 static void adsi_login(struct ast_channel *chan)
7466 {
7467  unsigned char buf[256];
7468  int bytes = 0;
7469  unsigned char keys[8];
7470  int x;
7471  if (!ast_adsi_available(chan))
7472  return;
7473 
7474  for (x = 0; x < 8; x++)
7475  keys[x] = 0;
7476  /* Set one key for next */
7477  keys[3] = ADSI_KEY_APPS + 3;
7478 
7479  bytes += adsi_logo(buf + bytes);
7480  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
7481  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
7482  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7483  bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
7484  bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
7485  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
7486  bytes += ast_adsi_set_keys(buf + bytes, keys);
7487  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7488  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7489 }
7490 
7491 static void adsi_password(struct ast_channel *chan)
7492 {
7493  unsigned char buf[256];
7494  int bytes = 0;
7495  unsigned char keys[8];
7496  int x;
7497  if (!ast_adsi_available(chan))
7498  return;
7499 
7500  for (x = 0; x < 8; x++)
7501  keys[x] = 0;
7502  /* Set one key for next */
7503  keys[3] = ADSI_KEY_APPS + 3;
7504 
7505  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7506  bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
7507  bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
7508  bytes += ast_adsi_set_keys(buf + bytes, keys);
7509  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7510  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7511 }
7512 
7513 static void adsi_folders(struct ast_channel *chan, int start, char *label)
7514 {
7515  unsigned char buf[256];
7516  int bytes = 0;
7517  unsigned char keys[8];
7518  int x, y;
7519 
7520  if (!ast_adsi_available(chan))
7521  return;
7522 
7523  for (x = 0; x < 5; x++) {
7524  y = ADSI_KEY_APPS + 12 + start + x;
7525  if (y > ADSI_KEY_APPS + 12 + 4)
7526  y = 0;
7527  keys[x] = ADSI_KEY_SKT | y;
7528  }
7529  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
7530  keys[6] = 0;
7531  keys[7] = 0;
7532 
7533  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
7534  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
7535  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7536  bytes += ast_adsi_set_keys(buf + bytes, keys);
7537  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7538 
7539  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7540 }
7541 
7542 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
7543 {
7544  int bytes = 0;
7545  unsigned char buf[256];
7546  char buf1[256], buf2[256];
7547  char fn2[PATH_MAX];
7548 
7549  char cid[256] = "";
7550  char *val;
7551  char *name, *num;
7552  char datetime[21] = "";
7553  FILE *f;
7554 
7555  unsigned char keys[8];
7556 
7557  int x;
7558 
7559  if (!ast_adsi_available(chan))
7560  return;
7561 
7562  /* Retrieve important info */
7563  snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
7564  f = fopen(fn2, "r");
7565  if (f) {
7566  while (!feof(f)) {
7567  if (!fgets((char *) buf, sizeof(buf), f)) {
7568  continue;
7569  }
7570  if (!feof(f)) {
7571  char *stringp = NULL;
7572  stringp = (char *) buf;
7573  strsep(&stringp, "=");
7574  val = strsep(&stringp, "=");
7575  if (!ast_strlen_zero(val)) {
7576  if (!strcmp((char *) buf, "callerid"))
7577  ast_copy_string(cid, val, sizeof(cid));
7578  if (!strcmp((char *) buf, "origdate"))
7579  ast_copy_string(datetime, val, sizeof(datetime));
7580  }
7581  }
7582  }
7583  fclose(f);
7584  }
7585  /* New meaning for keys */
7586  for (x = 0; x < 5; x++)
7587  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
7588  keys[6] = 0x0;
7589  keys[7] = 0x0;
7590 
7591  if (!vms->curmsg) {
7592  /* No prev key, provide "Folder" instead */
7593  keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
7594  }
7595  if (vms->curmsg >= vms->lastmsg) {
7596  /* If last message ... */
7597  if (vms->curmsg) {
7598  /* but not only message, provide "Folder" instead */
7599  keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
7600  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7601 
7602  } else {
7603  /* Otherwise if only message, leave blank */
7604  keys[3] = 1;
7605  }
7606  }
7607 
7608  if (!ast_strlen_zero(cid)) {
7609  ast_callerid_parse(cid, &name, &num);
7610  if (!name)
7611  name = num;
7612  } else {
7613  name = "Unknown Caller";
7614  }
7615 
7616  /* If deleted, show "undeleted" */
7617 #ifdef IMAP_STORAGE
7618  ast_mutex_lock(&vms->lock);
7619 #endif
7620  if (vms->deleted[vms->curmsg]) {
7621  keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
7622  }
7623 #ifdef IMAP_STORAGE
7624  ast_mutex_unlock(&vms->lock);
7625 #endif
7626 
7627  /* Except "Exit" */
7628  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
7629  snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
7630  strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
7631  snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
7632 
7633  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
7634  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
7635  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
7636  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
7637  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7638  bytes += ast_adsi_set_keys(buf + bytes, keys);
7639  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7640 
7641  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7642 }
7643 
7644 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
7645 {
7646  int bytes = 0;
7647  unsigned char buf[256];
7648  unsigned char keys[8];
7649 
7650  int x;
7651 
7652  if (!ast_adsi_available(chan))
7653  return;
7654 
7655  /* New meaning for keys */
7656  for (x = 0; x < 5; x++)
7657  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
7658 
7659  keys[6] = 0x0;
7660  keys[7] = 0x0;
7661 
7662  if (!vms->curmsg) {
7663  /* No prev key, provide "Folder" instead */
7664  keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
7665  }
7666  if (vms->curmsg >= vms->lastmsg) {
7667  /* If last message ... */
7668  if (vms->curmsg) {
7669  /* but not only message, provide "Folder" instead */
7670  keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
7671  } else {
7672  /* Otherwise if only message, leave blank */
7673  keys[3] = 1;
7674  }
7675  }
7676 
7677  /* If deleted, show "undeleted" */
7678 #ifdef IMAP_STORAGE
7679  ast_mutex_lock(&vms->lock);
7680 #endif
7681  if (vms->deleted[vms->curmsg]) {
7682  keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
7683  }
7684 #ifdef IMAP_STORAGE
7685  ast_mutex_unlock(&vms->lock);
7686 #endif
7687 
7688  /* Except "Exit" */
7689  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
7690  bytes += ast_adsi_set_keys(buf + bytes, keys);
7691  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7692 
7693  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7694 }
7695 
7696 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
7697 {
7698  unsigned char buf[256] = "";
7699  char buf1[256] = "", buf2[256] = "";
7700  int bytes = 0;
7701  unsigned char keys[8];
7702  int x;
7703 
7704  char *newm = (vms->newmessages == 1) ? "message" : "messages";
7705  char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
7706  if (!ast_adsi_available(chan))
7707  return;
7708  if (vms->newmessages) {
7709  snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
7710  if (vms->oldmessages) {
7711  strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
7712  snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
7713  } else {
7714  snprintf(buf2, sizeof(buf2), "%s.", newm);
7715  }
7716  } else if (vms->oldmessages) {
7717  snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
7718  snprintf(buf2, sizeof(buf2), "%s.", oldm);
7719  } else {
7720  strcpy(buf1, "You have no messages.");
7721  buf2[0] = ' ';
7722  buf2[1] = '\0';
7723  }
7724  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
7725  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
7726  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7727 
7728  for (x = 0; x < 6; x++)
7729  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
7730  keys[6] = 0;
7731  keys[7] = 0;
7732 
7733  /* Don't let them listen if there are none */
7734  if (vms->lastmsg < 0)
7735  keys[0] = 1;
7736  bytes += ast_adsi_set_keys(buf + bytes, keys);
7737 
7738  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7739 
7740  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7741 }
7742 
7743 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
7744 {
7745  unsigned char buf[256] = "";
7746  char buf1[256] = "", buf2[256] = "";
7747  int bytes = 0;
7748  unsigned char keys[8];
7749  int x;
7750 
7751  char *mess = (vms->lastmsg == 0) ? "message" : "messages";
7752 
7753  if (!ast_adsi_available(chan))
7754  return;
7755 
7756  /* Original command keys */
7757  for (x = 0; x < 6; x++)
7758  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
7759 
7760  keys[6] = 0;
7761  keys[7] = 0;
7762 
7763  if ((vms->lastmsg + 1) < 1)
7764  keys[0] = 0;
7765 
7766  snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
7767  strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
7768 
7769  if (vms->lastmsg + 1)
7770  snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
7771  else
7772  strcpy(buf2, "no messages.");
7773  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
7774  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
7775  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
7776  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7777  bytes += ast_adsi_set_keys(buf + bytes, keys);
7778 
7779  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7780 
7781  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7782 
7783 }
7784 
7785 /*
7786 static void adsi_clear(struct ast_channel *chan)
7787 {
7788  char buf[256];
7789  int bytes=0;
7790  if (!ast_adsi_available(chan))
7791  return;
7792  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7793  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7794 
7795  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7796 }
7797 */
7798 
7799 static void adsi_goodbye(struct ast_channel *chan)
7800 {
7801  unsigned char buf[256];
7802  int bytes = 0;
7803 
7804  if (!ast_adsi_available(chan))
7805  return;
7806  bytes += adsi_logo(buf + bytes);
7807  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
7808  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
7809  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7810  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7811 
7812  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7813 }
7814 
7815 /*!\brief get_folder: Folder menu
7816  * Plays "press 1 for INBOX messages" etc.
7817  * Should possibly be internationalized
7818  */
7819 static int get_folder(struct ast_channel *chan, int start)
7820 {
7821  int x;
7822  int d;
7823  char fn[PATH_MAX];
7824  d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
7825  if (d)
7826  return d;
7827  for (x = start; x < 5; x++) { /* For all folders */
7828  if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), NULL)))
7829  return d;
7830  d = ast_play_and_wait(chan, "vm-for"); /* "for" */
7831  if (d)
7832  return d;
7833  snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
7834 
7835  /* The inbox folder can have its name changed under certain conditions
7836  * so this checks if the sound file exists for the inbox folder name and
7837  * if it doesn't, plays the default name instead. */
7838  if (x == 0) {
7839  if (ast_fileexists(fn, NULL, NULL)) {
7840  d = vm_play_folder_name(chan, fn);
7841  } else {
7842  ast_verb(4, "Failed to find file %s; falling back to INBOX\n", fn);
7843  d = vm_play_folder_name(chan, "vm-INBOX");
7844  }
7845  } else {
7846  ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
7847  d = vm_play_folder_name(chan, fn);
7848  }
7849 
7850  if (d)
7851  return d;
7852  d = ast_waitfordigit(chan, 500);
7853  if (d)
7854  return d;
7855  }
7856 
7857  d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
7858  if (d)
7859  return d;
7860  d = ast_waitfordigit(chan, 4000);
7861  return d;
7862 }
7863 
7864 /* Japanese Syntax */
7865 static int get_folder_ja(struct ast_channel *chan, int start)
7866 {
7867  int x;
7868  int d;
7869  char fn[256];
7870  for (x = start; x < 5; x++) { /* For all folders */
7871  if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL))) {
7872  return d;
7873  }
7874  snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
7875  d = vm_play_folder_name(chan, fn);
7876  if (d) {
7877  return d;
7878  }
7879  d = ast_waitfordigit(chan, 500);
7880  if (d) {
7881  return d;
7882  }
7883  }
7884  d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
7885  if (d) {
7886  return d;
7887  }
7888  d = ast_waitfordigit(chan, 4000);
7889  return d;
7890 }
7891 
7892 /*!
7893  * \brief plays a prompt and waits for a keypress.
7894  * \param chan
7895  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
7896  * \param start Does not appear to be used at this time.
7897  *
7898  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
7899  * After playing the message identified by the fn parameter value, it calls get_folder(), which plays the
7900  * prompting for the number inputs that correspond to the available folders.
7901  *
7902  * \return zero on success, or -1 on error.
7903  */
7904 static int get_folder2(struct ast_channel *chan, char *fn, int start)
7905 {
7906  int res = 0;
7907  int loops = 0;
7908 
7909  res = ast_play_and_wait(chan, fn); /* Folder name */
7910  while (((res < '0') || (res > '9')) &&
7911  (res != '#') && (res >= 0) &&
7912  loops < 4) {
7913  /* res = get_folder(chan, 0); */
7914  if (!strcasecmp(ast_channel_language(chan), "ja")) { /* Japanese syntax */
7915  res = get_folder_ja(chan, 0);
7916  } else { /* Default syntax */
7917  res = get_folder(chan, 0);
7918  }
7919  loops++;
7920  }
7921  if (loops == 4) { /* give up */
7922  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
7923  return '#';
7924  }
7925  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
7926  isprint(res) ? res : '?', isprint(res) ? res : '?');
7927  return res;
7928 }
7929 
7930 /*!
7931  * \brief presents the option to prepend to an existing message when forwarding it.
7932  * \param chan
7933  * \param vmu
7934  * \param curdir
7935  * \param curmsg
7936  * \param vm_fmts
7937  * \param context
7938  * \param record_gain
7939  * \param duration
7940  * \param vms
7941  * \param flag
7942  *
7943  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
7944  *
7945  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
7946  * \return zero on success, -1 on error.
7947  */
7948 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
7949  char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
7950 {
7951  int cmd = 0;
7952  int retries = 0, prepend_duration = 0, already_recorded = 0;
7953  char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
7954  char textfile[PATH_MAX];
7955  struct ast_config *msg_cfg;
7956  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
7957 #ifndef IMAP_STORAGE
7958  signed char zero_gain = 0;
7959 #else
7960  const char *msg_id = NULL;
7961 #endif
7962  const char *duration_str;
7963 
7964  /* Must always populate duration correctly */
7965  make_file(msgfile, sizeof(msgfile), curdir, curmsg);
7966  strcpy(textfile, msgfile);
7967  strcpy(backup, msgfile);
7968  strcpy(backup_textfile, msgfile);
7969  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
7970  strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
7971  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
7972 
7973  if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
7974  *duration = atoi(duration_str);
7975  } else {
7976  *duration = 0;
7977  }
7978 
7979  while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
7980  if (cmd)
7981  retries = 0;
7982  switch (cmd) {
7983  case '1':
7984 
7985 #ifdef IMAP_STORAGE
7986  /* Record new intro file */
7987  if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
7988  msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
7989  }
7990  make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
7991  strncat(vms->introfn, "intro", sizeof(vms->introfn));
7992  ast_play_and_wait(chan, "vm-record-prepend");
7993  ast_play_and_wait(chan, "beep");
7994  cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag, msg_id, 1);
7995  if (cmd == -1) {
7996  break;
7997  }
7998  cmd = 't';
7999 #else
8000 
8001  /* prepend a message to the current message, update the metadata and return */
8002 
8003  make_file(msgfile, sizeof(msgfile), curdir, curmsg);
8004  strcpy(textfile, msgfile);
8005  strncat(textfile, ".txt", sizeof(textfile) - 1);
8006  *duration = 0;
8007 
8008  /* if we can't read the message metadata, stop now */
8009  if (!valid_config(msg_cfg)) {
8010  cmd = 0;
8011  break;
8012  }
8013 
8014  /* Back up the original file, so we can retry the prepend and restore it after forward. */
8015 #ifndef IMAP_STORAGE
8016  if (already_recorded) {
8017  ast_filecopy(backup, msgfile, NULL);
8018  copy(backup_textfile, textfile);
8019  }
8020  else {
8021  ast_filecopy(msgfile, backup, NULL);
8022  copy(textfile, backup_textfile);
8023  }
8024 #endif
8025  already_recorded = 1;
8026 
8027  if (record_gain)
8028  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
8029 
8030  cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
8031 
8032  if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
8033  ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
8034  ast_stream_and_wait(chan, vm_prepend_timeout, "");
8035  ast_filerename(backup, msgfile, NULL);
8036  }
8037 
8038  if (record_gain)
8039  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
8040 
8041 
8042  if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
8043  *duration = atoi(duration_str);
8044 
8045  if (prepend_duration) {
8046  struct ast_category *msg_cat;
8047  /* need enough space for a maximum-length message duration */
8048  char duration_buf[12];
8049 
8050  *duration += prepend_duration;
8051  msg_cat = ast_category_get(msg_cfg, "message", NULL);
8052  snprintf(duration_buf, sizeof(duration_buf), "%ld", *duration);
8053  if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
8054  ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
8055  }
8056  }
8057 
8058 #endif
8059  break;
8060  case '2':
8061  /* NULL out introfile so we know there is no intro! */
8062 #ifdef IMAP_STORAGE
8063  *vms->introfn = '\0';
8064 #endif
8065  cmd = 't';
8066  break;
8067  case '*':
8068  cmd = '*';
8069  break;
8070  default:
8071  /* If time_out and return to menu, reset already_recorded */
8072  already_recorded = 0;
8073 
8074  cmd = ast_play_and_wait(chan, "vm-forwardoptions");
8075  /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
8076  if (!cmd) {
8077  cmd = ast_play_and_wait(chan, "vm-starmain");
8078  /* "press star to return to the main menu" */
8079  }
8080  if (!cmd) {
8081  cmd = ast_waitfordigit(chan, 6000);
8082  }
8083  if (!cmd) {
8084  retries++;
8085  }
8086  if (retries > 3) {
8087  cmd = '*'; /* Let's cancel this beast */
8088  }
8089  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8090  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
8091  }
8092  }
8093 
8094  if (valid_config(msg_cfg))
8095  ast_config_destroy(msg_cfg);
8096  if (prepend_duration)
8097  *duration = prepend_duration;
8098 
8099  if (already_recorded && cmd == -1) {
8100  /* restore original message if prepention cancelled */
8101  ast_filerename(backup, msgfile, NULL);
8102  rename(backup_textfile, textfile);
8103  }
8104 
8105  if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
8106  cmd = 0;
8107  return cmd;
8108 }
8109 
8110 static void queue_mwi_event(const char *channel_id, const char *box, int urgent, int new, int old)
8111 {
8112  char *mailbox;
8113  char *context;
8114 
8115  if (separate_mailbox(ast_strdupa(box), &mailbox, &context)) {
8116  return;
8117  }
8118 
8119  ast_debug(3, "Queueing event for mailbox %s New: %d Old: %d\n", box, new + urgent, old);
8120  ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
8121 
8122  if (!ast_strlen_zero(aliasescontext)) {
8123  struct ao2_iterator *aliases;
8124  struct mailbox_alias_mapping *mapping;
8125 
8126  aliases = ao2_find(mailbox_alias_mappings, box, OBJ_SEARCH_KEY | OBJ_MULTIPLE);
8127  while ((mapping = ao2_iterator_next(aliases))) {
8128  char alias[strlen(mapping->alias) + 1];
8129  strcpy(alias, mapping->alias); /* safe */
8130  mailbox = NULL;
8131  context = NULL;
8132  ast_debug(3, "Found alias mapping: %s -> %s\n", mapping->alias, box);
8133  separate_mailbox(alias, &mailbox, &context);
8134  ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
8135  ao2_ref(mapping, -1);
8136  }
8137  ao2_iterator_destroy(aliases);
8138  }
8139 }
8140 
8141 /*!
8142  * \brief Sends email notification that a user has a new voicemail waiting for them.
8143  * \param chan
8144  * \param vmu
8145  * \param vms
8146  * \param msgnum
8147  * \param duration
8148  * \param fmt
8149  * \param cidnum The Caller ID phone number value.
8150  * \param cidname The Caller ID name value.
8151  * \param flag
8152  *
8153  * \return zero on success, -1 on error.
8154  */
8155 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)
8156 {
8157  char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
8158  int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
8159  const char *category;
8160  char *myserveremail = serveremail;
8161 
8162  ast_channel_lock(chan);
8163  if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
8164  category = ast_strdupa(category);
8165  }
8166  ast_channel_unlock(chan);
8167 
8168 #ifndef IMAP_STORAGE
8169  make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
8170 #else
8171  snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
8172 #endif
8173  make_file(fn, sizeof(fn), todir, msgnum);
8174  snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
8175 
8176  if (!ast_strlen_zero(vmu->attachfmt)) {
8177  if (strstr(fmt, vmu->attachfmt))
8178  fmt = vmu->attachfmt;
8179  else
8180  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);
8181  }
8182 
8183  /* Attach only the first format */
8184  fmt = ast_strdupa(fmt);
8185  stringp = fmt;
8186  strsep(&stringp, "|");
8187 
8188  if (!ast_strlen_zero(vmu->serveremail))
8189  myserveremail = vmu->serveremail;
8190 
8191  if (!ast_strlen_zero(vmu->email)) {
8192  int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
8193  char *msg_id = NULL;
8194 #ifdef IMAP_STORAGE
8195  struct ast_config *msg_cfg;
8196  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8197  char filename[PATH_MAX];
8198 
8199  snprintf(filename, sizeof(filename), "%s.txt", fn);
8200  msg_cfg = ast_config_load(filename, config_flags);
8201  if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
8202  msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
8203  ast_config_destroy(msg_cfg);
8204  }
8205 #endif
8206 
8207  if (attach_user_voicemail)
8208  RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
8209 
8210  /* XXX possible imap issue, should category be NULL XXX */
8211  sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag, msg_id);
8212 
8213  if (attach_user_voicemail)
8214  DISPOSE(todir, msgnum);
8215  }
8216 
8217  if (!ast_strlen_zero(vmu->pager)) {
8218  sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
8219  }
8220 
8221  if (ast_test_flag(vmu, VM_DELETE))
8222  DELETE(todir, msgnum, fn, vmu);
8223 
8224  /* Leave voicemail for someone */
8225  if (ast_app_has_voicemail(ext_context, NULL))
8226  ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
8227 
8228  queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgentmsgs, newmsgs, oldmsgs);
8229  run_externnotify(vmu->context, vmu->mailbox, flag);
8230 
8231 #ifdef IMAP_STORAGE
8232  vm_delete(fn); /* Delete the file, but not the IMAP message */
8233  if (ast_test_flag(vmu, VM_DELETE)) { /* Delete the IMAP message if delete = yes */
8234  vm_imap_delete(NULL, vms->curmsg, vmu);
8235  vms->newmessages--; /* Fix new message count */
8236  }
8237 #endif
8238 
8239  return 0;
8240 }
8241 
8242 /*!
8243  * \brief Sends a voicemail message to a mailbox recipient.
8244  * \param chan
8245  * \param context
8246  * \param vms
8247  * \param sender
8248  * \param fmt
8249  * \param is_new_message Used to indicate the mode for which this method was invoked.
8250  * Will be 0 when called to forward an existing message (option 8)
8251  * Will be 1 when called to leave a message (option 3->5)
8252  * \param record_gain
8253  * \param urgent
8254  *
8255  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
8256  *
8257  * When in the leave message mode (is_new_message == 1):
8258  * - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
8259  * - attempt to determine the context and mailbox, and then invoke leave_message() function to record and store the message.
8260  *
8261  * When in the forward message mode (is_new_message == 0):
8262  * - retrieves the current message to be forwarded
8263  * - copies the original message to a temporary file, so updates to the envelope can be done.
8264  * - determines the target mailbox and folders
8265  * - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
8266  *
8267  * \return zero on success, -1 on error.
8268  */
8269 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)
8270 {
8271 #ifdef IMAP_STORAGE
8272  int todircount = 0;
8273  struct vm_state *dstvms;
8274 #endif
8275  char username[70]="";
8276  char fn[PATH_MAX]; /* for playback of name greeting */
8277  char ecodes[16] = "#";
8278  int res = 0, cmd = 0;
8279  struct ast_vm_user *receiver = NULL, *vmtmp;
8281  char *stringp;
8282  const char *s;
8283  const char mailbox_context[256];
8284  int saved_messages = 0;
8285  int valid_extensions = 0;
8286  char *dir;
8287  int curmsg;
8288  char urgent_str[7] = "";
8289  int prompt_played = 0;
8290 #ifndef IMAP_STORAGE
8291  char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
8292 #endif
8293  if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
8294  ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
8295  }
8296 
8297  if (vms == NULL) return -1;
8298  dir = vms->curdir;
8299  curmsg = vms->curmsg;
8300 
8301  ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
8302  while (!res && !valid_extensions) {
8303  int use_directory = 0;
8304  if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
8305  int done = 0;
8306  int retries = 0;
8307  cmd = 0;
8308  while ((cmd >= 0) && !done ){
8309  if (cmd)
8310  retries = 0;
8311  switch (cmd) {
8312  case '1':
8313  use_directory = 0;
8314  done = 1;
8315  break;
8316  case '2':
8317  use_directory = 1;
8318  done = 1;
8319  break;
8320  case '*':
8321  cmd = 't';
8322  done = 1;
8323  break;
8324  default:
8325  /* Press 1 to enter an extension press 2 to use the directory */
8326  cmd = ast_play_and_wait(chan, "vm-forward");
8327  if (!cmd) {
8328  cmd = ast_waitfordigit(chan, 3000);
8329  }
8330  if (!cmd) {
8331  retries++;
8332  }
8333  if (retries > 3) {
8334  cmd = 't';
8335  done = 1;
8336  }
8337  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8338  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
8339  }
8340  }
8341  if (cmd < 0 || cmd == 't')
8342  break;
8343  }
8344 
8345  if (use_directory) {
8346  /* use app_directory */
8347 
8348  struct ast_app* directory_app;
8349 
8350  directory_app = pbx_findapp("Directory");
8351  if (directory_app) {
8352  char vmcontext[256];
8353  char old_context[strlen(ast_channel_context(chan)) + 1];
8354  char old_exten[strlen(ast_channel_exten(chan)) + 1];
8355  int old_priority;
8356  /* make backup copies */
8357  strcpy(old_context, ast_channel_context(chan)); /* safe */
8358  strcpy(old_exten, ast_channel_exten(chan)); /* safe */
8359  old_priority = ast_channel_priority(chan);
8360 
8361  /* call the Directory, changes the channel */
8362  snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
8363  res = pbx_exec(chan, directory_app, vmcontext);
8364 
8365  ast_copy_string(username, ast_channel_exten(chan), sizeof(username));
8366 
8367  /* restore the old context, exten, and priority */
8368  ast_channel_context_set(chan, old_context);
8369  ast_channel_exten_set(chan, old_exten);
8370  ast_channel_priority_set(chan, old_priority);
8371  } else {
8372  ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
8373  ast_clear_flag((&globalflags), VM_DIRECFORWARD);
8374  }
8375  } else {
8376  /* Ask for an extension */
8377  res = ast_streamfile(chan, "vm-extension", ast_channel_language(chan)); /* "extension" */
8378  prompt_played++;
8379  if (res || prompt_played > 4)
8380  break;
8381  if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#")) < 0)
8382  break;
8383  }
8384 
8385  /* start all over if no username */
8386  if (ast_strlen_zero(username))
8387  continue;
8388  stringp = username;
8389  s = strsep(&stringp, "*");
8390  /* start optimistic */
8391  valid_extensions = 1;
8392  while (s) {
8393  snprintf((char*)mailbox_context, sizeof(mailbox_context), "%s@%s", s, context ? context : "default");
8394  if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
8395  int oldmsgs;
8396  int newmsgs;
8397  int capacity;
8398 
8399  if (inboxcount(mailbox_context, &newmsgs, &oldmsgs)) {
8400  ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", mailbox_context);
8401  /* Shouldn't happen, but allow trying another extension if it does */
8402  res = ast_play_and_wait(chan, "pbx-invalid");
8403  valid_extensions = 0;
8404  break;
8405  }
8406 #ifdef IMAP_STORAGE
8407  if (!(dstvms = get_vm_state_by_mailbox(s, context, 0))) {
8408  if (!(dstvms = create_vm_state_from_user(receiver))) {
8409  ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
8410  /* Shouldn't happen, but allow trying another extension if it does */
8411  res = ast_play_and_wait(chan, "pbx-invalid");
8412  valid_extensions = 0;
8413  break;
8414  }
8415  }
8416  check_quota(dstvms, imapfolder);
8417  if (dstvms->quota_limit && dstvms->quota_usage >= dstvms->quota_limit) {
8418  ast_log(LOG_NOTICE, "Mailbox '%s' is exceeded quota %u >= %u\n", mailbox_context, dstvms->quota_usage, dstvms->quota_limit);
8419  res = ast_play_and_wait(chan, "vm-mailboxfull");
8420  valid_extensions = 0;
8421  while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8422  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8423  free_user(vmtmp);
8424  }
8425  break;
8426  }
8427 #endif
8428  capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
8429  if ((newmsgs + oldmsgs) >= capacity) {
8430  ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", mailbox_context, capacity);
8431  res = ast_play_and_wait(chan, "vm-mailboxfull");
8432  valid_extensions = 0;
8433  while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8434  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8435  free_user(vmtmp);
8436  }
8437  inprocess_count(receiver->mailbox, receiver->context, -1);
8438  break;
8439  }
8440  AST_LIST_INSERT_HEAD(&extensions, receiver, list);
8441  } else {
8442  /* XXX Optimization for the future. When we encounter a single bad extension,
8443  * bailing out on all of the extensions may not be the way to go. We should
8444  * probably just bail on that single extension, then allow the user to enter
8445  * several more. XXX
8446  */
8447  while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8448  free_user(receiver);
8449  }
8450  ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", mailbox_context);
8451  /* "I am sorry, that's not a valid extension. Please try again." */
8452  res = ast_play_and_wait(chan, "pbx-invalid");
8453  valid_extensions = 0;
8454  break;
8455  }
8456 
8457  /* play name if available, else play extension number */
8458  snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
8459  RETRIEVE(fn, -1, s, receiver->context);
8460  if (ast_fileexists(fn, NULL, NULL) > 0) {
8461  res = ast_stream_and_wait(chan, fn, ecodes);
8462  if (res) {
8463  DISPOSE(fn, -1);
8464  return res;
8465  }
8466  } else {
8467  res = ast_say_digit_str(chan, s, ecodes, ast_channel_language(chan));
8468  }
8469  DISPOSE(fn, -1);
8470 
8471  s = strsep(&stringp, "*");
8472  }
8473  /* break from the loop of reading the extensions */
8474  if (valid_extensions)
8475  break;
8476  }
8477  /* check if we're clear to proceed */
8478  if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
8479  return res;
8480  if (is_new_message == 1) {
8481  struct leave_vm_options leave_options;
8482  char mailbox[AST_MAX_EXTENSION * 2 + 2];
8483  snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
8484 
8485  /* Send VoiceMail */
8486  memset(&leave_options, 0, sizeof(leave_options));
8487  leave_options.record_gain = record_gain;
8488  leave_options.beeptone = "beep";
8489  cmd = leave_voicemail(chan, mailbox, &leave_options);
8490  } else {
8491  /* Forward VoiceMail */
8492  long duration = 0;
8493  struct vm_state vmstmp;
8494  int copy_msg_result = 0;
8495 #ifdef IMAP_STORAGE
8496  char filename[PATH_MAX];
8497  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8498  const char *msg_id = NULL;
8499  struct ast_config *msg_cfg;
8500 #endif
8501  memcpy(&vmstmp, vms, sizeof(vmstmp));
8502 
8503  RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
8504 #ifdef IMAP_STORAGE
8505  make_file(filename, sizeof(filename), dir, curmsg);
8506  strncat(filename, ".txt", sizeof(filename) - strlen(filename) - 1);
8507  msg_cfg = ast_config_load(filename, config_flags);
8508  if (msg_cfg && msg_cfg == CONFIG_STATUS_FILEINVALID) {
8509  msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
8510  ast_config_destroy(msg_cfg);
8511  }
8512 #endif
8513 
8514  cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
8515  if (!cmd) {
8516  AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
8517 #ifdef IMAP_STORAGE
8518  int attach_user_voicemail;
8519  char *myserveremail = serveremail;
8520 
8521  /* get destination mailbox */
8522  dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
8523  if (!dstvms) {
8524  dstvms = create_vm_state_from_user(vmtmp);
8525  }
8526  if (dstvms) {
8527  init_mailstream(dstvms, 0);
8528  if (!dstvms->mailstream) {
8529  ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
8530  } else {
8531  copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
8532  run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str);
8533  }
8534  } else {
8535  ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
8536  }
8537  if (!ast_strlen_zero(vmtmp->serveremail))
8538  myserveremail = vmtmp->serveremail;
8539  attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
8540  /* NULL category for IMAP storage */
8541  sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
8542  dstvms->curbox,
8543  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
8544  S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
8545  vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
8546  NULL, urgent_str, msg_id);
8547 #else
8548  copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str, NULL);
8549 #endif
8550  saved_messages++;
8552  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8553  free_user(vmtmp);
8554  if (res)
8555  break;
8556  }
8558  if (saved_messages > 0 && !copy_msg_result) {
8559  /* give confirmation that the message was saved */
8560  /* commented out since we can't forward batches yet
8561  if (saved_messages == 1)
8562  res = ast_play_and_wait(chan, "vm-message");
8563  else
8564  res = ast_play_and_wait(chan, "vm-messages");
8565  if (!res)
8566  res = ast_play_and_wait(chan, "vm-saved"); */
8567  res = ast_play_and_wait(chan, "vm-msgforwarded");
8568  }
8569 #ifndef IMAP_STORAGE
8570  else {
8571  /* with IMAP, mailbox full warning played by imap_check_limits */
8572  res = ast_play_and_wait(chan, "vm-mailboxfull");
8573  }
8574  /* Restore original message without prepended message if backup exists */
8575  make_file(msgfile, sizeof(msgfile), dir, curmsg);
8576  strcpy(textfile, msgfile);
8577  strcpy(backup, msgfile);
8578  strcpy(backup_textfile, msgfile);
8579  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
8580  strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
8581  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
8582  if (ast_fileexists(backup, NULL, NULL) > 0) {
8583  ast_filerename(backup, msgfile, NULL);
8584  rename(backup_textfile, textfile);
8585  }
8586 #endif
8587  }
8588  DISPOSE(dir, curmsg);
8589 #ifndef IMAP_STORAGE
8590  if (cmd) { /* assuming hangup, cleanup backup file */
8591  make_file(msgfile, sizeof(msgfile), dir, curmsg);
8592  strcpy(textfile, msgfile);
8593  strcpy(backup_textfile, msgfile);
8594  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
8595  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
8596  rename(backup_textfile, textfile);
8597  }
8598 #endif
8599  }
8600 
8601  /* If anything failed above, we still have this list to free */
8602  while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8603  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8604  free_user(vmtmp);
8605  }
8606  return res ? res : cmd;
8607 }
8608 
8609 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
8610 {
8611  int res;
8612  if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
8613  ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file);
8614  return res;
8615 }
8616 
8617 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
8618 {
8619  ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
8620  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);
8621 }
8622 
8623 static int play_message_category(struct ast_channel *chan, const char *category)
8624 {
8625  int res = 0;
8626 
8627  if (!ast_strlen_zero(category))
8628  res = ast_play_and_wait(chan, category);
8629 
8630  if (res) {
8631  ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
8632  res = 0;
8633  }
8634 
8635  return res;
8636 }
8637 
8638 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
8639 {
8640  int res = 0;
8641  struct vm_zone *the_zone = NULL;
8642  time_t t;
8643 
8644  if (ast_get_time_t(origtime, &t, 0, NULL)) {
8645  ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
8646  return 0;
8647  }
8648 
8649  /* Does this user have a timezone specified? */
8650  if (!ast_strlen_zero(vmu->zonetag)) {
8651  /* Find the zone in the list */
8652  struct vm_zone *z;
8653  AST_LIST_LOCK(&zones);
8654  AST_LIST_TRAVERSE(&zones, z, list) {
8655  if (!strcmp(z->name, vmu->zonetag)) {
8656  the_zone = z;
8657  break;
8658  }
8659  }
8661  }
8662 
8663 /* No internal variable parsing for now, so we'll comment it out for the time being */
8664 #if 0
8665  /* Set the DIFF_* variables */
8666  ast_localtime(&t, &time_now, NULL);
8667  tv_now = ast_tvnow();
8668  ast_localtime(&tv_now, &time_then, NULL);
8669 
8670  /* Day difference */
8671  if (time_now.tm_year == time_then.tm_year)
8672  snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
8673  else
8674  snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
8675  pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
8676 
8677  /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
8678 #endif
8679  if (the_zone) {
8680  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), the_zone->msg_format, the_zone->timezone);
8681  } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
8682  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
8683  } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
8684  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q H 'digits/kai' M ", NULL);
8685  } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */
8686  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
8687  } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
8688  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
8689  } else if (!strcasecmp(ast_channel_language(chan),"ja")) { /* Japanese syntax */
8690  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "PHM q 'jp-ni' 'vm-received'", NULL);
8691  } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
8692  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/nl-om' HM", NULL);
8693  } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
8694  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
8695  } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
8696  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q HM", NULL);
8697  } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* Brazillian PORTUGUESE syntax */
8698  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
8699  } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
8700  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' dB 'digits/at' k 'and' M", NULL);
8701  } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
8702  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "qR 'vm-received'", NULL);
8703  } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
8704  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' A 'digits/day' dB 'digits/year' Y 'digits/at' k 'hours' M 'minutes'", NULL);
8705  } else {
8706  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' IMp", NULL);
8707  }
8708 #if 0
8709  pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
8710 #endif
8711  return res;
8712 }
8713 
8714 
8715 
8716 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback, int saycidnumber)
8717 {
8718  int res = 0;
8719  int i;
8720  char *callerid, *name;
8721  char prefile[PATH_MAX] = "";
8722 
8723  /* If voicemail cid is not enabled, or we didn't get cid or context from
8724  * the attribute file, leave now.
8725  *
8726  * TODO Still need to change this so that if this function is called by the
8727  * message envelope (and someone is explicitly requesting to hear the CID),
8728  * it does not check to see if CID is enabled in the config file.
8729  */
8730  if ((cid == NULL)||(context == NULL))
8731  return res;
8732 
8733  /* Strip off caller ID number from name */
8734  ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
8735  ast_callerid_parse(cid, &name, &callerid);
8736  if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
8737  /* Check for internal contexts and only */
8738  /* say extension when the call didn't come from an internal context in the list */
8739  for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
8740  ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
8741  if ((strcmp(cidinternalcontexts[i], context) == 0))
8742  break;
8743  }
8744  if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
8745  if (!res) {
8746  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
8747  if (!ast_strlen_zero(prefile)) {
8748  /* See if we can find a recorded name for this callerid
8749  * and if found, use that instead of saying number. */
8750  if (ast_fileexists(prefile, NULL, NULL) > 0) {
8751  ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
8752  if (!callback)
8753  res = wait_file2(chan, vms, "vm-from");
8754  res = ast_stream_and_wait(chan, prefile, "");
8755  } else {
8756  ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
8757  /* Say "from extension" as one saying to sound smoother */
8758  if (!callback)
8759  res = wait_file2(chan, vms, "vm-from-extension");
8760  res = ast_say_digit_str(chan, callerid, "", ast_channel_language(chan));
8761  }
8762  }
8763  }
8764  } else if (!res) {
8765  ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
8766  /* If there is a recording for this numeric callerid then play that */
8767  if (!callback) {
8768  /* See if we can find a recorded name for this person instead of their extension number */
8769  snprintf(prefile, sizeof(prefile), "%s/recordings/callerids/%s", ast_config_AST_SPOOL_DIR, callerid);
8770  if (!saycidnumber && ast_fileexists(prefile, NULL, NULL) > 0) {
8771  ast_verb(3, "Playing recorded name for CID number '%s' - '%s'\n", callerid,prefile);
8772  wait_file2(chan, vms, "vm-from");
8773  res = ast_stream_and_wait(chan, prefile, "");
8774  ast_verb(3, "Played recorded name result '%d'\n", res);
8775  } else {
8776  /* Since this is all nicely figured out, why not say "from phone number" in this case" */
8777  wait_file2(chan, vms, "vm-from-phonenumber");
8778  res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
8779  }
8780  } else {
8781  res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
8782  }
8783  }
8784  } else {
8785  /* Number unknown */
8786  ast_debug(1, "VM-CID: From an unknown number\n");
8787  /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
8788  res = wait_file2(chan, vms, "vm-unknown-caller");
8789  }
8790  return res;
8791 }
8792 
8793 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
8794 {
8795  int res = 0;
8796  int durationm;
8797  int durations;
8798  /* Verify that we have a duration for the message */
8799  if (duration == NULL)
8800  return res;
8801 
8802  /* Convert from seconds to minutes */
8803  durations = atoi(duration);
8804  durationm = (durations / 60);
8805 
8806  ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
8807 
8808  if ((!res) && (durationm >= minduration)) {
8809  res = wait_file2(chan, vms, "vm-duration");
8810 
8811  /* POLISH syntax */
8812  if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
8813  div_t num = div(durationm, 10);
8814 
8815  if (durationm == 1) {
8816  res = ast_play_and_wait(chan, "digits/1z");
8817  res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
8818  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
8819  if (num.rem == 2) {
8820  if (!num.quot) {
8821  res = ast_play_and_wait(chan, "digits/2-ie");
8822  } else {
8823  res = say_and_wait(chan, durationm - 2 , ast_channel_language(chan));
8824  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
8825  }
8826  } else {
8827  res = say_and_wait(chan, durationm, ast_channel_language(chan));
8828  }
8829  res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
8830  } else {
8831  res = say_and_wait(chan, durationm, ast_channel_language(chan));
8832  res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
8833  }
8834  /* DEFAULT syntax */
8835  } else {
8836  res = ast_say_number(chan, durationm, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
8837  res = wait_file2(chan, vms, "vm-minutes");
8838  }
8839  }
8840  return res;
8841 }
8842 
8843 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
8844 {
8845  int res = 0;
8846  char filename[PATH_MAX], *cid;
8847  const char *origtime, *context, *category, *duration, *flag;
8848  struct ast_config *msg_cfg;
8849  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8850 
8851  vms->starting = 0;
8852  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
8853  adsi_message(chan, vms);
8854  if (!vms->curmsg) {
8855  res = wait_file2(chan, vms, "vm-first"); /* "First" */
8856  } else if (vms->curmsg == vms->lastmsg) {
8857  res = wait_file2(chan, vms, "vm-last"); /* "last" */
8858  }
8859 
8860  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
8861  RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
8862  msg_cfg = ast_config_load(filename, config_flags);
8863  if (!valid_config(msg_cfg)) {
8864  ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
8865  return 0;
8866  }
8867  flag = ast_variable_retrieve(msg_cfg, "message", "flag");
8868 
8869  /* Play the word urgent if we are listening to urgent messages */
8870  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
8871  res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
8872  }
8873 
8874  if (!res) {
8875  /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
8876  /* POLISH syntax */
8877  if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
8878  if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
8879  int ten, one;
8880  char nextmsg[256];
8881  ten = (vms->curmsg + 1) / 10;
8882  one = (vms->curmsg + 1) % 10;
8883 
8884  if (vms->curmsg < 20) {
8885  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
8886  res = wait_file2(chan, vms, nextmsg);
8887  } else {
8888  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
8889  res = wait_file2(chan, vms, nextmsg);
8890  if (one > 0) {
8891  if (!res) {
8892  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
8893  res = wait_file2(chan, vms, nextmsg);
8894  }
8895  }
8896  }
8897  }
8898  if (!res)
8899  res = wait_file2(chan, vms, "vm-message");
8900  /* HEBREW syntax */
8901  } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
8902  if (!vms->curmsg) {
8903  res = wait_file2(chan, vms, "vm-message");
8904  res = wait_file2(chan, vms, "vm-first");
8905  } else if (vms->curmsg == vms->lastmsg) {
8906  res = wait_file2(chan, vms, "vm-message");
8907  res = wait_file2(chan, vms, "vm-last");
8908  } else {
8909  res = wait_file2(chan, vms, "vm-message");
8910  res = wait_file2(chan, vms, "vm-number");
8911  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
8912  }
8913  /* ICELANDIC syntax */
8914  } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) {
8915  res = wait_file2(chan, vms, "vm-message");
8916  if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
8917  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "n");
8918  }
8919  /* VIETNAMESE syntax */
8920  } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
8921  if (!vms->curmsg) {
8922  res = wait_file2(chan, vms, "vm-message");
8923  res = wait_file2(chan, vms, "vm-first");
8924  } else if (vms->curmsg == vms->lastmsg) {
8925  res = wait_file2(chan, vms, "vm-message");
8926  res = wait_file2(chan, vms, "vm-last");
8927  } else {
8928  res = wait_file2(chan, vms, "vm-message");
8929  res = wait_file2(chan, vms, "vm-number");
8930  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
8931  }
8932  } else {
8933  if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
8934  res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
8935  } else { /* DEFAULT syntax */
8936  res = wait_file2(chan, vms, "vm-message");
8937  }
8938  if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
8939  if (!res) {
8940  ast_test_suite_event_notify("PLAYBACK", "Message: message number");
8941  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
8942  }
8943  }
8944  }
8945  }
8946 
8947  if (!valid_config(msg_cfg)) {
8948  ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
8949  return 0;
8950  }
8951 
8952  if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
8953  ast_log(AST_LOG_WARNING, "No origtime?!\n");
8954  DISPOSE(vms->curdir, vms->curmsg);
8955  ast_config_destroy(msg_cfg);
8956  return 0;
8957  }
8958 
8959  cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
8960  duration = ast_variable_retrieve(msg_cfg, "message", "duration");
8961  category = ast_variable_retrieve(msg_cfg, "message", "category");
8962 
8963  context = ast_variable_retrieve(msg_cfg, "message", "context");
8964  if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
8965  context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
8966  if (!res) {
8967  res = play_message_category(chan, category);
8968  }
8969  if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
8970  res = play_message_datetime(chan, vmu, origtime, filename);
8971  }
8972  if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
8973  res = play_message_callerid(chan, vms, cid, context, 0, 0);
8974  }
8975  if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
8976  res = play_message_duration(chan, vms, duration, vmu->saydurationm);
8977  }
8978  /* Allow pressing '1' to skip envelope / callerid */
8979  if (res == '1') {
8980  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
8981  res = 0;
8982  }
8983  ast_config_destroy(msg_cfg);
8984 
8985  if (!res) {
8986  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
8987 #ifdef IMAP_STORAGE
8988  ast_mutex_lock(&vms->lock);
8989 #endif
8990  vms->heard[vms->curmsg] = 1;
8991 #ifdef IMAP_STORAGE
8992  ast_mutex_unlock(&vms->lock);
8993  /*IMAP storage stores any prepended message from a forward
8994  * as a separate file from the rest of the message
8995  */
8996  if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
8997  wait_file(chan, vms, vms->introfn);
8998  }
8999 #endif
9000  if ((res = wait_file(chan, vms, vms->fn)) < 0) {
9001  ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
9002  res = 0;
9003  }
9004  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
9005  isprint(res) ? res : '?', isprint(res) ? res : '?');
9006  }
9007  DISPOSE(vms->curdir, vms->curmsg);
9008  return res;
9009 }
9010 
9011 #ifdef IMAP_STORAGE
9012 static int imap_remove_file(char *dir, int msgnum)
9013 {
9014  char fn[PATH_MAX];
9015  char full_fn[PATH_MAX];
9016  char intro[PATH_MAX] = {0,};
9017 
9018  if (msgnum > -1) {
9019  make_file(fn, sizeof(fn), dir, msgnum);
9020  snprintf(intro, sizeof(intro), "%sintro", fn);
9021  } else
9022  ast_copy_string(fn, dir, sizeof(fn));
9023 
9024  if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
9025  ast_filedelete(fn, NULL);
9026  if (!ast_strlen_zero(intro)) {
9027  ast_filedelete(intro, NULL);
9028  }
9029  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
9030  unlink(full_fn);
9031  }
9032  return 0;
9033 }
9034 
9035 
9036 
9037 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
9038 {
9039  char *file, *filename;
9040  char arg[11];
9041  int i;
9042  BODY* body;
9043  int curr_mbox;
9044 
9045  file = strrchr(ast_strdupa(dir), '/');
9046  if (file) {
9047  *file++ = '\0';
9048  } else {
9049  ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
9050  return -1;
9051  }
9052 
9053  ast_mutex_lock(&vms->lock);
9054 
9055  /* get the current mailbox so that we can point the mailstream back to it later */
9056  curr_mbox = get_folder_by_name(vms->curbox);
9057 
9058  if (init_mailstream(vms, GREETINGS_FOLDER) || !vms->mailstream) {
9059  ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
9060  ast_mutex_unlock(&vms->lock);
9061  return -1;
9062  }
9063 
9064  for (i = 0; i < vms->mailstream->nmsgs; i++) {
9065  mail_fetchstructure(vms->mailstream, i + 1, &body);
9066  /* We have the body, now we extract the file name of the first attachment. */
9067  if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
9068  char *attachment = body->nested.part->next->body.parameter->value;
9069  char copy[strlen(attachment) + 1];
9070 
9071  strcpy(copy, attachment); /* safe */
9072  attachment = copy;
9073 
9074  filename = strsep(&attachment, ".");
9075  if (!strcmp(filename, file)) {
9076  snprintf(arg, sizeof(arg), "%d", i + 1);
9077  mail_setflag(vms->mailstream, arg, "\\DELETED");
9078  }
9079  } else {
9080  ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
9081  ast_mutex_unlock(&vms->lock);
9082  return -1;
9083  }
9084  }
9085  mail_expunge(vms->mailstream);
9086 
9087  if (curr_mbox != -1) {
9088  /* restore previous mbox stream */
9089  if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
9090  ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
9091  }
9092  }
9093 
9094  ast_mutex_unlock(&vms->lock);
9095  return 0;
9096 }
9097 
9098 #elif !defined(IMAP_STORAGE)
9099 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
9100 {
9101  int count_msg, last_msg;
9102 
9103  ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
9104 
9105  /* Rename the member vmbox HERE so that we don't try to return before
9106  * we know what's going on.
9107  */
9108  snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
9109 
9110  /* Faster to make the directory than to check if it exists. */
9111  create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
9112 
9113  /* traverses directory using readdir (or select query for ODBC) */
9114  count_msg = count_messages(vmu, vms->curdir);
9115  if (count_msg < 0) {
9116  return count_msg;
9117  } else {
9118  vms->lastmsg = count_msg - 1;
9119  }
9120 
9121  if (vm_allocate_dh(vms, vmu, count_msg)) {
9122  return -1;
9123  }
9124 
9125  /*
9126  The following test is needed in case sequencing gets messed up.
9127  There appears to be more than one way to mess up sequence, so
9128  we will not try to find all of the root causes--just fix it when
9129  detected.
9130  */
9131 
9132  if (vm_lock_path(vms->curdir)) {
9133  ast_log(AST_LOG_ERROR, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
9134  return ERROR_LOCK_PATH;
9135  }
9136 
9137  /* for local storage, checks directory for messages up to maxmsg limit */
9138  last_msg = last_message_index(vmu, vms->curdir);
9139  ast_unlock_path(vms->curdir);
9140 
9141  if (last_msg < -1) {
9142  return last_msg;
9143  } else if (vms->lastmsg != last_msg) {
9144  ast_log(LOG_NOTICE, "Resequencing Mailbox: %s, expected %d but found %d message(s) in box with max threshold of %d.\n", vms->curdir, last_msg + 1, vms->lastmsg + 1, vmu->maxmsg);
9145  resequence_mailbox(vmu, vms->curdir, count_msg);
9146  }
9147 
9148  return 0;
9149 }
9150 #endif
9151 
9152 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
9153 {
9154  int x = 0;
9155  int last_msg_idx = 0;
9156 
9157 #ifndef IMAP_STORAGE
9158  int res = 0, nummsg;
9159  char fn2[PATH_MAX];
9160 #endif
9161 
9162  if (vms->lastmsg <= -1) {
9163  goto done;
9164  }
9165 
9166  vms->curmsg = -1;
9167 #ifndef IMAP_STORAGE
9168  /* Get the deleted messages fixed */
9169  if (vm_lock_path(vms->curdir)) {
9170  return ERROR_LOCK_PATH;
9171  }
9172 
9173  /* update count as message may have arrived while we've got mailbox open */
9174  last_msg_idx = last_message_index(vmu, vms->curdir);
9175  if (last_msg_idx != vms->lastmsg) {
9176  ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
9177  }
9178 
9179  /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
9180  for (x = 0; x < last_msg_idx + 1; x++) {
9181  if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
9182  /* Save this message. It's not in INBOX or hasn't been heard */
9183  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9184  if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
9185  break;
9186  }
9187  vms->curmsg++;
9188  make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
9189  if (strcmp(vms->fn, fn2)) {
9190  RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
9191  }
9192  } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
9193  /* Move to old folder before deleting */
9194  res = save_to_folder(vmu, vms, x, 1, NULL, 0);
9195  if (res == ERROR_LOCK_PATH || res == ERROR_MAX_MSGS) {
9196  /* If save failed do not delete the message */
9197  ast_log(AST_LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
9198  vms->deleted[x] = 0;
9199  vms->heard[x] = 0;
9200  --x;
9201  }
9202  } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
9203  /* Move to deleted folder */
9204  res = save_to_folder(vmu, vms, x, 10, NULL, 0);
9205  if (res == ERROR_LOCK_PATH) {
9206  /* If save failed do not delete the message */
9207  vms->deleted[x] = 0;
9208  vms->heard[x] = 0;
9209  --x;
9210  }
9211  } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
9212  /* If realtime storage enabled - we should explicitly delete this message,
9213  cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
9214  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9215  if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
9216  DELETE(vms->curdir, x, vms->fn, vmu);
9217  }
9218  }
9219  }
9220 
9221  /* Delete ALL remaining messages */
9222  nummsg = x - 1;
9223  for (x = vms->curmsg + 1; x <= nummsg; x++) {
9224  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9225  if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
9226  DELETE(vms->curdir, x, vms->fn, vmu);
9227  }
9228  }
9229  ast_unlock_path(vms->curdir);
9230 #else /* defined(IMAP_STORAGE) */
9231  ast_mutex_lock(&vms->lock);
9232  if (vms->deleted) {
9233  /* Since we now expunge after each delete, deleting in reverse order
9234  * ensures that no reordering occurs between each step. */
9235  last_msg_idx = vms->dh_arraysize;
9236  for (x = last_msg_idx - 1; x >= 0; x--) {
9237  if (vms->deleted[x]) {
9238  ast_debug(3, "IMAP delete of %d\n", x);
9239  DELETE(vms->curdir, x, vms->fn, vmu);
9240  }
9241  }
9242  }
9243 #endif
9244 
9245 done:
9246  if (vms->deleted) {
9247  ast_free(vms->deleted);
9248  vms->deleted = NULL;
9249  }
9250  if (vms->heard) {
9251  ast_free(vms->heard);
9252  vms->heard = NULL;
9253  }
9254  vms->dh_arraysize = 0;
9255 #ifdef IMAP_STORAGE
9256  ast_mutex_unlock(&vms->lock);
9257 #endif
9258 
9259  return 0;
9260 }
9261 
9262 /* In Greek even though we CAN use a syntax like "friends messages"
9263  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
9264  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
9265  * syntax for the above three categories which is more elegant.
9266  */
9267 
9268 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
9269 {
9270  int cmd;
9271  char *buf;
9272 
9273  buf = ast_alloca(strlen(box) + 2);
9274  strcpy(buf, box);
9275  strcat(buf, "s");
9276 
9277  if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
9278  cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
9279  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
9280  } else {
9281  cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
9282  return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
9283  }
9284 }
9285 
9286 static int vm_play_folder_name_ja(struct ast_channel *chan, char *box)
9287 {
9288  int cmd;
9289 
9290  if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
9291  cmd = ast_play_and_wait(chan, box);
9292  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9293  } else {
9294  cmd = ast_play_and_wait(chan, box);
9295  return cmd;
9296  }
9297 }
9298 
9299 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
9300 {
9301  int cmd;
9302 
9303  if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
9304  if (!strcasecmp(box, "vm-INBOX"))
9305  cmd = ast_play_and_wait(chan, "vm-new-e");
9306  else
9307  cmd = ast_play_and_wait(chan, "vm-old-e");
9308  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9309  } else {
9310  cmd = ast_play_and_wait(chan, "vm-messages");
9311  return cmd ? cmd : ast_play_and_wait(chan, box);
9312  }
9313 }
9314 
9315 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
9316 {
9317  int cmd;
9318 
9319  if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
9320  cmd = ast_play_and_wait(chan, "vm-messages");
9321  return cmd ? cmd : ast_play_and_wait(chan, box);
9322  } else {
9323  cmd = ast_play_and_wait(chan, box);
9324  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9325  }
9326 }
9327 
9328 static int vm_play_folder_name(struct ast_channel *chan, char *box)
9329 {
9330  int cmd;
9331 
9332  if ( !strncasecmp(ast_channel_language(chan), "it", 2) ||
9333  !strncasecmp(ast_channel_language(chan), "es", 2) ||
9334  !strncasecmp(ast_channel_language(chan), "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
9335  cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
9336  return cmd ? cmd : ast_play_and_wait(chan, box);
9337  } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {
9338  return vm_play_folder_name_gr(chan, box);
9339  } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* Hebrew syntax */
9340  return ast_play_and_wait(chan, box);
9341  } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
9342  return vm_play_folder_name_ja(chan, box);
9343  } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9344  return vm_play_folder_name_pl(chan, box);
9345  } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian syntax */
9346  return vm_play_folder_name_ua(chan, box);
9347  } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
9348  return ast_play_and_wait(chan, box);
9349  } else { /* Default English */
9350  cmd = ast_play_and_wait(chan, box);
9351  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
9352  }
9353 }
9354 
9355 /* GREEK SYNTAX
9356  In greek the plural for old/new is
9357  different so we need the following files
9358  We also need vm-denExeteMynhmata because
9359  this syntax is different.
9360 
9361  -> vm-Olds.wav : "Palia"
9362  -> vm-INBOXs.wav : "Nea"
9363  -> vm-denExeteMynhmata : "den exete mynhmata"
9364 */
9365 
9366 
9367 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
9368 {
9369  int res = 0;
9370 
9371  if (vms->newmessages) {
9372  res = ast_play_and_wait(chan, "vm-youhave");
9373  if (!res)
9375  if (!res) {
9376  if (vms->newmessages == 1) {
9377  res = ast_play_and_wait(chan, "vm-INBOX");
9378  if (!res)
9379  res = ast_play_and_wait(chan, "vm-message");
9380  } else {
9381  res = ast_play_and_wait(chan, "vm-INBOXs");
9382  if (!res)
9383  res = ast_play_and_wait(chan, "vm-messages");
9384  }
9385  }
9386  } else if (vms->oldmessages){
9387  res = ast_play_and_wait(chan, "vm-youhave");
9388  if (!res)
9390  if (vms->oldmessages == 1){
9391  res = ast_play_and_wait(chan, "vm-Old");
9392  if (!res)
9393  res = ast_play_and_wait(chan, "vm-message");
9394  } else {
9395  res = ast_play_and_wait(chan, "vm-Olds");
9396  if (!res)
9397  res = ast_play_and_wait(chan, "vm-messages");
9398  }
9399  } else if (!vms->oldmessages && !vms->newmessages)
9400  res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
9401  return res;
9402 }
9403 
9404 /* Version of vm_intro() designed to work for many languages.
9405  *
9406  * It is hoped that this function can prevent the proliferation of
9407  * language-specific vm_intro() functions and in time replace the language-
9408  * specific functions which already exist. An examination of the language-
9409  * specific functions revealed that they all corrected the same deficiencies
9410  * in vm_intro_en() (which was the default function). Namely:
9411  *
9412  * 1) The vm-Old and vm-INBOX sound files were overloaded. The English
9413  * wording of the voicemail greeting hides this problem. For example,
9414  * vm-INBOX contains only the word "new". This means that both of these
9415  * sequences produce valid utterances:
9416  * * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
9417  * * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
9418  * However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
9419  * in many languages) the first utterance becomes "you have 1 the new message".
9420  * 2) The function contains hardcoded rules for pluralizing the word "message".
9421  * These rules are correct for English, but not for many other languages.
9422  * 3) No attempt is made to pluralize the adjectives ("old" and "new") as
9423  * required in many languages.
9424  * 4) The gender of the word for "message" is not specified. This is a problem
9425  * because in many languages the gender of the number in phrases such
9426  * as "you have one new message" must match the gender of the word
9427  * meaning "message".
9428  *
9429  * Fixing these problems for each new language has meant duplication of effort.
9430  * This new function solves the problems in the following general ways:
9431  * 1) Add new sound files vm-new and vm-old. These can be linked to vm-INBOX
9432  * and vm-Old respectively for those languages where it makes sense.
9433  * 2) Call ast_say_counted_noun() to put the proper gender and number prefix
9434  * on vm-message.
9435  * 3) Call ast_say_counted_adjective() to put the proper gender and number
9436  * prefix on vm-new and vm-old (none for English).
9437  * 4) Pass the gender of the language's word for "message" as an agument to
9438  * this function which is can in turn pass on to the functions which
9439  * say numbers and put endings on nounds and adjectives.
9440  *
9441  * All languages require these messages:
9442  * vm-youhave "You have..."
9443  * vm-and "and"
9444  * vm-no "no" (in the sense of "none", as in "you have no messages")
9445  *
9446  * To use it for English, you will need these additional sound files:
9447  * vm-new "new"
9448  * vm-message "message", singular
9449  * vm-messages "messages", plural
9450  *
9451  * If you use it for Russian and other slavic languages, you will need these additional sound files:
9452  *
9453  * vm-newn "novoye" (singular, neuter)
9454  * vm-newx "novikh" (counting plural form, genative plural)
9455  * vm-message "sobsheniye" (singular form)
9456  * vm-messagex1 "sobsheniya" (first counting plural form, genative singular)
9457  * vm-messagex2 "sobsheniy" (second counting plural form, genative plural)
9458  * digits/1n "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
9459  * digits/2n "dva" (neuter singular)
9460  */
9461 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
9462 {
9463  int res;
9464  int lastnum = 0;
9465 
9466  res = ast_play_and_wait(chan, "vm-youhave");
9467 
9468  if (!res && vms->newmessages) {
9469  lastnum = vms->newmessages;
9470 
9471  if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
9472  res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
9473  }
9474 
9475  if (!res && vms->oldmessages) {
9476  res = ast_play_and_wait(chan, "vm-and");
9477  }
9478  }
9479 
9480  if (!res && vms->oldmessages) {
9481  lastnum = vms->oldmessages;
9482 
9483  if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
9484  res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
9485  }
9486  }
9487 
9488  if (!res) {
9489  if (lastnum == 0) {
9490  res = ast_play_and_wait(chan, "vm-no");
9491  }
9492  if (!res) {
9493  res = ast_say_counted_noun(chan, lastnum, "vm-message");
9494  }
9495  }
9496 
9497  return res;
9498 }
9499 
9500 /* Default Hebrew syntax */
9501 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
9502 {
9503  int res = 0;
9504 
9505  /* Introduce messages they have */
9506  if (!res) {
9507  if ((vms->newmessages) || (vms->oldmessages)) {
9508  res = ast_play_and_wait(chan, "vm-youhave");
9509  }
9510  /*
9511  * The word "shtei" refers to the number 2 in hebrew when performing a count
9512  * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
9513  * an element, this is one of them.
9514  */
9515  if (vms->newmessages) {
9516  if (!res) {
9517  if (vms->newmessages == 1) {
9518  res = ast_play_and_wait(chan, "vm-INBOX1");
9519  } else {
9520  if (vms->newmessages == 2) {
9521  res = ast_play_and_wait(chan, "vm-shtei");
9522  } else {
9523  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9524  }
9525  res = ast_play_and_wait(chan, "vm-INBOX");
9526  }
9527  }
9528  if (vms->oldmessages && !res) {
9529  res = ast_play_and_wait(chan, "vm-and");
9530  if (vms->oldmessages == 1) {
9531  res = ast_play_and_wait(chan, "vm-Old1");
9532  } else {
9533  if (vms->oldmessages == 2) {
9534  res = ast_play_and_wait(chan, "vm-shtei");
9535  } else {
9536  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9537  }
9538  res = ast_play_and_wait(chan, "vm-Old");
9539  }
9540  }
9541  }
9542  if (!res && vms->oldmessages && !vms->newmessages) {
9543  if (!res) {
9544  if (vms->oldmessages == 1) {
9545  res = ast_play_and_wait(chan, "vm-Old1");
9546  } else {
9547  if (vms->oldmessages == 2) {
9548  res = ast_play_and_wait(chan, "vm-shtei");
9549  } else {
9550  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9551  }
9552  res = ast_play_and_wait(chan, "vm-Old");
9553  }
9554  }
9555  }
9556  if (!res) {
9557  if (!vms->oldmessages && !vms->newmessages) {
9558  if (!res) {
9559  res = ast_play_and_wait(chan, "vm-nomessages");
9560  }
9561  }
9562  }
9563  }
9564  return res;
9565 }
9566 
9567 /* Japanese syntax */
9568 static int vm_intro_ja(struct ast_channel *chan,struct vm_state *vms)
9569 {
9570  /* Introduce messages they have */
9571  int res;
9572  if (vms->newmessages) {
9573  res = ast_play_and_wait(chan, "vm-INBOX");
9574  if (!res)
9575  res = ast_play_and_wait(chan, "vm-message");
9576  if (!res)
9577  res = ast_play_and_wait(chan, "jp-ga");
9578  if (!res)
9579  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
9580  if (vms->oldmessages && !res)
9581  res = ast_play_and_wait(chan, "silence/1");
9582 
9583  }
9584  if (vms->oldmessages) {
9585  res = ast_play_and_wait(chan, "vm-Old");
9586  if (!res)
9587  res = ast_play_and_wait(chan, "vm-message");
9588  if (!res)
9589  res = ast_play_and_wait(chan, "jp-ga");
9590  if (!res)
9591  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
9592  }
9593  if (!vms->oldmessages && !vms->newmessages) {
9594  res = ast_play_and_wait(chan, "vm-messages");
9595  if (!res)
9596  res = ast_play_and_wait(chan, "jp-wa");
9597  if (!res)
9598  res = ast_play_and_wait(chan, "jp-arimasen");
9599  }
9600  else {
9601  res = ast_play_and_wait(chan, "jp-arimasu");
9602  }
9603  return res;
9604 } /* Japanese */
9605 
9606 /* Default English syntax */
9607 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
9608 {
9609  int res;
9610 
9611  /* Introduce messages they have */
9612  res = ast_play_and_wait(chan, "vm-youhave");
9613  if (!res) {
9614  if (vms->urgentmessages) {
9615  res = say_and_wait(chan, vms->urgentmessages, ast_channel_language(chan));
9616  if (!res)
9617  res = ast_play_and_wait(chan, "vm-Urgent");
9618  if ((vms->oldmessages || vms->newmessages) && !res) {
9619  res = ast_play_and_wait(chan, "vm-and");
9620  } else if (!res) {
9621  if (vms->urgentmessages == 1)
9622  res = ast_play_and_wait(chan, "vm-message");
9623  else
9624  res = ast_play_and_wait(chan, "vm-messages");
9625  }
9626  }
9627  if (vms->newmessages) {
9628  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
9629  if (!res)
9630  res = ast_play_and_wait(chan, "vm-INBOX");
9631  if (vms->oldmessages && !res)
9632  res = ast_play_and_wait(chan, "vm-and");
9633  else if (!res) {
9634  if (vms->newmessages == 1)
9635  res = ast_play_and_wait(chan, "vm-message");
9636  else
9637  res = ast_play_and_wait(chan, "vm-messages");
9638  }
9639 
9640  }
9641  if (!res && vms->oldmessages) {
9642  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
9643  if (!res)
9644  res = ast_play_and_wait(chan, "vm-Old");
9645  if (!res) {
9646  if (vms->oldmessages == 1)
9647  res = ast_play_and_wait(chan, "vm-message");
9648  else
9649  res = ast_play_and_wait(chan, "vm-messages");
9650  }
9651  }
9652  if (!res) {
9653  if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
9654  res = ast_play_and_wait(chan, "vm-no");
9655  if (!res)
9656  res = ast_play_and_wait(chan, "vm-messages");
9657  }
9658  }
9659  }
9660  return res;
9661 }
9662 
9663 /* ICELANDIC syntax */
9664 static int vm_intro_is(struct ast_channel *chan, struct vm_state *vms)
9665 {
9666  int res;
9667 
9668  /* Introduce messages they have */
9669  res = ast_play_and_wait(chan, "vm-youhave");
9670  if (!res) {
9671  if (vms->urgentmessages) {
9672  /* Digits 1-4 are spoken in neutral and plural when talking about messages,
9673  however, feminine is used for 1 as it is the same as the neutral for plural,
9674  and singular neutral is the same after 1. */
9675  if (vms->urgentmessages < 5) {
9676  char recname[16];
9677  if (vms->urgentmessages == 1)
9678  snprintf(recname, sizeof(recname), "digits/1kvk");
9679  else
9680  snprintf(recname, sizeof(recname), "digits/%dhk", vms->urgentmessages);
9681  res = ast_play_and_wait(chan, recname);
9682  } else if (!res)
9683  res = ast_play_and_wait(chan, "vm-Urgent");
9684  if ((vms->oldmessages || vms->newmessages) && !res) {
9685  res = ast_play_and_wait(chan, "vm-and");
9686  } else if (!res)
9687  res = ast_play_and_wait(chan, "vm-messages");
9688  }
9689  if (vms->newmessages) {
9690  if (vms->newmessages < 5) {
9691  char recname[16];
9692  if (vms->newmessages == 1)
9693  snprintf(recname, sizeof(recname), "digits/1kvk");
9694  else
9695  snprintf(recname, sizeof(recname), "digits/%dhk", vms->newmessages);
9696  res = ast_play_and_wait(chan, recname);
9697  } else
9698  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
9699  if (!res)
9700  res = ast_play_and_wait(chan, "vm-INBOX");
9701  if (vms->oldmessages && !res)
9702  res = ast_play_and_wait(chan, "vm-and");
9703  else if (!res)
9704  res = ast_play_and_wait(chan, "vm-messages");
9705  }
9706  if (!res && vms->oldmessages) {
9707  if (vms->oldmessages < 5) {
9708  char recname[16];
9709  if (vms->oldmessages == 1)
9710  snprintf(recname, sizeof(recname), "digits/1kvk");
9711  else
9712  snprintf(recname, sizeof(recname), "digits/%dhk", vms->oldmessages);
9713  res = ast_play_and_wait(chan, recname);
9714  } else
9715  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
9716  if (!res)
9717  res = ast_play_and_wait(chan, "vm-Old");
9718  if (!res)
9719  res = ast_play_and_wait(chan, "vm-messages");
9720  }
9721  if (!res) {
9722  if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
9723  res = ast_play_and_wait(chan, "vm-no");
9724  if (!res)
9725  res = ast_play_and_wait(chan, "vm-messages");
9726  }
9727  }
9728  }
9729  return res;
9730 }
9731 
9732 /* ITALIAN syntax */
9733 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
9734 {
9735  /* Introduce messages they have */
9736  int res;
9737  if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
9738  res = ast_play_and_wait(chan, "vm-no") ||
9739  ast_play_and_wait(chan, "vm-message");
9740  else
9741  res = ast_play_and_wait(chan, "vm-youhave");
9742  if (!res && vms->newmessages) {
9743  res = (vms->newmessages == 1) ?
9744  ast_play_and_wait(chan, "digits/un") ||
9745  ast_play_and_wait(chan, "vm-nuovo") ||
9746  ast_play_and_wait(chan, "vm-message") :
9747  /* 2 or more new messages */
9748  say_and_wait(chan, vms->newmessages, ast_channel_language(chan)) ||
9749  ast_play_and_wait(chan, "vm-nuovi") ||
9750  ast_play_and_wait(chan, "vm-messages");
9751  if (!res && vms->oldmessages)
9752  res = ast_play_and_wait(chan, "vm-and");
9753  }
9754  if (!res && vms->oldmessages) {
9755  res = (vms->oldmessages == 1) ?
9756  ast_play_and_wait(chan, "digits/un") ||
9757  ast_play_and_wait(chan, "vm-vecchio") ||
9758  ast_play_and_wait(chan, "vm-message") :
9759  /* 2 or more old messages */
9760  say_and_wait(chan, vms->oldmessages, ast_channel_language(chan)) ||
9761  ast_play_and_wait(chan, "vm-vecchi") ||
9762  ast_play_and_wait(chan, "vm-messages");
9763  }
9764  return res;
9765 }
9766 
9767 /* POLISH syntax */
9768 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
9769 {
9770  /* Introduce messages they have */
9771  int res;
9772  div_t num;
9773 
9774  if (!vms->oldmessages && !vms->newmessages) {
9775  res = ast_play_and_wait(chan, "vm-no");
9776  res = res ? res : ast_play_and_wait(chan, "vm-messages");
9777  return res;
9778  } else {
9779  res = ast_play_and_wait(chan, "vm-youhave");
9780  }
9781 
9782  if (vms->newmessages) {
9783  num = div(vms->newmessages, 10);
9784  if (vms->newmessages == 1) {
9785  res = ast_play_and_wait(chan, "digits/1-a");
9786  res = res ? res : ast_play_and_wait(chan, "vm-new-a");
9787  res = res ? res : ast_play_and_wait(chan, "vm-message");
9788  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
9789  if (num.rem == 2) {
9790  if (!num.quot) {
9791  res = ast_play_and_wait(chan, "digits/2-ie");
9792  } else {
9793  res = say_and_wait(chan, vms->newmessages - 2 , ast_channel_language(chan));
9794  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
9795  }
9796  } else {
9797  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
9798  }
9799  res = res ? res : ast_play_and_wait(chan, "vm-new-e");
9800  res = res ? res : ast_play_and_wait(chan, "vm-messages");
9801  } else {
9802  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
9803  res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
9804  res = res ? res : ast_play_and_wait(chan, "vm-messages");
9805  }
9806  if (!res && vms->oldmessages)
9807  res = ast_play_and_wait(chan, "vm-and");
9808  }
9809  if (!res && vms->oldmessages) {
9810  num = div(vms->oldmessages, 10);
9811  if (vms->oldmessages == 1) {
9812  res = ast_play_and_wait(chan, "digits/1-a");
9813  res = res ? res : ast_play_and_wait(chan, "vm-old-a");
9814  res = res ? res : ast_play_and_wait(chan, "vm-message");
9815  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
9816  if (num.rem == 2) {
9817  if (!num.quot) {
9818  res = ast_play_and_wait(chan, "digits/2-ie");
9819  } else {
9820  res = say_and_wait(chan, vms->oldmessages - 2 , ast_channel_language(chan));
9821  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
9822  }
9823  } else {
9824  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
9825  }
9826  res = res ? res : ast_play_and_wait(chan, "vm-old-e");
9827  res = res ? res : ast_play_and_wait(chan, "vm-messages");
9828  } else {
9829  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
9830  res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
9831  res = res ? res : ast_play_and_wait(chan, "vm-messages");
9832  }
9833  }
9834 
9835  return res;
9836 }
9837 
9838 /* SWEDISH syntax */
9839 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
9840 {
9841  /* Introduce messages they have */
9842  int res;
9843 
9844  res = ast_play_and_wait(chan, "vm-youhave");
9845  if (res)
9846  return res;
9847 
9848  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
9849  res = ast_play_and_wait(chan, "vm-no");
9850  res = res ? res : ast_play_and_wait(chan, "vm-messages");
9851  return res;
9852  }
9853 
9854  if (vms->newmessages) {
9855  if (vms->newmessages == 1) {
9856  res = ast_play_and_wait(chan, "digits/ett");
9857  res = res ? res : ast_play_and_wait(chan, "vm-nytt");
9858  res = res ? res : ast_play_and_wait(chan, "vm-message");
9859  } else {
9860  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
9861  res = res ? res : ast_play_and_wait(chan, "vm-nya");
9862  res = res ? res : ast_play_and_wait(chan, "vm-messages");
9863  }
9864  if (!res && vms->oldmessages)
9865  res = ast_play_and_wait(chan, "vm-and");
9866  }
9867  if (!res && vms->oldmessages) {
9868  if (vms->oldmessages == 1) {
9869  res = ast_play_and_wait(chan, "digits/ett");
9870  res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
9871  res = res ? res : ast_play_and_wait(chan, "vm-message");
9872  } else {
9873  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
9874  res = res ? res : ast_play_and_wait(chan, "vm-gamla");
9875  res = res ? res : ast_play_and_wait(chan, "vm-messages");
9876  }
9877  }
9878 
9879  return res;
9880 }
9881 
9882 /* NORWEGIAN syntax */
9883 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
9884 {
9885  /* Introduce messages they have */
9886  int res;
9887 
9888  res = ast_play_and_wait(chan, "vm-youhave");
9889  if (res)
9890  return res;
9891 
9892  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
9893  res = ast_play_and_wait(chan, "vm-no");
9894  res = res ? res : ast_play_and_wait(chan, "vm-messages");
9895  return res;
9896  }
9897 
9898  if (vms->newmessages) {
9899  if (vms->newmessages == 1) {
9900  res = ast_play_and_wait(chan, "digits/1");
9901  res = res ? res : ast_play_and_wait(chan, "vm-ny");
9902  res = res ? res : ast_play_and_wait(chan, "vm-message");
9903  } else {
9904  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
9905  res = res ? res : ast_play_and_wait(chan, "vm-nye");
9906  res = res ? res : ast_play_and_wait(chan, "vm-messages");
9907  }
9908  if (!res && vms->oldmessages)
9909  res = ast_play_and_wait(chan, "vm-and");
9910  }
9911  if (!res && vms->oldmessages) {
9912  if (vms->oldmessages == 1) {
9913  res = ast_play_and_wait(chan, "digits/1");
9914  res = res ? res : ast_play_and_wait(chan, "vm-gamel");
9915  res = res ? res : ast_play_and_wait(chan, "vm-message");
9916  } else {
9917  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
9918  res = res ? res : ast_play_and_wait(chan, "vm-gamle");
9919  res = res ? res : ast_play_and_wait(chan, "vm-messages");
9920  }
9921  }
9922 
9923  return res;
9924 }
9925 
9926 /* GERMAN syntax */
9927 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
9928 {
9929  /* Introduce messages they have */
9930  int res;
9931  res = ast_play_and_wait(chan, "vm-youhave");
9932  if (!res) {
9933  if (vms->newmessages) {
9934  if (vms->newmessages == 1)
9935  res = ast_play_and_wait(chan, "digits/1F");
9936  else
9937  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
9938  if (!res)
9939  res = ast_play_and_wait(chan, "vm-INBOX");
9940  if (vms->oldmessages && !res)
9941  res = ast_play_and_wait(chan, "vm-and");
9942  else if (!res) {
9943  if (vms->newmessages == 1)
9944  res = ast_play_and_wait(chan, "vm-message");
9945  else
9946  res = ast_play_and_wait(chan, "vm-messages");
9947  }
9948 
9949  }
9950  if (!res && vms->oldmessages) {
9951  if (vms->oldmessages == 1)
9952  res = ast_play_and_wait(chan, "digits/1F");
9953  else
9954  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
9955  if (!res)
9956  res = ast_play_and_wait(chan, "vm-Old");
9957  if (!res) {
9958  if (vms->oldmessages == 1)
9959  res = ast_play_and_wait(chan, "vm-message");
9960  else
9961  res = ast_play_and_wait(chan, "vm-messages");
9962  }
9963  }
9964  if (!res) {
9965  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
9966  res = ast_play_and_wait(chan, "vm-no");
9967  if (!res)
9968  res = ast_play_and_wait(chan, "vm-messages");
9969  }
9970  }
9971  }
9972  return res;
9973 }
9974 
9975 /* SPANISH syntax */
9976 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
9977 {
9978  /* Introduce messages they have */
9979  int res;
9980  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
9981  res = ast_play_and_wait(chan, "vm-youhaveno");
9982  if (!res)
9983  res = ast_play_and_wait(chan, "vm-messages");
9984  } else {
9985  res = ast_play_and_wait(chan, "vm-youhave");
9986  }
9987  if (!res) {
9988  if (vms->newmessages) {
9989  if (!res) {
9990  if (vms->newmessages == 1) {
9991  res = ast_play_and_wait(chan, "digits/1M");
9992  if (!res)
9993  res = ast_play_and_wait(chan, "vm-message");
9994  if (!res)
9995  res = ast_play_and_wait(chan, "vm-INBOXs");
9996  } else {
9997  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
9998  if (!res)
9999  res = ast_play_and_wait(chan, "vm-messages");
10000  if (!res)
10001  res = ast_play_and_wait(chan, "vm-INBOX");
10002  }
10003  }
10004  if (vms->oldmessages && !res)
10005  res = ast_play_and_wait(chan, "vm-and");
10006  }
10007  if (vms->oldmessages) {
10008  if (!res) {
10009  if (vms->oldmessages == 1) {
10010  res = ast_play_and_wait(chan, "digits/1M");
10011  if (!res)
10012  res = ast_play_and_wait(chan, "vm-message");
10013  if (!res)
10014  res = ast_play_and_wait(chan, "vm-Olds");
10015  } else {
10016  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10017  if (!res)
10018  res = ast_play_and_wait(chan, "vm-messages");
10019  if (!res)
10020  res = ast_play_and_wait(chan, "vm-Old");
10021  }
10022  }
10023  }
10024  }
10025 return res;
10026 }
10027 
10028 /* BRAZILIAN PORTUGUESE syntax */
10029 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
10030  /* Introduce messages they have */
10031  int res;
10032  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10033  res = ast_play_and_wait(chan, "vm-nomessages");
10034  return res;
10035  } else {
10036  res = ast_play_and_wait(chan, "vm-youhave");
10037  }
10038  if (vms->newmessages) {
10039  if (!res)
10040  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10041  if (vms->newmessages == 1) {
10042  if (!res)
10043  res = ast_play_and_wait(chan, "vm-message");
10044  if (!res)
10045  res = ast_play_and_wait(chan, "vm-INBOXs");
10046  } else {
10047  if (!res)
10048  res = ast_play_and_wait(chan, "vm-messages");
10049  if (!res)
10050  res = ast_play_and_wait(chan, "vm-INBOX");
10051  }
10052  if (vms->oldmessages && !res)
10053  res = ast_play_and_wait(chan, "vm-and");
10054  }
10055  if (vms->oldmessages) {
10056  if (!res)
10057  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10058  if (vms->oldmessages == 1) {
10059  if (!res)
10060  res = ast_play_and_wait(chan, "vm-message");
10061  if (!res)
10062  res = ast_play_and_wait(chan, "vm-Olds");
10063  } else {
10064  if (!res)
10065  res = ast_play_and_wait(chan, "vm-messages");
10066  if (!res)
10067  res = ast_play_and_wait(chan, "vm-Old");
10068  }
10069  }
10070  return res;
10071 }
10072 
10073 /* FRENCH syntax */
10074 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
10075 {
10076  /* Introduce messages they have */
10077  int res;
10078  res = ast_play_and_wait(chan, "vm-youhave");
10079  if (!res) {
10080  if (vms->newmessages) {
10081  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10082  if (!res)
10083  res = ast_play_and_wait(chan, "vm-INBOX");
10084  if (vms->oldmessages && !res)
10085  res = ast_play_and_wait(chan, "vm-and");
10086  else if (!res) {
10087  if (vms->newmessages == 1)
10088  res = ast_play_and_wait(chan, "vm-message");
10089  else
10090  res = ast_play_and_wait(chan, "vm-messages");
10091  }
10092 
10093  }
10094  if (!res && vms->oldmessages) {
10095  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10096  if (!res)
10097  res = ast_play_and_wait(chan, "vm-Old");
10098  if (!res) {
10099  if (vms->oldmessages == 1)
10100  res = ast_play_and_wait(chan, "vm-message");
10101  else
10102  res = ast_play_and_wait(chan, "vm-messages");
10103  }
10104  }
10105  if (!res) {
10106  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10107  res = ast_play_and_wait(chan, "vm-no");
10108  if (!res)
10109  res = ast_play_and_wait(chan, "vm-messages");
10110  }
10111  }
10112  }
10113  return res;
10114 }
10115 
10116 /* DUTCH syntax */
10117 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
10118 {
10119  /* Introduce messages they have */
10120  int res;
10121  res = ast_play_and_wait(chan, "vm-youhave");
10122  if (!res) {
10123  if (vms->newmessages) {
10124  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10125  if (!res) {
10126  if (vms->newmessages == 1)
10127  res = ast_play_and_wait(chan, "vm-INBOXs");
10128  else
10129  res = ast_play_and_wait(chan, "vm-INBOX");
10130  }
10131  if (vms->oldmessages && !res)
10132  res = ast_play_and_wait(chan, "vm-and");
10133  else if (!res) {
10134  if (vms->newmessages == 1)
10135  res = ast_play_and_wait(chan, "vm-message");
10136  else
10137  res = ast_play_and_wait(chan, "vm-messages");
10138  }
10139 
10140  }
10141  if (!res && vms->oldmessages) {
10142  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10143  if (!res) {
10144  if (vms->oldmessages == 1)
10145  res = ast_play_and_wait(chan, "vm-Olds");
10146  else
10147  res = ast_play_and_wait(chan, "vm-Old");
10148  }
10149  if (!res) {
10150  if (vms->oldmessages == 1)
10151  res = ast_play_and_wait(chan, "vm-message");
10152  else
10153  res = ast_play_and_wait(chan, "vm-messages");
10154  }
10155  }
10156  if (!res) {
10157  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10158  res = ast_play_and_wait(chan, "vm-no");
10159  if (!res)
10160  res = ast_play_and_wait(chan, "vm-messages");
10161  }
10162  }
10163  }
10164  return res;
10165 }
10166 
10167 /* PORTUGUESE syntax */
10168 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
10169 {
10170  /* Introduce messages they have */
10171  int res;
10172  res = ast_play_and_wait(chan, "vm-youhave");
10173  if (!res) {
10174  if (vms->newmessages) {
10175  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10176  if (!res) {
10177  if (vms->newmessages == 1) {
10178  res = ast_play_and_wait(chan, "vm-message");
10179  if (!res)
10180  res = ast_play_and_wait(chan, "vm-INBOXs");
10181  } else {
10182  res = ast_play_and_wait(chan, "vm-messages");
10183  if (!res)
10184  res = ast_play_and_wait(chan, "vm-INBOX");
10185  }
10186  }
10187  if (vms->oldmessages && !res)
10188  res = ast_play_and_wait(chan, "vm-and");
10189  }
10190  if (!res && vms->oldmessages) {
10191  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10192  if (!res) {
10193  if (vms->oldmessages == 1) {
10194  res = ast_play_and_wait(chan, "vm-message");
10195  if (!res)
10196  res = ast_play_and_wait(chan, "vm-Olds");
10197  } else {
10198  res = ast_play_and_wait(chan, "vm-messages");
10199  if (!res)
10200  res = ast_play_and_wait(chan, "vm-Old");
10201  }
10202  }
10203  }
10204  if (!res) {
10205  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10206  res = ast_play_and_wait(chan, "vm-no");
10207  if (!res)
10208  res = ast_play_and_wait(chan, "vm-messages");
10209  }
10210  }
10211  }
10212  return res;
10213 }
10214 
10215 
10216 /* CZECH syntax */
10217 /* in czech there must be declension of word new and message
10218  * czech : english : czech : english
10219  * --------------------------------------------------------
10220  * vm-youhave : you have
10221  * vm-novou : one new : vm-zpravu : message
10222  * vm-nove : 2-4 new : vm-zpravy : messages
10223  * vm-novych : 5-infinite new : vm-zprav : messages
10224  * vm-starou : one old
10225  * vm-stare : 2-4 old
10226  * vm-starych : 5-infinite old
10227  * jednu : one - falling 4.
10228  * vm-no : no ( no messages )
10229  */
10230 
10231 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
10232 {
10233  int res;
10234  res = ast_play_and_wait(chan, "vm-youhave");
10235  if (!res) {
10236  if (vms->newmessages) {
10237  if (vms->newmessages == 1) {
10238  res = ast_play_and_wait(chan, "digits/jednu");
10239  } else {
10240  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10241  }
10242  if (!res) {
10243  if (vms->newmessages == 1)
10244  res = ast_play_and_wait(chan, "vm-novou");
10245  if ((vms->newmessages) > 1 && (vms->newmessages < 5))
10246  res = ast_play_and_wait(chan, "vm-nove");
10247  if (vms->newmessages > 4)
10248  res = ast_play_and_wait(chan, "vm-novych");
10249  }
10250  if (vms->oldmessages && !res)
10251  res = ast_play_and_wait(chan, "vm-and");
10252  else if (!res) {
10253  if (vms->newmessages == 1)
10254  res = ast_play_and_wait(chan, "vm-zpravu");
10255  if ((vms->newmessages) > 1 && (vms->newmessages < 5))
10256  res = ast_play_and_wait(chan, "vm-zpravy");
10257  if (vms->newmessages > 4)
10258  res = ast_play_and_wait(chan, "vm-zprav");
10259  }
10260  }
10261  if (!res && vms->oldmessages) {
10262  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10263  if (!res) {
10264  if (vms->oldmessages == 1)
10265  res = ast_play_and_wait(chan, "vm-starou");
10266  if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
10267  res = ast_play_and_wait(chan, "vm-stare");
10268  if (vms->oldmessages > 4)
10269  res = ast_play_and_wait(chan, "vm-starych");
10270  }
10271  if (!res) {
10272  if (vms->oldmessages == 1)
10273  res = ast_play_and_wait(chan, "vm-zpravu");
10274  if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
10275  res = ast_play_and_wait(chan, "vm-zpravy");
10276  if (vms->oldmessages > 4)
10277  res = ast_play_and_wait(chan, "vm-zprav");
10278  }
10279  }
10280  if (!res) {
10281  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10282  res = ast_play_and_wait(chan, "vm-no");
10283  if (!res)
10284  res = ast_play_and_wait(chan, "vm-zpravy");
10285  }
10286  }
10287  }
10288  return res;
10289 }
10290 
10291 /* CHINESE (Taiwan) syntax */
10292 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
10293 {
10294  int res;
10295  /* Introduce messages they have */
10296  res = ast_play_and_wait(chan, "vm-you");
10297 
10298  if (!res && vms->newmessages) {
10299  res = ast_play_and_wait(chan, "vm-have");
10300  if (!res)
10301  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10302  if (!res)
10303  res = ast_play_and_wait(chan, "vm-tong");
10304  if (!res)
10305  res = ast_play_and_wait(chan, "vm-INBOX");
10306  if (vms->oldmessages && !res)
10307  res = ast_play_and_wait(chan, "vm-and");
10308  else if (!res)
10309  res = ast_play_and_wait(chan, "vm-messages");
10310  }
10311  if (!res && vms->oldmessages) {
10312  res = ast_play_and_wait(chan, "vm-have");
10313  if (!res)
10314  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10315  if (!res)
10316  res = ast_play_and_wait(chan, "vm-tong");
10317  if (!res)
10318  res = ast_play_and_wait(chan, "vm-Old");
10319  if (!res)
10320  res = ast_play_and_wait(chan, "vm-messages");
10321  }
10322  if (!res && !vms->oldmessages && !vms->newmessages) {
10323  res = ast_play_and_wait(chan, "vm-haveno");
10324  if (!res)
10325  res = ast_play_and_wait(chan, "vm-messages");
10326  }
10327  return res;
10328 }
10329 
10330 /* Vietnamese syntax */
10331 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
10332 {
10333  int res;
10334 
10335  /* Introduce messages they have */
10336  res = ast_play_and_wait(chan, "vm-youhave");
10337  if (!res) {
10338  if (vms->newmessages) {
10339  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10340  if (!res)
10341  res = ast_play_and_wait(chan, "vm-INBOX");
10342  if (vms->oldmessages && !res)
10343  res = ast_play_and_wait(chan, "vm-and");
10344  }
10345  if (!res && vms->oldmessages) {
10346  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10347  if (!res)
10348  res = ast_play_and_wait(chan, "vm-Old");
10349  }
10350  if (!res) {
10351  if (!vms->oldmessages && !vms->newmessages) {
10352  res = ast_play_and_wait(chan, "vm-no");
10353  if (!res)
10354  res = ast_play_and_wait(chan, "vm-message");
10355  }
10356  }
10357  }
10358  return res;
10359 }
10360 
10361 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
10362 {
10363  char prefile[256];
10364 
10365  /* Notify the user that the temp greeting is set and give them the option to remove it */
10366  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
10367  if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10368  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
10369  if (ast_fileexists(prefile, NULL, NULL) > 0) {
10370  ast_play_and_wait(chan, "vm-tempgreetactive");
10371  }
10372  DISPOSE(prefile, -1);
10373  }
10374 
10375  /* Play voicemail intro - syntax is different for different languages */
10376  if (0) {
10377  return 0;
10378  } else if (!strncasecmp(ast_channel_language(chan), "cs", 2)) { /* CZECH syntax */
10379  return vm_intro_cs(chan, vms);
10380  } else if (!strncasecmp(ast_channel_language(chan), "cz", 2)) { /* deprecated CZECH syntax */
10381  static int deprecation_warning = 0;
10382  if (deprecation_warning++ % 10 == 0) {
10383  ast_log(LOG_WARNING, "cz is not a standard language code. Please switch to using cs instead.\n");
10384  }
10385  return vm_intro_cs(chan, vms);
10386  } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
10387  return vm_intro_de(chan, vms);
10388  } else if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH syntax */
10389  return vm_intro_es(chan, vms);
10390  } else if (!strncasecmp(ast_channel_language(chan), "fr", 2)) { /* FRENCH syntax */
10391  return vm_intro_fr(chan, vms);
10392  } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
10393  return vm_intro_gr(chan, vms);
10394  } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW syntax */
10395  return vm_intro_he(chan, vms);
10396  } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */
10397  return vm_intro_is(chan, vms);
10398  } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
10399  return vm_intro_it(chan, vms);
10400  } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE syntax */
10401  return vm_intro_ja(chan, vms);
10402  } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
10403  return vm_intro_nl(chan, vms);
10404  } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
10405  return vm_intro_no(chan, vms);
10406  } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
10407  return vm_intro_pl(chan, vms);
10408  } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* BRAZILIAN PORTUGUESE syntax */
10409  return vm_intro_pt_BR(chan, vms);
10410  } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE syntax */
10411  return vm_intro_pt(chan, vms);
10412  } else if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* RUSSIAN syntax */
10413  return vm_intro_multilang(chan, vms, "n");
10414  } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
10415  return vm_intro_se(chan, vms);
10416  } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* UKRAINIAN syntax */
10417  return vm_intro_multilang(chan, vms, "n");
10418  } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
10419  return vm_intro_vi(chan, vms);
10420  } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
10421  return vm_intro_zh(chan, vms);
10422  } else { /* Default to ENGLISH */
10423  return vm_intro_en(chan, vms);
10424  }
10425 }
10426 
10427 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
10428 {
10429  int res = 0;
10430  /* Play instructions and wait for new command */
10431  while (!res) {
10432  if (vms->starting) {
10433  if (vms->lastmsg > -1) {
10434  if (skipadvanced)
10435  res = ast_play_and_wait(chan, "vm-onefor-full");
10436  else
10437  res = ast_play_and_wait(chan, "vm-onefor");
10438  if (!res)
10439  res = vm_play_folder_name(chan, vms->vmbox);
10440  }
10441  if (!res) {
10442  if (skipadvanced)
10443  res = ast_play_and_wait(chan, "vm-opts-full");
10444  else
10445  res = ast_play_and_wait(chan, "vm-opts");
10446  }
10447  } else {
10448  /* Added for additional help */
10449  if (skipadvanced) {
10450  res = ast_play_and_wait(chan, "vm-onefor-full");
10451  if (!res)
10452  res = vm_play_folder_name(chan, vms->vmbox);
10453  res = ast_play_and_wait(chan, "vm-opts-full");
10454  }
10455  /* Logic:
10456  * If the current message is not the first OR
10457  * if we're listening to the first new message and there are
10458  * also urgent messages, then prompt for navigation to the
10459  * previous message
10460  */
10461  if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
10462  res = ast_play_and_wait(chan, "vm-prev");
10463  }
10464  if (!res && !skipadvanced)
10465  res = ast_play_and_wait(chan, "vm-advopts");
10466  if (!res)
10467  res = ast_play_and_wait(chan, "vm-repeat");
10468  /* Logic:
10469  * If we're not listening to the last message OR
10470  * we're listening to the last urgent message and there are
10471  * also new non-urgent messages, then prompt for navigation
10472  * to the next message
10473  */
10474  if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
10475  (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
10476  res = ast_play_and_wait(chan, "vm-next");
10477  }
10478  if (!res) {
10479  int curmsg_deleted;
10480 #ifdef IMAP_STORAGE
10481  ast_mutex_lock(&vms->lock);
10482 #endif
10483  curmsg_deleted = vms->deleted[vms->curmsg];
10484 #ifdef IMAP_STORAGE
10485  ast_mutex_unlock(&vms->lock);
10486 #endif
10487  if (!curmsg_deleted) {
10488  res = ast_play_and_wait(chan, "vm-delete");
10489  } else {
10490  res = ast_play_and_wait(chan, "vm-undelete");
10491  }
10492  if (!res) {
10493  res = ast_play_and_wait(chan, "vm-toforward");
10494  }
10495  if (!res) {
10496  res = ast_play_and_wait(chan, "vm-savemessage");
10497  }
10498  }
10499  }
10500  if (!res) {
10501  res = ast_play_and_wait(chan, "vm-helpexit");
10502  }
10503  if (!res)
10504  res = ast_waitfordigit(chan, 6000);
10505  if (!res) {
10506  vms->repeats++;
10507  if (vms->repeats > 2) {
10508  res = 't';
10509  }
10510  }
10511  }
10512  return res;
10513 }
10514 
10515 static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
10516 {
10517  int res = 0;
10518  /* Play instructions and wait for new command */
10519  while (!res) {
10520  if (vms->starting) {
10521  if (vms->lastmsg > -1) {
10522  res = vm_play_folder_name(chan, vms->vmbox);
10523  if (!res)
10524  res = ast_play_and_wait(chan, "jp-wa");
10525  if (!res)
10526  res = ast_play_and_wait(chan, "digits/1");
10527  if (!res)
10528  res = ast_play_and_wait(chan, "jp-wo");
10529  if (!res)
10530  res = ast_play_and_wait(chan, "silence/1");
10531  }
10532  if (!res)
10533  res = ast_play_and_wait(chan, "vm-opts");
10534  } else {
10535  /* Added for additional help */
10536  if (skipadvanced) {
10537  res = vm_play_folder_name(chan, vms->vmbox);
10538  if (!res)
10539  res = ast_play_and_wait(chan, "jp-wa");
10540  if (!res)
10541  res = ast_play_and_wait(chan, "digits/1");
10542  if (!res)
10543  res = ast_play_and_wait(chan, "jp-wo");
10544  if (!res)
10545  res = ast_play_and_wait(chan, "silence/1");
10546  res = ast_play_and_wait(chan, "vm-opts-full");
10547  }
10548  /* Logic:
10549  * If the current message is not the first OR
10550  * if we're listening to the first new message and there are
10551  * also urgent messages, then prompt for navigation to the
10552  * previous message
10553  */
10554  if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
10555  res = ast_play_and_wait(chan, "vm-prev");
10556  }
10557  if (!res && !skipadvanced)
10558  res = ast_play_and_wait(chan, "vm-advopts");
10559  if (!res)
10560  res = ast_play_and_wait(chan, "vm-repeat");
10561  /* Logic:
10562  * If we're not listening to the last message OR
10563  * we're listening to the last urgent message and there are
10564  * also new non-urgent messages, then prompt for navigation
10565  * to the next message
10566  */
10567  if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
10568  (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
10569  res = ast_play_and_wait(chan, "vm-next");
10570  }
10571  if (!res) {
10572  int curmsg_deleted;
10573 #ifdef IMAP_STORAGE
10574  ast_mutex_lock(&vms->lock);
10575 #endif
10576  curmsg_deleted = vms->deleted[vms->curmsg];
10577 #ifdef IMAP_STORAGE
10578  ast_mutex_unlock(&vms->lock);
10579 #endif
10580  if (!curmsg_deleted) {
10581  res = ast_play_and_wait(chan, "vm-delete");
10582  } else {
10583  res = ast_play_and_wait(chan, "vm-undelete");
10584  }
10585  if (!res) {
10586  res = ast_play_and_wait(chan, "vm-toforward");
10587  }
10588  if (!res) {
10589  res = ast_play_and_wait(chan, "vm-savemessage");
10590  }
10591  }
10592  }
10593 
10594  if (!res) {
10595  res = ast_play_and_wait(chan, "vm-helpexit");
10596  }
10597  if (!res)
10598  res = ast_waitfordigit(chan, 6000);
10599  if (!res) {
10600  vms->repeats++;
10601  if (vms->repeats > 2) {
10602  res = 't';
10603  }
10604  }
10605 
10606  }
10607 
10608  return res;
10609 }
10610 
10611 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
10612 {
10613  int res = 0;
10614  /* Play instructions and wait for new command */
10615  while (!res) {
10616  if (vms->lastmsg > -1) {
10617  res = ast_play_and_wait(chan, "vm-listen");
10618  if (!res)
10619  res = vm_play_folder_name(chan, vms->vmbox);
10620  if (!res)
10621  res = ast_play_and_wait(chan, "press");
10622  if (!res)
10623  res = ast_play_and_wait(chan, "digits/1");
10624  }
10625  if (!res)
10626  res = ast_play_and_wait(chan, "vm-opts");
10627  if (!res) {
10628  vms->starting = 0;
10629  return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
10630  }
10631  }
10632  return res;
10633 }
10634 
10635 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
10636 {
10637  if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
10638  return vm_instructions_ja(chan, vmu, vms, skipadvanced, in_urgent);
10639  } else if (vms->starting && !strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
10640  return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
10641  } else { /* Default to ENGLISH */
10642  return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
10643  }
10644 }
10645 
10646 static int vm_newuser_setup(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
10647 {
10648  int cmd = 0;
10649  int duration = 0;
10650  int tries = 0;
10651  char newpassword[80] = "";
10652  char newpassword2[80] = "";
10653  char prefile[PATH_MAX] = "";
10654  unsigned char buf[256];
10655  int bytes = 0;
10656 
10657  ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
10658  if (ast_adsi_available(chan)) {
10659  bytes += adsi_logo(buf + bytes);
10660  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
10661  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
10662  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
10663  bytes += ast_adsi_voice_mode(buf + bytes, 0);
10664  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
10665  }
10666 
10667  /* If forcename is set, have the user record their name */
10668  if (ast_test_flag(vmu, VM_FORCENAME)) {
10669  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
10670  if (ast_fileexists(prefile, NULL, NULL) < 1) {
10671  cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
10672  if (cmd < 0 || cmd == 't' || cmd == '#')
10673  return cmd;
10674  }
10675  }
10676 
10677  /* If forcegreetings is set, have the user record their greetings */
10678  if (ast_test_flag(vmu, VM_FORCEGREET)) {
10679  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
10680  if (ast_fileexists(prefile, NULL, NULL) < 1) {
10681  cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
10682  if (cmd < 0 || cmd == 't' || cmd == '#')
10683  return cmd;
10684  }
10685 
10686  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
10687  if (ast_fileexists(prefile, NULL, NULL) < 1) {
10688  cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
10689  if (cmd < 0 || cmd == 't' || cmd == '#')
10690  return cmd;
10691  }
10692  }
10693 
10694  /*
10695  * Change the password last since new users will be able to skip over any steps this one comes before
10696  * by hanging up and calling back to voicemail main since the password is used to verify new user status.
10697  */
10698  for (;;) {
10699  newpassword[1] = '\0';
10700  newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
10701  if (cmd == '#')
10702  newpassword[0] = '\0';
10703  if (cmd < 0 || cmd == 't' || cmd == '#')
10704  return cmd;
10705  cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
10706  if (cmd < 0 || cmd == 't' || cmd == '#')
10707  return cmd;
10708  cmd = check_password(vmu, newpassword); /* perform password validation */
10709  if (cmd != 0) {
10710  ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
10711  cmd = ast_play_and_wait(chan, vm_invalid_password);
10712  } else {
10713  newpassword2[1] = '\0';
10714  newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
10715  if (cmd == '#')
10716  newpassword2[0] = '\0';
10717  if (cmd < 0 || cmd == 't' || cmd == '#')
10718  return cmd;
10719  cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
10720  if (cmd < 0 || cmd == 't' || cmd == '#')
10721  return cmd;
10722  if (!strcmp(newpassword, newpassword2))
10723  break;
10724  ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
10725  cmd = ast_play_and_wait(chan, vm_mismatch);
10726  }
10727  if (++tries == 3)
10728  return -1;
10729  if (cmd != 0) {
10730  cmd = ast_play_and_wait(chan, vm_pls_try_again);
10731  }
10732  }
10733  if (pwdchange & PWDCHANGE_INTERNAL)
10734  vm_change_password(vmu, newpassword);
10735  if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
10736  vm_change_password_shell(vmu, newpassword);
10737 
10738  ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
10739  cmd = ast_play_and_wait(chan, vm_passchanged);
10740 
10741  return cmd;
10742 }
10743 
10744 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
10745 {
10746  int cmd = 0;
10747  int retries = 0;
10748  int duration = 0;
10749  char newpassword[80] = "";
10750  char newpassword2[80] = "";
10751  char prefile[PATH_MAX] = "";
10752  unsigned char buf[256];
10753  int bytes = 0;
10754 
10755  ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
10756  if (ast_adsi_available(chan)) {
10757  bytes += adsi_logo(buf + bytes);
10758  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
10759  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
10760  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
10761  bytes += ast_adsi_voice_mode(buf + bytes, 0);
10762  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
10763  }
10764  while ((cmd >= 0) && (cmd != 't')) {
10765  if (cmd)
10766  retries = 0;
10767  switch (cmd) {
10768  case '1': /* Record your unavailable message */
10769  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
10770  cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
10771  break;
10772  case '2': /* Record your busy message */
10773  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
10774  cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
10775  break;
10776  case '3': /* Record greeting */
10777  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
10778  cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
10779  break;
10780  case '4': /* manage the temporary greeting */
10781  cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
10782  break;
10783  case '5': /* change password */
10784  if (vmu->password[0] == '-') {
10785  cmd = ast_play_and_wait(chan, "vm-no");
10786  break;
10787  }
10788  newpassword[1] = '\0';
10789  newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
10790  if (cmd == '#')
10791  newpassword[0] = '\0';
10792  else {
10793  if (cmd < 0)
10794  break;
10795  if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
10796  break;
10797  }
10798  }
10799  cmd = check_password(vmu, newpassword); /* perform password validation */
10800  if (cmd != 0) {
10801  ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
10802  cmd = ast_play_and_wait(chan, vm_invalid_password);
10803  if (!cmd) {
10804  cmd = ast_play_and_wait(chan, vm_pls_try_again);
10805  }
10806  break;
10807  }
10808  newpassword2[1] = '\0';
10809  newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
10810  if (cmd == '#')
10811  newpassword2[0] = '\0';
10812  else {
10813  if (cmd < 0)
10814  break;
10815 
10816  if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
10817  break;
10818  }
10819  }
10820  if (strcmp(newpassword, newpassword2)) {
10821  ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
10822  cmd = ast_play_and_wait(chan, vm_mismatch);
10823  if (!cmd) {
10824  cmd = ast_play_and_wait(chan, vm_pls_try_again);
10825  }
10826  break;
10827  }
10828 
10829  if (pwdchange & PWDCHANGE_INTERNAL) {
10830  vm_change_password(vmu, newpassword);
10831  }
10832  if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
10833  vm_change_password_shell(vmu, newpassword);
10834  }
10835 
10836  ast_debug(1, "User %s set password to %s of length %d\n",
10837  vms->username, newpassword, (int) strlen(newpassword));
10838  cmd = ast_play_and_wait(chan, vm_passchanged);
10839  break;
10840  case '*':
10841  cmd = 't';
10842  break;
10843  default:
10844  cmd = 0;
10845  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
10846  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
10847  if (ast_fileexists(prefile, NULL, NULL)) {
10848  cmd = ast_play_and_wait(chan, "vm-tmpexists");
10849  }
10850  DISPOSE(prefile, -1);
10851  if (!cmd) {
10852  cmd = ast_play_and_wait(chan, "vm-options");
10853  }
10854  if (!cmd) {
10855  cmd = ast_waitfordigit(chan, 6000);
10856  }
10857  if (!cmd) {
10858  retries++;
10859  }
10860  if (retries > 3) {
10861  cmd = 't';
10862  }
10863  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
10864  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
10865  }
10866  }
10867  if (cmd == 't')
10868  cmd = 0;
10869  return cmd;
10870 }
10871 
10872 /*!
10873  * \brief The handler for 'record a temporary greeting'.
10874  * \param chan
10875  * \param vmu
10876  * \param vms
10877  * \param fmtc
10878  * \param record_gain
10879  *
10880  * This is option 4 from the mailbox options menu.
10881  * This function manages the following promptings:
10882  * 1: play / record / review the temporary greeting. : invokes play_record_review().
10883  * 2: remove (delete) the temporary greeting.
10884  * *: return to the main menu.
10885  *
10886  * \return zero on success, -1 on error.
10887  */
10888 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
10889 {
10890  int cmd = 0;
10891  int retries = 0;
10892  int duration = 0;
10893  char prefile[PATH_MAX] = "";
10894  unsigned char buf[256];
10895  int bytes = 0;
10896 
10897  if (ast_adsi_available(chan)) {
10898  bytes += adsi_logo(buf + bytes);
10899  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
10900  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
10901  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
10902  bytes += ast_adsi_voice_mode(buf + bytes, 0);
10903  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
10904  }
10905 
10906  ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
10907  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
10908  while ((cmd >= 0) && (cmd != 't')) {
10909  if (cmd)
10910  retries = 0;
10911  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
10912  if (ast_fileexists(prefile, NULL, NULL) <= 0) {
10913  cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
10914  if (cmd == -1) {
10915  break;
10916  }
10917  cmd = 't';
10918  } else {
10919  switch (cmd) {
10920  case '1':
10921  cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
10922  break;
10923  case '2':
10924  DELETE(prefile, -1, prefile, vmu);
10925  ast_play_and_wait(chan, "vm-tempremoved");
10926  cmd = 't';
10927  break;
10928  case '*':
10929  cmd = 't';
10930  break;
10931  default:
10932  cmd = ast_play_and_wait(chan,
10933  ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
10934  "vm-tempgreeting2" : "vm-tempgreeting");
10935  if (!cmd) {
10936  cmd = ast_waitfordigit(chan, 6000);
10937  }
10938  if (!cmd) {
10939  retries++;
10940  }
10941  if (retries > 3) {
10942  cmd = 't';
10943  }
10944  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
10945  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
10946  }
10947  }
10948  DISPOSE(prefile, -1);
10949  }
10950  if (cmd == 't')
10951  cmd = 0;
10952  return cmd;
10953 }
10954 
10955 /*!
10956  * \brief Greek syntax for 'You have N messages' greeting.
10957  * \param chan
10958  * \param vms
10959  * \param vmu
10960  *
10961  * \return zero on success, -1 on error.
10962  */
10963 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
10964 {
10965  int cmd = 0;
10966 
10967  if (vms->lastmsg > -1) {
10968  cmd = play_message(chan, vmu, vms);
10969  } else {
10970  cmd = ast_play_and_wait(chan, "vm-youhaveno");
10971  if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
10972  if (!cmd) {
10973  snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
10974  cmd = ast_play_and_wait(chan, vms->fn);
10975  }
10976  if (!cmd)
10977  cmd = ast_play_and_wait(chan, "vm-messages");
10978  } else {
10979  if (!cmd)
10980  cmd = ast_play_and_wait(chan, "vm-messages");
10981  if (!cmd) {
10982  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
10983  cmd = ast_play_and_wait(chan, vms->fn);
10984  }
10985  }
10986  }
10987  return cmd;
10988 }
10989 
10990 /* Hebrew Syntax */
10991 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
10992 {
10993  int cmd = 0;
10994 
10995  if (vms->lastmsg > -1) {
10996  cmd = play_message(chan, vmu, vms);
10997  } else {
10998  if (!strcasecmp(vms->fn, "INBOX")) {
10999  cmd = ast_play_and_wait(chan, "vm-nonewmessages");
11000  } else {
11001  cmd = ast_play_and_wait(chan, "vm-nomessages");
11002  }
11003  }
11004  return cmd;
11005 }
11006 
11007 /*!
11008  * \brief Default English syntax for 'You have N messages' greeting.
11009  * \param chan
11010  * \param vms
11011  * \param vmu
11012  *
11013  * \return zero on success, -1 on error.
11014  */
11015 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11016 {
11017  int cmd = 0;
11018 
11019  if (vms->lastmsg > -1) {
11020  cmd = play_message(chan, vmu, vms);
11021  } else {
11022  cmd = ast_play_and_wait(chan, "vm-youhave");
11023  if (!cmd)
11024  cmd = ast_play_and_wait(chan, "vm-no");
11025  if (!cmd) {
11026  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11027  cmd = ast_play_and_wait(chan, vms->fn);
11028  }
11029  if (!cmd)
11030  cmd = ast_play_and_wait(chan, "vm-messages");
11031  }
11032  return cmd;
11033 }
11034 
11035 /*!
11036  *\brief Italian syntax for 'You have N messages' greeting.
11037  * \param chan
11038  * \param vms
11039  * \param vmu
11040  *
11041  * \return zero on success, -1 on error.
11042  */
11043 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11044 {
11045  int cmd;
11046 
11047  if (vms->lastmsg > -1) {
11048  cmd = play_message(chan, vmu, vms);
11049  } else {
11050  cmd = ast_play_and_wait(chan, "vm-no");
11051  if (!cmd)
11052  cmd = ast_play_and_wait(chan, "vm-message");
11053  if (!cmd) {
11054  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11055  cmd = ast_play_and_wait(chan, vms->fn);
11056  }
11057  }
11058  return cmd;
11059 }
11060 
11061 /*!
11062  * \brief Japanese syntax for 'You have N messages' greeting.
11063  * \param chan
11064  * \param vms
11065  * \param vmu
11066  *
11067  * \return zero on success, -1 on error.
11068  */
11069 static int vm_browse_messages_ja(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11070 {
11071  int cmd = 0;
11072 
11073  if (vms->lastmsg > -1) {
11074  cmd = play_message(chan, vmu, vms);
11075  } else {
11076  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11077  cmd = ast_play_and_wait(chan, vms->fn);
11078  if (!cmd)
11079  cmd = ast_play_and_wait(chan, "vm-messages");
11080  if (!cmd)
11081  cmd = ast_play_and_wait(chan, "jp-wa");
11082  if (!cmd)
11083  cmd = ast_play_and_wait(chan, "jp-arimasen");
11084  }
11085  return cmd;
11086 }
11087 
11088 /*!
11089  * \brief Spanish syntax for 'You have N messages' greeting.
11090  * \param chan
11091  * \param vms
11092  * \param vmu
11093  *
11094  * \return zero on success, -1 on error.
11095  */
11096 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11097 {
11098  int cmd;
11099 
11100  if (vms->lastmsg > -1) {
11101  cmd = play_message(chan, vmu, vms);
11102  } else {
11103  cmd = ast_play_and_wait(chan, "vm-youhaveno");
11104  if (!cmd)
11105  cmd = ast_play_and_wait(chan, "vm-messages");
11106  if (!cmd) {
11107  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11108  cmd = ast_play_and_wait(chan, vms->fn);
11109  }
11110  }
11111  return cmd;
11112 }
11113 
11114 /*!
11115  * \brief Portuguese syntax for 'You have N messages' greeting.
11116  * \param chan
11117  * \param vms
11118  * \param vmu
11119  *
11120  * \return zero on success, -1 on error.
11121  */
11122 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11123 {
11124  int cmd;
11125 
11126  if (vms->lastmsg > -1) {
11127  cmd = play_message(chan, vmu, vms);
11128  } else {
11129  cmd = ast_play_and_wait(chan, "vm-no");
11130  if (!cmd) {
11131  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11132  cmd = ast_play_and_wait(chan, vms->fn);
11133  }
11134  if (!cmd)
11135  cmd = ast_play_and_wait(chan, "vm-messages");
11136  }
11137  return cmd;
11138 }
11139 
11140 /*!
11141  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
11142  * \param chan
11143  * \param vms
11144  * \param vmu
11145  *
11146  * \return zero on success, -1 on error.
11147  */
11148 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11149 {
11150  int cmd;
11151 
11152  if (vms->lastmsg > -1) {
11153  cmd = play_message(chan, vmu, vms);
11154  } else {
11155  cmd = ast_play_and_wait(chan, "vm-you");
11156  if (!cmd)
11157  cmd = ast_play_and_wait(chan, "vm-haveno");
11158  if (!cmd)
11159  cmd = ast_play_and_wait(chan, "vm-messages");
11160  if (!cmd) {
11161  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11162  cmd = ast_play_and_wait(chan, vms->fn);
11163  }
11164  }
11165  return cmd;
11166 }
11167 
11168 /*!
11169  * \brief Vietnamese syntax for 'You have N messages' greeting.
11170  * \param chan
11171  * \param vms
11172  * \param vmu
11173  *
11174  * \return zero on success, -1 on error.
11175  */
11176 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11177 {
11178  int cmd = 0;
11179 
11180  if (vms->lastmsg > -1) {
11181  cmd = play_message(chan, vmu, vms);
11182  } else {
11183  cmd = ast_play_and_wait(chan, "vm-no");
11184  if (!cmd) {
11185  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11186  cmd = ast_play_and_wait(chan, vms->fn);
11187  }
11188  }
11189  return cmd;
11190 }
11191 
11192 /*!
11193  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
11194  * \param chan The channel for the current user. We read the language property from this.
11195  * \param vms passed into the language-specific vm_browse_messages function.
11196  * \param vmu passed into the language-specific vm_browse_messages function.
11197  *
11198  * The method to be invoked is determined by the value of language code property in the user's channel.
11199  * The default (when unable to match) is to use english.
11200  *
11201  * \return zero on success, -1 on error.
11202  */
11203 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11204 {
11205  if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH */
11206  return vm_browse_messages_es(chan, vms, vmu);
11207  } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK */
11208  return vm_browse_messages_gr(chan, vms, vmu);
11209  } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW */
11210  return vm_browse_messages_he(chan, vms, vmu);
11211  } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN */
11212  return vm_browse_messages_it(chan, vms, vmu);
11213  } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE */
11214  return vm_browse_messages_ja(chan, vms, vmu);
11215  } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE */
11216  return vm_browse_messages_pt(chan, vms, vmu);
11217  } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE */
11218  return vm_browse_messages_vi(chan, vms, vmu);
11219  } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) */
11220  return vm_browse_messages_zh(chan, vms, vmu);
11221  } else { /* Default to English syntax */
11222  return vm_browse_messages_en(chan, vms, vmu);
11223  }
11224 }
11225 
11226 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
11227  struct ast_vm_user *res_vmu, const char *context, const char *prefix,
11228  int skipuser, int max_logins, int silent)
11229 {
11230  int useadsi = 0, valid = 0, logretries = 0;
11231  char password[AST_MAX_EXTENSION], *passptr = NULL;
11232  struct ast_vm_user vmus, *vmu = NULL;
11233 
11234  /* If ADSI is supported, setup login screen */
11235  adsi_begin(chan, &useadsi);
11236  if (!skipuser && useadsi)
11237  adsi_login(chan);
11238  if (!silent && !skipuser && ast_streamfile(chan, vm_login, ast_channel_language(chan))) {
11239  ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
11240  return -1;
11241  }
11242 
11243  /* Authenticate them and get their mailbox/password */
11244 
11245  while (!valid && (logretries < max_logins)) {
11246  /* Prompt for, and read in the username */
11247  if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
11248  ast_log(AST_LOG_WARNING, "Couldn't read username\n");
11249  return -1;
11250  }
11251  if (ast_strlen_zero(mailbox)) {
11252  if (ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str) {
11253  ast_copy_string(mailbox, ast_channel_caller(chan)->id.number.str, mailbox_size);
11254  } else {
11255  ast_verb(3, "Username not entered\n");
11256  return -1;
11257  }
11258  } else if (mailbox[0] == '*') {
11259  /* user entered '*' */
11260  ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
11261  if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
11262  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
11263  return -1;
11264  }
11265  ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
11266  mailbox[0] = '\0';
11267  }
11268 
11269  if (useadsi)
11270  adsi_password(chan);
11271 
11272  if (!ast_strlen_zero(prefix)) {
11273  char fullusername[80];
11274 
11275  ast_copy_string(fullusername, prefix, sizeof(fullusername));
11276  strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
11277  ast_copy_string(mailbox, fullusername, mailbox_size);
11278  }
11279 
11280  ast_debug(1, "Before find user for mailbox %s\n", mailbox);
11281  memset(&vmus, 0, sizeof(vmus));
11282  vmu = find_user(&vmus, context, mailbox);
11283  if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
11284  /* saved password is blank, so don't bother asking */
11285  password[0] = '\0';
11286  } else {
11287  if (ast_streamfile(chan, vm_password, ast_channel_language(chan))) {
11288  ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
11289  free_user(vmu);
11290  return -1;
11291  }
11292  if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
11293  ast_log(AST_LOG_WARNING, "Unable to read password\n");
11294  free_user(vmu);
11295  return -1;
11296  } else if (password[0] == '*') {
11297  /* user entered '*' */
11298  ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
11299  if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
11300  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
11301  mailbox[0] = '*';
11302  free_user(vmu);
11303  return -1;
11304  }
11305  ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
11306  mailbox[0] = '\0';
11307  /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
11308  free_user(vmu);
11309  vmu = NULL;
11310  }
11311  }
11312 
11313  if (vmu) {
11314  passptr = vmu->password;
11315  if (passptr[0] == '-') passptr++;
11316  }
11317  if (vmu && !strcmp(passptr, password))
11318  valid++;
11319  else {
11320  ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
11321  if (!ast_strlen_zero(prefix))
11322  mailbox[0] = '\0';
11323  }
11324  logretries++;
11325  if (!valid) {
11326  if (skipuser || logretries >= max_logins) {
11327  if (ast_streamfile(chan, "vm-incorrect", ast_channel_language(chan))) {
11328  ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
11329  free_user(vmu);
11330  return -1;
11331  }
11332  if (ast_waitstream(chan, "")) { /* Channel is hung up */
11333  free_user(vmu);
11334  return -1;
11335  }
11336  } else {
11337  if (useadsi)
11338  adsi_login(chan);
11339  if (ast_streamfile(chan, "vm-incorrect-mailbox", ast_channel_language(chan))) {
11340  ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
11341  free_user(vmu);
11342  return -1;
11343  }
11344  }
11345  }
11346  }
11347  if (!valid && (logretries >= max_logins)) {
11348  ast_stopstream(chan);
11349  ast_play_and_wait(chan, "vm-goodbye");
11350  free_user(vmu);
11351  return -1;
11352  }
11353  if (vmu && !skipuser) {
11354  *res_vmu = *vmu;
11355  }
11356  return 0;
11357 }
11358 
11359 static int play_message_by_id_helper(struct ast_channel *chan,
11360  struct ast_vm_user *vmu,
11361  struct vm_state *vms,
11362  const char *msg_id)
11363 {
11364  if (message_range_and_existence_check(vms, &msg_id, 1, &vms->curmsg, vmu)) {
11365  return -1;
11366  }
11367  /* Found the msg, so play it back */
11368 
11369  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
11370 
11371 #ifdef IMAP_STORAGE
11372  /*IMAP storage stores any prepended message from a forward
11373  * as a separate file from the rest of the message
11374  */
11375  if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
11376  wait_file(chan, vms, vms->introfn);
11377  }
11378 #endif
11379  RETRIEVE(vms->curdir,vms->curmsg,vmu->mailbox, vmu->context);
11380 
11381  if ((wait_file(chan, vms, vms->fn)) < 0) {
11382  ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
11383  } else {
11384 #ifdef IMAP_STORAGE
11385  ast_mutex_lock(&vms->lock);
11386 #endif
11387  vms->heard[vms->curmsg] = 1;
11388 #ifdef IMAP_STORAGE
11389  ast_mutex_unlock(&vms->lock);
11390 #endif
11391  }
11392  DISPOSE(vms->curdir, vms->curmsg);
11393  return 0;
11394 }
11395 
11396 /*!
11397  * \brief Finds a message in a specific mailbox by msg_id and plays it to the channel
11398  *
11399  * \retval 0 Success
11400  * \retval -1 Failure
11401  */
11402 static int play_message_by_id(struct ast_channel *chan, const char *mailbox, const char *context, const char *msg_id)
11403 {
11404  struct vm_state vms;
11405  struct ast_vm_user *vmu = NULL, vmus;
11406  int res = 0;
11407  int open = 0;
11408  int played = 0;
11409  int i;
11410 
11411  memset(&vmus, 0, sizeof(vmus));
11412  memset(&vms, 0, sizeof(vms));
11413 
11414  if (!(vmu = find_user(&vmus, context, mailbox))) {
11415  goto play_msg_cleanup;
11416  }
11417 
11418  /* Iterate through every folder, find the msg, and play it */
11419  for (i = 0; i < ARRAY_LEN(mailbox_folders) && !played; i++) {
11420  ast_copy_string(vms.username, mailbox, sizeof(vms.username));
11421  vms.lastmsg = -1;
11422 
11423  /* open the mailbox state */
11424  if ((res = open_mailbox(&vms, vmu, i)) < 0) {
11425  ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
11426  res = -1;
11427  goto play_msg_cleanup;
11428  }
11429  open = 1;
11430 
11431  /* play msg if it exists in this mailbox */
11432  if ((vms.lastmsg != -1) && !(play_message_by_id_helper(chan, vmu, &vms, msg_id))) {
11433  played = 1;
11434  }
11435 
11436  /* close mailbox */
11437  if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
11438  res = -1;
11439  goto play_msg_cleanup;
11440  }
11441  open = 0;
11442  }
11443 
11444 play_msg_cleanup:
11445  if (!played) {
11446  res = -1;
11447  }
11448 
11449  if (vmu && open) {
11450  close_mailbox(&vms, vmu);
11451  }
11452 
11453 #ifdef IMAP_STORAGE
11454  if (vmu) {
11455  vmstate_delete(&vms);
11456  }
11457 #endif
11458 
11459  free_user(vmu);
11460 
11461  return res;
11462 }
11463 
11464 static int vm_playmsgexec(struct ast_channel *chan, const char *data)
11465 {
11466  char *parse;
11467  char *mailbox = NULL;
11468  char *context = NULL;
11469  int res;
11470 
11472  AST_APP_ARG(mailbox);
11473  AST_APP_ARG(msg_id);
11474  );
11475 
11476  if (ast_channel_state(chan) != AST_STATE_UP) {
11477  ast_debug(1, "Before ast_answer\n");
11478  ast_answer(chan);
11479  }
11480 
11481  if (ast_strlen_zero(data)) {
11482  return -1;
11483  }
11484 
11485  parse = ast_strdupa(data);
11486  AST_STANDARD_APP_ARGS(args, parse);
11487 
11488  if (ast_strlen_zero(args.mailbox) || ast_strlen_zero(args.msg_id)) {
11489  return -1;
11490  }
11491 
11492  if ((context = strchr(args.mailbox, '@'))) {
11493  *context++ = '\0';
11494  }
11495  mailbox = args.mailbox;
11496 
11497  res = play_message_by_id(chan, mailbox, context, args.msg_id);
11498  pbx_builtin_setvar_helper(chan, "VOICEMAIL_PLAYBACKSTATUS", res ? "FAILED" : "SUCCESS");
11499 
11500  return 0;
11501 }
11502 
11503 static int vm_execmain(struct ast_channel *chan, const char *data)
11504 {
11505  /* XXX This is, admittedly, some pretty horrendous code. For some
11506  reason it just seemed a lot easier to do with GOTO's. I feel
11507  like I'm back in my GWBASIC days. XXX */
11508  int res = -1;
11509  int cmd = 0;
11510  int valid = 0;
11511  char prefixstr[80] ="";
11512  char ext_context[256]="";
11513  int box;
11514  int useadsi = 0;
11515  int skipuser = 0;
11516  struct vm_state vms = {{0}};
11517  struct ast_vm_user *vmu = NULL, vmus = {{0}};
11518  char *context = NULL;
11519  int silentexit = 0;
11520  struct ast_flags flags = { 0 };
11521  signed char record_gain = 0;
11522  int play_auto = 0;
11523  int play_folder = 0;
11524  int in_urgent = 0;
11525 #ifdef IMAP_STORAGE
11526  int deleted = 0;
11527 #endif
11528 
11529  /* Add the vm_state to the active list and keep it active */
11530  vms.lastmsg = -1;
11531 
11532  ast_test_suite_event_notify("START", "Message: vm_execmain started");
11533  if (ast_channel_state(chan) != AST_STATE_UP) {
11534  ast_debug(1, "Before ast_answer\n");
11535  ast_answer(chan);
11536  }
11537 
11538  if (!ast_strlen_zero(data)) {
11539  char *opts[OPT_ARG_ARRAY_SIZE];
11540  char *parse;
11542  AST_APP_ARG(argv0);
11543  AST_APP_ARG(argv1);
11544  );
11545 
11546  parse = ast_strdupa(data);
11547 
11548  AST_STANDARD_APP_ARGS(args, parse);
11549 
11550  if (args.argc == 2) {
11551  if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
11552  return -1;
11553  if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
11554  int gain;
11555  if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
11556  if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
11557  ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
11558  return -1;
11559  } else {
11560  record_gain = (signed char) gain;
11561  }
11562  } else {
11563  ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
11564  }
11565  }
11566  if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
11567  play_auto = 1;
11568  if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
11569  /* See if it is a folder name first */
11570  if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
11571  if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
11572  play_folder = -1;
11573  }
11574  } else {
11575  play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
11576  }
11577  } else {
11578  ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
11579  }
11580  if (play_folder > 9 || play_folder < 0) {
11582  "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
11583  opts[OPT_ARG_PLAYFOLDER]);
11584  play_folder = 0;
11585  }
11586  }
11587  } else {
11588  /* old style options parsing */
11589  while (*(args.argv0)) {
11590  if (*(args.argv0) == 's')
11591  ast_set_flag(&flags, OPT_SILENT);
11592  else if (*(args.argv0) == 'p')
11594  else
11595  break;
11596  (args.argv0)++;
11597  }
11598 
11599  }
11600 
11601  valid = ast_test_flag(&flags, OPT_SILENT);
11602 
11603  if ((context = strchr(args.argv0, '@')))
11604  *context++ = '\0';
11605 
11606  if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
11607  ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
11608  else
11609  ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
11610 
11611  if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
11612  skipuser++;
11613  else
11614  valid = 0;
11615  }
11616 
11617  if (!valid)
11618  res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
11619 
11620  ast_debug(1, "After vm_authenticate\n");
11621 
11622  if (vms.username[0] == '*') {
11623  ast_debug(1, "user pressed * in context '%s'\n", ast_channel_context(chan));
11624 
11625  /* user entered '*' */
11626  if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
11627  ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
11628  res = 0; /* prevent hangup */
11629  goto out;
11630  }
11631  }
11632 
11633  if (!res) {
11634  valid = 1;
11635  if (!skipuser)
11636  vmu = &vmus;
11637  } else {
11638  res = 0;
11639  }
11640 
11641  /* If ADSI is supported, setup login screen */
11642  adsi_begin(chan, &useadsi);
11643 
11644  if (!valid) {
11645  goto out;
11646  }
11647  ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
11648 
11649 #ifdef IMAP_STORAGE
11650  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
11651  pthread_setspecific(ts_vmstate.key, &vms);
11652 
11653  vms.interactive = 1;
11654  vms.updated = 1;
11655  if (vmu)
11656  ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
11657  vmstate_insert(&vms);
11658  init_vm_state(&vms);
11659 #endif
11660 
11661  /* Set language from config to override channel language */
11662  if (!ast_strlen_zero(vmu->language)) {
11663  ast_channel_lock(chan);
11664  ast_channel_language_set(chan, vmu->language);
11665  ast_channel_unlock(chan);
11666  }
11667 
11668  /* Retrieve urgent, old and new message counts */
11669  ast_debug(1, "Before open_mailbox\n");
11670  res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
11671  if (res < 0)
11672  goto out;
11673  vms.oldmessages = vms.lastmsg + 1;
11674  ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
11675  /* check INBOX */
11676  res = open_mailbox(&vms, vmu, NEW_FOLDER);
11677  if (res < 0)
11678  goto out;
11679  vms.newmessages = vms.lastmsg + 1;
11680  ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
11681  /* Start in Urgent */
11682  in_urgent = 1;
11683  res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
11684  if (res < 0)
11685  goto out;
11686  vms.urgentmessages = vms.lastmsg + 1;
11687  ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
11688 
11689  /* Select proper mailbox FIRST!! */
11690  if (play_auto) {
11691  ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
11692  if (vms.urgentmessages) {
11693  in_urgent = 1;
11694  res = open_mailbox(&vms, vmu, 11);
11695  } else {
11696  in_urgent = 0;
11697  res = open_mailbox(&vms, vmu, play_folder);
11698  }
11699  if (res < 0)
11700  goto out;
11701 
11702  /* If there are no new messages, inform the user and hangup */
11703  if (vms.lastmsg == -1) {
11704  in_urgent = 0;
11705  cmd = vm_browse_messages(chan, &vms, vmu);
11706  res = 0;
11707  goto out;
11708  }
11709  } else {
11710  if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
11711  /* If we only have old messages start here */
11712  res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
11713  in_urgent = 0;
11714  play_folder = 1;
11715  if (res < 0)
11716  goto out;
11717  } else if (!vms.urgentmessages && vms.newmessages) {
11718  /* If we have new messages but none are urgent */
11719  in_urgent = 0;
11720  res = open_mailbox(&vms, vmu, NEW_FOLDER);
11721  if (res < 0)
11722  goto out;
11723  }
11724  }
11725 
11726  if (useadsi)
11727  adsi_status(chan, &vms);
11728  res = 0;
11729 
11730  /* Check to see if this is a new user */
11731  if (!strcasecmp(vmu->mailbox, vmu->password) &&
11733  if (ast_play_and_wait(chan, vm_newuser) == -1)
11734  ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
11735  cmd = vm_newuser_setup(chan, vmu, &vms, vmfmts, record_gain);
11736  if ((cmd == 't') || (cmd == '#')) {
11737  /* Timeout */
11738  ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
11739  res = 0;
11740  goto out;
11741  } else if (cmd < 0) {
11742  /* Hangup */
11743  ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
11744  res = -1;
11745  goto out;
11746  }
11747  }
11748 #ifdef IMAP_STORAGE
11749  ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
11750  if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
11751  ast_debug(1, "*** QUOTA EXCEEDED!!\n");
11752  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
11753  }
11754  ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
11755  if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
11756  ast_log(AST_LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
11757  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
11758  }
11759 #endif
11760 
11761  ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
11762  if (play_auto) {
11763  cmd = '1';
11764  } else {
11765  cmd = vm_intro(chan, vmu, &vms);
11766  }
11767  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
11768  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
11769 
11770  vms.repeats = 0;
11771  vms.starting = 1;
11772  while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
11773  /* Run main menu */
11774  switch (cmd) {
11775  case '1': /* First message */
11776  vms.curmsg = 0;
11777  /* Fall through */
11778  case '5': /* Play current message */
11779  ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
11780  cmd = vm_browse_messages(chan, &vms, vmu);
11781  break;
11782  case '2': /* Change folders */
11783  ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
11784  if (useadsi)
11785  adsi_folders(chan, 0, "Change to folder...");
11786 
11787  cmd = get_folder2(chan, "vm-changeto", 0);
11788  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
11789  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
11790  if (cmd == '#') {
11791  cmd = 0;
11792  } else if (cmd > 0) {
11793  cmd = cmd - '0';
11794  res = close_mailbox(&vms, vmu);
11795  if (res == ERROR_LOCK_PATH)
11796  goto out;
11797  /* If folder is not urgent, set in_urgent to zero! */
11798  if (cmd != 11) in_urgent = 0;
11799  res = open_mailbox(&vms, vmu, cmd);
11800  if (res < 0)
11801  goto out;
11802  play_folder = cmd;
11803  cmd = 0;
11804  }
11805  if (useadsi)
11806  adsi_status2(chan, &vms);
11807 
11808  if (!cmd) {
11809  cmd = vm_play_folder_name(chan, vms.vmbox);
11810  }
11811 
11812  vms.starting = 1;
11813  vms.curmsg = 0;
11814  break;
11815  case '3': /* Advanced options */
11816  ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
11817  cmd = 0;
11818  vms.repeats = 0;
11819  while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
11820  switch (cmd) {
11821  case '1': /* Reply */
11822  if (vms.lastmsg > -1 && !vms.starting) {
11823  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
11824  if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
11825  res = cmd;
11826  goto out;
11827  }
11828  } else {
11829  cmd = ast_play_and_wait(chan, "vm-sorry");
11830  }
11831  cmd = 't';
11832  break;
11833  case '2': /* Callback */
11834  if (!vms.starting)
11835  ast_verb(3, "Callback Requested\n");
11836  if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
11837  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
11838  if (cmd == 9) {
11839  silentexit = 1;
11840  goto out;
11841  } else if (cmd == ERROR_LOCK_PATH) {
11842  res = cmd;
11843  goto out;
11844  }
11845  } else {
11846  cmd = ast_play_and_wait(chan, "vm-sorry");
11847  }
11848  cmd = 't';
11849  break;
11850  case '3': /* Envelope */
11851  if (vms.lastmsg > -1 && !vms.starting) {
11852  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
11853  if (cmd == ERROR_LOCK_PATH) {
11854  res = cmd;
11855  goto out;
11856  }
11857  } else {
11858  cmd = ast_play_and_wait(chan, "vm-sorry");
11859  }
11860  cmd = 't';
11861  break;
11862  case '4': /* Dialout */
11863  if (!ast_strlen_zero(vmu->dialout)) {
11864  cmd = dialout(chan, vmu, NULL, vmu->dialout);
11865  if (cmd == 9) {
11866  silentexit = 1;
11867  goto out;
11868  }
11869  } else {
11870  cmd = ast_play_and_wait(chan, "vm-sorry");
11871  }
11872  cmd = 't';
11873  break;
11874 
11875  case '5': /* Leave VoiceMail */
11876  if (ast_test_flag(vmu, VM_SVMAIL)) {
11877  cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
11878  if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
11879  res = cmd;
11880  goto out;
11881  }
11882  } else {
11883  cmd = ast_play_and_wait(chan, "vm-sorry");
11884  }
11885  cmd = 't';
11886  break;
11887 
11888  case '*': /* Return to main menu */
11889  cmd = 't';
11890  break;
11891 
11892  default:
11893  cmd = 0;
11894  if (!vms.starting) {
11895  cmd = ast_play_and_wait(chan, "vm-toreply");
11896  }
11897  if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
11898  cmd = ast_play_and_wait(chan, "vm-tocallback");
11899  }
11900  if (!cmd && !vms.starting) {
11901  cmd = ast_play_and_wait(chan, "vm-tohearenv");
11902  }
11903  if (!ast_strlen_zero(vmu->dialout) && !cmd) {
11904  cmd = ast_play_and_wait(chan, "vm-tomakecall");
11905  }
11906  if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
11907  cmd = ast_play_and_wait(chan, "vm-leavemsg");
11908  }
11909  if (!cmd) {
11910  cmd = ast_play_and_wait(chan, "vm-starmain");
11911  }
11912  if (!cmd) {
11913  cmd = ast_waitfordigit(chan, 6000);
11914  }
11915  if (!cmd) {
11916  vms.repeats++;
11917  }
11918  if (vms.repeats > 3) {
11919  cmd = 't';
11920  }
11921  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
11922  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
11923  }
11924  }
11925  if (cmd == 't') {
11926  cmd = 0;
11927  vms.repeats = 0;
11928  }
11929  break;
11930  case '4': /* Go to the previous message */
11931  ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
11932  if (vms.curmsg > 0) {
11933  vms.curmsg--;
11934  cmd = play_message(chan, vmu, &vms);
11935  } else {
11936  /* Check if we were listening to new
11937  messages. If so, go to Urgent messages
11938  instead of saying "no more messages"
11939  */
11940  if (in_urgent == 0 && vms.urgentmessages > 0) {
11941  /* Check for Urgent messages */
11942  in_urgent = 1;
11943  res = close_mailbox(&vms, vmu);
11944  if (res == ERROR_LOCK_PATH)
11945  goto out;
11946  res = open_mailbox(&vms, vmu, 11); /* Open Urgent folder */
11947  if (res < 0)
11948  goto out;
11949  ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
11950  vms.curmsg = vms.lastmsg;
11951  if (vms.lastmsg < 0) {
11952  cmd = ast_play_and_wait(chan, "vm-nomore");
11953  }
11954  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
11955  vms.curmsg = vms.lastmsg;
11956  cmd = play_message(chan, vmu, &vms);
11957  } else {
11958  cmd = ast_play_and_wait(chan, "vm-nomore");
11959  }
11960  }
11961  break;
11962  case '6': /* Go to the next message */
11963  ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
11964  if (vms.curmsg < vms.lastmsg) {
11965  vms.curmsg++;
11966  cmd = play_message(chan, vmu, &vms);
11967  } else {
11968  if (in_urgent && vms.newmessages > 0) {
11969  /* Check if we were listening to urgent
11970  * messages. If so, go to regular new messages
11971  * instead of saying "no more messages"
11972  */
11973  in_urgent = 0;
11974  res = close_mailbox(&vms, vmu);
11975  if (res == ERROR_LOCK_PATH)
11976  goto out;
11977  res = open_mailbox(&vms, vmu, NEW_FOLDER);
11978  if (res < 0)
11979  goto out;
11980  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
11981  vms.curmsg = -1;
11982  if (vms.lastmsg < 0) {
11983  cmd = ast_play_and_wait(chan, "vm-nomore");
11984  }
11985  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
11986  vms.curmsg = 0;
11987  cmd = play_message(chan, vmu, &vms);
11988  } else {
11989  cmd = ast_play_and_wait(chan, "vm-nomore");
11990  }
11991  }
11992  break;
11993  case '7': /* Delete the current message */
11994  if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
11995  vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
11996  if (useadsi)
11997  adsi_delete(chan, &vms);
11998  if (vms.deleted[vms.curmsg]) {
11999  if (play_folder == 0) {
12000  if (in_urgent) {
12001  vms.urgentmessages--;
12002  } else {
12003  vms.newmessages--;
12004  }
12005  }
12006  else if (play_folder == 1)
12007  vms.oldmessages--;
12008  cmd = ast_play_and_wait(chan, "vm-deleted");
12009  } else {
12010  if (play_folder == 0) {
12011  if (in_urgent) {
12012  vms.urgentmessages++;
12013  } else {
12014  vms.newmessages++;
12015  }
12016  }
12017  else if (play_folder == 1)
12018  vms.oldmessages++;
12019  cmd = ast_play_and_wait(chan, "vm-undeleted");
12020  }
12021  if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
12022  if (vms.curmsg < vms.lastmsg) {
12023  vms.curmsg++;
12024  cmd = play_message(chan, vmu, &vms);
12025  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12026  vms.curmsg = 0;
12027  cmd = play_message(chan, vmu, &vms);
12028  } else {
12029  /* Check if we were listening to urgent
12030  messages. If so, go to regular new messages
12031  instead of saying "no more messages"
12032  */
12033  if (in_urgent == 1) {
12034  /* Check for new messages */
12035  in_urgent = 0;
12036  res = close_mailbox(&vms, vmu);
12037  if (res == ERROR_LOCK_PATH)
12038  goto out;
12039  res = open_mailbox(&vms, vmu, NEW_FOLDER);
12040  if (res < 0)
12041  goto out;
12042  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12043  vms.curmsg = -1;
12044  if (vms.lastmsg < 0) {
12045  cmd = ast_play_and_wait(chan, "vm-nomore");
12046  }
12047  } else {
12048  cmd = ast_play_and_wait(chan, "vm-nomore");
12049  }
12050  }
12051  }
12052  } else /* Delete not valid if we haven't selected a message */
12053  cmd = 0;
12054 #ifdef IMAP_STORAGE
12055  deleted = 1;
12056 #endif
12057  break;
12058 
12059  case '8': /* Forward the current message */
12060  if (vms.lastmsg > -1) {
12061  cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
12062  if (cmd == ERROR_LOCK_PATH) {
12063  res = cmd;
12064  goto out;
12065  }
12066  } else {
12067  /* Check if we were listening to urgent
12068  messages. If so, go to regular new messages
12069  instead of saying "no more messages"
12070  */
12071  if (in_urgent == 1 && vms.newmessages > 0) {
12072  /* Check for new messages */
12073  in_urgent = 0;
12074  res = close_mailbox(&vms, vmu);
12075  if (res == ERROR_LOCK_PATH)
12076  goto out;
12077  res = open_mailbox(&vms, vmu, NEW_FOLDER);
12078  if (res < 0)
12079  goto out;
12080  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12081  vms.curmsg = -1;
12082  if (vms.lastmsg < 0) {
12083  cmd = ast_play_and_wait(chan, "vm-nomore");
12084  }
12085  } else {
12086  cmd = ast_play_and_wait(chan, "vm-nomore");
12087  }
12088  }
12089  break;
12090  case '9': /* Save message to folder */
12091  ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
12092  if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
12093  /* No message selected */
12094  cmd = 0;
12095  break;
12096  }
12097  if (useadsi)
12098  adsi_folders(chan, 1, "Save to folder...");
12099  cmd = get_folder2(chan, "vm-savefolder", 1);
12100  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12101  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12102  box = 0; /* Shut up compiler */
12103  if (cmd == '#') {
12104  cmd = 0;
12105  break;
12106  } else if (cmd > 0) {
12107  box = cmd = cmd - '0';
12108  cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd, NULL, 0);
12109  if (cmd == ERROR_LOCK_PATH) {
12110  res = cmd;
12111  goto out;
12112 #ifndef IMAP_STORAGE
12113  } else if (!cmd) {
12114  vms.deleted[vms.curmsg] = 1;
12115 #endif
12116  } else {
12117  vms.deleted[vms.curmsg] = 0;
12118  vms.heard[vms.curmsg] = 0;
12119  }
12120  }
12121  make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
12122  if (useadsi)
12123  adsi_message(chan, &vms);
12124  snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
12125  if (!cmd) {
12126  cmd = ast_play_and_wait(chan, "vm-message");
12127  if (!cmd)
12128  cmd = say_and_wait(chan, vms.curmsg + 1, ast_channel_language(chan));
12129  if (!cmd)
12130  cmd = ast_play_and_wait(chan, "vm-savedto");
12131  if (!cmd)
12132  cmd = vm_play_folder_name(chan, vms.fn);
12133  } else {
12134  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
12135  }
12136  if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
12137  if (vms.curmsg < vms.lastmsg) {
12138  vms.curmsg++;
12139  cmd = play_message(chan, vmu, &vms);
12140  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12141  vms.curmsg = 0;
12142  cmd = play_message(chan, vmu, &vms);
12143  } else {
12144  /* Check if we were listening to urgent
12145  messages. If so, go to regular new messages
12146  instead of saying "no more messages"
12147  */
12148  if (in_urgent == 1 && vms.newmessages > 0) {
12149  /* Check for new messages */
12150  in_urgent = 0;
12151  res = close_mailbox(&vms, vmu);
12152  if (res == ERROR_LOCK_PATH)
12153  goto out;
12154  res = open_mailbox(&vms, vmu, NEW_FOLDER);
12155  if (res < 0)
12156  goto out;
12157  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12158  vms.curmsg = -1;
12159  if (vms.lastmsg < 0) {
12160  cmd = ast_play_and_wait(chan, "vm-nomore");
12161  }
12162  } else {
12163  cmd = ast_play_and_wait(chan, "vm-nomore");
12164  }
12165  }
12166  }
12167  break;
12168  case '*': /* Help */
12169  if (!vms.starting) {
12170  if (!strncasecmp(ast_channel_language(chan), "ja", 2)) {
12171  cmd = vm_play_folder_name(chan, vms.vmbox);
12172  if (!cmd)
12173  cmd = ast_play_and_wait(chan, "jp-wa");
12174  if (!cmd)
12175  cmd = ast_play_and_wait(chan, "digits/1");
12176  if (!cmd)
12177  cmd = ast_play_and_wait(chan, "jp-wo");
12178  if (!cmd)
12179  cmd = ast_play_and_wait(chan, "silence/1");
12180  if (!cmd)
12181  cmd = ast_play_and_wait(chan, "vm-opts");
12182  if (!cmd)
12183  cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
12184  break;
12185  }
12186  cmd = ast_play_and_wait(chan, "vm-onefor");
12187  if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
12188  cmd = ast_play_and_wait(chan, "vm-for");
12189  }
12190  if (!cmd)
12191  cmd = vm_play_folder_name(chan, vms.vmbox);
12192  if (!cmd)
12193  cmd = ast_play_and_wait(chan, "vm-opts");
12194  if (!cmd)
12195  cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
12196  } else
12197  cmd = 0;
12198  break;
12199  case '0': /* Mailbox options */
12200  cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
12201  if (useadsi)
12202  adsi_status(chan, &vms);
12203  /* Reopen play_folder */
12204  res = open_mailbox(&vms, vmu, play_folder);
12205  if (res < 0) {
12206  goto out;
12207  }
12208  vms.starting = 1;
12209  break;
12210  default: /* Nothing */
12211  ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
12212  cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
12213  break;
12214  }
12215  }
12216  if ((cmd == 't') || (cmd == '#')) {
12217  /* Timeout */
12218  res = 0;
12219  } else {
12220  /* Hangup */
12221  res = -1;
12222  }
12223 
12224 out:
12225  if (res > -1) {
12226  ast_stopstream(chan);
12227  adsi_goodbye(chan);
12228  if (valid && res != OPERATOR_EXIT) {
12229  if (silentexit)
12230  res = ast_play_and_wait(chan, "vm-dialout");
12231  else
12232  res = ast_play_and_wait(chan, "vm-goodbye");
12233  }
12234  if ((valid && res > 0) || res == OPERATOR_EXIT) {
12235  res = 0;
12236  }
12237  if (useadsi)
12239  }
12240  if (vmu)
12241  close_mailbox(&vms, vmu);
12242  if (valid) {
12243  int new = 0, old = 0, urgent = 0;
12244  snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
12245  /* Urgent flag not passwd to externnotify here */
12246  run_externnotify(vmu->context, vmu->mailbox, NULL);
12247  ast_app_inboxcount2(ext_context, &urgent, &new, &old);
12248  queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgent, new, old);
12249  }
12250 #ifdef IMAP_STORAGE
12251  /* expunge message - use UID Expunge if supported on IMAP server*/
12252  ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
12253  if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
12254  ast_mutex_lock(&vms.lock);
12255 #ifdef HAVE_IMAP_TK2006
12256  if (LEVELUIDPLUS (vms.mailstream)) {
12257  mail_expunge_full(vms.mailstream, NIL, EX_UID);
12258  } else
12259 #endif
12260  mail_expunge(vms.mailstream);
12261  ast_mutex_unlock(&vms.lock);
12262  }
12263  /* before we delete the state, we should copy pertinent info
12264  * back to the persistent model */
12265  if (vmu) {
12266  vmstate_delete(&vms);
12267  }
12268 #endif
12269  if (vmu)
12270  free_user(vmu);
12271 
12272 #ifdef IMAP_STORAGE
12273  pthread_setspecific(ts_vmstate.key, NULL);
12274 #endif
12275  return res;
12276 }
12277 
12278 static int vm_exec(struct ast_channel *chan, const char *data)
12279 {
12280  int res = 0;
12281  char *tmp;
12282  struct leave_vm_options leave_options;
12283  struct ast_flags flags = { 0 };
12284  char *opts[OPT_ARG_ARRAY_SIZE];
12286  AST_APP_ARG(argv0);
12287  AST_APP_ARG(argv1);
12288  );
12289 
12290  memset(&leave_options, 0, sizeof(leave_options));
12291 
12292  if (!ast_strlen_zero(data)) {
12293  tmp = ast_strdupa(data);
12295  if (args.argc == 2) {
12296  if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
12297  return -1;
12299  if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
12300  int gain;
12301 
12302  if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
12303  ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
12304  return -1;
12305  } else {
12306  leave_options.record_gain = (signed char) gain;
12307  }
12308  }
12309  if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
12310  if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
12311  leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
12312  }
12313  }
12314  if (ast_test_flag(&flags, OPT_BEEP)) { /* Use custom beep (or none at all) */
12315  leave_options.beeptone = opts[OPT_ARG_BEEP_TONE];
12316  } else { /* Use default beep */
12317  leave_options.beeptone = "beep";
12318  }
12319  } else {
12320  char temp[256];
12321  res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
12322  if (res < 0)
12323  return res;
12324  if (ast_strlen_zero(temp))
12325  return 0;
12326  args.argv0 = ast_strdupa(temp);
12327  }
12328 
12329  if (ast_channel_state(chan) != AST_STATE_UP) {
12330  if (ast_test_flag(&flags, OPT_EARLYM_GREETING)) {
12332  } else {
12333  ast_answer(chan);
12334  }
12335  }
12336 
12337  res = leave_voicemail(chan, args.argv0, &leave_options);
12338  if (res == 't') {
12339  ast_play_and_wait(chan, "vm-goodbye");
12340  res = 0;
12341  }
12342 
12343  if (res == OPERATOR_EXIT) {
12344  res = 0;
12345  }
12346 
12347  if (res == ERROR_LOCK_PATH) {
12348  ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
12349  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
12350  res = 0;
12351  }
12352 
12353  return res;
12354 }
12355 
12356 static int add_message_id(struct ast_config *msg_cfg, char *dir, int msg, char *filename, char *id, size_t id_size, struct ast_vm_user *vmu, int folder)
12357 {
12358  struct ast_variable *var;
12359  struct ast_category *cat;
12360  generate_msg_id(id);
12361 
12362  var = ast_variable_new("msg_id", id, "");
12363  if (!var) {
12364  return -1;
12365  }
12366 
12367  cat = ast_category_get(msg_cfg, "message", NULL);
12368  if (!cat) {
12369  ast_log(LOG_ERROR, "Voicemail data file %s/%d.txt has no [message] category?\n", dir, msg);
12370  ast_variables_destroy(var);
12371  return -1;
12372  }
12373 
12374  ast_variable_append(cat, var);
12375 
12376  if (ast_config_text_file_save(filename, msg_cfg, "app_voicemail")) {
12377  ast_log(LOG_WARNING, "Unable to update %s to have a message ID\n", filename);
12378  return -1;
12379  }
12380 
12381  UPDATE_MSG_ID(dir, msg, id, vmu, msg_cfg, folder);
12382  return 0;
12383 }
12384 
12385 static struct ast_vm_user *find_or_create(const char *context, const char *box)
12386 {
12387  struct ast_vm_user *vmu;
12388 
12389  if (!ast_strlen_zero(box) && box[0] == '*') {
12390  ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character. The '*' character,"
12391  "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
12392  "\n\tpredefined extension 'a'. A mailbox or password beginning with '*' is not valid"
12393  "\n\tand will be ignored.\n", box, context);
12394  return NULL;
12395  }
12396 
12397  AST_LIST_TRAVERSE(&users, vmu, list) {
12398  if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
12399  if (strcasecmp(vmu->context, context)) {
12400  ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
12401  \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
12402  \n\tconfiguration creates an ambiguity that you likely do not want. Please\
12403  \n\tamend your voicemail.conf file to avoid this situation.\n", box);
12404  }
12405  ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
12406  return NULL;
12407  }
12408  if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
12409  ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
12410  return NULL;
12411  }
12412  }
12413 
12414  if (!(vmu = ast_calloc(1, sizeof(*vmu))))
12415  return NULL;
12416 
12417  ast_copy_string(vmu->context, context, sizeof(vmu->context));
12418  ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
12419 
12421 
12422  return vmu;
12423 }
12424 
12425 static int append_mailbox(const char *context, const char *box, const char *data)
12426 {
12427  /* Assumes lock is already held */
12428  char *tmp;
12429  char *stringp;
12430  char *s;
12431  struct ast_vm_user *vmu;
12432  char mailbox_full[MAX_VM_MAILBOX_LEN];
12433  int new = 0, old = 0, urgent = 0;
12434  char secretfn[PATH_MAX] = "";
12435 
12436  tmp = ast_strdupa(data);
12437 
12438  if (!(vmu = find_or_create(context, box)))
12439  return -1;
12440 
12441  populate_defaults(vmu);
12442 
12443  stringp = tmp;
12444  if ((s = strsep(&stringp, ","))) {
12445  if (!ast_strlen_zero(s) && s[0] == '*') {
12446  ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
12447  "\n\tmust be reset in voicemail.conf.\n", box);
12448  }
12449  /* assign password regardless of validity to prevent NULL password from being assigned */
12450  ast_copy_string(vmu->password, s, sizeof(vmu->password));
12451  }
12452  if (stringp && (s = strsep(&stringp, ","))) {
12453  ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
12454  }
12455  if (stringp && (s = strsep(&stringp, ","))) {
12456  vmu->email = ast_strdup(s);
12457  }
12458  if (stringp && (s = strsep(&stringp, ","))) {
12459  ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
12460  }
12461  if (stringp) {
12462  apply_options(vmu, stringp);
12463  }
12464 
12465  switch (vmu->passwordlocation) {
12466  case OPT_PWLOC_SPOOLDIR:
12467  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
12468  read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
12469  }
12470 
12471  snprintf(mailbox_full, MAX_VM_MAILBOX_LEN, "%s%s%s",
12472  box,
12473  ast_strlen_zero(context) ? "" : "@",
12474  context);
12475 
12476  inboxcount2(mailbox_full, &urgent, &new, &old);
12477 #ifdef IMAP_STORAGE
12478  imap_logout(mailbox_full);
12479 #endif
12480  queue_mwi_event(NULL, mailbox_full, urgent, new, old);
12481 
12482  return 0;
12483 }
12484 
12485 #ifdef TEST_FRAMEWORK
12486 AST_TEST_DEFINE(test_voicemail_vmuser)
12487 {
12488  int res = 0;
12489  struct ast_vm_user *vmu;
12490  /* language parameter seems to only be used for display in manager action */
12491  static const char options_string[] = "attach=yes|attachfmt=wav49|"
12492  "[email protected]|fromstring=Voicemail System|tz=central|delete=yes|saycid=yes|"
12493  "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
12494  "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
12495  "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
12496  "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
12497  "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
12498  "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
12499  "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
12500 #ifdef IMAP_STORAGE
12501  static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
12502  "imapfolder=INBOX|imapvmshareid=6000|imapserver=imapserver|imapport=1234|imapflags=flagged";
12503 #endif
12504 
12505  switch (cmd) {
12506  case TEST_INIT:
12507  info->name = "vmuser";
12508  info->category = "/apps/app_voicemail/";
12509  info->summary = "Vmuser unit test";
12510  info->description =
12511  "This tests passing all supported parameters to apply_options, the voicemail user config parser";
12512  return AST_TEST_NOT_RUN;
12513  case TEST_EXECUTE:
12514  break;
12515  }
12516 
12517  if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
12518  return AST_TEST_NOT_RUN;
12519  }
12520  populate_defaults(vmu);
12521  ast_set_flag(vmu, VM_ALLOCED);
12522 
12523  apply_options(vmu, options_string);
12524 
12525  if (!ast_test_flag(vmu, VM_ATTACH)) {
12526  ast_test_status_update(test, "Parse failure for attach option\n");
12527  res = 1;
12528  }
12529  if (strcasecmp(vmu->attachfmt, "wav49")) {
12530  ast_test_status_update(test, "Parse failure for attachftm option\n");
12531  res = 1;
12532  }
12533  if (strcasecmp(vmu->fromstring, "Voicemail System")) {
12534  ast_test_status_update(test, "Parse failure for fromstring option\n");
12535  res = 1;
12536  }
12537  if (strcasecmp(vmu->serveremail, "[email protected]")) {
12538  ast_test_status_update(test, "Parse failure for serveremail option\n");
12539  res = 1;
12540  }
12541  if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
12542  ast_test_status_update(test, "Parse failure for emailsubject option\n");
12543  res = 1;
12544  }
12545  if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
12546  ast_test_status_update(test, "Parse failure for emailbody option\n");
12547  res = 1;
12548  }
12549  if (strcasecmp(vmu->zonetag, "central")) {
12550  ast_test_status_update(test, "Parse failure for tz option\n");
12551  res = 1;
12552  }
12553  if (!ast_test_flag(vmu, VM_DELETE)) {
12554  ast_test_status_update(test, "Parse failure for delete option\n");
12555  res = 1;
12556  }
12557  if (!ast_test_flag(vmu, VM_SAYCID)) {
12558  ast_test_status_update(test, "Parse failure for saycid option\n");
12559  res = 1;
12560  }
12561  if (!ast_test_flag(vmu, VM_SVMAIL)) {
12562  ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
12563  res = 1;
12564  }
12565  if (!ast_test_flag(vmu, VM_REVIEW)) {
12566  ast_test_status_update(test, "Parse failure for review option\n");
12567  res = 1;
12568  }
12569  if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
12570  ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
12571  res = 1;
12572  }
12573  if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
12574  ast_test_status_update(test, "Parse failure for messagewrap option\n");
12575  res = 1;
12576  }
12577  if (!ast_test_flag(vmu, VM_OPERATOR)) {
12578  ast_test_status_update(test, "Parse failure for operator option\n");
12579  res = 1;
12580  }
12581  if (!ast_test_flag(vmu, VM_ENVELOPE)) {
12582  ast_test_status_update(test, "Parse failure for envelope option\n");
12583  res = 1;
12584  }
12585  if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
12586  ast_test_status_update(test, "Parse failure for moveheard option\n");
12587  res = 1;
12588  }
12589  if (!ast_test_flag(vmu, VM_SAYDURATION)) {
12590  ast_test_status_update(test, "Parse failure for sayduration option\n");
12591  res = 1;
12592  }
12593  if (vmu->saydurationm != 5) {
12594  ast_test_status_update(test, "Parse failure for saydurationm option\n");
12595  res = 1;
12596  }
12597  if (!ast_test_flag(vmu, VM_FORCENAME)) {
12598  ast_test_status_update(test, "Parse failure for forcename option\n");
12599  res = 1;
12600  }
12601  if (!ast_test_flag(vmu, VM_FORCEGREET)) {
12602  ast_test_status_update(test, "Parse failure for forcegreetings option\n");
12603  res = 1;
12604  }
12605  if (strcasecmp(vmu->callback, "somecontext")) {
12606  ast_test_status_update(test, "Parse failure for callbacks option\n");
12607  res = 1;
12608  }
12609  if (strcasecmp(vmu->dialout, "somecontext2")) {
12610  ast_test_status_update(test, "Parse failure for dialout option\n");
12611  res = 1;
12612  }
12613  if (strcasecmp(vmu->exit, "somecontext3")) {
12614  ast_test_status_update(test, "Parse failure for exitcontext option\n");
12615  res = 1;
12616  }
12617  if (vmu->minsecs != 10) {
12618  ast_test_status_update(test, "Parse failure for minsecs option\n");
12619  res = 1;
12620  }
12621  if (vmu->maxsecs != 100) {
12622  ast_test_status_update(test, "Parse failure for maxsecs option\n");
12623  res = 1;
12624  }
12625  if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
12626  ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
12627  res = 1;
12628  }
12629  if (vmu->maxdeletedmsg != 50) {
12630  ast_test_status_update(test, "Parse failure for backupdeleted option\n");
12631  res = 1;
12632  }
12633  if (vmu->volgain != 1.3) {
12634  ast_test_status_update(test, "Parse failure for volgain option\n");
12635  res = 1;
12636  }
12637  if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
12638  ast_test_status_update(test, "Parse failure for passwordlocation option\n");
12639  res = 1;
12640  }
12641 #ifdef IMAP_STORAGE
12642  apply_options(vmu, option_string2);
12643 
12644  if (strcasecmp(vmu->imapuser, "imapuser")) {
12645  ast_test_status_update(test, "Parse failure for imapuser option\n");
12646  res = 1;
12647  }
12648  if (strcasecmp(vmu->imappassword, "imappasswd")) {
12649  ast_test_status_update(test, "Parse failure for imappasswd option\n");
12650  res = 1;
12651  }
12652  if (strcasecmp(vmu->imapfolder, "INBOX")) {
12653  ast_test_status_update(test, "Parse failure for imapfolder option\n");
12654  res = 1;
12655  }
12656  if (strcasecmp(vmu->imapvmshareid, "6000")) {
12657  ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
12658  res = 1;
12659  }
12660  if (strcasecmp(vmu->imapserver, "imapserver")) {
12661  ast_test_status_update(test, "Parse failure for imapserver option\n");
12662  res = 1;
12663  }
12664  if (strcasecmp(vmu->imapport, "1234")) {
12665  ast_test_status_update(test, "Parse failure for imapport option\n");
12666  res = 1;
12667  }
12668  if (strcasecmp(vmu->imapflags, "flagged")) {
12669  ast_test_status_update(test, "Parse failure for imapflags option\n");
12670  res = 1;
12671  }
12672 #endif
12673 
12674  free_user(vmu);
12675  return res ? AST_TEST_FAIL : AST_TEST_PASS;
12676 }
12677 #endif
12678 
12679 static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
12680 {
12681  struct ast_vm_user svm;
12682  struct ast_vm_user *vmu = NULL;
12683  char *parse;
12684  char *mailbox;
12685  char *context;
12686  int res = 0;
12687 
12689  AST_APP_ARG(mailbox_context);
12690  AST_APP_ARG(attribute);
12691  AST_APP_ARG(folder);
12692  );
12693 
12694  buf[0] = '\0';
12695 
12696  if (ast_strlen_zero(args)) {
12697  ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
12698  return -1;
12699  }
12700 
12701  parse = ast_strdupa(args);
12702  AST_STANDARD_APP_ARGS(arg, parse);
12703 
12704  if (ast_strlen_zero(arg.mailbox_context)
12705  || ast_strlen_zero(arg.attribute)
12706  || separate_mailbox(ast_strdupa(arg.mailbox_context), &mailbox, &context)) {
12707  ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
12708  return -1;
12709  }
12710 
12711  memset(&svm, 0, sizeof(svm));
12712  vmu = find_user(&svm, context, mailbox);
12713 
12714  if (!strncasecmp(arg.attribute, "exists", 5)) {
12715  ast_copy_string(buf, vmu ? "1" : "0", len);
12716  free_user(vmu);
12717  return 0;
12718  }
12719 
12720  if (vmu) {
12721  if (!strncasecmp(arg.attribute, "password", 8)) {
12722  ast_copy_string(buf, vmu->password, len);
12723  } else if (!strncasecmp(arg.attribute, "fullname", 8)) {
12724  ast_copy_string(buf, vmu->fullname, len);
12725  } else if (!strncasecmp(arg.attribute, "email", 5)) {
12726  ast_copy_string(buf, vmu->email, len);
12727  } else if (!strncasecmp(arg.attribute, "pager", 5)) {
12728  ast_copy_string(buf, vmu->pager, len);
12729  } else if (!strncasecmp(arg.attribute, "language", 8)) {
12730  ast_copy_string(buf, S_OR(vmu->language, ast_channel_language(chan)), len);
12731  } else if (!strncasecmp(arg.attribute, "locale", 6)) {
12732  ast_copy_string(buf, vmu->locale, len);
12733  } else if (!strncasecmp(arg.attribute, "tz", 2)) {
12734  ast_copy_string(buf, vmu->zonetag, len);
12735  } else if (!strncasecmp(arg.attribute, "count", 5)) {
12736  char *mailbox_id;
12737 
12738  mailbox_id = ast_alloca(strlen(mailbox) + strlen(context) + 2);
12739  sprintf(mailbox_id, "%s@%s", mailbox, context);/* Safe */
12740 
12741  /* If mbxfolder is empty messagecount will default to INBOX */
12742  res = messagecount(mailbox_id, arg.folder);
12743  if (res < 0) {
12744  ast_log(LOG_ERROR, "Unable to retrieve message count for mailbox %s\n", arg.mailbox_context);
12745  free_user(vmu);
12746  return -1;
12747  }
12748  snprintf(buf, len, "%d", res);
12749  } else {
12750  ast_log(LOG_ERROR, "Unknown attribute '%s' for VM_INFO\n", arg.attribute);
12751  free_user(vmu);
12752  return -1;
12753  }
12754  free_user(vmu);
12755  }
12756 
12757  return 0;
12758 }
12759 
12761  .name = "VM_INFO",
12762  .read = acf_vm_info,
12763 };
12764 
12765 static int vmauthenticate(struct ast_channel *chan, const char *data)
12766 {
12767  char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
12768  struct ast_vm_user vmus = {{0}};
12769  char *options = NULL;
12770  int silent = 0, skipuser = 0;
12771  int res = -1;
12772 
12773  if (data) {
12774  s = ast_strdupa(data);
12775  user = strsep(&s, ",");
12776  options = strsep(&s, ",");
12777  if (user) {
12778  s = user;
12779  user = strsep(&s, "@");
12780  context = strsep(&s, "");
12781  if (!ast_strlen_zero(user))
12782  skipuser++;
12783  ast_copy_string(mailbox, user, sizeof(mailbox));
12784  }
12785  }
12786 
12787  if (options) {
12788  silent = (strchr(options, 's')) != NULL;
12789  }
12790 
12791  if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
12792  pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
12793  pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
12794  ast_play_and_wait(chan, "auth-thankyou");
12795  res = 0;
12796  } else if (mailbox[0] == '*') {
12797  /* user entered '*' */
12798  if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
12799  res = 0; /* prevent hangup */
12800  }
12801  }
12802 
12803  return res;
12804 }
12805 
12806 static char *show_users_realtime(int fd, const char *context)
12807 {
12808  struct ast_config *cfg;
12809  const char *cat = NULL;
12810 
12811  if (!(cfg = ast_load_realtime_multientry("voicemail",
12812  "context", context, SENTINEL))) {
12813  return CLI_FAILURE;
12814  }
12815 
12816  ast_cli(fd,
12817  "\n"
12818  "=============================================================\n"
12819  "=== Configured Voicemail Users ==============================\n"
12820  "=============================================================\n"
12821  "===\n");
12822 
12823  while ((cat = ast_category_browse(cfg, cat))) {
12824  struct ast_variable *var = NULL;
12825  ast_cli(fd,
12826  "=== Mailbox ...\n"
12827  "===\n");
12828  for (var = ast_variable_browse(cfg, cat); var; var = var->next)
12829  ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
12830  ast_cli(fd,
12831  "===\n"
12832  "=== ---------------------------------------------------------\n"
12833  "===\n");
12834  }
12835 
12836  ast_cli(fd,
12837  "=============================================================\n"
12838  "\n");
12839 
12840  ast_config_destroy(cfg);
12841 
12842  return CLI_SUCCESS;
12843 }
12844 
12845 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
12846 {
12847  int which = 0;
12848  int wordlen;
12849  struct ast_vm_user *vmu;
12850  const char *context = "";
12851 
12852  /* 0 - voicemail; 1 - show; 2 - users; 3 - for; 4 - <context> */
12853  if (pos > 4)
12854  return NULL;
12855  wordlen = strlen(word);
12856  AST_LIST_TRAVERSE(&users, vmu, list) {
12857  if (!strncasecmp(word, vmu->context, wordlen)) {
12858  if (context && strcmp(context, vmu->context) && ++which > state)
12859  return ast_strdup(vmu->context);
12860  /* ignore repeated contexts ? */
12861  context = vmu->context;
12862  }
12863  }
12864  return NULL;
12865 }
12866 
12867 /*! \brief Show a list of voicemail users in the CLI */
12868 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12869 {
12870  struct ast_vm_user *vmu;
12871 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
12872  const char *context = NULL;
12873  int users_counter = 0;
12874 
12875  switch (cmd) {
12876  case CLI_INIT:
12877  e->command = "voicemail show users [for]";
12878  e->usage =
12879  "Usage: voicemail show users [for <context>]\n"
12880  " Lists all mailboxes currently set up\n";
12881  return NULL;
12882  case CLI_GENERATE:
12883  return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
12884  }
12885 
12886  if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
12887  return CLI_SHOWUSAGE;
12888  if (a->argc == 5) {
12889  if (strcmp(a->argv[3],"for"))
12890  return CLI_SHOWUSAGE;
12891  context = a->argv[4];
12892  }
12893 
12894  if (ast_check_realtime("voicemail")) {
12895  if (!context) {
12896  ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
12897  return CLI_SHOWUSAGE;
12898  }
12899  return show_users_realtime(a->fd, context);
12900  }
12901 
12902  AST_LIST_LOCK(&users);
12903  if (AST_LIST_EMPTY(&users)) {
12904  ast_cli(a->fd, "There are no voicemail users currently defined\n");
12906  return CLI_FAILURE;
12907  }
12908  if (!context) {
12909  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
12910  } else {
12911  int count = 0;
12912  AST_LIST_TRAVERSE(&users, vmu, list) {
12913  if (!strcmp(context, vmu->context)) {
12914  count++;
12915  break;
12916  }
12917  }
12918  if (count) {
12919  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
12920  } else {
12921  ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
12923  return CLI_FAILURE;
12924  }
12925  }
12926  AST_LIST_TRAVERSE(&users, vmu, list) {
12927  int newmsgs = 0, oldmsgs = 0;
12928  char count[12], tmp[256] = "";
12929 
12930  if (!context || !strcmp(context, vmu->context)) {
12931  snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
12932  inboxcount(tmp, &newmsgs, &oldmsgs);
12933  snprintf(count, sizeof(count), "%d", newmsgs);
12934  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
12935  users_counter++;
12936  }
12937  }
12939  ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
12940  return CLI_SUCCESS;
12941 }
12942 
12943 /*! \brief Show a list of voicemail zones in the CLI */
12944 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12945 {
12946  struct vm_zone *zone;
12947 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
12948  char *res = CLI_SUCCESS;
12949 
12950  switch (cmd) {
12951  case CLI_INIT:
12952  e->command = "voicemail show zones";
12953  e->usage =
12954  "Usage: voicemail show zones\n"
12955  " Lists zone message formats\n";
12956  return NULL;
12957  case CLI_GENERATE:
12958  return NULL;
12959  }
12960 
12961  if (a->argc != 3)
12962  return CLI_SHOWUSAGE;
12963 
12964  AST_LIST_LOCK(&zones);
12965  if (!AST_LIST_EMPTY(&zones)) {
12966  ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
12967  AST_LIST_TRAVERSE(&zones, zone, list) {
12968  ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
12969  }
12970  } else {
12971  ast_cli(a->fd, "There are no voicemail zones currently defined\n");
12972  res = CLI_FAILURE;
12973  }
12975 
12976  return res;
12977 }
12978 
12979 /*! \brief Show a list of voicemail zones in the CLI */
12980 static char *handle_voicemail_show_aliases(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12981 {
12982  struct ao2_iterator aliases;
12983  struct alias_mailbox_mapping *mapping;
12984 #define ALIASES_OUTPUT_FORMAT "%-32s %-32s\n"
12985  char *res = CLI_SUCCESS;
12986 
12987  switch (cmd) {
12988  case CLI_INIT:
12989  e->command = "voicemail show aliases";
12990  e->usage =
12991  "Usage: voicemail show aliases\n"
12992  " Lists mailbox aliases\n";
12993  return NULL;
12994  case CLI_GENERATE:
12995  return NULL;
12996  }
12997 
12998  if (a->argc != 3)
12999  return CLI_SHOWUSAGE;
13000 
13001  if (ast_strlen_zero(aliasescontext)) {
13002  ast_cli(a->fd, "Aliases are not enabled\n");
13003  return res;
13004  }
13005 
13006  ast_cli(a->fd, "Aliases context: %s\n", aliasescontext);
13007  ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, "Alias", "Mailbox");
13008 
13009  aliases = ao2_iterator_init(alias_mailbox_mappings, 0);
13010  while ((mapping = ao2_iterator_next(&aliases))) {
13011  ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, mapping->alias, mapping->mailbox);
13012  ao2_ref(mapping, -1);
13013  }
13014  ao2_iterator_destroy(&aliases);
13015 
13016  return res;
13017 }
13018 
13019 /*! \brief Reload voicemail configuration from the CLI */
13020 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13021 {
13022  switch (cmd) {
13023  case CLI_INIT:
13024  e->command = "voicemail reload";
13025  e->usage =
13026  "Usage: voicemail reload\n"
13027  " Reload voicemail configuration\n";
13028  return NULL;
13029  case CLI_GENERATE:
13030  return NULL;
13031  }
13032 
13033  if (a->argc != 2)
13034  return CLI_SHOWUSAGE;
13035 
13036  ast_cli(a->fd, "Reloading voicemail configuration...\n");
13037  load_config(1);
13038 
13039  return CLI_SUCCESS;
13040 }
13041 
13042 static struct ast_cli_entry cli_voicemail[] = {
13043  AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
13044  AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
13045  AST_CLI_DEFINE(handle_voicemail_show_aliases, "List mailbox aliases"),
13046  AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
13047 };
13048 
13049 static int poll_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
13050 {
13051  int new = 0, old = 0, urgent = 0;
13052 
13053  if (!mwi_state) {
13054  /* This should only occur due to allocation failure of a default mwi state object */
13055  return 0;
13056  }
13057 
13058  inboxcount2(mwi_state->uniqueid, &urgent, &new, &old);
13059 
13060 #ifdef IMAP_STORAGE
13061  if (imap_poll_logout) {
13062  imap_logout(mwi_state->uniqueid);
13063  }
13064 #endif
13065 
13066  if (urgent != mwi_state->urgent_msgs || new != mwi_state->new_msgs || old != mwi_state->old_msgs) {
13067  queue_mwi_event(NULL, mwi_state->uniqueid, urgent, new, old);
13068  run_externnotify(NULL, mwi_state->uniqueid, NULL);
13069  }
13070 
13071  return 0;
13072 }
13073 
13074 static void *mb_poll_thread(void *data)
13075 {
13076  while (poll_thread_run) {
13077  struct timespec ts = { 0, };
13078  struct timeval wait;
13079 
13081 
13082  if (!poll_thread_run) {
13083  break;
13084  }
13085 
13086  wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
13087  ts.tv_sec = wait.tv_sec;
13088  ts.tv_nsec = wait.tv_usec * 1000;
13089 
13091  ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
13093  }
13094 
13095  return NULL;
13096 }
13097 
13098 #ifdef IMAP_STORAGE
13099 static void imap_logout(const char *mailbox_id)
13100 {
13101  char *context;
13102  char *mailbox;
13103  struct ast_vm_user vmus;
13104  RAII_VAR(struct ast_vm_user *, vmu, NULL, free_user);
13105  struct vm_state *vms = NULL;
13106 
13107  if (ast_strlen_zero(mailbox_id)
13108  || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
13109  return;
13110  }
13111 
13112  memset(&vmus, 0, sizeof(vmus));
13113 
13114  if (!(vmu = find_user(&vmus, context, mailbox)) || vmu->imapuser[0] == '\0') {
13115  return;
13116  }
13117 
13118  vms = get_vm_state_by_imapuser(vmu->imapuser, 0);
13119  if (!vms) {
13120  vms = get_vm_state_by_mailbox(mailbox, context, 0);
13121  }
13122  if (!vms) {
13123  return;
13124  }
13125 
13126  ast_mutex_lock(&vms->lock);
13127  vms->mailstream = mail_close(vms->mailstream);
13128  ast_mutex_unlock(&vms->lock);
13129 
13130  vmstate_delete(vms);
13131 }
13132 
13133 static int imap_close_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
13134 {
13135  if (mwi_state && !ast_strlen_zero(mwi_state->uniqueid)) {
13136  imap_logout(mwi_state->uniqueid);
13137  }
13138 
13139  return 0;
13140 }
13141 
13142 #endif
13143 
13144 static int mwi_handle_unsubscribe2(void *data)
13145 {
13146  struct ast_mwi_state *mwi_state = data;
13147 
13148  /*
13149  * Go ahead and clear the implicit MWI publisher here to avoid a leak. If a backing
13150  * configuration is available it'll re-initialize (reset the cached state) on its
13151  * next publish.
13152  */
13154 
13155 #ifdef IMAP_STORAGE
13156  imap_close_subscribed_mailbox(mwi_state, NULL);
13157 #endif
13158 
13159  ao2_ref(mwi_state, -1);
13160  return 0;
13161 }
13162 
13163 static void mwi_handle_unsubscribe(const char *id, struct ast_mwi_subscriber *sub)
13164 {
13165  void *data = ast_mwi_subscriber_data(sub);
13166 
13167  /* Don't bump data's reference. We'll just use the one returned above */
13168  if (ast_taskprocessor_push(mwi_subscription_tps, mwi_handle_unsubscribe2, data) < 0) {
13169  /* A reference was returned for data when retrieving, so remove it on error */
13170  ao2_ref(data, -1);
13171  }
13172 }
13173 
13174 static int mwi_handle_subscribe2(void *data)
13175 {
13177  ao2_ref(data, -1);
13178  return 0;
13179 }
13180 
13181 static void mwi_handle_subscribe(const char *id, struct ast_mwi_subscriber *sub)
13182 {
13183  void *data = ast_mwi_subscriber_data(sub);
13184 
13185  /* Don't bump data's reference. We'll just use the one returned above */
13186  if (ast_taskprocessor_push(mwi_subscription_tps, mwi_handle_subscribe2, data) < 0) {
13187  /* A reference was returned for data when retrieving, so remove it on error */
13188  ao2_ref(data, -1);
13189  }
13190 }
13191 
13194  .on_unsubscribe = mwi_handle_unsubscribe,
13195 };
13196 
13197 static void start_poll_thread(void)
13198 {
13199  int errcode;
13200  ast_mwi_add_observer(&mwi_observer);
13201 
13202  poll_thread_run = 1;
13203 
13204  if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
13205  ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
13206  }
13207 }
13208 
13209 static void stop_poll_thread(void)
13210 {
13211  poll_thread_run = 0;
13212 
13214  ast_cond_signal(&poll_cond);
13216 
13217  pthread_join(poll_thread, NULL);
13218  poll_thread = AST_PTHREADT_NULL;
13219 
13220  ast_mwi_remove_observer(&mwi_observer);
13221 }
13222 
13223 /*!
13224  * \brief Append vmu info string into given astman with event_name.
13225  * \return 0 failed. 1 otherwise.
13226 */
13228  struct mansession *s,
13229  struct ast_vm_user *vmu,
13230  const char* event_name,
13231  const char* actionid
13232  )
13233 {
13234  int new;
13235  int old;
13236  char *mailbox;
13237  int ret;
13238 
13239  if((s == NULL) || (vmu == NULL) || (event_name == NULL) || (actionid == NULL)) {
13240  ast_log(LOG_ERROR, "Wrong input parameter.");
13241  return 0;
13242  }
13243 
13244  /* create mailbox string */
13245  if (!ast_strlen_zero(vmu->context)) {
13246  ret = ast_asprintf(&mailbox, "%s@%s", vmu->mailbox, vmu->context);
13247  } else {
13248  ret = ast_asprintf(&mailbox, "%s", vmu->mailbox);
13249  }
13250  if (ret == -1) {
13251  ast_log(LOG_ERROR, "Could not create mailbox string. err[%s]\n", strerror(errno));
13252  return 0;
13253  }
13254 
13255  /* get mailbox count */
13256  ret = inboxcount(mailbox, &new, &old);
13257  ast_free(mailbox);
13258  if (ret == -1) {
13259  ast_log(LOG_ERROR, "Could not get mailbox count. user[%s], context[%s]\n",
13260  vmu->mailbox ?: "", vmu->context ?: "");
13261  return 0;
13262  }
13263 
13264  astman_append(s,
13265  "Event: %s\r\n"
13266  "%s"
13267  "VMContext: %s\r\n"
13268  "VoiceMailbox: %s\r\n"
13269  "Fullname: %s\r\n"
13270  "Email: %s\r\n"
13271  "Pager: %s\r\n"
13272  "ServerEmail: %s\r\n"
13273  "FromString: %s\r\n"
13274  "MailCommand: %s\r\n"
13275  "Language: %s\r\n"
13276  "TimeZone: %s\r\n"
13277  "Callback: %s\r\n"
13278  "Dialout: %s\r\n"
13279  "UniqueID: %s\r\n"
13280  "ExitContext: %s\r\n"
13281  "SayDurationMinimum: %d\r\n"
13282  "SayEnvelope: %s\r\n"
13283  "SayCID: %s\r\n"
13284  "AttachMessage: %s\r\n"
13285  "AttachmentFormat: %s\r\n"
13286  "DeleteMessage: %s\r\n"
13287  "VolumeGain: %.2f\r\n"
13288  "CanReview: %s\r\n"
13289  "CallOperator: %s\r\n"
13290  "MaxMessageCount: %d\r\n"
13291  "MaxMessageLength: %d\r\n"
13292  "NewMessageCount: %d\r\n"
13293  "OldMessageCount: %d\r\n"
13294 #ifdef IMAP_STORAGE
13295  "IMAPUser: %s\r\n"
13296  "IMAPServer: %s\r\n"
13297  "IMAPPort: %s\r\n"
13298  "IMAPFlags: %s\r\n"
13299 #endif
13300  "\r\n",
13301 
13302  event_name,
13303  actionid,
13304  vmu->context,
13305  vmu->mailbox,
13306  vmu->fullname,
13307  vmu->email,
13308  vmu->pager,
13309  ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
13310  ast_strlen_zero(vmu->fromstring) ? fromstring : vmu->fromstring,
13311  mailcmd,
13312  vmu->language,
13313  vmu->zonetag,
13314  vmu->callback,
13315  vmu->dialout,
13316  vmu->uniqueid,
13317  vmu->exit,
13318  vmu->saydurationm,
13319  ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
13320  ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
13321  ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
13322  vmu->attachfmt,
13323  ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
13324  vmu->volgain,
13325  ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
13326  ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
13327  vmu->maxmsg,
13328  vmu->maxsecs,
13329  new,
13330  old
13331 #ifdef IMAP_STORAGE
13332  ,
13333  vmu->imapuser,
13334  vmu->imapserver,
13335  vmu->imapport,
13336  vmu->imapflags
13337 #endif
13338  );
13339 
13340  return 1;
13341 
13342 }
13343 
13344 static int manager_match_mailbox(struct ast_mwi_state *mwi_state, void *data)
13345 {
13346  const char *context = astman_get_header(data, "Context");
13347  const char *mailbox = astman_get_header(data, "Mailbox");
13348  const char *at;
13349 
13350  if (!ast_strlen_zero(mwi_state->uniqueid)) {
13351  if (
13352  /* First case: everything matches */
13353  (ast_strlen_zero(context) && ast_strlen_zero(mailbox)) ||
13354  /* Second case: match the mailbox only */
13355  (ast_strlen_zero(context) && !ast_strlen_zero(mailbox) &&
13356  (at = strchr(mwi_state->uniqueid, '@')) &&
13357  strncmp(mailbox, mwi_state->uniqueid, at - mwi_state->uniqueid) == 0) ||
13358  /* Third case: match the context only */
13359  (!ast_strlen_zero(context) && ast_strlen_zero(mailbox) &&
13360  (at = strchr(mwi_state->uniqueid, '@')) &&
13361  strcmp(context, at + 1) == 0) ||
13362  /* Final case: match an exact specified mailbox */
13363  (!ast_strlen_zero(context) && !ast_strlen_zero(mailbox) &&
13364  (at = strchr(mwi_state->uniqueid, '@')) &&
13365  strncmp(mailbox, mwi_state->uniqueid, at - mwi_state->uniqueid) == 0 &&
13366  strcmp(context, at + 1) == 0)
13367  ) {
13368  poll_subscribed_mailbox(mwi_state, NULL);
13369  }
13370  }
13371 
13372  return 0;
13373 }
13374 
13375 static int manager_voicemail_refresh(struct mansession *s, const struct message *m)
13376 {
13378  astman_send_ack(s, m, "Refresh sent");
13379  return RESULT_SUCCESS;
13380 }
13381 
13382 static int manager_status_voicemail_user(struct mansession *s, const struct message *m)
13383 {
13384  struct ast_vm_user *vmu = NULL;
13385  const char *id = astman_get_header(m, "ActionID");
13386  char actionid[128];
13387  struct ast_vm_user svm;
13388  int ret;
13389 
13390  const char *context = astman_get_header(m, "Context");
13391  const char *mailbox = astman_get_header(m, "Mailbox");
13392 
13393  if ((ast_strlen_zero(context) || ast_strlen_zero(mailbox))) {
13394  astman_send_error(s, m, "Need 'Context' and 'Mailbox' parameters.");
13395  return RESULT_SUCCESS;
13396  }
13397 
13398  actionid[0] = '\0';
13399  if (!ast_strlen_zero(id)) {
13400  snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
13401  }
13402 
13403  /* find user */
13404  memset(&svm, 0, sizeof(svm));
13405  vmu = find_user(&svm, context, mailbox);
13406  if (!vmu) {
13407  /* could not find it */
13408  astman_send_ack(s, m, "There is no voicemail user of the given info.");
13409  return RESULT_SUCCESS;
13410  }
13411 
13412  astman_send_listack(s, m, "Voicemail user detail will follow", "start");
13413 
13414  /* append vmu info event */
13415  ret = append_vmu_info_astman(s, vmu, "VoicemailUserDetail", actionid);
13416  free_user(vmu);
13417  if(ret == 0) {
13418  ast_log(LOG_ERROR, "Could not append voicemail user info.");
13419  }
13420 
13421  astman_send_list_complete_start(s, m, "VoicemailUserDetailComplete", 1);
13423 
13424  return RESULT_SUCCESS;
13425 }
13426 
13427 /*! \brief Manager list voicemail users command */
13428 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
13429 {
13430  struct ast_vm_user *vmu = NULL;
13431  const char *id = astman_get_header(m, "ActionID");
13432  char actionid[128];
13433  int num_users = 0;
13434  int ret;
13435 
13436  actionid[0] = '\0';
13437  if (!ast_strlen_zero(id)) {
13438  snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
13439  }
13440 
13441  AST_LIST_LOCK(&users);
13442 
13443  if (AST_LIST_EMPTY(&users)) {
13444  astman_send_ack(s, m, "There are no voicemail users currently defined.");
13446  return RESULT_SUCCESS;
13447  }
13448 
13449  astman_send_listack(s, m, "Voicemail user list will follow", "start");
13450 
13451  AST_LIST_TRAVERSE(&users, vmu, list) {
13452  /* append vmu info event */
13453  ret = append_vmu_info_astman(s, vmu, "VoicemailUserEntry", actionid);
13454  if(ret == 0) {
13455  ast_log(LOG_ERROR, "Could not append voicemail user info.");
13456  continue;
13457  }
13458  ++num_users;
13459  }
13460 
13461  astman_send_list_complete_start(s, m, "VoicemailUserEntryComplete", num_users);
13463 
13465 
13466  return RESULT_SUCCESS;
13467 }
13468 
13469 /*! \brief Free the users structure. */
13470 static void free_vm_users(void)
13471 {
13472  struct ast_vm_user *current;
13473  AST_LIST_LOCK(&users);
13474  while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
13475  ast_set_flag(current, VM_ALLOCED);
13476  free_user_final(current);
13477  }
13479 }
13480 
13481 /*! \brief Free the zones structure. */
13482 static void free_vm_zones(void)
13483 {
13484  struct vm_zone *zcur;
13485  AST_LIST_LOCK(&zones);
13486  while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
13487  free_zone(zcur);
13489 }
13490 
13491 static const char *substitute_escapes(const char *value)
13492 {
13493  char *current;
13494 
13495  /* Add 16 for fudge factor */
13496  struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
13497 
13498  ast_str_reset(str);
13499 
13500  /* Substitute strings \r, \n, and \t into the appropriate characters */
13501  for (current = (char *) value; *current; current++) {
13502  if (*current == '\\') {
13503  current++;
13504  if (!*current) {
13505  ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
13506  break;
13507  }
13508  switch (*current) {
13509  case '\\':
13510  ast_str_append(&str, 0, "\\");
13511  break;
13512  case 'r':
13513  ast_str_append(&str, 0, "\r");
13514  break;
13515  case 'n':
13516 #ifdef IMAP_STORAGE
13517  if (!str->used || str->str[str->used - 1] != '\r') {
13518  ast_str_append(&str, 0, "\r");
13519  }
13520 #endif
13521  ast_str_append(&str, 0, "\n");
13522  break;
13523  case 't':
13524  ast_str_append(&str, 0, "\t");
13525  break;
13526  default:
13527  ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
13528  break;
13529  }
13530  } else {
13531  ast_str_append(&str, 0, "%c", *current);
13532  }
13533  }
13534 
13535  return ast_str_buffer(str);
13536 }
13537 
13538 static int load_config(int reload)
13539 {
13540  struct ast_config *cfg, *ucfg;
13541  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
13542  int res;
13543 
13544  ast_unload_realtime("voicemail");
13545  ast_unload_realtime("voicemail_data");
13546 
13547  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
13548  if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
13549  return 0;
13550  } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
13551  ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
13552  ucfg = NULL;
13553  }
13554  ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
13555  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
13556  ast_config_destroy(ucfg);
13557  ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
13558  return 0;
13559  }
13560  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
13561  ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
13562  return 0;
13563  } else {
13564  ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
13565  if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
13566  ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
13567  ucfg = NULL;
13568  }
13569  }
13570 
13571  res = actual_load_config(reload, cfg, ucfg);
13572 
13573  ast_config_destroy(cfg);
13574  ast_config_destroy(ucfg);
13575 
13576  return res;
13577 }
13578 
13579 #ifdef TEST_FRAMEWORK
13580 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
13581 {
13582  ast_unload_realtime("voicemail");
13583  ast_unload_realtime("voicemail_data");
13584  return actual_load_config(reload, cfg, ucfg);
13585 }
13586 #endif
13587 
13588 static struct alias_mailbox_mapping *alias_mailbox_mapping_create(const char *alias, const char *mailbox)
13589 {
13590  struct alias_mailbox_mapping *mapping;
13591  size_t from_len = strlen(alias) + 1;
13592  size_t to_len = strlen(mailbox) + 1;
13593 
13594  mapping = ao2_alloc(sizeof(*mapping) + from_len + to_len, NULL);
13595  if (!mapping) {
13596  return NULL;
13597  }
13598  mapping->alias = mapping->buf;
13599  mapping->mailbox = mapping->buf + from_len;
13600  ast_copy_string(mapping->alias, alias, from_len); /* Safe */
13601  ast_copy_string(mapping->mailbox, mailbox, to_len); /* Safe */
13602 
13603  return mapping;
13604 }
13605 
13606 static void load_aliases(struct ast_config *cfg)
13607 {
13608  struct ast_variable *var;
13609 
13610  if (ast_strlen_zero(aliasescontext)) {
13611  return;
13612  }
13613  var = ast_variable_browse(cfg, aliasescontext);
13614  while (var) {
13615  struct alias_mailbox_mapping *mapping = alias_mailbox_mapping_create(var->name, var->value);
13616  if (mapping) {
13617  ao2_link(alias_mailbox_mappings, mapping);
13618  ao2_link(mailbox_alias_mappings, mapping);
13619  ao2_ref(mapping, -1);
13620  }
13621  var = var->next;
13622  }
13623 }
13624 
13625 static void load_zonemessages(struct ast_config *cfg)
13626 {
13627  struct ast_variable *var;
13628 
13629  var = ast_variable_browse(cfg, "zonemessages");
13630  while (var) {
13631  if (var->value) {
13632  struct vm_zone *z;
13633  char *msg_format, *tzone;
13634  char storage[strlen(var->value) + 1];
13635 
13636  z = ast_malloc(sizeof(*z));
13637  if (!z) {
13638  return;
13639  }
13640 
13641  strcpy(storage, var->value); /* safe */
13642  msg_format = storage;
13643  tzone = strsep(&msg_format, "|,");
13644  if (msg_format) {
13645  ast_copy_string(z->name, var->name, sizeof(z->name));
13646  ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
13647  ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
13648  AST_LIST_LOCK(&zones);
13651  } else {
13652  ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
13653  ast_free(z);
13654  }
13655  }
13656  var = var->next;
13657  }
13658 }
13659 
13660 static void load_users(struct ast_config *cfg)
13661 {
13662  struct ast_variable *var;
13663  char *cat = NULL;
13664 
13665  while ((cat = ast_category_browse(cfg, cat))) {
13666  if (strcasecmp(cat, "general") == 0
13667  || strcasecmp(cat, aliasescontext) == 0
13668  || strcasecmp(cat, "zonemessages") == 0) {
13669  continue;
13670  }
13671 
13672  var = ast_variable_browse(cfg, cat);
13673  while (var) {
13674  append_mailbox(cat, var->name, var->value);
13675  var = var->next;
13676  }
13677  }
13678 }
13679 
13680 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
13681 {
13682  struct ast_vm_user *current;
13683  char *cat;
13684  const char *val;
13685  char *q, *stringp, *tmp;
13686  int x;
13687  unsigned int tmpadsi[4];
13688  char secretfn[PATH_MAX] = "";
13689  long tps_queue_low;
13690  long tps_queue_high;
13691 
13692 #ifdef IMAP_STORAGE
13693  ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
13694 #endif
13695  /* set audio control prompts */
13696  strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
13697  strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
13698  strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
13699  strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
13700  strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
13701 
13702 #ifdef IMAP_STORAGE
13703  ast_mwi_state_callback_all(imap_close_subscribed_mailbox, NULL);
13704 #endif
13705 
13706  /* Free all the users structure */
13707  free_vm_users();
13708 
13709  /* Free all the zones structure */
13710  free_vm_zones();
13711 
13712  /* Remove all aliases */
13713  ao2_callback(alias_mailbox_mappings, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
13714  ao2_callback(mailbox_alias_mappings, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
13715 
13716  AST_LIST_LOCK(&users);
13717 
13718  memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
13719  memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
13720 
13721  if (cfg) {
13722  /* General settings */
13723 
13724  if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
13725  val = "default";
13726  ast_copy_string(userscontext, val, sizeof(userscontext));
13727 
13728  aliasescontext[0] = '\0';
13729  val = ast_variable_retrieve(cfg, "general", "aliasescontext");
13730  ast_copy_string(aliasescontext, S_OR(val, ""), sizeof(aliasescontext));
13731 
13732  /* Attach voice message to mail message ? */
13733  if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
13734  val = "yes";
13735  ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH);
13736 
13737  if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
13738  val = "no";
13739  ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
13740 
13741  volgain = 0.0;
13742  if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
13743  sscanf(val, "%30lf", &volgain);
13744 
13745 #ifdef ODBC_STORAGE
13746  strcpy(odbc_database, "asterisk");
13747  if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
13748  ast_copy_string(odbc_database, val, sizeof(odbc_database));
13749  }
13750  strcpy(odbc_table, "voicemessages");
13751  if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
13752  ast_copy_string(odbc_table, val, sizeof(odbc_table));
13753  }
13754 #endif
13755  /* Mail command */
13756  strcpy(mailcmd, SENDMAIL);
13757  if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
13758  ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
13759 
13760  maxsilence = 0;
13761  if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
13762  maxsilence = atoi(val);
13763  if (maxsilence > 0)
13764  maxsilence *= 1000;
13765  }
13766 
13767  if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
13768  maxmsg = MAXMSG;
13769  } else {
13770  maxmsg = atoi(val);
13771  if (maxmsg < 0) {
13772  ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
13773  maxmsg = MAXMSG;
13774  } else if (maxmsg > MAXMSGLIMIT) {
13775  ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
13776  maxmsg = MAXMSGLIMIT;
13777  }
13778  }
13779 
13780  if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
13781  maxdeletedmsg = 0;
13782  } else {
13783  if (sscanf(val, "%30d", &x) == 1)
13784  maxdeletedmsg = x;
13785  else if (ast_true(val))
13786  maxdeletedmsg = MAXMSG;
13787  else
13788  maxdeletedmsg = 0;
13789 
13790  if (maxdeletedmsg < 0) {
13791  ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
13792  maxdeletedmsg = MAXMSG;
13793  } else if (maxdeletedmsg > MAXMSGLIMIT) {
13794  ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
13795  maxdeletedmsg = MAXMSGLIMIT;
13796  }
13797  }
13798 
13799  /* Load date format config for voicemail mail */
13800  if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
13801  ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
13802  }
13803 
13804  /* Load date format config for voicemail pager mail */
13805  if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
13806  ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
13807  }
13808 
13809  /* External password changing command */
13810  if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
13811  ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
13812  pwdchange = PWDCHANGE_EXTERNAL;
13813  } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
13814  ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
13815  pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
13816  }
13817 
13818  /* External password validation command */
13819  if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
13820  ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
13821  ast_debug(1, "found externpasscheck: %s\n", ext_pass_check_cmd);
13822  }
13823 
13824 #ifdef IMAP_STORAGE
13825  /* IMAP server address */
13826  if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
13827  ast_copy_string(imapserver, val, sizeof(imapserver));
13828  } else {
13829  ast_copy_string(imapserver, "localhost", sizeof(imapserver));
13830  }
13831  /* IMAP server port */
13832  if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
13833  ast_copy_string(imapport, val, sizeof(imapport));
13834  } else {
13835  ast_copy_string(imapport, "143", sizeof(imapport));
13836  }
13837  /* IMAP server flags */
13838  if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
13839  ast_copy_string(imapflags, val, sizeof(imapflags));
13840  }
13841  /* IMAP server master username */
13842  if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
13843  ast_copy_string(authuser, val, sizeof(authuser));
13844  }
13845  /* IMAP server master password */
13846  if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
13847  ast_copy_string(authpassword, val, sizeof(authpassword));
13848  }
13849  /* Expunge on exit */
13850  if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
13851  if (ast_false(val))
13852  expungeonhangup = 0;
13853  else
13854  expungeonhangup = 1;
13855  } else {
13856  expungeonhangup = 1;
13857  }
13858  /* IMAP voicemail folder */
13859  if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
13860  ast_copy_string(imapfolder, val, sizeof(imapfolder));
13861  } else {
13862  ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
13863  }
13864  if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
13865  ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
13866  }
13867  if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
13868  imapgreetings = ast_true(val);
13869  } else {
13870  imapgreetings = 0;
13871  }
13872  if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
13873  ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
13874  } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
13875  /* Also support greetingsfolder as documented in voicemail.conf.sample */
13876  ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
13877  } else {
13878  ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
13879  }
13880  if ((val = ast_variable_retrieve(cfg, "general", "imap_poll_logout"))) {
13881  imap_poll_logout = ast_true(val);
13882  } else {
13883  imap_poll_logout = 0;
13884  }
13885 
13886  /* There is some very unorthodox casting done here. This is due
13887  * to the way c-client handles the argument passed in. It expects a
13888  * void pointer and casts the pointer directly to a long without
13889  * first dereferencing it. */
13890  if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
13891  mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
13892  } else {
13893  mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
13894  }
13895 
13896  if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
13897  mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
13898  } else {
13899  mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
13900  }
13901 
13902  if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
13903  mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
13904  } else {
13905  mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
13906  }
13907 
13908  if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
13909  mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
13910  } else {
13911  mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
13912  }
13913 
13914  /* Increment configuration version */
13915  imapversion++;
13916 #endif
13917  /* External voicemail notify application */
13918  if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
13919  ast_copy_string(externnotify, val, sizeof(externnotify));
13920  ast_debug(1, "found externnotify: %s\n", externnotify);
13921  } else {
13922  externnotify[0] = '\0';
13923  }
13924 
13925  /* SMDI voicemail notification */
13926  if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
13927  ast_debug(1, "Enabled SMDI voicemail notification\n");
13928  if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
13929  smdi_iface = ast_smdi_interface_find(val);
13930  } else {
13931  ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
13932  smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
13933  }
13934  if (!smdi_iface) {
13935  ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
13936  }
13937  }
13938 
13939  /* Silence treshold */
13941  if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
13942  silencethreshold = atoi(val);
13943 
13944  if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
13945  val = ASTERISK_USERNAME;
13946  ast_copy_string(serveremail, val, sizeof(serveremail));
13947 
13948  vmmaxsecs = 0;
13949  if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
13950  if (sscanf(val, "%30d", &x) == 1) {
13951  vmmaxsecs = x;
13952  } else {
13953  ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
13954  }
13955  } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
13956  static int maxmessage_deprecate = 0;
13957  if (maxmessage_deprecate == 0) {
13958  maxmessage_deprecate = 1;
13959  ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
13960  }
13961  if (sscanf(val, "%30d", &x) == 1) {
13962  vmmaxsecs = x;
13963  } else {
13964  ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
13965  }
13966  }
13967 
13968  vmminsecs = 0;
13969  if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
13970  if (sscanf(val, "%30d", &x) == 1) {
13971  vmminsecs = x;
13972  if (maxsilence / 1000 >= vmminsecs) {
13973  ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
13974  }
13975  } else {
13976  ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
13977  }
13978  } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
13979  static int maxmessage_deprecate = 0;
13980  if (maxmessage_deprecate == 0) {
13981  maxmessage_deprecate = 1;
13982  ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
13983  }
13984  if (sscanf(val, "%30d", &x) == 1) {
13985  vmminsecs = x;
13986  if (maxsilence / 1000 >= vmminsecs) {
13987  ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
13988  }
13989  } else {
13990  ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
13991  }
13992  }
13993 
13994  val = ast_variable_retrieve(cfg, "general", "format");
13995  if (!val) {
13996  val = "wav";
13997  } else {
13998  tmp = ast_strdupa(val);
13999  val = ast_format_str_reduce(tmp);
14000  if (!val) {
14001  ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
14002  val = "wav";
14003  }
14004  }
14005  ast_copy_string(vmfmts, val, sizeof(vmfmts));
14006 
14007  skipms = 3000;
14008  if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
14009  if (sscanf(val, "%30d", &x) == 1) {
14010  maxgreet = x;
14011  } else {
14012  ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
14013  }
14014  }
14015 
14016  if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
14017  if (sscanf(val, "%30d", &x) == 1) {
14018  skipms = x;
14019  } else {
14020  ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
14021  }
14022  }
14023 
14024  maxlogins = 3;
14025  if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
14026  if (sscanf(val, "%30d", &x) == 1) {
14027  maxlogins = x;
14028  } else {
14029  ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
14030  }
14031  }
14032 
14033  minpassword = MINPASSWORD;
14034  if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
14035  if (sscanf(val, "%30d", &x) == 1) {
14036  minpassword = x;
14037  } else {
14038  ast_log(AST_LOG_WARNING, "Invalid minimum password length. Default to %d\n", minpassword);
14039  }
14040  }
14041 
14042  /* Force new user to record name ? */
14043  if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
14044  val = "no";
14045  ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
14046 
14047  /* Force new user to record greetings ? */
14048  if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
14049  val = "no";
14050  ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
14051 
14052  if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
14053  ast_debug(1, "VM_CID Internal context string: %s\n", val);
14054  stringp = ast_strdupa(val);
14055  for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
14056  if (!ast_strlen_zero(stringp)) {
14057  q = strsep(&stringp, ",");
14058  while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
14059  q++;
14060  ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
14061  ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
14062  } else {
14063  cidinternalcontexts[x][0] = '\0';
14064  }
14065  }
14066  }
14067  if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
14068  ast_debug(1, "VM Review Option disabled globally\n");
14069  val = "no";
14070  }
14071  ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW);
14072 
14073  /* Temporary greeting reminder */
14074  if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
14075  ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
14076  val = "no";
14077  } else {
14078  ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
14079  }
14080  ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
14081  if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
14082  ast_debug(1, "VM next message wrap disabled globally\n");
14083  val = "no";
14084  }
14085  ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);
14086 
14087  if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
14088  ast_debug(1, "VM Operator break disabled globally\n");
14089  val = "no";
14090  }
14091  ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);
14092 
14093  if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
14094  ast_debug(1, "VM CID Info before msg disabled globally\n");
14095  val = "no";
14096  }
14097  ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID);
14098 
14099  if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
14100  ast_debug(1, "Send Voicemail msg disabled globally\n");
14101  val = "no";
14102  }
14103  ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
14104 
14105  if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
14106  ast_debug(1, "ENVELOPE before msg enabled globally\n");
14107  val = "yes";
14108  }
14109  ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);
14110 
14111  if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
14112  ast_debug(1, "Move Heard enabled globally\n");
14113  val = "yes";
14114  }
14115  ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD);
14116 
14117  if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
14118  ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
14119  val = "no";
14120  }
14121  ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);
14122 
14123  if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
14124  ast_debug(1, "Duration info before msg enabled globally\n");
14125  val = "yes";
14126  }
14127  ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);
14128 
14129  saydurationminfo = 2;
14130  if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
14131  if (sscanf(val, "%30d", &x) == 1) {
14132  saydurationminfo = x;
14133  } else {
14134  ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
14135  }
14136  }
14137 
14138  if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
14139  ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
14140  val = "no";
14141  }
14142  ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
14143 
14144  if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
14145  ast_copy_string(dialcontext, val, sizeof(dialcontext));
14146  ast_debug(1, "found dialout context: %s\n", dialcontext);
14147  } else {
14148  dialcontext[0] = '\0';
14149  }
14150 
14151  if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
14152  ast_copy_string(callcontext, val, sizeof(callcontext));
14153  ast_debug(1, "found callback context: %s\n", callcontext);
14154  } else {
14155  callcontext[0] = '\0';
14156  }
14157 
14158  if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
14159  ast_copy_string(exitcontext, val, sizeof(exitcontext));
14160  ast_debug(1, "found operator context: %s\n", exitcontext);
14161  } else {
14162  exitcontext[0] = '\0';
14163  }
14164 
14165  /* load password sounds configuration */
14166  if ((val = ast_variable_retrieve(cfg, "general", "vm-login")))
14167  ast_copy_string(vm_login, val, sizeof(vm_login));
14168  if ((val = ast_variable_retrieve(cfg, "general", "vm-newuser")))
14169  ast_copy_string(vm_newuser, val, sizeof(vm_newuser));
14170  if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
14171  ast_copy_string(vm_password, val, sizeof(vm_password));
14172  if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
14173  ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
14174  if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
14175  ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
14176  if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
14177  ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
14178  if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
14179  ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
14180  if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
14181  ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
14182  if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
14183  ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
14184  }
14185  if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
14186  ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
14187  }
14188  /* load configurable audio prompts */
14189  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
14190  ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
14191  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
14192  ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
14193  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
14194  ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
14195  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
14196  ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
14197  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
14198  ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
14199 
14200  if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
14201  val = "no";
14202  ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD);
14203 
14204  if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
14205  val = "voicemail.conf";
14206  }
14207  if (!(strcmp(val, "spooldir"))) {
14208  passwordlocation = OPT_PWLOC_SPOOLDIR;
14209  } else {
14210  passwordlocation = OPT_PWLOC_VOICEMAILCONF;
14211  }
14212 
14213  poll_freq = DEFAULT_POLL_FREQ;
14214  if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
14215  if (sscanf(val, "%30u", &poll_freq) != 1) {
14216  poll_freq = DEFAULT_POLL_FREQ;
14217  ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
14218  }
14219  }
14220 
14221  poll_mailboxes = 0;
14222  if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
14223  poll_mailboxes = ast_true(val);
14224 
14225  memset(fromstring, 0, sizeof(fromstring));
14226  memset(pagerfromstring, 0, sizeof(pagerfromstring));
14227  strcpy(charset, "ISO-8859-1");
14228  if (emailbody) {
14229  ast_free(emailbody);
14230  emailbody = NULL;
14231  }
14232  if (emailsubject) {
14233  ast_free(emailsubject);
14234  emailsubject = NULL;
14235  }
14236  if (pagerbody) {
14237  ast_free(pagerbody);
14238  pagerbody = NULL;
14239  }
14240  if (pagersubject) {
14241  ast_free(pagersubject);
14242  pagersubject = NULL;
14243  }
14244  if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
14245  ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
14246  if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
14247  ast_copy_string(fromstring, val, sizeof(fromstring));
14248  if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
14249  ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
14250  if ((val = ast_variable_retrieve(cfg, "general", "charset")))
14251  ast_copy_string(charset, val, sizeof(charset));
14252  if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
14253  sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
14254  for (x = 0; x < 4; x++) {
14255  memcpy(&adsifdn[x], &tmpadsi[x], 1);
14256  }
14257  }
14258  if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
14259  sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
14260  for (x = 0; x < 4; x++) {
14261  memcpy(&adsisec[x], &tmpadsi[x], 1);
14262  }
14263  }
14264  if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
14265  if (atoi(val)) {
14266  adsiver = atoi(val);
14267  }
14268  }
14269  if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
14270  ast_copy_string(zonetag, val, sizeof(zonetag));
14271  }
14272  if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
14273  ast_copy_string(locale, val, sizeof(locale));
14274  }
14275  if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
14276  emailsubject = ast_strdup(substitute_escapes(val));
14277  }
14278  if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
14279  emailbody = ast_strdup(substitute_escapes(val));
14280  }
14281  if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
14282  pagersubject = ast_strdup(substitute_escapes(val));
14283  }
14284  if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
14285  pagerbody = ast_strdup(substitute_escapes(val));
14286  }
14287 
14288  tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
14289  if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_high"))) {
14290  if (sscanf(val, "%30ld", &tps_queue_high) != 1 || tps_queue_high <= 0) {
14291  ast_log(AST_LOG_WARNING, "Invalid the taskprocessor high water alert trigger level '%s'\n", val);
14292  tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
14293  }
14294  }
14295  tps_queue_low = -1;
14296  if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_low"))) {
14297  if (sscanf(val, "%30ld", &tps_queue_low) != 1 ||
14298  tps_queue_low < -1 || tps_queue_high < tps_queue_low) {
14299  ast_log(AST_LOG_WARNING, "Invalid the taskprocessor low water clear alert level '%s'\n", val);
14300  tps_queue_low = -1;
14301  }
14302  }
14303  if (ast_taskprocessor_alert_set_levels(mwi_subscription_tps, tps_queue_low, tps_queue_high)) {
14304  ast_log(AST_LOG_WARNING, "Failed to set alert levels for voicemail taskprocessor.\n");
14305  }
14306 
14307  /* load mailboxes from users.conf */
14308  if (ucfg) {
14309  for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
14310  if (!strcasecmp(cat, "general")) {
14311  continue;
14312  }
14313  if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
14314  continue;
14315  if ((current = find_or_create(userscontext, cat))) {
14316  populate_defaults(current);
14317  apply_options_full(current, ast_variable_browse(ucfg, cat));
14318  ast_copy_string(current->context, userscontext, sizeof(current->context));
14319  if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
14321  }
14322 
14323  switch (current->passwordlocation) {
14324  case OPT_PWLOC_SPOOLDIR:
14325  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
14326  read_password_from_file(secretfn, current->password, sizeof(current->password));
14327  }
14328  }
14329  }
14330  }
14331 
14332  /* load mailboxes from voicemail.conf */
14333 
14334  /*
14335  * Aliases must be loaded before users or the aliases won't be notified
14336  * if there's existing voicemail in the user mailbox.
14337  */
14338  load_aliases(cfg);
14339 
14340  load_zonemessages(cfg);
14341 
14342  load_users(cfg);
14343 
14345 
14346  if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
14348  if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
14349  stop_poll_thread();;
14350 
14351  return 0;
14352  } else {
14354  ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
14355  return 0;
14356  }
14357 }
14358 
14359 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
14360 {
14361  int res = -1;
14362  char dir[PATH_MAX];
14363  snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
14364  ast_debug(2, "About to try retrieving name file %s\n", dir);
14365  RETRIEVE(dir, -1, mailbox, context);
14366  if (ast_fileexists(dir, NULL, NULL)) {
14367  res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
14368  }
14369  DISPOSE(dir, -1);
14370  return res;
14371 }
14372 
14373 /*!
14374  * \internal
14375  * \brief Play a recorded user name for the mailbox to the specified channel.
14376  *
14377  * \param chan Where to play the recorded name file.
14378  * \param mailbox_id The mailbox name.
14379  *
14380  * \retval 0 Name played without interruption
14381  * \retval dtmf ASCII value of the DTMF which interrupted playback.
14382  * \retval -1 Unable to locate mailbox or hangup occurred.
14383  */
14384 static int vm_sayname(struct ast_channel *chan, const char *mailbox_id)
14385 {
14386  char *context;
14387  char *mailbox;
14388 
14389  if (ast_strlen_zero(mailbox_id)
14390  || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
14391  return -1;
14392  }
14393  return sayname(chan, mailbox, context);
14394 }
14395 
14396 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
14397  struct ast_config *pwconf;
14398  struct ast_flags config_flags = { 0 };
14399 
14400  pwconf = ast_config_load(secretfn, config_flags);
14401  if (valid_config(pwconf)) {
14402  const char *val = ast_variable_retrieve(pwconf, "general", "password");
14403  if (val) {
14404  ast_copy_string(password, val, passwordlen);
14405  ast_config_destroy(pwconf);
14406  return;
14407  }
14408  ast_config_destroy(pwconf);
14409  }
14410  ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
14411 }
14412 
14413 static int write_password_to_file(const char *secretfn, const char *password) {
14414  struct ast_config *conf;
14415  struct ast_category *cat;
14416  struct ast_variable *var;
14417  int res = -1;
14418 
14419  if (!(conf = ast_config_new())) {
14420  ast_log(LOG_ERROR, "Error creating new config structure\n");
14421  return res;
14422  }
14423  if (!(cat = ast_category_new("general", "", 1))) {
14424  ast_log(LOG_ERROR, "Error creating new category structure\n");
14425  ast_config_destroy(conf);
14426  return res;
14427  }
14428  if (!(var = ast_variable_new("password", password, ""))) {
14429  ast_log(LOG_ERROR, "Error creating new variable structure\n");
14430  ast_config_destroy(conf);
14431  ast_category_destroy(cat);
14432  return res;
14433  }
14434  ast_category_append(conf, cat);
14435  ast_variable_append(cat, var);
14436  if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
14437  res = 0;
14438  } else {
14439  ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
14440  }
14441 
14442  ast_config_destroy(conf);
14443  return res;
14444 }
14445 
14446 static int vmsayname_exec(struct ast_channel *chan, const char *data)
14447 {
14448  char *context;
14449  char *mailbox;
14450  int res;
14451 
14452  if (ast_strlen_zero(data)
14453  || separate_mailbox(ast_strdupa(data), &mailbox, &context)) {
14454  ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
14455  return -1;
14456  }
14457 
14458  if ((res = sayname(chan, mailbox, context)) < 0) {
14459  ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", mailbox, context);
14460  res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
14461  if (!res) {
14463  }
14464  }
14465 
14466  return res;
14467 }
14468 
14469 #ifdef TEST_FRAMEWORK
14470 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
14471 {
14472  return 0;
14473 }
14474 
14475 static struct ast_frame *fake_read(struct ast_channel *ast)
14476 {
14477  return &ast_null_frame;
14478 }
14479 
14480 AST_TEST_DEFINE(test_voicemail_vmsayname)
14481 {
14482  char dir[PATH_MAX];
14483  char dir2[PATH_MAX];
14484  static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
14485  static const char TEST_EXTENSION[] = "1234";
14486 
14487  struct ast_channel *test_channel1 = NULL;
14488  int res = -1;
14489  struct ast_format_cap *capabilities;
14490 
14491  static const struct ast_channel_tech fake_tech = {
14492  .write = fake_write,
14493  .read = fake_read,
14494  };
14495 
14496  switch (cmd) {
14497  case TEST_INIT:
14498  info->name = "vmsayname_exec";
14499  info->category = "/apps/app_voicemail/";
14500  info->summary = "Vmsayname unit test";
14501  info->description =
14502  "This tests passing various parameters to vmsayname";
14503  return AST_TEST_NOT_RUN;
14504  case TEST_EXECUTE:
14505  break;
14506  }
14507 
14508  if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL,
14509  NULL, NULL, 0, 0, "TestChannel1"))) {
14510  goto exit_vmsayname_test;
14511  }
14512 
14513  /* normally this is done in the channel driver */
14515  if (!capabilities) {
14516  goto exit_vmsayname_test;
14517  }
14518  ast_format_cap_append(capabilities, ast_format_gsm, 0);
14519  ast_channel_nativeformats_set(test_channel1, capabilities);
14520  ao2_ref(capabilities, -1);
14525  ast_channel_tech_set(test_channel1, &fake_tech);
14526 
14527  ast_channel_unlock(test_channel1);
14528 
14529  ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
14530  snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
14531  if (!(res = vmsayname_exec(test_channel1, dir))) {
14532  snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
14533  if (ast_fileexists(dir, NULL, NULL)) {
14534  ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
14535  res = -1;
14536  goto exit_vmsayname_test;
14537  } else {
14538  /* no greeting already exists as expected, let's create one to fully test sayname */
14539  if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
14540  ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
14541  goto exit_vmsayname_test;
14542  }
14543  snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_DATA_DIR);
14544  snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
14545  /* we're not going to hear the sound anyway, just use a valid gsm audio file */
14546  if ((res = symlink(dir, dir2))) {
14547  ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
14548  goto exit_vmsayname_test;
14549  }
14550  ast_test_status_update(test, "Test playing created mailbox greeting...\n");
14551  snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
14552  res = vmsayname_exec(test_channel1, dir);
14553 
14554  /* TODO: there may be a better way to do this */
14555  unlink(dir2);
14556  snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
14557  rmdir(dir2);
14558  snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
14559  rmdir(dir2);
14560  }
14561  }
14562 
14563 exit_vmsayname_test:
14564 
14565  ast_hangup(test_channel1);
14566 
14567  return res ? AST_TEST_FAIL : AST_TEST_PASS;
14568 }
14569 
14570 struct test_files {
14571  char dir[256];
14572  char file[256];
14573  char txtfile[256];
14574 };
14575 
14576 AST_TEST_DEFINE(test_voicemail_msgcount)
14577 {
14578  int i, j, res = AST_TEST_PASS, syserr;
14579  struct ast_vm_user *vmu;
14580  struct ast_vm_user svm;
14581  struct vm_state vms;
14582 #ifdef IMAP_STORAGE
14583  struct ast_channel *chan = NULL;
14584 #endif
14585  /* Using ast_alloca instead of just declaring tmp as an array is a workaround for a GCC 10 issue with -Wrestrict */
14586  struct test_files *tmp = ast_alloca(sizeof(struct test_files) * 3);
14587  char syscmd[256];
14588  const char origweasels[] = "tt-weasels";
14589  const char testcontext[] = "test";
14590  const char testmailbox[] = "00000000";
14591  const char testspec[] = "00000000@test";
14592  FILE *txt;
14593  int new, old, urgent;
14594  const char *folders[3] = { "Old", "Urgent", "INBOX" };
14595  const int folder2mbox[3] = { 1, 11, 0 };
14596  const int expected_results[3][12] = {
14597  /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
14598  { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
14599  { 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
14600  { 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 2 },
14601  };
14602 
14603  switch (cmd) {
14604  case TEST_INIT:
14605  info->name = "test_voicemail_msgcount";
14606  info->category = "/apps/app_voicemail/";
14607  info->summary = "Test Voicemail status checks";
14608  info->description =
14609  "Verify that message counts are correct when retrieved through the public API";
14610  return AST_TEST_NOT_RUN;
14611  case TEST_EXECUTE:
14612  break;
14613  }
14614 
14615  /* Make sure the original path was completely empty */
14616  snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
14617  if ((syserr = ast_safe_system(syscmd))) {
14618  ast_test_status_update(test, "Unable to clear test directory: %s\n",
14619  syserr > 0 ? strerror(syserr) : "unable to fork()");
14620  return AST_TEST_FAIL;
14621  }
14622 
14623 #ifdef IMAP_STORAGE
14624  if (!(chan = ast_dummy_channel_alloc())) {
14625  ast_test_status_update(test, "Unable to create dummy channel\n");
14626  return AST_TEST_FAIL;
14627  }
14628 #endif
14629 
14630  memset(&svm, 0, sizeof(svm));
14631  if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
14632  !(vmu = find_or_create(testcontext, testmailbox))) {
14633  ast_test_status_update(test, "Cannot create vmu structure\n");
14635 #ifdef IMAP_STORAGE
14636  chan = ast_channel_unref(chan);
14637 #endif
14638  return AST_TEST_FAIL;
14639  }
14640 
14641  populate_defaults(vmu);
14642  memset(&vms, 0, sizeof(vms));
14643 
14644  /* Create temporary voicemail */
14645  for (i = 0; i < 3; i++) {
14646  create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
14647  make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
14648  snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
14649 
14650  if (ast_fileexists(origweasels, "gsm", "en") > 0) {
14651  snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
14652  VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
14653  if ((syserr = ast_safe_system(syscmd))) {
14654  ast_test_status_update(test, "Unable to create test voicemail: %s\n",
14655  syserr > 0 ? strerror(syserr) : "unable to fork()");
14657 #ifdef IMAP_STORAGE
14658  chan = ast_channel_unref(chan);
14659 #endif
14660  free_user(vmu);
14661  return AST_TEST_FAIL;
14662  }
14663  }
14664 
14665  if ((txt = fopen(tmp[i].txtfile, "w+"))) {
14666  fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
14667  fclose(txt);
14668  } else {
14669  ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
14670  res = AST_TEST_FAIL;
14671  break;
14672  }
14673  open_mailbox(&vms, vmu, folder2mbox[i]);
14674  STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent", NULL);
14675 
14676  /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
14677  for (j = 0; j < 3; j++) {
14678  /* folder[2] is INBOX, __has_voicemail will default back to INBOX */
14679  if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
14680  ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
14681  testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
14682  res = AST_TEST_FAIL;
14683  }
14684  }
14685 
14686  new = old = urgent = 0;
14687  if (ast_app_inboxcount(testspec, &new, &old)) {
14688  ast_test_status_update(test, "inboxcount returned failure\n");
14689  res = AST_TEST_FAIL;
14690  } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
14691  ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
14692  testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
14693  res = AST_TEST_FAIL;
14694  }
14695 
14696  new = old = urgent = 0;
14697  if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
14698  ast_test_status_update(test, "inboxcount2 returned failure\n");
14699  res = AST_TEST_FAIL;
14700  } else if (old != expected_results[i][6 + 0] ||
14701  urgent != expected_results[i][6 + 1] ||
14702  new != expected_results[i][6 + 2] ) {
14703  ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
14704  testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
14705  res = AST_TEST_FAIL;
14706  }
14707 
14708  new = old = urgent = 0;
14709  for (j = 0; j < 3; j++) {
14710  if (ast_app_messagecount(testspec, folders[j]) != expected_results[i][9 + j]) {
14711  ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
14712  testspec, folders[j], ast_app_messagecount(testspec, folders[j]), expected_results[i][9 + j]);
14713  res = AST_TEST_FAIL;
14714  }
14715  }
14716  }
14717 
14718  for (i = 0; i < 3; i++) {
14719  /* This is necessary if the voicemails are stored on an ODBC/IMAP
14720  * server, in which case, the rm below will not affect the
14721  * voicemails. */
14722  DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
14723  DISPOSE(tmp[i].dir, 0);
14724  }
14725 
14726  if (vms.deleted) {
14727  ast_free(vms.deleted);
14728  }
14729  if (vms.heard) {
14730  ast_free(vms.heard);
14731  }
14732 
14733 #ifdef IMAP_STORAGE
14734  chan = ast_channel_unref(chan);
14735 #endif
14736 
14737  /* And remove test directory */
14738  snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
14739  if ((syserr = ast_safe_system(syscmd))) {
14740  ast_test_status_update(test, "Unable to clear test directory: %s\n",
14741  syserr > 0 ? strerror(syserr) : "unable to fork()");
14742  }
14743 
14744  free_user(vmu);
14745  return res;
14746 }
14747 
14748 AST_TEST_DEFINE(test_voicemail_notify_endl)
14749 {
14750  int res = AST_TEST_PASS;
14751  char testcontext[] = "test";
14752  char testmailbox[] = "00000000";
14753  char from[] = "[email protected]", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
14754  char attach[256], attach2[256];
14755  char buf[256] = ""; /* No line should actually be longer than 80 */
14756  struct ast_channel *chan = NULL;
14757  struct ast_vm_user *vmu, vmus = {
14758  .flags = 0,
14759  };
14760  FILE *file;
14761  struct {
14762  char *name;
14763  enum { INT, FLAGVAL, STATIC, STRPTR } type;
14764  void *location;
14765  union {
14766  int intval;
14767  char *strval;
14768  } u;
14769  } test_items[] = {
14770  { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
14771  { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
14772  { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
14773  { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <[email protected]>" },
14774  { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
14775  { "attach2", STRPTR, attach2, .u.strval = "" },
14776  { "attach", STRPTR, attach, .u.strval = "" },
14777  };
14778  int which;
14779 
14780  switch (cmd) {
14781  case TEST_INIT:
14782  info->name = "test_voicemail_notify_endl";
14783  info->category = "/apps/app_voicemail/";
14784  info->summary = "Test Voicemail notification end-of-line";
14785  info->description =
14786  "Verify that notification emails use a consistent end-of-line character";
14787  return AST_TEST_NOT_RUN;
14788  case TEST_EXECUTE:
14789  break;
14790  }
14791 
14792  snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_DATA_DIR);
14793  snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_DATA_DIR);
14794 
14795  if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
14796  !(vmu = find_or_create(testcontext, testmailbox))) {
14797  ast_test_status_update(test, "Cannot create vmu structure\n");
14798  return AST_TEST_NOT_RUN;
14799  }
14800 
14801  if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
14802  ast_test_status_update(test, "Cannot find vmu structure?!!\n");
14803  return AST_TEST_NOT_RUN;
14804  }
14805 
14806  populate_defaults(vmu);
14808 #ifdef IMAP_STORAGE
14809  /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
14810 #endif
14811 
14812  file = tmpfile();
14813  for (which = 0; which < ARRAY_LEN(test_items); which++) {
14814  /* Kill previous test, if any */
14815  rewind(file);
14816  if (ftruncate(fileno(file), 0)) {
14817  ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
14818  res = AST_TEST_FAIL;
14819  break;
14820  }
14821 
14822  /* Make each change, in order, to the test mailbox */
14823  if (test_items[which].type == INT) {
14824  *((int *) test_items[which].location) = test_items[which].u.intval;
14825  } else if (test_items[which].type == FLAGVAL) {
14826  if (ast_test_flag(vmu, test_items[which].u.intval)) {
14827  ast_clear_flag(vmu, test_items[which].u.intval);
14828  } else {
14829  ast_set_flag(vmu, test_items[which].u.intval);
14830  }
14831  } else if (test_items[which].type == STATIC) {
14832  strcpy(test_items[which].location, test_items[which].u.strval);
14833  } else if (test_items[which].type == STRPTR) {
14834  test_items[which].location = test_items[which].u.strval;
14835  }
14836 
14837  make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL, NULL);
14838  rewind(file);
14839  while (fgets(buf, sizeof(buf), file)) {
14840  if (
14841  (strlen(buf) > 1 &&
14842 #ifdef IMAP_STORAGE
14843  buf[strlen(buf) - 2] != '\r'
14844 #else
14845  buf[strlen(buf) - 2] == '\r'
14846 #endif
14847  )
14848  || buf[strlen(buf) - 1] != '\n') {
14849  res = AST_TEST_FAIL;
14850  }
14851  }
14852  }
14853  fclose(file);
14854  free_user(vmu);
14855  return res;
14856 }
14857 
14858 AST_TEST_DEFINE(test_voicemail_load_config)
14859 {
14860  int res = AST_TEST_PASS;
14861  struct ast_vm_user *vmu;
14862  struct ast_config *cfg;
14863  char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
14864  int fd;
14865  FILE *file;
14866  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
14867 
14868  switch (cmd) {
14869  case TEST_INIT:
14870  info->name = "test_voicemail_load_config";
14871  info->category = "/apps/app_voicemail/";
14872  info->summary = "Test loading Voicemail config";
14873  info->description =
14874  "Verify that configuration is loaded consistently. "
14875  "This is to test regressions of ASTERISK-18838 where it was noticed that "
14876  "some options were loaded after the mailboxes were instantiated, causing "
14877  "those options not to be set correctly.";
14878  return AST_TEST_NOT_RUN;
14879  case TEST_EXECUTE:
14880  break;
14881  }
14882 
14883  /* build a config file by hand... */
14884  if ((fd = mkstemp(config_filename)) < 0) {
14885  return AST_TEST_FAIL;
14886  }
14887  if (!(file = fdopen(fd, "w"))) {
14888  close(fd);
14889  unlink(config_filename);
14890  return AST_TEST_FAIL;
14891  }
14892  fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
14893  fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
14894  fputs("00000002 => 9999,Mrs. Test\n", file);
14895  fclose(file);
14896 
14897  if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
14898  res = AST_TEST_FAIL;
14899  goto cleanup;
14900  }
14901 
14902  load_config_from_memory(1, cfg, NULL);
14903  ast_config_destroy(cfg);
14904 
14905 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
14906  ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
14907  u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
14908 
14909  AST_LIST_LOCK(&users);
14910  AST_LIST_TRAVERSE(&users, vmu, list) {
14911  if (!strcmp(vmu->mailbox, "00000001")) {
14912  if (0); /* trick to get CHECK to work */
14913  CHECK(vmu, callback, "othercontext")
14914  CHECK(vmu, locale, "nl_NL.UTF-8")
14915  CHECK(vmu, zonetag, "central")
14916  } else if (!strcmp(vmu->mailbox, "00000002")) {
14917  if (0); /* trick to get CHECK to work */
14918  CHECK(vmu, callback, "somecontext")
14919  CHECK(vmu, locale, "de_DE.UTF-8")
14920  CHECK(vmu, zonetag, "european")
14921  }
14922  }
14924 
14925 #undef CHECK
14926 
14927  /* restore config */
14928  load_config(1); /* this might say "Failed to load configuration file." */
14929 
14930 cleanup:
14931  unlink(config_filename);
14932  return res;
14933 }
14934 
14935 AST_TEST_DEFINE(test_voicemail_vm_info)
14936 {
14937  struct ast_vm_user *vmu;
14938  struct ast_channel *chan = NULL;
14939  const char testcontext[] = "test";
14940  const char testmailbox[] = "00000000";
14941  const char vminfo_cmd[] = "VM_INFO";
14942  char vminfo_buf[256], vminfo_args[256];
14943  int res = AST_TEST_PASS;
14944  int test_ret = 0;
14945  int test_counter = 0;
14946 
14947  struct {
14948  char *vminfo_test_args;
14949  char *vminfo_expected;
14950  int vminfo_ret;
14951  } test_items[] = {
14952  { "", "", -1 }, /* Missing argument */
14953  { "00000000@test,badparam", "", -1 }, /* Wrong argument */
14954  { "00000000@test", "", -1 }, /* Missing argument */
14955  { "00000000@test,exists", "1", 0 },
14956  { "11111111@test,exists", "0", 0 }, /* Invalid mailbox */
14957  { "00000000@test,email", "[email protected]", 0 },
14958  { "11111111@test,email", "", 0 }, /* Invalid mailbox */
14959  { "00000000@test,fullname", "Test Framework Mailbox", 0 },
14960  { "00000000@test,pager", "[email protected]", 0 },
14961  { "00000000@test,locale", "en_US", 0 },
14962  { "00000000@test,tz", "central", 0 },
14963  { "00000000@test,language", "en", 0 },
14964  { "00000000@test,password", "9876", 0 },
14965  };
14966 
14967  switch (cmd) {
14968  case TEST_INIT:
14969  info->name = "test_voicemail_vm_info";
14970  info->category = "/apps/app_voicemail/";
14971  info->summary = "VM_INFO unit test";
14972  info->description =
14973  "This tests passing various parameters to VM_INFO";
14974  return AST_TEST_NOT_RUN;
14975  case TEST_EXECUTE:
14976  break;
14977  }
14978 
14979  if (!(chan = ast_dummy_channel_alloc())) {
14980  ast_test_status_update(test, "Unable to create dummy channel\n");
14981  return AST_TEST_FAIL;
14982  }
14983 
14984  if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
14985  !(vmu = find_or_create(testcontext, testmailbox))) {
14986  ast_test_status_update(test, "Cannot create vmu structure\n");
14987  chan = ast_channel_unref(chan);
14988  return AST_TEST_FAIL;
14989  }
14990 
14991  populate_defaults(vmu);
14992 
14994  ast_copy_string(vmu->fullname, "Test Framework Mailbox", sizeof(vmu->fullname));
14995  ast_copy_string(vmu->pager, "[email protected]", sizeof(vmu->pager));
14996  ast_copy_string(vmu->language, "en", sizeof(vmu->language));
14997  ast_copy_string(vmu->zonetag, "central", sizeof(vmu->zonetag));
14998  ast_copy_string(vmu->locale, "en_US", sizeof(vmu->zonetag));
14999  ast_copy_string(vmu->password, "9876", sizeof(vmu->password));
15000 
15001  for (test_counter = 0; test_counter < ARRAY_LEN(test_items); test_counter++) {
15002  ast_copy_string(vminfo_args, test_items[test_counter].vminfo_test_args, sizeof(vminfo_args));
15003  test_ret = acf_vm_info(chan, vminfo_cmd, vminfo_args, vminfo_buf, sizeof(vminfo_buf));
15004  if (strcmp(vminfo_buf, test_items[test_counter].vminfo_expected)) {
15005  ast_test_status_update(test, "VM_INFO respose was: '%s', but expected: '%s'\n", vminfo_buf, test_items[test_counter].vminfo_expected);
15006  res = AST_TEST_FAIL;
15007  }
15008  if (!(test_ret == test_items[test_counter].vminfo_ret)) {
15009  ast_test_status_update(test, "VM_INFO return code was: '%i', but expected '%i'\n", test_ret, test_items[test_counter].vminfo_ret);
15010  res = AST_TEST_FAIL;
15011  }
15012  }
15013 
15014  chan = ast_channel_unref(chan);
15015  free_user(vmu);
15016  return res;
15017 }
15018 #endif /* defined(TEST_FRAMEWORK) */
15019 
15020 static const struct ast_vm_functions vm_table = {
15022  .module_name = AST_MODULE,
15023 
15024  .has_voicemail = has_voicemail,
15025  .inboxcount = inboxcount,
15026  .inboxcount2 = inboxcount2,
15027  .messagecount = messagecount,
15028  .copy_recording_to_vm = msg_create_from_file,
15029  .index_to_foldername = vm_index_to_foldername,
15030  .mailbox_snapshot_create = vm_mailbox_snapshot_create,
15031  .mailbox_snapshot_destroy = vm_mailbox_snapshot_destroy,
15032  .msg_move = vm_msg_move,
15033  .msg_remove = vm_msg_remove,
15034  .msg_forward = vm_msg_forward,
15035  .msg_play = vm_msg_play,
15036 };
15037 
15040  .module_name = AST_MODULE,
15041 
15042  .sayname = vm_sayname,
15043 };
15044 
15045 static int reload(void)
15046 {
15047  return load_config(1);
15048 }
15049 
15050 static int unload_module(void)
15051 {
15052  int res;
15053 
15054  res = ast_unregister_application(voicemail_app);
15055  res |= ast_unregister_application(voicemailmain_app);
15056  res |= ast_unregister_application(vmauthenticate_app);
15057  res |= ast_unregister_application(playmsg_app);
15058  res |= ast_unregister_application(sayname_app);
15059  res |= ast_custom_function_unregister(&vm_info_acf);
15060  res |= ast_manager_unregister("VoicemailUsersList");
15061  res |= ast_manager_unregister("VoicemailUserStatus");
15062  res |= ast_manager_unregister("VoicemailRefresh");
15063 #ifdef TEST_FRAMEWORK
15064  res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
15065  res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
15066  res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
15067  res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
15068  res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
15069  res |= AST_TEST_UNREGISTER(test_voicemail_vm_info);
15070 #endif
15071  ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
15072  ast_vm_unregister(vm_table.module_name);
15073  ast_vm_greeter_unregister(vm_greeter_table.module_name);
15074 #ifdef TEST_FRAMEWORK
15076 #endif
15077  ao2_ref(inprocess_container, -1);
15078 
15079  ao2_container_unregister("voicemail_alias_mailbox_mappings");
15080  ao2_cleanup(alias_mailbox_mappings);
15081  ao2_container_unregister("voicemail_mailbox_alias_mappings");
15082  ao2_cleanup(mailbox_alias_mappings);
15083 
15084  if (poll_thread != AST_PTHREADT_NULL)
15085  stop_poll_thread();
15086 
15087  mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
15088  ast_unload_realtime("voicemail");
15089  ast_unload_realtime("voicemail_data");
15090 
15091 #ifdef IMAP_STORAGE
15092  ast_mwi_state_callback_all(imap_close_subscribed_mailbox, NULL);
15093 #endif
15094  free_vm_users();
15095  free_vm_zones();
15096  return res;
15097 }
15098 
15099 static void print_mappings(void *v_obj, void *where, ao2_prnt_fn *prnt)
15100 {
15101  struct alias_mailbox_mapping *mapping = v_obj;
15102 
15103  if (!mapping) {
15104  return;
15105  }
15106  prnt(where, "Alias: %s Mailbox: %s", mapping->alias, mapping->mailbox);
15107 }
15108 
15109 /*!
15110  * \brief Load the module
15111  *
15112  * Module loading including tests for configuration or dependencies.
15113  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
15114  * or AST_MODULE_LOAD_SUCCESS.
15115  *
15116  * If a dependency, allocation or environment variable fails tests, return AST_MODULE_LOAD_FAILURE.
15117  *
15118  * If the module can't load the configuration file, can't register as a provider or
15119  * has another issue not fatal to Asterisk itself, return AST_MODULE_LOAD_DECLINE.
15120  *
15121  * On success return AST_MODULE_LOAD_SUCCESS.
15122  */
15123 static int load_module(void)
15124 {
15125  int res;
15126  my_umask = umask(0);
15127  umask(my_umask);
15128 
15129  inprocess_container = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 573,
15131  if (!inprocess_container) {
15132  return AST_MODULE_LOAD_DECLINE;
15133  }
15134 
15136  alias_mailbox_mapping_hash_fn, NULL, alias_mailbox_mapping_cmp_fn);
15137  if (!alias_mailbox_mappings) {
15138  ast_log(LOG_ERROR, "Unable to create alias_mailbox_mappings container\n");
15139  ao2_cleanup(inprocess_container);
15140  return AST_MODULE_LOAD_DECLINE;
15141  }
15142  res = ao2_container_register("voicemail_alias_mailbox_mappings", alias_mailbox_mappings, print_mappings);
15143  if (res) {
15144  ast_log(LOG_ERROR, "Unable to register alias_mailbox_mappings container\n");
15145  ao2_cleanup(inprocess_container);
15146  ao2_cleanup(alias_mailbox_mappings);
15147  return AST_MODULE_LOAD_DECLINE;
15148  }
15149 
15151  mailbox_alias_mapping_hash_fn, NULL, mailbox_alias_mapping_cmp_fn);
15152  if (!mailbox_alias_mappings) {
15153  ast_log(LOG_ERROR, "Unable to create mailbox_alias_mappings container\n");
15154  ao2_cleanup(inprocess_container);
15155  ao2_container_unregister("voicemail_alias_mailbox_mappings");
15156  ao2_cleanup(alias_mailbox_mappings);
15157  return AST_MODULE_LOAD_DECLINE;
15158  }
15159  res = ao2_container_register("voicemail_mailbox_alias_mappings", mailbox_alias_mappings, print_mappings);
15160  if (res) {
15161  ast_log(LOG_ERROR, "Unable to register mailbox_alias_mappings container\n");
15162  ao2_cleanup(inprocess_container);
15163  ao2_container_unregister("voicemail_alias_mailbox_mappings");
15164  ao2_cleanup(alias_mailbox_mappings);
15165  ao2_cleanup(mailbox_alias_mappings);
15166  return AST_MODULE_LOAD_DECLINE;
15167  }
15168 
15169  /* compute the location of the voicemail spool directory */
15170  snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
15171 
15172  if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
15173  ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor. MWI will not work\n");
15174  }
15175 
15176  if ((res = load_config(0))) {
15177  unload_module();
15178  return AST_MODULE_LOAD_DECLINE;
15179  }
15180 
15181  res = ast_register_application_xml(voicemail_app, vm_exec);
15182  res |= ast_register_application_xml(voicemailmain_app, vm_execmain);
15183  res |= ast_register_application_xml(vmauthenticate_app, vmauthenticate);
15184  res |= ast_register_application_xml(playmsg_app, vm_playmsgexec);
15185  res |= ast_register_application_xml(sayname_app, vmsayname_exec);
15186  res |= ast_custom_function_register(&vm_info_acf);
15190 #ifdef TEST_FRAMEWORK
15191  res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
15192  res |= AST_TEST_REGISTER(test_voicemail_msgcount);
15193  res |= AST_TEST_REGISTER(test_voicemail_vmuser);
15194  res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
15195  res |= AST_TEST_REGISTER(test_voicemail_load_config);
15196  res |= AST_TEST_REGISTER(test_voicemail_vm_info);
15197 #endif
15198 
15199  if (res) {
15200  ast_log(LOG_ERROR, "Failure registering applications, functions or tests\n");
15201  unload_module();
15202  return AST_MODULE_LOAD_DECLINE;
15203  }
15204 
15205  /* ast_vm_register may return DECLINE if another module registered for vm */
15206  res = ast_vm_register(&vm_table);
15207  if (res) {
15208  ast_log(LOG_ERROR, "Failure registering as a voicemail provider\n");
15209  unload_module();
15210  return AST_MODULE_LOAD_DECLINE;
15211  }
15212 
15213  /* ast_vm_greeter_register may return DECLINE if another module registered as a greeter */
15214  res = ast_vm_greeter_register(&vm_greeter_table);
15215  if (res) {
15216  ast_log(LOG_ERROR, "Failure registering as a greeter provider\n");
15217  unload_module();
15218  return AST_MODULE_LOAD_DECLINE;
15219  }
15220 
15221  ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
15222 
15223 #ifdef TEST_FRAMEWORK
15225 #endif
15226 
15227  ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
15228  ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
15229 
15230  return AST_MODULE_LOAD_SUCCESS;
15231 }
15232 
15233 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
15234 {
15235  int cmd = 0;
15236  char destination[80] = "";
15237  int retries = 0;
15238 
15239  if (!num) {
15240  ast_verb(3, "Destination number will be entered manually\n");
15241  while (retries < 3 && cmd != 't') {
15242  destination[1] = '\0';
15243  destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
15244  if (!cmd)
15245  destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
15246  if (!cmd)
15247  destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
15248  if (!cmd) {
15249  cmd = ast_waitfordigit(chan, 6000);
15250  if (cmd)
15251  destination[0] = cmd;
15252  }
15253  if (!cmd) {
15254  retries++;
15255  } else {
15256 
15257  if (cmd < 0)
15258  return 0;
15259  if (cmd == '*') {
15260  ast_verb(3, "User hit '*' to cancel outgoing call\n");
15261  return 0;
15262  }
15263  if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0)
15264  retries++;
15265  else
15266  cmd = 't';
15267  }
15268  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
15269  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
15270  }
15271  if (retries >= 3) {
15272  return 0;
15273  }
15274 
15275  } else {
15276  ast_verb(3, "Destination number is CID number '%s'\n", num);
15277  ast_copy_string(destination, num, sizeof(destination));
15278  }
15279 
15280  if (!ast_strlen_zero(destination)) {
15281  if (destination[strlen(destination) -1 ] == '*')
15282  return 0;
15283  ast_verb(3, "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, ast_channel_context(chan));
15284  ast_channel_exten_set(chan, destination);
15285  ast_channel_context_set(chan, outgoing_context);
15286  ast_channel_priority_set(chan, 0);
15287  return 9;
15288  }
15289  return 0;
15290 }
15291 
15292 /*!
15293  * \brief The advanced options within a message.
15294  * \param chan
15295  * \param vmu
15296  * \param vms
15297  * \param msg
15298  * \param option
15299  * \param record_gain
15300  *
15301  * Provides handling for the play message envelope, call the person back, or reply to message.
15302  *
15303  * \return zero on success, -1 on error.
15304  */
15305 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)
15306 {
15307  int res = 0;
15308  char filename[PATH_MAX];
15309  struct ast_config *msg_cfg = NULL;
15310  const char *origtime, *context;
15311  char *name, *num;
15312  int retries = 0;
15313  char *cid;
15314  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
15315 
15316  vms->starting = 0;
15317 
15318  make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
15319 
15320  /* Retrieve info from VM attribute file */
15321  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
15322  RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
15323  msg_cfg = ast_config_load(filename, config_flags);
15324  DISPOSE(vms->curdir, vms->curmsg);
15325  if (!valid_config(msg_cfg)) {
15326  ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
15327  return 0;
15328  }
15329 
15330  if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
15331  ast_config_destroy(msg_cfg);
15332  return 0;
15333  }
15334 
15335  cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
15336 
15337  context = ast_variable_retrieve(msg_cfg, "message", "context");
15338  if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
15339  context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
15340  switch (option) {
15341  case 3: /* Play message envelope */
15342  if (!res) {
15343  res = play_message_datetime(chan, vmu, origtime, filename);
15344  }
15345  if (!res) {
15346  res = play_message_callerid(chan, vms, cid, context, 0, 1);
15347  }
15348 
15349  res = 't';
15350  break;
15351 
15352  case 2: /* Call back */
15353 
15354  if (ast_strlen_zero(cid))
15355  break;
15356 
15357  ast_callerid_parse(cid, &name, &num);
15358  while ((res > -1) && (res != 't')) {
15359  switch (res) {
15360  case '1':
15361  if (num) {
15362  /* Dial the CID number */
15363  res = dialout(chan, vmu, num, vmu->callback);
15364  if (res) {
15365  ast_config_destroy(msg_cfg);
15366  return 9;
15367  }
15368  } else {
15369  res = '2';
15370  }
15371  break;
15372 
15373  case '2':
15374  /* Want to enter a different number, can only do this if there's a dialout context for this user */
15375  if (!ast_strlen_zero(vmu->dialout)) {
15376  res = dialout(chan, vmu, NULL, vmu->dialout);
15377  if (res) {
15378  ast_config_destroy(msg_cfg);
15379  return 9;
15380  }
15381  } else {
15382  ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
15383  res = ast_play_and_wait(chan, "vm-sorry");
15384  }
15385  ast_config_destroy(msg_cfg);
15386  return res;
15387  case '*':
15388  res = 't';
15389  break;
15390  case '3':
15391  case '4':
15392  case '5':
15393  case '6':
15394  case '7':
15395  case '8':
15396  case '9':
15397  case '0':
15398 
15399  res = ast_play_and_wait(chan, "vm-sorry");
15400  retries++;
15401  break;
15402  default:
15403  if (num) {
15404  ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
15405  res = ast_play_and_wait(chan, "vm-num-i-have");
15406  if (!res)
15407  res = play_message_callerid(chan, vms, num, vmu->context, 1, 1);
15408  if (!res)
15409  res = ast_play_and_wait(chan, "vm-tocallnum");
15410  /* Only prompt for a caller-specified number if there is a dialout context specified */
15411  if (!ast_strlen_zero(vmu->dialout)) {
15412  if (!res)
15413  res = ast_play_and_wait(chan, "vm-calldiffnum");
15414  }
15415  } else {
15416  res = ast_play_and_wait(chan, "vm-nonumber");
15417  if (!ast_strlen_zero(vmu->dialout)) {
15418  if (!res)
15419  res = ast_play_and_wait(chan, "vm-toenternumber");
15420  }
15421  }
15422  if (!res) {
15423  res = ast_play_and_wait(chan, "vm-star-cancel");
15424  }
15425  if (!res) {
15426  res = ast_waitfordigit(chan, 6000);
15427  }
15428  if (!res) {
15429  retries++;
15430  if (retries > 3) {
15431  res = 't';
15432  }
15433  }
15434  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
15435  isprint(res) ? res : '?', isprint(res) ? res : '?');
15436  break;
15437 
15438  }
15439  if (res == 't')
15440  res = 0;
15441  else if (res == '*')
15442  res = -1;
15443  }
15444  break;
15445 
15446  case 1: /* Reply */
15447  /* Send reply directly to sender */
15448  if (ast_strlen_zero(cid))
15449  break;
15450 
15451  ast_callerid_parse(cid, &name, &num);
15452  if (!num) {
15453  ast_verb(3, "No CID number available, no reply sent\n");
15454  if (!res)
15455  res = ast_play_and_wait(chan, "vm-nonumber");
15456  ast_config_destroy(msg_cfg);
15457  return res;
15458  } else {
15459  struct ast_vm_user vmu2, *vmu3;
15460  memset(&vmu2, 0, sizeof(vmu2));
15461  vmu3 = find_user(&vmu2, vmu->context, num);
15462  if (vmu3) {
15463  struct leave_vm_options leave_options;
15464  char mailbox[AST_MAX_EXTENSION * 2 + 2];
15465  snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
15466 
15467  ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
15468 
15469  memset(&leave_options, 0, sizeof(leave_options));
15470  leave_options.record_gain = record_gain;
15471  leave_options.beeptone = "beep";
15472  res = leave_voicemail(chan, mailbox, &leave_options);
15473  if (!res)
15474  res = 't';
15475  ast_config_destroy(msg_cfg);
15476  free_user(vmu3);
15477  return res;
15478  } else {
15479  /* Sender has no mailbox, can't reply */
15480  ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
15481  ast_play_and_wait(chan, "vm-nobox");
15482  res = 't';
15483  ast_config_destroy(msg_cfg);
15484  return res;
15485  }
15486  }
15487  res = 0;
15488 
15489  break;
15490  }
15491 
15492  ast_config_destroy(msg_cfg);
15493 
15494 #ifndef IMAP_STORAGE
15495  if (!res) {
15496  make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
15497  vms->heard[msg] = 1;
15498  res = wait_file(chan, vms, vms->fn);
15499  }
15500 #endif
15501  return res;
15502 }
15503 
15504 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
15505  int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
15506  signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro)
15507 {
15508  /* Record message & let caller review or re-record it, or set options if applicable */
15509  int res = 0;
15510  int cmd = 0;
15511  int max_attempts = 3;
15512  int attempts = 0;
15513  int recorded = 0;
15514  int msg_exists = 0;
15515  signed char zero_gain = 0;
15516  char tempfile[PATH_MAX];
15517  char *acceptdtmf = "#";
15518  char *canceldtmf = "";
15519  int canceleddtmf = 0;
15520 
15521  /* Note that urgent and private are for flagging messages as such in the future */
15522 
15523  /* barf if no pointer passed to store duration in */
15524  if (duration == NULL) {
15525  ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
15526  return -1;
15527  }
15528 
15529  if (!outsidecaller)
15530  snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
15531  else
15532  ast_copy_string(tempfile, recordfile, sizeof(tempfile));
15533 
15534  cmd = '3'; /* Want to start by recording */
15535 
15536  while ((cmd >= 0) && (cmd != 't')) {
15537  switch (cmd) {
15538  case '1':
15539  if (!msg_exists) {
15540  /* In this case, 1 is to record a message */
15541  cmd = '3';
15542  break;
15543  } else {
15544  /* Otherwise 1 is to save the existing message */
15545  ast_verb(3, "Saving message as is\n");
15546  if (!outsidecaller)
15547  ast_filerename(tempfile, recordfile, NULL);
15548  if (!forwardintro) {
15549  ast_stream_and_wait(chan, "vm-msgsaved", "");
15550  }
15551  if (!outsidecaller) {
15552  /* Saves to IMAP server only if imapgreeting=yes */
15553  STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag, msg_id);
15554  DISPOSE(recordfile, -1);
15555  }
15556  cmd = 't';
15557  return res;
15558  }
15559  case '2':
15560  /* Review */
15561  ast_verb(3, "Reviewing the message\n");
15562  cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
15563  break;
15564  case '3':
15565  msg_exists = 0;
15566  /* Record */
15567  if (recorded == 1)
15568  ast_verb(3, "Re-recording the message\n");
15569  else
15570  ast_verb(3, "Recording the message\n");
15571 
15572  if (recorded && outsidecaller) {
15573  if (forwardintro) {
15574  cmd = ast_play_and_wait(chan, "vm-record-prepend");
15575  } else {
15576  cmd = ast_play_and_wait(chan, INTRO);
15577  }
15578  cmd = ast_play_and_wait(chan, "beep");
15579  }
15580  recorded = 1;
15581  /* 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 */
15582  if (record_gain)
15583  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
15584  if (ast_test_flag(vmu, VM_OPERATOR))
15585  canceldtmf = "0";
15586  cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, 0, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf, 0, AST_RECORD_IF_EXISTS_OVERWRITE);
15587  if (strchr(canceldtmf, cmd)) {
15588  /* need this flag here to distinguish between pressing '0' during message recording or after */
15589  canceleddtmf = 1;
15590  }
15591  if (record_gain)
15592  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
15593  if (cmd == -1) {
15594  /* User has hung up, no options to give */
15595  if (!outsidecaller) {
15596  /* user was recording a greeting and they hung up, so let's delete the recording. */
15597  ast_filedelete(tempfile, NULL);
15598  }
15599  return cmd;
15600  }
15601  if (cmd == '0') {
15602  break;
15603  } else if (cmd == '*') {
15604  break;
15605 #if 0
15606  } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
15607  /* Message is too short */
15608  ast_verb(3, "Message too short\n");
15609  cmd = ast_play_and_wait(chan, "vm-tooshort");
15610  cmd = ast_filedelete(tempfile, NULL);
15611  break;
15612  } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
15613  /* Message is all silence */
15614  ast_verb(3, "Nothing recorded\n");
15615  cmd = ast_filedelete(tempfile, NULL);
15616  cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
15617  if (!cmd)
15618  cmd = ast_play_and_wait(chan, "vm-speakup");
15619  break;
15620 #endif
15621  } else {
15622  /* If all is well, a message exists */
15623  msg_exists = 1;
15624  cmd = 0;
15625  }
15626  break;
15627  case '4':
15628  if (outsidecaller) { /* only mark vm messages */
15629  /* Mark Urgent */
15630  if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
15631  ast_verb(3, "marking message as Urgent\n");
15632  res = ast_play_and_wait(chan, "vm-marked-urgent");
15633  strcpy(flag, "Urgent");
15634  } else if (flag) {
15635  ast_verb(3, "UNmarking message as Urgent\n");
15636  res = ast_play_and_wait(chan, "vm-marked-nonurgent");
15637  strcpy(flag, "");
15638  } else {
15639  ast_play_and_wait(chan, "vm-sorry");
15640  }
15641  cmd = 0;
15642  } else {
15643  cmd = ast_play_and_wait(chan, "vm-sorry");
15644  }
15645  break;
15646  case '5':
15647  case '6':
15648  case '7':
15649  case '8':
15650  case '9':
15651  case '*':
15652  case '#':
15653  cmd = ast_play_and_wait(chan, "vm-sorry");
15654  break;
15655 #if 0
15656 /* XXX Commented out for the moment because of the dangers of deleting
15657  a message while recording (can put the message numbers out of sync) */
15658  case '*':
15659  /* Cancel recording, delete message, offer to take another message*/
15660  cmd = ast_play_and_wait(chan, "vm-deleted");
15661  cmd = ast_filedelete(tempfile, NULL);
15662  if (outsidecaller) {
15663  res = vm_exec(chan, NULL);
15664  return res;
15665  }
15666  else
15667  return 1;
15668 #endif
15669  case '0':
15670  if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
15671  cmd = ast_play_and_wait(chan, "vm-sorry");
15672  break;
15673  }
15674  if (msg_exists || recorded) {
15675  cmd = ast_play_and_wait(chan, "vm-saveoper");
15676  if (!cmd)
15677  cmd = ast_waitfordigit(chan, 3000);
15678  if (cmd == '1') {
15679  ast_filerename(tempfile, recordfile, NULL);
15680  ast_play_and_wait(chan, "vm-msgsaved");
15681  cmd = '0';
15682  } else if (cmd == '4') {
15683  if (flag) {
15684  ast_play_and_wait(chan, "vm-marked-urgent");
15685  strcpy(flag, "Urgent");
15686  }
15687  ast_play_and_wait(chan, "vm-msgsaved");
15688  cmd = '0';
15689  } else {
15690  ast_play_and_wait(chan, "vm-deleted");
15691  DELETE(tempfile, -1, tempfile, vmu);
15692  DISPOSE(tempfile, -1);
15693  cmd = '0';
15694  }
15695  }
15696  return cmd;
15697  default:
15698  /* If the caller is an ouside caller and the review option is enabled or it's forward intro
15699  allow them to review the message, but let the owner of the box review
15700  their OGM's */
15701  if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW) && !forwardintro)
15702  return cmd;
15703  if (msg_exists) {
15704  cmd = ast_play_and_wait(chan, "vm-review");
15705  if (!cmd && outsidecaller) {
15706  if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
15707  cmd = ast_play_and_wait(chan, "vm-review-urgent");
15708  } else if (flag) {
15709  cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
15710  }
15711  }
15712  } else {
15713  cmd = ast_play_and_wait(chan, "vm-torerecord");
15714  if (!cmd)
15715  cmd = ast_waitfordigit(chan, 600);
15716  }
15717 
15718  if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
15719  cmd = ast_play_and_wait(chan, "vm-reachoper");
15720  if (!cmd)
15721  cmd = ast_waitfordigit(chan, 600);
15722  }
15723 #if 0
15724  if (!cmd)
15725  cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
15726 #endif
15727  if (!cmd)
15728  cmd = ast_waitfordigit(chan, 6000);
15729  if (!cmd) {
15730  attempts++;
15731  }
15732  if (attempts > max_attempts) {
15733  cmd = 't';
15734  }
15735  }
15736  }
15737  if (!outsidecaller && (cmd == -1 || cmd == 't')) {
15738  /* Hang up or timeout, so delete the recording. */
15739  ast_filedelete(tempfile, NULL);
15740  }
15741 
15742  if (cmd != 't' && outsidecaller)
15743  ast_play_and_wait(chan, "vm-goodbye");
15744 
15745  return cmd;
15746 }
15747 
15749 {
15750  struct ast_vm_msg_snapshot *msg_snapshot;
15751 
15752  if (!(msg_snapshot = ast_calloc(1, sizeof(*msg_snapshot)))) {
15753  return NULL;
15754  }
15755 
15756  if (ast_string_field_init(msg_snapshot, 512)) {
15757  ast_free(msg_snapshot);
15758  return NULL;
15759  }
15760 
15761  return msg_snapshot;
15762 }
15763 
15765 {
15766  ast_string_field_free_memory(msg_snapshot);
15767  ast_free(msg_snapshot);
15768 
15769  return NULL;
15770 }
15771 
15772 #ifdef TEST_FRAMEWORK
15773 
15774 static int vm_test_destroy_user(const char *context, const char *mailbox)
15775 {
15776  struct ast_vm_user *vmu;
15777 
15778  AST_LIST_LOCK(&users);
15780  if (!strcmp(context, vmu->context)
15781  && !strcmp(mailbox, vmu->mailbox)) {
15783  ast_free(vmu);
15784  break;
15785  }
15786  }
15789  return 0;
15790 }
15791 
15792 static int vm_test_create_user(const char *context, const char *mailbox)
15793 {
15794  struct ast_vm_user *vmu;
15795 
15796  if (!(vmu = find_or_create(context, mailbox))) {
15797  return -1;
15798  }
15799  populate_defaults(vmu);
15800  return 0;
15801 }
15802 
15803 #endif
15804 
15805 /*!
15806  * \brief Create and store off all the msgs in an open mailbox
15807  *
15808  * \note TODO XXX This function should work properly for all
15809  * voicemail storage options, but is far more expensive for
15810  * ODBC at the moment. This is because the RETRIEVE macro
15811  * not only pulls out the message's meta data file from the
15812  * database, but also the actual audio for each message, temporarily
15813  * writing it to the file system. This is an area that needs
15814  * to be made more efficient.
15815  */
15816 static int vm_msg_snapshot_create(struct ast_vm_user *vmu,
15817  struct vm_state *vms,
15818  struct ast_vm_mailbox_snapshot *mailbox_snapshot,
15819  int snapshot_index,
15820  int mailbox_index,
15821  int descending,
15822  enum ast_vm_snapshot_sort_val sort_val)
15823 {
15824  struct ast_vm_msg_snapshot *msg_snapshot;
15825  struct ast_vm_msg_snapshot *msg_snapshot_tmp;
15826  struct ast_config *msg_cfg;
15827  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
15828  char filename[PATH_MAX];
15829  const char *value;
15830 
15831  for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
15832  int inserted = 0;
15833  /* Find the msg */
15834  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
15835  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
15836  RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
15837  msg_cfg = ast_config_load(filename, config_flags);
15838  if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
15839  DISPOSE(vms->curdir, vms->curmsg);
15840  continue;
15841  }
15842 
15843  /* Create the snapshot object */
15844  if (!(msg_snapshot = vm_msg_snapshot_alloc())) {
15845  ast_config_destroy(msg_cfg);
15846  return -1;
15847  }
15848 
15849  /* Fill in the snapshot object */
15850  if ((value = ast_variable_retrieve(msg_cfg, "message", "msg_id"))) {
15851  ast_string_field_set(msg_snapshot, msg_id, value);
15852  } else {
15853  /* Message snapshots *really* should have a
15854  * message ID. Add one to the message config
15855  * if it does not already exist
15856  */
15857  char id[MSG_ID_LEN];
15858  if (!(add_message_id(msg_cfg, vms->curdir, vms->curmsg,
15859  filename, id, sizeof(id), vmu, mailbox_index))) {
15860  ast_string_field_set(msg_snapshot, msg_id, id);
15861  } else {
15862  ast_log(LOG_WARNING, "Unable to create a message ID for message %s/%d\n", vms->curdir, vms->curmsg);
15863  }
15864  }
15865  if ((value = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
15866  ast_string_field_set(msg_snapshot, callerid, value);
15867  }
15868  if ((value = ast_variable_retrieve(msg_cfg, "message", "callerchan"))) {
15869  ast_string_field_set(msg_snapshot, callerchan, value);
15870  }
15871  if ((value = ast_variable_retrieve(msg_cfg, "message", "exten"))) {
15872  ast_string_field_set(msg_snapshot, exten, value);
15873  }
15874  if ((value = ast_variable_retrieve(msg_cfg, "message", "origdate"))) {
15875  ast_string_field_set(msg_snapshot, origdate, value);
15876  }
15877  if ((value = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
15878  ast_string_field_set(msg_snapshot, origtime, value);
15879  }
15880  if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
15881  ast_string_field_set(msg_snapshot, duration, value);
15882  }
15883  if ((value = ast_variable_retrieve(msg_cfg, "message", "flag"))) {
15884  ast_string_field_set(msg_snapshot, flag, value);
15885  }
15886  msg_snapshot->msg_number = vms->curmsg;
15887  ast_string_field_set(msg_snapshot, folder_name, mailbox_folders[mailbox_index]);
15888 
15889  /* store msg snapshot in mailbox snapshot */
15890  switch (sort_val) {
15891  default:
15893  if (descending) {
15894  AST_LIST_INSERT_HEAD(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
15895  } else {
15896  AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
15897  }
15898  inserted = 1;
15899  break;
15901  AST_LIST_TRAVERSE_SAFE_BEGIN(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot_tmp, msg) {
15902  int val = strcmp(msg_snapshot->origtime, msg_snapshot_tmp->origtime);
15903  if (descending && val >= 0) {
15904  AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
15905  inserted = 1;
15906  break;
15907  } else if (!descending && val <= 0) {
15908  AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
15909  inserted = 1;
15910  break;
15911  }
15912  }
15914  break;
15915  }
15916 
15917  if (!inserted) {
15918  AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
15919  }
15920 
15921  mailbox_snapshot->total_msg_num++;
15922 
15923  /* cleanup configs and msg */
15924  ast_config_destroy(msg_cfg);
15925  DISPOSE(vms->curdir, vms->curmsg);
15926  }
15927 
15928  return 0;
15929 }
15930 
15931 static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_create(const char *mailbox,
15932  const char *context,
15933  const char *folder,
15934  int descending,
15935  enum ast_vm_snapshot_sort_val sort_val,
15936  int combine_INBOX_and_OLD)
15937 {
15938  struct ast_vm_mailbox_snapshot *mailbox_snapshot;
15939  struct vm_state vms;
15940  struct ast_vm_user *vmu = NULL, vmus;
15941  int res;
15942  int i;
15943  int this_index_only = -1;
15944  int open = 0;
15945  int inbox_index = get_folder_by_name("INBOX");
15946  int old_index = get_folder_by_name("Old");
15947  int urgent_index = get_folder_by_name("Urgent");
15948 
15949  if (ast_strlen_zero(mailbox)) {
15950  ast_log(LOG_WARNING, "Cannot create a mailbox snapshot since no mailbox was specified\n");
15951  return NULL;
15952  }
15953 
15954  memset(&vmus, 0, sizeof(vmus));
15955 
15956  if (!(ast_strlen_zero(folder))) {
15957  /* find the folder index */
15958  for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
15959  if (!strcasecmp(mailbox_folders[i], folder)) {
15960  this_index_only = i;
15961  break;
15962  }
15963  }
15964  if (this_index_only == -1) {
15965  /* Folder was specified and it did not match any folder in our list */
15966  return NULL;
15967  }
15968  }
15969 
15970  if (!(vmu = find_user(&vmus, context, mailbox))) {
15971  ast_log(AST_LOG_WARNING, "Failed to create mailbox snapshot for unknown voicemail user %s@%s\n", mailbox, context);
15972  return NULL;
15973  }
15974 
15975  if (!(mailbox_snapshot = ast_calloc(1, sizeof(*mailbox_snapshot)))) {
15976  ast_log(AST_LOG_ERROR, "Failed to allocate memory for mailbox snapshot\n");
15977  free_user(vmu);
15978  return NULL;
15979  }
15980 
15981  if (!(mailbox_snapshot->snapshots = ast_calloc(ARRAY_LEN(mailbox_folders), sizeof(*mailbox_snapshot->snapshots)))) {
15982  ast_free(mailbox_snapshot);
15983  free_user(vmu);
15984  return NULL;
15985  }
15986 
15987  mailbox_snapshot->folders = ARRAY_LEN(mailbox_folders);
15988 
15989  for (i = 0; i < mailbox_snapshot->folders; i++) {
15990  int msg_folder_index = i;
15991 
15992  /* We want this message in the snapshot if any of the following:
15993  * No folder was specified.
15994  * The specified folder matches the current folder.
15995  * The specified folder is INBOX AND we were asked to combine messages AND the current folder is either Old or Urgent.
15996  */
15997  if (!(this_index_only == -1 || this_index_only == i || (this_index_only == inbox_index && combine_INBOX_and_OLD && (i == old_index || i == urgent_index)))) {
15998  continue;
15999  }
16000 
16001  /* Make sure that Old or Urgent messages are marked as being in INBOX. */
16002  if (combine_INBOX_and_OLD && (i == old_index || i == urgent_index)) {
16003  msg_folder_index = inbox_index;
16004  }
16005 
16006  memset(&vms, 0, sizeof(vms));
16007  ast_copy_string(vms.username, mailbox, sizeof(vms.username));
16008  vms.lastmsg = -1;
16009  open = 0;
16010 
16011  /* open the mailbox state */
16012  if ((res = open_mailbox(&vms, vmu, i)) < 0) {
16013  ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
16014  goto snapshot_cleanup;
16015  }
16016  open = 1;
16017 
16018  /* Iterate through each msg, storing off info */
16019  if (vms.lastmsg != -1) {
16020  if ((vm_msg_snapshot_create(vmu, &vms, mailbox_snapshot, msg_folder_index, i, descending, sort_val))) {
16021  ast_log(LOG_WARNING, "Failed to create msg snapshots for %s@%s\n", mailbox, context);
16022  goto snapshot_cleanup;
16023  }
16024  }
16025 
16026  /* close mailbox */
16027  if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
16028  goto snapshot_cleanup;
16029  }
16030  open = 0;
16031  }
16032 
16033 snapshot_cleanup:
16034  if (vmu && open) {
16035  close_mailbox(&vms, vmu);
16036  }
16037 
16038 #ifdef IMAP_STORAGE
16039  if (vmu) {
16040  vmstate_delete(&vms);
16041  }
16042 #endif
16043 
16044  free_user(vmu);
16045  return mailbox_snapshot;
16046 }
16047 
16049 {
16050  int i;
16051  struct ast_vm_msg_snapshot *msg_snapshot;
16052 
16053  for (i = 0; i < mailbox_snapshot->folders; i++) {
16054  while ((msg_snapshot = AST_LIST_REMOVE_HEAD(&mailbox_snapshot->snapshots[i], msg))) {
16055  msg_snapshot = vm_msg_snapshot_destroy(msg_snapshot);
16056  }
16057  }
16058  ast_free(mailbox_snapshot->snapshots);
16059  ast_free(mailbox_snapshot);
16060  return NULL;
16061 }
16062 
16063 /*!
16064  * \brief common bounds checking and existence check for Voicemail API functions.
16065  *
16066  * \details
16067  * This is called by vm_msg_move, vm_msg_remove, and vm_msg_forward to
16068  * ensure that data passed in are valid. This ensures that given the
16069  * desired message IDs, they can be found.
16070  *
16071  * \param vms The voicemail state corresponding to an open mailbox
16072  * \param msg_ids An array of message identifiers
16073  * \param num_msgs The number of identifiers in msg_ids
16074  * \param msg_nums [out] The message indexes corresponding to the given
16075  * \param vmu
16076  * message IDs
16077  * \pre vms must have open_mailbox() called on it prior to this function.
16078  *
16079  * \retval -1 Failure
16080  * \retval 0 Success
16081  */
16082 static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids [], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu)
16083 {
16084  int i;
16085  int res = 0;
16086  for (i = 0; i < num_msgs; ++i) {
16087  const char *msg_id = msg_ids[i];
16088  int found = 0;
16089  for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
16090  const char *other_msg_id;
16091  char filename[PATH_MAX];
16092  struct ast_config *msg_cfg;
16093  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
16094 
16095  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
16096  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
16097  RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
16098  msg_cfg = ast_config_load(filename, config_flags);
16099  if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
16100  DISPOSE(vms->curdir, vms->curmsg);
16101  res = -1;
16102  goto done;
16103  }
16104 
16105  other_msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
16106 
16107  if (!ast_strlen_zero(other_msg_id) && !strcmp(other_msg_id, msg_id)) {
16108  /* Message found. We can get out of this inner loop
16109  * and move on to the next message to find
16110  */
16111  found = 1;
16112  msg_nums[i] = vms->curmsg;
16113  ast_config_destroy(msg_cfg);
16114  DISPOSE(vms->curdir, vms->curmsg);
16115  break;
16116  }
16117  ast_config_destroy(msg_cfg);
16118  DISPOSE(vms->curdir, vms->curmsg);
16119  }
16120  if (!found) {
16121  /* If we can't find one of the message IDs requested, then OH NO! */
16122  res = -1;
16123  goto done;
16124  }
16125  }
16126 
16127 done:
16128  return res;
16129 }
16130 
16131 static void notify_new_state(struct ast_vm_user *vmu)
16132 {
16133  int new = 0, old = 0, urgent = 0;
16134  char ext_context[1024];
16135 
16136  snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
16137  run_externnotify(vmu->context, vmu->mailbox, NULL);
16138  ast_app_inboxcount2(ext_context, &urgent, &new, &old);
16139  queue_mwi_event(NULL, ext_context, urgent, new, old);
16140 }
16141 
16142 static int vm_msg_forward(const char *from_mailbox,
16143  const char *from_context,
16144  const char *from_folder,
16145  const char *to_mailbox,
16146  const char *to_context,
16147  const char *to_folder,
16148  size_t num_msgs,
16149  const char *msg_ids [],
16150  int delete_old)
16151 {
16152  struct vm_state from_vms;
16153  struct ast_vm_user *vmu = NULL, vmus;
16154  struct ast_vm_user *to_vmu = NULL, to_vmus;
16155  struct ast_config *msg_cfg;
16156  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
16157  char filename[PATH_MAX];
16158  int from_folder_index;
16159  int open = 0;
16160  int res = 0;
16161  int i;
16162  int *msg_nums;
16163 
16164  if (ast_strlen_zero(from_mailbox) || ast_strlen_zero(to_mailbox)) {
16165  ast_log(LOG_WARNING, "Cannot forward message because either the from or to mailbox was not specified\n");
16166  return -1;
16167  }
16168 
16169  if (!num_msgs) {
16170  ast_log(LOG_WARNING, "Invalid number of messages specified to forward: %zu\n", num_msgs);
16171  return -1;
16172  }
16173 
16174  if (ast_strlen_zero(from_folder) || ast_strlen_zero(to_folder)) {
16175  ast_log(LOG_WARNING, "Cannot forward message because the from_folder or to_folder was not specified\n");
16176  return -1;
16177  }
16178 
16179  memset(&vmus, 0, sizeof(vmus));
16180  memset(&to_vmus, 0, sizeof(to_vmus));
16181  memset(&from_vms, 0, sizeof(from_vms));
16182 
16183  from_folder_index = get_folder_by_name(from_folder);
16184  if (from_folder_index == -1) {
16185  return -1;
16186  }
16187 
16188  if (get_folder_by_name(to_folder) == -1) {
16189  return -1;
16190  }
16191 
16192  if (!(vmu = find_user(&vmus, from_context, from_mailbox))) {
16193  ast_log(LOG_WARNING, "Can't find voicemail user to forward from (%s@%s)\n", from_mailbox, from_context);
16194  return -1;
16195  }
16196 
16197  if (!(to_vmu = find_user(&to_vmus, to_context, to_mailbox))) {
16198  ast_log(LOG_WARNING, "Can't find voicemail user to forward to (%s@%s)\n", to_mailbox, to_context);
16199  free_user(vmu);
16200  return -1;
16201  }
16202 
16203  ast_copy_string(from_vms.username, from_mailbox, sizeof(from_vms.username));
16204  from_vms.lastmsg = -1;
16205  open = 0;
16206 
16207  /* open the mailbox state */
16208  if ((res = open_mailbox(&from_vms, vmu, from_folder_index)) < 0) {
16209  ast_log(LOG_WARNING, "Could not open mailbox %s\n", from_mailbox);
16210  res = -1;
16211  goto vm_forward_cleanup;
16212  }
16213 
16214  open = 1;
16215 
16216  if ((from_vms.lastmsg + 1) < num_msgs) {
16217  ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", from_folder, num_msgs);
16218  res = -1;
16219  goto vm_forward_cleanup;
16220  }
16221 
16222  msg_nums = ast_alloca(sizeof(int) * num_msgs);
16223 
16224  if ((res = message_range_and_existence_check(&from_vms, msg_ids, num_msgs, msg_nums, vmu) < 0)) {
16225  goto vm_forward_cleanup;
16226  }
16227 
16228  /* Now we actually forward the messages */
16229  for (i = 0; i < num_msgs; i++) {
16230  int cur_msg = msg_nums[i];
16231  int duration = 0;
16232  const char *value;
16233 
16234  make_file(from_vms.fn, sizeof(from_vms.fn), from_vms.curdir, cur_msg);
16235  snprintf(filename, sizeof(filename), "%s.txt", from_vms.fn);
16236  RETRIEVE(from_vms.curdir, cur_msg, vmu->mailbox, vmu->context);
16237  msg_cfg = ast_config_load(filename, config_flags);
16238  /* XXX This likely will not fail since we previously ensured that the
16239  * message we are looking for exists. However, there still could be some
16240  * circumstance where this fails, so atomicity is not guaranteed.
16241  */
16242  if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
16243  DISPOSE(from_vms.curdir, cur_msg);
16244  continue;
16245  }
16246  if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
16247  duration = atoi(value);
16248  }
16249 
16250  copy_message(NULL, vmu, from_folder_index, cur_msg, duration, to_vmu, vmfmts, from_vms.curdir, "", to_folder);
16251 
16252  if (delete_old) {
16253  from_vms.deleted[cur_msg] = 1;
16254  }
16255  ast_config_destroy(msg_cfg);
16256  DISPOSE(from_vms.curdir, cur_msg);
16257  }
16258 
16259  /* close mailbox */
16260  if ((res = close_mailbox(&from_vms, vmu) == ERROR_LOCK_PATH)) {
16261  res = -1;
16262  goto vm_forward_cleanup;
16263  }
16264  open = 0;
16265 
16266 vm_forward_cleanup:
16267  if (vmu && open) {
16268  close_mailbox(&from_vms, vmu);
16269  }
16270 #ifdef IMAP_STORAGE
16271  if (vmu) {
16272  vmstate_delete(&from_vms);
16273  }
16274 #endif
16275 
16276  if (!res) {
16277  notify_new_state(to_vmu);
16278  }
16279 
16280  free_user(vmu);
16281  free_user(to_vmu);
16282  return res;
16283 }
16284 
16285 static int vm_msg_move(const char *mailbox,
16286  const char *context,
16287  size_t num_msgs,
16288  const char *oldfolder,
16289  const char *old_msg_ids [],
16290  const char *newfolder)
16291 {
16292  struct vm_state vms;
16293  struct ast_vm_user *vmu = NULL, vmus;
16294  int old_folder_index;
16295  int new_folder_index;
16296  int open = 0;
16297  int res = 0;
16298  int i;
16299  int *old_msg_nums;
16300 
16301  if (ast_strlen_zero(mailbox)) {
16302  ast_log(LOG_WARNING, "Cannot move message because no mailbox was specified\n");
16303  return -1;
16304  }
16305 
16306  if (!num_msgs) {
16307  ast_log(LOG_WARNING, "Invalid number of messages specified to move: %zu\n", num_msgs);
16308  return -1;
16309  }
16310 
16311  if (ast_strlen_zero(oldfolder) || ast_strlen_zero(newfolder)) {
16312  ast_log(LOG_WARNING, "Cannot move message because either oldfolder or newfolder was not specified\n");
16313  return -1;
16314  }
16315 
16316  old_folder_index = get_folder_by_name(oldfolder);
16317  new_folder_index = get_folder_by_name(newfolder);
16318 
16319  memset(&vmus, 0, sizeof(vmus));
16320  memset(&vms, 0, sizeof(vms));
16321 
16322  if (old_folder_index == -1 || new_folder_index == -1) {
16323  return -1;
16324  }
16325 
16326  if (!(vmu = find_user(&vmus, context, mailbox))) {
16327  return -1;
16328  }
16329 
16330  ast_copy_string(vms.username, mailbox, sizeof(vms.username));
16331  vms.lastmsg = -1;
16332  open = 0;
16333 
16334  /* open the mailbox state */
16335  if ((res = open_mailbox(&vms, vmu, old_folder_index)) < 0) {
16336  ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
16337  res = -1;
16338  goto vm_move_cleanup;
16339  }
16340 
16341  open = 1;
16342 
16343  if ((vms.lastmsg + 1) < num_msgs) {
16344  ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", oldfolder, num_msgs);
16345  res = -1;
16346  goto vm_move_cleanup;
16347  }
16348 
16349  old_msg_nums = ast_alloca(sizeof(int) * num_msgs);
16350 
16351  if ((res = message_range_and_existence_check(&vms, old_msg_ids, num_msgs, old_msg_nums, vmu)) < 0) {
16352  goto vm_move_cleanup;
16353  }
16354 
16355  /* Now actually move the message */
16356  for (i = 0; i < num_msgs; ++i) {
16357  if (save_to_folder(vmu, &vms, old_msg_nums[i], new_folder_index, NULL, 0)) {
16358  res = -1;
16359  goto vm_move_cleanup;
16360  }
16361  vms.deleted[old_msg_nums[i]] = 1;
16362  }
16363 
16364  /* close mailbox */
16365  if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
16366  res = -1;
16367  goto vm_move_cleanup;
16368  }
16369  open = 0;
16370 
16371 vm_move_cleanup:
16372  if (vmu && open) {
16373  close_mailbox(&vms, vmu);
16374  }
16375 #ifdef IMAP_STORAGE
16376  if (vmu) {
16377  vmstate_delete(&vms);
16378  }
16379 #endif
16380 
16381  if (!res) {
16382  notify_new_state(vmu);
16383  }
16384 
16385  free_user(vmu);
16386  return res;
16387 }
16388 
16389 static int vm_msg_remove(const char *mailbox,
16390  const char *context,
16391  size_t num_msgs,
16392  const char *folder,
16393  const char *msgs[])
16394 {
16395  struct vm_state vms;
16396  struct ast_vm_user *vmu = NULL, vmus;
16397  int folder_index;
16398  int open = 0;
16399  int res = 0;
16400  int i;
16401  int *msg_nums;
16402 
16403  if (ast_strlen_zero(mailbox)) {
16404  ast_log(LOG_WARNING, "Cannot remove message because no mailbox was specified\n");
16405  return -1;
16406  }
16407 
16408  if (!num_msgs) {
16409  ast_log(LOG_WARNING, "Invalid number of messages specified to remove: %zu\n", num_msgs);
16410  return -1;
16411  }
16412 
16413  if (ast_strlen_zero(folder)) {
16414  ast_log(LOG_WARNING, "Cannot remove message because no folder was specified\n");
16415  return -1;
16416  }
16417 
16418  memset(&vmus, 0, sizeof(vmus));
16419  memset(&vms, 0, sizeof(vms));
16420 
16421  folder_index = get_folder_by_name(folder);
16422  if (folder_index == -1) {
16423  ast_log(LOG_WARNING, "Could not remove msgs from unknown folder %s\n", folder);
16424  return -1;
16425  }
16426 
16427  if (!(vmu = find_user(&vmus, context, mailbox))) {
16428  ast_log(LOG_WARNING, "Can't find voicemail user to remove msg from (%s@%s)\n", mailbox, context);
16429  return -1;
16430  }
16431 
16432  ast_copy_string(vms.username, mailbox, sizeof(vms.username));
16433  vms.lastmsg = -1;
16434  open = 0;
16435 
16436  /* open the mailbox state */
16437  if ((res = open_mailbox(&vms, vmu, folder_index)) < 0) {
16438  ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
16439  res = -1;
16440  goto vm_remove_cleanup;
16441  }
16442 
16443  open = 1;
16444 
16445  if ((vms.lastmsg + 1) < num_msgs) {
16446  ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", folder, num_msgs);
16447  res = -1;
16448  goto vm_remove_cleanup;
16449  }
16450 
16451  msg_nums = ast_alloca(sizeof(int) * num_msgs);
16452 
16453  if ((res = message_range_and_existence_check(&vms, msgs, num_msgs, msg_nums, vmu)) < 0) {
16454  goto vm_remove_cleanup;
16455  }
16456 
16457  for (i = 0; i < num_msgs; i++) {
16458  vms.deleted[msg_nums[i]] = 1;
16459  }
16460 
16461  /* close mailbox */
16462  if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
16463  res = -1;
16464  ast_log(AST_LOG_ERROR, "Failed to close mailbox folder %s while removing msgs\n", folder);
16465  goto vm_remove_cleanup;
16466  }
16467  open = 0;
16468 
16469 vm_remove_cleanup:
16470  if (vmu && open) {
16471  close_mailbox(&vms, vmu);
16472  }
16473 #ifdef IMAP_STORAGE
16474  if (vmu) {
16475  vmstate_delete(&vms);
16476  }
16477 #endif
16478 
16479  if (!res) {
16480  notify_new_state(vmu);
16481  }
16482 
16483  free_user(vmu);
16484  return res;
16485 }
16486 
16487 static int vm_msg_play(struct ast_channel *chan,
16488  const char *mailbox,
16489  const char *context,
16490  const char *folder,
16491  const char *msg_id,
16492  ast_vm_msg_play_cb cb)
16493 {
16494  struct vm_state vms;
16495  struct ast_vm_user *vmu = NULL, vmus;
16496  int res = 0;
16497  int open = 0;
16498  int i;
16499  char filename[PATH_MAX];
16500  struct ast_config *msg_cfg;
16501  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
16502  int duration = 0;
16503  const char *value;
16504 
16505  if (ast_strlen_zero(mailbox)) {
16506  ast_log(LOG_WARNING, "Cannot play message because no mailbox was specified\n");
16507  return -1;
16508  }
16509 
16510  if (ast_strlen_zero(folder)) {
16511  ast_log(LOG_WARNING, "Cannot play message because no folder was specified\n");
16512  return -1;
16513  }
16514 
16515  if (ast_strlen_zero(msg_id)) {
16516  ast_log(LOG_WARNING, "Cannot play message because no message number was specified\n");
16517  return -1;
16518  }
16519 
16520  memset(&vmus, 0, sizeof(vmus));
16521  memset(&vms, 0, sizeof(vms));
16522 
16523  if (ast_strlen_zero(context)) {
16524  context = "default";
16525  }
16526 
16527  if (!(vmu = find_user(&vmus, context, mailbox))) {
16528  return -1;
16529  }
16530 
16531  i = get_folder_by_name(folder);
16532  ast_copy_string(vms.username, mailbox, sizeof(vms.username));
16533  vms.lastmsg = -1;
16534  if ((res = open_mailbox(&vms, vmu, i)) < 0) {
16535  ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
16536  goto play2_msg_cleanup;
16537  }
16538  open = 1;
16539 
16540  if (message_range_and_existence_check(&vms, &msg_id, 1, &vms.curmsg, vmu)) {
16541  res = -1;
16542  goto play2_msg_cleanup;
16543  }
16544 
16545  /* Find the msg */
16546  make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
16547  snprintf(filename, sizeof(filename), "%s.txt", vms.fn);
16548  RETRIEVE(vms.curdir, vms.curmsg, vmu->mailbox, vmu->context);
16549 
16550  msg_cfg = ast_config_load(filename, config_flags);
16551  if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
16552  DISPOSE(vms.curdir, vms.curmsg);
16553  res = -1;
16554  goto play2_msg_cleanup;
16555  }
16556  if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
16557  duration = atoi(value);
16558  }
16559  ast_config_destroy(msg_cfg);
16560 
16561 #ifdef IMAP_STORAGE
16562  /*IMAP storage stores any prepended message from a forward
16563  * as a separate file from the rest of the message
16564  */
16565  if (!ast_strlen_zero(vms.introfn) && ast_fileexists(vms.introfn, NULL, NULL) > 0) {
16566  wait_file(chan, &vms, vms.introfn);
16567  }
16568 #endif
16569  if (cb) {
16570  cb(chan, vms.fn, duration);
16571  } else if ((wait_file(chan, &vms, vms.fn)) < 0) {
16572  ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms.fn);
16573  } else {
16574  res = 0;
16575  }
16576 
16577  vms.heard[vms.curmsg] = 1;
16578 
16579  /* cleanup configs and msg */
16580  DISPOSE(vms.curdir, vms.curmsg);
16581 
16582 play2_msg_cleanup:
16583  if (vmu && open) {
16584  close_mailbox(&vms, vmu);
16585  }
16586 
16587 #ifdef IMAP_STORAGE
16588  if (vmu) {
16589  vmstate_delete(&vms);
16590  }
16591 #endif
16592 
16593  if (!res) {
16594  notify_new_state(vmu);
16595  }
16596 
16597  free_user(vmu);
16598  return res;
16599 }
16600 
16601 /* This is a workaround so that menuselect displays a proper description
16602  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
16603  */
16604 
16606  .support_level = AST_MODULE_SUPPORT_CORE,
16607  .load = load_module,
16608  .unload = unload_module,
16609  .reload = reload,
16610  .optional_modules = "res_adsi,res_smdi",
16611 );
static char user[512]
double volgain
#define VM_TEMPGREETWARN
unsigned int module_version
The version of this function table.
const char * name
Definition: pbx.h:119
int ast_filecopy(const char *oldname, const char *newname, const char *fmt)
Copies a file.
Definition: file.c:1108
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
SQLHDBC con
Definition: res_odbc.h:47
struct ast_category * ast_category_new(const char *name, const char *in_file, int lineno)
Create a category.
Definition: extconf.c:2790
static int mwi_handle_unsubscribe2(void *data)
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3339
struct ast_variable * next
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:84
static const char type[]
Definition: chan_ooh323.c:109
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, const char *dest_folder)
Copies a message from one mailbox to another.
static int saydurationminfo
#define MSG_ID_LEN
#define ast_channel_lock(chan)
Definition: channel.h:2945
static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
static char exten[AST_MAX_EXTENSION]
Definition: chan_alsa.c:118
static char userscontext[AST_MAX_EXTENSION]
Main Channel structure associated with a channel.
int ast_adsi_voice_mode(unsigned char *buf, int when)
Puts CPE in voice mode.
Definition: adsi.c:252
static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
int ast_delete_mwi_state_full(const char *mailbox, const char *context, struct ast_eid *eid)
Delete MWI state cached by stasis with all parameters.
Definition: mwi.c:399
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
static char * vm_check_password_shell(char *command, char *buf, size_t len)
char * str
Subscriber phone number (Malloced)
Definition: channel.h:292
static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
int ast_config_text_file_save(const char *filename, const struct ast_config *cfg, const char *generator)
Save a config text file preserving the pre 13.2 behavior.
Definition: main/config.c:2531
int oldmessages
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1250
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
static void free_vm_zones(void)
Free the zones structure.
static char ext_pass_cmd[128]
Asterisk locking-related definitions:
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3080
static struct ast_custom_function vm_info_acf
Asterisk main include file. File version handling, generic pbx functions.
static int vm_play_folder_name_ja(struct ast_channel *chan, char *box)
static const char * substitute_escapes(const char *value)
struct vm_zone::@83 list
static int minpassword
static const struct ast_vm_functions vm_table
#define ast_realloc(p, len)
A wrapper for realloc()
Definition: astmm.h:228
static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
int ast_adsi_begin_download(struct ast_channel *chan, char *service, unsigned char *fdn, unsigned char *sec, int version)
Definition: adsi.c:32
static int load_config(int reload)
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
Loads the options specific to a voicemail user.
static int get_folder_ja(struct ast_channel *chan, int start)
static char mailcmd[160]
#define MAX_VM_CONTEXT_LEN
static FILE * vm_mkftemp(char *template)
unsigned char iobuf[BASEMAXINLINE]
#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY
int ast_callerid_split(const char *src, char *name, int namelen, char *num, int numlen)
Definition: callerid.c:1092
#define MAX_NUM_CID_CONTEXTS
static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Default English syntax for &#39;You have N messages&#39; greeting.
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition: pbx_app.c:471
static char vm_newuser[80]
static void rename_file(char *sfn, char *dfn)
Renames a message in a mailbox folder.
void ast_unreplace_sigchld(void)
Restore the SIGCHLD handler.
Definition: extconf.c:815
String manipulation functions.
#define ADSI_MSG_DISPLAY
Definition: adsi.h:32
static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
#define TEST_EXTENSION
Definition: test_message.c:47
char username[80]
static unsigned char adsifdn[4]
static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
Manager list voicemail users command.
void ast_channel_set_writeformat(struct ast_channel *chan, struct ast_format *format)
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1263
int ast_adsi_data_mode(unsigned char *buf)
Puts CPE in data mode.
Definition: adsi.c:219
char timezone[80]
Definition: ast_expr2.c:325
static void free_vm_users(void)
Free the users structure.
static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
#define AST_DIGIT_ANY
Definition: file.h:48
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
int(*const write)(struct ast_channel *chan, struct ast_frame *frame)
Write a frame, in standard format (see frame.h)
Definition: channel.h:751
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2981
static char * voicemailmain_app
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1105
static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
basically mkdir -p $dest/$context/$ext/$folder
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define ast_test_flag(p, flag)
Definition: utils.h:63
static int vm_msg_snapshot_create(struct ast_vm_user *vmu, struct vm_state *vms, struct ast_vm_mailbox_snapshot *mailbox_snapshot, int snapshot_index, int mailbox_index, int descending, enum ast_vm_snapshot_sort_val sort_val)
Create and store off all the msgs in an open mailbox.
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1216
const ast_string_field recording_file
static char pagerdateformat[32]
static int adsi_logo(unsigned char *buf)
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition: channel.c:4322
static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
Creates a file system path expression for a folder within the voicemail data folder and the appropria...
Time-related functions and macros.
unsigned int flags
int * deleted
struct ast_party_name name
Subscriber name.
Definition: channel.h:341
const ast_string_field mailbox
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static int manager_status_voicemail_user(struct mansession *s, const struct message *m)
static int * map
Definition: misdn_config.c:438
#define SMDI_MWI_WAIT_TIMEOUT
Convenient Signal Processing routines.
static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback, int saycidnumber)
static int adsiver
static int inprocess_cmp_fn(void *obj, void *arg, int flags)
static int reload(void)
void ast_channel_set_rawwriteformat(struct ast_channel *chan, struct ast_format *format)
#define ast_set_flag(p, flag)
Definition: utils.h:70
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)
Sends email notification that a user has a new voicemail waiting for them.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
void ast_install_vm_test_functions(ast_vm_test_create_user_fn *vm_test_create_user_func, ast_vm_test_destroy_user_fn *vm_test_destroy_user_func)
Definition: main/app.c:599
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
Definition: manager.c:3237
#define ERROR_LOCK_PATH
static char * handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail users in the CLI.
vm_option_flags
static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Greek syntax for &#39;You have N messages&#39; greeting.
#define VM_SAYDURATION
descriptor for a cli entry.
Definition: cli.h:171
const int argc
Definition: cli.h:160
#define LOG_WARNING
Definition: logger.h:274
void ast_uninstall_vm_test_functions(void)
Definition: main/app.c:606
char * context
static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
static int maxsilence
static struct ao2_container * alias_mailbox_mappings
int ast_file_is_readable(const char *filename)
Test that a file exists and is readable by the effective user.
Definition: main/utils.c:2855
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
Voicemail greeter function table definition.
static char intro[ADSI_MAX_INTRO][20]
Definition: res_adsi.c:65
static int debug
Global debug status.
Definition: res_xmpp.c:435
#define AST_TASKPROCESSOR_HIGH_WATER_LEVEL
Definition: taskprocessor.h:63
static int pwdchange
static struct test_val d
#define ao2_callback(c, flags, cb_fn, arg)
Definition: astobj2.h:1716
static int vm_intro_ja(struct ast_channel *chan, struct vm_state *vms)
struct ast_taskprocessor * ast_taskprocessor_get(const char *name, enum ast_tps_options create)
Get a reference to a taskprocessor with the specified name and create the taskprocessor if necessary...
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
Definition: main/config.c:3406
#define CONFIG_STATUS_FILEINVALID
static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
static struct stasis_rest_handlers mailboxes
REST handler for /api-docs/mailboxes.json.
static int maxdeletedmsg
struct ast_smdi_interface * ast_smdi_interface_find(const char *iface_name)
Find an SMDI interface with the specified name.
Definition: res_smdi.c:563
static void print_mappings(void *v_obj, void *where, ao2_prnt_fn *prnt)
int ast_odbc_prepare(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Prepares a SQL query on a statement.
Definition: res_odbc.c:463
static int tmp()
Definition: bt_open.c:389
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
#define tdesc
static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
static void copy_plain_file(char *frompath, char *topath)
Copies a voicemail information (envelope) file.
static char email[80]
Definition: pbx_dundi.c:206
void(* on_subscribe)(const char *mailbox, struct ast_mwi_subscriber *sub)
Raised when MWI is being subscribed.
Definition: mwi.h:259
static int vm_browse_messages_ja(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Japanese syntax for &#39;You have N messages&#39; greeting.
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3363
Structure for variables, used for configurations and for channel variables.
void ast_close_fds_above_n(int n)
Common routine for child processes, to close all fds prior to exec(2)
Definition: main/app.c:3042
vm_option_args
static int append_vmu_info_astman(struct mansession *s, struct ast_vm_user *vmu, const char *event_name, const char *actionid)
Append vmu info string into given astman with event_name.
#define AST_LOG_WARNING
Definition: logger.h:279
#define var
Definition: ast_expr2f.c:614
#define MAPPING_BUCKETS
static unsigned char adsisec[4]
static int vm_sayname(struct ast_channel *chan, const char *mailbox_id)
static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
static int count_messages(struct ast_vm_user *vmu, char *dir)
Find all .txt files - even if they are not in sequence from 0000.
#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY
int ast_adsi_set_keys(unsigned char *buf, unsigned char *keys)
Set which soft keys should be displayed.
Definition: adsi.c:307
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:438
int ast_adsi_load_session(struct ast_channel *chan, unsigned char *app, int ver, int data)
Check if scripts for a given app are already loaded. Version may be -1, if any version is okay...
Definition: adsi.c:76
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
static int manager_voicemail_refresh(struct mansession *s, const struct message *m)
Test Framework API.
static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
int ast_say_digit_str(struct ast_channel *chan, const char *num, const char *ints, const char *lang)
says digits of a string
Definition: channel.c:8355
#define VM_MESSAGEWRAP
#define EVENT_FLAG_CALL
Definition: manager.h:72
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
Definition: cli.h:152
static int messagecount(const char *mailbox_id, const char *folder)
static int maxgreet
int ast_adsi_download_disconnect(unsigned char *buf)
Disconnects (and hopefully saves) a downloaded script.
Definition: adsi.c:208
ast_channel_state
ast_channel states
Definition: channelstate.h:35
char curdir[PATH_MAX]
char * str
Subscriber name (Malloced)
Definition: channel.h:265
static void adsi_login(struct ast_channel *chan)
static pthread_t poll_thread
static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
static char listen_control_reverse_key[12]
#define VM_ATTACH
static const struct ast_vm_greeter_functions vm_greeter_table
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
void ast_vm_unregister(const char *module_name)
Unregister the specified voicemail provider.
Definition: main/app.c:473
static struct ast_threadstorage buf2
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
static int copy(char *infile, char *outfile)
Utility function to copy a file.
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime_sec, char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence_ms)
Record a file based on input frm a channel. Recording is performed in &#39;prepend&#39; mode which works a li...
Definition: main/app.c:2002
#define VM_GREETER_MODULE_VERSION
char pager[80]
#define ADSI_JUST_LEFT
Definition: adsi.h:112
static int vm_newuser_setup(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
#define INTRO
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
static char locale[20]
static int play_message_by_id_helper(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, const char *msg_id)
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:3191
#define ast_mutex_lock(a)
Definition: lock.h:187
static int get_folder(struct ast_channel *chan, int start)
get_folder: Folder menu Plays "press 1 for INBOX messages" etc. Should possibly be internationalized ...
static int get_folder_by_name(const char *name)
#define ao2_unlock(a)
Definition: astobj2.h:730
static struct test_val c
static char aliasescontext[MAX_VM_CONTEXT_LEN]
#define ast_copy_flags(dest, src, flagz)
Definition: utils.h:84
char msg_format[512]
#define ast_str_alloca(init_len)
Definition: strings.h:800
#define MAXHOSTNAMELEN
Definition: network.h:69
static void load_aliases(struct ast_config *cfg)
static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg)
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
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, const char *msg_id)
static int unload_module(void)
static int check_mime(const char *str)
Check if the string would need encoding within the MIME standard, to avoid confusing certain mail sof...
const char * str
Definition: app_jack.c:147
void ast_mwi_state_callback_subscribed(on_mwi_state handler, void *data)
For each managed mailbox that has a subscriber call the given handler.
Definition: mwi.c:343
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
int ast_taskprocessor_alert_set_levels(struct ast_taskprocessor *tps, long low_water, long high_water)
Set the high and low alert water marks of the given taskprocessor queue.
static ast_cond_t poll_cond
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3328
static void mwi_handle_subscribe(const char *id, struct ast_mwi_subscriber *sub)
static char externnotify[160]
static int inprocess_count(const char *context, const char *mailbox, int delta)
const char * args
int ast_unlock_path(const char *path)
Unlock a path.
Definition: main/app.c:2473
struct ast_mwi_state * ast_mwi_subscriber_data(struct ast_mwi_subscriber *sub)
Retrieves the state data object associated with the MWI subscriber.
Definition: mwi.c:264
char password[80]
#define NULL
Definition: resample.c:96
static int inbuf(struct baseio *bio, FILE *fi)
utility used by inchar(), for base_encode()
static int vmminsecs
static char * emailsubject
int ast_control_streamfile(struct ast_channel *chan, const char *file, const char *fwd, const char *rev, const char *stop, const char *pause, const char *restart, int skipms, long *offsetms)
Stream a file with fast forward, pause, reverse, restart.
Definition: main/app.c:1319
Definitions to aid in the use of thread local storage.
char * end
Definition: eagi_proxy.c:73
char mailbox[0]
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1098
#define DISPOSE(a, b)
int value
Definition: syslog.c:37
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
#define CHUNKSIZE
char fn[PATH_MAX]
ADSI Support (built upon Caller*ID)
static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
char curbox[80]
void ast_category_destroy(struct ast_category *cat)
Definition: extconf.c:2847
static int input(yyscan_t yyscanner)
Definition: ast_expr2f.c:1584
int ast_mwi_add_observer(struct ast_mwi_observer *observer)
Add an observer to receive MWI state related events.
Definition: mwi.c:296
#define LOG_DEBUG
Definition: logger.h:241
static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
Check the given mailbox&#39;s message count.
int ast_channel_setoption(struct ast_channel *channel, int option, void *data, int datalen, int block)
Sets an option on a channel.
Definition: channel.c:7522
list of users found in the config file
static int vm_msg_remove(const char *mailbox, const char *context, size_t num_msgs, const char *folder, const char *msgs[])
static int priority
const char * ext
Definition: http.c:147
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define CHECK(u, attr, value)
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:614
static char cid_num[AST_MAX_EXTENSION]
Definition: chan_mgcp.c:164
#define ast_cond_signal(cond)
Definition: lock.h:201
int ast_channel_priority(const struct ast_channel *chan)
int urgentmessages
static struct ast_str * password
Definition: cdr_mysql.c:77
const ast_string_field folder
static char * playmsg_app
static struct ast_smdi_interface * smdi_iface
static char fromstring[100]
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
Definition: main/config.c:3489
static int transfer
Definition: chan_mgcp.c:194
#define ast_verb(level,...)
Definition: logger.h:463
void() ast_vm_msg_play_cb(struct ast_channel *chan, const char *playfile, int duration)
Voicemail playback callback function definition.
static const struct ast_app_option vm_app_options[128]
char context[MAX_VM_CONTEXT_LEN]
char exit[80]
static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:755
const char * line
Definition: cli.h:162
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Looks for a valid matching extension.
Definition: pbx.c:4194
#define VM_SAYCID
static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
#define ADSI_COMM_PAGE
Definition: adsi.h:107
static int get_date(char *s, int len)
Gets the current date and time, as formatted string.
def from_mailbox(key, val, section, pjsip, nmapped)
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
int dh_arraysize
#define STORE(a, b, c, d, e, f, g, h, i, j, k)
static int vm_msg_play(struct ast_channel *chan, const char *mailbox, const char *context, const char *folder, const char *msg_num, ast_vm_msg_play_cb cb)
#define MAX_LANGUAGE
Definition: channel.h:173
const ast_string_field recording_ext
#define VM_SVMAIL
Utility functions.
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:269
static void load_users(struct ast_config *cfg)
const char * astman_get_header(const struct message *m, char *var)
Get header from mananger transaction.
Definition: manager.c:2820
void() ao2_prnt_fn(void *where, const char *fmt,...)
Print output.
Definition: astobj2.h:1442
pthread_cond_t ast_cond_t
Definition: lock.h:176
#define ast_strlen_zero(foo)
Definition: strings.h:52
static const char * mbox(struct ast_vm_user *vmu, int id)
char cause[SMDI_MWI_FAIL_CAUSE_LEN+1]
Definition: smdi.h:54
int ast_update2_realtime(const char *family,...) attribute_sentinel
Update realtime configuration.
Definition: main/config.c:3525
All configuration options for statsd client.
Definition: res_statsd.c:95
off_t ast_tellstream(struct ast_filestream *fs)
Tell where we are in a stream.
Definition: file.c:1048
static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
static int make_file(char *dest, const int len, const char *dir, const int num)
Creates a file system path expression for a folder within the voicemail data folder and the appropria...
static char * emailbody
struct ast_vm_user::@82 list
AO2_STRING_FIELD_HASH_FN(alias_mailbox_mapping, alias)
int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
Set the MWI indicator for a mailbox.
Definition: res_smdi.c:309
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
void ast_channel_tech_set(struct ast_channel *chan, const struct ast_channel_tech *value)
int done
Definition: test_amihooks.c:48
#define ALIASES_OUTPUT_FORMAT
Number structure.
Definition: app_followme.c:154
static int add_message_id(struct ast_config *msg_cfg, char *dir, int msg, char *filename, char *id, size_t id_size, struct ast_vm_user *vmu, int folder)
AST_TEST_DEFINE(test_voicemail_vmuser)
#define ADSI_DIR_FROM_LEFT
Definition: adsi.h:120
Custom localtime functions for multiple timezones.
static void adsi_begin(struct ast_channel *chan, int *useadsi)
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
#define DEFAULT_LISTEN_CONTROL_PAUSE_KEY
struct ast_party_id id
Caller party ID.
Definition: channel.h:421
static char pagerfromstring[100]
char context[80]
enum AST_LOCK_RESULT ast_lock_path(const char *path)
Lock a filesystem path.
Definition: main/app.c:2457
static char * pagerbody
#define AST_LOG_NOTICE
Definition: logger.h:268
Configuration File Parser.
static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
The handler for the change password option.
void ao2_container_unregister(const char *name)
Unregister a container for CLI stats and integrity check.
static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Vietnamese syntax for &#39;You have N messages&#39; greeting.
#define VM_SEARCH
static char mailbox[AST_MAX_MAILBOX_UNIQUEID]
Definition: chan_mgcp.c:204
static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
int ast_app_inboxcount2(const char *mailboxes, int *urgentmsgs, int *newmsgs, int *oldmsgs)
Determine number of urgent/new/old messages in a mailbox.
Definition: main/app.c:692
int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
get values from config variables.
Definition: main/utils.c:2198
#define VM_ENVELOPE
static int passwordlocation
static char exitcontext[AST_MAX_CONTEXT]
int ao2_container_register(const char *name, struct ao2_container *self, ao2_prnt_obj_fn *prnt_obj)
Register a container for CLI stats and integrity check.
static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:452
static char * sayname_app
#define ast_log
Definition: astobj2.c:42
static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Italian syntax for &#39;You have N messages&#39; greeting.
int ast_adsi_set_line(unsigned char *buf, int page, int line)
Sets the current line and page.
Definition: adsi.c:285
void ast_channel_set_rawreadformat(struct ast_channel *chan, struct ast_format *format)
#define MINPASSWORD
static int inprocess_hash_fn(const void *obj, const int flags)
static int vm_msg_forward(const char *from_mailbox, const char *from_context, const char *from_folder, const char *to_mailbox, const char *to_context, const char *to_folder, size_t num_msgs, const char *msg_ids[], int delete_old)
#define SENTINEL
Definition: compiler.h:87
char txtfile[256]
#define TEST_CONTEXT
Definition: test_message.c:46
static void load_zonemessages(struct ast_config *cfg)
static struct ast_vm_msg_snapshot * vm_msg_snapshot_destroy(struct ast_vm_msg_snapshot *msg_snapshot)
char locale[20]
#define ast_config_load(filename, flags)
Load a config file.
char dialout[80]
int ast_adsi_input_format(unsigned char *buf, int num, int dir, int wrap, char *format1, char *format2)
Set input format.
Definition: adsi.c:329
static char * config_filename
Definition: extconf.c:2121
static char * complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
#define AST_LOG_ERROR
Definition: logger.h:290
static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Spanish syntax for &#39;You have N messages&#39; greeting.
int ast_say_counted_noun(struct ast_channel *chan, int num, const char *noun)
int ast_store_realtime(const char *family,...) attribute_sentinel
Create realtime configuration.
Definition: main/config.c:3570
static char host[256]
Definition: muted.c:77
static int vmsayname_exec(struct ast_channel *chan, const char *data)
int ast_build_string(char **buffer, size_t *space, const char *fmt,...)
Build a string in a buffer, designed to be called repeatedly.
Definition: main/utils.c:1919
char * ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown)
Definition: callerid.c:1073
#define MAXMSG
static char * strip_control_and_high(const char *input, char *buf, size_t buflen)
Strips control and non 7-bit clean characters from input string.
const ast_string_field call_macrocontext
static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
General Asterisk PBX channel definitions.
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
Definition: main/config.c:3382
static char dialcontext[AST_MAX_CONTEXT]
#define VM_FORCENAME
#define VM_DELETE
Asterisk file paths, configured in asterisk.conf.
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:3245
ODBC container.
Definition: res_odbc.h:46
static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static const char *const mailbox_folders[]
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:911
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define VOICEMAIL_CONFIG
static char * vmauthenticate_app
const int fd
Definition: cli.h:159
static int vm_execmain(struct ast_channel *chan, const char *data)
void ast_channel_nativeformats_set(struct ast_channel *chan, struct ast_format_cap *value)
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:353
static struct ast_flags globalflags
char zonetag[80]
static void notify_new_state(struct ast_vm_user *vmu)
int * heard
static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
Performs a change of the voicemail passowrd in the realtime engine.
#define DEFAULT_LISTEN_CONTROL_STOP_KEY
#define AST_PTHREADT_NULL
Definition: lock.h:66
const int n
Definition: cli.h:165
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Definition: channel.h:1283
struct sla_ringing_trunk * last
Definition: app_meetme.c:1092
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Chinese (Taiwan)syntax for &#39;You have N messages&#39; greeting.
static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
ast_mutex_t lock
Definition: app_meetme.c:1091
static int silencethreshold
static void * mb_poll_thread(void *data)
#define AST_MAX_EXTENSION
Definition: channel.h:135
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, const char *category, const char *flag)
int passwordlocation
Caller Party information.
Definition: channel.h:419
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:556
static char vm_reenterpassword[80]
structure to hold extensions
struct ast_format * ast_format_gsm
Built-in cached gsm format.
Definition: format_cache.c:101
static int my_umask
int ast_play_and_wait(struct ast_channel *chan, const char *fn)
Play a stream and wait for a digit, returning the digit that was pressed.
Definition: main/app.c:1470
#define ao2_ref(o, delta)
Definition: astobj2.h:464
void ast_channel_set_readformat(struct ast_channel *chan, struct ast_format *format)
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:85
int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence_ms, const char *path, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists)
Record a file based on input from a channel This function will play "auth-thankyou" upon successful r...
Definition: main/app.c:1992
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
In case you didn&#39;t read that giant block of text above the mansession_session struct, the struct mansession is named this solely to keep the API the same in Asterisk. This structure really represents data that is different from Manager action to Manager action. The mansession_session pointer contained within points to session-specific data.
Definition: manager.c:1625
#define VM_MODULE_VERSION
long int ast_random(void)
Definition: main/utils.c:2064
static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64]
static void free_zone(struct vm_zone *z)
#define ao2_lock(a)
Definition: astobj2.h:718
int old_msgs
Definition: mwi.h:462
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
Definition: time.h:238
static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
static int check_password(struct ast_vm_user *vmu, char *password)
Check that password meets minimum required length.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
static void generate_msg_id(char *dst)
Sets the destination string to a uniquely identifying msg_id string.
#define VM_MOVEHEARD
static char * addesc
const ast_string_field origtime
static int say_and_wait(struct ast_channel *chan, int num, const char *language)
static const char * vm_index_to_foldername(int id)
static void queue_mwi_event(const char *channel_id, const char *box, int urgent, int new, int old)
#define COPY(a, b, c, d, e, f, g, h)
#define ast_format_cap_append(cap, format, framing)
Definition: format_cap.h:103
static char language[MAX_LANGUAGE]
Definition: chan_alsa.c:117
int ast_adsi_display(unsigned char *buf, int page, int line, int just, int wrap, char *col1, char *col2)
Loads a line of info into the display.
Definition: adsi.c:274
int ast_strftime_locale(char *buf, size_t len, const char *format, const struct ast_tm *tm, const char *locale)
Definition: localtime.c:2452
char * ast_format_str_reduce(char *fmts)
Definition: file.c:1826
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:193
static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size, struct ast_vm_user *res_vmu, const char *context, const char *prefix, int skipuser, int max_logins, int silent)
static char zonetag[80]
static ast_mutex_t poll_lock
#define ENDL
static const char * ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
Wraps a character sequence in double quotes, escaping occurences of quotes within the string...
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)
The advanced options within a message.
#define DELETE(a, b, c, d)
#define ast_variable_new(name, value, filename)
static int write_password_to_file(const char *secretfn, const char *password)
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
static double volgain
ODBC resource manager.
static char VM_SPOOL_DIR[PATH_MAX]
int ast_say_character_str(struct ast_channel *chan, const char *num, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity)
function to pronounce character and phonetic strings
Definition: channel.c:8367
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:4179
char name[80]
static int last_message_index(struct ast_vm_user *vmu, char *dir)
Determines the highest message number in use for a given user and mailbox folder. ...
static void free_user(struct ast_vm_user *vmu)
#define ast_format_cap_alloc(flags)
Definition: format_cap.h:52
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:2906
static int vm_delete(char *file)
Removes the voicemail sound and information file.
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: extconf.c:3276
int ast_ratestream(struct ast_filestream *fs)
Return the sample rate of the stream&#39;s format.
Definition: file.c:1053
#define PWDCHANGE_INTERNAL
static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
Structure to describe a channel "technology", ie a channel driver See for examples: ...
Definition: channel.h:629
static void apply_options(struct ast_vm_user *vmu, const char *options)
Destructively Parse options and apply.
const char * ast_channel_exten(const struct ast_channel *chan)
Core PBX routines and definitions.
static char vm_mismatch[80]
#define ERROR
Definition: hash.c:85
void ast_vm_greeter_unregister(const char *module_name)
Unregister the specified voicemail greeter provider.
Definition: main/app.c:584
const ast_string_field call_callerid
static char vm_pls_try_again[80]
#define CONFIG_STATUS_FILEUNCHANGED
static struct ast_taskprocessor * mwi_subscription_tps
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:290
static char vm_invalid_password[80]
static int get_folder2(struct ast_channel *chan, char *fn, int start)
plays a prompt and waits for a keypress.
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:196
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:290
const char * ast_channel_uniqueid(const struct ast_channel *chan)
const char *const * argv
Definition: cli.h:161
static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
Sets a specific property value.
#define ast_vm_register(vm_table)
See __ast_vm_register()
static void run_externnotify(const char *context, const char *extension, const char *flag)
static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:7258
#define DEFAULT_POLL_FREQ
#define VM_PBXSKIP
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define HVSU_OUTPUT_FORMAT
int ast_adsi_transmit_message(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype)
Definition: adsi.c:98
char fromstring[100]
#define VM_OPERATOR
static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
static char callcontext[AST_MAX_CONTEXT]
static int skipms
static void adsi_password(struct ast_channel *chan)
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3452
static int poll_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
SAY_EXTERN int(* ast_say_date_with_format)(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *timezone) SAY_INIT(ast_say_date_with_format)
Definition: say.h:189
#define AST_LIST_HEAD_NOLOCK_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:345
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Definition: astobj2.h:1310
static struct ast_vm_user * find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
Finds a voicemail user from the realtime engine.
static int vm_test_destroy_user(const char *context, const char *mailbox)
SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT(*exec_cb)(struct odbc_obj *obj, void *data), void *data)
Executes an non prepared statement and returns the resulting statement handle.
Definition: res_odbc.c:369
static char ext_pass_check_cmd[128]
static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: main/utils.c:1951
static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir, signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro)
char * emailbody
AO2_STRING_FIELD_CMP_FN(alias_mailbox_mapping, alias)
static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
int ast_safe_system(const char *s)
Safely spawn an OS shell command while closing file descriptors.
Definition: extconf.c:829
void ast_mwi_remove_observer(struct ast_mwi_observer *observer)
Remove an MWI state observer.
Definition: mwi.c:302
char * usage
Definition: utils/frame.c:37
#define AST_OPTION_RXGAIN
static char * handle_voicemail_show_aliases(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail zones in the CLI.
#define RENAME(a, b, c, d, e, f, g, h)
char * mkdtemp(char *template_s)
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
#define CLI_SHOWUSAGE
Definition: cli.h:45
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)
Sends a voicemail message to a mailbox recipient.
struct ast_vm_mailbox_snapshot::@223 * snapshots
const char * module_name
The name of the module that provides the voicemail greeter functionality.
static unsigned int poll_mailboxes
static char listen_control_restart_key[12]
static struct ast_vm_mailbox_snapshot * vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot)
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
#define OPERATOR_EXIT
static const struct cfalias aliases[]
Definition: chan_sip.c:8510
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the &#39;nonstandard&#39; argument separation process for an application.
const ast_string_field context
#define EVENT_FLAG_USER
Definition: manager.h:77
def info(msg)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define VM_REVIEW
int errno
static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Top level method to invoke the language variant vm_browse_messages_XX function.
static int vm_intro_is(struct ast_channel *chan, struct vm_state *vms)
int ast_safe_fork(int stop_reaper)
Common routine to safely fork without a chance of a signal handler firing badly in the child...
Definition: main/app.c:3047
static void adsi_folders(struct ast_channel *chan, int start, char *label)
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2283
#define ADSI_KEY_SKT
Definition: adsi.h:117
static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
#define ao2_iterator_next(iter)
Definition: astobj2.h:1933
static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
Prompts the user and records a voicemail to a mailbox.
int linelength
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:411
static int ochar(struct baseio *bio, int c, FILE *so)
utility used by base_encode()
#define LOG_NOTICE
Definition: logger.h:263
char vmbox[PATH_MAX]
const ast_string_field call_callerchan
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:8793
#define VOICEMAIL_DIR_MODE
char * strcasestr(const char *, const char *)
static void adsi_goodbye(struct ast_channel *chan)
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
Seeks into stream.
Definition: file.c:1038
static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
int ast_say_counted_adjective(struct ast_channel *chan, int num, const char *adjective, const char *gender)
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
static struct ast_threadstorage buf1
long int flag
Definition: f2c.h:83
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:710
static int manager_match_mailbox(struct ast_mwi_state *mwi_state, void *data)
#define ast_channel_unlock(chan)
Definition: channel.h:2946
static struct ast_vm_user * find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
Finds a voicemail user from the users file or the realtime engine.
#define CLI_FAILURE
Definition: cli.h:46
static int vm_playmsgexec(struct ast_channel *chan, const char *data)
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1872
#define AST_MAX_CONTEXT
Definition: channel.h:136
static int mwi_handle_subscribe2(void *data)
static const char name[]
Definition: cdr_mysql.c:74
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move)
static char emaildateformat[32]
void ast_mwi_state_callback_all(on_mwi_state handler, void *data)
For each managed mailbox call the given handler.
Definition: mwi.c:333
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:559
static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Portuguese syntax for &#39;You have N messages&#39; greeting.
static void mwi_handle_unsubscribe(const char *id, struct ast_mwi_subscriber *sub)
static int separate_mailbox(char *mailbox_id, char **mailbox, char **context)
static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
int ast_adsi_end_download(struct ast_channel *chan)
Definition: adsi.c:43
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1814
#define AST_FLAGS_ALL
Definition: utils.h:196
#define ast_odbc_request_obj(a, b)
Definition: res_odbc.h:122
static char * voicemail_app
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1068
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2548
static int inchar(struct baseio *bio, FILE *fi)
utility used by base_encode()
static int load_module(void)
Load the module.
#define BASELINELEN
static char vm_passchanged[80]
SMDI support for Asterisk.
const char * word
Definition: cli.h:163
int urgent_msgs
Definition: mwi.h:466
static int msg_create_from_file(struct ast_vm_recording_data *recdata)
int ast_adsi_load_soft_key(unsigned char *buf, int key, const char *llabel, const char *slabel, char *ret, int data)
Creates "load soft key" parameters.
Definition: adsi.c:296
static int maxlogins
int ast_variable_update(struct ast_category *category, const char *variable, const char *value, const char *match, unsigned int object)
Update variable value within a config.
Definition: main/config.c:1444
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
static int base_encode(char *filename, FILE *so)
Performs a base 64 encode algorithm on the contents of a File.
static struct ast_cli_entry cli_voicemail[]
const char * ast_config_AST_SPOOL_DIR
Definition: options.c:154
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
#define ast_publish_mwi_state_channel(mailbox, context, new_msgs, old_msgs, channel_id)
Publish a MWI state update associated with some channel.
Definition: mwi.h:397
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1756
#define MAX_VM_MBOX_ID_LEN
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
#define ADSI_MSG_DOWNLOAD
Definition: adsi.h:33
An API for managing task processing threads that can be shared across modules.
int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
Plays a stream and gets DTMF data from a channel.
Definition: main/app.c:197
char fwd_st[SMDI_MAX_STATION_NUM_LEN+1]
Definition: smdi.h:53
char language[MAX_LANGUAGE]
static char listen_control_pause_key[12]
charset
Definition: chan_unistim.c:336
#define SENDMAIL
structure to hold users read from users.conf
static struct ao2_container * mailbox_alias_mappings
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
Structure used to handle boolean flags.
Definition: utils.h:199
static char listen_control_forward_key[12]
int ast_app_has_voicemail(const char *mailboxes, const char *folder)
Determine if a given mailbox has any voicemail If folder is NULL, defaults to "INBOX". If folder is "INBOX", includes the number of messages in the "Urgent" folder.
Definition: main/app.c:655
#define ast_clear_flag(p, flag)
Definition: utils.h:77
#define ERROR_MAX_MSGS
struct ast_party_redirecting * ast_channel_redirecting(struct ast_channel *chan)
Support for logging to various files, console and syslog Configuration in file logger.conf.
ast_vm_snapshot_sort_val
static char cid_name[AST_MAX_EXTENSION]
Definition: chan_mgcp.c:165
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
Options for leaving voicemail with the voicemail() application.
Definition: app_minivm.c:650
const char * usage
Definition: cli.h:177
void ast_channel_exten_set(struct ast_channel *chan, const char *value)
static void read_password_from_file(const char *secretfn, char *password, int passwordlen)
static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: extconf.c:1178
int ast_app_messagecount(const char *mailbox_id, const char *folder)
Get the number of messages in a given mailbox folder.
Definition: main/app.c:718
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms)
void ast_category_append(struct ast_config *config, struct ast_category *cat)
Appends a category to a config.
Definition: extconf.c:2835
#define RETRIEVE(a, b, c, d)
#define VALID_DTMF
struct ast_frame ast_null_frame
Definition: main/frame.c:79
static struct ast_vm_msg_snapshot * vm_msg_snapshot_alloc(void)
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
Definition: channel.c:3184
const char * module_name
The name of the module that provides the voicemail functionality.
signed char record_gain
Definition: app_minivm.c:652
SQLRETURN ast_odbc_execute_sql(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Execute a nonprepared SQL query.
Definition: res_odbc.c:478
vm_passwordlocation
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:653
#define EVENT_FLAG_REPORTING
Definition: manager.h:80
#define CLI_SUCCESS
Definition: cli.h:44
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:694
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:688
SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
Prepares, executes, and returns the resulting statement handle.
Definition: res_odbc.c:407
int ast_adsi_available(struct ast_channel *chan)
Returns non-zero if Channel does or might support ADSI.
Definition: adsi.c:263
char * strsep(char **str, const char *delims)
Structure used for ast_copy_recording_to_vm in order to cleanly supply data needed for making the rec...
char serveremail[80]
A ast_taskprocessor structure is a singleton by name.
Definition: taskprocessor.c:69
int ast_taskprocessor_push(struct ast_taskprocessor *tps, int(*task_exe)(void *datap), void *datap) attribute_warn_unused_result
Push a task into the specified taskprocessor queue and signal the taskprocessor thread.
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Definition: mod_format.h:101
FILE * out
Definition: utils/frame.c:33
char attachfmt[20]
static int vmmaxsecs
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1841
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
int ast_say_number(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options)
says a number
Definition: channel.c:8337
Standard Command Line Interface.
int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Destroy realtime configuration.
Definition: main/config.c:3606
static int has_voicemail(const char *mailbox, const char *folder)
Determines if the given folder has messages.
void ast_channel_context_set(struct ast_channel *chan, const char *value)
#define AST_MODULE
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
static void start_poll_thread(void)
static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
#define AST_LOG_DEBUG
Definition: logger.h:246
ast_app: A registered application
Definition: pbx_app.c:45
char uniqueid[80]
const char * ast_channel_name(const struct ast_channel *chan)
static struct ast_vm_user * find_or_create(const char *context, const char *box)
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
Definition: main/utils.c:1968
static int vm_lock_path(const char *path)
Lock file path only return failure if ast_lock_path returns &#39;timeout&#39;, not if the path does not exist...
const int pos
Definition: cli.h:164
MWI state event interface.
Definition: mwi.h:252
int new_msgs
Definition: mwi.h:461
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1776
static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1086
static char * handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Reload voicemail configuration from the CLI.
const ast_string_field uniqueid
Definition: mwi.h:460
#define EXISTS(a, b, c, d)
char callback[80]
Asterisk MWI API.
static void free_user_final(struct ast_vm_user *vmu)
static ENTRY retval
Definition: hsearch.c:50
#define VM_SKIPAFTERCMD
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2814
static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
struct odbc_obj * next
Definition: res_odbc.h:55
static char * pagersubject
static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
char * emailsubject
#define BASEMAXINLINE
static char * show_users_realtime(int fd, const char *context)
static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
static int append_mailbox(const char *context, const char *box, const char *data)
vm_box
static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
const char * ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
Retrieve a configuration variable within the configuration set.
Definition: main/config.c:684
struct stasis_forward * sub
Definition: res_corosync.c:240
static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
Data structure associated with a single frame of data.
#define MAXMSGLIMIT
struct ast_filestream * ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
Starts reading from a file.
Definition: file.c:1309
const char * ast_channel_language(const struct ast_channel *chan)
#define VM_ALLOCED
#define VMSTATE_MAX_MSG_ARRAY
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)
unsigned int module_version
The version of this function table.
static char vm_password[80]
static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
static int play_message_category(struct ast_channel *chan, const char *category)
static char * handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail zones in the CLI.
#define ASTERISK_USERNAME
#define VM_DIRECFORWARD
#define VOICEMAIL_FILE_MODE
int ast_filerename(const char *oldname, const char *newname, const char *fmt)
Renames a file.
Definition: file.c:1103
struct ast_mwi_observer mwi_observer
const char * ast_channel_context(const struct ast_channel *chan)
#define PWDCHANGE_EXTERNAL
static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts, char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
presents the option to prepend to an existing message when forwarding it.
static int is_valid_dtmf(const char *key)
Determines if a DTMF key entered is valid.
#define UPDATE_MSG_ID(a, b, c, d, e, f)
enum queue_result id
Definition: app_queue.c:1507
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
#define PATH_MAX
Definition: asterisk.h:40
static struct ast_vm_mailbox_snapshot * vm_mailbox_snapshot_create(const char *mailbox, const char *context, const char *folder, int descending, enum ast_vm_snapshot_sort_val sort_val, int combine_INBOX_and_OLD)
static int vm_exec(struct ast_channel *chan, const char *data)
const ast_string_field call_context
int ast_adsi_input_control(unsigned char *buf, int page, int line, int display, int format, int just)
Set input information.
Definition: adsi.c:318
char fullname[80]
#define HVSZ_OUTPUT_FORMAT
#define ast_mutex_init(pmutex)
Definition: lock.h:184
Generic container type.
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:861
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:280
static struct test_options options
int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
Unset the MWI indicator for a mailbox.
Definition: res_smdi.c:314
#define VM_FWDURGAUTO
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:116
static int vm_msg_move(const char *mailbox, const char *context, size_t num_msgs, const char *oldfolder, const char *old_msg_ids[], const char *newfolder)
int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders)
Reads multiple digits.
Definition: channel.c:6655
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
#define ast_mutex_destroy(a)
Definition: lock.h:186
static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
static char vm_login[80]
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name, const char *filter)
Retrieve a category if it exists.
Definition: main/config.c:1022
static unsigned char poll_thread_run
static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids [], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu)
common bounds checking and existence check for Voicemail API functions.
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:598
int ast_adsi_unload_session(struct ast_channel *chan)
Definition: adsi.c:87
static int vm_test_create_user(const char *context, const char *mailbox)
static int maxmsg
const char * ast_channel_macrocontext(const struct ast_channel *chan)
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:813
The structure that contains MWI state.
Definition: mwi.h:457
static struct ast_frame * fake_read(struct ast_channel *ast)
struct ast_app * pbx_findapp(const char *app)
Look up an application.
Definition: ael_main.c:165
void ast_party_caller_init(struct ast_party_caller *init)
Initialize the given caller structure.
Definition: channel.c:1978
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:186
Say numbers and dates (maybe words one day too)
static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
#define DEBUG_ATLEAST(level)
Definition: logger.h:441
static char vm_newpassword[80]
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, const char *msg_id)
Creates the email file to be sent to indicate a new voicemail exists for a user.
void ast_channel_priority_set(struct ast_channel *chan, int value)
#define RESULT_SUCCESS
Definition: cli.h:40
#define ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, assignedids, requestor, amaflag,...)
Create a channel structure.
Definition: channel.h:1259
static const struct ast_tm * vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
fill in *tm for current time according to the proper timezone, if any.
struct ast_smdi_mwi_message * ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout, const char *station)
Definition: res_smdi.c:556
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:1996
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3159
Asterisk module definitions.
Voicemail function table definition.
static void delete_file(struct phoneprov_file *file)
static int msg_id_incrementor
static char vmfmts[80]
static snd_pcm_format_t format
Definition: chan_alsa.c:102
#define ADSI_JUST_CENT
Definition: adsi.h:114
static int vmauthenticate(struct ast_channel *chan, const char *data)
char mailbox[MAX_VM_MBOX_ID_LEN]
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:368
static char serveremail[80]
#define ast_vm_greeter_register(vm_table)
See __ast_vm_greeter_register()
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
static struct alias_mailbox_mapping * alias_mailbox_mapping_create(const char *alias, const char *mailbox)
int newmessages
static int play_message_by_id(struct ast_channel *chan, const char *mailbox, const char *context, const char *msg_id)
Finds a message in a specific mailbox by msg_id and plays it to the channel.
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:298
static const char * ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
Encode a string according to the MIME rules for encoding strings that are not 7-bit clean or contain ...
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:204
#define ADSI_KEY_APPS
Definition: adsi.h:109
static int valid_config(const struct ast_config *cfg)
Check if configuration file is valid.
static char listen_control_stop_key[12]
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1508
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:518
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:187
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
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)
Structure for mutex and tracking information.
Definition: lock.h:135
An SMDI message waiting indicator message.
Definition: smdi.h:51
const ast_string_field call_extension
jack_status_t status
Definition: app_jack.c:146
static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
Resets a user password to a specified password.
#define MAX_VM_MAILBOX_LEN
#define VM_FORCEGREET
#define DEFAULT_LISTEN_CONTROL_RESTART_KEY
static unsigned int poll_freq
Media Format Cache API.
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
struct ao2_container * inprocess_container
short word
int ast_app_inboxcount(const char *mailboxes, int *newmsgs, int *oldmsgs)
Determine number of new/old messages in a mailbox.
Definition: main/app.c:677
#define ast_mutex_unlock(a)
Definition: lock.h:188
static void stop_poll_thread(void)
static char prefix[MAX_PREFIX]
Definition: http.c:141
static void populate_defaults(struct ast_vm_user *vmu)
Sets default voicemail system options to a voicemail user.
static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
The handler for &#39;record a temporary greeting&#39;.
#define AST_APP_ARG(name)
Define an application argument.
static char vm_prepend_timeout[80]
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: main/utils.c:2231
int ast_callerid_parse(char *instr, char **name, char **location)
Destructively parse inbuf into name and location (or number)
Definition: callerid.c:1008
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:514
static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
#define MAX_MAIL_BODY_CONTENT_SIZE
static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
Definition: manager.c:3201
static struct test_val a
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:343
#define ao2_link(container, obj)
Definition: astobj2.h:1549