Asterisk - The Open Source Telephony Project  21.4.1
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 <markster@digium.com>
7  *
8  * See http://www.asterisk.org for more information about
9  * the Asterisk project. Please do not directly contact
10  * any of the maintainers of this project for assistance;
11  * the project provides a web site, mailing lists and IRC
12  * channels for your use.
13  *
14  * This program is free software, distributed under the terms of
15  * the GNU General Public License Version 2. See the LICENSE file
16  * at the top of the source tree.
17  */
18 
19 /*!
20  * \file
21  * \author Mark Spencer <markster@digium.com>
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 voicemail.conf "Config_voicemail"
30  * \note For information about voicemail IMAP storage, https://docs.asterisk.org/Configuration/Applications/Voicemail/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="S">
153  <para>Skip the playback of instructions for leaving a message to the
154  calling party, but only if a greeting has been recorded by the
155  mailbox user.</para>
156  </option>
157  <option name="t">
158  <argument name="x" required="false" />
159  <para>Play a custom beep tone to the caller instead of the default one.
160  If this option is used but no file is specified, the beep is suppressed.</para>
161  </option>
162  <option name="u">
163  <para>Play the <literal>unavailable</literal> greeting.</para>
164  </option>
165  <option name="U">
166  <para>Mark message as <literal>URGENT</literal>.</para>
167  </option>
168  <option name="P">
169  <para>Mark message as <literal>PRIORITY</literal>.</para>
170  </option>
171  </optionlist>
172  </parameter>
173  </syntax>
174  <description>
175  <para>This application allows the calling party to leave a message for the specified
176  list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
177  the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
178  exist.</para>
179  <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
180  <enumlist>
181  <enum name="0">
182  <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
183  </enum>
184  <enum name="*">
185  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
186  </enum>
187  </enumlist>
188  <para>This application will set the following channel variable upon completion:</para>
189  <variablelist>
190  <variable name="VMSTATUS">
191  <para>This indicates the status of the execution of the VoiceMail application.</para>
192  <value name="SUCCESS" />
193  <value name="USEREXIT" />
194  <value name="FAILED" />
195  </variable>
196  </variablelist>
197  </description>
198  <see-also>
199  <ref type="application">VoiceMailMain</ref>
200  </see-also>
201  </application>
202  <application name="VoiceMailMain" language="en_US">
203  <synopsis>
204  Check Voicemail messages.
205  </synopsis>
206  <syntax>
207  <parameter name="mailbox" required="true" argsep="@">
208  <argument name="mailbox" />
209  <argument name="context" />
210  </parameter>
211  <parameter name="options">
212  <optionlist>
213  <option name="p">
214  <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
215  the mailbox that is entered by the caller.</para>
216  </option>
217  <option name="g">
218  <argument name="#" required="true" />
219  <para>Use the specified amount of gain when recording a voicemail message.
220  The units are whole-number decibels (dB).</para>
221  </option>
222  <option name="r">
223  <para>"Read only". Prevent user from deleting any messages.</para>
224  <para>This applies only to specific executions of VoiceMailMain, NOT the mailbox itself.</para>
225  </option>
226  <option name="s">
227  <para>Skip checking the passcode for the mailbox.</para>
228  </option>
229  <option name="a">
230  <argument name="folder" required="true" />
231  <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
232  Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
233  <enumlist>
234  <enum name="0"><para>INBOX</para></enum>
235  <enum name="1"><para>Old</para></enum>
236  <enum name="2"><para>Work</para></enum>
237  <enum name="3"><para>Family</para></enum>
238  <enum name="4"><para>Friends</para></enum>
239  <enum name="5"><para>Cust1</para></enum>
240  <enum name="6"><para>Cust2</para></enum>
241  <enum name="7"><para>Cust3</para></enum>
242  <enum name="8"><para>Cust4</para></enum>
243  <enum name="9"><para>Cust5</para></enum>
244  </enumlist>
245  </option>
246  </optionlist>
247  </parameter>
248  </syntax>
249  <description>
250  <para>This application allows the calling party to check voicemail messages. A specific
251  <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
252  may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
253  be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
254  <literal>default</literal> context will be used.</para>
255  <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
256  or Password, and the extension exists:</para>
257  <enumlist>
258  <enum name="*">
259  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
260  </enum>
261  </enumlist>
262  </description>
263  <see-also>
264  <ref type="application">VoiceMail</ref>
265  </see-also>
266  </application>
267  <application name="VMAuthenticate" language="en_US">
268  <synopsis>
269  Authenticate with Voicemail passwords.
270  </synopsis>
271  <syntax>
272  <parameter name="mailbox" required="true" argsep="@">
273  <argument name="mailbox" />
274  <argument name="context" />
275  </parameter>
276  <parameter name="options">
277  <optionlist>
278  <option name="s">
279  <para>Skip playing the initial prompts.</para>
280  </option>
281  </optionlist>
282  </parameter>
283  </syntax>
284  <description>
285  <para>This application behaves the same way as the Authenticate application, but the passwords
286  are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
287  specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
288  is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
289  mailbox.</para>
290  <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
291  or Password, and the extension exists:</para>
292  <enumlist>
293  <enum name="*">
294  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
295  </enum>
296  </enumlist>
297  </description>
298  </application>
299  <application name="VoiceMailPlayMsg" language="en_US">
300  <synopsis>
301  Play a single voice mail msg from a mailbox by msg id.
302  </synopsis>
303  <syntax>
304  <parameter name="mailbox" required="true" argsep="@">
305  <argument name="mailbox" />
306  <argument name="context" />
307  </parameter>
308  <parameter name="msg_id" required="true">
309  <para>The msg id of the msg to play back. </para>
310  </parameter>
311  </syntax>
312  <description>
313  <para>This application sets the following channel variable upon completion:</para>
314  <variablelist>
315  <variable name="VOICEMAIL_PLAYBACKSTATUS">
316  <para>The status of the playback attempt as a text string.</para>
317  <value name="SUCCESS"/>
318  <value name="FAILED"/>
319  </variable>
320  </variablelist>
321  </description>
322  </application>
323  <application name="VMSayName" language="en_US">
324  <synopsis>
325  Play the name of a voicemail user
326  </synopsis>
327  <syntax>
328  <parameter name="mailbox" required="true" argsep="@">
329  <argument name="mailbox" />
330  <argument name="context" />
331  </parameter>
332  </syntax>
333  <description>
334  <para>This application will say the recorded name of the voicemail user specified as the
335  argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
336  <para>Similar to the Background() application, playback of the recorded
337  name can be interrupted by entering an extension, which will be searched
338  for in the current context.</para>
339  </description>
340  </application>
341  <function name="VM_INFO" language="en_US">
342  <synopsis>
343  Returns the selected attribute from a mailbox.
344  </synopsis>
345  <syntax argsep=",">
346  <parameter name="mailbox" argsep="@" required="true">
347  <argument name="mailbox" required="true" />
348  <argument name="context" />
349  </parameter>
350  <parameter name="attribute" required="true">
351  <optionlist>
352  <option name="count">
353  <para>Count of messages in specified <replaceable>folder</replaceable>.
354  If <replaceable>folder</replaceable> is not specified, defaults to <literal>INBOX</literal>.</para>
355  </option>
356  <option name="email">
357  <para>E-mail address associated with the mailbox.</para>
358  </option>
359  <option name="exists">
360  <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.</para>
361  </option>
362  <option name="fullname">
363  <para>Full name associated with the mailbox.</para>
364  </option>
365  <option name="language">
366  <para>Mailbox language if overridden, otherwise the language of the channel.</para>
367  </option>
368  <option name="locale">
369  <para>Mailbox locale if overridden, otherwise global locale.</para>
370  </option>
371  <option name="pager">
372  <para>Pager e-mail address associated with the mailbox.</para>
373  </option>
374  <option name="password">
375  <para>Mailbox access password.</para>
376  </option>
377  <option name="tz">
378  <para>Mailbox timezone if overridden, otherwise global timezone</para>
379  </option>
380  </optionlist>
381  </parameter>
382  <parameter name="folder" required="false">
383  <para>If not specified, <literal>INBOX</literal> is assumed.</para>
384  </parameter>
385  </syntax>
386  <description>
387  <para>Returns the selected attribute from the specified <replaceable>mailbox</replaceable>.
388  If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
389  context. Where the <replaceable>folder</replaceable> can be specified, common folders
390  include <literal>INBOX</literal>, <literal>Old</literal>, <literal>Work</literal>,
391  <literal>Family</literal> and <literal>Friends</literal>.</para>
392  </description>
393  </function>
394  <manager name="VoicemailUsersList" language="en_US">
395  <synopsis>
396  List All Voicemail User Information.
397  </synopsis>
398  <syntax>
399  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
400  </syntax>
401  <description>
402  </description>
403  </manager>
404  <manager name="VoicemailUserStatus" language="en_US">
405  <synopsis>
406  Show the status of given voicemail user's info.
407  </synopsis>
408  <syntax>
409  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
410  <parameter name="Context" required="true">
411  <para>The context you want to check.</para>
412  </parameter>
413  <parameter name="Mailbox" required="true">
414  <para>The mailbox you want to check.</para>
415  </parameter>
416  </syntax>
417  <description>
418  <para>Retrieves the status of the given voicemail user.</para>
419  </description>
420  </manager>
421  <manager name="VoicemailRefresh" language="en_US">
422  <synopsis>
423  Tell Asterisk to poll mailboxes for a change
424  </synopsis>
425  <syntax>
426  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
427  <parameter name="Context" />
428  <parameter name="Mailbox" />
429  </syntax>
430  <description>
431  <para>Normally, MWI indicators are only sent when Asterisk itself
432  changes a mailbox. With external programs that modify the content
433  of a mailbox from outside the application, an option exists called
434  <literal>pollmailboxes</literal> that will cause voicemail to
435  continually scan all mailboxes on a system for changes. This can
436  cause a large amount of load on a system. This command allows
437  external applications to signal when a particular mailbox has
438  changed, thus permitting external applications to modify mailboxes
439  and MWI to work without introducing considerable CPU load.</para>
440  <para>If <replaceable>Context</replaceable> is not specified, all
441  mailboxes on the system will be polled for changes. If
442  <replaceable>Context</replaceable> is specified, but
443  <replaceable>Mailbox</replaceable> is omitted, then all mailboxes
444  within <replaceable>Context</replaceable> will be polled.
445  Otherwise, only a single mailbox will be polled for changes.</para>
446  </description>
447  </manager>
448  <manager name="VoicemailBoxSummary" language="en_US">
449  <synopsis>
450  Show the mailbox contents of given voicemail user.
451  </synopsis>
452  <syntax>
453  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
454  <parameter name="Context" required="true">
455  <para>The context you want to check.</para>
456  </parameter>
457  <parameter name="Mailbox" required="true">
458  <para>The mailbox you want to check.</para>
459  </parameter>
460  </syntax>
461  <description>
462  <para>Retrieves the contents of the given voicemail user's mailbox.</para>
463  </description>
464  </manager>
465  <manager name="VoicemailMove" language="en_US">
466  <synopsis>
467  Move Voicemail between mailbox folders of given user.
468  </synopsis>
469  <syntax>
470  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
471  <parameter name="Context" required="true">
472  <para>The context of the Voicemail you want to move.</para>
473  </parameter>
474  <parameter name="Mailbox" required="true">
475  <para>The mailbox of the Voicemail you want to move.</para>
476  </parameter>
477  <parameter name="Folder" required="true">
478  <para>The Folder containing the Voicemail you want to move.</para>
479  </parameter>
480  <parameter name="ID" required="true">
481  <para>The ID of the Voicemail you want to move.</para>
482  </parameter>
483  <parameter name="ToFolder" required="true">
484  <para>The Folder you want to move the Voicemail to.</para>
485  </parameter>
486  </syntax>
487  <description>
488  <para>Move a given Voicemail between Folders within a user's Mailbox.</para>
489  </description>
490  </manager>
491  <manager name="VoicemailRemove" language="en_US">
492  <synopsis>
493  Remove Voicemail from mailbox folder.
494  </synopsis>
495  <syntax>
496  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
497  <parameter name="Context" required="true">
498  <para>The context of the Voicemail you want to remove.</para>
499  </parameter>
500  <parameter name="Mailbox" required="true">
501  <para>The mailbox of the Voicemail you want to remove.</para>
502  </parameter>
503  <parameter name="Folder" required="true">
504  <para>The Folder containing the Voicemail you want to remove.</para>
505  </parameter>
506  <parameter name="ID" required="true">
507  <para>The ID of the Voicemail you want to remove.</para>
508  </parameter>
509  </syntax>
510  <description>
511  <para>Remove a given Voicemail from a user's Mailbox Folder.</para>
512  </description>
513  </manager>
514  <manager name="VoicemailForward" language="en_US">
515  <synopsis>
516  Forward Voicemail from one mailbox folder to another between given users.
517  </synopsis>
518  <syntax>
519  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
520  <parameter name="Context" required="true">
521  <para>The context of the Voicemail you want to move.</para>
522  </parameter>
523  <parameter name="Mailbox" required="true">
524  <para>The mailbox of the Voicemail you want to move.</para>
525  </parameter>
526  <parameter name="Folder" required="true">
527  <para>The Folder containing the Voicemail you want to move.</para>
528  </parameter>
529  <parameter name="ID" required="true">
530  <para>The ID of the Voicemail you want to move.</para>
531  </parameter>
532  <parameter name="ToContext" required="true">
533  <para>The context you want to move the Voicemail to.</para>
534  </parameter>
535  <parameter name="ToMailbox" required="true">
536  <para>The mailbox you want to move the Voicemail to.</para>
537  </parameter>
538  <parameter name="ToFolder" required="true">
539  <para>The Folder you want to move the Voicemail to.</para>
540  </parameter>
541  </syntax>
542  <description>
543  <para>Forward a given Voicemail from a user's Mailbox Folder to
544  another user's Mailbox Folder. Can be used to copy between
545  Folders within a mailbox by specifying the to context and user
546  as the same as the from.</para>
547  </description>
548  </manager>
549  <managerEvent language="en_US" name="VoicemailPasswordChange">
550  <managerEventInstance class="EVENT_FLAG_USER">
551  <synopsis>Raised in response to a mailbox password change.</synopsis>
552  <syntax>
553  <parameter name="Context">
554  <para>Mailbox context.</para>
555  </parameter>
556  <parameter name="Mailbox">
557  <para>Mailbox name.</para>
558  </parameter>
559  <parameter name="NewPassword">
560  <para>New password for mailbox.</para>
561  </parameter>
562  </syntax>
563  </managerEventInstance>
564  </managerEvent>
565  ***/
566 
567 #ifdef IMAP_STORAGE
568 static char imapserver[48] = "localhost";
569 static char imapport[8] = "143";
570 static char imapflags[128];
571 static char imapfolder[64] = "INBOX";
572 static char imapparentfolder[64];
573 static char greetingfolder[64] = "INBOX";
574 static char authuser[32];
575 static char authpassword[42];
576 static int imapversion = 1;
577 
578 static int expungeonhangup = 1;
579 static int imapgreetings;
580 static int imap_poll_logout;
581 static char delimiter;
582 
583 /* mail_open cannot be protected on a stream basis */
584 ast_mutex_t mail_open_lock;
585 
586 struct vm_state;
587 struct ast_vm_user;
588 
589 AST_THREADSTORAGE(ts_vmstate);
590 
591 /* Forward declarations for IMAP */
592 static int init_mailstream(struct vm_state *vms, int box);
593 static void write_file(char *filename, char *buffer, unsigned long len);
594 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
595 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
596 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
597 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
598 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
599 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
600 static void vmstate_insert(struct vm_state *vms);
601 static void vmstate_delete(struct vm_state *vms);
602 static void set_update(MAILSTREAM * stream);
603 static void init_vm_state(struct vm_state *vms);
604 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
605 static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream);
606 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
607 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
608 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);
609 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);
610 static void update_messages_by_imapuser(const char *user, unsigned long number);
611 static int vm_delete(char *file);
612 
613 static int imap_remove_file (char *dir, int msgnum);
614 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
615 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
616 static void check_quota(struct vm_state *vms, char *mailbox);
617 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
618 static void imap_logout(const char *mailbox_id);
619 
620 struct vmstate {
621  struct vm_state *vms;
622  AST_LIST_ENTRY(vmstate) list;
623 };
624 
625 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
626 
627 #endif
628 
629 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
630 
631 #define COMMAND_TIMEOUT 5000
632 /* Don't modify these here; set your umask at runtime instead */
633 #define VOICEMAIL_DIR_MODE 0777
634 #define VOICEMAIL_FILE_MODE 0666
635 #define CHUNKSIZE 65536
636 
637 #define VOICEMAIL_CONFIG "voicemail.conf"
638 #define ASTERISK_USERNAME "asterisk"
639 
640 /* Define fast-forward, pause, restart, and reverse keys
641  * while listening to a voicemail message - these are
642  * strings, not characters */
643 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
644 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
645 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
646 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
647 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
648 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
649 
650 /* Default mail command to mail voicemail. Change it with the
651  * mailcmd= command in voicemail.conf */
652 #define SENDMAIL "/usr/sbin/sendmail -t"
653 #define INTRO "vm-intro"
654 
655 #define MAX_MAIL_BODY_CONTENT_SIZE 134217728L // 128 Mbyte
656 
657 #define MAXMSG 100
658 #define MAXMSGLIMIT 9999
659 
660 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
661 
662 #ifdef IMAP_STORAGE
663 #define ENDL "\r\n"
664 #else
665 #define ENDL "\n"
666 #endif
667 
668 #define MAX_DATETIME_FORMAT 512
669 #define MAX_NUM_CID_CONTEXTS 10
670 
671 #define VM_REVIEW (1 << 0) /*!< After recording, permit the caller to review the recording before saving */
672 #define VM_OPERATOR (1 << 1) /*!< Allow 0 to be pressed to go to 'o' extension */
673 #define VM_SAYCID (1 << 2) /*!< Repeat the CallerID info during envelope playback */
674 #define VM_SVMAIL (1 << 3) /*!< Allow the user to compose a new VM from within VoicemailMain */
675 #define VM_ENVELOPE (1 << 4) /*!< Play the envelope information (who-from, time received, etc.) */
676 #define VM_SAYDURATION (1 << 5) /*!< Play the length of the message during envelope playback */
677 #define VM_SKIPAFTERCMD (1 << 6) /*!< After deletion, assume caller wants to go to the next message */
678 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
679 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
680 #define VM_PBXSKIP (1 << 9) /*!< Skip the [PBX] preamble in the Subject line of emails */
681 #define VM_DIRECTFORWARD (1 << 10) /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
682 #define VM_ATTACH (1 << 11) /*!< Attach message to voicemail notifications? */
683 #define VM_DELETE (1 << 12) /*!< Delete message after sending notification */
684 #define VM_ALLOCED (1 << 13) /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
685 #define VM_SEARCH (1 << 14) /*!< Search all contexts for a matching mailbox */
686 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
687 #define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
688 #define VM_MESSAGEWRAP (1 << 17) /*!< Wrap around from the last message to the first, and vice-versa */
689 #define VM_FWDURGAUTO (1 << 18) /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
690 #define VM_EMAIL_EXT_RECS (1 << 19) /*!< Send voicemail emails when an external recording is added to a mailbox */
691 #define VM_MARK_URGENT (1 << 20) /*!< After recording, permit the caller to mark the message as urgent */
692 #define VM_ODBC_AUDIO_ON_DISK (1 << 21) /*!< When using ODBC, leave the message and greeting recordings on disk instead of moving them into the table */
693 
694 #define ERROR_LOCK_PATH -100
695 #define ERROR_MAX_MSGS -101
696 #define OPERATOR_EXIT 300
697 
698 #define MSGFILE_LEN (7) /*!< Length of the message file name: msgXXXX */
699 
700 enum vm_box {
701  NEW_FOLDER = 0,
702  OLD_FOLDER = 1,
703  WORK_FOLDER = 2,
704  FAMILY_FOLDER = 3,
705  FRIENDS_FOLDER = 4,
706  GREETINGS_FOLDER = -1
707 };
708 
709 enum vm_option_flags {
710  OPT_SILENT = (1 << 0),
711  OPT_BUSY_GREETING = (1 << 1),
712  OPT_UNAVAIL_GREETING = (1 << 2),
713  OPT_RECORDGAIN = (1 << 3),
714  OPT_PREPEND_MAILBOX = (1 << 4),
715  OPT_AUTOPLAY = (1 << 6),
716  OPT_DTMFEXIT = (1 << 7),
717  OPT_MESSAGE_Urgent = (1 << 8),
718  OPT_MESSAGE_PRIORITY = (1 << 9),
719  OPT_EARLYM_GREETING = (1 << 10),
720  OPT_BEEP = (1 << 11),
721  OPT_SILENT_IF_GREET = (1 << 12),
722  OPT_READONLY = (1 << 13),
723 };
724 
725 enum vm_option_args {
726  OPT_ARG_RECORDGAIN = 0,
727  OPT_ARG_PLAYFOLDER = 1,
728  OPT_ARG_DTMFEXIT = 2,
729  OPT_ARG_BEEP_TONE = 3,
730  /* This *must* be the last value in this enum! */
731  OPT_ARG_ARRAY_SIZE = 4,
732 };
733 
734 enum vm_passwordlocation {
735  OPT_PWLOC_VOICEMAILCONF = 0,
736  OPT_PWLOC_SPOOLDIR = 1,
737  OPT_PWLOC_USERSCONF = 2,
738 };
739 
740 AST_APP_OPTIONS(vm_app_options, {
741  AST_APP_OPTION('s', OPT_SILENT),
742  AST_APP_OPTION('S', OPT_SILENT_IF_GREET),
743  AST_APP_OPTION('b', OPT_BUSY_GREETING),
744  AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
745  AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
746  AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
747  AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
748  AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
749  AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
750  AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY),
751  AST_APP_OPTION('e', OPT_EARLYM_GREETING),
752  AST_APP_OPTION_ARG('t', OPT_BEEP, OPT_ARG_BEEP_TONE),
753  AST_APP_OPTION('r', OPT_READONLY),
754 });
755 
756 static const char * const mailbox_folders[] = {
757 #ifdef IMAP_STORAGE
758  imapfolder,
759 #else
760  "INBOX",
761 #endif
762  "Old",
763  "Work",
764  "Family",
765  "Friends",
766  "Cust1",
767  "Cust2",
768  "Cust3",
769  "Cust4",
770  "Cust5",
771  "Deleted",
772  "Urgent",
773 };
774 
775 /*!
776  * \brief Reload voicemail.conf
777  * \param reload Whether this is a reload as opposed to module load
778  * \param force Forcefully reload the config, even it has not changed
779  * \retval 0 on success, nonzero on failure
780  */
781 static int load_config_force(int reload, int force);
782 
783 /*! \brief Forcibly reload voicemail.conf, even if it has not changed.
784  * This is necessary after running unit tests. */
785 #define force_reload_config() load_config_force(1, 1)
786 
787 static int load_config(int reload);
788 #ifdef TEST_FRAMEWORK
789 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
790 #endif
791 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
792 
793 /*! \page vmlang Voicemail Language Syntaxes Supported
794 
795  \par Syntaxes supported, not really language codes.
796  \arg \b en - English
797  \arg \b de - German
798  \arg \b es - Spanish
799  \arg \b fr - French
800  \arg \b it - Italian
801  \arg \b nl - Dutch
802  \arg \b pt - Portuguese
803  \arg \b pt_BR - Portuguese (Brazil)
804  \arg \b gr - Greek
805  \arg \b no - Norwegian
806  \arg \b se - Swedish
807  \arg \b tw - Chinese (Taiwan)
808  \arg \b ua - Ukrainian
809 
810 German requires the following additional soundfile:
811 \arg \b 1F einE (feminine)
812 
813 Spanish requires the following additional soundfile:
814 \arg \b 1M un (masculine)
815 
816 Dutch, Portuguese & Spanish require the following additional soundfiles:
817 \arg \b vm-INBOXs singular of 'new'
818 \arg \b vm-Olds singular of 'old/heard/read'
819 
820 NB these are plural:
821 \arg \b vm-INBOX nieuwe (nl)
822 \arg \b vm-Old oude (nl)
823 
824 Polish uses:
825 \arg \b vm-new-a 'new', feminine singular accusative
826 \arg \b vm-new-e 'new', feminine plural accusative
827 \arg \b vm-new-ych 'new', feminine plural genitive
828 \arg \b vm-old-a 'old', feminine singular accusative
829 \arg \b vm-old-e 'old', feminine plural accusative
830 \arg \b vm-old-ych 'old', feminine plural genitive
831 \arg \b digits/1-a 'one', not always same as 'digits/1'
832 \arg \b digits/2-ie 'two', not always same as 'digits/2'
833 
834 Swedish uses:
835 \arg \b vm-nytt singular of 'new'
836 \arg \b vm-nya plural of 'new'
837 \arg \b vm-gammalt singular of 'old'
838 \arg \b vm-gamla plural of 'old'
839 \arg \b digits/ett 'one', not always same as 'digits/1'
840 
841 Norwegian uses:
842 \arg \b vm-ny singular of 'new'
843 \arg \b vm-nye plural of 'new'
844 \arg \b vm-gammel singular of 'old'
845 \arg \b vm-gamle plural of 'old'
846 
847 Dutch also uses:
848 \arg \b nl-om 'at'?
849 
850 Spanish also uses:
851 \arg \b vm-youhaveno
852 
853 Italian requires the following additional soundfile:
854 
855 For vm_intro_it:
856 \arg \b vm-nuovo new
857 \arg \b vm-nuovi new plural
858 \arg \b vm-vecchio old
859 \arg \b vm-vecchi old plural
860 
861 Japanese requires the following additional soundfile:
862 \arg \b jp-arimasu there is
863 \arg \b jp-arimasen there is not
864 \arg \b jp-oshitekudasai please press
865 \arg \b jp-ni article ni
866 \arg \b jp-ga article ga
867 \arg \b jp-wa article wa
868 \arg \b jp-wo article wo
869 
870 Chinese (Taiwan) requires the following additional soundfile:
871 \arg \b vm-tong A class-word for call (tong1)
872 \arg \b vm-ri A class-word for day (ri4)
873 \arg \b vm-you You (ni3)
874 \arg \b vm-haveno Have no (mei2 you3)
875 \arg \b vm-have Have (you3)
876 \arg \b vm-listen To listen (yao4 ting1)
877 
878 
879 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
880 spelled among others when you have to change folder. For the above reasons, vm-INBOX
881 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
882 
883 */
884 
885 #define MAX_VM_MBOX_ID_LEN (AST_MAX_EXTENSION)
886 #define MAX_VM_CONTEXT_LEN (AST_MAX_CONTEXT)
887 /* MAX_VM_MAILBOX_LEN allows enough room for the '@' and NULL terminator */
888 #define MAX_VM_MAILBOX_LEN (MAX_VM_MBOX_ID_LEN + MAX_VM_CONTEXT_LEN)
889 
890 /*! Structure for linked list of users
891  * Use ast_vm_user_destroy() to free one of these structures. */
892 struct ast_vm_user {
893  char context[MAX_VM_CONTEXT_LEN];/*!< Voicemail context */
894  char mailbox[MAX_VM_MBOX_ID_LEN];/*!< Mailbox id, unique within vm context */
895  char password[80]; /*!< Secret pin code, numbers only */
896  char fullname[80]; /*!< Full name, for directory app */
897  char *email; /*!< E-mail address */
898  char *emailsubject; /*!< E-mail subject */
899  char *emailbody; /*!< E-mail body */
900  char pager[80]; /*!< E-mail address to pager (no attachment) */
901  char serveremail[80]; /*!< From: Mail address */
902  char fromstring[100]; /*!< From: Username */
903  char language[MAX_LANGUAGE]; /*!< Config: Language setting */
904  char zonetag[80]; /*!< Time zone */
905  char locale[20]; /*!< The locale (for presentation of date/time) */
906  char callback[80];
907  char dialout[80];
908  char uniqueid[80]; /*!< Unique integer identifier */
909  char exit[80];
910  char attachfmt[20]; /*!< Attachment format */
911  unsigned int flags; /*!< VM_ flags */
912  int saydurationm;
913  int minsecs; /*!< Minimum number of seconds per message for this mailbox */
914  int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
915  int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
916  int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
917  int passwordlocation; /*!< Storage location of the password */
918 #ifdef IMAP_STORAGE
919  char imapserver[48]; /*!< IMAP server address */
920  char imapport[8]; /*!< IMAP server port */
921  char imapflags[128]; /*!< IMAP optional flags */
922  char imapuser[80]; /*!< IMAP server login */
923  char imappassword[80]; /*!< IMAP server password if authpassword not defined */
924  char imapfolder[64]; /*!< IMAP voicemail folder */
925  char imapvmshareid[80]; /*!< Shared mailbox ID to use rather than the dialed one */
926  int imapversion; /*!< If configuration changes, use the new values */
927 #endif
928  double volgain; /*!< Volume gain for voicemails sent via email */
930 };
931 
932 /*! Voicemail time zones */
933 struct vm_zone {
934  AST_LIST_ENTRY(vm_zone) list;
935  char name[80];
936  char timezone[80];
937  char msg_format[512];
938 };
939 
940 #define VMSTATE_MAX_MSG_ARRAY 256
941 
942 /*! Voicemail mailbox state */
943 struct vm_state {
944  char curbox[80];
945  char username[80];
946  char context[80];
947  char curdir[PATH_MAX];
948  char vmbox[PATH_MAX];
949  char fn[PATH_MAX];
950  char intro[PATH_MAX];
951  int *deleted;
952  int *heard;
953  int dh_arraysize; /* used for deleted / heard allocation */
954  int curmsg;
955  int lastmsg;
956  int newmessages;
957  int oldmessages;
958  int urgentmessages;
959  int starting;
960  int repeats;
961 #ifdef IMAP_STORAGE
963  int updated; /*!< decremented on each mail check until 1 -allows delay */
964  long *msgArray;
965  unsigned msg_array_max;
966  MAILSTREAM *mailstream;
967  int vmArrayIndex;
968  char imapuser[80]; /*!< IMAP server login */
969  char imapfolder[64]; /*!< IMAP voicemail folder */
970  char imapserver[48]; /*!< IMAP server address */
971  char imapport[8]; /*!< IMAP server port */
972  char imapflags[128]; /*!< IMAP optional flags */
973  int imapversion;
974  int interactive;
975  char introfn[PATH_MAX]; /*!< Name of prepended file */
976  unsigned int quota_limit;
977  unsigned int quota_usage;
978  struct vm_state *persist_vms;
979 #endif
980 };
981 
982 #ifdef ODBC_STORAGE
983 static char odbc_database[80] = "asterisk";
984 static char odbc_table[80] = "voicemessages";
985 size_t odbc_table_len = sizeof(odbc_table);
986 #define COUNT(a, b) odbc_count_messages(a,b)
987 #define LAST_MSG_INDEX(a) odbc_last_message_index(a)
988 #define RETRIEVE(a,b,c,d) odbc_retrieve_message(a,b)
989 #define DISPOSE(a,b) odbc_remove_files(a,b)
990 #define STORE(a,b,c,d,e,f,g,h,i,j,k) odbc_store_message(a,b,c,d)
991 #define EXISTS(a,b,c,d) (odbc_message_exists(a,b))
992 #define RENAME(a,b,c,d,e,f,g,h) (odbc_rename_message(a,b,c,d,e,f))
993 #define COPY(a,b,c,d,e,f,g,h) (odbc_copy_message(a,b,c,d,e,f))
994 #define DELETE(a,b,c,d) (odbc_delete_message(a,b))
995 #define UPDATE_MSG_ID(a, b, c, d, e, f) (odbc_update_msg_id((a), (b), (c)))
996 #else
997 #ifdef IMAP_STORAGE
998 #define DISPOSE(a,b) (imap_remove_file(a,b))
999 #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))
1000 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
1001 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
1002 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
1003 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
1004 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
1005 #define UPDATE_MSG_ID(a, b, c, d, e, f) (vm_imap_update_msg_id((a), (b), (c), (d), (e), (f)))
1006 #else
1007 #define COUNT(a, b) count_messages(a,b)
1008 #define LAST_MSG_INDEX(a) last_message_index(a)
1009 #define RETRIEVE(a,b,c,d)
1010 #define DISPOSE(a,b)
1011 #define STORE(a,b,c,d,e,f,g,h,i,j,k)
1012 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
1013 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
1014 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
1015 #define DELETE(a,b,c,d) (vm_delete(c))
1016 #define UPDATE_MSG_ID(a, b, c, d, e, f)
1017 #endif
1018 #endif
1019 
1020 static char VM_SPOOL_DIR[PATH_MAX];
1021 
1022 static char ext_pass_cmd[128];
1023 static char ext_pass_check_cmd[128];
1024 
1025 static int my_umask;
1026 
1027 #define PWDCHANGE_INTERNAL (1 << 1)
1028 #define PWDCHANGE_EXTERNAL (1 << 2)
1029 static int pwdchange = PWDCHANGE_INTERNAL;
1030 
1031 #ifdef ODBC_STORAGE
1032 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
1033 #else
1034 # ifdef IMAP_STORAGE
1035 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
1036 # else
1037 # define tdesc "Comedian Mail (Voicemail System)"
1038 # endif
1039 #endif
1040 
1041 static char userscontext[AST_MAX_EXTENSION] = "default";
1042 
1043 static char *addesc = "Comedian Mail";
1044 
1045 /* Leave a message */
1046 static char *voicemail_app = "VoiceMail";
1047 
1048 /* Check mail, control, etc */
1049 static char *voicemailmain_app = "VoiceMailMain";
1050 
1051 static char *vmauthenticate_app = "VMAuthenticate";
1052 
1053 static char *playmsg_app = "VoiceMailPlayMsg";
1054 
1055 static char *sayname_app = "VMSayName";
1056 
1059 static char zonetag[80];
1060 static char locale[20];
1061 static int maxsilence;
1062 static int maxmsg = MAXMSG;
1063 static int maxdeletedmsg;
1064 static int silencethreshold = 128;
1065 static char serveremail[80] = ASTERISK_USERNAME;
1066 static char mailcmd[160] = SENDMAIL; /* Configurable mail cmd */
1067 static char externnotify[160];
1068 static struct ast_smdi_interface *smdi_iface = NULL;
1069 static char vmfmts[80] = "wav";
1070 static double volgain;
1071 static int vmminsecs;
1072 static int vmmaxsecs;
1073 static int maxgreet;
1074 static int skipms = 3000;
1075 static int maxlogins = 3;
1076 static int minpassword = MINPASSWORD;
1077 static int passwordlocation;
1078 static char aliasescontext[MAX_VM_CONTEXT_LEN];
1079 
1080 /*! Poll mailboxes for changes since there is something external to
1081  * app_voicemail that may change them. */
1082 static unsigned int poll_mailboxes;
1083 
1084 /*! By default, poll every 30 seconds */
1085 #define DEFAULT_POLL_FREQ 30
1086 /*! Polling frequency */
1087 static unsigned int poll_freq = DEFAULT_POLL_FREQ;
1088 
1089 AST_MUTEX_DEFINE_STATIC(poll_lock);
1090 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
1091 static pthread_t poll_thread = AST_PTHREADT_NULL;
1092 static unsigned char poll_thread_run;
1093 
1094 static struct ast_taskprocessor *mwi_subscription_tps;
1095 
1097  char *alias;
1098  char *mailbox;
1099  char buf[0];
1100 };
1101 
1103  char *alias;
1104  char *mailbox;
1105  char buf[0];
1106 };
1107 
1108 #define MAPPING_BUCKETS 511
1109 static struct ao2_container *alias_mailbox_mappings;
1112 
1113 static struct ao2_container *mailbox_alias_mappings;
1116 
1117 /* custom audio control prompts for voicemail playback */
1118 static char listen_control_forward_key[12];
1119 static char listen_control_reverse_key[12];
1120 static char listen_control_pause_key[12];
1121 static char listen_control_restart_key[12];
1122 static char listen_control_stop_key[12];
1123 
1124 /* custom password sounds */
1125 static char vm_login[80] = "vm-login";
1126 static char vm_newuser[80] = "vm-newuser";
1127 static char vm_password[80] = "vm-password";
1128 static char vm_newpassword[80] = "vm-newpassword";
1129 static char vm_passchanged[80] = "vm-passchanged";
1130 static char vm_reenterpassword[80] = "vm-reenterpassword";
1131 static char vm_mismatch[80] = "vm-mismatch";
1132 static char vm_invalid_password[80] = "vm-invalid-password";
1133 static char vm_pls_try_again[80] = "vm-pls-try-again";
1134 
1135 /*
1136  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
1137  * 1. create a sound along the lines of "Please try again. When done, press the pound key" which could be spliced
1138  * from existing sound clips. This would require some programming changes in the area of vm_forward options and also
1139  * app.c's __ast_play_and_record function
1140  * 2. create a sound prompt saying "Please try again. When done recording, press any key to stop and send the prepended
1141  * message." At the time of this comment, I think this would require new voice work to be commissioned.
1142  * 3. Something way different like providing instructions before a time out or a post-recording menu. This would require
1143  * more effort than either of the other two.
1144  */
1145 static char vm_prepend_timeout[80] = "vm-then-pound";
1146 
1147 static struct ast_flags globalflags = {0};
1148 
1149 static int saydurationminfo = 2;
1150 
1151 static char dialcontext[AST_MAX_CONTEXT] = "";
1152 static char callcontext[AST_MAX_CONTEXT] = "";
1153 static char exitcontext[AST_MAX_CONTEXT] = "";
1154 
1155 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
1156 
1157 
1158 static char *emailbody;
1159 static char *emailsubject;
1160 static char *pagerbody;
1161 static char *pagersubject;
1162 static char fromstring[100];
1163 static char pagerfromstring[100];
1164 static char charset[32] = "ISO-8859-1";
1165 
1166 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
1167 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
1168 static int adsiver = 1;
1169 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
1170 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
1171 
1172 /* Forward declarations - generic */
1173 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
1174 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu);
1175 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);
1176 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
1177 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
1178  char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
1179  signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro);
1180 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
1181 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
1182 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);
1183 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);
1184 static void apply_options(struct ast_vm_user *vmu, const char *options);
1185 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);
1186 static int is_valid_dtmf(const char *key);
1187 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
1188 static int write_password_to_file(const char *secretfn, const char *password);
1189 static const char *substitute_escapes(const char *value);
1190 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);
1191 static void notify_new_state(struct ast_vm_user *vmu);
1192 static int append_vmu_info_astman(struct mansession *s, struct ast_vm_user *vmu, const char* event_name, const char* actionid);
1193 static int append_vmbox_info_astman(struct mansession *s, const struct message *m, struct ast_vm_user *vmu, const char* event_name, const char* actionid);
1194 
1195 
1196 /*!
1197  * Place a message in the indicated folder
1198  *
1199  * \param vmu Voicemail user
1200  * \param vms Current voicemail state for the user
1201  * \param msg The message number to save
1202  * \param box The folder into which the message should be saved
1203  * \param[out] newmsg The new message number of the saved message
1204  * \param move Tells whether to copy or to move the message
1205  *
1206  * \note the "move" parameter is only honored for IMAP voicemail presently
1207  * \retval 0 Success
1208  * \retval other Failure
1209  */
1210 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move);
1211 
1212 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);
1213 static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot);
1214 
1215 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);
1216 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);
1217 static int vm_msg_remove(const char *mailbox, const char *context, size_t num_msgs, const char *folder, const char *msgs[]);
1218 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);
1219 
1220 #ifdef TEST_FRAMEWORK
1221 static int vm_test_destroy_user(const char *context, const char *mailbox);
1222 static int vm_test_create_user(const char *context, const char *mailbox);
1223 #endif
1224 
1225 /*!
1226  * \internal
1227  * \brief Parse the given mailbox_id into mailbox and context.
1228  * \since 12.0.0
1229  *
1230  * \param mailbox_id The mailbox\@context string to separate.
1231  * \param mailbox Where the mailbox part will start.
1232  * \param context Where the context part will start. ("default" if not present)
1233  *
1234  * \retval 0 on success.
1235  * \retval -1 on error.
1236  */
1237 static int separate_mailbox(char *mailbox_id, char **mailbox, char **context)
1238 {
1239  if (ast_strlen_zero(mailbox_id) || !mailbox || !context) {
1240  return -1;
1241  }
1242  *context = mailbox_id;
1243  *mailbox = strsep(context, "@");
1244  if (ast_strlen_zero(*mailbox)) {
1245  return -1;
1246  }
1247  if (ast_strlen_zero(*context)) {
1248  *context = "default";
1249  }
1250  return 0;
1251 }
1252 
1253 struct ao2_container *inprocess_container;
1254 
1255 struct inprocess {
1256  int count;
1257  char *context;
1258  char mailbox[0];
1259 };
1260 
1261 static int inprocess_hash_fn(const void *obj, const int flags)
1262 {
1263  const struct inprocess *i = obj;
1264  return atoi(i->mailbox);
1265 }
1266 
1267 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
1268 {
1269  struct inprocess *i = obj, *j = arg;
1270  if (strcmp(i->mailbox, j->mailbox)) {
1271  return 0;
1272  }
1273  return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
1274 }
1275 
1276 static int inprocess_count(const char *context, const char *mailbox, int delta)
1277 {
1278  int context_len = strlen(context) + 1;
1279  int mailbox_len = strlen(mailbox) + 1;
1280  struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + context_len + mailbox_len);
1281  arg->context = arg->mailbox + mailbox_len;
1282  ast_copy_string(arg->mailbox, mailbox, mailbox_len); /* SAFE */
1283  ast_copy_string(arg->context, context, context_len); /* SAFE */
1284  ao2_lock(inprocess_container);
1285  if ((i = ao2_find(inprocess_container, arg, 0))) {
1286  int ret = ast_atomic_fetchadd_int(&i->count, delta);
1287  ao2_unlock(inprocess_container);
1288  ao2_ref(i, -1);
1289  return ret;
1290  }
1291  if (delta < 0) {
1292  ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
1293  }
1294  if (!(i = ao2_alloc(sizeof(*i) + context_len + mailbox_len, NULL))) {
1295  ao2_unlock(inprocess_container);
1296  return 0;
1297  }
1298  i->context = i->mailbox + mailbox_len;
1299  ast_copy_string(i->mailbox, mailbox, mailbox_len); /* SAFE */
1300  ast_copy_string(i->context, context, context_len); /* SAFE */
1301  i->count = delta;
1302  ao2_link(inprocess_container, i);
1303  ao2_unlock(inprocess_container);
1304  ao2_ref(i, -1);
1305  return 0;
1306 }
1307 
1308 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
1309 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
1310 #endif
1311 
1312 /*!
1313  * \brief Strips control and non 7-bit clean characters from input string.
1314  *
1315  * \note To map control and none 7-bit characters to a 7-bit clean characters
1316  * please use ast_str_encode_mine().
1317  */
1318 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
1319 {
1320  char *bufptr = buf;
1321  for (; *input; input++) {
1322  if (*input < 32) {
1323  continue;
1324  }
1325  *bufptr++ = *input;
1326  if (bufptr == buf + buflen - 1) {
1327  break;
1328  }
1329  }
1330  *bufptr = '\0';
1331  return buf;
1332 }
1333 
1334 /*
1335  * These macros are currently only used by the ODBC code but
1336  * should be used anyplace we're allocating a PATH_MAX
1337  * sized buffer for something which will be much smaller
1338  * in practice.
1339  */
1340 #ifdef ODBC_STORAGE
1341 /*!
1342  * \internal
1343  * \brief Get the length needed for a message path base.
1344  *
1345  * \param dir The absolute path to a directory
1346  *
1347  * \note The directory provided should include all components
1348  * down to the folder level. For example:
1349  * /var/spool/asterisk/voicemail/default/1234/INBOX
1350  *
1351  * \returns Number of bytes needed for the message path base.
1352  */
1353 static size_t get_msg_path_len(const char *dir)
1354 {
1355  if (ast_strlen_zero(dir)) {
1356  return 0;
1357  }
1358  /* dir + '/' + MSGFILE_LEN + '\0' */
1359  return strlen(dir) + 1 + MSGFILE_LEN + 1;
1360 }
1361 
1362 /*
1363  * The longest sound file extension we currently
1364  * have is "siren14" which is 7 characters long
1365  * but we'll make this 12 to be extra safe.
1366  */
1367 #define MAX_SOUND_EXTEN_LEN 12
1368 /*!
1369  * \internal
1370  * \brief Get the length needed for a message sound or metadata file path.
1371  *
1372  * \param dir The absolute path to a directory
1373  *
1374  * \note The directory provided should include all components
1375  * down to the folder level. For example:
1376  * /var/spool/asterisk/voicemail/default/1234/INBOX
1377  *
1378  * \returns Number of bytes needed for the message sound or metadata file path.
1379  */
1380 static size_t get_msg_path_ext_len(const char *dir)
1381 {
1382  if (ast_strlen_zero(dir)) {
1383  return 0;
1384  }
1385  /* dir + '/' + MSGFILE_LEN + extlen(including '.') + '\0' */
1386  return strlen(dir) + 1 + MSGFILE_LEN + MAX_SOUND_EXTEN_LEN + 1;
1387 }
1388 
1389 /*!
1390  * \internal
1391  * \brief Allocate a buffer on the stack containing a message file path base.
1392  *
1393  * \param dir The absolute path to a directory
1394  * \param msgnum The message number or -1 for a prompt file
1395  *
1396  * \note The returned buffer will automatically be freed when it
1397  * goes out of scope.
1398  *
1399  * If passed (/var/spool/asterisk/voicemail/default/1234/INBOX, 0)
1400  * the returned path would be:
1401  * /var/spool/asterisk/voicemail/default/1234/INBOX/msg0000
1402  *
1403  * If passed (/var/spool/asterisk/voicemail/default/1234/unavail, -1)
1404  * the returned path would be:
1405  * /var/spool/asterisk/voicemail/default/1234/unavail
1406  * (no change)
1407  *
1408  * \returns A pointer to the buffer containing the path.
1409  */
1410 #define MAKE_FILE_PTRA(dir, msgnum) \
1411 ({ \
1412  size_t __len = get_msg_path_len(dir); \
1413  char *__var; \
1414  if (msgnum < 0) { \
1415  __var = ast_strdupa(dir); \
1416  } else { \
1417  __var = ast_alloca(__len); \
1418  snprintf(__var, __len, "%s/msg%04d", dir, msgnum); \
1419  } \
1420  __var; \
1421 })
1422 
1423 /*!
1424  * \internal
1425  * \brief Allocate a buffer on the stack for a message sound or metadata file path.
1426  *
1427  * \param dir The absolute path to a directory
1428  * \param msgnum The message number or -1 for a prompt file
1429  *
1430  * \note The returned buffer will automatically be freed when it
1431  * goes out of scope.
1432  *
1433  * If passed (/var/spool/asterisk/voicemail/default/1234/INBOX, 0, "txt")
1434  * the returned path would be:
1435  * /var/spool/asterisk/voicemail/default/1234/INBOX/msg0000.txt
1436  *
1437  * If passed (/var/spool/asterisk/voicemail/default/1234/unavail, -1, "WAV")
1438  * the returned path would be:
1439  * /var/spool/asterisk/voicemail/default/1234/unavail.WAV
1440  *
1441  * The buffer returned has enough space that you can safely re-use it
1442  * for other sound file extensions. For instance:
1443  *
1444  * char *fn = MAKE_FILE_PTRA(dir, msgnum);
1445  * char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
1446  * char *fmt = "g722";
1447  * ...do domething with full_fn then re-use it...
1448  * sprintf(full_fn, "%s.%s", fn, fmt); * Safe since the same dir and msgnum were used *
1449  *
1450  * \returns A pointer to the allocated buffer
1451  */
1452 #define MAKE_FILE_EXT_PTRA(dir, msgnum, ext) \
1453 ({ \
1454  size_t __len = get_msg_path_ext_len(dir); \
1455  char *__var = ast_alloca(__len); \
1456  if (msgnum < 0) { \
1457  snprintf(__var, __len, "%s.%s", dir, ext); \
1458  } else { \
1459  snprintf(__var, __len, "%s/msg%04d.%s", dir, msgnum, ext); \
1460  } \
1461  __var; \
1462 })
1463 #endif
1464 
1465 /*!
1466  * \brief Sets default voicemail system options to a voicemail user.
1467  *
1468  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
1469  * - all the globalflags
1470  * - the saydurationminfo
1471  * - the callcontext
1472  * - the dialcontext
1473  * - the exitcontext
1474  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
1475  * - volume gain.
1476  * - emailsubject, emailbody set to NULL
1477  */
1478 static void populate_defaults(struct ast_vm_user *vmu)
1479 {
1480  ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
1481  vmu->passwordlocation = passwordlocation;
1482  if (saydurationminfo) {
1483  vmu->saydurationm = saydurationminfo;
1484  }
1485  ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
1486  ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
1487  ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
1488  ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
1489  ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
1490  if (vmminsecs) {
1491  vmu->minsecs = vmminsecs;
1492  }
1493  if (vmmaxsecs) {
1494  vmu->maxsecs = vmmaxsecs;
1495  }
1496  if (maxmsg) {
1497  vmu->maxmsg = maxmsg;
1498  }
1499  if (maxdeletedmsg) {
1500  vmu->maxdeletedmsg = maxdeletedmsg;
1501  }
1502  vmu->volgain = volgain;
1503  ast_free(vmu->email);
1504  vmu->email = NULL;
1505  ast_free(vmu->emailsubject);
1506  vmu->emailsubject = NULL;
1507  ast_free(vmu->emailbody);
1508  vmu->emailbody = NULL;
1509 #ifdef IMAP_STORAGE
1510  ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
1511  ast_copy_string(vmu->imapserver, imapserver, sizeof(vmu->imapserver));
1512  ast_copy_string(vmu->imapport, imapport, sizeof(vmu->imapport));
1513  ast_copy_string(vmu->imapflags, imapflags, sizeof(vmu->imapflags));
1514 #endif
1515 }
1516 
1517 /*!
1518  * \brief Sets a specific property value.
1519  * \param vmu The voicemail user object to work with.
1520  * \param var The name of the property to be set.
1521  * \param value The value to be set to the property.
1522  *
1523  * The property name must be one of the understood properties. See the source for details.
1524  */
1525 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
1526 {
1527  int x;
1528  if (!strcasecmp(var, "attach")) {
1529  ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
1530  } else if (!strcasecmp(var, "attachfmt")) {
1531  ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
1532  } else if (!strcasecmp(var, "attachextrecs")) {
1533  ast_set2_flag(vmu, ast_true(value), VM_EMAIL_EXT_RECS);
1534  } else if (!strcasecmp(var, "serveremail")) {
1535  ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
1536  } else if (!strcasecmp(var, "fromstring")) {
1537  ast_copy_string(vmu->fromstring, value, sizeof(vmu->fromstring));
1538  } else if (!strcasecmp(var, "emailbody")) {
1539  ast_free(vmu->emailbody);
1540  vmu->emailbody = ast_strdup(substitute_escapes(value));
1541  } else if (!strcasecmp(var, "emailsubject")) {
1542  ast_free(vmu->emailsubject);
1543  vmu->emailsubject = ast_strdup(substitute_escapes(value));
1544  } else if (!strcasecmp(var, "language")) {
1545  ast_copy_string(vmu->language, value, sizeof(vmu->language));
1546  } else if (!strcasecmp(var, "tz")) {
1547  ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
1548  } else if (!strcasecmp(var, "locale")) {
1549  ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
1550 #ifdef IMAP_STORAGE
1551  } else if (!strcasecmp(var, "imapuser")) {
1552  ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
1553  vmu->imapversion = imapversion;
1554  } else if (!strcasecmp(var, "imapserver")) {
1555  ast_copy_string(vmu->imapserver, value, sizeof(vmu->imapserver));
1556  vmu->imapversion = imapversion;
1557  } else if (!strcasecmp(var, "imapport")) {
1558  ast_copy_string(vmu->imapport, value, sizeof(vmu->imapport));
1559  vmu->imapversion = imapversion;
1560  } else if (!strcasecmp(var, "imapflags")) {
1561  ast_copy_string(vmu->imapflags, value, sizeof(vmu->imapflags));
1562  vmu->imapversion = imapversion;
1563  } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
1564  ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
1565  vmu->imapversion = imapversion;
1566  } else if (!strcasecmp(var, "imapfolder")) {
1567  ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
1568  vmu->imapversion = imapversion;
1569  } else if (!strcasecmp(var, "imapvmshareid")) {
1570  ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
1571  vmu->imapversion = imapversion;
1572 #endif
1573  } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
1574  ast_set2_flag(vmu, ast_true(value), VM_DELETE);
1575  } else if (!strcasecmp(var, "saycid")){
1576  ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
1577  } else if (!strcasecmp(var, "sendvoicemail")){
1578  ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
1579  } else if (!strcasecmp(var, "review")){
1580  ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
1581  } else if (!strcasecmp(var, "leaveurgent")){
1582  ast_set2_flag(vmu, ast_true(value), VM_MARK_URGENT);
1583  } else if (!strcasecmp(var, "tempgreetwarn")){
1584  ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
1585  } else if (!strcasecmp(var, "messagewrap")){
1586  ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);
1587  } else if (!strcasecmp(var, "operator")) {
1588  ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
1589  } else if (!strcasecmp(var, "envelope")){
1590  ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
1591  } else if (!strcasecmp(var, "moveheard")){
1592  ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
1593  } else if (!strcasecmp(var, "sayduration")){
1594  ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
1595  } else if (!strcasecmp(var, "saydurationm")){
1596  if (sscanf(value, "%30d", &x) == 1) {
1597  vmu->saydurationm = x;
1598  } else {
1599  ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
1600  }
1601  } else if (!strcasecmp(var, "forcename")){
1602  ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
1603  } else if (!strcasecmp(var, "forcegreetings")){
1604  ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
1605  } else if (!strcasecmp(var, "callback")) {
1606  ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
1607  } else if (!strcasecmp(var, "dialout")) {
1608  ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
1609  } else if (!strcasecmp(var, "exitcontext")) {
1610  ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
1611  } else if (!strcasecmp(var, "minsecs")) {
1612  if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
1613  vmu->minsecs = x;
1614  } else {
1615  ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
1616  vmu->minsecs = vmminsecs;
1617  }
1618  } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
1619  vmu->maxsecs = atoi(value);
1620  if (vmu->maxsecs <= 0) {
1621  ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
1622  vmu->maxsecs = vmmaxsecs;
1623  } else {
1624  vmu->maxsecs = atoi(value);
1625  }
1626  if (!strcasecmp(var, "maxmessage"))
1627  ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
1628  } else if (!strcasecmp(var, "maxmsg")) {
1629  vmu->maxmsg = atoi(value);
1630  /* Accept maxmsg=0 (Greetings only voicemail) */
1631  if (vmu->maxmsg < 0) {
1632  ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
1633  vmu->maxmsg = MAXMSG;
1634  } else if (vmu->maxmsg > MAXMSGLIMIT) {
1635  ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
1636  vmu->maxmsg = MAXMSGLIMIT;
1637  }
1638  } else if (!strcasecmp(var, "nextaftercmd")) {
1639  ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
1640  } else if (!strcasecmp(var, "backupdeleted")) {
1641  if (sscanf(value, "%30d", &x) == 1)
1642  vmu->maxdeletedmsg = x;
1643  else if (ast_true(value))
1644  vmu->maxdeletedmsg = MAXMSG;
1645  else
1646  vmu->maxdeletedmsg = 0;
1647 
1648  if (vmu->maxdeletedmsg < 0) {
1649  ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
1650  vmu->maxdeletedmsg = MAXMSG;
1651  } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
1652  ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
1653  vmu->maxdeletedmsg = MAXMSGLIMIT;
1654  }
1655  } else if (!strcasecmp(var, "volgain")) {
1656  sscanf(value, "%30lf", &vmu->volgain);
1657  } else if (!strcasecmp(var, "passwordlocation")) {
1658  if (!strcasecmp(value, "spooldir")) {
1659  vmu->passwordlocation = OPT_PWLOC_SPOOLDIR;
1660  } else {
1661  vmu->passwordlocation = OPT_PWLOC_VOICEMAILCONF;
1662  }
1663  } else if (!strcasecmp(var, "options")) {
1664  apply_options(vmu, value);
1665  }
1666 }
1667 
1668 static char *vm_check_password_shell(char *command, char *buf, size_t len)
1669 {
1670  int fds[2], pid = 0;
1671 
1672  memset(buf, 0, len);
1673 
1674  if (pipe(fds)) {
1675  snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
1676  } else {
1677  /* good to go*/
1678  pid = ast_safe_fork(0);
1679 
1680  if (pid < 0) {
1681  /* ok maybe not */
1682  close(fds[0]);
1683  close(fds[1]);
1684  snprintf(buf, len, "FAILURE: Fork failed");
1685  } else if (pid) {
1686  /* parent */
1687  close(fds[1]);
1688  if (read(fds[0], buf, len) < 0) {
1689  ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
1690  }
1691  close(fds[0]);
1692  } else {
1693  /* child */
1695  AST_APP_ARG(v)[20];
1696  );
1697  char *mycmd = ast_strdupa(command);
1698 
1699  close(fds[0]);
1700  dup2(fds[1], STDOUT_FILENO);
1701  close(fds[1]);
1702  ast_close_fds_above_n(STDOUT_FILENO);
1703 
1704  AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
1705 
1706  execv(arg.v[0], arg.v);
1707  printf("FAILURE: %s", strerror(errno));
1708  _exit(0);
1709  }
1710  }
1711  return buf;
1712 }
1713 
1714 /*!
1715  * \brief Check that password meets minimum required length
1716  * \param vmu The voicemail user to change the password for.
1717  * \param password The password string to check
1718  *
1719  * \return zero on ok, 1 on not ok.
1720  */
1721 static int check_password(struct ast_vm_user *vmu, char *password)
1722 {
1723  /* check minimum length */
1724  if (strlen(password) < minpassword)
1725  return 1;
1726  /* check that password does not contain '*' character */
1727  if (!ast_strlen_zero(password) && password[0] == '*')
1728  return 1;
1729  if (!ast_strlen_zero(ext_pass_check_cmd)) {
1730  char cmd[255], buf[255];
1731 
1732  ast_debug(1, "Verify password policies for %s\n", password);
1733 
1734  snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
1735  if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
1736  ast_debug(5, "Result: %s\n", buf);
1737  if (!strncasecmp(buf, "VALID", 5)) {
1738  ast_debug(3, "Passed password check: '%s'\n", buf);
1739  return 0;
1740  } else if (!strncasecmp(buf, "FAILURE", 7)) {
1741  ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
1742  return 0;
1743  } else {
1744  ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
1745  return 1;
1746  }
1747  }
1748  }
1749  return 0;
1750 }
1751 
1752 /*!
1753  * \brief Performs a change of the voicemail passowrd in the realtime engine.
1754  * \param vmu The voicemail user to change the password for.
1755  * \param password The new value to be set to the password for this user.
1756  *
1757  * This only works if there is a realtime engine configured.
1758  * This is called from the (top level) vm_change_password.
1759  *
1760  * \return zero on success, -1 on error.
1761  */
1762 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
1763 {
1764  int res = -1;
1765  if (!strcmp(vmu->password, password)) {
1766  /* No change (but an update would return 0 rows updated, so we opt out here) */
1767  return 0;
1768  }
1769 
1770  if (strlen(password) > 10) {
1771  ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
1772  }
1773  if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
1774  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
1775  ast_copy_string(vmu->password, password, sizeof(vmu->password));
1776  res = 0;
1777  }
1778  return res;
1779 }
1780 
1781 /*!
1782  * \brief Destructively Parse options and apply.
1783  */
1784 static void apply_options(struct ast_vm_user *vmu, const char *options)
1785 {
1786  char *stringp;
1787  char *s;
1788  char *var, *value;
1789  stringp = ast_strdupa(options);
1790  while ((s = strsep(&stringp, "|"))) {
1791  value = s;
1792  if ((var = strsep(&value, "=")) && value) {
1793  apply_option(vmu, var, value);
1794  }
1795  }
1796 }
1797 
1798 /*!
1799  * \brief Loads the options specific to a voicemail user.
1800  *
1801  * This is called when a vm_user structure is being set up, such as from load_options.
1802  */
1803 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
1804 {
1805  for (; var; var = var->next) {
1806  if (!strcasecmp(var->name, "vmsecret")) {
1807  ast_copy_string(retval->password, var->value, sizeof(retval->password));
1808  } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
1809  if (ast_strlen_zero(retval->password)) {
1810  if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
1811  ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
1812  "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
1813  } else {
1814  ast_copy_string(retval->password, var->value, sizeof(retval->password));
1815  }
1816  }
1817  } else if (!strcasecmp(var->name, "uniqueid")) {
1818  ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
1819  } else if (!strcasecmp(var->name, "pager")) {
1820  ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
1821  } else if (!strcasecmp(var->name, "email")) {
1822  ast_free(retval->email);
1823  retval->email = ast_strdup(var->value);
1824  } else if (!strcasecmp(var->name, "fullname")) {
1825  ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
1826  } else if (!strcasecmp(var->name, "context")) {
1827  ast_copy_string(retval->context, var->value, sizeof(retval->context));
1828  } else if (!strcasecmp(var->name, "emailsubject")) {
1829  ast_free(retval->emailsubject);
1830  retval->emailsubject = ast_strdup(substitute_escapes(var->value));
1831  } else if (!strcasecmp(var->name, "emailbody")) {
1832  ast_free(retval->emailbody);
1833  retval->emailbody = ast_strdup(substitute_escapes(var->value));
1834 #ifdef IMAP_STORAGE
1835  } else if (!strcasecmp(var->name, "imapuser")) {
1836  ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
1837  retval->imapversion = imapversion;
1838  } else if (!strcasecmp(var->name, "imapserver")) {
1839  ast_copy_string(retval->imapserver, var->value, sizeof(retval->imapserver));
1840  retval->imapversion = imapversion;
1841  } else if (!strcasecmp(var->name, "imapport")) {
1842  ast_copy_string(retval->imapport, var->value, sizeof(retval->imapport));
1843  retval->imapversion = imapversion;
1844  } else if (!strcasecmp(var->name, "imapflags")) {
1845  ast_copy_string(retval->imapflags, var->value, sizeof(retval->imapflags));
1846  retval->imapversion = imapversion;
1847  } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
1848  ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
1849  retval->imapversion = imapversion;
1850  } else if (!strcasecmp(var->name, "imapfolder")) {
1851  ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
1852  retval->imapversion = imapversion;
1853  } else if (!strcasecmp(var->name, "imapvmshareid")) {
1854  ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
1855  retval->imapversion = imapversion;
1856 #endif
1857  } else
1858  apply_option(retval, var->name, var->value);
1859  }
1860 }
1861 
1862 /*!
1863  * \brief Determines if a DTMF key entered is valid.
1864  * \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.
1865  *
1866  * Tests the character entered against the set of valid DTMF characters.
1867  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1868  */
1869 static int is_valid_dtmf(const char *key)
1870 {
1871  int i;
1872  char *local_key = ast_strdupa(key);
1873 
1874  for (i = 0; i < strlen(key); ++i) {
1875  if (!strchr(VALID_DTMF, *local_key)) {
1876  ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1877  return 0;
1878  }
1879  local_key++;
1880  }
1881  return 1;
1882 }
1883 
1884 /*!
1885  * \brief Finds a voicemail user from the realtime engine.
1886  * \param ivm
1887  * \param context
1888  * \param mailbox
1889  *
1890  * 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.
1891  *
1892  * \return The ast_vm_user structure for the user that was found.
1893  */
1894 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1895 {
1896  struct ast_variable *var;
1897  struct ast_vm_user *retval;
1898 
1899  if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1900  if (ivm) {
1901  memset(retval, 0, sizeof(*retval));
1902  }
1903  populate_defaults(retval);
1904  if (!ivm) {
1905  ast_set_flag(retval, VM_ALLOCED);
1906  }
1907  if (mailbox) {
1908  ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1909  }
1910  if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
1911  var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1912  } else {
1913  var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1914  }
1915  if (var) {
1916  apply_options_full(retval, var);
1917  ast_variables_destroy(var);
1918  } else {
1919  if (!ivm)
1920  ast_free(retval);
1921  retval = NULL;
1922  }
1923  }
1924  return retval;
1925 }
1926 
1927 /*!
1928  * \brief Finds a voicemail user from the users file or the realtime engine.
1929  * \param ivm
1930  * \param context
1931  * \param mailbox
1932  *
1933  * \return The ast_vm_user structure for the user that was found.
1934  */
1935 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1936 {
1937  /* This function could be made to generate one from a database, too */
1938  struct ast_vm_user *vmu = NULL, *cur;
1939  AST_LIST_LOCK(&users);
1940 
1941  if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
1942  context = "default";
1943 
1944  AST_LIST_TRAVERSE(&users, cur, list) {
1945 #ifdef IMAP_STORAGE
1946  if (cur->imapversion != imapversion) {
1947  continue;
1948  }
1949 #endif
1950  if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1951  break;
1952  if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1953  break;
1954  }
1955  if (cur) {
1956  /* Make a copy, so that on a reload, we have no race */
1957  if ((vmu = (ivm ? ivm : ast_calloc(1, sizeof(*vmu))))) {
1958  ast_free(vmu->email);
1959  ast_free(vmu->emailbody);
1960  ast_free(vmu->emailsubject);
1961  *vmu = *cur;
1962  vmu->email = ast_strdup(cur->email);
1963  vmu->emailbody = ast_strdup(cur->emailbody);
1964  vmu->emailsubject = ast_strdup(cur->emailsubject);
1965  ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1966  AST_LIST_NEXT(vmu, list) = NULL;
1967  }
1968  }
1970  if (!vmu) {
1971  vmu = find_user_realtime(ivm, context, mailbox);
1972  }
1973  if (!vmu && !ast_strlen_zero(aliasescontext)) {
1974  struct alias_mailbox_mapping *mapping;
1975  char *search_string = ast_alloca(MAX_VM_MAILBOX_LEN);
1976 
1977  snprintf(search_string, MAX_VM_MAILBOX_LEN, "%s%s%s",
1978  mailbox,
1979  ast_strlen_zero(context) ? "" : "@",
1980  S_OR(context, ""));
1981 
1982  mapping = ao2_find(alias_mailbox_mappings, search_string, OBJ_SEARCH_KEY);
1983  if (mapping) {
1984  char *search_mailbox = NULL;
1985  char *search_context = NULL;
1986 
1987  separate_mailbox(ast_strdupa(mapping->mailbox), &search_mailbox, &search_context);
1988  ao2_ref(mapping, -1);
1989  vmu = find_user(ivm, search_mailbox, search_context);
1990  }
1991  }
1992 
1993  return vmu;
1994 }
1995 
1996 /*!
1997  * \brief Resets a user password to a specified password.
1998  * \param context
1999  * \param mailbox
2000  * \param newpass
2001  *
2002  * This does the actual change password work, called by the vm_change_password() function.
2003  *
2004  * \return zero on success, -1 on error.
2005  */
2006 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
2007 {
2008  /* This function could be made to generate one from a database, too */
2009  struct ast_vm_user *cur;
2010  int res = -1;
2011  AST_LIST_LOCK(&users);
2012  AST_LIST_TRAVERSE(&users, cur, list) {
2013  if ((!context || !strcasecmp(context, cur->context)) &&
2014  (!strcasecmp(mailbox, cur->mailbox)))
2015  break;
2016  }
2017  if (cur) {
2018  ast_copy_string(cur->password, newpass, sizeof(cur->password));
2019  res = 0;
2020  }
2022  if (!res) {
2023  struct ast_json *json_object;
2024 
2025  json_object = ast_json_pack("{s: s, s: s, s: s}",
2026  "Context", S_OR(context, "default"),
2027  "Mailbox", mailbox,
2028  "NewPassword", newpass);
2029  ast_manager_publish_event("VoicemailPasswordChange", EVENT_FLAG_SYSTEM | EVENT_FLAG_USER, json_object);
2030  ast_json_unref(json_object);
2031  }
2032  return res;
2033 }
2034 
2035 /*!
2036  * \brief Check if configuration file is valid
2037  */
2038 static inline int valid_config(const struct ast_config *cfg)
2039 {
2040  return cfg && cfg != CONFIG_STATUS_FILEINVALID;
2041 }
2042 
2043 /*!
2044  * \brief The handler for the change password option.
2045  * \param vmu The voicemail user to work with.
2046  * \param newpassword The new password (that has been gathered from the appropriate prompting).
2047  * 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.
2048  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
2049  */
2050 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
2051 {
2052  struct ast_config *cfg = NULL;
2053  struct ast_variable *var = NULL;
2054  struct ast_category *cat = NULL;
2055  char *category = NULL;
2056  const char *tmp = NULL;
2057  struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
2058  char secretfn[PATH_MAX] = "";
2059  int found = 0;
2060 
2061  if (!change_password_realtime(vmu, newpassword))
2062  return;
2063 
2064  /* check if we should store the secret in the spool directory next to the messages */
2065  switch (vmu->passwordlocation) {
2066  case OPT_PWLOC_SPOOLDIR:
2067  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
2068  if (write_password_to_file(secretfn, newpassword) == 0) {
2069  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
2070  ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
2071  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2072  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2073  break;
2074  } else {
2075  ast_log(LOG_WARNING, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
2076  }
2077  /* Fall-through */
2078  case OPT_PWLOC_VOICEMAILCONF:
2079  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
2080  while ((category = ast_category_browse(cfg, category))) {
2081  if (!strcasecmp(category, vmu->context)) {
2082  char *value = NULL;
2083  char *new = NULL;
2084  if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
2085  ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
2086  break;
2087  }
2088  value = strstr(tmp, ",");
2089  if (!value) {
2090  new = ast_malloc(strlen(newpassword) + 1);
2091  sprintf(new, "%s", newpassword);
2092  } else {
2093  new = ast_malloc((strlen(value) + strlen(newpassword) + 1));
2094  sprintf(new, "%s%s", newpassword, value);
2095  }
2096  if (!(cat = ast_category_get(cfg, category, NULL))) {
2097  ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
2098  ast_free(new);
2099  break;
2100  }
2101  ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
2102  found = 1;
2103  ast_free(new);
2104  }
2105  }
2106  /* save the results */
2107  if (found) {
2108  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
2109  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2110  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2111  ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "app_voicemail");
2112  ast_config_destroy(cfg);
2113  break;
2114  }
2115 
2116  ast_config_destroy(cfg);
2117  }
2118  /* Fall-through */
2119  case OPT_PWLOC_USERSCONF:
2120  /* check users.conf and update the password stored for the mailbox */
2121  /* if no vmsecret entry exists create one. */
2122  if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
2123  ast_debug(4, "we are looking for %s\n", vmu->mailbox);
2124  for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
2125  ast_debug(4, "users.conf: %s\n", category);
2126  if (!strcasecmp(category, vmu->mailbox)) {
2127  char new[strlen(newpassword) + 1];
2128  if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
2129  ast_debug(3, "looks like we need to make vmsecret!\n");
2130  var = ast_variable_new("vmsecret", newpassword, "");
2131  } else {
2132  var = NULL;
2133  }
2134 
2135  sprintf(new, "%s", newpassword);
2136  if (!(cat = ast_category_get(cfg, category, NULL))) {
2137  ast_debug(4, "failed to get category!\n");
2138  ast_free(var);
2139  break;
2140  }
2141  if (!var) {
2142  ast_variable_update(cat, "vmsecret", new, NULL, 0);
2143  } else {
2144  ast_variable_append(cat, var);
2145  }
2146  found = 1;
2147  break;
2148  }
2149  }
2150  /* save the results and clean things up */
2151  if (found) {
2152  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
2153  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2154  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2155  ast_config_text_file_save("users.conf", cfg, "app_voicemail");
2156  }
2157 
2158  ast_config_destroy(cfg);
2159  }
2160  }
2161 }
2162 
2163 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
2164 {
2165  char buf[255];
2166  snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
2167  ast_debug(1, "External password: %s\n",buf);
2168  if (!ast_safe_system(buf)) {
2169  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
2170  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2171  /* Reset the password in memory, too */
2172  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2173  }
2174 }
2175 
2176 /*!
2177  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
2178  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
2179  * \param len The length of the path string that was written out.
2180  * \param context
2181  * \param ext
2182  * \param folder
2183  *
2184  * The path is constructed as
2185  * VM_SPOOL_DIRcontext/ext/folder
2186  *
2187  * \return zero on success, -1 on error.
2188  */
2189 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
2190 {
2191  return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
2192 }
2193 
2194 /*!
2195  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
2196  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
2197  * \param len The length of the path string that was written out.
2198  * \param dir
2199  * \param num
2200  *
2201  * The path is constructed as
2202  * VM_SPOOL_DIRcontext/ext/folder
2203  *
2204  * \return zero on success, -1 on error.
2205  */
2206 static int make_file(char *dest, const int len, const char *dir, const int num)
2207 {
2208  return snprintf(dest, len, "%s/msg%04d", dir, num);
2209 }
2210 
2211 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
2212  * \param dest String. base directory.
2213  * \param len Length of dest.
2214  * \param context String. Ignored if is null or empty string.
2215  * \param ext String. Ignored if is null or empty string.
2216  * \param folder String. Ignored if is null or empty string.
2217  * \return -1 on failure, 0 on success.
2218  */
2219 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
2220 {
2221  mode_t mode = VOICEMAIL_DIR_MODE;
2222  int res;
2223 
2224  make_dir(dest, len, context, ext, folder);
2225  if ((res = ast_mkdir(dest, mode))) {
2226  ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
2227  return -1;
2228  }
2229  return 0;
2230 }
2231 
2232 static const char *mbox(struct ast_vm_user *vmu, int id)
2233 {
2234 #ifdef IMAP_STORAGE
2235  if (vmu && id == 0) {
2236  return vmu->imapfolder;
2237  }
2238 #endif
2239  return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
2240 }
2241 
2242 static const char *vm_index_to_foldername(int id)
2243 {
2244  return mbox(NULL, id);
2245 }
2246 
2247 
2248 static int get_folder_by_name(const char *name)
2249 {
2250  size_t i;
2251 
2252  for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
2253  if (strcasecmp(name, mailbox_folders[i]) == 0) {
2254  return i;
2255  }
2256  }
2257 
2258  return -1;
2259 }
2260 
2261 static void free_user(struct ast_vm_user *vmu)
2262 {
2263  if (!vmu) {
2264  return;
2265  }
2266 
2267  ast_free(vmu->email);
2268  vmu->email = NULL;
2269  ast_free(vmu->emailbody);
2270  vmu->emailbody = NULL;
2271  ast_free(vmu->emailsubject);
2272  vmu->emailsubject = NULL;
2273 
2274  if (ast_test_flag(vmu, VM_ALLOCED)) {
2275  ast_free(vmu);
2276  }
2277 }
2278 
2279 static void free_user_final(struct ast_vm_user *vmu)
2280 {
2281  if (!vmu) {
2282  return;
2283  }
2284 
2285  if (!ast_strlen_zero(vmu->mailbox)) {
2286  ast_delete_mwi_state_full(vmu->mailbox, vmu->context, NULL);
2287  }
2288 
2289  free_user(vmu);
2290 }
2291 
2292 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
2293 
2294  int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
2295 
2296  /* remove old allocation */
2297  if (vms->deleted) {
2298  ast_free(vms->deleted);
2299  vms->deleted = NULL;
2300  }
2301  if (vms->heard) {
2302  ast_free(vms->heard);
2303  vms->heard = NULL;
2304  }
2305  vms->dh_arraysize = 0;
2306 
2307  if (arraysize > 0) {
2308  if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
2309  return -1;
2310  }
2311  if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
2312  ast_free(vms->deleted);
2313  vms->deleted = NULL;
2314  return -1;
2315  }
2316  vms->dh_arraysize = arraysize;
2317  }
2318 
2319  return 0;
2320 }
2321 
2322 /* All IMAP-specific functions should go in this block. This
2323  * keeps them from being spread out all over the code */
2324 #ifdef IMAP_STORAGE
2325 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
2326 {
2327  char arg[10];
2328  struct vm_state *vms;
2329  unsigned long messageNum;
2330 
2331  /* If greetings aren't stored in IMAP, just delete the file */
2332  if (msgnum < 0 && !imapgreetings) {
2333  ast_filedelete(file, NULL);
2334  return;
2335  }
2336 
2337  if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2338  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);
2339  return;
2340  }
2341 
2342  if (msgnum < 0) {
2343  imap_delete_old_greeting(file, vms);
2344  return;
2345  }
2346 
2347  /* find real message number based on msgnum */
2348  /* this may be an index into vms->msgArray based on the msgnum. */
2349  messageNum = vms->msgArray[msgnum];
2350  if (messageNum == 0) {
2351  ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
2352  return;
2353  }
2354  ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
2355  /* delete message */
2356  snprintf (arg, sizeof(arg), "%lu", messageNum);
2357  ast_mutex_lock(&vms->lock);
2358  mail_setflag (vms->mailstream, arg, "\\DELETED");
2359  mail_expunge(vms->mailstream);
2360  ast_mutex_unlock(&vms->lock);
2361 }
2362 
2363 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)
2364 {
2365  struct ast_channel *chan;
2366  char *cid;
2367  char *cid_name;
2368  char *cid_num;
2369  struct vm_state *vms;
2370  const char *duration_str;
2371  int duration = 0;
2372 
2373  /*
2374  * First, get things initially set up. If any of this fails, then
2375  * back out before doing anything substantial
2376  */
2377  vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0);
2378  if (!vms) {
2379  return;
2380  }
2381 
2382  if (open_mailbox(vms, vmu, folder)) {
2383  return;
2384  }
2385 
2386  chan = ast_dummy_channel_alloc();
2387  if (!chan) {
2388  close_mailbox(vms, vmu);
2389  return;
2390  }
2391 
2392  /*
2393  * We need to make sure the new message we save has the same
2394  * callerid, flag, and duration as the original message
2395  */
2396  cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
2397 
2398  if (!ast_strlen_zero(cid)) {
2399  ast_callerid_parse(cid, &cid_name, &cid_num);
2400  ast_party_caller_init(ast_channel_caller(chan));
2401  if (!ast_strlen_zero(cid_name)) {
2402  ast_channel_caller(chan)->id.name.valid = 1;
2403  ast_channel_caller(chan)->id.name.str = ast_strdup(cid_name);
2404  }
2405  if (!ast_strlen_zero(cid_num)) {
2406  ast_channel_caller(chan)->id.number.valid = 1;
2407  ast_channel_caller(chan)->id.number.str = ast_strdup(cid_num);
2408  }
2409  }
2410 
2411  duration_str = ast_variable_retrieve(msg_cfg, "message", "duration");
2412 
2413  if (!ast_strlen_zero(duration_str)) {
2414  sscanf(duration_str, "%30d", &duration);
2415  }
2416 
2417  /*
2418  * IMAP messages cannot be altered once delivered. So we have to delete the
2419  * current message and then re-add it with the updated message ID.
2420  *
2421  * Furthermore, there currently is no atomic way to create a new message and to
2422  * store it in an arbitrary folder. So we have to save it to the INBOX and then
2423  * move to the appropriate folder.
2424  */
2425  if (!imap_store_file(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, vmfmts,
2426  duration, vms, ast_variable_retrieve(msg_cfg, "message", "flag"), msg_id)) {
2427  if (folder != NEW_FOLDER) {
2428  save_to_folder(vmu, vms, msgnum, folder, NULL, 1);
2429  }
2430  vm_imap_delete(dir, msgnum, vmu);
2431  }
2432  close_mailbox(vms, vmu);
2433  ast_channel_unref(chan);
2434 }
2435 
2436 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
2437 {
2438  struct vm_state *vms_p;
2439  char *file, *filename;
2440  char dest[PATH_MAX];
2441  int i;
2442  BODY *body;
2443  int ret = 0;
2444  int curr_mbox;
2445 
2446  /* This function is only used for retrieval of IMAP greetings
2447  * regular messages are not retrieved this way, nor are greetings
2448  * if they are stored locally*/
2449  if (msgnum > -1 || !imapgreetings) {
2450  return 0;
2451  } else {
2452  file = strrchr(ast_strdupa(dir), '/');
2453  if (file)
2454  *file++ = '\0';
2455  else {
2456  ast_debug(1, "Failed to procure file name from directory passed.\n");
2457  return -1;
2458  }
2459  }
2460 
2461  /* check if someone is accessing this box right now... */
2462  if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) &&
2463  !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2464  /* Unlike when retrieving a message, it is reasonable not to be able to find a
2465  * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
2466  * that's all we need to do.
2467  */
2468  if (!(vms_p = create_vm_state_from_user(vmu))) {
2469  ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
2470  return -1;
2471  }
2472  }
2473 
2474  /* Greetings will never have a prepended message */
2475  *vms_p->introfn = '\0';
2476 
2477  ast_mutex_lock(&vms_p->lock);
2478 
2479  /* get the current mailbox so that we can point the mailstream back to it later */
2480  curr_mbox = get_folder_by_name(vms_p->curbox);
2481 
2482  if (init_mailstream(vms_p, GREETINGS_FOLDER) || !vms_p->mailstream) {
2483  ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2484  ast_mutex_unlock(&vms_p->lock);
2485  return -1;
2486  }
2487 
2488  /*XXX Yuck, this could probably be done a lot better */
2489  for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
2490  mail_fetchstructure(vms_p->mailstream, i + 1, &body);
2491  /* We have the body, now we extract the file name of the first attachment. */
2492  if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2493  char *attachment = body->nested.part->next->body.parameter->value;
2494  char copy[strlen(attachment) + 1];
2495 
2496  strcpy(copy, attachment); /* safe */
2497  attachment = copy;
2498 
2499  filename = strsep(&attachment, ".");
2500  if (!strcmp(filename, file)) {
2501  ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
2502  vms_p->msgArray[vms_p->curmsg] = i + 1;
2503  create_dirpath(dest, sizeof(dest), vmu->context, vms_p->username, "");
2504  save_body(body, vms_p, "2", attachment, 0);
2505  ret = 0;
2506  break;
2507  }
2508  } else {
2509  ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
2510  ret = -1;
2511  break;
2512  }
2513  }
2514 
2515  if (curr_mbox != -1) {
2516  /* restore previous mbox stream */
2517  if (init_mailstream(vms_p, curr_mbox) || !vms_p->mailstream) {
2518  ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2519  ret = -1;
2520  }
2521  }
2522  ast_mutex_unlock(&vms_p->lock);
2523  return ret;
2524 }
2525 
2526 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
2527 {
2528  BODY *body;
2529  char *header_content;
2530  char *attachedfilefmt;
2531  char buf[80];
2532  struct vm_state *vms;
2533  char text_file[PATH_MAX];
2534  FILE *text_file_ptr;
2535  int res = 0;
2536  struct ast_vm_user *vmu;
2537  int curr_mbox;
2538 
2539  if (!(vmu = find_user(NULL, context, mailbox))) {
2540  ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
2541  return -1;
2542  }
2543 
2544  if (msgnum < 0) {
2545  if (imapgreetings) {
2546  res = imap_retrieve_greeting(dir, msgnum, vmu);
2547  goto exit;
2548  } else {
2549  res = 0;
2550  goto exit;
2551  }
2552  }
2553 
2554  /* Before anything can happen, we need a vm_state so that we can
2555  * actually access the imap server through the vms->mailstream
2556  */
2557  if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2558  /* This should not happen. If it does, then I guess we'd
2559  * need to create the vm_state, extract which mailbox to
2560  * open, and then set up the msgArray so that the correct
2561  * IMAP message could be accessed. If I have seen correctly
2562  * though, the vms should be obtainable from the vmstates list
2563  * and should have its msgArray properly set up.
2564  */
2565  ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
2566  res = -1;
2567  goto exit;
2568  }
2569 
2570  /* Ensure we have the correct mailbox open and have a valid mailstream for it */
2571  curr_mbox = get_folder_by_name(vms->curbox);
2572  if (curr_mbox < 0) {
2573  ast_debug(3, "Mailbox folder curbox not set, defaulting to Inbox\n");
2574  curr_mbox = 0;
2575  }
2576  init_mailstream(vms, curr_mbox);
2577  if (!vms->mailstream) {
2578  ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmu->mailbox);
2579  res = -1;
2580  goto exit;
2581  }
2582 
2583  make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
2584  snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
2585 
2586  /* Don't try to retrieve a message from IMAP if it already is on the file system */
2587  if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
2588  res = 0;
2589  goto exit;
2590  }
2591 
2592  ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
2593  if (vms->msgArray[msgnum] == 0) {
2594  ast_log(LOG_WARNING, "Trying to access unknown message\n");
2595  res = -1;
2596  goto exit;
2597  }
2598 
2599  /* This will only work for new messages... */
2600  ast_mutex_lock(&vms->lock);
2601  header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
2602  ast_mutex_unlock(&vms->lock);
2603  /* empty string means no valid header */
2604  if (ast_strlen_zero(header_content)) {
2605  ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
2606  res = -1;
2607  goto exit;
2608  }
2609 
2610  ast_mutex_lock(&vms->lock);
2611  mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
2612  ast_mutex_unlock(&vms->lock);
2613 
2614  /* We have the body, now we extract the file name of the first attachment. */
2615  if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2616  attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
2617  } else {
2618  ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
2619  res = -1;
2620  goto exit;
2621  }
2622 
2623  /* Find the format of the attached file */
2624 
2625  strsep(&attachedfilefmt, ".");
2626  if (!attachedfilefmt) {
2627  ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
2628  res = -1;
2629  goto exit;
2630  }
2631 
2632  save_body(body, vms, "2", attachedfilefmt, 0);
2633  if (save_body(body, vms, "3", attachedfilefmt, 1)) {
2634  *vms->introfn = '\0';
2635  }
2636 
2637  /* Get info from headers!! */
2638  snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
2639 
2640  if (!(text_file_ptr = fopen(text_file, "w"))) {
2641  ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
2642  goto exit;
2643  }
2644 
2645  fprintf(text_file_ptr, "%s\n", "[message]");
2646 
2647  if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))) {
2648  fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
2649  }
2650  if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))) {
2651  fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
2652  }
2653  if (get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))) {
2654  fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
2655  }
2656  if (get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))) {
2657  fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
2658  }
2659  if (get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))) {
2660  fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
2661  }
2662  if (get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))) {
2663  fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
2664  }
2665  if (get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf))) {
2666  fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
2667  }
2668  if (get_header_by_tag(header_content, "X-Asterisk-VM-Message-ID:", buf, sizeof(buf))) {
2669  fprintf(text_file_ptr, "msg_id=%s\n", S_OR(buf, ""));
2670  }
2671  fclose(text_file_ptr);
2672 
2673 exit:
2674  free_user(vmu);
2675  return res;
2676 }
2677 
2678 static int folder_int(const char *folder)
2679 {
2680  /*assume a NULL folder means INBOX*/
2681  if (!folder) {
2682  return 0;
2683  }
2684  if (!strcasecmp(folder, imapfolder)) {
2685  return 0;
2686  } else if (!strcasecmp(folder, "Old")) {
2687  return 1;
2688  } else if (!strcasecmp(folder, "Work")) {
2689  return 2;
2690  } else if (!strcasecmp(folder, "Family")) {
2691  return 3;
2692  } else if (!strcasecmp(folder, "Friends")) {
2693  return 4;
2694  } else if (!strcasecmp(folder, "Cust1")) {
2695  return 5;
2696  } else if (!strcasecmp(folder, "Cust2")) {
2697  return 6;
2698  } else if (!strcasecmp(folder, "Cust3")) {
2699  return 7;
2700  } else if (!strcasecmp(folder, "Cust4")) {
2701  return 8;
2702  } else if (!strcasecmp(folder, "Cust5")) {
2703  return 9;
2704  } else if (!strcasecmp(folder, "Urgent")) {
2705  return 11;
2706  } else { /*assume they meant INBOX if folder is not found otherwise*/
2707  return 0;
2708  }
2709 }
2710 
2711 static int __messagecount(const char *context, const char *mailbox, const char *folder)
2712 {
2713  SEARCHPGM *pgm;
2714  SEARCHHEADER *hdr;
2715 
2716  struct ast_vm_user *vmu, vmus;
2717  struct vm_state *vms_p;
2718  int ret = 0;
2719  int fold = folder_int(folder);
2720  int urgent = 0;
2721 
2722  /* If URGENT, then look at INBOX */
2723  if (fold == 11) {
2724  fold = NEW_FOLDER;
2725  urgent = 1;
2726  }
2727 
2728  if (ast_strlen_zero(mailbox))
2729  return 0;
2730 
2731  /* We have to get the user before we can open the stream! */
2732  memset(&vmus, 0, sizeof(vmus));
2733  vmu = find_user(&vmus, context, mailbox);
2734  if (!vmu) {
2735  ast_log(AST_LOG_WARNING, "Couldn't find mailbox %s in context %s\n", mailbox, context);
2736  free_user(vmu);
2737  return -1;
2738  } else {
2739  /* No IMAP account available */
2740  if (vmu->imapuser[0] == '\0') {
2741  ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2742  free_user(vmu);
2743  return -1;
2744  }
2745  }
2746 
2747  /* No IMAP account available */
2748  if (vmu->imapuser[0] == '\0') {
2749  ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2750  free_user(vmu);
2751  return -1;
2752  }
2753 
2754  /* check if someone is accessing this box right now... */
2755  vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
2756  if (!vms_p) {
2757  vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
2758  }
2759  if (vms_p) {
2760  ast_debug(3, "Returning before search - user is logged in\n");
2761  if (fold == 0) { /* INBOX */
2762  free_user(vmu);
2763  return urgent ? vms_p->urgentmessages : vms_p->newmessages;
2764  }
2765  if (fold == 1) { /* Old messages */
2766  free_user(vmu);
2767  return vms_p->oldmessages;
2768  }
2769  }
2770 
2771  /* add one if not there... */
2772  vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
2773  if (!vms_p) {
2774  vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
2775  }
2776 
2777  if (!vms_p) {
2778  vms_p = create_vm_state_from_user(vmu);
2779  }
2780  ret = init_mailstream(vms_p, fold);
2781  if (!vms_p->mailstream) {
2782  ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
2783  free_user(vmu);
2784  return -1;
2785  }
2786  if (ret == 0) {
2787  ast_mutex_lock(&vms_p->lock);
2788  pgm = mail_newsearchpgm ();
2789  hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
2790  hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
2791  pgm->header = hdr;
2792  if (fold != OLD_FOLDER) {
2793  pgm->unseen = 1;
2794  pgm->seen = 0;
2795  }
2796  /* In the special case where fold is 1 (old messages) we have to do things a bit
2797  * differently. Old messages are stored in the INBOX but are marked as "seen"
2798  */
2799  else {
2800  pgm->unseen = 0;
2801  pgm->seen = 1;
2802  }
2803  /* look for urgent messages */
2804  if (fold == NEW_FOLDER) {
2805  if (urgent) {
2806  pgm->flagged = 1;
2807  pgm->unflagged = 0;
2808  } else {
2809  pgm->flagged = 0;
2810  pgm->unflagged = 1;
2811  }
2812  }
2813  pgm->undeleted = 1;
2814  pgm->deleted = 0;
2815 
2816  vms_p->vmArrayIndex = 0;
2817  mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2818  if (fold == 0 && urgent == 0)
2819  vms_p->newmessages = vms_p->vmArrayIndex;
2820  if (fold == 1)
2821  vms_p->oldmessages = vms_p->vmArrayIndex;
2822  if (fold == 0 && urgent == 1)
2823  vms_p->urgentmessages = vms_p->vmArrayIndex;
2824  /*Freeing the searchpgm also frees the searchhdr*/
2825  mail_free_searchpgm(&pgm);
2826  ast_mutex_unlock(&vms_p->lock);
2827  free_user(vmu);
2828  vms_p->updated = 0;
2829  return vms_p->vmArrayIndex;
2830  } else {
2831  ast_mutex_lock(&vms_p->lock);
2832  mail_ping(vms_p->mailstream);
2833  ast_mutex_unlock(&vms_p->lock);
2834  }
2835  free_user(vmu);
2836  return 0;
2837 }
2838 
2839 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
2840 {
2841  /* Check if mailbox is full */
2842  check_quota(vms, vmu->imapfolder);
2843  if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
2844  ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
2845  if (chan) {
2846  ast_play_and_wait(chan, "vm-mailboxfull");
2847  }
2848  return -1;
2849  }
2850 
2851  /* Check if we have exceeded maxmsg */
2852  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));
2853  if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
2854  ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
2855  if (chan) {
2856  ast_play_and_wait(chan, "vm-mailboxfull");
2857  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2858  }
2859  return -1;
2860  }
2861 
2862  return 0;
2863 }
2864 
2865 /*!
2866  * \brief Gets the number of messages that exist in a mailbox folder.
2867  * \param mailbox_id
2868  * \param folder
2869  *
2870  * This method is used when IMAP backend is used.
2871  * \return The number of messages in this mailbox folder (zero or more).
2872  */
2873 static int messagecount(const char *mailbox_id, const char *folder)
2874 {
2875  char *context;
2876  char *mailbox;
2877  int count;
2878 
2879  if (ast_strlen_zero(mailbox_id)
2880  || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
2881  return 0;
2882  }
2883 
2884  if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
2885  count = __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
2886  } else {
2887  count = __messagecount(context, mailbox, folder);
2888  }
2889  return count < 0 ? 0 : count;
2890 }
2891 
2892 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)
2893 {
2894  char *myserveremail = serveremail;
2895  char fn[PATH_MAX];
2896  char introfn[PATH_MAX];
2897  char mailbox[256];
2898  char *stringp;
2899  FILE *p = NULL;
2900  char tmp[80] = "/tmp/astmail-XXXXXX";
2901  long len;
2902  void *buf;
2903  int tempcopy = 0;
2904  STRING str;
2905  int ret; /* for better error checking */
2906  char *imap_flags = NIL;
2907  int msgcount;
2908  int box = NEW_FOLDER;
2909 
2910  snprintf(mailbox, sizeof(mailbox), "%s@%s", vmu->mailbox, vmu->context);
2911  msgcount = messagecount(mailbox, "INBOX") + messagecount(mailbox, "Old");
2912 
2913  /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
2914  if (msgnum < 0) {
2915  if(!imapgreetings) {
2916  return 0;
2917  } else {
2918  box = GREETINGS_FOLDER;
2919  }
2920  }
2921 
2922  if (imap_check_limits(chan, vms, vmu, msgcount)) {
2923  return -1;
2924  }
2925 
2926  /* Set urgent flag for IMAP message */
2927  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
2928  ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
2929  imap_flags = "\\FLAGGED";
2930  }
2931 
2932  /* Attach only the first format */
2933  fmt = ast_strdupa(fmt);
2934  stringp = fmt;
2935  strsep(&stringp, "|");
2936 
2937  if (!ast_strlen_zero(vmu->serveremail))
2938  myserveremail = vmu->serveremail;
2939 
2940  if (msgnum > -1)
2941  make_file(fn, sizeof(fn), dir, msgnum);
2942  else
2943  ast_copy_string (fn, dir, sizeof(fn));
2944 
2945  snprintf(introfn, sizeof(introfn), "%sintro", fn);
2946  if (ast_fileexists(introfn, NULL, NULL) <= 0) {
2947  *introfn = '\0';
2948  }
2949 
2950  if (ast_strlen_zero(vmu->email)) {
2951  /* We need the vmu->email to be set when we call make_email_file, but
2952  * if we keep it set, a duplicate e-mail will be created. So at the end
2953  * of this function, we will revert back to an empty string if tempcopy
2954  * is 1.
2955  */
2956  vmu->email = ast_strdup(vmu->imapuser);
2957  tempcopy = 1;
2958  }
2959 
2960  if (!strcmp(fmt, "wav49"))
2961  fmt = "WAV";
2962  ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2963 
2964  /* Make a temporary file instead of piping directly to sendmail, in case the mail
2965  command hangs. */
2966  if (!(p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask))) {
2967  ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2968  if (tempcopy) {
2969  ast_free(vmu->email);
2970  vmu->email = NULL;
2971  }
2972  return -1;
2973  }
2974 
2975  if (msgnum < 0 && imapgreetings) {
2976  if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
2977  ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
2978  return -1;
2979  }
2980  imap_delete_old_greeting(fn, vms);
2981  }
2982 
2983  make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
2984  chan ? S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL) : NULL,
2985  chan ? S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL) : NULL,
2986  fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
2987  /* read mail file to memory */
2988  len = ftell(p);
2989  rewind(p);
2990  if (!(buf = ast_malloc(len + 1))) {
2991  ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
2992  fclose(p);
2993  if (tempcopy)
2994  *(vmu->email) = '\0';
2995  return -1;
2996  }
2997  if (fread(buf, 1, len, p) != len) {
2998  if (ferror(p)) {
2999  ast_log(LOG_ERROR, "Error while reading mail file: %s\n", tmp);
3000  return -1;
3001  }
3002  }
3003  ((char *) buf)[len] = '\0';
3004  INIT(&str, mail_string, buf, len);
3005  ret = init_mailstream(vms, box);
3006  if (ret == 0) {
3007  imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
3008  ast_mutex_lock(&vms->lock);
3009  if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
3010  ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
3011  ast_mutex_unlock(&vms->lock);
3012  fclose(p);
3013  unlink(tmp);
3014  ast_free(buf);
3015  } else {
3016  ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
3017  fclose(p);
3018  unlink(tmp);
3019  ast_free(buf);
3020  return -1;
3021  }
3022  ast_debug(3, "%s stored\n", fn);
3023 
3024  if (tempcopy)
3025  *(vmu->email) = '\0';
3026  inprocess_count(vmu->mailbox, vmu->context, -1);
3027  return 0;
3028 
3029 }
3030 
3031 /*!
3032  * \brief Gets the number of messages that exist in the inbox folder.
3033  * \param mailbox_context
3034  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
3035  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
3036  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
3037  *
3038  * This method is used when IMAP backend is used.
3039  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
3040  *
3041  * \return zero on success, -1 on error.
3042  */
3043 
3044 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
3045 {
3046  char tmp[PATH_MAX] = "";
3047  char *mailboxnc;
3048  char *context;
3049  char *mb;
3050  char *cur;
3051  int count = 0;
3052  if (newmsgs)
3053  *newmsgs = 0;
3054  if (oldmsgs)
3055  *oldmsgs = 0;
3056  if (urgentmsgs)
3057  *urgentmsgs = 0;
3058 
3059  ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
3060  /* If no mailbox, return immediately */
3061  if (ast_strlen_zero(mailbox_context))
3062  return 0;
3063 
3064  ast_copy_string(tmp, mailbox_context, sizeof(tmp));
3065  context = strchr(tmp, '@');
3066  if (strchr(mailbox_context, ',')) {
3067  int tmpnew, tmpold, tmpurgent;
3068  ast_copy_string(tmp, mailbox_context, sizeof(tmp));
3069  mb = tmp;
3070  while ((cur = strsep(&mb, ", "))) {
3071  if (!ast_strlen_zero(cur)) {
3072  if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
3073  return -1;
3074  else {
3075  if (newmsgs)
3076  *newmsgs += tmpnew;
3077  if (oldmsgs)
3078  *oldmsgs += tmpold;
3079  if (urgentmsgs)
3080  *urgentmsgs += tmpurgent;
3081  }
3082  }
3083  }
3084  return 0;
3085  }
3086  if (context) {
3087  *context = '\0';
3088  mailboxnc = tmp;
3089  context++;
3090  } else {
3091  context = "default";
3092  mailboxnc = (char *) mailbox_context;
3093  }
3094 
3095  if (newmsgs) {
3096  struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
3097  if (!vmu) {
3098  ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
3099  return -1;
3100  }
3101  if ((count = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
3102  free_user(vmu);
3103  return -1;
3104  }
3105  *newmsgs = count;
3106  free_user(vmu);
3107  }
3108  if (oldmsgs) {
3109  if ((count = __messagecount(context, mailboxnc, "Old")) < 0) {
3110  return -1;
3111  }
3112  *oldmsgs = count;
3113  }
3114  if (urgentmsgs) {
3115  if ((count = __messagecount(context, mailboxnc, "Urgent")) < 0) {
3116  return -1;
3117  }
3118  *urgentmsgs = count;
3119  }
3120  return 0;
3121 }
3122 
3123 /*!
3124  * \brief Determines if the given folder has messages.
3125  * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
3126  * \param folder the folder to look in
3127  *
3128  * This function is used when the mailbox is stored in an IMAP back end.
3129  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
3130  * \return 1 if the folder has one or more messages. zero otherwise.
3131  */
3132 
3133 static int has_voicemail(const char *mailbox, const char *folder)
3134 {
3135  char tmp[256], *tmp2, *box, *context;
3136  ast_copy_string(tmp, mailbox, sizeof(tmp));
3137  tmp2 = tmp;
3138  if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
3139  while ((box = strsep(&tmp2, ",&"))) {
3140  if (!ast_strlen_zero(box)) {
3141  if (has_voicemail(box, folder)) {
3142  return 1;
3143  }
3144  }
3145  }
3146  }
3147  if ((context = strchr(tmp, '@'))) {
3148  *context++ = '\0';
3149  } else {
3150  context = "default";
3151  }
3152  return __messagecount(context, tmp, folder) > 0 ? 1 : 0;
3153 }
3154 
3155 /*!
3156  * \brief Copies a message from one mailbox to another.
3157  * \param chan
3158  * \param vmu
3159  * \param imbox
3160  * \param msgnum
3161  * \param duration
3162  * \param recip
3163  * \param fmt
3164  * \param dir
3165  * \param flag, dest_folder
3166  *
3167  * This works with IMAP storage based mailboxes.
3168  *
3169  * \return zero on success, -1 on error.
3170  */
3171 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)
3172 {
3173  struct vm_state *sendvms = NULL;
3174  char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
3175  if (msgnum >= recip->maxmsg) {
3176  ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
3177  return -1;
3178  }
3179  if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
3180  ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
3181  return -1;
3182  }
3183  if (!get_vm_state_by_imapuser(recip->imapuser, 0)) {
3184  ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
3185  return -1;
3186  }
3187  snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
3188  ast_mutex_lock(&sendvms->lock);
3189  if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
3190  ast_mutex_unlock(&sendvms->lock);
3191  return 0;
3192  }
3193  ast_mutex_unlock(&sendvms->lock);
3194  ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
3195  return -1;
3196 }
3197 
3198 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
3199 {
3200  char tmp[256], *t = tmp;
3201  size_t left = sizeof(tmp);
3202 
3203  if (box == OLD_FOLDER) {
3204  ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
3205  } else {
3206  ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
3207  }
3208 
3209  if (box == NEW_FOLDER) {
3210  ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
3211  } else {
3212  snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
3213  }
3214 
3215  /* Build up server information */
3216  ast_build_string(&t, &left, "{%s:%s/imap", S_OR(vms->imapserver, imapserver), S_OR(vms->imapport, imapport));
3217 
3218  /* Add authentication user if present */
3219  if (!ast_strlen_zero(authuser))
3220  ast_build_string(&t, &left, "/authuser=%s", authuser);
3221 
3222  /* Add flags if present */
3223  if (!ast_strlen_zero(imapflags) || !(ast_strlen_zero(vms->imapflags))) {
3224  ast_build_string(&t, &left, "/%s", S_OR(vms->imapflags, imapflags));
3225  }
3226 
3227  /* End with username */
3228 #if 1
3229  ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
3230 #else
3231  ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
3232 #endif
3233  if (box == NEW_FOLDER || box == OLD_FOLDER)
3234  snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
3235  else if (box == GREETINGS_FOLDER)
3236  snprintf(spec, len, "%s%s", tmp, greetingfolder);
3237  else { /* Other folders such as Friends, Family, etc... */
3238  if (!ast_strlen_zero(imapparentfolder)) {
3239  /* imapparentfolder would typically be set to INBOX */
3240  snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
3241  } else {
3242  snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
3243  }
3244  }
3245 }
3246 
3247 static int init_mailstream(struct vm_state *vms, int box)
3248 {
3249  MAILSTREAM *stream = NIL;
3250  long debug;
3251  char tmp[256];
3252 
3253  if (!vms) {
3254  ast_log(LOG_ERROR, "vm_state is NULL!\n");
3255  return -1;
3256  }
3257  ast_debug(3, "vm_state user is:%s\n", vms->imapuser);
3258  if (vms->mailstream == NIL || !vms->mailstream) {
3259  ast_debug(1, "mailstream not set.\n");
3260  } else {
3261  stream = vms->mailstream;
3262  }
3263  /* debug = T; user wants protocol telemetry? */
3264  debug = NIL; /* NO protocol telemetry? */
3265 
3266  if (delimiter == '\0') { /* did not probe the server yet */
3267  char *cp;
3268 #ifdef USE_SYSTEM_IMAP
3269 #include <imap/linkage.c>
3270 #elif defined(USE_SYSTEM_CCLIENT)
3271 #include <c-client/linkage.c>
3272 #else
3273 #include "linkage.c"
3274 #endif
3275  /* Connect to INBOX first to get folders delimiter */
3276  imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
3277  ast_mutex_lock(&vms->lock);
3278  ast_mutex_lock(&mail_open_lock);
3279  stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
3280  ast_mutex_unlock(&mail_open_lock);
3281  ast_mutex_unlock(&vms->lock);
3282  if (stream == NIL) {
3283  ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
3284  return -1;
3285  }
3286  get_mailbox_delimiter(vms, stream);
3287  /* update delimiter in imapfolder */
3288  for (cp = vms->imapfolder; *cp; cp++)
3289  if (*cp == '/')
3290  *cp = delimiter;
3291  }
3292  /* Now connect to the target folder */
3293  imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
3294  ast_debug(3, "Before mail_open, server: %s, box:%d\n", tmp, box);
3295  ast_mutex_lock(&vms->lock);
3296  ast_mutex_lock(&mail_open_lock);
3297  vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
3298  /* Create the folder if it doesn't exist */
3299  if (vms->mailstream && !mail_status(vms->mailstream, tmp, SA_UIDNEXT)) {
3300  mail_create(vms->mailstream, tmp);
3301  }
3302  ast_mutex_unlock(&mail_open_lock);
3303  ast_mutex_unlock(&vms->lock);
3304  if (vms->mailstream == NIL) {
3305  return -1;
3306  } else {
3307  return 0;
3308  }
3309 }
3310 
3311 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
3312 {
3313  SEARCHPGM *pgm;
3314  SEARCHHEADER *hdr;
3315  int urgent = 0;
3316 
3317  /* If Urgent, then look at INBOX */
3318  if (box == 11) {
3319  box = NEW_FOLDER;
3320  urgent = 1;
3321  }
3322 
3323  ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
3324  ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
3325  ast_copy_string(vms->imapserver, vmu->imapserver, sizeof(vms->imapserver));
3326  ast_copy_string(vms->imapport, vmu->imapport, sizeof(vms->imapport));
3327  ast_copy_string(vms->imapflags, vmu->imapflags, sizeof(vms->imapflags));
3328  vms->imapversion = vmu->imapversion;
3329  ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
3330 
3331  if (init_mailstream(vms, box) || !vms->mailstream) {
3332  ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
3333  return -1;
3334  }
3335 
3336  create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
3337 
3338  /* Check Quota */
3339  if (box == 0) {
3340  ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
3341  check_quota(vms, (char *) mbox(vmu, box));
3342  }
3343 
3344  ast_mutex_lock(&vms->lock);
3345  pgm = mail_newsearchpgm();
3346 
3347  /* Check IMAP folder for Asterisk messages only... */
3348  hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
3349  hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
3350  pgm->header = hdr;
3351  pgm->deleted = 0;
3352  pgm->undeleted = 1;
3353 
3354  /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
3355  if (box == NEW_FOLDER && urgent == 1) {
3356  pgm->unseen = 1;
3357  pgm->seen = 0;
3358  pgm->flagged = 1;
3359  pgm->unflagged = 0;
3360  } else if (box == NEW_FOLDER && urgent == 0) {
3361  pgm->unseen = 1;
3362  pgm->seen = 0;
3363  pgm->flagged = 0;
3364  pgm->unflagged = 1;
3365  } else if (box == OLD_FOLDER) {
3366  pgm->seen = 1;
3367  pgm->unseen = 0;
3368  }
3369 
3370  ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
3371 
3372  vms->vmArrayIndex = 0;
3373  mail_search_full (vms->mailstream, NULL, pgm, NIL);
3374  vms->lastmsg = vms->vmArrayIndex - 1;
3375  mail_free_searchpgm(&pgm);
3376  /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
3377  * ensure to allocate enough space to account for all of them. Warn if old messages
3378  * have not been checked first as that is required.
3379  */
3380  if (box == 0 && !vms->dh_arraysize) {
3381  ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
3382  }
3383  if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
3384  ast_mutex_unlock(&vms->lock);
3385  return -1;
3386  }
3387 
3388  ast_mutex_unlock(&vms->lock);
3389  return 0;
3390 }
3391 
3392 static void write_file(char *filename, char *buffer, unsigned long len)
3393 {
3394  FILE *output;
3395 
3396  if (!filename || !buffer) {
3397  return;
3398  }
3399 
3400  if (!(output = fopen(filename, "w"))) {
3401  ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", filename, strerror(errno));
3402  return;
3403  }
3404 
3405  if (fwrite(buffer, len, 1, output) != 1) {
3406  if (ferror(output)) {
3407  ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
3408  }
3409  }
3410  fclose (output);
3411 }
3412 
3413 static void update_messages_by_imapuser(const char *user, unsigned long number)
3414 {
3415  struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
3416 
3417  if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
3418  return;
3419  }
3420 
3421  ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
3422 
3423  /* Ensure we have room for the next message. */
3424  if (vms->vmArrayIndex >= vms->msg_array_max) {
3425  long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
3426  if (!new_mem) {
3427  return;
3428  }
3429  vms->msgArray = new_mem;
3430  vms->msg_array_max *= 2;
3431  }
3432 
3433  vms->msgArray[vms->vmArrayIndex++] = number;
3434 }
3435 
3436 void mm_searched(MAILSTREAM *stream, unsigned long number)
3437 {
3438  char *mailbox = stream->mailbox, buf[1024] = "", *user;
3439 
3440  if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
3441  return;
3442 
3443  update_messages_by_imapuser(user, number);
3444 }
3445 
3446 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
3447 {
3448  struct ast_variable *var;
3449  struct ast_vm_user *vmu;
3450 
3451  vmu = ast_calloc(1, sizeof *vmu);
3452  if (!vmu)
3453  return NULL;
3454 
3455  populate_defaults(vmu);
3456  ast_set_flag(vmu, VM_ALLOCED);
3457 
3458  var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
3459  if (var) {
3460  apply_options_full(vmu, var);
3461  ast_variables_destroy(var);
3462  return vmu;
3463  } else {
3464  ast_free(vmu);
3465  return NULL;
3466  }
3467 }
3468 
3469 /* Interfaces to C-client */
3470 
3471 void mm_exists(MAILSTREAM * stream, unsigned long number)
3472 {
3473  /* mail_ping will callback here if new mail! */
3474  ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
3475  if (number == 0) return;
3476  set_update(stream);
3477 }
3478 
3479 
3480 void mm_expunged(MAILSTREAM * stream, unsigned long number)
3481 {
3482  /* mail_ping will callback here if expunged mail! */
3483  ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
3484  if (number == 0) return;
3485  set_update(stream);
3486 }
3487 
3488 
3489 void mm_flags(MAILSTREAM * stream, unsigned long number)
3490 {
3491  /* mail_ping will callback here if read mail! */
3492  ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
3493  if (number == 0) return;
3494  set_update(stream);
3495 }
3496 
3497 
3498 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
3499 {
3500  ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
3501  mm_log (string, errflg);
3502 }
3503 
3504 
3505 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
3506 {
3507  if (delimiter == '\0') {
3508  delimiter = delim;
3509  }
3510 
3511  ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
3512  if (attributes & LATT_NOINFERIORS)
3513  ast_debug(5, "no inferiors\n");
3514  if (attributes & LATT_NOSELECT)
3515  ast_debug(5, "no select\n");
3516  if (attributes & LATT_MARKED)
3517  ast_debug(5, "marked\n");
3518  if (attributes & LATT_UNMARKED)
3519  ast_debug(5, "unmarked\n");
3520 }
3521 
3522 
3523 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
3524 {
3525  ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
3526  if (attributes & LATT_NOINFERIORS)
3527  ast_debug(5, "no inferiors\n");
3528  if (attributes & LATT_NOSELECT)
3529  ast_debug(5, "no select\n");
3530  if (attributes & LATT_MARKED)
3531  ast_debug(5, "marked\n");
3532  if (attributes & LATT_UNMARKED)
3533  ast_debug(5, "unmarked\n");
3534 }
3535 
3536 
3537 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
3538 {
3539  struct ast_str *str;
3540 
3541  if (!DEBUG_ATLEAST(5) || !(str = ast_str_create(256))) {
3542  return;
3543  }
3544 
3545  ast_str_append(&str, 0, " Mailbox %s", mailbox);
3546  if (status->flags & SA_MESSAGES) {
3547  ast_str_append(&str, 0, ", %lu messages", status->messages);
3548  }
3549  if (status->flags & SA_RECENT) {
3550  ast_str_append(&str, 0, ", %lu recent", status->recent);
3551  }
3552  if (status->flags & SA_UNSEEN) {
3553  ast_str_append(&str, 0, ", %lu unseen", status->unseen);
3554  }
3555  if (status->flags & SA_UIDVALIDITY) {
3556  ast_str_append(&str, 0, ", %lu UID validity", status->uidvalidity);
3557  }
3558  if (status->flags & SA_UIDNEXT) {
3559  ast_str_append(&str, 0, ", %lu next UID", status->uidnext);
3560  }
3561  ast_log(LOG_DEBUG, "%s\n", ast_str_buffer(str));
3562 
3563  ast_free(str);
3564 }
3565 
3566 
3567 void mm_log(char *string, long errflg)
3568 {
3569  switch ((short) errflg) {
3570  case NIL:
3571  ast_debug(1, "IMAP Info: %s\n", string);
3572  break;
3573  case PARSE:
3574  case WARN:
3575  ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
3576  break;
3577  case ERROR:
3578  ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
3579  break;
3580  }
3581 }
3582 
3583 
3584 void mm_dlog(char *string)
3585 {
3586  ast_log(AST_LOG_NOTICE, "%s\n", string);
3587 }
3588 
3589 
3590 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
3591 {
3592  struct ast_vm_user *vmu;
3593 
3594  ast_debug(4, "Entering callback mm_login\n");
3595 
3596  ast_copy_string(user, mb->user, MAILTMPLEN);
3597 
3598  /* We should only do this when necessary */
3599  if (!ast_strlen_zero(authpassword)) {
3600  ast_copy_string(pwd, authpassword, MAILTMPLEN);
3601  } else {
3602  AST_LIST_TRAVERSE(&users, vmu, list) {
3603  if (!strcasecmp(mb->user, vmu->imapuser)) {
3604  ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
3605  break;
3606  }
3607  }
3608  if (!vmu) {
3609  if ((vmu = find_user_realtime_imapuser(mb->user))) {
3610  ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
3611  free_user(vmu);
3612  }
3613  }
3614  }
3615 }
3616 
3617 
3618 void mm_critical(MAILSTREAM * stream)
3619 {
3620 }
3621 
3622 
3623 void mm_nocritical(MAILSTREAM * stream)
3624 {
3625 }
3626 
3627 
3628 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
3629 {
3630  kill (getpid (), SIGSTOP);
3631  return NIL;
3632 }
3633 
3634 
3635 void mm_fatal(char *string)
3636 {
3637  ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
3638 }
3639 
3640 /* C-client callback to handle quota */
3641 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
3642 {
3643  struct vm_state *vms;
3644  char *mailbox = stream->mailbox, *user;
3645  char buf[1024] = "";
3646  unsigned long usage = 0, limit = 0;
3647 
3648  while (pquota) {
3649  usage = pquota->usage;
3650  limit = pquota->limit;
3651  pquota = pquota->next;
3652  }
3653 
3654  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)))) {
3655  ast_log(AST_LOG_ERROR, "No state found.\n");
3656  return;
3657  }
3658 
3659  ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
3660 
3661  vms->quota_usage = usage;
3662  vms->quota_limit = limit;
3663 }
3664 
3665 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
3666 {
3667  char *start, *eol_pnt;
3668  int taglen;
3669 
3670  if (ast_strlen_zero(header) || ast_strlen_zero(tag))
3671  return NULL;
3672 
3673  taglen = strlen(tag) + 1;
3674  if (taglen < 1)
3675  return NULL;
3676 
3677  if (!(start = strcasestr(header, tag)))
3678  return NULL;
3679 
3680  /* Since we can be called multiple times we should clear our buffer */
3681  memset(buf, 0, len);
3682 
3683  ast_copy_string(buf, start+taglen, len);
3684  if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
3685  *eol_pnt = '\0';
3686  return buf;
3687 }
3688 
3689 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
3690 {
3691  char *start, *eol_pnt, *quote;
3692 
3693  if (ast_strlen_zero(mailbox))
3694  return NULL;
3695 
3696  if (!(start = strstr(mailbox, "/user=")))
3697  return NULL;
3698 
3699  ast_copy_string(buf, start+6, len);
3700 
3701  if (!(quote = strchr(buf, '"'))) {
3702  if ((eol_pnt = strchr(buf, '/')) || (eol_pnt = strchr(buf, '}'))) {
3703  *eol_pnt = '\0';
3704  }
3705  return buf;
3706  } else {
3707  if ((eol_pnt = strchr(quote + 1, '"'))) {
3708  *eol_pnt = '\0';
3709  }
3710  return quote + 1;
3711  }
3712 }
3713 
3714 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
3715 {
3716  struct vm_state *vms_p;
3717 
3718  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3719  if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
3720  return vms_p;
3721  }
3722  ast_debug(5, "Adding new vmstate for %s\n", vmu->imapuser);
3723  /* XXX: Is this correctly freed always? */
3724  if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
3725  return NULL;
3726  ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
3727  ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
3728  ast_copy_string(vms_p->imapserver, vmu->imapserver, sizeof(vms_p->imapserver));
3729  ast_copy_string(vms_p->imapport, vmu->imapport, sizeof(vms_p->imapport));
3730  ast_copy_string(vms_p->imapflags, vmu->imapflags, sizeof(vms_p->imapflags));
3731  ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
3732  ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
3733  vms_p->mailstream = NIL; /* save for access from interactive entry point */
3734  vms_p->imapversion = vmu->imapversion;
3735  ast_debug(5, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
3736  vms_p->updated = 1;
3737  /* set mailbox to INBOX! */
3738  ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
3739  init_vm_state(vms_p);
3740  vmstate_insert(vms_p);
3741  return vms_p;
3742 }
3743 
3744 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
3745 {
3746  struct vmstate *vlist = NULL;
3747 
3748  if (interactive) {
3749  struct vm_state *vms;
3750  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3751  if ((vms = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms->imapuser, user)) {
3752  return vms;
3753  }
3754  }
3755 
3756  AST_LIST_LOCK(&vmstates);
3757  AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3758  if (!vlist->vms) {
3759  ast_debug(3, "error: vms is NULL for %s\n", user);
3760  continue;
3761  }
3762  if (vlist->vms->imapversion != imapversion) {
3763  continue;
3764  }
3765 
3766  if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
3767  AST_LIST_UNLOCK(&vmstates);
3768  return vlist->vms;
3769  }
3770  }
3771  AST_LIST_UNLOCK(&vmstates);
3772 
3773  ast_debug(3, "%s not found in vmstates\n", user);
3774 
3775  return NULL;
3776 }
3777 
3778 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
3779 {
3780 
3781  struct vmstate *vlist = NULL;
3782  const char *local_context = S_OR(context, "default");
3783 
3784  if (interactive) {
3785  struct vm_state *vms;
3786  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3787  if ((vms = pthread_getspecific(ts_vmstate.key)) &&
3788  !strcmp(vms->username,mailbox) && !strcmp(vms->context, local_context)) {
3789  return vms;
3790  }
3791  }
3792 
3793  AST_LIST_LOCK(&vmstates);
3794  AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3795  if (!vlist->vms) {
3796  ast_debug(3, "error: vms is NULL for %s\n", mailbox);
3797  continue;
3798  }
3799  if (vlist->vms->imapversion != imapversion) {
3800  continue;
3801  }
3802 
3803  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);
3804 
3805  if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
3806  ast_debug(3, "Found it!\n");
3807  AST_LIST_UNLOCK(&vmstates);
3808  return vlist->vms;
3809  }
3810  }
3811  AST_LIST_UNLOCK(&vmstates);
3812 
3813  ast_debug(3, "%s not found in vmstates\n", mailbox);
3814 
3815  return NULL;
3816 }
3817 
3818 static void vmstate_insert(struct vm_state *vms)
3819 {
3820  struct vmstate *v;
3821  struct vm_state *altvms;
3822 
3823  /* If interactive, it probably already exists, and we should
3824  use the one we already have since it is more up to date.
3825  We can compare the username to find the duplicate */
3826  if (vms->interactive == 1) {
3827  altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
3828  if (altvms) {
3829  ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3830  vms->newmessages = altvms->newmessages;
3831  vms->oldmessages = altvms->oldmessages;
3832  vms->vmArrayIndex = altvms->vmArrayIndex;
3833  /* XXX: no msgArray copying? */
3834  vms->lastmsg = altvms->lastmsg;
3835  vms->curmsg = altvms->curmsg;
3836  /* get a pointer to the persistent store */
3837  vms->persist_vms = altvms;
3838  /* Reuse the mailstream? */
3839 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
3840  vms->mailstream = altvms->mailstream;
3841 #else
3842  vms->mailstream = NIL;
3843 #endif
3844  }
3845  return;
3846  }
3847 
3848  if (!(v = ast_calloc(1, sizeof(*v))))
3849  return;
3850 
3851  v->vms = vms;
3852 
3853  ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3854 
3855  AST_LIST_LOCK(&vmstates);
3856  AST_LIST_INSERT_TAIL(&vmstates, v, list);
3857  AST_LIST_UNLOCK(&vmstates);
3858 }
3859 
3860 static void vmstate_delete(struct vm_state *vms)
3861 {
3862  struct vmstate *vc = NULL;
3863  struct vm_state *altvms = NULL;
3864 
3865  /* If interactive, we should copy pertinent info
3866  back to the persistent state (to make update immediate) */
3867  if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
3868  ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3869  altvms->newmessages = vms->newmessages;
3870  altvms->oldmessages = vms->oldmessages;
3871  altvms->updated = 1;
3872  vms->mailstream = mail_close(vms->mailstream);
3873 
3874  /* Interactive states are not stored within the persistent list */
3875  return;
3876  }
3877 
3878  ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3879 
3880  AST_LIST_LOCK(&vmstates);
3881  AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
3882  if (vc->vms == vms) {
3884  break;
3885  }
3886  }
3888  AST_LIST_UNLOCK(&vmstates);
3889 
3890  if (vc) {
3891  ast_mutex_destroy(&vc->vms->lock);
3892  ast_free(vc->vms->msgArray);
3893  vc->vms->msgArray = NULL;
3894  vc->vms->msg_array_max = 0;
3895  /* XXX: is no one supposed to free vms itself? */
3896  ast_free(vc);
3897  } else {
3898  ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3899  }
3900 }
3901 
3902 static void set_update(MAILSTREAM * stream)
3903 {
3904  struct vm_state *vms;
3905  char *mailbox = stream->mailbox, *user;
3906  char buf[1024] = "";
3907 
3908  if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
3909  if (user && DEBUG_ATLEAST(3))
3910  ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
3911  return;
3912  }
3913 
3914  ast_debug(3, "User %s mailbox set for update.\n", user);
3915 
3916  vms->updated = 1; /* Set updated flag since mailbox changed */
3917 }
3918 
3919 static void init_vm_state(struct vm_state *vms)
3920 {
3921  vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
3922  vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
3923  if (!vms->msgArray) {
3924  /* Out of mem? This can't be good. */
3925  vms->msg_array_max = 0;
3926  }
3927  vms->vmArrayIndex = 0;
3928  ast_mutex_init(&vms->lock);
3929 }
3930 
3931 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro)
3932 {
3933  char *body_content;
3934  char *body_decoded;
3935  char *fn = is_intro ? vms->introfn : vms->fn;
3936  unsigned long len = 0;
3937  unsigned long newlen = 0;
3938  char filename[256];
3939 
3940  if (!body || body == NIL)
3941  return -1;
3942 
3943  ast_mutex_lock(&vms->lock);
3944  body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
3945  ast_mutex_unlock(&vms->lock);
3946  if (len > MAX_MAIL_BODY_CONTENT_SIZE) {
3947  ast_log(AST_LOG_ERROR,
3948  "Msgno %ld, section %s. The body's content size %ld is huge (max %ld). User:%s, mailbox %s\n",
3949  vms->msgArray[vms->curmsg], section, len, MAX_MAIL_BODY_CONTENT_SIZE, vms->imapuser, vms->username);
3950  return -1;
3951  }
3952  if (body_content != NIL && len) {
3953  snprintf(filename, sizeof(filename), "%s.%s", fn, format);
3954  /* ast_debug(1, body_content); */
3955  body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
3956  /* If the body of the file is empty, return an error */
3957  if (!newlen || !body_decoded) {
3958  return -1;
3959  }
3960  write_file(filename, (char *) body_decoded, newlen);
3961  } else {
3962  ast_debug(5, "Body of message is NULL.\n");
3963  return -1;
3964  }
3965  return 0;
3966 }
3967 
3968 /*!
3969  * \brief Get delimiter via mm_list callback
3970  * \param vms The voicemail state object
3971  * \param stream
3972  *
3973  * Determines the delimiter character that is used by the underlying IMAP based mail store.
3974  */
3975 /* MUTEX should already be held */
3976 static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream) {
3977  char tmp[50];
3978  snprintf(tmp, sizeof(tmp), "{%s}", S_OR(vms->imapserver, imapserver));
3979  mail_list(stream, tmp, "*");
3980 }
3981 
3982 /*!
3983  * \brief Check Quota for user
3984  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
3985  * \param mailbox the mailbox to check the quota for.
3986  *
3987  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
3988  */
3989 static void check_quota(struct vm_state *vms, char *mailbox) {
3990  ast_mutex_lock(&vms->lock);
3991  mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
3992  ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
3993  if (vms && vms->mailstream != NULL) {
3994  imap_getquotaroot(vms->mailstream, mailbox);
3995  } else {
3996  ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
3997  }
3998  ast_mutex_unlock(&vms->lock);
3999 }
4000 
4001 #endif /* IMAP_STORAGE */
4002 
4003 /*! \brief Lock file path
4004  * only return failure if ast_lock_path returns 'timeout',
4005  * not if the path does not exist or any other reason
4006  */
4007 static int vm_lock_path(const char *path)
4008 {
4009  switch (ast_lock_path(path)) {
4010  case AST_LOCK_TIMEOUT:
4011  return -1;
4012  default:
4013  return 0;
4014  }
4015 }
4016 
4017 #define MSG_ID_LEN 256
4018 
4019 /* Used to attach a unique identifier to an msg_id */
4020 static int msg_id_incrementor;
4021 
4022 /*!
4023  * \brief Sets the destination string to a uniquely identifying msg_id string
4024  * \param dst pointer to a character buffer that should contain MSG_ID_LEN characters.
4025  */
4026 static void generate_msg_id(char *dst);
4027 
4028 #ifdef ODBC_STORAGE
4029 
4030 /*!
4031  * \internal
4032  * \brief Create a buffer containing the SQL statement with the table name inserted.
4033  *
4034  * \param __sql_fmt The SQL statement with a single '%s' where the table name should be inserted.
4035  *
4036  * \note The buffer is allocated on the stack and should not be freed.
4037  *
4038  * \return A pointer to a buffer containing the SQL statement with the table name inserted.
4039  */
4040 #define MAKE_SQL_PTRA(__sql_fmt) \
4041 ({ \
4042  /* The NULL terminator is included in odbc_table_len. */ \
4043  char *__sql = ast_alloca(strlen(__sql_fmt) + odbc_table_len); \
4044  sprintf(__sql, __sql_fmt, odbc_table); /* Safe */ \
4045  __sql; \
4046 })
4047 
4048 /*!
4049  * \internal
4050  * \brief Create a buffer containing the SQL statement with the table name inserted twice.
4051  *
4052  * \param __sql_fmt The SQL statement with two '%s' where the table name should be inserted.
4053  *
4054  * \note The buffer is allocated on the stack and should not be freed.
4055  *
4056  * \return A pointer to a buffer containing the SQL statement with the table name inserted.
4057  */
4058 #define MAKE_SQL_PTRA2(__sql_fmt) \
4059 ({ \
4060  /* The NULL terminator is included in odbc_table_len. */ \
4061  char *__sql = ast_alloca(strlen(__sql_fmt) + (odbc_table_len * 2)); \
4062  sprintf(__sql, __sql_fmt, odbc_table, odbc_table); /* Safe */ \
4063  __sql; \
4064 })
4065 
4066 struct generic_prepare_struct {
4067  char *sql;
4068  int argc;
4069  char **argv;
4070 };
4071 
4072 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
4073 {
4074  struct generic_prepare_struct *gps = data;
4075  int res, i;
4076  SQLHSTMT stmt;
4077 
4078  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4079  if (!SQL_SUCCEEDED(res)) {
4080  ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
4081  return NULL;
4082  }
4083  res = ast_odbc_prepare(obj, stmt, gps->sql);
4084  if (!SQL_SUCCEEDED(res)) {
4085  ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
4086  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4087  return NULL;
4088  }
4089  for (i = 0; i < gps->argc; i++)
4090  SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
4091 
4092  return stmt;
4093 }
4094 
4095 static void odbc_update_msg_id(char *dir, int msg_num, char *msg_id)
4096 {
4097  SQLHSTMT stmt;
4098  char *sql = MAKE_SQL_PTRA("UPDATE %s SET msg_id=? WHERE dir=? AND msgnum=?");
4099  struct odbc_obj *obj;
4100  char msg_num_str[20];
4101  char *argv[] = { msg_id, dir, msg_num_str };
4102  struct generic_prepare_struct gps = { .sql = sql, .argc = 3, .argv = argv };
4103  SCOPE_ENTER(3, "dir: %s msg_num: %d msg_id: %s\n", dir, msg_num, msg_id);
4104 
4105  obj = ast_odbc_request_obj(odbc_database, 0);
4106  if (!obj) {
4107  SCOPE_EXIT_LOG_RTN(LOG_WARNING, "Unable to update message ID for message %d in %s\n", msg_num, dir);
4108  }
4109 
4110  snprintf(msg_num_str, sizeof(msg_num_str), "%d", msg_num);
4111  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4112  if (!stmt) {
4113  ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4114  } else {
4115  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4116  }
4117  ast_odbc_release_obj(obj);
4118  SCOPE_EXIT_RTN();
4119 }
4120 
4121 #define AUDIO_ON_DISK_MAGIC "AUDMAGIC"
4122 #define AUDIO_ON_DISK_MAGIC_LEN 8
4123 
4124 static void odbc_update_set_audmagic(char *dir, int msg_num)
4125 {
4126  SQLHSTMT stmt;
4127  char *sql = MAKE_SQL_PTRA("UPDATE %s SET recording=? WHERE dir=? AND msgnum=?");
4128  struct odbc_obj *obj;
4129  SQLLEN datalen = AUDIO_ON_DISK_MAGIC_LEN;
4130  SQLLEN indlen = datalen;
4131  int res;
4132  char msg_num_str[20];
4133  SCOPE_ENTER(3, "dir: %s msg_num: %d\n", dir, msg_num);
4134 
4135  obj = ast_odbc_request_obj(odbc_database, 0);
4136  if (!obj) {
4137  SCOPE_EXIT_LOG_RTN(LOG_WARNING, "Unable to request obj for message %d in %s\n", msg_num, dir);
4138  }
4139 
4140  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4141  if (!SQL_SUCCEEDED(res)) {
4142  ast_odbc_release_obj(obj);
4143  SCOPE_EXIT_LOG_RTN(LOG_WARNING, "Unable to allocate stmt for message %d in %s\n", msg_num, dir);
4144  }
4145 
4146  snprintf(msg_num_str, sizeof(msg_num_str), "%d", msg_num);
4147 
4148  SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
4149  datalen, 0, (void *) AUDIO_ON_DISK_MAGIC,
4150  datalen, &indlen);
4151 
4152  SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
4153  strlen(dir), 0, (void *) dir, 0, NULL);
4154 
4155  SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
4156  strlen(msg_num_str), 0, (void *) msg_num_str, 0, NULL);
4157 
4158  res = ast_odbc_execute_sql(obj, stmt, sql);
4159  if (!SQL_SUCCEEDED(res)) {
4160  ast_log(LOG_WARNING, "Unable to execute stmt for message %d in %s\n", msg_num, dir);
4161  ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute");
4162  }
4163  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4164  stmt = NULL;
4165 
4166  ast_odbc_release_obj(obj);
4167  SCOPE_EXIT_RTN("Done\n");
4168 }
4169 
4170 static int odbc_store_message(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum);
4171 
4172 /*!
4173  * \brief Retrieves a file from an ODBC data store.
4174  * \param dir the path to the file to be retrieved.
4175  * \param msgnum the message number, such as within a mailbox folder.
4176  *
4177  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
4178  * 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.
4179  *
4180  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
4181  * The output is the message information file with the name msgnum and the extension .txt
4182  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
4183  *
4184  * \return 0 on success, -1 on error.
4185  */
4186 static int odbc_retrieve_message(char *dir, int msgnum)
4187 {
4188  int x = 0;
4189  int res;
4190  int fd = -1;
4191  size_t fdlen = 0;
4192  void *fdm = MAP_FAILED;
4193  SQLSMALLINT colcount = 0;
4194  SQLHSTMT stmt;
4195  char *sql = MAKE_SQL_PTRA("SELECT * FROM %s WHERE dir=? AND msgnum=?");
4196  char fmt[80] = "";
4197  char *c;
4198  char coltitle[256];
4199  SQLSMALLINT collen;
4200  SQLSMALLINT datatype;
4201  SQLSMALLINT decimaldigits;
4202  SQLSMALLINT nullable;
4203  SQLULEN colsize;
4204  SQLLEN colsize2;
4205  FILE *f = NULL;
4206  char rowdata[80];
4207  char *fn = MAKE_FILE_PTRA(dir, msgnum);
4208  char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
4209  char msgnums[80];
4210  char *mailboxuser = NULL;
4211  char *mailboxcontext = NULL;
4212  char msg_id[MSG_ID_LEN] = "";
4213  char *argv[] = { dir, msgnums };
4214  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4215  struct odbc_obj *obj;
4216  int storage_conversion_to_disk = 0;
4217  int storage_conversion_to_odbc = 0;
4218  SCOPE_ENTER(3, "dir: %s msgnum: %d msgtype: %s\n", dir, msgnum, msgnum < 0 ? "Greeting" : "Message");
4219 
4220  obj = ast_odbc_request_obj(odbc_database, 0);
4221  if (!obj) {
4222  SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4223  }
4224 
4225  ast_copy_string(fmt, vmfmts, sizeof(fmt));
4226  c = strchr(fmt, '|');
4227  if (c)
4228  *c = '\0';
4229  if (!strcasecmp(fmt, "wav49"))
4230  strcpy(fmt, "WAV");
4231 
4232  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4233 
4234  ast_trace(-1, "Opening '%s' for writing\n", full_fn);
4235  if (!(f = fopen(full_fn, "w+"))) {
4236  ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
4237  goto bail;
4238  }
4239 
4240  sprintf(full_fn, "%s.%s", fn, fmt); /* Safe. We're just replacing the file exten. */
4241 
4242  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4243  if (!stmt) {
4244  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4245  goto bail;
4246  }
4247 
4248  res = SQLFetch(stmt);
4249  if (!SQL_SUCCEEDED(res)) {
4250  if (res != SQL_NO_DATA) {
4251  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4252  }
4253  goto bail_with_handle;
4254  }
4255 
4256  res = SQLNumResultCols(stmt, &colcount);
4257  if (!SQL_SUCCEEDED(res)) {
4258  ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
4259  goto bail_with_handle;
4260  }
4261 
4262  fprintf(f, "[message]\n");
4263  for (x = 0; x < colcount; x++) {
4264  rowdata[0] = '\0';
4265  colsize = 0;
4266  collen = sizeof(coltitle);
4267  res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen,
4268  &datatype, &colsize, &decimaldigits, &nullable);
4269  if (!SQL_SUCCEEDED(res)) {
4270  ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
4271  goto bail_with_handle;
4272  }
4273 
4274  if (!strcasecmp(coltitle, "recording")) {
4275  off_t offset;
4276  char tmp[1] = "";
4277 
4278  res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
4279  fdlen = colsize2;
4280  ast_trace(-1, "Audio size: %ld\n", colsize2);
4281  if (colsize2 == AUDIO_ON_DISK_MAGIC_LEN) {
4282  res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, AUDIO_ON_DISK_MAGIC_LEN, NULL);
4283  if (memcmp(rowdata, AUDIO_ON_DISK_MAGIC, AUDIO_ON_DISK_MAGIC_LEN) != 0) {
4284  ast_log(AST_LOG_WARNING, "Invalid audio magic number '0x%02X%02X%02X%02X%02X%02X%02X%02X' for '%s'\n",
4285  rowdata[0], rowdata[1], rowdata[2], rowdata[3], rowdata[4], rowdata[5], rowdata[6],
4286  rowdata[7], full_fn);
4287  goto bail_with_handle;
4288  }
4289  ast_trace(-1, "Audio is stored on disk. No need to write '%s'\n", full_fn);
4290  if (!ast_test_flag((&globalflags), VM_ODBC_AUDIO_ON_DISK)) {
4291  storage_conversion_to_odbc = 1;
4292  }
4293 
4294  continue;
4295  }
4296 
4297  ast_trace(-1, "Opening '%s' for writing\n", full_fn);
4298  fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
4299  if (fd < 0) {
4300  ast_log(AST_LOG_WARNING, "Failed to open '%s' for writing: %s\n", full_fn, strerror(errno));
4301  goto bail_with_handle;
4302  }
4303  if (ast_test_flag((&globalflags), VM_ODBC_AUDIO_ON_DISK)) {
4304  storage_conversion_to_disk = 1;
4305  }
4306 
4307  lseek(fd, fdlen - 1, SEEK_SET);
4308  if (write(fd, tmp, 1) != 1) {
4309  close(fd);
4310  fd = -1;
4311  continue;
4312  }
4313  /* Read out in small chunks */
4314  for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
4315  if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
4316  ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
4317  goto bail_with_handle;
4318  }
4319  res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
4320  munmap(fdm, CHUNKSIZE);
4321  if (!SQL_SUCCEEDED(res)) {
4322  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4323  unlink(full_fn);
4324  goto bail_with_handle;
4325  }
4326  }
4327  if (truncate(full_fn, fdlen) < 0) {
4328  ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
4329  }
4330  ast_trace(-1, "Wrote %d bytes to '%s'\n", (int)fdlen, full_fn);
4331  } else {
4332  res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4333  if (res == SQL_NULL_DATA && !strcasecmp(coltitle, "msg_id")) {
4334  /*
4335  * Generate msg_id if there wasn't one, but don't store it until we're
4336  * done with this connection.
4337  */
4338  generate_msg_id(msg_id);
4339  ast_trace(-1, "msg_id was NULL. Generating new one: %s\n", msg_id);
4340  snprintf(rowdata, sizeof(rowdata), "%s", msg_id);
4341  } else if (!strcasecmp(coltitle, "mailboxuser")) {
4342  mailboxuser = ast_strdupa(rowdata);
4343  } else if (!strcasecmp(coltitle, "mailboxcontext")) {
4344  mailboxcontext = ast_strdupa(rowdata);
4345  } else if (res == SQL_NULL_DATA && !strcasecmp(coltitle, "category")) {
4346  /* Ignore null column value for category */
4347  ast_trace(-1, "Ignoring null category column in ODBC voicemail retrieve_file.\n");
4348  continue;
4349  } else if (!SQL_SUCCEEDED(res)) {
4350  ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
4351  goto bail_with_handle;
4352  }
4353  if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir")) {
4354  fprintf(f, "%s=%s\n", coltitle, rowdata);
4355  }
4356  }
4357  }
4358 
4359 bail_with_handle:
4360  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4361 
4362 bail:
4363  if (f)
4364  fclose(f);
4365  if (fd > -1)
4366  close(fd);
4367 
4368  ast_odbc_release_obj(obj);
4369 
4370  /* If res_odbc is configured to only allow a single database connection, we
4371  will deadlock if we try to do this before releasing the connection we
4372  were just using. */
4373  if (!ast_strlen_zero(msg_id)) {
4374  odbc_update_msg_id(dir, msgnum, msg_id);
4375  }
4376 
4377  if (SQL_SUCCEEDED(res)) {
4378  if (storage_conversion_to_disk) {
4379  /*
4380  * If we're currently storing audio on disk but there was pre-existing
4381  * audio in the database, we need to update the database to set the
4382  * 'recording' column to AUDIO_ON_DISK_MAGIC.
4383  */
4384  SCOPE_CALL(-1, odbc_update_set_audmagic, dir, msgnum);
4385  }
4386  if (storage_conversion_to_odbc) {
4387  /*
4388  * If we're currently storing audio in the database but there was
4389  * pre-existing audio on disk, we need to add the audio back
4390  * into the database overwriting the AUDIO_ON_DISK_MAGIC
4391  * magic number.
4392  */
4393  SCOPE_CALL(-1, odbc_store_message, dir, mailboxuser, mailboxcontext, msgnum);
4394  }
4395  }
4396 
4397  SCOPE_EXIT_RTN_VALUE(x - 1, "Done. msg_id: %s RC: %d\n", msg_id, x - 1);
4398 }
4399 
4400 /*!
4401  * \brief Determines the highest message number in use for a given user and mailbox folder.
4402  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
4403  *
4404  * This method is used when mailboxes are stored in an ODBC back end.
4405  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
4406  *
4407  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
4408  */
4409 static int odbc_last_message_index(char *dir)
4410 {
4411  int x = -1;
4412  int res;
4413  SQLHSTMT stmt;
4414  char *sql = MAKE_SQL_PTRA("SELECT msgnum FROM %s WHERE dir=? order by msgnum desc");
4415  char rowdata[20];
4416  char *argv[] = { dir };
4417  struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
4418  struct odbc_obj *obj;
4419  SCOPE_ENTER(3, "dir: %s\n", dir);
4420 
4421  obj = ast_odbc_request_obj(odbc_database, 0);
4422  if (!obj) {
4423  SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4424  }
4425 
4426  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4427  if (!stmt) {
4428  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4429  goto bail;
4430  }
4431 
4432  res = SQLFetch(stmt);
4433  if (!SQL_SUCCEEDED(res)) {
4434  if (res == SQL_NO_DATA) {
4435  ast_trace(-1, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
4436  } else {
4437  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4438  }
4439  goto bail_with_handle;
4440  }
4441 
4442  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4443  if (!SQL_SUCCEEDED(res)) {
4444  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4445  goto bail_with_handle;
4446  }
4447 
4448  if (sscanf(rowdata, "%30d", &x) != 1) {
4449  ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
4450  }
4451 
4452 bail_with_handle:
4453  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4454 
4455 bail:
4456  ast_odbc_release_obj(obj);
4457 
4458  SCOPE_EXIT_RTN_VALUE(x, "Done. Last message index: %d\n", x);
4459 }
4460 
4461 /*!
4462  * \brief Determines if the specified message exists.
4463  * \param dir the folder the mailbox folder to look for messages.
4464  * \param msgnum the message index to query for.
4465  *
4466  * This method is used when mailboxes are stored in an ODBC back end.
4467  *
4468  * \return greater than zero if the message exists, zero when the message does not exist or on error.
4469  */
4470 static int odbc_message_exists(char *dir, int msgnum)
4471 {
4472  int x = 0;
4473  int res;
4474  SQLHSTMT stmt;
4475  char *sql = MAKE_SQL_PTRA("SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?");
4476  char rowdata[20];
4477  char msgnums[20];
4478  char *argv[] = { dir, msgnums };
4479  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4480  struct odbc_obj *obj;
4481  SCOPE_ENTER(3, "dir: %s msgnum: %d\n", dir, msgnum);
4482 
4483  obj = ast_odbc_request_obj(odbc_database, 0);
4484  if (!obj) {
4485  SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4486  }
4487 
4488  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4489  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4490  if (!stmt) {
4491  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4492  goto bail;
4493  }
4494 
4495  res = SQLFetch(stmt);
4496  if (!SQL_SUCCEEDED(res)) {
4497  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4498  goto bail_with_handle;
4499  }
4500 
4501  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4502  if (!SQL_SUCCEEDED(res)) {
4503  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4504  goto bail_with_handle;
4505  }
4506 
4507  if (sscanf(rowdata, "%30d", &x) != 1) {
4508  ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
4509  }
4510 
4511 bail_with_handle:
4512  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4513 
4514 bail:
4515  ast_odbc_release_obj(obj);
4516  SCOPE_EXIT_RTN_VALUE(x, "Done. Msg %s\n", x ? "exists" : "does not exist");
4517 }
4518 
4519 /*!
4520  * \brief returns the number of messages found.
4521  * \param vmu
4522  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
4523  *
4524  * This method is used when mailboxes are stored in an ODBC back end.
4525  *
4526  * \return The count of messages being zero or more, less than zero on error.
4527  */
4528 static int odbc_count_messages(struct ast_vm_user *vmu, char *dir)
4529 {
4530  int x = -1;
4531  int res;
4532  SQLHSTMT stmt;
4533  char *sql = MAKE_SQL_PTRA("SELECT COUNT(*) FROM %s WHERE dir=?");
4534  char rowdata[20];
4535  char *argv[] = { dir };
4536  struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
4537  struct odbc_obj *obj;
4538  SCOPE_ENTER(3, "dir: %s\n", dir);
4539 
4540  obj = ast_odbc_request_obj(odbc_database, 0);
4541  if (!obj) {
4542  SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4543  }
4544 
4545  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4546  if (!stmt) {
4547  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4548  goto bail;
4549  }
4550 
4551  res = SQLFetch(stmt);
4552  if (!SQL_SUCCEEDED(res)) {
4553  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4554  goto bail_with_handle;
4555  }
4556 
4557  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4558  if (!SQL_SUCCEEDED(res)) {
4559  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4560  goto bail_with_handle;
4561  }
4562 
4563  if (sscanf(rowdata, "%30d", &x) != 1) {
4564  ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
4565  }
4566 
4567 bail_with_handle:
4568  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4569 
4570 bail:
4571  ast_odbc_release_obj(obj);
4572  SCOPE_EXIT_RTN_VALUE(x, "Done. Count %d\n", x);
4573 }
4574 
4575 /*!
4576  * \brief Deletes a message from the mailbox folder.
4577  * \param sdir The mailbox folder to work in.
4578  * \param smsg The message index to be deleted.
4579  *
4580  * This method is used when mailboxes are stored in an ODBC back end.
4581  * The specified message is directly deleted from the database 'voicemessages' table.
4582  */
4583 #define DELETE_SQL_FMT "DELETE FROM %s WHERE dir=? AND msgnum=?"
4584 static void odbc_delete_message(const char *sdir, int smsg)
4585 {
4586  SQLHSTMT stmt;
4587  char *sql = MAKE_SQL_PTRA(DELETE_SQL_FMT);
4588  char msgnums[20];
4589  char *argv[] = { NULL, msgnums };
4590  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4591  struct odbc_obj *obj;
4592  SCOPE_ENTER(3, "sdir: %s smsg: %d\n", sdir, smsg);
4593 
4594  obj = ast_odbc_request_obj(odbc_database, 0);
4595  if (!obj) {
4596  SCOPE_EXIT_LOG_RTN(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4597  }
4598 
4599  argv[0] = ast_strdupa(sdir);
4600 
4601  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4602  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4603  if (!stmt) {
4604  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4605  } else {
4606  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4607  }
4608  ast_odbc_release_obj(obj);
4609 
4610  if (ast_test_flag((&globalflags), VM_ODBC_AUDIO_ON_DISK)) {
4611  char *src_fn = MAKE_FILE_PTRA(sdir, smsg);
4612  ast_trace(-1, "Audio stored on disk. Deleting '%s'\n", src_fn);
4613  ast_filedelete(src_fn, NULL);
4614  }
4615 
4616  SCOPE_EXIT_RTN("Done\n");
4617 }
4618 
4619 /*!
4620  * \brief Copies a voicemail from one mailbox to another.
4621  * \param sdir the folder for which to look for the message to be copied.
4622  * \param smsg the index of the message to be copied.
4623  * \param ddir the destination folder to copy the message into.
4624  * \param dmsg the index to be used for the copied message.
4625  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
4626  * \param dmailboxcontext The context for the destination user.
4627  *
4628  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
4629  */
4630 #define COPY_SQL_FMT "INSERT INTO %s (dir, msgnum, msg_id, context, callerid, origtime, " \
4631  "duration, recording, flag, mailboxuser, mailboxcontext) " \
4632  "SELECT ?,?,msg_id,context,callerid,origtime,duration,recording,flag,?,? " \
4633  "FROM %s WHERE dir=? AND msgnum=?"
4634 static void odbc_copy_message(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
4635 {
4636  SQLHSTMT stmt;
4637  char *sql = MAKE_SQL_PTRA2(COPY_SQL_FMT);
4638  char msgnums[20];
4639  char msgnumd[20];
4640  struct odbc_obj *obj;
4641  char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
4642  struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
4643  SCOPE_ENTER(3, "sdir: %s smsg: %d duser: %s dcontext: %s ddir: %s dmsg: %d\n",
4644  sdir, smsg, dmailboxuser, dmailboxcontext, ddir, dmsg);
4645 
4646  SCOPE_CALL(-1, odbc_delete_message, ddir, dmsg);
4647 
4648  obj = ast_odbc_request_obj(odbc_database, 0);
4649  if (!obj) {
4650  SCOPE_EXIT_LOG_RTN(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4651  }
4652 
4653  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4654  snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
4655 
4656  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4657  if (!stmt)
4658  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
4659  else
4660  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4661  ast_odbc_release_obj(obj);
4662 
4663  if (ast_test_flag((&globalflags), VM_ODBC_AUDIO_ON_DISK)) {
4664  char *src_fn = MAKE_FILE_PTRA(sdir, smsg);
4665  char *dst_fn = MAKE_FILE_PTRA(ddir, dmsg);
4666 
4667  ast_trace(-1, "Audio stored on disk. Copying '%s' to '%s'\n", src_fn, dst_fn);
4668  ast_filecopy(src_fn, dst_fn, NULL);
4669  }
4670 
4671  SCOPE_EXIT_RTN("Done\n");
4672 }
4673 #undef COPY_SQL_FMT
4674 
4675 struct insert_data {
4676  const char *dir;
4677  const char *msgnums;
4678  void *data;
4679  SQLLEN datalen;
4680  SQLLEN indlen;
4681  const char *context;
4682  const char *callerid;
4683  const char *origtime;
4684  const char *duration;
4685  const char *mailboxuser;
4686  const char *mailboxcontext;
4687  const char *category;
4688  const char *flag;
4689  const char *msg_id;
4690 };
4691 
4692 #define STORE_SQL_FMT_CAT "INSERT INTO %s (dir, msgnum, recording, context, callerid, " \
4693  "origtime, duration, mailboxuser, mailboxcontext, flag, msg_id, category) " \
4694  "VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"
4695 #define STORE_SQL_FMT "INSERT INTO %s (dir, msgnum, recording, context, callerid, "\
4696  "origtime, duration, mailboxuser, mailboxcontext, flag, msg_id) "\
4697  "VALUES (?,?,?,?,?,?,?,?,?,?,?)"
4698 
4699 static SQLHSTMT odbc_insert_data_cb(struct odbc_obj *obj, void *vdata)
4700 {
4701  struct insert_data *data = vdata;
4702  char *insert_sql;
4703  char *delete_sql;
4704  int res;
4705  SQLHSTMT stmt;
4706  SCOPE_ENTER(3, "dir: %s msgnums: %s msg_id: %s\n", data->dir, data->msgnums,
4707  data->msg_id);
4708 
4709  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4710  if (!SQL_SUCCEEDED(res)) {
4711  SCOPE_EXIT_LOG_RTN_VALUE(NULL, AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
4712  }
4713 
4714  /* Delete any existing row with the same dir and msgnum */
4715  delete_sql = MAKE_SQL_PTRA(DELETE_SQL_FMT);
4716  SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
4717  SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
4718  res = ast_odbc_execute_sql(obj, stmt, delete_sql);
4719  if (!SQL_SUCCEEDED(res)) {
4720  ast_trace(-1, "There wasn't an existing row. Good.\n");
4721  } else {
4722  ast_trace(-1, "There WAS an existing row. This is OK if we're replacing a message.\n");
4723  }
4724  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4725  stmt = NULL;
4726 
4727  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4728  if (!SQL_SUCCEEDED(res)) {
4729  SCOPE_EXIT_LOG_RTN_VALUE(NULL, AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
4730  }
4731 
4732  SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
4733  SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
4734  SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
4735  SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
4736  SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
4737  SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
4738  SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
4739  SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
4740  SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
4741  SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
4742  SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msg_id), 0, (void *) data->msg_id, 0, NULL);
4743  if (!ast_strlen_zero(data->category)) {
4744  insert_sql = MAKE_SQL_PTRA(STORE_SQL_FMT_CAT);
4745  SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
4746  } else {
4747  insert_sql = MAKE_SQL_PTRA(STORE_SQL_FMT);
4748  }
4749  res = ast_odbc_execute_sql(obj, stmt, insert_sql);
4750  if (!SQL_SUCCEEDED(res)) {
4751  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n", insert_sql);
4752  ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute");
4753  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4754  stmt = NULL;
4755  }
4756 
4757  SCOPE_EXIT_RTN_VALUE(stmt, "%s\n", stmt ? "Success" : "Failed");
4758 }
4759 
4760 /*!
4761  * \brief Stores a voicemail into the database.
4762  * \param dir the folder the mailbox folder to store the message.
4763  * \param mailboxuser the user owning the mailbox folder.
4764  * \param mailboxcontext
4765  * \param msgnum the message index for the message to be stored.
4766  *
4767  * This method is used when mailboxes are stored in an ODBC back end.
4768  * The message sound file and information file is looked up on the file system.
4769  * A SQL query is invoked to store the message into the (MySQL) database.
4770  *
4771  * \return the zero on success -1 on error.
4772  */
4773 static int odbc_store_message(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
4774 {
4775  int res = 0;
4776  int fd = -1;
4777  void *fdm = MAP_FAILED;
4778  off_t fdlen = -1;
4779  SQLHSTMT stmt;
4780  char msgnums[20];
4781  char *fn = MAKE_FILE_PTRA(dir, msgnum);
4782  char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
4783  char fmt[80]="";
4784  char *c;
4785  struct ast_config *cfg = NULL;
4786  struct odbc_obj *obj;
4787  struct insert_data idata = { .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
4788  .context = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "", .msg_id = "" };
4789  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
4790  SCOPE_ENTER(3, "dir: %s user: %s context: %s msgnum: %d msgtype: %s\n",
4791  dir, mailboxuser, mailboxcontext, msgnum, msgnum < 0 ? "Greeting" : "Message");
4792 
4793  obj = ast_odbc_request_obj(odbc_database, 0);
4794  if (!obj) {
4795  SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4796  }
4797 
4798  do {
4799  ast_copy_string(fmt, vmfmts, sizeof(fmt));
4800  c = strchr(fmt, '|');
4801  if (c)
4802  *c = '\0';
4803  if (!strcasecmp(fmt, "wav49"))
4804  strcpy(fmt, "WAV");
4805 
4806  ast_trace(-1, "Formats: %s Using format: '%s'\n", vmfmts, fmt);
4807  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4808 
4809  ast_trace(-1, "Base path: '%s'\n", fn);
4810  ast_trace(-1, "Opening '%s'\n", full_fn);
4811  cfg = ast_config_load(full_fn, config_flags);
4812  if (!valid_config(cfg)) {
4813  if (msgnum < 0) {
4814  ast_trace(-1, "No information file found for '%s'. This is a greeting so this is OK.\n", full_fn);
4815  } else {
4816  ast_log(AST_LOG_WARNING, "Failed to open '%s'\n", full_fn);
4817  res = -1;
4818  break;
4819  }
4820  }
4821 
4822  sprintf(full_fn, "%s.%s", fn, fmt); /* Safe */
4823  if (ast_test_flag((&globalflags), VM_ODBC_AUDIO_ON_DISK)) {
4824  ast_trace(-1, "Audio stored on disk. No need to open '%s'\n", full_fn);
4825  } else {
4826  ast_trace(-1, "Opening '%s'\n", full_fn);
4827  fd = open(full_fn, O_RDWR);
4828  if (fd < 0) {
4829  ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
4830  res = -1;
4831  break;
4832  }
4833  }
4834 
4835  if (valid_config(cfg)) {
4836  ast_trace(-1, "Using information file '%s'\n", fn);
4837  if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
4838  idata.context = "";
4839  }
4840  if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
4841  idata.callerid = "";
4842  }
4843  if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
4844  idata.origtime = "";
4845  }
4846  if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
4847  idata.duration = "";
4848  }
4849  if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
4850  idata.category = "";
4851  }
4852  if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
4853  idata.flag = "";
4854  }
4855  if (!(idata.msg_id = ast_variable_retrieve(cfg, "message", "msg_id"))) {
4856  idata.msg_id = "";
4857  }
4858  }
4859 
4860  if (fd < 0) {
4861  ast_trace(-1, "Audio stored on disk. Not reading sound file '%s' but setting magic number.\n", full_fn);
4862  idata.data = AUDIO_ON_DISK_MAGIC;
4863  idata.datalen = idata.indlen = AUDIO_ON_DISK_MAGIC_LEN;
4864  } else {
4865  ast_trace(-1, "Reading sound file '%s'\n", full_fn);
4866  fdlen = lseek(fd, 0, SEEK_END);
4867  if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
4868  ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
4869  res = -1;
4870  break;
4871  }
4872  fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
4873  if (fdm == MAP_FAILED) {
4874  ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
4875  res = -1;
4876  break;
4877  }
4878  idata.data = fdm;
4879  idata.datalen = idata.indlen = fdlen;
4880  }
4881 
4882  if (ast_strlen_zero(idata.origtime)) {
4883  idata.origtime = "0";
4884  }
4885 
4886  if (ast_strlen_zero(idata.duration)) {
4887  idata.duration = "0";
4888  }
4889 
4890  if ((stmt = ast_odbc_direct_execute(obj, odbc_insert_data_cb, &idata))) {
4891  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4892  } else {
4893  res = -1;
4894  }
4895  } while (0);
4896 
4897  ast_odbc_release_obj(obj);
4898 
4899  if (valid_config(cfg))
4900  ast_config_destroy(cfg);
4901  if (fdm != MAP_FAILED)
4902  munmap(fdm, fdlen);
4903  if (fd > -1)
4904  close(fd);
4905  SCOPE_EXIT_RTN_VALUE(res, "%s\n", res ? "Failed" : "Success");
4906 }
4907 #undef STORE_SQL_FMT
4908 #undef STORE_SQL_FMT_CAT
4909 
4910 /*!
4911  * \brief Renames a message in a mailbox folder.
4912  * \param sdir The folder of the message to be renamed.
4913  * \param smsg The index of the message to be renamed.
4914  * \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.
4915  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
4916  * \param ddir The destination folder for the message to be renamed into
4917  * \param dmsg The destination message for the message to be renamed.
4918  *
4919  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
4920  * 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.
4921  * 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.
4922  */
4923 static void odbc_rename_message(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
4924 {
4925  SQLHSTMT stmt;
4926  char *sql = MAKE_SQL_PTRA("UPDATE %s SET dir=?, msgnum=? WHERE mailboxuser=? AND mailboxcontext=? AND dir=? AND msgnum=?");
4927  char msgnums[20];
4928  char msgnumd[20];
4929  struct odbc_obj *obj;
4930  char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
4931  struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
4932  SCOPE_ENTER(3, "sdir: %s smsg: %d user: %s context: %s ddir: %s dmsg: %d\n", sdir, smsg,
4933  mailboxuser, mailboxcontext, ddir, dmsg);
4934 
4935  SCOPE_CALL(-1, odbc_delete_message, ddir, dmsg);
4936 
4937  obj = ast_odbc_request_obj(odbc_database, 0);
4938  if (!obj) {
4939  SCOPE_EXIT_LOG_RTN(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4940  }
4941 
4942  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4943  snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
4944 
4945  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
4946  if (!stmt)
4947  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4948  else
4949  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4950  ast_odbc_release_obj(obj);
4951 
4952  if (ast_test_flag((&globalflags), VM_ODBC_AUDIO_ON_DISK)) {
4953  char *src_fn = MAKE_FILE_PTRA(sdir, smsg);
4954  char *dst_fn = MAKE_FILE_PTRA(ddir, dmsg);
4955 
4956  ast_trace(-1, "Recordings stored on disk. Renaming '%s' to '%s'\n", src_fn, dst_fn);
4957  ast_filerename(src_fn, dst_fn, NULL);
4958  }
4959 
4960  SCOPE_EXIT_RTN("Done.\n");
4961 }
4962 
4963 /*!
4964  * \brief Removes a voicemail message file.
4965  * \param dir the path to the message file.
4966  * \param msgnum the unique number for the message within the mailbox.
4967  *
4968  * Removes the message content file and the information file.
4969  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
4970  * Typical use is to clean up after a RETRIEVE operation.
4971  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
4972  * \return zero on success, -1 on error.
4973  */
4974 static int odbc_remove_files(char *dir, int msgnum)
4975 {
4976  char *fn = MAKE_FILE_PTRA(dir, msgnum);
4977  char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
4978  SCOPE_ENTER(3, "dir: %s msgnum: %d\n", dir, msgnum);
4979 
4980  if (ast_test_flag((&globalflags), VM_ODBC_AUDIO_ON_DISK)) {
4981  ast_trace(-1, "Audio stored on disk. Keeping '%s' sound files\n", fn);
4982  } else {
4983  ast_trace(-1, "Audio stored in ODBC. Removing '%s' sound files\n", fn);
4984  ast_filedelete(fn, NULL);
4985  }
4986 
4987  /* Always remove the information file */
4988  ast_trace(-1, "Removing '%s' information file\n", full_fn);
4989  unlink(full_fn);
4990  SCOPE_EXIT_RTN_VALUE(0, "Done.\n");
4991 }
4992 #else
4993 #ifndef IMAP_STORAGE
4994 /*!
4995  * \brief Find all .txt files - even if they are not in sequence from 0000.
4996  * \param vmu
4997  * \param dir
4998  *
4999  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
5000  *
5001  * \return the count of messages, zero or more.
5002  */
5003 static int count_messages(struct ast_vm_user *vmu, char *dir)
5004 {
5005 
5006  int vmcount = 0;
5007  DIR *vmdir = NULL;
5008  struct dirent *vment = NULL;
5009 
5010  if (vm_lock_path(dir))
5011  return ERROR_LOCK_PATH;
5012 
5013  if ((vmdir = opendir(dir))) {
5014  while ((vment = readdir(vmdir))) {
5015  if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
5016  vmcount++;
5017  }
5018  }
5019  closedir(vmdir);
5020  }
5021  ast_unlock_path(dir);
5022 
5023  return vmcount;
5024 }
5025 
5026 /*!
5027  * \brief Renames a message in a mailbox folder.
5028  * \param sfn The path to the mailbox information and data file to be renamed.
5029  * \param dfn The path for where the message data and information files will be renamed to.
5030  *
5031  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
5032  */
5033 static void rename_file(char *sfn, char *dfn)
5034 {
5035  char stxt[PATH_MAX];
5036  char dtxt[PATH_MAX];
5037  ast_filerename(sfn, dfn, NULL);
5038  snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
5039  snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
5040  if (ast_check_realtime("voicemail_data")) {
5041  ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
5042  }
5043  rename(stxt, dtxt);
5044 }
5045 
5046 /*!
5047  * \brief Determines the highest message number in use for a given user and mailbox folder.
5048  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
5049  *
5050  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
5051  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
5052  *
5053  * \note Should always be called with a lock already set on dir.
5054  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
5055  */
5056 static int last_message_index(char *dir)
5057 {
5058  int x;
5059  unsigned char map[MAXMSGLIMIT] = "";
5060  DIR *msgdir;
5061  struct dirent *msgdirent;
5062  int msgdirint;
5063  char extension[4];
5064  int stopcount = 0;
5065 
5066  /* Reading the entire directory into a file map scales better than
5067  * doing a stat repeatedly on a predicted sequence. I suspect this
5068  * is partially due to stat(2) internally doing a readdir(2) itself to
5069  * find each file. */
5070  if (!(msgdir = opendir(dir))) {
5071  return -1;
5072  }
5073 
5074  while ((msgdirent = readdir(msgdir))) {
5075  if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
5076  map[msgdirint] = 1;
5077  stopcount++;
5078  ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
5079  }
5080  }
5081  closedir(msgdir);
5082 
5083  for (x = 0; x < MAXMSGLIMIT && stopcount; x++) {
5084  stopcount -= map[x];
5085  }
5086 
5087  return x - 1;
5088 }
5089 
5090 #endif /* #ifndef IMAP_STORAGE */
5091 #endif /* #else of #ifdef ODBC_STORAGE */
5092 #ifndef IMAP_STORAGE
5093 /*!
5094  * \brief Utility function to copy a file.
5095  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
5096  * \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.
5097  *
5098  * 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.
5099  * The copy operation copies up to 4096 bytes at once.
5100  *
5101  * \return zero on success, -1 on error.
5102  */
5103 static int copy(char *infile, char *outfile)
5104 {
5105  int ifd;
5106  int ofd;
5107  int res = -1;
5108  int len;
5109  char buf[4096];
5110 
5111 #ifdef HARDLINK_WHEN_POSSIBLE
5112  /* Hard link if possible; saves disk space & is faster */
5113  if (!link(infile, outfile)) {
5114  return 0;
5115  }
5116 #endif
5117 
5118  if ((ifd = open(infile, O_RDONLY)) < 0) {
5119  ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
5120  return -1;
5121  }
5122 
5123  if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
5124  ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
5125  close(ifd);
5126  return -1;
5127  }
5128 
5129  for (;;) {
5130  int wrlen;
5131 
5132  len = read(ifd, buf, sizeof(buf));
5133  if (!len) {
5134  res = 0;
5135  break;
5136  }
5137 
5138  if (len < 0) {
5139  ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
5140  break;
5141  }
5142 
5143  wrlen = write(ofd, buf, len);
5144  if (errno == ENOMEM || errno == ENOSPC || wrlen != len) {
5145  ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, wrlen, len, strerror(errno));
5146  break;
5147  }
5148  }
5149 
5150  close(ifd);
5151  close(ofd);
5152  if (res) {
5153  unlink(outfile);
5154  }
5155 
5156  return res;
5157 }
5158 
5159 /*!
5160  * \brief Copies a voicemail information (envelope) file.
5161  * \param frompath
5162  * \param topath
5163  *
5164  * Every voicemail has the data (.wav) file, and the information file.
5165  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
5166  * This is used by the COPY macro when not using IMAP storage.
5167  */
5168 static void copy_plain_file(char *frompath, char *topath)
5169 {
5170  char frompath2[PATH_MAX], topath2[PATH_MAX];
5171  struct ast_variable *tmp, *var = NULL;
5172  const char *origmailbox = "", *context = "", *exten = "";
5173  const char *priority = "", *callerchan = "", *callerid = "", *origdate = "";
5174  const char *origtime = "", *category = "", *duration = "";
5175 
5176  ast_filecopy(frompath, topath, NULL);
5177  snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
5178  snprintf(topath2, sizeof(topath2), "%s.txt", topath);
5179 
5180  if (ast_check_realtime("voicemail_data")) {
5181  var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
5182  /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
5183  for (tmp = var; tmp; tmp = tmp->next) {
5184  if (!strcasecmp(tmp->name, "origmailbox")) {
5185  origmailbox = tmp->value;
5186  } else if (!strcasecmp(tmp->name, "context")) {
5187  context = tmp->value;
5188  } else if (!strcasecmp(tmp->name, "exten")) {
5189  exten = tmp->value;
5190  } else if (!strcasecmp(tmp->name, "priority")) {
5191  priority = tmp->value;
5192  } else if (!strcasecmp(tmp->name, "callerchan")) {
5193  callerchan = tmp->value;
5194  } else if (!strcasecmp(tmp->name, "callerid")) {
5195  callerid = tmp->value;
5196  } else if (!strcasecmp(tmp->name, "origdate")) {
5197  origdate = tmp->value;
5198  } else if (!strcasecmp(tmp->name, "origtime")) {
5199  origtime = tmp->value;
5200  } else if (!strcasecmp(tmp->name, "category")) {
5201  category = tmp->value;
5202  } else if (!strcasecmp(tmp->name, "duration")) {
5203  duration = tmp->value;
5204  }
5205  }
5206  ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, SENTINEL);
5207  }
5208  copy(frompath2, topath2);
5209  ast_variables_destroy(var);
5210 }
5211 #endif
5212 
5213 /*!
5214  * \brief Removes the voicemail sound and information file.
5215  * \param file The path to the sound file. This will be the folder and message index, without the extension.
5216  *
5217  * This is used by the DELETE macro when voicemails are stored on the file system.
5218  *
5219  * \return zero on success, -1 on error.
5220  */
5221 static int vm_delete(char *file)
5222 {
5223  char *txt;
5224  int txtsize = 0;
5225  int res = 0;
5226  SCOPE_ENTER(3, "file: %s\n", file);
5227 
5228  txtsize = (strlen(file) + 5)*sizeof(char);
5229  txt = ast_alloca(txtsize);
5230  /* Sprintf here would safe because we alloca'd exactly the right length,
5231  * but trying to eliminate all sprintf's anyhow
5232  */
5233  if (ast_check_realtime("voicemail_data")) {
5234  ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
5235  }
5236  snprintf(txt, txtsize, "%s.txt", file);
5237  ast_trace(-1, "unlinking '%s'\n", txt);
5238  unlink(txt);
5239  ast_trace(-1, "deleting sound files '%s'\n", file);
5240  res = ast_filedelete(file, NULL);
5241  SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d\n", res);
5242 }
5243 
5244 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)
5245 {
5246  char callerid[256];
5247  char num[12];
5248  char fromdir[256], fromfile[256];
5249  struct ast_config *msg_cfg;
5250  const char *origcallerid, *origtime;
5251  char origcidname[80], origcidnum[80], origdate[80];
5252  int inttime;
5253  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
5254 
5255  /* Prepare variables for substitution in email body and subject */
5256  pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
5257  pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
5258  snprintf(num, sizeof(num), "%d", msgnum);
5259  pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
5260  pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
5261  pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
5262  pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
5263  ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
5264  pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
5265  pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
5266  pbx_builtin_setvar_helper(ast, "VM_DATE", date);
5267  pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
5268  pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
5269 
5270  /* Retrieve info from VM attribute file */
5271  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
5272  make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
5273  if (strlen(fromfile) < sizeof(fromfile) - 5) {
5274  strcat(fromfile, ".txt");
5275  }
5276  if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
5277  ast_debug(1, "Config load for message text file '%s' failed\n", fromfile);
5278  return;
5279  }
5280 
5281  if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
5282  pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
5283  ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
5284  pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
5285  pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
5286  }
5287 
5288  if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
5289  struct timeval tv = { inttime, };
5290  struct ast_tm tm;
5291  ast_localtime(&tv, &tm, NULL);
5292  ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5293  pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
5294  }
5295  ast_config_destroy(msg_cfg);
5296 }
5297 
5298 /*!
5299  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
5300  * \param from The string to work with.
5301  * \param buf The buffer into which to write the modified quoted string.
5302  * \param maxlen Always zero, but see \see ast_str
5303  *
5304  * \return The destination string with quotes wrapped on it (the to field).
5305  */
5306 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
5307 {
5308  const char *ptr;
5309 
5310  /* We're only ever passing 0 to maxlen, so short output isn't possible */
5311  ast_str_set(buf, maxlen, "\"");
5312  for (ptr = from; *ptr; ptr++) {
5313  if (*ptr == '"' || *ptr == '\\') {
5314  ast_str_append(buf, maxlen, "\\%c", *ptr);
5315  } else {
5316  ast_str_append(buf, maxlen, "%c", *ptr);
5317  }
5318  }
5319  ast_str_append(buf, maxlen, "\"");
5320 
5321  return ast_str_buffer(*buf);
5322 }
5323 
5324 /*! \brief
5325  * fill in *tm for current time according to the proper timezone, if any.
5326  * \return tm so it can be used as a function argument.
5327  */
5328 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
5329 {
5330  const struct vm_zone *z = NULL;
5331  struct timeval t = ast_tvnow();
5332 
5333  /* Does this user have a timezone specified? */
5334  if (!ast_strlen_zero(vmu->zonetag)) {
5335  /* Find the zone in the list */
5336  AST_LIST_LOCK(&zones);
5337  AST_LIST_TRAVERSE(&zones, z, list) {
5338  if (!strcmp(z->name, vmu->zonetag))
5339  break;
5340  }
5342  }
5343  ast_localtime(&t, tm, z ? z->timezone : NULL);
5344  return tm;
5345 }
5346 
5347 /*!\brief Check if the string would need encoding within the MIME standard, to
5348  * avoid confusing certain mail software that expects messages to be 7-bit
5349  * clean.
5350  */
5351 static int check_mime(const char *str)
5352 {
5353  for (; *str; str++) {
5354  if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
5355  return 1;
5356  }
5357  }
5358  return 0;
5359 }
5360 
5361 /*!\brief Encode a string according to the MIME rules for encoding strings
5362  * that are not 7-bit clean or contain control characters.
5363  *
5364  * Additionally, if the encoded string would exceed the MIME limit of 76
5365  * characters per line, then the encoding will be broken up into multiple
5366  * sections, separated by a space character, in order to facilitate
5367  * breaking up the associated header across multiple lines.
5368  *
5369  * \param end An expandable buffer for holding the result
5370  * \param maxlen Always zero, but see \see ast_str
5371  * \param start A string to be encoded
5372  * \param preamble The length of the first line already used for this string,
5373  * to ensure that each line maintains a maximum length of 76 chars.
5374  * \param postamble the length of any additional characters appended to the
5375  * line, used to ensure proper field wrapping.
5376  * \retval The encoded string.
5377  */
5378 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
5379 {
5380  struct ast_str *tmp = ast_str_alloca(80);
5381  int first_section = 1;
5382 
5383  ast_str_reset(*end);
5384  ast_str_set(&tmp, -1, "=?%s?Q?", charset);
5385  for (; *start; start++) {
5386  int need_encoding = 0;
5387  if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
5388  need_encoding = 1;
5389  }
5390  if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
5391  (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
5392  (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
5393  (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
5394  /* Start new line */
5395  ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
5396  ast_str_set(&tmp, -1, "=?%s?Q?", charset);
5397  first_section = 0;
5398  }
5399  if (need_encoding && *start == ' ') {
5400  ast_str_append(&tmp, -1, "_");
5401  } else if (need_encoding) {
5402  ast_str_append(&tmp, -1, "=%hhX", *start);
5403  } else {
5404  ast_str_append(&tmp, -1, "%c", *start);
5405  }
5406  }
5407  ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
5408  return ast_str_buffer(*end);
5409 }
5410 
5411 /*!
5412  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
5413  * \param p The output file to generate the email contents into.
5414  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
5415  * \param vmu The voicemail user who is sending the voicemail.
5416  * \param msgnum The message index in the mailbox folder.
5417  * \param context
5418  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
5419  * \param fromfolder
5420  * \param cidnum The caller ID number.
5421  * \param cidname The caller ID name.
5422  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
5423  * \param attach2
5424  * \param format The message sound file format. i.e. .wav
5425  * \param duration The time of the message content, in seconds.
5426  * \param attach_user_voicemail if 1, the sound file is attached to the email.
5427  * \param chan
5428  * \param category
5429  * \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.
5430  * \param flag, msg_id
5431  *
5432  * The email body, and base 64 encoded attachment (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.
5433  */
5434 static void make_email_file(FILE *p,
5435  char *srcemail,
5436  struct ast_vm_user *vmu,
5437  int msgnum,
5438  char *context,
5439  char *mailbox,
5440  const char *fromfolder,
5441  char *cidnum,
5442  char *cidname,
5443  char *attach,
5444  char *attach2,
5445  char *format,
5446  int duration,
5447  int attach_user_voicemail,
5448  struct ast_channel *chan,
5449  const char *category,
5450  int imap,
5451  const char *flag,
5452  const char *msg_id)
5453 {
5454  char date[256];
5455  char host[MAXHOSTNAMELEN] = "";
5456  char who[256];
5457  char bound[256];
5458  char dur[256];
5459  struct ast_tm tm;
5460  char enc_cidnum[256] = "", enc_cidname[256] = "";
5461  struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
5462  char *greeting_attachment;
5463  char filename[256];
5464  int first_line;
5465  char *emailsbuf;
5466  char *email;
5467 
5468  if (!str1 || !str2) {
5469  ast_free(str1);
5470  ast_free(str2);
5471  return;
5472  }
5473 
5474  if (cidnum) {
5475  strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
5476  }
5477  if (cidname) {
5478  strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
5479  }
5480  gethostname(host, sizeof(host) - 1);
5481 
5482  if (strchr(srcemail, '@')) {
5483  ast_copy_string(who, srcemail, sizeof(who));
5484  } else {
5485  snprintf(who, sizeof(who), "%s@%s", srcemail, host);
5486  }
5487 
5488  greeting_attachment = strrchr(ast_strdupa(attach), '/');
5489  if (greeting_attachment) {
5490  *greeting_attachment++ = '\0';
5491  }
5492 
5493  snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
5494  ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
5495  fprintf(p, "Date: %s" ENDL, date);
5496 
5497  /* Set date format for voicemail mail */
5498  ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5499 
5500  if (!ast_strlen_zero(fromstring) || !ast_strlen_zero(vmu->fromstring)) {
5501  struct ast_channel *ast;
5502  char *e_fromstring = !ast_strlen_zero(vmu->fromstring) ? vmu->fromstring : fromstring;
5503  if ((ast = ast_dummy_channel_alloc())) {
5504  char *ptr;
5505  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
5506  ast_str_substitute_variables(&str1, 0, ast, e_fromstring);
5507 
5508  if (check_mime(ast_str_buffer(str1))) {
5509  first_line = 1;
5510  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
5511  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5512  *ptr = '\0';
5513  fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
5514  first_line = 0;
5515  /* Substring is smaller, so this will never grow */
5516  ast_str_set(&str2, 0, "%s", ptr + 1);
5517  }
5518  fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
5519  } else {
5520  fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
5521  }
5522  ast = ast_channel_unref(ast);
5523  } else {
5524  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5525  }
5526  } else {
5527  fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
5528  }
5529 
5530  emailsbuf = ast_strdupa(vmu->email);
5531  fprintf(p, "To:");
5532  first_line = 1;
5533  while ((email = strsep(&emailsbuf, "|"))) {
5534  char *next = emailsbuf;
5535  if (check_mime(vmu->fullname)) {
5536  char *ptr;
5537  ast_str_encode_mime(&str2, 0, vmu->fullname, first_line ? strlen("To: ") : 0, strlen(email) + 3 + (next ? strlen(",") : 0));
5538  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5539  *ptr = '\0';
5540  fprintf(p, " %s" ENDL, ast_str_buffer(str2));
5541  /* Substring is smaller, so this will never grow */
5542  ast_str_set(&str2, 0, "%s", ptr + 1);
5543  }
5544  fprintf(p, " %s <%s>%s" ENDL, ast_str_buffer(str2), email, next ? "," : "");
5545  } else {
5546  fprintf(p, " %s <%s>%s" ENDL, ast_str_quote(&str2, 0, vmu->fullname), email, next ? "," : "");
5547  }
5548  first_line = 0;
5549  }
5550 
5551  if (msgnum <= -1) {
5552  fprintf(p, "Subject: New greeting '%s' on %s." ENDL, greeting_attachment, date);
5553  } else if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
5554  char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
5555  struct ast_channel *ast;
5556  if ((ast = ast_dummy_channel_alloc())) {
5557  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5558  ast_str_substitute_variables(&str1, 0, ast, e_subj);
5559  if (check_mime(ast_str_buffer(str1))) {
5560  char *ptr;
5561  first_line = 1;
5562  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
5563  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5564  *ptr = '\0';
5565  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5566  first_line = 0;
5567  /* Substring is smaller, so this will never grow */
5568  ast_str_set(&str2, 0, "%s", ptr + 1);
5569  }
5570  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5571  } else {
5572  fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
5573  }
5574  ast = ast_channel_unref(ast);
5575  } else {
5576  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5577  }
5578  } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
5579  if (ast_strlen_zero(flag)) {
5580  fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
5581  } else {
5582  fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
5583  }
5584  } else {
5585  if (ast_strlen_zero(flag)) {
5586  fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
5587  } else {
5588  fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
5589  }
5590  }
5591 
5592  fprintf(p, "Message-ID: <Asterisk-%d-%u-%s-%d@%s>" ENDL, msgnum + 1,
5593  (unsigned int) ast_random(), mailbox, (int) getpid(), host);
5594  if (imap) {
5595  /* additional information needed for IMAP searching */
5596  fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
5597  /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
5598  fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
5599  fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
5600 #ifdef IMAP_STORAGE
5601  fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
5602 #else
5603  fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
5604 #endif
5605  /* flag added for Urgent */
5606  fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, S_OR(flag, ""));
5607  fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan ? ast_channel_priority(chan) : 0);
5608  fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
5609  fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
5610  fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
5611  if (!ast_strlen_zero(category)) {
5612  fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
5613  } else {
5614  fprintf(p, "X-Asterisk-VM-Category: " ENDL);
5615  }
5616  fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
5617  fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
5618  fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
5619  fprintf(p, "X-Asterisk-VM-Message-ID: %s" ENDL, msg_id);
5620  }
5621  if (!ast_strlen_zero(cidnum)) {
5622  fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
5623  }
5624  if (!ast_strlen_zero(cidname)) {
5625  fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
5626  }
5627  fprintf(p, "MIME-Version: 1.0" ENDL);
5628  if (attach_user_voicemail) {
5629  /* Something unique. */
5630  snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%u", msgnum + 1, mailbox,
5631  (int) getpid(), (unsigned int) ast_random());
5632 
5633  fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
5634  fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
5635  fprintf(p, "--%s" ENDL, bound);
5636  }
5637  fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
5638  if (msgnum <= -1) {
5639  fprintf(p, "This message is to let you know that your greeting '%s' was changed on %s." ENDL
5640  "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL,
5641  greeting_attachment, date);
5642  } else if (emailbody || vmu->emailbody) {
5643  char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
5644  struct ast_channel *ast;
5645  if ((ast = ast_dummy_channel_alloc())) {
5646  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5647  ast_str_substitute_variables(&str1, 0, ast, e_body);
5648 #ifdef IMAP_STORAGE
5649  {
5650  /* Convert body to native line terminators for IMAP backend */
5651  char *line = ast_str_buffer(str1), *next;
5652  do {
5653  /* Terminate line before outputting it to the file */
5654  if ((next = strchr(line, '\n'))) {
5655  *next++ = '\0';
5656  }
5657  fprintf(p, "%s" ENDL, line);
5658  line = next;
5659  } while (!ast_strlen_zero(line));
5660  }
5661 #else
5662  fprintf(p, "%s" ENDL, ast_str_buffer(str1));
5663 #endif
5664  ast = ast_channel_unref(ast);
5665  } else {
5666  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5667  }
5668  } else {
5669  if (strcmp(vmu->mailbox, mailbox)) {
5670  /* Forwarded type */
5671  struct ast_config *msg_cfg;
5672  const char *v;
5673  int inttime;
5674  char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
5675  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
5676  /* Retrieve info from VM attribute file */
5677  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
5678  make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
5679  if (strlen(fromfile) < sizeof(fromfile) - 5) {
5680  strcat(fromfile, ".txt");
5681  }
5682  if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
5683  if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
5684  ast_copy_string(origcallerid, v, sizeof(origcallerid));
5685  }
5686 
5687  /* You might be tempted to do origdate, except that a) it's in the wrong
5688  * format, and b) it's missing for IMAP recordings. */
5689  if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
5690  struct timeval tv = { inttime, };
5691  struct ast_tm tm;
5692  ast_localtime(&tv, &tm, NULL);
5693  ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5694  }
5695  fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
5696  " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
5697  "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
5698  " chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
5699  msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
5700  date, origcallerid, origdate);
5701  ast_config_destroy(msg_cfg);
5702  } else {
5703  goto plain_message;
5704  }
5705  } else {
5706 plain_message:
5707  fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
5708  "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
5709  "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
5710  ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
5711  (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
5712  }
5713  }
5714 
5715  if (imap || attach_user_voicemail) {
5716  if (!ast_strlen_zero(attach2)) {
5717  snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
5718  ast_debug(5, "creating second attachment filename %s\n", filename);
5719  add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
5720  snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
5721  ast_debug(5, "creating attachment filename %s\n", filename);
5722  add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
5723  } else {
5724  snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
5725  ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
5726  add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
5727  }
5728  }
5729  ast_free(str1);
5730  ast_free(str2);
5731 }
5732 
5733 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)
5734 {
5735  char fname[PATH_MAX] = "";
5736  char sox_gain_tmpdir[PATH_MAX];
5737  char *file_to_delete = NULL, *dir_to_delete = NULL;
5738  int res;
5739  char altfname[PATH_MAX] = "";
5740  int altused = 0;
5741  char altformat[80] = "";
5742  char *c = NULL;
5743 
5744  /* Eww. We want formats to tell us their own MIME type */
5745  char *mime_type = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
5746 
5747  /* Users of multiple file formats need special attention. */
5748  snprintf(fname, sizeof(fname), "%s.%s", attach, format);
5749  if (!ast_file_is_readable(fname)) {
5750  ast_copy_string(altformat, vmfmts, sizeof(altformat));
5751  c = strchr(altformat, '|');
5752  if (c) {
5753  *c = '\0';
5754  }
5755  ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s - trying first/alternate format %s\n", fname, strerror(errno), altformat);
5756  snprintf(altfname, sizeof(altfname), "%s.%s", attach, altformat);
5757  if (!ast_file_is_readable(altfname)) {
5758  ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s - alternate format %s failure\n", altfname, strerror(errno), altformat);
5759  } else {
5760  altused = 1;
5761  }
5762  }
5763 
5764  /* This 'while' loop will only execute once. We use it so that we can 'break' */
5765  while (vmu->volgain < -.001 || vmu->volgain > .001 || altused) {
5766  char tmpdir[PATH_MAX];
5767 
5768  create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
5769 
5770  res = snprintf(sox_gain_tmpdir, sizeof(sox_gain_tmpdir), "%s/vm-gain-XXXXXX", tmpdir);
5771  if (res >= sizeof(sox_gain_tmpdir)) {
5772  ast_log(LOG_ERROR, "Failed to create temporary directory path %s: Out of buffer space\n", tmpdir);
5773  break;
5774  }
5775 
5776  if (mkdtemp(sox_gain_tmpdir)) {
5777  int soxstatus = 0;
5778  char sox_gain_cmd[PATH_MAX];
5779 
5780  ast_debug(3, "sox_gain_tmpdir: %s\n", sox_gain_tmpdir);
5781 
5782  /* Save for later */
5783  dir_to_delete = sox_gain_tmpdir;
5784 
5785  res = snprintf(fname, sizeof(fname), "%s/output.%s", sox_gain_tmpdir, format);
5786  if (res >= sizeof(fname)) {
5787  ast_log(LOG_ERROR, "Failed to create filename buffer for %s/output.%s: Too long\n", sox_gain_tmpdir, format);
5788  break;
5789  }
5790 
5791  if (!altused) {
5792  res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s",
5793  vmu->volgain, attach, format, fname);
5794  } else {
5795  if (!strcasecmp(format, "wav")) {
5796  if (vmu->volgain < -.001 || vmu->volgain > .001) {
5797  res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s -e signed-integer -b 16 %s",
5798  vmu->volgain, attach, altformat, fname);
5799  } else {
5800  res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox %s.%s -e signed-integer -b 16 %s",
5801  attach, altformat, fname);
5802  }
5803  } else {
5804  if (vmu->volgain < -.001 || vmu->volgain > .001) {
5805  res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s",
5806  vmu->volgain, attach, altformat, fname);
5807  } else {
5808  res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox %s.%s %s",
5809  attach, altformat, fname);
5810  }
5811  }
5812  }
5813 
5814  if (res >= sizeof(sox_gain_cmd)) {
5815  ast_log(LOG_ERROR, "Failed to generate sox command, out of buffer space\n");
5816  break;
5817  }
5818 
5819  soxstatus = ast_safe_system(sox_gain_cmd);
5820  if (!soxstatus) {
5821  /* Save for later */
5822  file_to_delete = fname;
5823  ast_debug(3, "VOLGAIN: Stored at: %s - Level: %.4f - Mailbox: %s\n", fname, vmu->volgain, mailbox);
5824  } else {
5825  ast_log(LOG_WARNING, "Sox failed to re-encode %s: %s (have you installed support for all sox file formats?)\n",
5826  fname,
5827  soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
5828  ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
5829  }
5830  }
5831 
5832  break;
5833  }
5834 
5835  if (!file_to_delete) {
5836  res = snprintf(fname, sizeof(fname), "%s.%s", attach, format);
5837  if (res >= sizeof(fname)) {
5838  ast_log(LOG_ERROR, "Failed to create filename buffer for %s.%s: Too long\n", attach, format);
5839  return -1;
5840  }
5841  }
5842 
5843  fprintf(p, "--%s" ENDL, bound);
5844  if (msgnum > -1)
5845  fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, mime_type, format, filename);
5846  else
5847  fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, mime_type, format, greeting_attachment, format);
5848  fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
5849  fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
5850  if (msgnum > -1)
5851  fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
5852  else
5853  fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
5854  ast_base64_encode_file_path(fname, p, ENDL);
5855  if (last)
5856  fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
5857 
5858  if (file_to_delete) {
5859  unlink(file_to_delete);
5860  }
5861 
5862  if (dir_to_delete) {
5863  rmdir(dir_to_delete);
5864  }
5865 
5866  return 0;
5867 }
5868 
5869 static int sendmail(char *srcemail,
5870  struct ast_vm_user *vmu,
5871  int msgnum,
5872  char *context,
5873  char *mailbox,
5874  const char *fromfolder,
5875  char *cidnum,
5876  char *cidname,
5877  char *attach,
5878  char *attach2,
5879  char *format,
5880  int duration,
5881  int attach_user_voicemail,
5882  struct ast_channel *chan,
5883  const char *category,
5884  const char *flag,
5885  const char *msg_id)
5886 {
5887  FILE *p = NULL;
5888  char tmp[80] = "/tmp/astmail-XXXXXX";
5889  char tmp2[256];
5890  char *stringp;
5891 
5892  if (vmu && ast_strlen_zero(vmu->email)) {
5893  ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
5894  return(0);
5895  }
5896 
5897  /* Mail only the first format */
5898  format = ast_strdupa(format);
5899  stringp = format;
5900  strsep(&stringp, "|");
5901 
5902  if (!strcmp(format, "wav49"))
5903  format = "WAV";
5904  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));
5905  /* Make a temporary file instead of piping directly to sendmail, in case the mail
5906  command hangs */
5907  if ((p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask)) == NULL) {
5908  ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
5909  return -1;
5910  } else {
5911  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);
5912  fclose(p);
5913  snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
5914  ast_safe_system(tmp2);
5915  ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
5916  }
5917  return 0;
5918 }
5919 
5920 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)
5921 {
5922  char enc_cidnum[256], enc_cidname[256];
5923  char date[256];
5924  char host[MAXHOSTNAMELEN] = "";
5925  char who[256];
5926  char dur[PATH_MAX];
5927  char tmp[80] = "/tmp/astmail-XXXXXX";
5928  char tmp2[PATH_MAX];
5929  struct ast_tm tm;
5930  FILE *p;
5931  struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
5932 
5933  if (!str1 || !str2) {
5934  ast_free(str1);
5935  ast_free(str2);
5936  return -1;
5937  }
5938 
5939  if (cidnum) {
5940  strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
5941  }
5942  if (cidname) {
5943  strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
5944  }
5945 
5946  if ((p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask)) == NULL) {
5947  ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
5948  ast_free(str1);
5949  ast_free(str2);
5950  return -1;
5951  }
5952  gethostname(host, sizeof(host)-1);
5953  if (strchr(srcemail, '@')) {
5954  ast_copy_string(who, srcemail, sizeof(who));
5955  } else {
5956  snprintf(who, sizeof(who), "%s@%s", srcemail, host);
5957  }
5958  snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
5959  ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
5960  fprintf(p, "Date: %s\n", date);
5961 
5962  /* Reformat for custom pager format */
5963  ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
5964 
5965  if (!ast_strlen_zero(pagerfromstring)) {
5966  struct ast_channel *ast;
5967  if ((ast = ast_dummy_channel_alloc())) {
5968  char *ptr;
5969  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
5970  ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
5971 
5972  if (check_mime(ast_str_buffer(str1))) {
5973  int first_line = 1;
5974  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
5975  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5976  *ptr = '\0';
5977  fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
5978  first_line = 0;
5979  /* Substring is smaller, so this will never grow */
5980  ast_str_set(&str2, 0, "%s", ptr + 1);
5981  }
5982  fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
5983  } else {
5984  fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
5985  }
5986  ast = ast_channel_unref(ast);
5987  } else {
5988  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5989  }
5990  } else {
5991  fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
5992  }
5993 
5994  if (check_mime(vmu->fullname)) {
5995  int first_line = 1;
5996  char *ptr;
5997  ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
5998  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5999  *ptr = '\0';
6000  fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
6001  first_line = 0;
6002  /* Substring is smaller, so this will never grow */
6003  ast_str_set(&str2, 0, "%s", ptr + 1);
6004  }
6005  fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
6006  } else {
6007  fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
6008  }
6009 
6010  if (!ast_strlen_zero(pagersubject)) {
6011  struct ast_channel *ast;
6012  if ((ast = ast_dummy_channel_alloc())) {
6013  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
6014  ast_str_substitute_variables(&str1, 0, ast, pagersubject);
6015  if (check_mime(ast_str_buffer(str1))) {
6016  int first_line = 1;
6017  char *ptr;
6018  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
6019  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
6020  *ptr = '\0';
6021  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
6022  first_line = 0;
6023  /* Substring is smaller, so this will never grow */
6024  ast_str_set(&str2, 0, "%s", ptr + 1);
6025  }
6026  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
6027  } else {
6028  fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
6029  }
6030  ast = ast_channel_unref(ast);
6031  } else {
6032  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
6033  }
6034  } else {
6035  if (ast_strlen_zero(flag)) {
6036  fprintf(p, "Subject: New VM\n\n");
6037  } else {
6038  fprintf(p, "Subject: New %s VM\n\n", flag);
6039  }
6040  }
6041 
6042  if (pagerbody) {
6043  struct ast_channel *ast;
6044  if ((ast = ast_dummy_channel_alloc())) {
6045  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
6046  ast_str_substitute_variables(&str1, 0, ast, pagerbody);
6047  fprintf(p, "%s" ENDL, ast_str_buffer(str1));
6048  ast = ast_channel_unref(ast);
6049  } else {
6050  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
6051  }
6052  } else {
6053  fprintf(p, "New %s long %s msg in box %s\n"
6054  "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
6055  }
6056 
6057  fclose(p);
6058  snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
6059  ast_safe_system(tmp2);
6060  ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
6061  ast_free(str1);
6062  ast_free(str2);
6063  return 0;
6064 }
6065 
6066 /*!
6067  * \brief Gets the current date and time, as formatted string.
6068  * \param s The buffer to hold the output formatted date.
6069  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
6070  *
6071  * The date format string used is "%a %b %e %r UTC %Y".
6072  *
6073  * \return zero on success, -1 on error.
6074  */
6075 static int get_date(char *s, int len)
6076 {
6077  struct ast_tm tm;
6078  struct timeval t = ast_tvnow();
6079 
6080  ast_localtime(&t, &tm, "UTC");
6081 
6082  return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
6083 }
6084 
6085 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
6086 {
6087  int res;
6088  char fn[PATH_MAX];
6089  char dest[PATH_MAX];
6090 
6091  snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
6092 
6093  if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
6094  ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
6095  return -1;
6096  }
6097 
6098  RETRIEVE(fn, -1, ext, context);
6099  if (ast_fileexists(fn, NULL, NULL) > 0) {
6100  res = ast_stream_and_wait(chan, fn, ecodes);
6101  if (res) {
6102  DISPOSE(fn, -1);
6103  return res;
6104  }
6105  } else {
6106  /* Dispose just in case */
6107  DISPOSE(fn, -1);
6108  res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
6109  if (res)
6110  return res;
6111  res = ast_say_digit_str(chan, ext, ecodes, ast_channel_language(chan));
6112  if (res)
6113  return res;
6114  }
6115  res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
6116  return res;
6117 }
6118 
6119 static void free_zone(struct vm_zone *z)
6120 {
6121  ast_free(z);
6122 }
6123 
6124 #ifdef ODBC_STORAGE
6125 #define COUNT_MSGS_SQL_FMT "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'"
6126 static int count_messages_in_folder(struct odbc_obj *odbc, const char *context, const char *mailbox, const char *folder, int *messages)
6127 {
6128  int res;
6129  char sql[sizeof(COUNT_MSGS_SQL_FMT) + odbc_table_len + strlen(VM_SPOOL_DIR)
6130  + strlen(context) + strlen(mailbox) + strlen(folder)];
6131  char rowdata[20];
6132  SQLHSTMT stmt = NULL;
6133  struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
6134  SCOPE_ENTER(3, "context: %s mb: %s folder: %s", context, mailbox, folder);
6135 
6136  if (!messages) {
6137  SCOPE_EXIT_RTN_VALUE(0, "No messages pointer\n");
6138  }
6139 
6140  snprintf(sql, sizeof(sql), COUNT_MSGS_SQL_FMT, odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
6141  if (!(stmt = ast_odbc_prepare_and_execute(odbc, generic_prepare, &gps))) {
6142  SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
6143  }
6144  res = SQLFetch(stmt);
6145  if (!SQL_SUCCEEDED(res)) {
6146  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6147  SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
6148  }
6149  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
6150  if (!SQL_SUCCEEDED(res)) {
6151  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6152  SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
6153  }
6154 
6155  *messages = atoi(rowdata);
6156  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6157 
6158  SCOPE_EXIT_RTN_VALUE(0, "messages: %d\n", *messages);
6159 }
6160 #undef COUNT_MSGS_SQL_FMT
6161 
6162 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
6163 {
6164  char tmp[PATH_MAX] = "";
6165  struct odbc_obj *obj;
6166  char *context;
6167  SCOPE_ENTER(3, "mb: %s", mailbox);
6168 
6169  if (newmsgs)
6170  *newmsgs = 0;
6171  if (oldmsgs)
6172  *oldmsgs = 0;
6173  if (urgentmsgs)
6174  *urgentmsgs = 0;
6175 
6176  /* If no mailbox, return immediately */
6177  if (ast_strlen_zero(mailbox)) {
6178  SCOPE_EXIT_RTN_VALUE(0, "No mailbox\n");
6179  }
6180 
6181  ast_copy_string(tmp, mailbox, sizeof(tmp));
6182 
6183  if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
6184  int u, n, o;
6185  char *next, *remaining = tmp;
6186  while ((next = strsep(&remaining, " ,"))) {
6187  if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
6188  SCOPE_EXIT_RTN_VALUE(-1, "Failed to obtain message count for mailbox %s\n", next);
6189  }
6190  if (urgentmsgs) {
6191  *urgentmsgs += u;
6192  }
6193  if (newmsgs) {
6194  *newmsgs += n;
6195  }
6196  if (oldmsgs) {
6197  *oldmsgs += o;
6198  }
6199  }
6200  SCOPE_EXIT_RTN_VALUE(0, "Urgent: %d New: %d Old: %d\n", urgentmsgs ? *urgentmsgs : 0, newmsgs ? *newmsgs : 0, oldmsgs ? *oldmsgs : 0);
6201  }
6202 
6203  context = strchr(tmp, '@');
6204  if (context) {
6205  *context = '\0';
6206  context++;
6207  } else
6208  context = "default";
6209 
6210  obj = ast_odbc_request_obj(odbc_database, 0);
6211  if (!obj) {
6212  SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
6213  }
6214 
6215  if (count_messages_in_folder(obj, context, tmp, "INBOX", newmsgs)
6216  || count_messages_in_folder(obj, context, tmp, "Old", oldmsgs)
6217  || count_messages_in_folder(obj, context, tmp, "Urgent", urgentmsgs)) {
6218  ast_log(AST_LOG_WARNING, "Failed to obtain message count for mailbox %s@%s\n",
6219  tmp, context);
6220  }
6221 
6222  ast_odbc_release_obj(obj);
6223  SCOPE_EXIT_RTN_VALUE(0, "Urgent: %d New: %d Old: %d\n", urgentmsgs ? *urgentmsgs : 0, newmsgs ? *newmsgs : 0, oldmsgs ? *oldmsgs : 0);
6224 }
6225 
6226 /*!
6227  * \brief Gets the number of messages that exist in a mailbox folder.
6228  * \param mailbox_id
6229  * \param folder
6230  *
6231  * This method is used when ODBC backend is used.
6232  * \return The number of messages in this mailbox folder (zero or more).
6233  */
6234 #define MSGCOUNT_SQL_FMT_INBOX "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/INBOX' OR dir = '%s%s/%s/Urgent'"
6235 #define MSGCOUNT_SQL_FMT "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'"
6236 static int messagecount(const char *mailbox_id, const char *folder)
6237 {
6238  struct odbc_obj *obj = NULL;
6239  char *context;
6240  char *mailbox;
6241  int nummsgs = 0;
6242  int res;
6243  SQLHSTMT stmt = NULL;
6244  char rowdata[20];
6245  struct generic_prepare_struct gps = { .argc = 0 };
6246  SCOPE_ENTER(3, "mb: %s folder: %s", mailbox_id, folder);
6247 
6248  /* If no mailbox, return immediately */
6249  if (ast_strlen_zero(mailbox_id)
6250  || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
6251  SCOPE_EXIT_RTN_VALUE(0, "Couldn't parse mailbox\n");
6252  }
6253 
6254  if (ast_strlen_zero(folder)) {
6255  folder = "INBOX";
6256  }
6257 
6258  obj = ast_odbc_request_obj(odbc_database, 0);
6259  if (!obj) {
6260  SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
6261  }
6262 
6263  if (!strcmp(folder, "INBOX")) {
6264  gps.sql = ast_alloca(sizeof(MSGCOUNT_SQL_FMT_INBOX) + odbc_table_len + (strlen(VM_SPOOL_DIR) + strlen(context) + strlen(mailbox) * 2));
6265  sprintf(gps.sql, MSGCOUNT_SQL_FMT_INBOX, odbc_table, VM_SPOOL_DIR, context, mailbox, VM_SPOOL_DIR, context, mailbox); /* Safe */
6266  } else {
6267  gps.sql = ast_alloca(sizeof(MSGCOUNT_SQL_FMT) + odbc_table_len + strlen(VM_SPOOL_DIR) + strlen(context) + strlen(mailbox) + strlen(folder));
6268  sprintf(gps.sql, MSGCOUNT_SQL_FMT, odbc_table, VM_SPOOL_DIR, context, mailbox, folder); /* Safe */
6269  }
6270 
6271  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
6272  if (!stmt) {
6273  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", gps.sql);
6274  goto bail;
6275  }
6276  res = SQLFetch(stmt);
6277  if (!SQL_SUCCEEDED(res)) {
6278  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", gps.sql);
6279  goto bail_with_handle;
6280  }
6281  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
6282  if (!SQL_SUCCEEDED(res)) {
6283  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", gps.sql);
6284  goto bail_with_handle;
6285  }
6286  nummsgs = atoi(rowdata);
6287 
6288 bail_with_handle:
6289  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6290 
6291 bail:
6292  ast_odbc_release_obj(obj);
6293  SCOPE_EXIT_RTN_VALUE(nummsgs, "Messages: %d\n", nummsgs);
6294 }
6295 #undef MSGCOUNT_SQL_FMT
6296 #undef MSGCOUNT_SQL_FMT_INBOX
6297 
6298 /*!
6299  * \brief Determines if the given folder has messages.
6300  * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
6301  *
6302  * This function is used when the mailbox is stored in an ODBC back end.
6303  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
6304  * \return 1 if the folder has one or more messages. zero otherwise.
6305  */
6306 static int has_voicemail(const char *mailboxes, const char *folder)
6307 {
6308  char *parse;
6309  char *mailbox;
6310 
6311  parse = ast_strdupa(mailboxes);
6312  while ((mailbox = strsep(&parse, ",&"))) {
6313  if (messagecount(mailbox, folder)) {
6314  return 1;
6315  }
6316  }
6317  return 0;
6318 }
6319 #endif
6320 #ifndef IMAP_STORAGE
6321 /*!
6322  * \brief Copies a message from one mailbox to another.
6323  * \param chan
6324  * \param vmu
6325  * \param imbox
6326  * \param msgnum
6327  * \param duration
6328  * \param recip
6329  * \param fmt
6330  * \param dir
6331  * \param flag, dest_folder
6332  *
6333  * This is only used by file storage based mailboxes.
6334  *
6335  * \return zero on success, -1 on error.
6336  */
6337 static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum,
6338  long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag,
6339  const char *dest_folder)
6340 {
6341  char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
6342  const char *frombox = mbox(vmu, imbox);
6343  const char *userfolder;
6344  int recipmsgnum;
6345  int res = 0;
6346  SCOPE_ENTER(3, "mb: %s imb: %d msgnum: %d recip: %s dir: %s dest_folder: %s",
6347  vmu->mailbox, imbox, msgnum, recip->mailbox, dir, dest_folder);
6348 
6349  ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
6350 
6351  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
6352  userfolder = "Urgent";
6353  } else if (!ast_strlen_zero(dest_folder)) {
6354  userfolder = dest_folder;
6355  } else {
6356  userfolder = "INBOX";
6357  }
6358 
6359  create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
6360  ast_trace(-1, "todir: %s\n", todir);
6361 
6362  if (!dir)
6363  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
6364  else
6365  ast_copy_string(fromdir, dir, sizeof(fromdir));
6366 
6367  ast_trace(-1, "fromdir: %s\n", fromdir);
6368 
6369  make_file(frompath, sizeof(frompath), fromdir, msgnum);
6370  ast_trace(-1, "frompath: %s\n", frompath);
6371 
6372  make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
6373  ast_trace(-1, "todir: %s\n", todir);
6374 
6375  if (vm_lock_path(todir)) {
6376  SCOPE_EXIT_RTN_VALUE(ERROR_LOCK_PATH);
6377  }
6378 
6379  recipmsgnum = LAST_MSG_INDEX(todir) + 1;
6380  ast_trace(-1, "recip msgnum: %d\n", recipmsgnum);
6381 
6382  if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
6383  int exists = 0;
6384 
6385  make_file(topath, sizeof(topath), todir, recipmsgnum);
6386  ast_trace(-1, "topath: %s\n", topath);
6387 
6388  exists = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, fromdir, msgnum, frompath, chan ? ast_channel_language(chan) : "");
6389  if (exists) {
6390  SCOPE_CALL(-1, COPY, fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
6391  } else {
6392  SCOPE_CALL(-1, copy_plain_file,frompath, topath);
6393  SCOPE_CALL(-1, STORE, todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL, NULL);
6394  SCOPE_CALL(-1, vm_delete, topath);
6395 
6396  }
6397  } else {
6398  ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
6399  res = -1;
6400  }
6401  ast_unlock_path(todir);
6402  if (chan) {
6403  struct ast_party_caller *caller = ast_channel_caller(chan);
6404  notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
6405  S_COR(caller->id.number.valid, caller->id.number.str, NULL),
6406  S_COR(caller->id.name.valid, caller->id.name.str, NULL),
6407  flag);
6408  }
6409 
6410  SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d\n", res);
6411 }
6412 #endif
6413 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
6414 
6415 static int messagecount(const char *mailbox_id, const char *folder)
6416 {
6417  char *context;
6418  char *mailbox;
6419 
6420  if (ast_strlen_zero(mailbox_id)
6421  || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
6422  return 0;
6423  }
6424 
6425  return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
6426 }
6427 
6428 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
6429 {
6430  DIR *dir;
6431  struct dirent *de;
6432  char fn[256];
6433  int ret = 0;
6434  struct alias_mailbox_mapping *mapping;
6435  char *c;
6436  char *m;
6437 
6438  /* If no mailbox, return immediately */
6439  if (ast_strlen_zero(mailbox))
6440  return 0;
6441 
6442  if (ast_strlen_zero(folder))
6443  folder = "INBOX";
6444  if (ast_strlen_zero(context))
6445  context = "default";
6446 
6447  c = (char *)context;
6448  m = (char *)mailbox;
6449 
6450  if (!ast_strlen_zero(aliasescontext)) {
6451  char tmp[MAX_VM_MAILBOX_LEN];
6452 
6453  snprintf(tmp, MAX_VM_MAILBOX_LEN, "%s@%s", mailbox, context);
6454  mapping = ao2_find(alias_mailbox_mappings, tmp, OBJ_SEARCH_KEY);
6455  if (mapping) {
6456  separate_mailbox(ast_strdupa(mapping->mailbox), &m, &c);
6457  ao2_ref(mapping, -1);
6458  }
6459  }
6460 
6461  snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, c, m, folder);
6462 
6463  if (!(dir = opendir(fn)))
6464  return 0;
6465 
6466  while ((de = readdir(dir))) {
6467  if (!strncasecmp(de->d_name, "msg", 3)) {
6468  if (shortcircuit) {
6469  ret = 1;
6470  break;
6471  } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
6472  ret++;
6473  }
6474  }
6475  }
6476 
6477  closedir(dir);
6478 
6479  return ret;
6480 }
6481 
6482 /*!
6483  * \brief Determines if the given folder has messages.
6484  * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
6485  * \param folder the folder to look in
6486  *
6487  * This function is used when the mailbox is stored in a filesystem back end.
6488  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
6489  * \return 1 if the folder has one or more messages. zero otherwise.
6490  */
6491 static int has_voicemail(const char *mailbox, const char *folder)
6492 {
6493  char tmp[256], *tmp2 = tmp, *box, *context;
6494  ast_copy_string(tmp, mailbox, sizeof(tmp));
6495  if (ast_strlen_zero(folder)) {
6496  folder = "INBOX";
6497  }
6498  while ((box = strsep(&tmp2, ",&"))) {
6499  if ((context = strchr(box, '@')))
6500  *context++ = '\0';
6501  else
6502  context = "default";
6503  if (__has_voicemail(context, box, folder, 1))
6504  return 1;
6505  /* If we are checking INBOX, we should check Urgent as well */
6506  if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
6507  return 1;
6508  }
6509  }
6510  return 0;
6511 }
6512 
6513 /*!
6514  * \brief Check the given mailbox's message count.
6515  * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
6516  * \param urgentmsgs urgent message count.
6517  * \param newmsgs new message count.
6518  * \param oldmsgs old message count pointer
6519  * \return -1 if error occurred, 0 otherwise.
6520  */
6521 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
6522 {
6523  char tmp[256];
6524  char *context;
6525 
6526  /* If no mailbox, return immediately */
6527  if (ast_strlen_zero(mailbox)) {
6528  return 0;
6529  }
6530 
6531  if (newmsgs) {
6532  *newmsgs = 0;
6533  }
6534  if (oldmsgs) {
6535  *oldmsgs = 0;
6536  }
6537  if (urgentmsgs) {
6538  *urgentmsgs = 0;
6539  }
6540 
6541  if (strchr(mailbox, ',')) {
6542  int tmpnew, tmpold, tmpurgent;
6543  char *mb, *cur;
6544 
6545  ast_copy_string(tmp, mailbox, sizeof(tmp));
6546  mb = tmp;
6547  while ((cur = strsep(&mb, ", "))) {
6548  if (!ast_strlen_zero(cur)) {
6549  if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL)) {
6550  return -1;
6551  } else {
6552  if (newmsgs) {
6553  *newmsgs += tmpnew;
6554  }
6555  if (oldmsgs) {
6556  *oldmsgs += tmpold;
6557  }
6558  if (urgentmsgs) {
6559  *urgentmsgs += tmpurgent;
6560  }
6561  }
6562  }
6563  }
6564  return 0;
6565  }
6566 
6567  ast_copy_string(tmp, mailbox, sizeof(tmp));
6568 
6569  if ((context = strchr(tmp, '@'))) {
6570  *context++ = '\0';
6571  } else {
6572  context = "default";
6573  }
6574 
6575  if (newmsgs) {
6576  *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
6577  }
6578  if (oldmsgs) {
6579  *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
6580  }
6581  if (urgentmsgs) {
6582  *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
6583  }
6584 
6585  return 0;
6586 }
6587 
6588 #endif
6589 
6590 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
6591 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
6592 {
6593  int urgentmsgs = 0;
6594  int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
6595  if (newmsgs) {
6596  *newmsgs += urgentmsgs;
6597  }
6598  return res;
6599 }
6600 
6601 static void run_externnotify(const char *context, const char *extension, const char *flag)
6602 {
6603  char arguments[255];
6604  char ext_context[256] = "";
6605  int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
6606  struct ast_smdi_mwi_message *mwi_msg;
6607 
6608  if (!ast_strlen_zero(context))
6609  snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
6610  else
6611  ast_copy_string(ext_context, extension, sizeof(ext_context));
6612 
6613  if (smdi_iface) {
6614  if (ast_app_has_voicemail(ext_context, NULL))
6615  ast_smdi_mwi_set(smdi_iface, extension);
6616  else
6617  ast_smdi_mwi_unset(smdi_iface, extension);
6618 
6619  if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
6620  ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
6621  if (!strncmp(mwi_msg->cause, "INV", 3))
6622  ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
6623  else if (!strncmp(mwi_msg->cause, "BLK", 3))
6624  ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
6625  ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
6626  ao2_ref(mwi_msg, -1);
6627  } else {
6628  ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
6629  }
6630  }
6631 
6632  if (!ast_strlen_zero(externnotify)) {
6633  if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
6634  ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
6635  } else {
6636  snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
6637  externnotify, S_OR(context, "\"\""),
6638  extension, newvoicemails,
6639  oldvoicemails, urgentvoicemails);
6640  ast_debug(1, "Executing %s\n", arguments);
6641  ast_safe_system(arguments);
6642  }
6643  }
6644 }
6645 
6646 /*!
6647  * \brief Variables used for saving a voicemail.
6648  *
6649  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
6650  */
6651 struct leave_vm_options {
6652  unsigned int flags;
6653  signed char record_gain;
6654  char *exitcontext;
6655  char *beeptone;
6656 };
6657 
6658 static void generate_msg_id(char *dst)
6659 {
6660  /* msg id is time of msg_id generation plus an incrementing value
6661  * called each time a new msg_id is generated. This should achieve uniqueness,
6662  * but only in single system solutions.
6663  */
6664  unsigned int unique_counter = ast_atomic_fetchadd_int(&msg_id_incrementor, +1);
6665  snprintf(dst, MSG_ID_LEN, "%ld-%08x", (long) time(NULL), unique_counter);
6666 }
6667 
6668 /*!
6669  * \internal
6670  * \brief Creates a voicemail based on a specified file to a mailbox.
6671  * \param recdata A vm_recording_data containing filename and voicemail txt info.
6672  * \retval -1 failure
6673  * \retval 0 success
6674  *
6675  * This is installed to the app.h voicemail functions and accommodates all voicemail
6676  * storage methods. It should probably be broken out along with leave_voicemail at
6677  * some point in the future.
6678  *
6679  * This function currently only works for a single recipient and only uses the format
6680  * specified in recording_ext.
6681  */
6682 static int msg_create_from_file(struct ast_vm_recording_data *recdata)
6683 {
6684  /* voicemail recipient structure */
6685  struct ast_vm_user *recipient; /* points to svm once it's been created */
6686  struct ast_vm_user svm; /* struct storing the voicemail recipient */
6687 
6688  /* File paths */
6689  char tmpdir[PATH_MAX]; /* directory temp files are stored in */
6690  char tmptxtfile[PATH_MAX]; /* tmp file for voicemail txt file */
6691  char desttxtfile[PATH_MAX]; /* final destination for txt file */
6692  char tmpaudiofile[PATH_MAX]; /* tmp file where audio is stored */
6693  char dir[PATH_MAX]; /* destination for tmp files on completion */
6694  char destination[PATH_MAX]; /* destination with msgXXXX. Basically <dir>/msgXXXX */
6695 
6696  /* stuff that only seems to be needed for IMAP */
6697  #ifdef IMAP_STORAGE
6698  struct vm_state *vms = NULL;
6699  char ext_context[256] = "";
6700  int newmsgs = 0;
6701  int oldmsgs = 0;
6702  #endif
6703 
6704  /* miscellaneous operational variables */
6705  int res = 0; /* Used to store error codes from functions */
6706  int txtdes /* File descriptor for the text file used to write the voicemail info */;
6707  FILE *txt; /* FILE pointer to text file used to write the voicemail info */
6708  char date[256]; /* string used to hold date of the voicemail (only used for ODBC) */
6709  int msgnum; /* the 4 digit number designated to the voicemail */
6710  int duration = 0; /* Length of the audio being recorded in seconds */
6711  struct ast_filestream *recording_fs; /*used to read the recording to get duration data */
6712 
6713  /* We aren't currently doing anything with category, since it comes from a channel variable and
6714  * this function doesn't use channels, but this function could add that as an argument later. */
6715  const char *category = NULL; /* pointless for now */
6716  char msg_id[MSG_ID_LEN];
6717 
6718  /* Start by checking to see if the file actually exists... */
6719  if (!(ast_fileexists(recdata->recording_file, recdata->recording_ext, NULL))) {
6720  ast_log(LOG_ERROR, "File: %s not found.\n", recdata->recording_file);
6721  return -1;
6722  }
6723 
6724  memset(&svm, 0, sizeof(svm));
6725  if (!(recipient = find_user(&svm, recdata->context, recdata->mailbox))) {
6726  ast_log(LOG_ERROR, "No entry in voicemail config file for '%s@%s'\n", recdata->mailbox, recdata->context);
6727  return -1;
6728  }
6729 
6730  /* determine duration in seconds */
6731  if ((recording_fs = ast_readfile(recdata->recording_file, recdata->recording_ext, NULL, 0, 0, VOICEMAIL_DIR_MODE))) {
6732  if (!ast_seekstream(recording_fs, 0, SEEK_END)) {
6733  long framelength = ast_tellstream(recording_fs);
6734  int sample_rate = ast_ratestream(recording_fs);
6735  if (sample_rate) {
6736  duration = (int) (framelength / sample_rate);
6737  } else {
6738  ast_log(LOG_ERROR,"Unable to determine sample rate of recording %s\n", recdata->recording_file);
6739  }
6740  }
6741  ast_closestream(recording_fs);
6742  }
6743 
6744  /* If the duration was below the minimum duration for the user, let's just drop the whole thing now */
6745  if (duration < recipient->minsecs) {
6746  ast_log(LOG_NOTICE, "Copying recording to voicemail %s@%s skipped because duration was shorter than "
6747  "minmessage of recipient\n", recdata->mailbox, recdata->context);
6748  return -1;
6749  }
6750 
6751  /* Note that this number must be dropped back to a net sum of zero before returning from this function */
6752 
6753  if ((res = create_dirpath(tmpdir, sizeof(tmpdir), recipient->context, recdata->mailbox, "tmp"))) {
6754  ast_log(LOG_ERROR, "Failed to make directory.\n");
6755  }
6756 
6757  snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
6758  txtdes = mkstemp(tmptxtfile);
6759  if (txtdes < 0) {
6760  chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
6761  /* Something screwed up. Abort. */
6762  ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
6763  free_user(recipient);
6764  return -1;
6765  }
6766 
6767  /* Store information */
6768  txt = fdopen(txtdes, "w+");
6769  if (txt) {
6770  generate_msg_id(msg_id);
6771  get_date(date, sizeof(date));
6772  fprintf(txt,
6773  ";\n"
6774  "; Message Information file\n"
6775  ";\n"
6776  "[message]\n"
6777  "origmailbox=%s\n"
6778  "context=%s\n"
6779  "exten=%s\n"
6780  "rdnis=Unknown\n"
6781  "priority=%d\n"
6782  "callerchan=%s\n"
6783  "callerid=%s\n"
6784  "origdate=%s\n"
6785  "origtime=%ld\n"
6786  "category=%s\n"
6787  "msg_id=%s\n"
6788  "flag=\n" /* flags not supported in copy from file yet */
6789  "duration=%d\n", /* Don't have any reliable way to get duration of file. */
6790 
6791  recdata->mailbox,
6792  S_OR(recdata->call_context, ""),
6793  S_OR(recdata->call_extension, ""),
6794  recdata->call_priority,
6795  S_OR(recdata->call_callerchan, "Unknown"),
6796  S_OR(recdata->call_callerid, "Unknown"),
6797  date, (long) time(NULL),
6798  S_OR(category, ""),
6799  msg_id,
6800  duration);
6801 
6802  /* Since we are recording from a file, we shouldn't need to do anything else with
6803  * this txt file */
6804  fclose(txt);
6805 
6806  } else {
6807  ast_log(LOG_WARNING, "Error opening text file for output\n");
6808  if (ast_check_realtime("voicemail_data")) {
6809  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6810  }
6811  free_user(recipient);
6812  return -1;
6813  }
6814 
6815  /* At this point, the actual creation of a voicemail message should be finished.
6816  * Now we just need to copy the files being recorded into the receiving folder. */
6817 
6818  create_dirpath(dir, sizeof(dir), recipient->context, recipient->mailbox, recdata->folder);
6819 
6820 #ifdef IMAP_STORAGE
6821  /* make recipient info into an inboxcount friendly string */
6822  snprintf(ext_context, sizeof(ext_context), "%s@%s", recipient->mailbox, recipient->context);
6823 
6824  /* Is ext a mailbox? */
6825  /* must open stream for this user to get info! */
6826  res = inboxcount(ext_context, &newmsgs, &oldmsgs);
6827  if (res < 0) {
6828  ast_log(LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
6829  free_user(recipient);
6830  unlink(tmptxtfile);
6831  return -1;
6832  }
6833  if (!(vms = get_vm_state_by_mailbox(recipient->mailbox, recipient->context, 0))) {
6834  /* It is possible under certain circumstances that inboxcount did not
6835  * create a vm_state when it was needed. This is a catchall which will
6836  * rarely be used.
6837  */
6838  if (!(vms = create_vm_state_from_user(recipient))) {
6839  ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
6840  free_user(recipient);
6841  unlink(tmptxtfile);
6842  return -1;
6843  }
6844  }
6845  vms->newmessages++;
6846 
6847  /* here is a big difference! We add one to it later */
6848  msgnum = newmsgs + oldmsgs;
6849  ast_debug(3, "Messagecount set to %d\n", msgnum);
6850  snprintf(destination, sizeof(destination), "%simap/msg%s%04d", VM_SPOOL_DIR, recipient->mailbox, msgnum);
6851 
6852  /* Check to see if we have enough room in the mailbox. If not, spit out an error and end
6853  * Note that imap_check_limits raises inprocess_count if successful */
6854  if ((res = imap_check_limits(NULL, vms, recipient, msgnum))) {
6855  ast_log(LOG_NOTICE, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
6856  inprocess_count(recipient->mailbox, recipient->context, -1);
6857  free_user(recipient);
6858  unlink(tmptxtfile);
6859  return -1;
6860  }
6861 
6862 #else
6863 
6864  /* Check to see if the mailbox is full for ODBC/File storage */
6865  ast_debug(3, "mailbox = %d : inprocess = %d\n", COUNT(recipient, dir),
6866  inprocess_count(recipient->mailbox, recipient->context, 0));
6867  if (COUNT(recipient, dir) > recipient->maxmsg - inprocess_count(recipient->mailbox, recipient->context, +1)) {
6868  ast_log(AST_LOG_WARNING, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
6869  inprocess_count(recipient->mailbox, recipient->context, -1);
6870  free_user(recipient);
6871  unlink(tmptxtfile);
6872  return -1;
6873  }
6874 
6875  msgnum = LAST_MSG_INDEX(dir) + 1;
6876 #endif
6877 
6878  /* Lock the directory receiving the voicemail since we want it to still exist when we attempt to copy the voicemail.
6879  * We need to unlock it before we return. */
6880  if (vm_lock_path(dir)) {
6881  ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
6882  /* Delete files */
6883  ast_filedelete(tmptxtfile, NULL);
6884  unlink(tmptxtfile);
6885  free_user(recipient);
6886  return -1;
6887  }
6888 
6889  make_file(destination, sizeof(destination), dir, msgnum);
6890 
6891  make_file(tmpaudiofile, sizeof(tmpaudiofile), tmpdir, msgnum);
6892 
6893  if (ast_filecopy(recdata->recording_file, tmpaudiofile, recdata->recording_ext)) {
6894  ast_log(LOG_ERROR, "Audio file failed to copy to tmp dir. Probably low disk space.\n");
6895 
6896  inprocess_count(recipient->mailbox, recipient->context, -1);
6897  ast_unlock_path(dir);
6898  free_user(recipient);
6899  unlink(tmptxtfile);
6900  return -1;
6901  }
6902 
6903  /* Alright, try to copy to the destination folder now. */
6904  if (ast_filerename(tmpaudiofile, destination, recdata->recording_ext)) {
6905  ast_log(LOG_ERROR, "Audio file failed to move to destination directory. Permissions/Overlap?\n");
6906  inprocess_count(recipient->mailbox, recipient->context, -1);
6907  ast_unlock_path(dir);
6908  free_user(recipient);
6909  unlink(tmptxtfile);
6910  return -1;
6911  }
6912 
6913  snprintf(desttxtfile, sizeof(desttxtfile), "%s.txt", destination);
6914  rename(tmptxtfile, desttxtfile);
6915 
6916  if (chmod(desttxtfile, VOICEMAIL_FILE_MODE) < 0) {
6917  ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", desttxtfile, strerror(errno));
6918  }
6919 
6920 
6921  ast_unlock_path(dir);
6922  inprocess_count(recipient->mailbox, recipient->context, -1);
6923 
6924  /* If we copied something, we should store it either to ODBC or IMAP if we are using those. The STORE macro allows us
6925  * 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
6926  * time to create the voicemail database entry. */
6927  if (ast_fileexists(destination, NULL, NULL) > 0) {
6928  struct ast_channel *chan = NULL;
6929  char fmt[80];
6930  char clid[80];
6931  char cidnum[80], cidname[80];
6932  int send_email;
6933 
6934  if (ast_check_realtime("voicemail_data")) {
6935  get_date(date, sizeof(date));
6936  ast_store_realtime("voicemail_data",
6937  "origmailbox", recdata->mailbox,
6938  "context", S_OR(recdata->context, ""),
6939  "exten", S_OR(recdata->call_extension, ""),
6940  "priority", recdata->call_priority,
6941  "callerchan", S_OR(recdata->call_callerchan, "Unknown"),
6942  "callerid", S_OR(recdata->call_callerid, "Unknown"),
6943  "origdate", date,
6944  "origtime", time(NULL),
6945  "category", S_OR(category, ""),
6946  "filename", tmptxtfile,
6947  "duration", duration,
6948  SENTINEL);
6949  }
6950 
6951  STORE(dir, recipient->mailbox, recipient->context, msgnum, NULL, recipient, fmt, 0, vms, "", msg_id);
6952 
6953  send_email = ast_test_flag(recipient, VM_EMAIL_EXT_RECS);
6954 
6955  if (send_email) {
6956  /* Send an email if possible, fall back to just notifications if not. */
6957  ast_copy_string(fmt, recdata->recording_ext, sizeof(fmt));
6958  ast_copy_string(clid, recdata->call_callerid, sizeof(clid));
6959  ast_callerid_split(clid, cidname, sizeof(cidname), cidnum, sizeof(cidnum));
6960 
6961  /* recdata->call_callerchan itself no longer exists, so we can't use the real channel. Use a dummy one. */
6962  chan = ast_dummy_channel_alloc();
6963  }
6964  if (chan) {
6965  notify_new_message(chan, recipient, NULL, msgnum, duration, fmt, cidnum, cidname, "");
6966  ast_channel_unref(chan);
6967  } else {
6968  if (send_email) { /* We tried and failed. */
6969  ast_log(LOG_WARNING, "Failed to allocate dummy channel, email will not be sent\n");
6970  }
6971  notify_new_state(recipient);
6972  }
6973  }
6974 
6975  free_user(recipient);
6976  unlink(tmptxtfile);
6977  return 0;
6978 }
6979 
6980 /*!
6981  * \brief Prompts the user and records a voicemail to a mailbox.
6982  * \param chan
6983  * \param ext
6984  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
6985  *
6986  *
6987  *
6988  * \return zero on success, -1 on error.
6989  */
6990 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
6991 {
6992 #ifdef IMAP_STORAGE
6993  int newmsgs, oldmsgs;
6994 #endif
6995  char txtfile[PATH_MAX];
6996  char tmptxtfile[PATH_MAX];
6997  struct vm_state *vms = NULL;
6998  char callerid[256];
6999  FILE *txt;
7000  char date[256];
7001  int txtdes;
7002  int res = 0;
7003  int msgnum;
7004  int duration = 0;
7005  int sound_duration = 0;
7006  int ouseexten = 0;
7007  int greeting_only = 0;
7008  char tmpdur[16];
7009  char priority[16];
7010  char origtime[16];
7011  char dir[PATH_MAX];
7012  char tmpdir[PATH_MAX];
7013  char fn[PATH_MAX];
7014  char prefile[PATH_MAX] = "";
7015  char tempfile[PATH_MAX] = "";
7016  char ext_context[256] = "";
7017  char fmt[80];
7018  char *context;
7019  char ecodes[17] = "#";
7020  struct ast_str *tmp = ast_str_create(16);
7021  char *tmpptr;
7022  struct ast_vm_user *vmu;
7023  struct ast_vm_user svm;
7024  const char *category = NULL;
7025  const char *code;
7026  const char *alldtmf = "0123456789ABCD*#";
7027  char flag[80];
7028  SCOPE_ENTER(3, "%s: %s\n", ast_channel_name(chan), ext);
7029 
7030  if (!tmp) {
7031  SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for tmp\n");
7032  }
7033 
7034  ast_str_set(&tmp, 0, "%s", ext);
7035  ext = ast_str_buffer(tmp);
7036  if ((context = strchr(ext, '@'))) {
7037  *context++ = '\0';
7038  tmpptr = strchr(context, '&');
7039  } else {
7040  tmpptr = strchr(ext, '&');
7041  }
7042 
7043  if (tmpptr)
7044  *tmpptr++ = '\0';
7045 
7046  ast_channel_lock(chan);
7047  if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
7048  category = ast_strdupa(category);
7049  }
7050  ast_channel_unlock(chan);
7051 
7052  if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
7053  ast_copy_string(flag, "Urgent", sizeof(flag));
7054  } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
7055  ast_copy_string(flag, "PRIORITY", sizeof(flag));
7056  } else {
7057  flag[0] = '\0';
7058  }
7059 
7060  ast_debug(3, "Before find_user\n");
7061  memset(&svm, 0, sizeof(svm));
7062  if (!(vmu = find_user(&svm, context, ext))) {
7063  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7064  ast_free(tmp);
7065  SCOPE_EXIT_LOG_RTN_VALUE(res, LOG_WARNING,
7066  "%s: Exten: %s: No entry in voicemail config file for '%s'\n", ast_channel_name(chan), ext, ext);
7067  }
7068 
7069  /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
7070  if (vmu->maxmsg == 0) {
7071  greeting_only = 1;
7072  ast_set_flag(options, OPT_SILENT);
7073  }
7074 
7075  /* Setup pre-file if appropriate */
7076  if (strcmp(vmu->context, "default"))
7077  snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
7078  else
7079  ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
7080 
7081  /* Set the path to the prefile. Will be one of
7082  VM_SPOOL_DIRcontext/ext/busy
7083  VM_SPOOL_DIRcontext/ext/unavail
7084  Depending on the flag set in options.
7085  */
7086  if (ast_test_flag(options, OPT_BUSY_GREETING)) {
7087  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
7088  } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
7089  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
7090  }
7091  ast_trace(-1, "prefile: %s\n", prefile);
7092  /* Set the path to the tmpfile as
7093  VM_SPOOL_DIR/context/ext/temp
7094  and attempt to create the folder structure.
7095  */
7096  snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
7097  ast_trace(-1, "tempfile: %s\n", tempfile);
7098  if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
7099  free_user(vmu);
7100  ast_free(tmp);
7101  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING,
7102  "%s: Exten: %s: Failed to make directory (%s)\n", ast_channel_name(chan), ext, tempfile);
7103  }
7104  SCOPE_CALL(-1, RETRIEVE, tempfile, -1, vmu->mailbox, vmu->context);
7105  if (ast_fileexists(tempfile, NULL, NULL) > 0) {
7106  ast_copy_string(prefile, tempfile, sizeof(prefile));
7107  ast_trace(-1, "new prefile: %s\n", prefile);
7108  }
7109 
7110  SCOPE_CALL(-1, DISPOSE, tempfile, -1);
7111 
7112  /* It's easier just to try to make it than to check for its existence */
7113 #ifndef IMAP_STORAGE
7114  create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
7115 #else
7116  snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
7117  if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
7118  ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
7119  }
7120 #endif
7121 
7122  /* Check current context for special extensions */
7123  if (ast_test_flag(vmu, VM_OPERATOR)) {
7124  if (!ast_strlen_zero(vmu->exit)) {
7125  if (ast_exists_extension(chan, vmu->exit, "o", 1,
7126  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7127  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
7128  ouseexten = 1;
7129  }
7130  } else if (ast_exists_extension(chan, ast_channel_context(chan), "o", 1,
7131  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7132  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
7133  ouseexten = 1;
7134  }
7135  }
7136 
7137  if (!ast_strlen_zero(vmu->exit)) {
7138  if (ast_exists_extension(chan, vmu->exit, "a", 1,
7139  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7140  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
7141  }
7142  } else if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
7143  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7144  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
7145  }
7146 
7147  if (ast_test_flag(options, OPT_DTMFEXIT)) {
7148  for (code = alldtmf; *code; code++) {
7149  char e[2] = "";
7150  e[0] = *code;
7151  if (strchr(ecodes, e[0]) == NULL
7152  && ast_canmatch_extension(chan,
7153  (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : ast_channel_context(chan)),
7154  e, 1, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7155  strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
7156  }
7157  }
7158  }
7159 
7160  /* Play the beginning intro if desired */
7161  if (!ast_strlen_zero(prefile)) {
7162 #if defined(ODBC_STORAGE)
7163  int success = SCOPE_CALL_WITH_INT_RESULT(-1, RETRIEVE, prefile, -1, ext, context);
7164 #elif defined(IMAP_STORAGE)
7165  SCOPE_CALL(-1, RETRIEVE, prefile, -1, ext, context);
7166 #endif
7167 
7168  if (ast_fileexists(prefile, NULL, NULL) > 0) {
7169  if (ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1) {
7170  /* We know we have a greeting at this point, so squelch the instructions
7171  * if that is what is being asked of us */
7172  if (ast_test_flag(options, OPT_SILENT_IF_GREET)) {
7173  ast_set_flag(options, OPT_SILENT);
7174  }
7175  res = ast_waitstream(chan, ecodes);
7176  }
7177 #ifdef ODBC_STORAGE
7178  if (success == -1) {
7179  /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
7180  ast_trace(-1, "Greeting '%s' not retrieved from database, but found in file storage. Inserting into database\n", prefile);
7181  SCOPE_CALL(-1, odbc_store_message, prefile, vmu->mailbox, vmu->context, -1);
7182  }
7183 #endif
7184  } else {
7185  ast_trace(-1, "%s doesn't exist, doing what we can\n", prefile);
7186  res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
7187  }
7188  SCOPE_CALL(-1, DISPOSE, prefile, -1);
7189  if (res < 0) {
7190  free_user(vmu);
7191  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7192  ast_free(tmp);
7193  SCOPE_EXIT_RTN_VALUE(-1, "Hang up during prefile playback\n");
7194  }
7195  }
7196  if (res == '#') {
7197  /* On a '#' we skip the instructions */
7198  ast_set_flag(options, OPT_SILENT);
7199  res = 0;
7200  }
7201  if (!res && !ast_test_flag(options, OPT_SILENT)) {
7202  res = ast_stream_and_wait(chan, INTRO, ecodes);
7203  if (res == '#') {
7204  ast_set_flag(options, OPT_SILENT);
7205  res = 0;
7206  }
7207  }
7208  if (res > 0)
7209  ast_stopstream(chan);
7210  /* Check for a '*' here in case the caller wants to escape from voicemail to something
7211  other than the operator -- an automated attendant or mailbox login for example */
7212  if (res == '*') {
7213  ast_channel_exten_set(chan, "a");
7214  if (!ast_strlen_zero(vmu->exit)) {
7215  ast_channel_context_set(chan, vmu->exit);
7216  }
7217  ast_channel_priority_set(chan, 0);
7218  free_user(vmu);
7219  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
7220  ast_free(tmp);
7221  SCOPE_EXIT_RTN_VALUE(0, "User escaped with '*'\n");
7222  }
7223 
7224  /* Check for a '0' here */
7225  if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
7226  transfer:
7227  if (ouseexten) {
7228  ast_channel_exten_set(chan, "o");
7229  if (!ast_strlen_zero(vmu->exit)) {
7230  ast_channel_context_set(chan, vmu->exit);
7231  }
7232  ast_play_and_wait(chan, "transfer");
7233  ast_channel_priority_set(chan, 0);
7234  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
7235  }
7236  free_user(vmu);
7237  ast_free(tmp);
7238  SCOPE_EXIT_RTN_VALUE(OPERATOR_EXIT, "User escaped with '0'\n");
7239  }
7240 
7241  /* Allow all other digits to exit Voicemail and return to the dialplan */
7242  if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
7243  if (!ast_strlen_zero(options->exitcontext)) {
7244  ast_channel_context_set(chan, options->exitcontext);
7245  }
7246  free_user(vmu);
7247  ast_free(tmp);
7248  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
7249  SCOPE_EXIT_RTN_VALUE(res, "User escaped back to dialplan '%c'\n", res);
7250  }
7251 
7252  if (greeting_only) {
7253  ast_debug(3, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
7254  pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
7255  res = 0;
7256  goto leave_vm_out;
7257  }
7258 
7259  if (res < 0) {
7260  free_user(vmu);
7261  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7262  ast_free(tmp);
7263  SCOPE_EXIT_RTN_VALUE(-1, "Other error '%d'\n", res);
7264  }
7265  /* The meat of recording the message... All the announcements and beeps have been played*/
7266  if (ast_channel_state(chan) != AST_STATE_UP) {
7267  ast_answer(chan);
7268  }
7269  ast_copy_string(fmt, vmfmts, sizeof(fmt));
7270  if (!ast_strlen_zero(fmt)) {
7271  char msg_id[MSG_ID_LEN] = "";
7272  msgnum = 0;
7273 
7274 #ifdef IMAP_STORAGE
7275  /* Is ext a mailbox? */
7276  /* must open stream for this user to get info! */
7277  res = inboxcount(ext_context, &newmsgs, &oldmsgs);
7278  if (res < 0) {
7279  ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
7280  free_user(vmu);
7281  ast_free(tmp);
7282  return -1;
7283  }
7284  if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
7285  /* It is possible under certain circumstances that inboxcount did not
7286  * create a vm_state when it was needed. This is a catchall which will
7287  * rarely be used.
7288  */
7289  if (!(vms = create_vm_state_from_user(vmu))) {
7290  ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
7291  free_user(vmu);
7292  ast_free(tmp);
7293  return -1;
7294  }
7295  }
7296  vms->newmessages++;
7297 
7298  /* here is a big difference! We add one to it later */
7299  msgnum = newmsgs + oldmsgs;
7300  ast_debug(3, "Messagecount set to %d\n", msgnum);
7301  snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
7302  /* set variable for compatibility */
7303  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
7304 
7305  if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
7306  goto leave_vm_out;
7307  }
7308 #else
7309  if (COUNT(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
7310  res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
7311  if (!res)
7312  res = ast_waitstream(chan, "");
7313  ast_log(AST_LOG_WARNING, "No more messages possible\n");
7314  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7315  inprocess_count(vmu->mailbox, vmu->context, -1);
7316  goto leave_vm_out;
7317  }
7318 
7319 #endif
7320  snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
7321  ast_trace(-1, "Tempfile: %s\n", tmptxtfile);
7322  txtdes = mkstemp(tmptxtfile);
7323  chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
7324  if (txtdes < 0) {
7325  res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
7326  if (!res)
7327  res = ast_waitstream(chan, "");
7328  ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
7329  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7330  inprocess_count(vmu->mailbox, vmu->context, -1);
7331  goto leave_vm_out;
7332  }
7333 
7334  /* Now play the beep once we have the message number for our next message. */
7335  if (res >= 0) {
7336  /* Unless we're *really* silent, try to send the beep */
7337  /* Play default or custom beep, unless no beep desired */
7338  if (!ast_strlen_zero(options->beeptone)) {
7339  res = ast_stream_and_wait(chan, options->beeptone, "");
7340  }
7341  }
7342 
7343  /* Store information in real-time storage */
7344  if (ast_check_realtime("voicemail_data")) {
7345  snprintf(priority, sizeof(priority), "%d", ast_channel_priority(chan));
7346  snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
7347  get_date(date, sizeof(date));
7348  ast_callerid_merge(callerid, sizeof(callerid),
7349  S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7350  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7351  "Unknown");
7352  ast_store_realtime("voicemail_data",
7353  "origmailbox", ext,
7354  "context", ast_channel_context(chan),
7355  "exten", ast_channel_exten(chan),
7356  "priority", priority,
7357  "callerchan", ast_channel_name(chan),
7358  "callerid", callerid,
7359  "origdate", date,
7360  "origtime", origtime,
7361  "category", S_OR(category, ""),
7362  "filename", tmptxtfile,
7363  SENTINEL);
7364  }
7365 
7366  /* Store information */
7367  txt = fdopen(txtdes, "w+");
7368  if (txt) {
7369  generate_msg_id(msg_id);
7370  get_date(date, sizeof(date));
7371  ast_callerid_merge(callerid, sizeof(callerid),
7372  S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7373  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7374  "Unknown");
7375  fprintf(txt,
7376  ";\n"
7377  "; Message Information file\n"
7378  ";\n"
7379  "[message]\n"
7380  "origmailbox=%s\n"
7381  "context=%s\n"
7382  "exten=%s\n"
7383  "rdnis=%s\n"
7384  "priority=%d\n"
7385  "callerchan=%s\n"
7386  "callerid=%s\n"
7387  "origdate=%s\n"
7388  "origtime=%ld\n"
7389  "category=%s\n"
7390  "msg_id=%s\n",
7391  ext,
7392  ast_channel_context(chan),
7393  ast_channel_exten(chan),
7394  S_COR(ast_channel_redirecting(chan)->from.number.valid,
7395  ast_channel_redirecting(chan)->from.number.str, "unknown"),
7396  ast_channel_priority(chan),
7397  ast_channel_name(chan),
7398  callerid,
7399  date, (long) time(NULL),
7400  category ? category : "",
7401  msg_id);
7402  ast_trace(-1, "Saving txt file mbox: %s msg_id: %s\n", ext, msg_id);
7403  } else {
7404  ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
7405  inprocess_count(vmu->mailbox, vmu->context, -1);
7406  if (ast_check_realtime("voicemail_data")) {
7407  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7408  }
7409  res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
7410  goto leave_vm_out;
7411  }
7412 
7413  res = SCOPE_CALL_WITH_INT_RESULT(-1, play_record_review, chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag, msg_id, 0);
7414 
7415  /* At this point, either we were instructed to make the message Urgent
7416  by arguments to VoiceMail or during the review process by the person
7417  leaving the message. So we update the directory where we want this
7418  message to go. */
7419  if (!strcmp(flag, "Urgent")) {
7420  create_dirpath(dir, sizeof(dir), vmu->context, ext, "Urgent");
7421  }
7422 
7423  if (txt) {
7424  fprintf(txt, "flag=%s\n", flag);
7425  if (sound_duration < vmu->minsecs) {
7426  fclose(txt);
7427  ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
7428  ast_filedelete(tmptxtfile, NULL);
7429  unlink(tmptxtfile);
7430  if (ast_check_realtime("voicemail_data")) {
7431  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7432  }
7433  inprocess_count(vmu->mailbox, vmu->context, -1);
7434  } else {
7435  fprintf(txt, "duration=%d\n", duration);
7436  fclose(txt);
7437  if (vm_lock_path(dir)) {
7438  ast_log(AST_LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
7439  /* Delete files */
7440  ast_filedelete(tmptxtfile, NULL);
7441  unlink(tmptxtfile);
7442  inprocess_count(vmu->mailbox, vmu->context, -1);
7443  } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
7444  ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
7445  unlink(tmptxtfile);
7446  ast_unlock_path(dir);
7447  inprocess_count(vmu->mailbox, vmu->context, -1);
7448  if (ast_check_realtime("voicemail_data")) {
7449  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7450  }
7451  } else {
7452 #ifndef IMAP_STORAGE
7453  msgnum = LAST_MSG_INDEX(dir) + 1;
7454 #endif
7455  make_file(fn, sizeof(fn), dir, msgnum);
7456 
7457  /* assign a variable with the name of the voicemail file */
7458 #ifndef IMAP_STORAGE
7459  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
7460 #else
7461  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
7462 #endif
7463 
7464  snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
7465 
7466  ast_trace(-1, "Renaming recordings '%s' -> fn '%s'\n", tmptxtfile, fn);
7467  /* ast_filerename renames the recordings but not the txt file */
7468  ast_filerename(tmptxtfile, fn, NULL);
7469 
7470  ast_trace(-1, "Renaming txt file '%s' -> fn '%s'\n", tmptxtfile, txtfile);
7471  rename(tmptxtfile, txtfile);
7472  inprocess_count(vmu->mailbox, vmu->context, -1);
7473 
7474  /* Properly set permissions on voicemail text descriptor file.
7475  Unfortunately mkstemp() makes this file 0600 on most unix systems. */
7476  if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
7477  ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
7478 
7479  ast_unlock_path(dir);
7480  if (ast_check_realtime("voicemail_data")) {
7481  snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
7482  ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
7483  }
7484  /* We must store the file first, before copying the message, because
7485  * ODBC storage does the entire copy with SQL.
7486  */
7487  if (ast_fileexists(fn, NULL, NULL) > 0) {
7488  SCOPE_CALL(-1, STORE, dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag, msg_id);
7489  }
7490 
7491  /* Are there to be more recipients of this message? */
7492  while (tmpptr) {
7493  struct ast_vm_user recipu, *recip;
7494  char *exten, *cntx;
7495 
7496  exten = strsep(&tmpptr, "&");
7497  cntx = strchr(exten, '@');
7498  if (cntx) {
7499  *cntx = '\0';
7500  cntx++;
7501  }
7502  memset(&recipu, 0, sizeof(recipu));
7503  if ((recip = find_user(&recipu, cntx, exten))) {
7504  copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag, NULL);
7505  free_user(recip);
7506  }
7507  }
7508 
7509  /* Notification needs to happen after the copy, though. */
7510  if (ast_fileexists(fn, NULL, NULL)) {
7511 #ifdef IMAP_STORAGE
7512  notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
7513  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7514  S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7515  flag);
7516 #else
7517  notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
7518  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7519  S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7520  flag);
7521 #endif
7522  }
7523 
7524  /* Disposal needs to happen after the optional move and copy */
7525  if (ast_fileexists(fn, NULL, NULL)) {
7526  SCOPE_CALL(-1, DISPOSE, dir, msgnum);
7527  }
7528  }
7529  }
7530  } else {
7531  inprocess_count(vmu->mailbox, vmu->context, -1);
7532  }
7533  if (res == '0') {
7534  goto transfer;
7535  } else if (res > 0 && res != 't')
7536  res = 0;
7537 
7538  if (sound_duration < vmu->minsecs)
7539  /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
7540  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7541  else
7542  pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
7543  } else
7544  ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
7545 leave_vm_out:
7546  free_user(vmu);
7547 
7548 #ifdef IMAP_STORAGE
7549  /* expunge message - use UID Expunge if supported on IMAP server*/
7550  ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
7551  if (expungeonhangup == 1 && vms->mailstream != NULL) {
7552  ast_mutex_lock(&vms->lock);
7553 #ifdef HAVE_IMAP_TK2006
7554  if (LEVELUIDPLUS (vms->mailstream)) {
7555  mail_expunge_full(vms->mailstream, NIL, EX_UID);
7556  } else
7557 #endif
7558  mail_expunge(vms->mailstream);
7559  ast_mutex_unlock(&vms->lock);
7560  }
7561 #endif
7562 
7563  ast_free(tmp);
7564  SCOPE_EXIT_RTN_VALUE(res, "Done: '%d'\n", res);
7565 }
7566 
7567 #if !defined(IMAP_STORAGE)
7568 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
7569 {
7570  /* we know the actual number of messages, so stop process when number is hit */
7571 
7572  int x, dest;
7573  char sfn[PATH_MAX];
7574  char dfn[PATH_MAX];
7575 
7576  if (vm_lock_path(dir)) {
7577  return ERROR_LOCK_PATH;
7578  }
7579 
7580  for (x = 0, dest = 0; dest != stopcount && x < MAXMSGLIMIT; x++) {
7581  make_file(sfn, sizeof(sfn), dir, x);
7582  if (EXISTS(dir, x, sfn, NULL)) {
7583 
7584  if (x != dest) {
7585  make_file(dfn, sizeof(dfn), dir, dest);
7586  RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
7587  }
7588 
7589  dest++;
7590  }
7591  }
7592  ast_unlock_path(dir);
7593 
7594  return dest;
7595 }
7596 #endif
7597 
7598 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
7599 {
7600  int d;
7601  d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
7602  return d;
7603 }
7604 
7605 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move)
7606 {
7607 #ifdef IMAP_STORAGE
7608  /* we must use mbox(x) folder names, and copy the message there */
7609  /* simple. huh? */
7610  char sequence[10];
7611  char mailbox[256];
7612  int res;
7613  int curr_mbox;
7614 
7615  /* get the real IMAP message number for this message */
7616  snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
7617 
7618  ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
7619  ast_mutex_lock(&vms->lock);
7620  /* if save to Old folder, put in INBOX as read */
7621  if (box == OLD_FOLDER) {
7622  mail_setflag(vms->mailstream, sequence, "\\Seen");
7623  } else if (box == NEW_FOLDER) {
7624  mail_clearflag(vms->mailstream, sequence, "\\Seen");
7625  }
7626  if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
7627  ast_mutex_unlock(&vms->lock);
7628  return 0;
7629  }
7630 
7631  /* get the current mailbox so that we can point the mailstream back to it later */
7632  curr_mbox = get_folder_by_name(vms->curbox);
7633 
7634  /* Create the folder if it doesn't exist */
7635  imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
7636  if (vms->mailstream && !mail_status(vms->mailstream, mailbox, SA_UIDNEXT)) {
7637  if (mail_create(vms->mailstream, mailbox) != NIL) {
7638  ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
7639  }
7640  }
7641 
7642  /* restore previous mbox stream */
7643  if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
7644  ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
7645  res = -1;
7646  } else {
7647  if (move) {
7648  res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
7649  } else {
7650  res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
7651  }
7652  }
7653  ast_mutex_unlock(&vms->lock);
7654  return res;
7655 #else
7656  char *dir = vms->curdir;
7657  char *username = vms->username;
7658  char *context = vmu->context;
7659  char sfn[PATH_MAX];
7660  char dfn[PATH_MAX];
7661  char ddir[PATH_MAX];
7662  const char *dbox = mbox(vmu, box);
7663  int x, i;
7664  SCOPE_ENTER(3, "dir: %s msg: %d box: %d dbox: %s move? %d \n", dir, msg, box, dbox, move);
7665 
7666  create_dirpath(ddir, sizeof(ddir), context, username, dbox);
7667  ast_trace(-1, "ddir: %s\n", ddir);
7668 
7669  if (vm_lock_path(ddir)) {
7670  SCOPE_EXIT_RTN_VALUE(ERROR_LOCK_PATH, "Failed to lock path %s\n", ddir);
7671  }
7672 
7673  x = LAST_MSG_INDEX(ddir) + 1;
7674 
7675  if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
7676  ast_trace(-1, "Deleting message %d\n", msg);
7677  x--;
7678  for (i = 1; i <= x; i++) {
7679  /* Push files down a "slot". The oldest file (msg0000) will be deleted. */
7680  make_file(sfn, sizeof(sfn), ddir, i);
7681  make_file(dfn, sizeof(dfn), ddir, i - 1);
7682  if (EXISTS(ddir, i, sfn, NULL)) {
7683  SCOPE_CALL(-1, RENAME, ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
7684  } else
7685  break;
7686  }
7687  } else {
7688  if (x >= vmu->maxmsg) {
7689  ast_unlock_path(ddir);
7690  SCOPE_EXIT_RTN_VALUE(ERROR_MAX_MSGS, "Max messages reached\n");
7691  }
7692  }
7693  make_file(sfn, sizeof(sfn), dir, msg);
7694  make_file(dfn, sizeof(dfn), ddir, x);
7695  if (strcmp(sfn, dfn)) {
7696  ast_trace(-1, "Copying message '%s' to '%s'\n", sfn, dfn);
7697  SCOPE_CALL(-1, COPY, dir, msg, ddir, x, username, context, sfn, dfn);
7698  }
7699  ast_unlock_path(ddir);
7700 
7701  if (newmsg) {
7702  *newmsg = x;
7703  }
7704  SCOPE_EXIT_RTN_VALUE(0, "Done\n");
7705 #endif
7706 }
7707 
7708 static int adsi_logo(unsigned char *buf)
7709 {
7710  int bytes = 0;
7711  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
7712  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
7713  return bytes;
7714 }
7715 
7716 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
7717 {
7718  unsigned char buf[256];
7719  int bytes = 0;
7720  int x;
7721  char num[5];
7722 
7723  *useadsi = 0;
7724  bytes += ast_adsi_data_mode(buf + bytes);
7725  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7726 
7727  bytes = 0;
7728  bytes += adsi_logo(buf);
7729  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
7730 #ifdef DISPLAY
7731  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
7732 #endif
7733  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7734  bytes += ast_adsi_data_mode(buf + bytes);
7735  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7736 
7737  if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
7738  bytes = 0;
7739  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
7740  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
7741  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7742  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7743  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7744  return 0;
7745  }
7746 
7747 #ifdef DISPLAY
7748  /* Add a dot */
7749  bytes = 0;
7750  bytes += ast_adsi_logo(buf);
7751  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
7752  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
7753  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7754  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7755 #endif
7756  bytes = 0;
7757  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
7758  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
7759  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advanced", "3", 1);
7760  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
7761  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
7762  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
7763  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
7764 
7765 #ifdef DISPLAY
7766  /* Add another dot */
7767  bytes = 0;
7768  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
7769  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7770 
7771  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7772  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7773 #endif
7774 
7775  bytes = 0;
7776  /* These buttons we load but don't use yet */
7777  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
7778  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
7779  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
7780  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
7781  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
7782  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
7783  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
7784 
7785 #ifdef DISPLAY
7786  /* Add another dot */
7787  bytes = 0;
7788  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
7789  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7790  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7791 #endif
7792 
7793  bytes = 0;
7794  for (x = 0; x < 5; x++) {
7795  snprintf(num, sizeof(num), "%d", x);
7796  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
7797  }
7798  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
7799  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
7800 
7801 #ifdef DISPLAY
7802  /* Add another dot */
7803  bytes = 0;
7804  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
7805  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7806  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7807 #endif
7808 
7809  if (ast_adsi_end_download(chan)) {
7810  bytes = 0;
7811  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
7812  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
7813  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7814  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7815  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7816  return 0;
7817  }
7818  bytes = 0;
7819  bytes += ast_adsi_download_disconnect(buf + bytes);
7820  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7821  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
7822 
7823  ast_debug(1, "Done downloading scripts...\n");
7824 
7825 #ifdef DISPLAY
7826  /* Add last dot */
7827  bytes = 0;
7828  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
7829  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7830 #endif
7831  ast_debug(1, "Restarting session...\n");
7832 
7833  bytes = 0;
7834  /* Load the session now */
7835  if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
7836  *useadsi = 1;
7837  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
7838  } else
7839  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
7840 
7841  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7842  return 0;
7843 }
7844 
7845 static void adsi_begin(struct ast_channel *chan, int *useadsi)
7846 {
7847  int x;
7848  if (!ast_adsi_available(chan))
7849  return;
7850  x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
7851  if (x < 0) {
7852  *useadsi = 0;
7853  ast_channel_adsicpe_set(chan, AST_ADSI_UNAVAILABLE);
7854  return;
7855  }
7856  if (!x) {
7857  if (adsi_load_vmail(chan, useadsi)) {
7858  ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
7859  return;
7860  }
7861  } else
7862  *useadsi = 1;
7863 }
7864 
7865 static void adsi_login(struct ast_channel *chan)
7866 {
7867  unsigned char buf[256];
7868  int bytes = 0;
7869  unsigned char keys[8];
7870  int x;
7871  if (!ast_adsi_available(chan))
7872  return;
7873 
7874  for (x = 0; x < 8; x++)
7875  keys[x] = 0;
7876  /* Set one key for next */
7877  keys[3] = ADSI_KEY_APPS + 3;
7878 
7879  bytes += adsi_logo(buf + bytes);
7880  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
7881  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
7882  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7883  bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
7884  bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
7885  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
7886  bytes += ast_adsi_set_keys(buf + bytes, keys);
7887  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7888  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7889 }
7890 
7891 static void adsi_password(struct ast_channel *chan)
7892 {
7893  unsigned char buf[256];
7894  int bytes = 0;
7895  unsigned char keys[8];
7896  int x;
7897  if (!ast_adsi_available(chan))
7898  return;
7899 
7900  for (x = 0; x < 8; x++)
7901  keys[x] = 0;
7902  /* Set one key for next */
7903  keys[3] = ADSI_KEY_APPS + 3;
7904 
7905  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7906  bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
7907  bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
7908  bytes += ast_adsi_set_keys(buf + bytes, keys);
7909  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7910  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7911 }
7912 
7913 static void adsi_folders(struct ast_channel *chan, int start, char *label)
7914 {
7915  unsigned char buf[256];
7916  int bytes = 0;
7917  unsigned char keys[8];
7918  int x, y;
7919 
7920  if (!ast_adsi_available(chan))
7921  return;
7922 
7923  for (x = 0; x < 5; x++) {
7924  y = ADSI_KEY_APPS + 12 + start + x;
7925  if (y > ADSI_KEY_APPS + 12 + 4)
7926  y = 0;
7927  keys[x] = ADSI_KEY_SKT | y;
7928  }
7929  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
7930  keys[6] = 0;
7931  keys[7] = 0;
7932 
7933  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
7934  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
7935  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7936  bytes += ast_adsi_set_keys(buf + bytes, keys);
7937  bytes += ast_adsi_voice_mode(buf + bytes, 0);
7938 
7939  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
7940 }
7941 
7942 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
7943 {
7944  int bytes = 0;
7945  unsigned char buf[256];
7946  char buf1[256], buf2[256];
7947  char fn2[PATH_MAX];
7948 
7949  char cid[256] = "";
7950  char *val;
7951  char *name, *num;
7952  char datetime[21] = "";
7953  FILE *f;
7954 
7955  unsigned char keys[8];
7956 
7957  int x;
7958 
7959  if (!ast_adsi_available(chan))
7960  return;
7961 
7962  /* Retrieve important info */
7963  snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
7964  f = fopen(fn2, "r");
7965  if (f) {
7966  while (!feof(f)) {
7967  if (!fgets((char *) buf, sizeof(buf), f)) {
7968  continue;
7969  }
7970  if (!feof(f)) {
7971  char *stringp = NULL;
7972  stringp = (char *) buf;
7973  strsep(&stringp, "=");
7974  val = strsep(&stringp, "=");
7975  if (!ast_strlen_zero(val)) {
7976  if (!strcmp((char *) buf, "callerid"))
7977  ast_copy_string(cid, val, sizeof(cid));
7978  if (!strcmp((char *) buf, "origdate"))
7979  ast_copy_string(datetime, val, sizeof(datetime));
7980  }
7981  }
7982  }
7983  fclose(f);
7984  }
7985  /* New meaning for keys */
7986  for (x = 0; x < 5; x++)
7987  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
7988  keys[6] = 0x0;
7989  keys[7] = 0x0;
7990 
7991  if (!vms->curmsg) {
7992  /* No prev key, provide "Folder" instead */
7993  keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
7994  }
7995  if (vms->curmsg >= vms->lastmsg) {
7996  /* If last message ... */
7997  if (vms->curmsg) {
7998  /* but not only message, provide "Folder" instead */
7999  keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8000  bytes += ast_adsi_voice_mode(buf + bytes, 0);
8001 
8002  } else {
8003  /* Otherwise if only message, leave blank */
8004  keys[3] = 1;
8005  }
8006  }
8007 
8008  if (!ast_strlen_zero(cid)) {
8009  ast_callerid_parse(cid, &name, &num);
8010  if (!name)
8011  name = num;
8012  } else {
8013  name = "Unknown Caller";
8014  }
8015 
8016  /* If deleted, show "undeleted" */
8017 #ifdef IMAP_STORAGE
8018  ast_mutex_lock(&vms->lock);
8019 #endif
8020  if (vms->deleted[vms->curmsg]) {
8021  keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
8022  }
8023 #ifdef IMAP_STORAGE
8024  ast_mutex_unlock(&vms->lock);
8025 #endif
8026 
8027  /* Except "Exit" */
8028  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
8029  snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
8030  strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
8031  snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
8032 
8033  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8034  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8035  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
8036  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
8037  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8038  bytes += ast_adsi_set_keys(buf + bytes, keys);
8039  bytes += ast_adsi_voice_mode(buf + bytes, 0);
8040 
8041  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
8042 }
8043 
8044 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
8045 {
8046  int bytes = 0;
8047  unsigned char buf[256];
8048  unsigned char keys[8];
8049 
8050  int x;
8051 
8052  if (!ast_adsi_available(chan))
8053  return;
8054 
8055  /* New meaning for keys */
8056  for (x = 0; x < 5; x++)
8057  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
8058 
8059  keys[6] = 0x0;
8060  keys[7] = 0x0;
8061 
8062  if (!vms->curmsg) {
8063  /* No prev key, provide "Folder" instead */
8064  keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8065  }
8066  if (vms->curmsg >= vms->lastmsg) {
8067  /* If last message ... */
8068  if (vms->curmsg) {
8069  /* but not only message, provide "Folder" instead */
8070  keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8071  } else {
8072  /* Otherwise if only message, leave blank */
8073  keys[3] = 1;
8074  }
8075  }
8076 
8077  /* If deleted, show "undeleted" */
8078 #ifdef IMAP_STORAGE
8079  ast_mutex_lock(&vms->lock);
8080 #endif
8081  if (vms->deleted[vms->curmsg]) {
8082  keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
8083  }
8084 #ifdef IMAP_STORAGE
8085  ast_mutex_unlock(&vms->lock);
8086 #endif
8087 
8088  /* Except "Exit" */
8089  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
8090  bytes += ast_adsi_set_keys(buf + bytes, keys);
8091  bytes += ast_adsi_voice_mode(buf + bytes, 0);
8092 
8093  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
8094 }
8095 
8096 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
8097 {
8098  unsigned char buf[256] = "";
8099  char buf1[256] = "", buf2[256] = "";
8100  int bytes = 0;
8101  unsigned char keys[8];
8102  int x;
8103 
8104  char *newm = (vms->newmessages == 1) ? "message" : "messages";
8105  char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
8106  if (!ast_adsi_available(chan))
8107  return;
8108  if (vms->newmessages) {
8109  snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
8110  if (vms->oldmessages) {
8111  strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
8112  snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
8113  } else {
8114  snprintf(buf2, sizeof(buf2), "%s.", newm);
8115  }
8116  } else if (vms->oldmessages) {
8117  snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
8118  snprintf(buf2, sizeof(buf2), "%s.", oldm);
8119  } else {
8120  strcpy(buf1, "You have no messages.");
8121  buf2[0] = ' ';
8122  buf2[1] = '\0';
8123  }
8124  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8125  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8126  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8127 
8128  for (x = 0; x < 6; x++)
8129  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
8130  keys[6] = 0;
8131  keys[7] = 0;
8132 
8133  /* Don't let them listen if there are none */
8134  if (vms->lastmsg < 0)
8135  keys[0] = 1;
8136  bytes += ast_adsi_set_keys(buf + bytes, keys);
8137 
8138  bytes += ast_adsi_voice_mode(buf + bytes, 0);
8139 
8140  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
8141 }
8142 
8143 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
8144 {
8145  unsigned char buf[256] = "";
8146  char buf1[256] = "", buf2[256] = "";
8147  int bytes = 0;
8148  unsigned char keys[8];
8149  int x;
8150 
8151  char *mess = (vms->lastmsg == 0) ? "message" : "messages";
8152 
8153  if (!ast_adsi_available(chan))
8154  return;
8155 
8156  /* Original command keys */
8157  for (x = 0; x < 6; x++)
8158  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
8159 
8160  keys[6] = 0;
8161  keys[7] = 0;
8162 
8163  if ((vms->lastmsg + 1) < 1)
8164  keys[0] = 0;
8165 
8166  snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
8167  strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
8168 
8169  if (vms->lastmsg + 1)
8170  snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
8171  else
8172  strcpy(buf2, "no messages.");
8173  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8174  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8175  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
8176  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8177  bytes += ast_adsi_set_keys(buf + bytes, keys);
8178 
8179  bytes += ast_adsi_voice_mode(buf + bytes, 0);
8180 
8181  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
8182 
8183 }
8184 
8185 /*
8186 static void adsi_clear(struct ast_channel *chan)
8187 {
8188  char buf[256];
8189  int bytes=0;
8190  if (!ast_adsi_available(chan))
8191  return;
8192  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8193  bytes += ast_adsi_voice_mode(buf + bytes, 0);
8194 
8195  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
8196 }
8197 */
8198 
8199 static void adsi_goodbye(struct ast_channel *chan)
8200 {
8201  unsigned char buf[256];
8202  int bytes = 0;
8203 
8204  if (!ast_adsi_available(chan))
8205  return;
8206  bytes += adsi_logo(buf + bytes);
8207  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
8208  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
8209  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8210  bytes += ast_adsi_voice_mode(buf + bytes, 0);
8211 
8212  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
8213 }
8214 
8215 /*!\brief get_folder: Folder menu
8216  * Plays "press 1 for INBOX messages" etc.
8217  * Should possibly be internationalized
8218  */
8219 static int get_folder(struct ast_channel *chan, int start)
8220 {
8221  int x;
8222  int d;
8223  char fn[PATH_MAX];
8224  d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
8225  if (d)
8226  return d;
8227  for (x = start; x < 5; x++) { /* For all folders */
8228  if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), NULL)))
8229  return d;
8230  d = ast_play_and_wait(chan, "vm-for"); /* "for" */
8231  if (d)
8232  return d;
8233  snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
8234 
8235  /* The inbox folder can have its name changed under certain conditions
8236  * so this checks if the sound file exists for the inbox folder name and
8237  * if it doesn't, plays the default name instead. */
8238  if (x == 0) {
8239  if (ast_fileexists(fn, NULL, NULL)) {
8240  d = vm_play_folder_name(chan, fn);
8241  } else {
8242  ast_verb(4, "Failed to find file %s; falling back to INBOX\n", fn);
8243  d = vm_play_folder_name(chan, "vm-INBOX");
8244  }
8245  } else {
8246  ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
8247  d = vm_play_folder_name(chan, fn);
8248  }
8249 
8250  if (d)
8251  return d;
8252  d = ast_waitfordigit(chan, 500);
8253  if (d)
8254  return d;
8255  }
8256 
8257  d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
8258  if (d)
8259  return d;
8260  d = ast_waitfordigit(chan, 4000);
8261  return d;
8262 }
8263 
8264 /* Japanese Syntax */
8265 static int get_folder_ja(struct ast_channel *chan, int start)
8266 {
8267  int x;
8268  int d;
8269  char fn[256];
8270  for (x = start; x < 5; x++) { /* For all folders */
8271  if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL))) {
8272  return d;
8273  }
8274  snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
8275  d = vm_play_folder_name(chan, fn);
8276  if (d) {
8277  return d;
8278  }
8279  d = ast_waitfordigit(chan, 500);
8280  if (d) {
8281  return d;
8282  }
8283  }
8284  d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
8285  if (d) {
8286  return d;
8287  }
8288  d = ast_waitfordigit(chan, 4000);
8289  return d;
8290 }
8291 
8292 /*!
8293  * \brief plays a prompt and waits for a keypress.
8294  * \param chan
8295  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
8296  * \param start Does not appear to be used at this time.
8297  *
8298  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
8299  * After playing the message identified by the fn parameter value, it calls get_folder(), which plays the
8300  * prompting for the number inputs that correspond to the available folders.
8301  *
8302  * \return zero on success, or -1 on error.
8303  */
8304 static int get_folder2(struct ast_channel *chan, char *fn, int start)
8305 {
8306  int res = 0;
8307  int loops = 0;
8308 
8309  res = ast_play_and_wait(chan, fn); /* Folder name */
8310  while (((res < '0') || (res > '9')) &&
8311  (res != '#') && (res >= 0) &&
8312  loops < 4) {
8313  /* res = get_folder(chan, 0); */
8314  if (!strcasecmp(ast_channel_language(chan), "ja")) { /* Japanese syntax */
8315  res = get_folder_ja(chan, 0);
8316  } else { /* Default syntax */
8317  res = get_folder(chan, 0);
8318  }
8319  loops++;
8320  }
8321  if (loops == 4) { /* give up */
8322  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
8323  return '#';
8324  }
8325  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8326  isprint(res) ? res : '?', isprint(res) ? res : '?');
8327  return res;
8328 }
8329 
8330 /*!
8331  * \brief presents the option to prepend to an existing message when forwarding it.
8332  * \param chan
8333  * \param vmu
8334  * \param curdir
8335  * \param curmsg
8336  * \param vm_fmts
8337  * \param context
8338  * \param record_gain
8339  * \param duration
8340  * \param vms
8341  * \param flag
8342  *
8343  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
8344  *
8345  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
8346  * \return zero on success, -1 on error.
8347  */
8348 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir,
8349  int curmsg, char *vm_fmts, char *context, signed char record_gain, long *duration,
8350  struct vm_state *vms, char *flag)
8351 {
8352  int cmd = 0;
8353  int retries = 0, prepend_duration = 0, already_recorded = 0;
8354  char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
8355  char textfile[PATH_MAX];
8356  struct ast_config *msg_cfg;
8357  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8358 #ifndef IMAP_STORAGE
8359  signed char zero_gain = 0;
8360 #else
8361  const char *msg_id = NULL;
8362 #endif
8363  const char *duration_str;
8364  SCOPE_ENTER(3, "mbox: %s msgnum: %d curdir: %s", vmu->mailbox, curmsg, curdir);
8365 
8366  /* Must always populate duration correctly */
8367  make_file(msgfile, sizeof(msgfile), curdir, curmsg);
8368  ast_trace(-1, "msgfile: %s\n", msgfile);
8369  strcpy(textfile, msgfile);
8370  strcpy(backup, msgfile);
8371  strcpy(backup_textfile, msgfile);
8372  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
8373  strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
8374  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
8375 
8376  if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
8377  *duration = atoi(duration_str);
8378  } else {
8379  *duration = 0;
8380  }
8381 
8382  while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
8383  if (cmd)
8384  retries = 0;
8385  switch (cmd) {
8386  case '1':
8387 
8388 #ifdef IMAP_STORAGE
8389  /* Record new intro file */
8390  if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
8391  msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
8392  }
8393  make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
8394  strncat(vms->introfn, "intro", sizeof(vms->introfn));
8395  ast_play_and_wait(chan, "vm-record-prepend");
8396  ast_play_and_wait(chan, "beep");
8397  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);
8398  if (cmd == -1) {
8399  break;
8400  }
8401  cmd = 't';
8402 #else
8403 
8404  /* prepend a message to the current message, update the metadata and return */
8405  ast_trace(-1, "Prepending to message %d\n", curmsg);
8406 
8407  make_file(msgfile, sizeof(msgfile), curdir, curmsg);
8408  ast_trace(-1, "msgfile: %s\n", msgfile);
8409 
8410  strcpy(textfile, msgfile);
8411  strncat(textfile, ".txt", sizeof(textfile) - 1);
8412  *duration = 0;
8413 
8414  /* if we can't read the message metadata, stop now */
8415  if (!valid_config(msg_cfg)) {
8416  cmd = 0;
8417  break;
8418  }
8419 
8420  /* Back up the original file, so we can retry the prepend and restore it after forward. */
8421 #ifndef IMAP_STORAGE
8422  if (already_recorded) {
8423  ast_trace(-1, "Restoring '%s' to '%s'\n", backup, msgfile);
8424  ast_filecopy(backup, msgfile, NULL);
8425  copy(backup_textfile, textfile);
8426  }
8427  else {
8428  ast_trace(-1, "Backing up '%s' to '%s'\n", backup, msgfile);
8429  ast_filecopy(msgfile, backup, NULL);
8430  copy(textfile, backup_textfile);
8431  }
8432 #endif
8433  already_recorded = 1;
8434 
8435  if (record_gain)
8436  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
8437 
8438  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, ast_play_and_prepend, chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
8439 
8440  if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
8441  ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
8442  ast_stream_and_wait(chan, vm_prepend_timeout, "");
8443  ast_filerename(backup, msgfile, NULL);
8444  }
8445 
8446  if (record_gain)
8447  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
8448 
8449 
8450  if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
8451  *duration = atoi(duration_str);
8452 
8453  if (prepend_duration) {
8454  struct ast_category *msg_cat;
8455  /* need enough space for a maximum-length message duration */
8456  char duration_buf[12];
8457 
8458  *duration += prepend_duration;
8459  ast_trace(-1, "Prepending duration: %d total duration: %ld\n", prepend_duration, *duration);
8460  msg_cat = ast_category_get(msg_cfg, "message", NULL);
8461  snprintf(duration_buf, sizeof(duration_buf), "%ld", *duration);
8462  if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
8463  ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
8464  }
8465  }
8466 
8467 #endif
8468  break;
8469  case '2':
8470  /* NULL out introfile so we know there is no intro! */
8471 #ifdef IMAP_STORAGE
8472  *vms->introfn = '\0';
8473 #endif
8474  cmd = 't';
8475  break;
8476  case '*':
8477  cmd = '*';
8478  break;
8479  default:
8480  /* If time_out and return to menu, reset already_recorded */
8481  already_recorded = 0;
8482 
8483  cmd = ast_play_and_wait(chan, "vm-forwardoptions");
8484  /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
8485  if (!cmd) {
8486  cmd = ast_play_and_wait(chan, "vm-starmain");
8487  /* "press star to return to the main menu" */
8488  }
8489  if (!cmd) {
8490  cmd = ast_waitfordigit(chan, 6000);
8491  }
8492  if (!cmd) {
8493  retries++;
8494  }
8495  if (retries > 3) {
8496  cmd = '*'; /* Let's cancel this beast */
8497  }
8498  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8499  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
8500  }
8501  }
8502 
8503  if (valid_config(msg_cfg))
8504  ast_config_destroy(msg_cfg);
8505  if (prepend_duration)
8506  *duration = prepend_duration;
8507 
8508  if (already_recorded && cmd == -1) {
8509  /* restore original message if prepention cancelled */
8510  ast_trace(-1, "Restoring '%s' to '%s'\n", backup, msgfile);
8511  ast_filerename(backup, msgfile, NULL);
8512  rename(backup_textfile, textfile);
8513  }
8514 
8515  if (cmd == 't' || cmd == 'S') { /* XXX entering this block with a value of 'S' is probably no longer possible. */
8516  cmd = 0;
8517  }
8518  SCOPE_EXIT_RTN_VALUE(cmd, "Done. CMD: %d %c\n", cmd, cmd >= 32 && cmd < 127 ? cmd : '?');
8519 }
8520 
8521 static void queue_mwi_event(const char *channel_id, const char *box, int urgent, int new, int old)
8522 {
8523  char *mailbox;
8524  char *context;
8525 
8526  if (separate_mailbox(ast_strdupa(box), &mailbox, &context)) {
8527  return;
8528  }
8529 
8530  ast_debug(3, "Queueing event for mailbox %s New: %d Old: %d\n", box, new + urgent, old);
8531  ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
8532 
8533  if (!ast_strlen_zero(aliasescontext)) {
8534  struct ao2_iterator *aliases;
8535  struct mailbox_alias_mapping *mapping;
8536 
8537  aliases = ao2_find(mailbox_alias_mappings, box, OBJ_SEARCH_KEY | OBJ_MULTIPLE);
8538  while ((mapping = ao2_iterator_next(aliases))) {
8539  char alias[strlen(mapping->alias) + 1];
8540  strcpy(alias, mapping->alias); /* safe */
8541  mailbox = NULL;
8542  context = NULL;
8543  ast_debug(3, "Found alias mapping: %s -> %s\n", mapping->alias, box);
8544  separate_mailbox(alias, &mailbox, &context);
8545  ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
8546  ao2_ref(mapping, -1);
8547  }
8548  ao2_iterator_destroy(aliases);
8549  }
8550 }
8551 
8552 /*!
8553  * \brief Sends email notification that a user has a new voicemail waiting for them.
8554  * \param chan
8555  * \param vmu
8556  * \param vms
8557  * \param msgnum
8558  * \param duration
8559  * \param fmt
8560  * \param cidnum The Caller ID phone number value.
8561  * \param cidname The Caller ID name value.
8562  * \param flag
8563  *
8564  * \return zero on success, -1 on error.
8565  */
8566 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)
8567 {
8568  char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
8569  int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
8570  const char *category;
8571  char *myserveremail = serveremail;
8572 
8573  ast_channel_lock(chan);
8574  if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
8575  category = ast_strdupa(category);
8576  }
8577  ast_channel_unlock(chan);
8578 
8579 #ifndef IMAP_STORAGE
8580  make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
8581 #else
8582  snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
8583 #endif
8584  make_file(fn, sizeof(fn), todir, msgnum);
8585  snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
8586 
8587  if (!ast_strlen_zero(vmu->attachfmt)) {
8588  if (strstr(fmt, vmu->attachfmt))
8589  fmt = vmu->attachfmt;
8590  else
8591  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);
8592  }
8593 
8594  /* Attach only the first format */
8595  fmt = ast_strdupa(fmt);
8596  stringp = fmt;
8597  strsep(&stringp, "|");
8598 
8599  if (!ast_strlen_zero(vmu->serveremail))
8600  myserveremail = vmu->serveremail;
8601 
8602  if (!ast_strlen_zero(vmu->email)) {
8603  int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
8604  char *msg_id = NULL;
8605 #ifdef IMAP_STORAGE
8606  struct ast_config *msg_cfg;
8607  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8608  char filename[PATH_MAX];
8609 
8610  snprintf(filename, sizeof(filename), "%s.txt", fn);
8611  msg_cfg = ast_config_load(filename, config_flags);
8612  if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
8613  msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
8614  ast_config_destroy(msg_cfg);
8615  }
8616 #endif
8617 
8618  if (attach_user_voicemail)
8619  RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
8620 
8621  /* XXX possible imap issue, should category be NULL XXX */
8622  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);
8623 
8624  if (attach_user_voicemail)
8625  DISPOSE(todir, msgnum);
8626  }
8627 
8628  if (!ast_strlen_zero(vmu->pager)) {
8629  sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
8630  }
8631 
8632  if (ast_test_flag(vmu, VM_DELETE))
8633  DELETE(todir, msgnum, fn, vmu);
8634 
8635  /* Leave voicemail for someone */
8636  if (ast_app_has_voicemail(ext_context, NULL))
8637  ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
8638 
8639  queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgentmsgs, newmsgs, oldmsgs);
8640  run_externnotify(vmu->context, vmu->mailbox, flag);
8641 
8642 #ifdef IMAP_STORAGE
8643  vm_delete(fn); /* Delete the file, but not the IMAP message */
8644  if (ast_test_flag(vmu, VM_DELETE)) { /* Delete the IMAP message if delete = yes */
8645  vm_imap_delete(NULL, vms->curmsg, vmu);
8646  vms->newmessages--; /* Fix new message count */
8647  }
8648 #endif
8649 
8650  return 0;
8651 }
8652 
8653 /*!
8654  * \brief Sends a voicemail message to a mailbox recipient.
8655  * \param chan
8656  * \param context
8657  * \param vms
8658  * \param sender
8659  * \param fmt
8660  * \param is_new_message Used to indicate the mode for which this method was invoked.
8661  * Will be 0 when called to forward an existing message (option 8)
8662  * Will be 1 when called to leave a message (option 3->5)
8663  * \param record_gain
8664  * \param urgent
8665  *
8666  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
8667  *
8668  * When in the leave message mode (is_new_message == 1):
8669  * - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
8670  * - attempt to determine the context and mailbox, and then invoke leave_message() function to record and store the message.
8671  *
8672  * When in the forward message mode (is_new_message == 0):
8673  * - retrieves the current message to be forwarded
8674  * - copies the original message to a temporary file, so updates to the envelope can be done.
8675  * - determines the target mailbox and folders
8676  * - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
8677  *
8678  * \return zero on success, -1 on error.
8679  */
8680 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)
8681 {
8682 #ifdef IMAP_STORAGE
8683  int todircount = 0;
8684  struct vm_state *dstvms;
8685 #endif
8686  char username[70]="";
8687  char fn[PATH_MAX]; /* for playback of name greeting */
8688  char ecodes[16] = "#";
8689  int res = 0, cmd = 0;
8690  struct ast_vm_user *receiver = NULL, *vmtmp;
8692  char *stringp;
8693  const char *s;
8694  const char mailbox_context[256];
8695  int saved_messages = 0;
8696  int valid_extensions = 0;
8697  char *dir;
8698  int curmsg;
8699  char urgent_str[7] = "";
8700  int prompt_played = 0;
8701 #ifndef IMAP_STORAGE
8702  char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
8703 #endif
8704  SCOPE_ENTER(3, "%s: user: %s dir: %s msg: %d\n", ast_channel_name(chan),
8705  vms->username, vms->curdir, vms->curmsg);
8706 
8707  if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
8708  ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
8709  }
8710 
8711  if (vms == NULL) {
8712  SCOPE_EXIT_RTN_VALUE(-1, "vms is NULL\n");
8713  }
8714  dir = vms->curdir;
8715  curmsg = vms->curmsg;
8716 
8717  ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
8718  while (!res && !valid_extensions) {
8719  int use_directory = 0;
8720  if (ast_test_flag((&globalflags), VM_DIRECTFORWARD)) {
8721  int done = 0;
8722  int retries = 0;
8723  cmd = 0;
8724  while ((cmd >= 0) && !done ){
8725  if (cmd)
8726  retries = 0;
8727  switch (cmd) {
8728  case '1':
8729  use_directory = 0;
8730  done = 1;
8731  break;
8732  case '2':
8733  use_directory = 1;
8734  done = 1;
8735  break;
8736  case '*':
8737  cmd = 't';
8738  done = 1;
8739  break;
8740  default:
8741  /* Press 1 to enter an extension press 2 to use the directory */
8742  cmd = ast_play_and_wait(chan, "vm-forward");
8743  if (!cmd) {
8744  cmd = ast_waitfordigit(chan, 3000);
8745  }
8746  if (!cmd) {
8747  retries++;
8748  }
8749  if (retries > 3) {
8750  cmd = 't';
8751  done = 1;
8752  }
8753  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8754  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
8755  }
8756  }
8757  if (cmd < 0 || cmd == 't')
8758  break;
8759  }
8760 
8761  if (use_directory) {
8762  /* use app_directory */
8763 
8764  struct ast_app* directory_app;
8765 
8766  directory_app = pbx_findapp("Directory");
8767  if (directory_app) {
8768  char vmcontext[256];
8769  char old_context[strlen(ast_channel_context(chan)) + 1];
8770  char old_exten[strlen(ast_channel_exten(chan)) + 1];
8771  int old_priority;
8772  /* make backup copies */
8773  strcpy(old_context, ast_channel_context(chan)); /* safe */
8774  strcpy(old_exten, ast_channel_exten(chan)); /* safe */
8775  old_priority = ast_channel_priority(chan);
8776 
8777  /* call the Directory, changes the channel */
8778  snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
8779  res = pbx_exec(chan, directory_app, vmcontext);
8780 
8781  ast_copy_string(username, ast_channel_exten(chan), sizeof(username));
8782 
8783  /* restore the old context, exten, and priority */
8784  ast_channel_context_set(chan, old_context);
8785  ast_channel_exten_set(chan, old_exten);
8786  ast_channel_priority_set(chan, old_priority);
8787  } else {
8788  ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
8789  ast_clear_flag((&globalflags), VM_DIRECTFORWARD);
8790  }
8791  } else {
8792  /* Ask for an extension */
8793  res = ast_streamfile(chan, "vm-extension", ast_channel_language(chan)); /* "extension" */
8794  prompt_played++;
8795  if (res || prompt_played > 4)
8796  break;
8797  if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#")) < 0)
8798  break;
8799  }
8800 
8801  /* start all over if no username */
8802  if (ast_strlen_zero(username))
8803  continue;
8804  stringp = username;
8805  s = strsep(&stringp, "*");
8806  /* start optimistic */
8807  valid_extensions = 1;
8808  while (s) {
8809  snprintf((char*)mailbox_context, sizeof(mailbox_context), "%s@%s", s, context ? context : "default");
8810  if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
8811  int oldmsgs;
8812  int newmsgs;
8813  int capacity;
8814 
8815  if (inboxcount(mailbox_context, &newmsgs, &oldmsgs)) {
8816  ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", mailbox_context);
8817  /* Shouldn't happen, but allow trying another extension if it does */
8818  res = ast_play_and_wait(chan, "pbx-invalid");
8819  valid_extensions = 0;
8820  break;
8821  }
8822 #ifdef IMAP_STORAGE
8823  if (!(dstvms = get_vm_state_by_mailbox(s, context, 0))) {
8824  if (!(dstvms = create_vm_state_from_user(receiver))) {
8825  ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
8826  /* Shouldn't happen, but allow trying another extension if it does */
8827  res = ast_play_and_wait(chan, "pbx-invalid");
8828  valid_extensions = 0;
8829  break;
8830  }
8831  }
8832  check_quota(dstvms, imapfolder);
8833  if (dstvms->quota_limit && dstvms->quota_usage >= dstvms->quota_limit) {
8834  ast_log(LOG_NOTICE, "Mailbox '%s' is exceeded quota %u >= %u\n", mailbox_context, dstvms->quota_usage, dstvms->quota_limit);
8835  res = ast_play_and_wait(chan, "vm-mailboxfull");
8836  valid_extensions = 0;
8837  while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8838  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8839  free_user(vmtmp);
8840  }
8841  break;
8842  }
8843 #endif
8844  capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
8845  if ((newmsgs + oldmsgs) >= capacity) {
8846  ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", mailbox_context, capacity);
8847  res = ast_play_and_wait(chan, "vm-mailboxfull");
8848  valid_extensions = 0;
8849  while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8850  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8851  free_user(vmtmp);
8852  }
8853  inprocess_count(receiver->mailbox, receiver->context, -1);
8854  break;
8855  }
8856  AST_LIST_INSERT_HEAD(&extensions, receiver, list);
8857  } else {
8858  /* XXX Optimization for the future. When we encounter a single bad extension,
8859  * bailing out on all of the extensions may not be the way to go. We should
8860  * probably just bail on that single extension, then allow the user to enter
8861  * several more. XXX
8862  */
8863  while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8864  free_user(receiver);
8865  }
8866  ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", mailbox_context);
8867  /* "I am sorry, that's not a valid extension. Please try again." */
8868  res = ast_play_and_wait(chan, "pbx-invalid");
8869  valid_extensions = 0;
8870  break;
8871  }
8872 
8873  /* play name if available, else play extension number */
8874  snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
8875  SCOPE_CALL(-1, RETRIEVE, fn, -1, s, receiver->context);
8876  if (ast_fileexists(fn, NULL, NULL) > 0) {
8877  res = ast_stream_and_wait(chan, fn, ecodes);
8878  if (res) {
8879  SCOPE_CALL(-1, DISPOSE, fn, -1);
8880  return res;
8881  }
8882  } else {
8883  res = ast_say_digit_str(chan, s, ecodes, ast_channel_language(chan));
8884  }
8885  SCOPE_CALL(-1, DISPOSE, fn, -1);
8886 
8887  s = strsep(&stringp, "*");
8888  }
8889  /* break from the loop of reading the extensions */
8890  if (valid_extensions)
8891  break;
8892  }
8893  /* check if we're clear to proceed */
8894  if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
8895  return res;
8896  if (is_new_message == 1) {
8897  struct leave_vm_options leave_options;
8898  char mailbox[AST_MAX_EXTENSION * 2 + 2];
8899  snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
8900 
8901  /* Send VoiceMail */
8902  memset(&leave_options, 0, sizeof(leave_options));
8903  leave_options.record_gain = record_gain;
8904  leave_options.beeptone = "beep";
8905  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, leave_voicemail, chan, mailbox, &leave_options);
8906  } else {
8907  /* Forward VoiceMail */
8908  long duration = 0;
8909  struct vm_state vmstmp;
8910  int copy_msg_result = 0;
8911 #ifdef IMAP_STORAGE
8912  char filename[PATH_MAX];
8913  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8914  const char *msg_id = NULL;
8915  struct ast_config *msg_cfg;
8916 #endif
8917  memcpy(&vmstmp, vms, sizeof(vmstmp));
8918 
8919  SCOPE_CALL(-1, RETRIEVE, dir, curmsg, sender->mailbox, sender->context);
8920 #ifdef IMAP_STORAGE
8921  make_file(filename, sizeof(filename), dir, curmsg);
8922  strncat(filename, ".txt", sizeof(filename) - strlen(filename) - 1);
8923  msg_cfg = ast_config_load(filename, config_flags);
8924  if (msg_cfg && msg_cfg == CONFIG_STATUS_FILEINVALID) {
8925  msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
8926  ast_config_destroy(msg_cfg);
8927  }
8928 #endif
8929 
8930  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, vm_forwardoptions, chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
8931  if (!cmd) {
8932  AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
8933 #ifdef IMAP_STORAGE
8934  int attach_user_voicemail;
8935  char *myserveremail = serveremail;
8936 
8937  /* get destination mailbox */
8938  dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
8939  if (!dstvms) {
8940  dstvms = create_vm_state_from_user(vmtmp);
8941  }
8942  if (dstvms) {
8943  init_mailstream(dstvms, 0);
8944  if (!dstvms->mailstream) {
8945  ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
8946  } else {
8947  copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
8948  run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str);
8949  }
8950  } else {
8951  ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
8952  }
8953  if (!ast_strlen_zero(vmtmp->serveremail))
8954  myserveremail = vmtmp->serveremail;
8955  attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
8956  /* NULL category for IMAP storage */
8957  sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
8958  dstvms->curbox,
8959  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
8960  S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
8961  vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
8962  NULL, urgent_str, msg_id);
8963 #else
8964  copy_msg_result = SCOPE_CALL_WITH_INT_RESULT(-1, copy_message, chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str, NULL);
8965 #endif
8966  saved_messages++;
8968  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8969  free_user(vmtmp);
8970  if (res)
8971  break;
8972  }
8974  if (saved_messages > 0 && !copy_msg_result) {
8975  /* give confirmation that the message was saved */
8976  /* commented out since we can't forward batches yet
8977  if (saved_messages == 1)
8978  res = ast_play_and_wait(chan, "vm-message");
8979  else
8980  res = ast_play_and_wait(chan, "vm-messages");
8981  if (!res)
8982  res = ast_play_and_wait(chan, "vm-saved"); */
8983  res = ast_play_and_wait(chan, "vm-msgforwarded");
8984  }
8985 #ifndef IMAP_STORAGE
8986  else {
8987  /* with IMAP, mailbox full warning played by imap_check_limits */
8988  res = ast_play_and_wait(chan, "vm-mailboxfull");
8989  }
8990  /* Restore original message without prepended message if backup exists */
8991  make_file(msgfile, sizeof(msgfile), dir, curmsg);
8992  strcpy(textfile, msgfile);
8993  strcpy(backup, msgfile);
8994  strcpy(backup_textfile, msgfile);
8995  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
8996  strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
8997  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
8998  if (ast_fileexists(backup, NULL, NULL) > 0) {
8999  ast_filerename(backup, msgfile, NULL);
9000  rename(backup_textfile, textfile);
9001  }
9002 #endif
9003  }
9004  SCOPE_CALL(-1, DISPOSE, dir, curmsg);
9005 #ifndef IMAP_STORAGE
9006  if (cmd) { /* assuming hangup, cleanup backup file */
9007  make_file(msgfile, sizeof(msgfile), dir, curmsg);
9008  strcpy(textfile, msgfile);
9009  strcpy(backup_textfile, msgfile);
9010  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
9011  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
9012  rename(backup_textfile, textfile);
9013  }
9014 #endif
9015  }
9016 
9017  /* If anything failed above, we still have this list to free */
9018  while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
9019  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
9020  free_user(vmtmp);
9021  }
9022  SCOPE_EXIT_RTN_VALUE(res ? res : cmd, "Done. res: %d cmd: %d\n", res, cmd);
9023 }
9024 
9025 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
9026 {
9027  int res;
9028  if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
9029  ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file);
9030  return res;
9031 }
9032 
9033 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
9034 {
9035  ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
9036  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);
9037 }
9038 
9039 static int play_message_category(struct ast_channel *chan, const char *category)
9040 {
9041  int res = 0;
9042 
9043  if (!ast_strlen_zero(category))
9044  res = ast_play_and_wait(chan, category);
9045 
9046  if (res) {
9047  ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
9048  res = 0;
9049  }
9050 
9051  return res;
9052 }
9053 
9054 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
9055 {
9056  int res = 0;
9057  struct vm_zone *the_zone = NULL;
9058  time_t t;
9059 
9060  if (ast_get_time_t(origtime, &t, 0, NULL)) {
9061  ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
9062  return 0;
9063  }
9064 
9065  /* Does this user have a timezone specified? */
9066  if (!ast_strlen_zero(vmu->zonetag)) {
9067  /* Find the zone in the list */
9068  struct vm_zone *z;
9069  AST_LIST_LOCK(&zones);
9070  AST_LIST_TRAVERSE(&zones, z, list) {
9071  if (!strcmp(z->name, vmu->zonetag)) {
9072  the_zone = z;
9073  break;
9074  }
9075  }
9077  }
9078 
9079 /* No internal variable parsing for now, so we'll comment it out for the time being */
9080 #if 0
9081  /* Set the DIFF_* variables */
9082  ast_localtime(&t, &time_now, NULL);
9083  tv_now = ast_tvnow();
9084  ast_localtime(&tv_now, &time_then, NULL);
9085 
9086  /* Day difference */
9087  if (time_now.tm_year == time_then.tm_year)
9088  snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
9089  else
9090  snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
9091  pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
9092 
9093  /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
9094 #endif
9095  if (the_zone) {
9096  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), the_zone->msg_format, the_zone->timezone);
9097  } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
9098  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9099  } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
9100  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q H 'digits/kai' M ", NULL);
9101  } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */
9102  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9103  } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
9104  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);
9105  } else if (!strcasecmp(ast_channel_language(chan),"ja")) { /* Japanese syntax */
9106  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "PHM q 'jp-ni' 'vm-received'", NULL);
9107  } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
9108  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/nl-om' HM", NULL);
9109  } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
9110  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9111  } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
9112  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q HM", NULL);
9113  } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* Brazilian PORTUGUESE syntax */
9114  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);
9115  } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
9116  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' dB 'digits/at' k 'and' M", NULL);
9117  } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
9118  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "qR 'vm-received'", NULL);
9119  } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
9120  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);
9121  } else {
9122  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' IMp", NULL);
9123  }
9124 #if 0
9125  pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
9126 #endif
9127  return res;
9128 }
9129 
9130 
9131 
9132 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback, int saycidnumber)
9133 {
9134  int res = 0;
9135  int i;
9136  char *callerid, *name;
9137  char prefile[PATH_MAX] = "";
9138 
9139  /* If voicemail cid is not enabled, or we didn't get cid or context from
9140  * the attribute file, leave now.
9141  *
9142  * TODO Still need to change this so that if this function is called by the
9143  * message envelope (and someone is explicitly requesting to hear the CID),
9144  * it does not check to see if CID is enabled in the config file.
9145  */
9146  if ((cid == NULL)||(context == NULL))
9147  return res;
9148 
9149  /* Strip off caller ID number from name */
9150  ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
9151  ast_callerid_parse(cid, &name, &callerid);
9152  if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
9153  /* Check for internal contexts and only */
9154  /* say extension when the call didn't come from an internal context in the list */
9155  for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
9156  ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
9157  if ((strcmp(cidinternalcontexts[i], context) == 0))
9158  break;
9159  }
9160  if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
9161  if (!res) {
9162  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
9163  if (!ast_strlen_zero(prefile)) {
9164  /* See if we can find a recorded name for this callerid
9165  * and if found, use that instead of saying number. */
9166  if (ast_fileexists(prefile, NULL, NULL) > 0) {
9167  ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
9168  if (!callback)
9169  res = wait_file2(chan, vms, "vm-from");
9170  res = ast_stream_and_wait(chan, prefile, "");
9171  } else {
9172  ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
9173  /* Say "from extension" as one saying to sound smoother */
9174  if (!callback)
9175  res = wait_file2(chan, vms, "vm-from-extension");
9176  res = ast_say_digit_str(chan, callerid, "", ast_channel_language(chan));
9177  }
9178  }
9179  }
9180  } else if (!res) {
9181  ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
9182  /* If there is a recording for this numeric callerid then play that */
9183  if (!callback) {
9184  /* See if we can find a recorded name for this person instead of their extension number */
9185  snprintf(prefile, sizeof(prefile), "%s/recordings/callerids/%s", ast_config_AST_SPOOL_DIR, callerid);
9186  if (!saycidnumber && ast_fileexists(prefile, NULL, NULL) > 0) {
9187  ast_verb(3, "Playing recorded name for CID number '%s' - '%s'\n", callerid,prefile);
9188  wait_file2(chan, vms, "vm-from");
9189  res = ast_stream_and_wait(chan, prefile, "");
9190  ast_verb(3, "Played recorded name result '%d'\n", res);
9191  } else {
9192  /* Since this is all nicely figured out, why not say "from phone number" in this case" */
9193  wait_file2(chan, vms, "vm-from-phonenumber");
9194  res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
9195  }
9196  } else {
9197  res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
9198  }
9199  }
9200  } else {
9201  /* Number unknown */
9202  ast_debug(1, "VM-CID: From an unknown number\n");
9203  /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
9204  res = wait_file2(chan, vms, "vm-unknown-caller");
9205  }
9206  return res;
9207 }
9208 
9209 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
9210 {
9211  int res = 0;
9212  int durationm;
9213  int durations;
9214  /* Verify that we have a duration for the message */
9215  if (duration == NULL)
9216  return res;
9217 
9218  /* Convert from seconds to minutes */
9219  durations = atoi(duration);
9220  durationm = (durations / 60);
9221 
9222  ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
9223 
9224  if ((!res) && (durationm >= minduration)) {
9225  res = wait_file2(chan, vms, "vm-duration");
9226 
9227  /* POLISH syntax */
9228  if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9229  div_t num = div(durationm, 10);
9230 
9231  if (durationm == 1) {
9232  res = ast_play_and_wait(chan, "digits/1z");
9233  res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
9234  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
9235  if (num.rem == 2) {
9236  if (!num.quot) {
9237  res = ast_play_and_wait(chan, "digits/2-ie");
9238  } else {
9239  res = say_and_wait(chan, durationm - 2 , ast_channel_language(chan));
9240  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
9241  }
9242  } else {
9243  res = say_and_wait(chan, durationm, ast_channel_language(chan));
9244  }
9245  res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
9246  } else {
9247  res = say_and_wait(chan, durationm, ast_channel_language(chan));
9248  res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
9249  }
9250  /* DEFAULT syntax */
9251  } else {
9252  res = ast_say_number(chan, durationm, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
9253  res = wait_file2(chan, vms, "vm-minutes");
9254  }
9255  }
9256  return res;
9257 }
9258 
9259 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
9260 {
9261  int res = 0;
9262  char filename[PATH_MAX], *cid;
9263  const char *origtime, *context, *category, *duration, *flag;
9264  struct ast_config *msg_cfg;
9265  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
9266  SCOPE_ENTER(3, "%s: user: %s dir: %s msg: %d\n", ast_channel_name(chan),
9267  vms->username, vms->curdir, vms->curmsg);
9268 
9269  vms->starting = 0;
9270  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
9271  adsi_message(chan, vms);
9272  if (!vms->curmsg) {
9273  res = wait_file2(chan, vms, "vm-first"); /* "First" */
9274  } else if (vms->curmsg == vms->lastmsg) {
9275  res = wait_file2(chan, vms, "vm-last"); /* "last" */
9276  }
9277 
9278  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
9279  SCOPE_CALL(-1, RETRIEVE, vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
9280  msg_cfg = ast_config_load(filename, config_flags);
9281  if (!valid_config(msg_cfg)) {
9282  ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
9283  return 0;
9284  }
9285  flag = ast_variable_retrieve(msg_cfg, "message", "flag");
9286 
9287  /* Play the word urgent if we are listening to urgent messages */
9288  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
9289  res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
9290  }
9291 
9292  if (!res) {
9293  /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
9294  /* POLISH syntax */
9295  if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9296  if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9297  int ten, one;
9298  char nextmsg[256];
9299  ten = (vms->curmsg + 1) / 10;
9300  one = (vms->curmsg + 1) % 10;
9301 
9302  if (vms->curmsg < 20) {
9303  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
9304  res = wait_file2(chan, vms, nextmsg);
9305  } else {
9306  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
9307  res = wait_file2(chan, vms, nextmsg);
9308  if (one > 0) {
9309  if (!res) {
9310  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
9311  res = wait_file2(chan, vms, nextmsg);
9312  }
9313  }
9314  }
9315  }
9316  if (!res)
9317  res = wait_file2(chan, vms, "vm-message");
9318  /* HEBREW syntax */
9319  } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
9320  if (!vms->curmsg) {
9321  res = wait_file2(chan, vms, "vm-message");
9322  res = wait_file2(chan, vms, "vm-first");
9323  } else if (vms->curmsg == vms->lastmsg) {
9324  res = wait_file2(chan, vms, "vm-message");
9325  res = wait_file2(chan, vms, "vm-last");
9326  } else {
9327  res = wait_file2(chan, vms, "vm-message");
9328  res = wait_file2(chan, vms, "vm-number");
9329  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9330  }
9331  /* ICELANDIC syntax */
9332  } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) {
9333  res = wait_file2(chan, vms, "vm-message");
9334  if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9335  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "n");
9336  }
9337  /* VIETNAMESE syntax */
9338  } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
9339  if (!vms->curmsg) {
9340  res = wait_file2(chan, vms, "vm-message");
9341  res = wait_file2(chan, vms, "vm-first");
9342  } else if (vms->curmsg == vms->lastmsg) {
9343  res = wait_file2(chan, vms, "vm-message");
9344  res = wait_file2(chan, vms, "vm-last");
9345  } else {
9346  res = wait_file2(chan, vms, "vm-message");
9347  res = wait_file2(chan, vms, "vm-number");
9348  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9349  }
9350  } else {
9351  if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
9352  res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
9353  } else { /* DEFAULT syntax */
9354  res = wait_file2(chan, vms, "vm-message");
9355  }
9356  if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9357  if (!res) {
9358  ast_test_suite_event_notify("PLAYBACK", "Message: message number");
9359  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
9360  }
9361  }
9362  }
9363  }
9364 
9365  if (!valid_config(msg_cfg)) {
9366  SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
9367  }
9368 
9369  if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
9370  SCOPE_CALL(-1, DISPOSE, vms->curdir, vms->curmsg);
9371  ast_config_destroy(msg_cfg);
9372  SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "No origtime?!\n");
9373  }
9374 
9375  cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
9376  duration = ast_variable_retrieve(msg_cfg, "message", "duration");
9377  category = ast_variable_retrieve(msg_cfg, "message", "category");
9378 
9379  context = ast_variable_retrieve(msg_cfg, "message", "context");
9380  if (!res) {
9381  res = play_message_category(chan, category);
9382  }
9383  if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
9384  res = play_message_datetime(chan, vmu, origtime, filename);
9385  }
9386  if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
9387  res = play_message_callerid(chan, vms, cid, context, 0, 0);
9388  }
9389  if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
9390  res = play_message_duration(chan, vms, duration, vmu->saydurationm);
9391  }
9392  /* Allow pressing '1' to skip envelope / callerid */
9393  if (res == '1') {
9394  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
9395  res = 0;
9396  }
9397  ast_config_destroy(msg_cfg);
9398 
9399  if (!res) {
9400  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
9401 #ifdef IMAP_STORAGE
9402  ast_mutex_lock(&vms->lock);
9403 #endif
9404  vms->heard[vms->curmsg] = 1;
9405 #ifdef IMAP_STORAGE
9406  ast_mutex_unlock(&vms->lock);
9407  /*IMAP storage stores any prepended message from a forward
9408  * as a separate file from the rest of the message
9409  */
9410  if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
9411  wait_file(chan, vms, vms->introfn);
9412  }
9413 #endif
9414  if ((res = wait_file(chan, vms, vms->fn)) < 0) {
9415  ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
9416  res = 0;
9417  }
9418  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
9419  isprint(res) ? res : '?', isprint(res) ? res : '?');
9420  }
9421  SCOPE_CALL(-1, DISPOSE, vms->curdir, vms->curmsg);
9422  SCOPE_EXIT_RTN_VALUE(res, "Done: RC: %d\n", res);
9423 }
9424 
9425 #ifdef IMAP_STORAGE
9426 static int imap_remove_file(char *dir, int msgnum)
9427 {
9428  char fn[PATH_MAX];
9429  char full_fn[PATH_MAX];
9430  char intro[PATH_MAX] = {0,};
9431 
9432  if (msgnum > -1) {
9433  make_file(fn, sizeof(fn), dir, msgnum);
9434  snprintf(intro, sizeof(intro), "%sintro", fn);
9435  } else
9436  ast_copy_string(fn, dir, sizeof(fn));
9437 
9438  if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
9439  ast_filedelete(fn, NULL);
9440  if (!ast_strlen_zero(intro)) {
9441  ast_filedelete(intro, NULL);
9442  }
9443  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
9444  unlink(full_fn);
9445  }
9446  return 0;
9447 }
9448 
9449 
9450 
9451 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
9452 {
9453  char *file, *filename;
9454  char arg[11];
9455  int i;
9456  BODY* body;
9457  int curr_mbox;
9458 
9459  file = strrchr(ast_strdupa(dir), '/');
9460  if (file) {
9461  *file++ = '\0';
9462  } else {
9463  ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
9464  return -1;
9465  }
9466 
9467  ast_mutex_lock(&vms->lock);
9468 
9469  /* get the current mailbox so that we can point the mailstream back to it later */
9470  curr_mbox = get_folder_by_name(vms->curbox);
9471 
9472  if (init_mailstream(vms, GREETINGS_FOLDER) || !vms->mailstream) {
9473  ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
9474  ast_mutex_unlock(&vms->lock);
9475  return -1;
9476  }
9477 
9478  for (i = 0; i < vms->mailstream->nmsgs; i++) {
9479  mail_fetchstructure(vms->mailstream, i + 1, &body);
9480  /* We have the body, now we extract the file name of the first attachment. */
9481  if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
9482  char *attachment = body->nested.part->next->body.parameter->value;
9483  char copy[strlen(attachment) + 1];
9484 
9485  strcpy(copy, attachment); /* safe */
9486  attachment = copy;
9487 
9488  filename = strsep(&attachment, ".");
9489  if (!strcmp(filename, file)) {
9490  snprintf(arg, sizeof(arg), "%d", i + 1);
9491  mail_setflag(vms->mailstream, arg, "\\DELETED");
9492  }
9493  } else {
9494  ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
9495  ast_mutex_unlock(&vms->lock);
9496  return -1;
9497  }
9498  }
9499  mail_expunge(vms->mailstream);
9500 
9501  if (curr_mbox != -1) {
9502  /* restore previous mbox stream */
9503  if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
9504  ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
9505  }
9506  }
9507 
9508  ast_mutex_unlock(&vms->lock);
9509  return 0;
9510 }
9511 
9512 #elif !defined(IMAP_STORAGE)
9513 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
9514 {
9515  int count_msg, last_msg;
9516  SCOPE_ENTER(3, "user: %s dir: %s msg: %d box %d\n",
9517  vms->username, vms->curdir, vms->curmsg, box);
9518 
9519  ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
9520 
9521  /* Rename the member vmbox HERE so that we don't try to return before
9522  * we know what's going on.
9523  */
9524  snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
9525 
9526  /* Faster to make the directory than to check if it exists. */
9527  create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
9528 
9529  /* traverses directory using readdir (or select query for ODBC) */
9530  count_msg = COUNT(vmu, vms->curdir);
9531  if (count_msg < 0) {
9532  SCOPE_EXIT_RTN_VALUE(count_msg, "msgs: %d\n", count_msg);
9533  } else {
9534  vms->lastmsg = count_msg - 1;
9535  }
9536 
9537  if (vm_allocate_dh(vms, vmu, count_msg)) {
9538  SCOPE_EXIT_RTN_VALUE(-1, "failed to allocate dh\n");
9539  }
9540 
9541  /*
9542  The following test is needed in case sequencing gets messed up.
9543  There appears to be more than one way to mess up sequence, so
9544  we will not try to find all of the root causes--just fix it when
9545  detected.
9546  */
9547 
9548  if (vm_lock_path(vms->curdir)) {
9549  SCOPE_EXIT_LOG_RTN_VALUE(ERROR_LOCK_PATH, AST_LOG_ERROR, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
9550  }
9551 
9552  /* for local storage, checks directory for messages up to MAXMSGLIMIT */
9553  last_msg = LAST_MSG_INDEX(vms->curdir);
9554  ast_unlock_path(vms->curdir);
9555 
9556  if (last_msg < -1) {
9557  SCOPE_EXIT_RTN_VALUE(last_msg, "last msg: %d\n", last_msg);
9558  } else if (vms->lastmsg != last_msg) {
9559  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);
9560  resequence_mailbox(vmu, vms->curdir, count_msg);
9561  }
9562 
9563  SCOPE_EXIT_RTN_VALUE(0, "Done\n");
9564 }
9565 #endif
9566 
9567 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
9568 {
9569  int x = 0;
9570  int last_msg_idx = 0;
9571 
9572 #ifndef IMAP_STORAGE
9573  int res = 0, nummsg;
9574  char fn2[PATH_MAX];
9575 #endif
9576  SCOPE_ENTER(3, "user: %s dir: %s msg: %d\n",
9577  vms->username, vms->curdir, vms->curmsg);
9578 
9579  if (vms->lastmsg <= -1) {
9580  ast_trace(-1, "No messages in mailbox\n");
9581  goto done;
9582  }
9583 
9584  vms->curmsg = -1;
9585 #ifndef IMAP_STORAGE
9586  /* Get the deleted messages fixed */
9587  if (vm_lock_path(vms->curdir)) {
9588  SCOPE_EXIT_RTN_VALUE(ERROR_LOCK_PATH, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
9589  }
9590 
9591  /* update count as message may have arrived while we've got mailbox open */
9592  last_msg_idx = LAST_MSG_INDEX(vms->curdir);
9593  if (last_msg_idx != vms->lastmsg) {
9594  ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
9595  }
9596 
9597  /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
9598  for (x = 0; x < last_msg_idx + 1; x++) {
9599  if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
9600  /* Save this message. It's not in INBOX or hasn't been heard */
9601  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9602  if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
9603  break;
9604  }
9605  vms->curmsg++;
9606  make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
9607  if (strcmp(vms->fn, fn2)) {
9608  SCOPE_CALL(-1, RENAME, vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
9609  }
9610  } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
9611  /* Move to old folder before deleting */
9612  res = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, vms, x, 1, NULL, 0);
9613  if (res == ERROR_LOCK_PATH || res == ERROR_MAX_MSGS) {
9614  /* If save failed do not delete the message */
9615  ast_log(AST_LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
9616  vms->deleted[x] = 0;
9617  vms->heard[x] = 0;
9618  --x;
9619  }
9620  } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
9621  /* Move to deleted folder */
9622  res = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, vms, x, 10, NULL, 0);
9623  if (res == ERROR_LOCK_PATH) {
9624  ast_trace(-1, "Unable to lock path. Not moving message to deleted folder.\n");
9625  /* If save failed do not delete the message */
9626  vms->deleted[x] = 0;
9627  vms->heard[x] = 0;
9628  --x;
9629  }
9630  } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
9631  /* If realtime storage enabled - we should explicitly delete this message,
9632  cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
9633  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9634  res = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, vms->curdir, x, vms->fn, NULL);
9635  if (res) {
9636  SCOPE_CALL(-1, DELETE, vms->curdir, x, vms->fn, vmu);
9637  }
9638  }
9639  }
9640 
9641  /* Delete ALL remaining messages */
9642  nummsg = x - 1;
9643  for (x = vms->curmsg + 1; x <= nummsg; x++) {
9644  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9645  res = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, vms->curdir, x, vms->fn, NULL);
9646  if (res) {
9647  SCOPE_CALL(-1, DELETE, vms->curdir, x, vms->fn, vmu);
9648  }
9649  }
9650  ast_unlock_path(vms->curdir);
9651 #else /* defined(IMAP_STORAGE) */
9652  ast_mutex_lock(&vms->lock);
9653  if (vms->deleted) {
9654  /* Since we now expunge after each delete, deleting in reverse order
9655  * ensures that no reordering occurs between each step. */
9656  last_msg_idx = vms->dh_arraysize;
9657  for (x = last_msg_idx - 1; x >= 0; x--) {
9658  if (vms->deleted[x]) {
9659  ast_debug(3, "IMAP delete of %d\n", x);
9660  DELETE(vms->curdir, x, vms->fn, vmu);
9661  }
9662  }
9663  }
9664 #endif
9665 
9666 done:
9667  if (vms->deleted) {
9668  ast_free(vms->deleted);
9669  vms->deleted = NULL;
9670  }
9671  if (vms->heard) {
9672  ast_free(vms->heard);
9673  vms->heard = NULL;
9674  }
9675  vms->dh_arraysize = 0;
9676 #ifdef IMAP_STORAGE
9677  ast_mutex_unlock(&vms->lock);
9678 #endif
9679 
9680  SCOPE_EXIT_RTN_VALUE(0, "Done\n");
9681 }
9682 
9683 /* In Greek even though we CAN use a syntax like "friends messages"
9684  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
9685  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
9686  * syntax for the above three categories which is more elegant.
9687  */
9688 
9689 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
9690 {
9691  int cmd;
9692  char *buf;
9693 
9694  buf = ast_alloca(strlen(box) + 2);
9695  strcpy(buf, box);
9696  strcat(buf, "s");
9697 
9698  if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
9699  cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
9700  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
9701  } else {
9702  cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
9703  return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
9704  }
9705 }
9706 
9707 static int vm_play_folder_name_ja(struct ast_channel *chan, char *box)
9708 {
9709  int cmd;
9710 
9711  if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
9712  cmd = ast_play_and_wait(chan, box);
9713  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9714  } else {
9715  cmd = ast_play_and_wait(chan, box);
9716  return cmd;
9717  }
9718 }
9719 
9720 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
9721 {
9722  int cmd;
9723 
9724  if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
9725  if (!strcasecmp(box, "vm-INBOX"))
9726  cmd = ast_play_and_wait(chan, "vm-new-e");
9727  else
9728  cmd = ast_play_and_wait(chan, "vm-old-e");
9729  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9730  } else {
9731  cmd = ast_play_and_wait(chan, "vm-messages");
9732  return cmd ? cmd : ast_play_and_wait(chan, box);
9733  }
9734 }
9735 
9736 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
9737 {
9738  int cmd;
9739 
9740  if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
9741  cmd = ast_play_and_wait(chan, "vm-messages");
9742  return cmd ? cmd : ast_play_and_wait(chan, box);
9743  } else {
9744  cmd = ast_play_and_wait(chan, box);
9745  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9746  }
9747 }
9748 
9749 static int vm_play_folder_name(struct ast_channel *chan, char *box)
9750 {
9751  int cmd;
9752 
9753  if ( !strncasecmp(ast_channel_language(chan), "it", 2) ||
9754  !strncasecmp(ast_channel_language(chan), "es", 2) ||
9755  !strncasecmp(ast_channel_language(chan), "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
9756  cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
9757  return cmd ? cmd : ast_play_and_wait(chan, box);
9758  } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {
9759  return vm_play_folder_name_gr(chan, box);
9760  } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* Hebrew syntax */
9761  return ast_play_and_wait(chan, box);
9762  } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
9763  return vm_play_folder_name_ja(chan, box);
9764  } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9765  return vm_play_folder_name_pl(chan, box);
9766  } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian syntax */
9767  return vm_play_folder_name_ua(chan, box);
9768  } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
9769  return ast_play_and_wait(chan, box);
9770  } else { /* Default English */
9771  cmd = ast_play_and_wait(chan, box);
9772  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
9773  }
9774 }
9775 
9776 /* GREEK SYNTAX
9777  In greek the plural for old/new is
9778  different so we need the following files
9779  We also need vm-denExeteMynhmata because
9780  this syntax is different.
9781 
9782  -> vm-Olds.wav : "Palia"
9783  -> vm-INBOXs.wav : "Nea"
9784  -> vm-denExeteMynhmata : "den exete mynhmata"
9785 */
9786 
9787 
9788 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
9789 {
9790  int res = 0;
9791 
9792  if (vms->newmessages) {
9793  res = ast_play_and_wait(chan, "vm-youhave");
9794  if (!res)
9795  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
9796  if (!res) {
9797  if (vms->newmessages == 1) {
9798  res = ast_play_and_wait(chan, "vm-INBOX");
9799  if (!res)
9800  res = ast_play_and_wait(chan, "vm-message");
9801  } else {
9802  res = ast_play_and_wait(chan, "vm-INBOXs");
9803  if (!res)
9804  res = ast_play_and_wait(chan, "vm-messages");
9805  }
9806  }
9807  } else if (vms->oldmessages){
9808  res = ast_play_and_wait(chan, "vm-youhave");
9809  if (!res)
9810  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
9811  if (vms->oldmessages == 1){
9812  res = ast_play_and_wait(chan, "vm-Old");
9813  if (!res)
9814  res = ast_play_and_wait(chan, "vm-message");
9815  } else {
9816  res = ast_play_and_wait(chan, "vm-Olds");
9817  if (!res)
9818  res = ast_play_and_wait(chan, "vm-messages");
9819  }
9820  } else if (!vms->oldmessages && !vms->newmessages)
9821  res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
9822  return res;
9823 }
9824 
9825 /* Version of vm_intro() designed to work for many languages.
9826  *
9827  * It is hoped that this function can prevent the proliferation of
9828  * language-specific vm_intro() functions and in time replace the language-
9829  * specific functions which already exist. An examination of the language-
9830  * specific functions revealed that they all corrected the same deficiencies
9831  * in vm_intro_en() (which was the default function). Namely:
9832  *
9833  * 1) The vm-Old and vm-INBOX sound files were overloaded. The English
9834  * wording of the voicemail greeting hides this problem. For example,
9835  * vm-INBOX contains only the word "new". This means that both of these
9836  * sequences produce valid utterances:
9837  * * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
9838  * * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
9839  * However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
9840  * in many languages) the first utterance becomes "you have 1 the new message".
9841  * 2) The function contains hardcoded rules for pluralizing the word "message".
9842  * These rules are correct for English, but not for many other languages.
9843  * 3) No attempt is made to pluralize the adjectives ("old" and "new") as
9844  * required in many languages.
9845  * 4) The gender of the word for "message" is not specified. This is a problem
9846  * because in many languages the gender of the number in phrases such
9847  * as "you have one new message" must match the gender of the word
9848  * meaning "message".
9849  *
9850  * Fixing these problems for each new language has meant duplication of effort.
9851  * This new function solves the problems in the following general ways:
9852  * 1) Add new sound files vm-new and vm-old. These can be linked to vm-INBOX
9853  * and vm-Old respectively for those languages where it makes sense.
9854  * 2) Call ast_say_counted_noun() to put the proper gender and number prefix
9855  * on vm-message.
9856  * 3) Call ast_say_counted_adjective() to put the proper gender and number
9857  * prefix on vm-new and vm-old (none for English).
9858  * 4) Pass the gender of the language's word for "message" as an argument to
9859  * this function which is can in turn pass on to the functions which
9860  * say numbers and put endings on nouns and adjectives.
9861  *
9862  * All languages require these messages:
9863  * vm-youhave "You have..."
9864  * vm-and "and"
9865  * vm-no "no" (in the sense of "none", as in "you have no messages")
9866  *
9867  * To use it for English, you will need these additional sound files:
9868  * vm-new "new"
9869  * vm-message "message", singular
9870  * vm-messages "messages", plural
9871  *
9872  * If you use it for Russian and other slavic languages, you will need these additional sound files:
9873  *
9874  * vm-newn "novoye" (singular, neuter)
9875  * vm-newx "novikh" (counting plural form, genative plural)
9876  * vm-message "sobsheniye" (singular form)
9877  * vm-messagex1 "sobsheniya" (first counting plural form, genative singular)
9878  * vm-messagex2 "sobsheniy" (second counting plural form, genative plural)
9879  * digits/1n "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
9880  * digits/2n "dva" (neuter singular)
9881  */
9882 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
9883 {
9884  int res;
9885  int lastnum = 0;
9886 
9887  res = ast_play_and_wait(chan, "vm-youhave");
9888 
9889  if (!res && vms->newmessages) {
9890  lastnum = vms->newmessages;
9891 
9892  if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
9893  res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
9894  }
9895 
9896  if (!res && vms->oldmessages) {
9897  res = ast_play_and_wait(chan, "vm-and");
9898  }
9899  }
9900 
9901  if (!res && vms->oldmessages) {
9902  lastnum = vms->oldmessages;
9903 
9904  if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
9905  res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
9906  }
9907  }
9908 
9909  if (!res) {
9910  if (lastnum == 0) {
9911  res = ast_play_and_wait(chan, "vm-no");
9912  }
9913  if (!res) {
9914  res = ast_say_counted_noun(chan, lastnum, "vm-message");
9915  }
9916  }
9917 
9918  return res;
9919 }
9920 
9921 /* Default Hebrew syntax */
9922 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
9923 {
9924  int res = 0;
9925 
9926  /* Introduce messages they have */
9927  if (!res) {
9928  if ((vms->newmessages) || (vms->oldmessages)) {
9929  res = ast_play_and_wait(chan, "vm-youhave");
9930  }
9931  /*
9932  * The word "shtei" refers to the number 2 in hebrew when performing a count
9933  * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
9934  * an element, this is one of them.
9935  */
9936  if (vms->newmessages) {
9937  if (!res) {
9938  if (vms->newmessages == 1) {
9939  res = ast_play_and_wait(chan, "vm-INBOX1");
9940  } else {
9941  if (vms->newmessages == 2) {
9942  res = ast_play_and_wait(chan, "vm-shtei");
9943  } else {
9944  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9945  }
9946  res = ast_play_and_wait(chan, "vm-INBOX");
9947  }
9948  }
9949  if (vms->oldmessages && !res) {
9950  res = ast_play_and_wait(chan, "vm-and");
9951  if (vms->oldmessages == 1) {
9952  res = ast_play_and_wait(chan, "vm-Old1");
9953  } else {
9954  if (vms->oldmessages == 2) {
9955  res = ast_play_and_wait(chan, "vm-shtei");
9956  } else {
9957  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9958  }
9959  res = ast_play_and_wait(chan, "vm-Old");
9960  }
9961  }
9962  }
9963  if (!res && vms->oldmessages && !vms->newmessages) {
9964  if (!res) {
9965  if (vms->oldmessages == 1) {
9966  res = ast_play_and_wait(chan, "vm-Old1");
9967  } else {
9968  if (vms->oldmessages == 2) {
9969  res = ast_play_and_wait(chan, "vm-shtei");
9970  } else {
9971  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9972  }
9973  res = ast_play_and_wait(chan, "vm-Old");
9974  }
9975  }
9976  }
9977  if (!res) {
9978  if (!vms->oldmessages && !vms->newmessages) {
9979  if (!res) {
9980  res = ast_play_and_wait(chan, "vm-nomessages");
9981  }
9982  }
9983  }
9984  }
9985  return res;
9986 }
9987 
9988 /* Japanese syntax */
9989 static int vm_intro_ja(struct ast_channel *chan,struct vm_state *vms)
9990 {
9991  /* Introduce messages they have */
9992  int res;
9993  if (vms->newmessages) {
9994  res = ast_play_and_wait(chan, "vm-INBOX");
9995  if (!res)
9996  res = ast_play_and_wait(chan, "vm-message");
9997  if (!res)
9998  res = ast_play_and_wait(chan, "jp-ga");
9999  if (!res)
10000  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10001  if (vms->oldmessages && !res)
10002  res = ast_play_and_wait(chan, "silence/1");
10003 
10004  }
10005  if (vms->oldmessages) {
10006  res = ast_play_and_wait(chan, "vm-Old");
10007  if (!res)
10008  res = ast_play_and_wait(chan, "vm-message");
10009  if (!res)
10010  res = ast_play_and_wait(chan, "jp-ga");
10011  if (!res)
10012  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10013  }
10014  if (!vms->oldmessages && !vms->newmessages) {
10015  res = ast_play_and_wait(chan, "vm-messages");
10016  if (!res)
10017  res = ast_play_and_wait(chan, "jp-wa");
10018  if (!res)
10019  res = ast_play_and_wait(chan, "jp-arimasen");
10020  }
10021  else {
10022  res = ast_play_and_wait(chan, "jp-arimasu");
10023  }
10024  return res;
10025 } /* Japanese */
10026 
10027 /* Default English syntax */
10028 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
10029 {
10030  int res;
10031 
10032  /* Introduce messages they have */
10033  res = ast_play_and_wait(chan, "vm-youhave");
10034  if (!res) {
10035  if (vms->urgentmessages) {
10036  res = say_and_wait(chan, vms->urgentmessages, ast_channel_language(chan));
10037  if (!res)
10038  res = ast_play_and_wait(chan, "vm-Urgent");
10039  if ((vms->oldmessages || vms->newmessages) && !res) {
10040  res = ast_play_and_wait(chan, "vm-and");
10041  } else if (!res) {
10042  if (vms->urgentmessages == 1)
10043  res = ast_play_and_wait(chan, "vm-message");
10044  else
10045  res = ast_play_and_wait(chan, "vm-messages");
10046  }
10047  }
10048  if (vms->newmessages) {
10049  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10050  if (!res)
10051  res = ast_play_and_wait(chan, "vm-INBOX");
10052  if (vms->oldmessages && !res)
10053  res = ast_play_and_wait(chan, "vm-and");
10054  else if (!res) {
10055  if (vms->newmessages == 1)
10056  res = ast_play_and_wait(chan, "vm-message");
10057  else
10058  res = ast_play_and_wait(chan, "vm-messages");
10059  }
10060 
10061  }
10062  if (!res && vms->oldmessages) {
10063  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10064  if (!res)
10065  res = ast_play_and_wait(chan, "vm-Old");
10066  if (!res) {
10067  if (vms->oldmessages == 1)
10068  res = ast_play_and_wait(chan, "vm-message");
10069  else
10070  res = ast_play_and_wait(chan, "vm-messages");
10071  }
10072  }
10073  if (!res) {
10074  if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
10075  res = ast_play_and_wait(chan, "vm-no");
10076  if (!res)
10077  res = ast_play_and_wait(chan, "vm-messages");
10078  }
10079  }
10080  }
10081  return res;
10082 }
10083 
10084 /* ICELANDIC syntax */
10085 static int vm_intro_is(struct ast_channel *chan, struct vm_state *vms)
10086 {
10087  int res;
10088 
10089  /* Introduce messages they have */
10090  res = ast_play_and_wait(chan, "vm-youhave");
10091  if (!res) {
10092  if (vms->urgentmessages) {
10093  /* Digits 1-4 are spoken in neutral and plural when talking about messages,
10094  however, feminine is used for 1 as it is the same as the neutral for plural,
10095  and singular neutral is the same after 1. */
10096  if (vms->urgentmessages < 5) {
10097  char recname[16];
10098  if (vms->urgentmessages == 1)
10099  snprintf(recname, sizeof(recname), "digits/1kvk");
10100  else
10101  snprintf(recname, sizeof(recname), "digits/%dhk", vms->urgentmessages);
10102  res = ast_play_and_wait(chan, recname);
10103  } else if (!res)
10104  res = ast_play_and_wait(chan, "vm-Urgent");
10105  if ((vms->oldmessages || vms->newmessages) && !res) {
10106  res = ast_play_and_wait(chan, "vm-and");
10107  } else if (!res)
10108  res = ast_play_and_wait(chan, "vm-messages");
10109  }
10110  if (vms->newmessages) {
10111  if (vms->newmessages < 5) {
10112  char recname[16];
10113  if (vms->newmessages == 1)
10114  snprintf(recname, sizeof(recname), "digits/1kvk");
10115  else
10116  snprintf(recname, sizeof(recname), "digits/%dhk", vms->newmessages);
10117  res = ast_play_and_wait(chan, recname);
10118  } else
10119  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10120  if (!res)
10121  res = ast_play_and_wait(chan, "vm-INBOX");
10122  if (vms->oldmessages && !res)
10123  res = ast_play_and_wait(chan, "vm-and");
10124  else if (!res)
10125  res = ast_play_and_wait(chan, "vm-messages");
10126  }
10127  if (!res && vms->oldmessages) {
10128  if (vms->oldmessages < 5) {
10129  char recname[16];
10130  if (vms->oldmessages == 1)
10131  snprintf(recname, sizeof(recname), "digits/1kvk");
10132  else
10133  snprintf(recname, sizeof(recname), "digits/%dhk", vms->oldmessages);
10134  res = ast_play_and_wait(chan, recname);
10135  } else
10136  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10137  if (!res)
10138  res = ast_play_and_wait(chan, "vm-Old");
10139  if (!res)
10140  res = ast_play_and_wait(chan, "vm-messages");
10141  }
10142  if (!res) {
10143  if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
10144  res = ast_play_and_wait(chan, "vm-no");
10145  if (!res)
10146  res = ast_play_and_wait(chan, "vm-messages");
10147  }
10148  }
10149  }
10150  return res;
10151 }
10152 
10153 /* ITALIAN syntax */
10154 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
10155 {
10156  /* Introduce messages they have */
10157  int res;
10158  if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
10159  res = ast_play_and_wait(chan, "vm-no") ||
10160  ast_play_and_wait(chan, "vm-message");
10161  else
10162  res = ast_play_and_wait(chan, "vm-youhave");
10163  if (!res && vms->newmessages) {
10164  res = (vms->newmessages == 1) ?
10165  ast_play_and_wait(chan, "digits/un") ||
10166  ast_play_and_wait(chan, "vm-nuovo") ||
10167  ast_play_and_wait(chan, "vm-message") :
10168  /* 2 or more new messages */
10169  say_and_wait(chan, vms->newmessages, ast_channel_language(chan)) ||
10170  ast_play_and_wait(chan, "vm-nuovi") ||
10171  ast_play_and_wait(chan, "vm-messages");
10172  if (!res && vms->oldmessages)
10173  res = ast_play_and_wait(chan, "vm-and");
10174  }
10175  if (!res && vms->oldmessages) {
10176  res = (vms->oldmessages == 1) ?
10177  ast_play_and_wait(chan, "digits/un") ||
10178  ast_play_and_wait(chan, "vm-vecchio") ||
10179  ast_play_and_wait(chan, "vm-message") :
10180  /* 2 or more old messages */
10181  say_and_wait(chan, vms->oldmessages, ast_channel_language(chan)) ||
10182  ast_play_and_wait(chan, "vm-vecchi") ||
10183  ast_play_and_wait(chan, "vm-messages");
10184  }
10185  return res;
10186 }
10187 
10188 /* POLISH syntax */
10189 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
10190 {
10191  /* Introduce messages they have */
10192  int res;
10193  div_t num;
10194 
10195  if (!vms->oldmessages && !vms->newmessages) {
10196  res = ast_play_and_wait(chan, "vm-no");
10197  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10198  return res;
10199  } else {
10200  res = ast_play_and_wait(chan, "vm-youhave");
10201  }
10202 
10203  if (vms->newmessages) {
10204  num = div(vms->newmessages, 10);
10205  if (vms->newmessages == 1) {
10206  res = ast_play_and_wait(chan, "digits/1-a");
10207  res = res ? res : ast_play_and_wait(chan, "vm-new-a");
10208  res = res ? res : ast_play_and_wait(chan, "vm-message");
10209  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
10210  if (num.rem == 2) {
10211  if (!num.quot) {
10212  res = ast_play_and_wait(chan, "digits/2-ie");
10213  } else {
10214  res = say_and_wait(chan, vms->newmessages - 2 , ast_channel_language(chan));
10215  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
10216  }
10217  } else {
10218  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10219  }
10220  res = res ? res : ast_play_and_wait(chan, "vm-new-e");
10221  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10222  } else {
10223  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10224  res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
10225  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10226  }
10227  if (!res && vms->oldmessages)
10228  res = ast_play_and_wait(chan, "vm-and");
10229  }
10230  if (!res && vms->oldmessages) {
10231  num = div(vms->oldmessages, 10);
10232  if (vms->oldmessages == 1) {
10233  res = ast_play_and_wait(chan, "digits/1-a");
10234  res = res ? res : ast_play_and_wait(chan, "vm-old-a");
10235  res = res ? res : ast_play_and_wait(chan, "vm-message");
10236  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
10237  if (num.rem == 2) {
10238  if (!num.quot) {
10239  res = ast_play_and_wait(chan, "digits/2-ie");
10240  } else {
10241  res = say_and_wait(chan, vms->oldmessages - 2 , ast_channel_language(chan));
10242  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
10243  }
10244  } else {
10245  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10246  }
10247  res = res ? res : ast_play_and_wait(chan, "vm-old-e");
10248  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10249  } else {
10250  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10251  res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
10252  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10253  }
10254  }
10255 
10256  return res;
10257 }
10258 
10259 /* SWEDISH syntax */
10260 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
10261 {
10262  /* Introduce messages they have */
10263  int res;
10264 
10265  res = ast_play_and_wait(chan, "vm-youhave");
10266  if (res)
10267  return res;
10268 
10269  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10270  res = ast_play_and_wait(chan, "vm-no");
10271  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10272  return res;
10273  }
10274 
10275  if (vms->newmessages) {
10276  if (vms->newmessages == 1) {
10277  res = ast_play_and_wait(chan, "digits/ett");
10278  res = res ? res : ast_play_and_wait(chan, "vm-nytt");
10279  res = res ? res : ast_play_and_wait(chan, "vm-message");
10280  } else {
10281  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10282  res = res ? res : ast_play_and_wait(chan, "vm-nya");
10283  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10284  }
10285  if (!res && vms->oldmessages)
10286  res = ast_play_and_wait(chan, "vm-and");
10287  }
10288  if (!res && vms->oldmessages) {
10289  if (vms->oldmessages == 1) {
10290  res = ast_play_and_wait(chan, "digits/ett");
10291  res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
10292  res = res ? res : ast_play_and_wait(chan, "vm-message");
10293  } else {
10294  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10295  res = res ? res : ast_play_and_wait(chan, "vm-gamla");
10296  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10297  }
10298  }
10299 
10300  return res;
10301 }
10302 
10303 /* NORWEGIAN syntax */
10304 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
10305 {
10306  /* Introduce messages they have */
10307  int res;
10308 
10309  res = ast_play_and_wait(chan, "vm-youhave");
10310  if (res)
10311  return res;
10312 
10313  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10314  res = ast_play_and_wait(chan, "vm-no");
10315  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10316  return res;
10317  }
10318 
10319  if (vms->newmessages) {
10320  if (vms->newmessages == 1) {
10321  res = ast_play_and_wait(chan, "digits/1");
10322  res = res ? res : ast_play_and_wait(chan, "vm-ny");
10323  res = res ? res : ast_play_and_wait(chan, "vm-message");
10324  } else {
10325  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10326  res = res ? res : ast_play_and_wait(chan, "vm-nye");
10327  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10328  }
10329  if (!res && vms->oldmessages)
10330  res = ast_play_and_wait(chan, "vm-and");
10331  }
10332  if (!res && vms->oldmessages) {
10333  if (vms->oldmessages == 1) {
10334  res = ast_play_and_wait(chan, "digits/1");
10335  res = res ? res : ast_play_and_wait(chan, "vm-gamel");
10336  res = res ? res : ast_play_and_wait(chan, "vm-message");
10337  } else {
10338  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10339  res = res ? res : ast_play_and_wait(chan, "vm-gamle");
10340  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10341  }
10342  }
10343 
10344  return res;
10345 }
10346 
10347 /* Danish syntax */
10348 static int vm_intro_da(struct ast_channel *chan, struct vm_state *vms)
10349 {
10350  /* Introduce messages they have */
10351  int res;
10352 
10353  res = ast_play_and_wait(chan, "vm-youhave");
10354  if (res)
10355  return res;
10356 
10357  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10358  res = ast_play_and_wait(chan, "vm-no");
10359  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10360  return res;
10361  }
10362 
10363  if (vms->newmessages) {
10364  if ((vms->newmessages == 1)) {
10365  res = ast_play_and_wait(chan, "digits/1");
10366  res = res ? res : ast_play_and_wait(chan, "vm-INBOX");
10367  res = res ? res : ast_play_and_wait(chan, "vm-message");
10368  } else {
10369  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10370  res = res ? res : ast_play_and_wait(chan, "vm-INBOXs");
10371  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10372  }
10373  if (!res && vms->oldmessages)
10374  res = ast_play_and_wait(chan, "vm-and");
10375  }
10376  if (!res && vms->oldmessages) {
10377  if (vms->oldmessages == 1) {
10378  res = ast_play_and_wait(chan, "digits/1");
10379  res = res ? res : ast_play_and_wait(chan, "vm-Old");
10380  res = res ? res : ast_play_and_wait(chan, "vm-message");
10381  } else {
10382  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10383  res = res ? res : ast_play_and_wait(chan, "vm-Olds");
10384  res = res ? res : ast_play_and_wait(chan, "vm-messages");
10385  }
10386  }
10387 
10388  return res;
10389 }
10390 
10391 
10392 /* GERMAN syntax */
10393 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
10394 {
10395  /* Introduce messages they have */
10396  int res;
10397  res = ast_play_and_wait(chan, "vm-youhave");
10398  if (!res) {
10399  if (vms->newmessages) {
10400  if (vms->newmessages == 1)
10401  res = ast_play_and_wait(chan, "digits/1F");
10402  else
10403  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10404  if (!res)
10405  res = ast_play_and_wait(chan, "vm-INBOX");
10406  if (vms->oldmessages && !res)
10407  res = ast_play_and_wait(chan, "vm-and");
10408  else if (!res) {
10409  if (vms->newmessages == 1)
10410  res = ast_play_and_wait(chan, "vm-message");
10411  else
10412  res = ast_play_and_wait(chan, "vm-messages");
10413  }
10414 
10415  }
10416  if (!res && vms->oldmessages) {
10417  if (vms->oldmessages == 1)
10418  res = ast_play_and_wait(chan, "digits/1F");
10419  else
10420  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10421  if (!res)
10422  res = ast_play_and_wait(chan, "vm-Old");
10423  if (!res) {
10424  if (vms->oldmessages == 1)
10425  res = ast_play_and_wait(chan, "vm-message");
10426  else
10427  res = ast_play_and_wait(chan, "vm-messages");
10428  }
10429  }
10430  if (!res) {
10431  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10432  res = ast_play_and_wait(chan, "vm-no");
10433  if (!res)
10434  res = ast_play_and_wait(chan, "vm-messages");
10435  }
10436  }
10437  }
10438  return res;
10439 }
10440 
10441 /* SPANISH syntax */
10442 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
10443 {
10444  /* Introduce messages they have */
10445  int res;
10446  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10447  res = ast_play_and_wait(chan, "vm-youhaveno");
10448  if (!res)
10449  res = ast_play_and_wait(chan, "vm-messages");
10450  } else {
10451  res = ast_play_and_wait(chan, "vm-youhave");
10452  }
10453  if (!res) {
10454  if (vms->newmessages) {
10455  if (!res) {
10456  if (vms->newmessages == 1) {
10457  res = ast_play_and_wait(chan, "digits/1M");
10458  if (!res)
10459  res = ast_play_and_wait(chan, "vm-message");
10460  if (!res)
10461  res = ast_play_and_wait(chan, "vm-INBOXs");
10462  } else {
10463  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10464  if (!res)
10465  res = ast_play_and_wait(chan, "vm-messages");
10466  if (!res)
10467  res = ast_play_and_wait(chan, "vm-INBOX");
10468  }
10469  }
10470  if (vms->oldmessages && !res)
10471  res = ast_play_and_wait(chan, "vm-and");
10472  }
10473  if (vms->oldmessages) {
10474  if (!res) {
10475  if (vms->oldmessages == 1) {
10476  res = ast_play_and_wait(chan, "digits/1M");
10477  if (!res)
10478  res = ast_play_and_wait(chan, "vm-message");
10479  if (!res)
10480  res = ast_play_and_wait(chan, "vm-Olds");
10481  } else {
10482  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10483  if (!res)
10484  res = ast_play_and_wait(chan, "vm-messages");
10485  if (!res)
10486  res = ast_play_and_wait(chan, "vm-Old");
10487  }
10488  }
10489  }
10490  }
10491 return res;
10492 }
10493 
10494 /* BRAZILIAN PORTUGUESE syntax */
10495 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
10496  /* Introduce messages they have */
10497  int res;
10498  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10499  res = ast_play_and_wait(chan, "vm-nomessages");
10500  return res;
10501  } else {
10502  res = ast_play_and_wait(chan, "vm-youhave");
10503  }
10504  if (vms->newmessages) {
10505  if (!res)
10506  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10507  if (vms->newmessages == 1) {
10508  if (!res)
10509  res = ast_play_and_wait(chan, "vm-message");
10510  if (!res)
10511  res = ast_play_and_wait(chan, "vm-INBOXs");
10512  } else {
10513  if (!res)
10514  res = ast_play_and_wait(chan, "vm-messages");
10515  if (!res)
10516  res = ast_play_and_wait(chan, "vm-INBOX");
10517  }
10518  if (vms->oldmessages && !res)
10519  res = ast_play_and_wait(chan, "vm-and");
10520  }
10521  if (vms->oldmessages) {
10522  if (!res)
10523  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10524  if (vms->oldmessages == 1) {
10525  if (!res)
10526  res = ast_play_and_wait(chan, "vm-message");
10527  if (!res)
10528  res = ast_play_and_wait(chan, "vm-Olds");
10529  } else {
10530  if (!res)
10531  res = ast_play_and_wait(chan, "vm-messages");
10532  if (!res)
10533  res = ast_play_and_wait(chan, "vm-Old");
10534  }
10535  }
10536  return res;
10537 }
10538 
10539 /* FRENCH syntax */
10540 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
10541 {
10542  /* Introduce messages they have */
10543  int res;
10544  res = ast_play_and_wait(chan, "vm-youhave");
10545  if (!res) {
10546  if (vms->newmessages) {
10547  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10548  if (!res)
10549  res = ast_play_and_wait(chan, "vm-INBOX");
10550  if (vms->oldmessages && !res)
10551  res = ast_play_and_wait(chan, "vm-and");
10552  else if (!res) {
10553  if (vms->newmessages == 1)
10554  res = ast_play_and_wait(chan, "vm-message");
10555  else
10556  res = ast_play_and_wait(chan, "vm-messages");
10557  }
10558 
10559  }
10560  if (!res && vms->oldmessages) {
10561  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10562  if (!res)
10563  res = ast_play_and_wait(chan, "vm-Old");
10564  if (!res) {
10565  if (vms->oldmessages == 1)
10566  res = ast_play_and_wait(chan, "vm-message");
10567  else
10568  res = ast_play_and_wait(chan, "vm-messages");
10569  }
10570  }
10571  if (!res) {
10572  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10573  res = ast_play_and_wait(chan, "vm-no");
10574  if (!res)
10575  res = ast_play_and_wait(chan, "vm-messages");
10576  }
10577  }
10578  }
10579  return res;
10580 }
10581 
10582 /* DUTCH syntax */
10583 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
10584 {
10585  /* Introduce messages they have */
10586  int res;
10587  res = ast_play_and_wait(chan, "vm-youhave");
10588  if (!res) {
10589  if (vms->newmessages) {
10590  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10591  if (!res) {
10592  if (vms->newmessages == 1)
10593  res = ast_play_and_wait(chan, "vm-INBOXs");
10594  else
10595  res = ast_play_and_wait(chan, "vm-INBOX");
10596  }
10597  if (vms->oldmessages && !res)
10598  res = ast_play_and_wait(chan, "vm-and");
10599  else if (!res) {
10600  if (vms->newmessages == 1)
10601  res = ast_play_and_wait(chan, "vm-message");
10602  else
10603  res = ast_play_and_wait(chan, "vm-messages");
10604  }
10605 
10606  }
10607  if (!res && vms->oldmessages) {
10608  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10609  if (!res) {
10610  if (vms->oldmessages == 1)
10611  res = ast_play_and_wait(chan, "vm-Olds");
10612  else
10613  res = ast_play_and_wait(chan, "vm-Old");
10614  }
10615  if (!res) {
10616  if (vms->oldmessages == 1)
10617  res = ast_play_and_wait(chan, "vm-message");
10618  else
10619  res = ast_play_and_wait(chan, "vm-messages");
10620  }
10621  }
10622  if (!res) {
10623  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10624  res = ast_play_and_wait(chan, "vm-no");
10625  if (!res)
10626  res = ast_play_and_wait(chan, "vm-messages");
10627  }
10628  }
10629  }
10630  return res;
10631 }
10632 
10633 /* PORTUGUESE syntax */
10634 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
10635 {
10636  /* Introduce messages they have */
10637  int res;
10638  res = ast_play_and_wait(chan, "vm-youhave");
10639  if (!res) {
10640  if (vms->newmessages) {
10641  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10642  if (!res) {
10643  if (vms->newmessages == 1) {
10644  res = ast_play_and_wait(chan, "vm-message");
10645  if (!res)
10646  res = ast_play_and_wait(chan, "vm-INBOXs");
10647  } else {
10648  res = ast_play_and_wait(chan, "vm-messages");
10649  if (!res)
10650  res = ast_play_and_wait(chan, "vm-INBOX");
10651  }
10652  }
10653  if (vms->oldmessages && !res)
10654  res = ast_play_and_wait(chan, "vm-and");
10655  }
10656  if (!res && vms->oldmessages) {
10657  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10658  if (!res) {
10659  if (vms->oldmessages == 1) {
10660  res = ast_play_and_wait(chan, "vm-message");
10661  if (!res)
10662  res = ast_play_and_wait(chan, "vm-Olds");
10663  } else {
10664  res = ast_play_and_wait(chan, "vm-messages");
10665  if (!res)
10666  res = ast_play_and_wait(chan, "vm-Old");
10667  }
10668  }
10669  }
10670  if (!res) {
10671  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10672  res = ast_play_and_wait(chan, "vm-no");
10673  if (!res)
10674  res = ast_play_and_wait(chan, "vm-messages");
10675  }
10676  }
10677  }
10678  return res;
10679 }
10680 
10681 
10682 /* CZECH syntax */
10683 /* in czech there must be declension of word new and message
10684  * czech : english : czech : english
10685  * --------------------------------------------------------
10686  * vm-youhave : you have
10687  * vm-novou : one new : vm-zpravu : message
10688  * vm-nove : 2-4 new : vm-zpravy : messages
10689  * vm-novych : 5-infinite new : vm-zprav : messages
10690  * vm-starou : one old
10691  * vm-stare : 2-4 old
10692  * vm-starych : 5-infinite old
10693  * jednu : one - falling 4.
10694  * vm-no : no ( no messages )
10695  */
10696 
10697 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
10698 {
10699  int res;
10700  res = ast_play_and_wait(chan, "vm-youhave");
10701  if (!res) {
10702  if (vms->newmessages) {
10703  if (vms->newmessages == 1) {
10704  res = ast_play_and_wait(chan, "digits/jednu");
10705  } else {
10706  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10707  }
10708  if (!res) {
10709  if (vms->newmessages == 1)
10710  res = ast_play_and_wait(chan, "vm-novou");
10711  if ((vms->newmessages) > 1 && (vms->newmessages < 5))
10712  res = ast_play_and_wait(chan, "vm-nove");
10713  if (vms->newmessages > 4)
10714  res = ast_play_and_wait(chan, "vm-novych");
10715  }
10716  if (vms->oldmessages && !res)
10717  res = ast_play_and_wait(chan, "vm-and");
10718  else if (!res) {
10719  if (vms->newmessages == 1)
10720  res = ast_play_and_wait(chan, "vm-zpravu");
10721  if ((vms->newmessages) > 1 && (vms->newmessages < 5))
10722  res = ast_play_and_wait(chan, "vm-zpravy");
10723  if (vms->newmessages > 4)
10724  res = ast_play_and_wait(chan, "vm-zprav");
10725  }
10726  }
10727  if (!res && vms->oldmessages) {
10728  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10729  if (!res) {
10730  if (vms->oldmessages == 1)
10731  res = ast_play_and_wait(chan, "vm-starou");
10732  if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
10733  res = ast_play_and_wait(chan, "vm-stare");
10734  if (vms->oldmessages > 4)
10735  res = ast_play_and_wait(chan, "vm-starych");
10736  }
10737  if (!res) {
10738  if (vms->oldmessages == 1)
10739  res = ast_play_and_wait(chan, "vm-zpravu");
10740  if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
10741  res = ast_play_and_wait(chan, "vm-zpravy");
10742  if (vms->oldmessages > 4)
10743  res = ast_play_and_wait(chan, "vm-zprav");
10744  }
10745  }
10746  if (!res) {
10747  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10748  res = ast_play_and_wait(chan, "vm-no");
10749  if (!res)
10750  res = ast_play_and_wait(chan, "vm-zpravy");
10751  }
10752  }
10753  }
10754  return res;
10755 }
10756 
10757 /* CHINESE (Taiwan) syntax */
10758 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
10759 {
10760  int res;
10761  /* Introduce messages they have */
10762  res = ast_play_and_wait(chan, "vm-you");
10763 
10764  if (!res && vms->newmessages) {
10765  res = ast_play_and_wait(chan, "vm-have");
10766  if (!res)
10767  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10768  if (!res)
10769  res = ast_play_and_wait(chan, "vm-tong");
10770  if (!res)
10771  res = ast_play_and_wait(chan, "vm-INBOX");
10772  if (vms->oldmessages && !res)
10773  res = ast_play_and_wait(chan, "vm-and");
10774  else if (!res)
10775  res = ast_play_and_wait(chan, "vm-messages");
10776  }
10777  if (!res && vms->oldmessages) {
10778  res = ast_play_and_wait(chan, "vm-have");
10779  if (!res)
10780  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10781  if (!res)
10782  res = ast_play_and_wait(chan, "vm-tong");
10783  if (!res)
10784  res = ast_play_and_wait(chan, "vm-Old");
10785  if (!res)
10786  res = ast_play_and_wait(chan, "vm-messages");
10787  }
10788  if (!res && !vms->oldmessages && !vms->newmessages) {
10789  res = ast_play_and_wait(chan, "vm-haveno");
10790  if (!res)
10791  res = ast_play_and_wait(chan, "vm-messages");
10792  }
10793  return res;
10794 }
10795 
10796 /* Vietnamese syntax */
10797 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
10798 {
10799  int res;
10800 
10801  /* Introduce messages they have */
10802  res = ast_play_and_wait(chan, "vm-youhave");
10803  if (!res) {
10804  if (vms->newmessages) {
10805  res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10806  if (!res)
10807  res = ast_play_and_wait(chan, "vm-INBOX");
10808  if (vms->oldmessages && !res)
10809  res = ast_play_and_wait(chan, "vm-and");
10810  }
10811  if (!res && vms->oldmessages) {
10812  res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10813  if (!res)
10814  res = ast_play_and_wait(chan, "vm-Old");
10815  }
10816  if (!res) {
10817  if (!vms->oldmessages && !vms->newmessages) {
10818  res = ast_play_and_wait(chan, "vm-no");
10819  if (!res)
10820  res = ast_play_and_wait(chan, "vm-message");
10821  }
10822  }
10823  }
10824  return res;
10825 }
10826 
10827 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
10828 {
10829  char prefile[256];
10830 
10831  /* Notify the user that the temp greeting is set and give them the option to remove it */
10832  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
10833  if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10834  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
10835  if (ast_fileexists(prefile, NULL, NULL) > 0) {
10836  ast_play_and_wait(chan, "vm-tempgreetactive");
10837  }
10838  DISPOSE(prefile, -1);
10839  }
10840 
10841  /* Play voicemail intro - syntax is different for different languages */
10842  if (0) {
10843  return 0;
10844  } else if (!strncasecmp(ast_channel_language(chan), "cs", 2)) { /* CZECH syntax */
10845  return vm_intro_cs(chan, vms);
10846  } else if (!strncasecmp(ast_channel_language(chan), "cz", 2)) { /* deprecated CZECH syntax */
10847  static int deprecation_warning = 0;
10848  if (deprecation_warning++ % 10 == 0) {
10849  ast_log(LOG_WARNING, "cz is not a standard language code. Please switch to using cs instead.\n");
10850  }
10851  return vm_intro_cs(chan, vms);
10852  } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
10853  return vm_intro_de(chan, vms);
10854  } else if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH syntax */
10855  return vm_intro_es(chan, vms);
10856  } else if (!strncasecmp(ast_channel_language(chan), "fr", 2)) { /* FRENCH syntax */
10857  return vm_intro_fr(chan, vms);
10858  } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
10859  return vm_intro_gr(chan, vms);
10860  } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW syntax */
10861  return vm_intro_he(chan, vms);
10862  } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */
10863  return vm_intro_is(chan, vms);
10864  } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
10865  return vm_intro_it(chan, vms);
10866  } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE syntax */
10867  return vm_intro_ja(chan, vms);
10868  } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
10869  return vm_intro_nl(chan, vms);
10870  } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
10871  return vm_intro_no(chan, vms);
10872  } else if (!strncasecmp(ast_channel_language(chan), "da", 2)) { /* DANISH syntax */
10873  return vm_intro_da(chan, vms);
10874  } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
10875  return vm_intro_pl(chan, vms);
10876  } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* BRAZILIAN PORTUGUESE syntax */
10877  return vm_intro_pt_BR(chan, vms);
10878  } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE syntax */
10879  return vm_intro_pt(chan, vms);
10880  } else if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* RUSSIAN syntax */
10881  return vm_intro_multilang(chan, vms, "n");
10882  } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
10883  return vm_intro_se(chan, vms);
10884  } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* UKRAINIAN syntax */
10885  return vm_intro_multilang(chan, vms, "n");
10886  } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
10887  return vm_intro_vi(chan, vms);
10888  } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
10889  return vm_intro_zh(chan, vms);
10890  } else { /* Default to ENGLISH */
10891  return vm_intro_en(chan, vms);
10892  }
10893 }
10894 
10895 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
10896 {
10897  int res = 0;
10898  /* Play instructions and wait for new command */
10899  while (!res) {
10900  if (vms->starting) {
10901  if (vms->lastmsg > -1) {
10902  if (skipadvanced)
10903  res = ast_play_and_wait(chan, "vm-onefor-full");
10904  else
10905  res = ast_play_and_wait(chan, "vm-onefor");
10906  if (!res)
10907  res = vm_play_folder_name(chan, vms->vmbox);
10908  }
10909  if (!res) {
10910  if (skipadvanced)
10911  res = ast_play_and_wait(chan, "vm-opts-full");
10912  else
10913  res = ast_play_and_wait(chan, "vm-opts");
10914  }
10915  } else {
10916  /* Added for additional help */
10917  if (skipadvanced) {
10918  res = ast_play_and_wait(chan, "vm-onefor-full");
10919  if (!res)
10920  res = vm_play_folder_name(chan, vms->vmbox);
10921  res = ast_play_and_wait(chan, "vm-opts-full");
10922  }
10923  /* Logic:
10924  * If the current message is not the first OR
10925  * if we're listening to the first new message and there are
10926  * also urgent messages, then prompt for navigation to the
10927  * previous message
10928  */
10929  if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
10930  res = ast_play_and_wait(chan, "vm-prev");
10931  }
10932  if (!res && !skipadvanced)
10933  res = ast_play_and_wait(chan, "vm-advopts");
10934  if (!res)
10935  res = ast_play_and_wait(chan, "vm-repeat");
10936  /* Logic:
10937  * If we're not listening to the last message OR
10938  * we're listening to the last urgent message and there are
10939  * also new non-urgent messages, then prompt for navigation
10940  * to the next message
10941  */
10942  if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
10943  (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
10944  res = ast_play_and_wait(chan, "vm-next");
10945  }
10946  if (!res) {
10947  int curmsg_deleted;
10948 #ifdef IMAP_STORAGE
10949  ast_mutex_lock(&vms->lock);
10950 #endif
10951  curmsg_deleted = vms->deleted[vms->curmsg];
10952 #ifdef IMAP_STORAGE
10953  ast_mutex_unlock(&vms->lock);
10954 #endif
10955  if (!nodelete) {
10956  if (!curmsg_deleted) {
10957  res = ast_play_and_wait(chan, "vm-delete");
10958  } else {
10959  res = ast_play_and_wait(chan, "vm-undelete");
10960  }
10961  }
10962  if (!res) {
10963  res = ast_play_and_wait(chan, "vm-toforward");
10964  }
10965  if (!res) {
10966  res = ast_play_and_wait(chan, "vm-savemessage");
10967  }
10968  }
10969  }
10970  if (!res) {
10971  res = ast_play_and_wait(chan, "vm-helpexit");
10972  }
10973  if (!res)
10974  res = ast_waitfordigit(chan, 6000);
10975  if (!res) {
10976  vms->repeats++;
10977  if (vms->repeats > 2) {
10978  res = 't';
10979  }
10980  }
10981  }
10982  return res;
10983 }
10984 
10985 static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
10986 {
10987  int res = 0;
10988  /* Play instructions and wait for new command */
10989  while (!res) {
10990  if (vms->starting) {
10991  if (vms->lastmsg > -1) {
10992  res = vm_play_folder_name(chan, vms->vmbox);
10993  if (!res)
10994  res = ast_play_and_wait(chan, "jp-wa");
10995  if (!res)
10996  res = ast_play_and_wait(chan, "digits/1");
10997  if (!res)
10998  res = ast_play_and_wait(chan, "jp-wo");
10999  if (!res)
11000  res = ast_play_and_wait(chan, "silence/1");
11001  }
11002  if (!res)
11003  res = ast_play_and_wait(chan, "vm-opts");
11004  } else {
11005  /* Added for additional help */
11006  if (skipadvanced) {
11007  res = vm_play_folder_name(chan, vms->vmbox);
11008  if (!res)
11009  res = ast_play_and_wait(chan, "jp-wa");
11010  if (!res)
11011  res = ast_play_and_wait(chan, "digits/1");
11012  if (!res)
11013  res = ast_play_and_wait(chan, "jp-wo");
11014  if (!res)
11015  res = ast_play_and_wait(chan, "silence/1");
11016  res = ast_play_and_wait(chan, "vm-opts-full");
11017  }
11018  /* Logic:
11019  * If the current message is not the first OR
11020  * if we're listening to the first new message and there are
11021  * also urgent messages, then prompt for navigation to the
11022  * previous message
11023  */
11024  if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
11025  res = ast_play_and_wait(chan, "vm-prev");
11026  }
11027  if (!res && !skipadvanced)
11028  res = ast_play_and_wait(chan, "vm-advopts");
11029  if (!res)
11030  res = ast_play_and_wait(chan, "vm-repeat");
11031  /* Logic:
11032  * If we're not listening to the last message OR
11033  * we're listening to the last urgent message and there are
11034  * also new non-urgent messages, then prompt for navigation
11035  * to the next message
11036  */
11037  if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
11038  (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
11039  res = ast_play_and_wait(chan, "vm-next");
11040  }
11041  if (!res) {
11042  int curmsg_deleted;
11043 #ifdef IMAP_STORAGE
11044  ast_mutex_lock(&vms->lock);
11045 #endif
11046  curmsg_deleted = vms->deleted[vms->curmsg];
11047 #ifdef IMAP_STORAGE
11048  ast_mutex_unlock(&vms->lock);
11049 #endif
11050  if (!curmsg_deleted) {
11051  res = ast_play_and_wait(chan, "vm-delete");
11052  } else {
11053  res = ast_play_and_wait(chan, "vm-undelete");
11054  }
11055  if (!res) {
11056  res = ast_play_and_wait(chan, "vm-toforward");
11057  }
11058  if (!res) {
11059  res = ast_play_and_wait(chan, "vm-savemessage");
11060  }
11061  }
11062  }
11063 
11064  if (!res) {
11065  res = ast_play_and_wait(chan, "vm-helpexit");
11066  }
11067  if (!res)
11068  res = ast_waitfordigit(chan, 6000);
11069  if (!res) {
11070  vms->repeats++;
11071  if (vms->repeats > 2) {
11072  res = 't';
11073  }
11074  }
11075 
11076  }
11077 
11078  return res;
11079 }
11080 
11081 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
11082 {
11083  int res = 0;
11084  /* Play instructions and wait for new command */
11085  while (!res) {
11086  if (vms->lastmsg > -1) {
11087  res = ast_play_and_wait(chan, "vm-listen");
11088  if (!res)
11089  res = vm_play_folder_name(chan, vms->vmbox);
11090  if (!res)
11091  res = ast_play_and_wait(chan, "press");
11092  if (!res)
11093  res = ast_play_and_wait(chan, "digits/1");
11094  }
11095  if (!res)
11096  res = ast_play_and_wait(chan, "vm-opts");
11097  if (!res) {
11098  vms->starting = 0;
11099  return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11100  }
11101  }
11102  return res;
11103 }
11104 
11105 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
11106 {
11107  if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
11108  return vm_instructions_ja(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11109  } else if (vms->starting && !strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
11110  return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11111  } else { /* Default to ENGLISH */
11112  return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11113  }
11114 }
11115 
11116 static int vm_newuser_setup(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11117 {
11118  int cmd = 0;
11119  int duration = 0;
11120  int tries = 0;
11121  char newpassword[80] = "";
11122  char newpassword2[80] = "";
11123  char prefile[PATH_MAX] = "";
11124  unsigned char buf[256];
11125  int bytes = 0;
11126 
11127  ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
11128  if (ast_adsi_available(chan)) {
11129  bytes += adsi_logo(buf + bytes);
11130  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
11131  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11132  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11133  bytes += ast_adsi_voice_mode(buf + bytes, 0);
11134  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
11135  }
11136 
11137  /* If forcename is set, have the user record their name */
11138  if (ast_test_flag(vmu, VM_FORCENAME)) {
11139  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
11140  if (ast_fileexists(prefile, NULL, NULL) < 1) {
11141  cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11142  if (cmd < 0 || cmd == 't' || cmd == '#')
11143  return cmd;
11144  }
11145  }
11146 
11147  /* If forcegreetings is set, have the user record their greetings */
11148  if (ast_test_flag(vmu, VM_FORCEGREET)) {
11149  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
11150  if (ast_fileexists(prefile, NULL, NULL) < 1) {
11151  cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11152  if (cmd < 0 || cmd == 't' || cmd == '#')
11153  return cmd;
11154  }
11155 
11156  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
11157  if (ast_fileexists(prefile, NULL, NULL) < 1) {
11158  cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11159  if (cmd < 0 || cmd == 't' || cmd == '#')
11160  return cmd;
11161  }
11162  }
11163 
11164  /*
11165  * Change the password last since new users will be able to skip over any steps this one comes before
11166  * by hanging up and calling back to voicemail main since the password is used to verify new user status.
11167  */
11168  for (;;) {
11169  newpassword[1] = '\0';
11170  newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
11171  if (cmd == '#')
11172  newpassword[0] = '\0';
11173  if (cmd < 0 || cmd == 't' || cmd == '#')
11174  return cmd;
11175  cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
11176  if (cmd < 0 || cmd == 't' || cmd == '#')
11177  return cmd;
11178  cmd = check_password(vmu, newpassword); /* perform password validation */
11179  if (cmd != 0) {
11180  ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
11181  cmd = ast_play_and_wait(chan, vm_invalid_password);
11182  } else {
11183  newpassword2[1] = '\0';
11184  newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
11185  if (cmd == '#')
11186  newpassword2[0] = '\0';
11187  if (cmd < 0 || cmd == 't' || cmd == '#')
11188  return cmd;
11189  cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
11190  if (cmd < 0 || cmd == 't' || cmd == '#')
11191  return cmd;
11192  if (!strcmp(newpassword, newpassword2))
11193  break;
11194  ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
11195  cmd = ast_play_and_wait(chan, vm_mismatch);
11196  }
11197  if (++tries == 3)
11198  return -1;
11199  if (cmd != 0) {
11200  cmd = ast_play_and_wait(chan, vm_pls_try_again);
11201  }
11202  }
11203  if (pwdchange & PWDCHANGE_INTERNAL)
11204  vm_change_password(vmu, newpassword);
11205  if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
11206  vm_change_password_shell(vmu, newpassword);
11207 
11208  ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
11209  cmd = ast_play_and_wait(chan, vm_passchanged);
11210 
11211  return cmd;
11212 }
11213 
11214 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11215 {
11216  int cmd = 0;
11217  int retries = 0;
11218  int duration = 0;
11219  char newpassword[80] = "";
11220  char newpassword2[80] = "";
11221  char prefile[PATH_MAX] = "";
11222  unsigned char buf[256];
11223  int bytes = 0;
11224  SCOPE_ENTER(3, "%s: %s entering mailbox options", ast_channel_name(chan), vms->username);
11225 
11226  ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
11227  if (ast_adsi_available(chan)) {
11228  bytes += adsi_logo(buf + bytes);
11229  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
11230  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11231  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11232  bytes += ast_adsi_voice_mode(buf + bytes, 0);
11233  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
11234  }
11235  while ((cmd >= 0) && (cmd != 't')) {
11236  if (cmd)
11237  retries = 0;
11238  switch (cmd) {
11239  case '1': /* Record your unavailable message */
11240  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
11241  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_record_review, chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11242  break;
11243  case '2': /* Record your busy message */
11244  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
11245  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_record_review, chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11246  break;
11247  case '3': /* Record greeting */
11248  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
11249  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_record_review, chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11250  break;
11251  case '4': /* manage the temporary greeting */
11252  cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
11253  break;
11254  case '5': /* change password */
11255  if (vmu->password[0] == '-') {
11256  cmd = ast_play_and_wait(chan, "vm-no");
11257  break;
11258  }
11259  newpassword[1] = '\0';
11260  newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
11261  if (cmd == '#')
11262  newpassword[0] = '\0';
11263  else {
11264  if (cmd < 0)
11265  break;
11266  if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
11267  break;
11268  }
11269  }
11270  cmd = check_password(vmu, newpassword); /* perform password validation */
11271  if (cmd != 0) {
11272  ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
11273  cmd = ast_play_and_wait(chan, vm_invalid_password);
11274  if (!cmd) {
11275  cmd = ast_play_and_wait(chan, vm_pls_try_again);
11276  }
11277  break;
11278  }
11279  newpassword2[1] = '\0';
11280  newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
11281  if (cmd == '#')
11282  newpassword2[0] = '\0';
11283  else {
11284  if (cmd < 0)
11285  break;
11286 
11287  if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
11288  break;
11289  }
11290  }
11291  if (strcmp(newpassword, newpassword2)) {
11292  ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
11293  cmd = ast_play_and_wait(chan, vm_mismatch);
11294  if (!cmd) {
11295  cmd = ast_play_and_wait(chan, vm_pls_try_again);
11296  }
11297  break;
11298  }
11299 
11300  if (pwdchange & PWDCHANGE_INTERNAL) {
11301  vm_change_password(vmu, newpassword);
11302  }
11303  if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
11304  vm_change_password_shell(vmu, newpassword);
11305  }
11306 
11307  ast_debug(1, "User %s set password to %s of length %d\n",
11308  vms->username, newpassword, (int) strlen(newpassword));
11309  cmd = ast_play_and_wait(chan, vm_passchanged);
11310  break;
11311  case '*':
11312  cmd = 't';
11313  break;
11314  default:
11315  cmd = 0;
11316  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
11317  SCOPE_CALL(-1, RETRIEVE, prefile, -1, vmu->mailbox, vmu->context);
11318  if (ast_fileexists(prefile, NULL, NULL)) {
11319  cmd = ast_play_and_wait(chan, "vm-tmpexists");
11320  }
11321  SCOPE_CALL(-1, DISPOSE, prefile, -1);
11322  if (!cmd) {
11323  cmd = ast_play_and_wait(chan, "vm-options");
11324  }
11325  if (!cmd) {
11326  cmd = ast_waitfordigit(chan, 6000);
11327  }
11328  if (!cmd) {
11329  retries++;
11330  }
11331  if (retries > 3) {
11332  cmd = 't';
11333  }
11334  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
11335  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
11336  }
11337  }
11338  if (cmd == 't')
11339  cmd = 0;
11340  SCOPE_EXIT_RTN_VALUE(cmd, "Done\n");
11341 }
11342 
11343 /*!
11344  * \brief The handler for 'record a temporary greeting'.
11345  * \param chan
11346  * \param vmu
11347  * \param vms
11348  * \param fmtc
11349  * \param record_gain
11350  *
11351  * This is option 4 from the mailbox options menu.
11352  * This function manages the following promptings:
11353  * 1: play / record / review the temporary greeting. : invokes play_record_review().
11354  * 2: remove (delete) the temporary greeting.
11355  * *: return to the main menu.
11356  *
11357  * \return zero on success, -1 on error.
11358  */
11359 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11360 {
11361  int cmd = 0;
11362  int retries = 0;
11363  int duration = 0;
11364  char prefile[PATH_MAX] = "";
11365  unsigned char buf[256];
11366  int bytes = 0;
11367 
11368  if (ast_adsi_available(chan)) {
11369  bytes += adsi_logo(buf + bytes);
11370  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
11371  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11372  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11373  bytes += ast_adsi_voice_mode(buf + bytes, 0);
11374  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
11375  }
11376 
11377  ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
11378  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
11379  while ((cmd >= 0) && (cmd != 't')) {
11380  if (cmd)
11381  retries = 0;
11382  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
11383  if (ast_fileexists(prefile, NULL, NULL) <= 0) {
11384  cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11385  if (cmd == -1) {
11386  break;
11387  }
11388  cmd = 't';
11389  } else {
11390  switch (cmd) {
11391  case '1':
11392  cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11393  break;
11394  case '2':
11395  DELETE(prefile, -1, prefile, vmu);
11396  ast_play_and_wait(chan, "vm-tempremoved");
11397  cmd = 't';
11398  break;
11399  case '*':
11400  cmd = 't';
11401  break;
11402  default:
11403  cmd = ast_play_and_wait(chan,
11404  ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
11405  "vm-tempgreeting2" : "vm-tempgreeting");
11406  if (!cmd) {
11407  cmd = ast_waitfordigit(chan, 6000);
11408  }
11409  if (!cmd) {
11410  retries++;
11411  }
11412  if (retries > 3) {
11413  cmd = 't';
11414  }
11415  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
11416  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
11417  }
11418  }
11419  DISPOSE(prefile, -1);
11420  }
11421  if (cmd == 't')
11422  cmd = 0;
11423  return cmd;
11424 }
11425 
11426 /*!
11427  * \brief Greek syntax for 'You have N messages' greeting.
11428  * \param chan
11429  * \param vms
11430  * \param vmu
11431  *
11432  * \return zero on success, -1 on error.
11433  */
11434 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11435 {
11436  int cmd = 0;
11437 
11438  if (vms->lastmsg > -1) {
11439  cmd = play_message(chan, vmu, vms);
11440  } else {
11441  cmd = ast_play_and_wait(chan, "vm-youhaveno");
11442  if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
11443  if (!cmd) {
11444  snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
11445  cmd = ast_play_and_wait(chan, vms->fn);
11446  }
11447  if (!cmd)
11448  cmd = ast_play_and_wait(chan, "vm-messages");
11449  } else {
11450  if (!cmd)
11451  cmd = ast_play_and_wait(chan, "vm-messages");
11452  if (!cmd) {
11453  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11454  cmd = ast_play_and_wait(chan, vms->fn);
11455  }
11456  }
11457  }
11458  return cmd;
11459 }
11460 
11461 /* Hebrew Syntax */
11462 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11463 {
11464  int cmd = 0;
11465 
11466  if (vms->lastmsg > -1) {
11467  cmd = play_message(chan, vmu, vms);
11468  } else {
11469  if (!strcasecmp(vms->fn, "INBOX")) {
11470  cmd = ast_play_and_wait(chan, "vm-nonewmessages");
11471  } else {
11472  cmd = ast_play_and_wait(chan, "vm-nomessages");
11473  }
11474  }
11475  return cmd;
11476 }
11477 
11478 /*!
11479  * \brief Default English syntax for 'You have N messages' greeting.
11480  * \param chan
11481  * \param vms
11482  * \param vmu
11483  *
11484  * \return zero on success, -1 on error.
11485  */
11486 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11487 {
11488  int cmd = 0;
11489 
11490  if (vms->lastmsg > -1) {
11491  cmd = play_message(chan, vmu, vms);
11492  } else {
11493  cmd = ast_play_and_wait(chan, "vm-youhave");
11494  if (!cmd)
11495  cmd = ast_play_and_wait(chan, "vm-no");
11496  if (!cmd) {
11497  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11498  cmd = ast_play_and_wait(chan, vms->fn);
11499  }
11500  if (!cmd)
11501  cmd = ast_play_and_wait(chan, "vm-messages");
11502  }
11503  return cmd;
11504 }
11505 
11506 /*!
11507  *\brief Italian syntax for 'You have N messages' greeting.
11508  * \param chan
11509  * \param vms
11510  * \param vmu
11511  *
11512  * \return zero on success, -1 on error.
11513  */
11514 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11515 {
11516  int cmd;
11517 
11518  if (vms->lastmsg > -1) {
11519  cmd = play_message(chan, vmu, vms);
11520  } else {
11521  cmd = ast_play_and_wait(chan, "vm-no");
11522  if (!cmd)
11523  cmd = ast_play_and_wait(chan, "vm-message");
11524  if (!cmd) {
11525  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11526  cmd = ast_play_and_wait(chan, vms->fn);
11527  }
11528  }
11529  return cmd;
11530 }
11531 
11532 /*!
11533  * \brief Japanese syntax for 'You have N messages' greeting.
11534  * \param chan
11535  * \param vms
11536  * \param vmu
11537  *
11538  * \return zero on success, -1 on error.
11539  */
11540 static int vm_browse_messages_ja(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11541 {
11542  int cmd = 0;
11543 
11544  if (vms->lastmsg > -1) {
11545  cmd = play_message(chan, vmu, vms);
11546  } else {
11547  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11548  cmd = ast_play_and_wait(chan, vms->fn);
11549  if (!cmd)
11550  cmd = ast_play_and_wait(chan, "vm-messages");
11551  if (!cmd)
11552  cmd = ast_play_and_wait(chan, "jp-wa");
11553  if (!cmd)
11554  cmd = ast_play_and_wait(chan, "jp-arimasen");
11555  }
11556  return cmd;
11557 }
11558 
11559 /*!
11560  * \brief Spanish syntax for 'You have N messages' greeting.
11561  * \param chan
11562  * \param vms
11563  * \param vmu
11564  *
11565  * \return zero on success, -1 on error.
11566  */
11567 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11568 {
11569  int cmd;
11570 
11571  if (vms->lastmsg > -1) {
11572  cmd = play_message(chan, vmu, vms);
11573  } else {
11574  cmd = ast_play_and_wait(chan, "vm-youhaveno");
11575  if (!cmd)
11576  cmd = ast_play_and_wait(chan, "vm-messages");
11577  if (!cmd) {
11578  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11579  cmd = ast_play_and_wait(chan, vms->fn);
11580  }
11581  }
11582  return cmd;
11583 }
11584 
11585 /*!
11586  * \brief Portuguese syntax for 'You have N messages' greeting.
11587  * \param chan
11588  * \param vms
11589  * \param vmu
11590  *
11591  * \return zero on success, -1 on error.
11592  */
11593 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11594 {
11595  int cmd;
11596 
11597  if (vms->lastmsg > -1) {
11598  cmd = play_message(chan, vmu, vms);
11599  } else {
11600  cmd = ast_play_and_wait(chan, "vm-no");
11601  if (!cmd) {
11602  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11603  cmd = ast_play_and_wait(chan, vms->fn);
11604  }
11605  if (!cmd)
11606  cmd = ast_play_and_wait(chan, "vm-messages");
11607  }
11608  return cmd;
11609 }
11610 
11611 /*!
11612  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
11613  * \param chan
11614  * \param vms
11615  * \param vmu
11616  *
11617  * \return zero on success, -1 on error.
11618  */
11619 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11620 {
11621  int cmd;
11622 
11623  if (vms->lastmsg > -1) {
11624  cmd = play_message(chan, vmu, vms);
11625  } else {
11626  cmd = ast_play_and_wait(chan, "vm-you");
11627  if (!cmd)
11628  cmd = ast_play_and_wait(chan, "vm-haveno");
11629  if (!cmd)
11630  cmd = ast_play_and_wait(chan, "vm-messages");
11631  if (!cmd) {
11632  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11633  cmd = ast_play_and_wait(chan, vms->fn);
11634  }
11635  }
11636  return cmd;
11637 }
11638 
11639 /*!
11640  * \brief Vietnamese syntax for 'You have N messages' greeting.
11641  * \param chan
11642  * \param vms
11643  * \param vmu
11644  *
11645  * \return zero on success, -1 on error.
11646  */
11647 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11648 {
11649  int cmd = 0;
11650 
11651  if (vms->lastmsg > -1) {
11652  cmd = play_message(chan, vmu, vms);
11653  } else {
11654  cmd = ast_play_and_wait(chan, "vm-no");
11655  if (!cmd) {
11656  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11657  cmd = ast_play_and_wait(chan, vms->fn);
11658  }
11659  }
11660  return cmd;
11661 }
11662 
11663 /*!
11664  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
11665  * \param chan The channel for the current user. We read the language property from this.
11666  * \param vms passed into the language-specific vm_browse_messages function.
11667  * \param vmu passed into the language-specific vm_browse_messages function.
11668  *
11669  * The method to be invoked is determined by the value of language code property in the user's channel.
11670  * The default (when unable to match) is to use english.
11671  *
11672  * \return zero on success, -1 on error.
11673  */
11674 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11675 {
11676  if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH */
11677  return vm_browse_messages_es(chan, vms, vmu);
11678  } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK */
11679  return vm_browse_messages_gr(chan, vms, vmu);
11680  } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW */
11681  return vm_browse_messages_he(chan, vms, vmu);
11682  } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN */
11683  return vm_browse_messages_it(chan, vms, vmu);
11684  } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE */
11685  return vm_browse_messages_ja(chan, vms, vmu);
11686  } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE */
11687  return vm_browse_messages_pt(chan, vms, vmu);
11688  } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE */
11689  return vm_browse_messages_vi(chan, vms, vmu);
11690  } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) */
11691  return vm_browse_messages_zh(chan, vms, vmu);
11692  } else { /* Default to English syntax */
11693  return vm_browse_messages_en(chan, vms, vmu);
11694  }
11695 }
11696 
11697 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
11698  struct ast_vm_user *res_vmu, const char *context, const char *prefix,
11699  int skipuser, int max_logins, int silent)
11700 {
11701  int useadsi = 0, valid = 0, logretries = 0;
11702  char password[AST_MAX_EXTENSION], *passptr = NULL;
11703  struct ast_vm_user vmus, *vmu = NULL;
11704 
11705  /* If ADSI is supported, setup login screen */
11706  adsi_begin(chan, &useadsi);
11707  if (!skipuser && useadsi)
11708  adsi_login(chan);
11709  if (!silent && !skipuser && ast_streamfile(chan, vm_login, ast_channel_language(chan))) {
11710  ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
11711  return -1;
11712  }
11713 
11714  /* Authenticate them and get their mailbox/password */
11715 
11716  while (!valid && (logretries < max_logins)) {
11717  /* Prompt for, and read in the username */
11718  if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
11719  ast_log(AST_LOG_WARNING, "Couldn't read username\n");
11720  return -1;
11721  }
11722  if (ast_strlen_zero(mailbox)) {
11723  if (ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str) {
11724  ast_copy_string(mailbox, ast_channel_caller(chan)->id.number.str, mailbox_size);
11725  } else {
11726  ast_verb(3, "Username not entered\n");
11727  return -1;
11728  }
11729  } else if (mailbox[0] == '*') {
11730  /* user entered '*' */
11731  ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
11732  if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
11733  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
11734  return -1;
11735  }
11736  ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
11737  mailbox[0] = '\0';
11738  }
11739 
11740  if (useadsi)
11741  adsi_password(chan);
11742 
11743  if (!ast_strlen_zero(prefix)) {
11744  char fullusername[80];
11745 
11746  ast_copy_string(fullusername, prefix, sizeof(fullusername));
11747  strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
11748  ast_copy_string(mailbox, fullusername, mailbox_size);
11749  }
11750 
11751  ast_debug(1, "Before find user for mailbox %s\n", mailbox);
11752  memset(&vmus, 0, sizeof(vmus));
11753  vmu = find_user(&vmus, context, mailbox);
11754  if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
11755  /* saved password is blank, so don't bother asking */
11756  password[0] = '\0';
11757  } else {
11758  if (ast_streamfile(chan, vm_password, ast_channel_language(chan))) {
11759  if (!ast_check_hangup(chan)) {
11760  ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
11761  }
11762  free_user(vmu);
11763  return -1;
11764  }
11765  if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
11766  ast_log(AST_LOG_NOTICE, "Unable to read password\n");
11767  free_user(vmu);
11768  return -1;
11769  } else if (password[0] == '*') {
11770  /* user entered '*' */
11771  ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
11772  if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
11773  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
11774  mailbox[0] = '*';
11775  free_user(vmu);
11776  return -1;
11777  }
11778  ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
11779  mailbox[0] = '\0';
11780  /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
11781  free_user(vmu);
11782  vmu = NULL;
11783  }
11784  }
11785 
11786  if (vmu) {
11787  passptr = vmu->password;
11788  if (passptr[0] == '-') passptr++;
11789  }
11790  if (vmu && !strcmp(passptr, password))
11791  valid++;
11792  else {
11793  ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
11794  if (!ast_strlen_zero(prefix))
11795  mailbox[0] = '\0';
11796  }
11797  logretries++;
11798  if (!valid) {
11799  if (skipuser || logretries >= max_logins) {
11800  if (ast_streamfile(chan, "vm-incorrect", ast_channel_language(chan))) {
11801  ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
11802  free_user(vmu);
11803  return -1;
11804  }
11805  if (ast_waitstream(chan, "")) { /* Channel is hung up */
11806  free_user(vmu);
11807  return -1;
11808  }
11809  } else {
11810  if (useadsi)
11811  adsi_login(chan);
11812  if (ast_streamfile(chan, "vm-incorrect-mailbox", ast_channel_language(chan))) {
11813  ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
11814  free_user(vmu);
11815  return -1;
11816  }
11817  }
11818  }
11819  }
11820  if (!valid && (logretries >= max_logins)) {
11821  ast_stopstream(chan);
11822  ast_play_and_wait(chan, "vm-goodbye");
11823  free_user(vmu);
11824  return -1;
11825  }
11826  if (vmu && !skipuser) {
11827  *res_vmu = *vmu;
11828  }
11829  return 0;
11830 }
11831 
11832 static int play_message_by_id_helper(struct ast_channel *chan,
11833  struct ast_vm_user *vmu,
11834  struct vm_state *vms,
11835  const char *msg_id)
11836 {
11837  if (message_range_and_existence_check(vms, &msg_id, 1, &vms->curmsg, vmu)) {
11838  return -1;
11839  }
11840  /* Found the msg, so play it back */
11841 
11842  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
11843 
11844 #ifdef IMAP_STORAGE
11845  /*IMAP storage stores any prepended message from a forward
11846  * as a separate file from the rest of the message
11847  */
11848  if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
11849  wait_file(chan, vms, vms->introfn);
11850  }
11851 #endif
11852  RETRIEVE(vms->curdir,vms->curmsg,vmu->mailbox, vmu->context);
11853 
11854  if ((wait_file(chan, vms, vms->fn)) < 0) {
11855  ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
11856  } else {
11857 #ifdef IMAP_STORAGE
11858  ast_mutex_lock(&vms->lock);
11859 #endif
11860  vms->heard[vms->curmsg] = 1;
11861 #ifdef IMAP_STORAGE
11862  ast_mutex_unlock(&vms->lock);
11863 #endif
11864  }
11865  DISPOSE(vms->curdir, vms->curmsg);
11866  return 0;
11867 }
11868 
11869 /*!
11870  * \brief Finds a message in a specific mailbox by msg_id and plays it to the channel
11871  *
11872  * \retval 0 Success
11873  * \retval -1 Failure
11874  */
11875 static int play_message_by_id(struct ast_channel *chan, const char *mailbox, const char *context, const char *msg_id)
11876 {
11877  struct vm_state vms;
11878  struct ast_vm_user *vmu = NULL, vmus;
11879  int res = 0;
11880  int open = 0;
11881  int played = 0;
11882  int i;
11883 
11884  memset(&vmus, 0, sizeof(vmus));
11885  memset(&vms, 0, sizeof(vms));
11886 
11887  if (!(vmu = find_user(&vmus, context, mailbox))) {
11888  goto play_msg_cleanup;
11889  }
11890 
11891  /* Iterate through every folder, find the msg, and play it */
11892  for (i = 0; i < ARRAY_LEN(mailbox_folders) && !played; i++) {
11893  ast_copy_string(vms.username, mailbox, sizeof(vms.username));
11894  vms.lastmsg = -1;
11895 
11896  /* open the mailbox state */
11897  if ((res = open_mailbox(&vms, vmu, i)) < 0) {
11898  ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
11899  res = -1;
11900  goto play_msg_cleanup;
11901  }
11902  open = 1;
11903 
11904  /* play msg if it exists in this mailbox */
11905  if ((vms.lastmsg != -1) && !(play_message_by_id_helper(chan, vmu, &vms, msg_id))) {
11906  played = 1;
11907  }
11908 
11909  /* close mailbox */
11910  if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
11911  res = -1;
11912  goto play_msg_cleanup;
11913  }
11914  open = 0;
11915  }
11916 
11917 play_msg_cleanup:
11918  if (!played) {
11919  res = -1;
11920  }
11921 
11922  if (vmu && open) {
11923  close_mailbox(&vms, vmu);
11924  }
11925 
11926 #ifdef IMAP_STORAGE
11927  if (vmu) {
11928  vmstate_delete(&vms);
11929  }
11930 #endif
11931 
11932  free_user(vmu);
11933 
11934  return res;
11935 }
11936 
11937 static int vm_playmsgexec(struct ast_channel *chan, const char *data)
11938 {
11939  char *parse;
11940  char *mailbox = NULL;
11941  char *context = NULL;
11942  int res;
11943 
11944  AST_DECLARE_APP_ARGS(args,
11945  AST_APP_ARG(mailbox);
11946  AST_APP_ARG(msg_id);
11947  );
11948 
11949  if (ast_channel_state(chan) != AST_STATE_UP) {
11950  ast_debug(1, "Before ast_answer\n");
11951  ast_answer(chan);
11952  }
11953 
11954  if (ast_strlen_zero(data)) {
11955  return -1;
11956  }
11957 
11958  parse = ast_strdupa(data);
11959  AST_STANDARD_APP_ARGS(args, parse);
11960 
11961  if (ast_strlen_zero(args.mailbox) || ast_strlen_zero(args.msg_id)) {
11962  return -1;
11963  }
11964 
11965  if ((context = strchr(args.mailbox, '@'))) {
11966  *context++ = '\0';
11967  }
11968  mailbox = args.mailbox;
11969 
11970  res = play_message_by_id(chan, mailbox, context, args.msg_id);
11971  pbx_builtin_setvar_helper(chan, "VOICEMAIL_PLAYBACKSTATUS", res ? "FAILED" : "SUCCESS");
11972 
11973  return 0;
11974 }
11975 
11976 static int show_mailbox_details(struct ast_cli_args *a)
11977 {
11978 #define VMBOX_STRING_HEADER_FORMAT "%-32.32s %-32.32s %-16.16s %-16.16s %-16.16s %-16.16s\n"
11979 #define VMBOX_STRING_DATA_FORMAT "%-32.32s %-32.32s %-16.16s %-16.16s %-16.16s %-16.16s\n"
11980 
11981  const char *mailbox = a->argv[3];
11982  const char *context = a->argv[4];
11983  struct vm_state vms;
11984  struct ast_vm_user *vmu = NULL, vmus;
11985  memset(&vmus, 0, sizeof(vmus));
11986  memset(&vms, 0, sizeof(vms));
11987 
11988  if (!(vmu = find_user(&vmus, context, mailbox))) {
11989  ast_cli(a->fd, "Can't find voicemail user %s@%s\n", mailbox, context);
11990  return -1;
11991  }
11992 
11993  ast_cli(a->fd, VMBOX_STRING_HEADER_FORMAT, "Full Name", "Email", "Pager", "Language", "Locale", "Time Zone");
11994  ast_cli(a->fd, VMBOX_STRING_DATA_FORMAT, vmu->fullname, vmu->email, vmu->pager, vmu->language, vmu->locale, vmu->zonetag);
11995 
11996  return 0;
11997 }
11998 
11999 static int show_mailbox_snapshot(struct ast_cli_args *a)
12000 {
12001 #define VM_STRING_HEADER_FORMAT "%-8.8s %-32.32s %-32.32s %-9.9s %-6.6s %-30.30s\n"
12002  const char *mailbox = a->argv[3];
12003  const char *context = a->argv[4];
12004  struct ast_vm_mailbox_snapshot *mailbox_snapshot;
12005  struct ast_vm_msg_snapshot *msg;
12006  int i;
12007 
12008  /* Take a snapshot of the mailbox and walk through each folder's contents */
12009  mailbox_snapshot = ast_vm_mailbox_snapshot_create(mailbox, context, NULL, 0, AST_VM_SNAPSHOT_SORT_BY_ID, 0);
12010  if (!mailbox_snapshot) {
12011  ast_cli(a->fd, "Can't create snapshot for voicemail user %s@%s\n", mailbox, context);
12012  return -1;
12013  }
12014 
12015  ast_cli(a->fd, VM_STRING_HEADER_FORMAT, "Folder", "Caller ID", "Date", "Duration", "Flag", "ID");
12016 
12017  for (i = 0; i < mailbox_snapshot->folders; i++) {
12018  AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
12019  ast_cli(a->fd, VM_STRING_HEADER_FORMAT, msg->folder_name, msg->callerid, msg->origdate, msg->duration,
12020  msg->flag, msg->msg_id);
12021  }
12022  }
12023 
12024  ast_cli(a->fd, "%d Message%s Total\n", mailbox_snapshot->total_msg_num, ESS(mailbox_snapshot->total_msg_num));
12025  /* done, destroy. */
12026  mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
12027 
12028  return 0;
12029 }
12030 
12031 static int show_messages_for_mailbox(struct ast_cli_args *a)
12032 {
12033  if (show_mailbox_details(a)){
12034  return -1;
12035  }
12036  ast_cli(a->fd, "\n");
12037  return show_mailbox_snapshot(a);
12038 }
12039 
12040 static int forward_message_from_mailbox(struct ast_cli_args *a)
12041 {
12042  const char *from_mailbox = a->argv[2];
12043  const char *from_context = a->argv[3];
12044  const char *from_folder = a->argv[4];
12045  const char *id[] = { a->argv[5] };
12046  const char *to_mailbox = a->argv[6];
12047  const char *to_context = a->argv[7];
12048  const char *to_folder = a->argv[8];
12049  int ret = vm_msg_forward(from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder, 1, id, 0);
12050  if (ret) {
12051  ast_cli(a->fd, "Error forwarding message %s from mailbox %s@%s %s to mailbox %s@%s %s\n",
12052  id[0], from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder);
12053  } else {
12054  ast_cli(a->fd, "Forwarded message %s from mailbox %s@%s %s to mailbox %s@%s %s\n",
12055  id[0], from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder);
12056  }
12057  return ret;
12058 }
12059 
12060 static int move_message_from_mailbox(struct ast_cli_args *a)
12061 {
12062  const char *mailbox = a->argv[2];
12063  const char *context = a->argv[3];
12064  const char *from_folder = a->argv[4];
12065  const char *id[] = { a->argv[5] };
12066  const char *to_folder = a->argv[6];
12067  int ret = vm_msg_move(mailbox, context, 1, from_folder, id, to_folder);
12068  if (ret) {
12069  ast_cli(a->fd, "Error moving message %s from mailbox %s@%s %s to %s\n",
12070  id[0], mailbox, context, from_folder, to_folder);
12071  } else {
12072  ast_cli(a->fd, "Moved message %s from mailbox %s@%s %s to %s\n",
12073  id[0], mailbox, context, from_folder, to_folder);
12074  }
12075  return ret;
12076 }
12077 
12078 static int remove_message_from_mailbox(struct ast_cli_args *a)
12079 {
12080  const char *mailbox = a->argv[2];
12081  const char *context = a->argv[3];
12082  const char *folder = a->argv[4];
12083  const char *id[] = { a->argv[5] };
12084  int ret = vm_msg_remove(mailbox, context, 1, folder, id);
12085  if (ret) {
12086  ast_cli(a->fd, "Error removing message %s from mailbox %s@%s %s\n",
12087  id[0], mailbox, context, folder);
12088  } else {
12089  ast_cli(a->fd, "Removed message %s from mailbox %s@%s %s\n",
12090  id[0], mailbox, context, folder);
12091  }
12092  return ret;
12093 }
12094 
12095 static char *complete_voicemail_show_mailbox(struct ast_cli_args *a)
12096 {
12097  const char *word = a->word;
12098  int pos = a->pos;
12099  int state = a->n;
12100  int which = 0;
12101  int wordlen;
12102  struct ast_vm_user *vmu;
12103  const char *context = "", *mailbox = "";
12104  char *ret = NULL;
12105 
12106  /* 0 - voicemail; 1 - show; 2 - mailbox; 3 - <mailbox>; 4 - <context> */
12107  if (pos == 3) {
12108  wordlen = strlen(word);
12109  AST_LIST_LOCK(&users);
12110  AST_LIST_TRAVERSE(&users, vmu, list) {
12111  if (!strncasecmp(word, vmu->mailbox , wordlen)) {
12112  if (mailbox && strcmp(mailbox, vmu->mailbox) && ++which > state) {
12113  ret = ast_strdup(vmu->mailbox);
12115  return ret;
12116  }
12117  mailbox = vmu->mailbox;
12118  }
12119  }
12121  } else if (pos == 4) {
12122  /* Only display contexts that match the user in pos 3 */
12123  const char *box = a->argv[3];
12124  wordlen = strlen(word);
12125  AST_LIST_LOCK(&users);
12126  AST_LIST_TRAVERSE(&users, vmu, list) {
12127  if (!strncasecmp(word, vmu->context, wordlen) && !strcasecmp(box, vmu->mailbox)) {
12128  if (context && strcmp(context, vmu->context) && ++which > state) {
12129  ret = ast_strdup(vmu->context);
12131  return ret;
12132  }
12133  context = vmu->context;
12134  }
12135  }
12137  }
12138 
12139  return ret;
12140 }
12141 
12142 static char *handle_voicemail_show_mailbox(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12143 {
12144  switch (cmd) {
12145  case CLI_INIT:
12146  e->command = "voicemail show mailbox";
12147  e->usage =
12148  "Usage: voicemail show mailbox <mailbox> <context>\n"
12149  " Show contents of mailbox <mailbox>@<context>\n";
12150  return NULL;
12151  case CLI_GENERATE:
12152  return complete_voicemail_show_mailbox(a);
12153  case CLI_HANDLER:
12154  break;
12155  }
12156 
12157  if (a->argc != 5) {
12158  return CLI_SHOWUSAGE;
12159  }
12160 
12161  if (show_messages_for_mailbox(a)) {
12162  return CLI_FAILURE;
12163  }
12164 
12165  return CLI_SUCCESS;
12166 }
12167 
12168 /* Handles filling in data for one of the following three formats (based on maxpos = 5|6|8):
12169 
12170  maxpos = 5
12171  0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12172  maxpos = 6
12173  0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12174  6 - <to_folder>;
12175  maxpos = 8
12176  0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12177  6 - <to_mailbox>; 7 - <to_context>; 8 - <to_folder>;
12178 
12179  Passing in the maximum expected position 'maxpos' helps us fill in the missing entries in one function
12180  instead of three by taking advantage of the overlap in the command sequence between forward, move and
12181  remove as each of these use nearly the same syntax up until their maximum number of arguments.
12182  The value of pos = 6 changes to be either <messageid> or <folder> based on maxpos being 6 or 8.
12183 */
12184 
12185 static char *complete_voicemail_move_message(struct ast_cli_args *a, int maxpos)
12186 {
12187  const char *word = a->word;
12188  int pos = a->pos;
12189  int state = a->n;
12190  int which = 0;
12191  int wordlen;
12192  struct ast_vm_user *vmu;
12193  const char *context = "", *mailbox = "", *folder = "", *id = "";
12194  char *ret = NULL;
12195 
12196  if (pos > maxpos) {
12197  /* If the passed in pos is above the max, return NULL to avoid 'over-filling' the cli */
12198  return NULL;
12199  }
12200 
12201  /* if we are in pos 2 or pos 6 in 'forward' mode */
12202  if (pos == 2 || (pos == 6 && maxpos == 8)) {
12203  /* find users */
12204  wordlen = strlen(word);
12205  AST_LIST_LOCK(&users);
12206  AST_LIST_TRAVERSE(&users, vmu, list) {
12207  if (!strncasecmp(word, vmu->mailbox , wordlen)) {
12208  if (mailbox && strcmp(mailbox, vmu->mailbox) && ++which > state) {
12209  ret = ast_strdup(vmu->mailbox);
12211  return ret;
12212  }
12213  mailbox = vmu->mailbox;
12214  }
12215  }
12217  } else if (pos == 3 || pos == 7) {
12218  /* find contexts that match the user */
12219  mailbox = (pos == 3) ? a->argv[2] : a->argv[6];
12220  wordlen = strlen(word);
12221  AST_LIST_LOCK(&users);
12222  AST_LIST_TRAVERSE(&users, vmu, list) {
12223  if (!strncasecmp(word, vmu->context, wordlen) && !strcasecmp(mailbox, vmu->mailbox)) {
12224  if (context && strcmp(context, vmu->context) && ++which > state) {
12225  ret = ast_strdup(vmu->context);
12227  return ret;
12228  }
12229  context = vmu->context;
12230  }
12231  }
12233  } else if (pos == 4 || pos == 8 || (pos == 6 && maxpos == 6) ) {
12234  int i;
12235  /* Walk through the standard folders */
12236  wordlen = strlen(word);
12237  for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
12238  if (folder && !strncasecmp(word, mailbox_folders[i], wordlen) && ++which > state) {
12239  return ast_strdup(mailbox_folders[i]);
12240  }
12241  folder = mailbox_folders[i];
12242  }
12243  } else if (pos == 5) {
12244  /* find messages in the folder */
12245  struct ast_vm_mailbox_snapshot *mailbox_snapshot;
12246  struct ast_vm_msg_snapshot *msg;
12247  mailbox = a->argv[2];
12248  context = a->argv[3];
12249  folder = a->argv[4];
12250  wordlen = strlen(word);
12251 
12252  /* Take a snapshot of the mailbox and snag the individual info */
12253  if ((mailbox_snapshot = ast_vm_mailbox_snapshot_create(mailbox, context, folder, 0, AST_VM_SNAPSHOT_SORT_BY_ID, 0))) {
12254  int i;
12255  /* we are only requesting the one folder, but we still need to know it's index */
12256  for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
12257  if (!strcasecmp(mailbox_folders[i], folder)) {
12258  break;
12259  }
12260  }
12261  AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
12262  if (id && !strncasecmp(word, msg->msg_id, wordlen) && ++which > state) {
12263  ret = ast_strdup(msg->msg_id);
12264  break;
12265  }
12266  id = msg->msg_id;
12267  }
12268  /* done, destroy. */
12269  mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
12270  }
12271  }
12272 
12273  return ret;
12274 }
12275 
12276 static char *handle_voicemail_forward_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12277 {
12278  switch (cmd) {
12279  case CLI_INIT:
12280  e->command = "voicemail forward";
12281  e->usage =
12282  "Usage: voicemail forward <from_mailbox> <from_context> <from_folder> <messageid> <to_mailbox> <to_context> <to_folder>\n"
12283  " Forward message <messageid> in mailbox <mailbox>@<context> <from_folder>\n"
12284  " to mailbox <mailbox>@<context> <to_folder>\n";
12285  return NULL;
12286  case CLI_GENERATE:
12287  return complete_voicemail_move_message(a, 8);
12288  case CLI_HANDLER:
12289  break;
12290  }
12291 
12292  if (a->argc != 9) {
12293  return CLI_SHOWUSAGE;
12294  }
12295 
12296  if (forward_message_from_mailbox(a)) {
12297  return CLI_FAILURE;
12298  }
12299 
12300  return CLI_SUCCESS;
12301 }
12302 
12303 static char *handle_voicemail_move_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12304 {
12305  switch (cmd) {
12306  case CLI_INIT:
12307  e->command = "voicemail move";
12308  e->usage =
12309  "Usage: voicemail move <mailbox> <context> <from_folder> <messageid> <to_folder>\n"
12310  " Move message <messageid> in mailbox <mailbox>&<context> from <from_folder> to <to_folder>\n";
12311  return NULL;
12312  case CLI_GENERATE:
12313  return complete_voicemail_move_message(a, 6);
12314  case CLI_HANDLER:
12315  break;
12316  }
12317 
12318  if (a->argc != 7) {
12319  return CLI_SHOWUSAGE;
12320  }
12321 
12322  if (move_message_from_mailbox(a)) {
12323  return CLI_FAILURE;
12324  }
12325 
12326  return CLI_SUCCESS;
12327 }
12328 
12329 static char *handle_voicemail_remove_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12330 {
12331  switch (cmd) {
12332  case CLI_INIT:
12333  e->command = "voicemail remove";
12334  e->usage =
12335  "Usage: voicemail remove <mailbox> <context> <from_folder> <messageid>\n"
12336  " Remove message <messageid> from <from_folder> in mailbox <mailbox>@<context>\n";
12337  return NULL;
12338  case CLI_GENERATE:
12339  return complete_voicemail_move_message(a, 5);
12340  case CLI_HANDLER:
12341  break;
12342  }
12343 
12344  if (a->argc != 6) {
12345  return CLI_SHOWUSAGE;
12346  }
12347 
12348  if (remove_message_from_mailbox(a)) {
12349  return CLI_FAILURE;
12350  }
12351 
12352  return CLI_SUCCESS;
12353 }
12354 
12355 static int vm_execmain(struct ast_channel *chan, const char *data)
12356 {
12357  /* XXX This is, admittedly, some pretty horrendous code. For some
12358  reason it just seemed a lot easier to do with GOTO's. I feel
12359  like I'm back in my GWBASIC days. XXX */
12360  int res = -1;
12361  int cmd = 0;
12362  int valid = 0;
12363  char prefixstr[80] ="";
12364  char ext_context[256]="";
12365  int box;
12366  int useadsi = 0;
12367  int skipuser = 0;
12368  struct vm_state vms = {{0}};
12369  struct ast_vm_user *vmu = NULL, vmus = {{0}};
12370  char *context = NULL;
12371  int silentexit = 0;
12372  struct ast_flags flags = { 0 };
12373  signed char record_gain = 0;
12374  int play_auto = 0;
12375  int play_folder = 0;
12376  int in_urgent = 0;
12377  int nodelete = 0;
12378 #ifdef IMAP_STORAGE
12379  int deleted = 0;
12380 #endif
12381  SCOPE_ENTER(3, "%s:\n", ast_channel_name(chan));
12382 
12383  /* Add the vm_state to the active list and keep it active */
12384  vms.lastmsg = -1;
12385 
12386  ast_test_suite_event_notify("START", "Message: vm_execmain started");
12387  if (ast_channel_state(chan) != AST_STATE_UP) {
12388  ast_debug(1, "Before ast_answer\n");
12389  ast_answer(chan);
12390  }
12391 
12392  if (!ast_strlen_zero(data)) {
12393  char *opts[OPT_ARG_ARRAY_SIZE];
12394  char *parse;
12395  AST_DECLARE_APP_ARGS(args,
12396  AST_APP_ARG(argv0);
12397  AST_APP_ARG(argv1);
12398  );
12399 
12400  parse = ast_strdupa(data);
12401 
12402  AST_STANDARD_APP_ARGS(args, parse);
12403 
12404  if (args.argc == 2) {
12405  if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
12406  SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid option string '%s'\n", args.argv1);
12407  }
12408  if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
12409  int gain;
12410  if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
12411  if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
12412  SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
12413  } else {
12414  record_gain = (signed char) gain;
12415  }
12416  } else {
12417  ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
12418  }
12419  }
12420  if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
12421  play_auto = 1;
12422  if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
12423  /* See if it is a folder name first */
12424  if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
12425  if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
12426  play_folder = -1;
12427  }
12428  } else {
12429  play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
12430  }
12431  } else {
12432  ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
12433  }
12434  if (play_folder > 9 || play_folder < 0) {
12435  ast_log(AST_LOG_WARNING,
12436  "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
12437  opts[OPT_ARG_PLAYFOLDER]);
12438  play_folder = 0;
12439  }
12440  }
12441  if (ast_test_flag(&flags, OPT_READONLY)) {
12442  nodelete = 1;
12443  }
12444  } else {
12445  /* old style options parsing */
12446  while (*(args.argv0)) {
12447  if (*(args.argv0) == 's')
12448  ast_set_flag(&flags, OPT_SILENT);
12449  else if (*(args.argv0) == 'p')
12450  ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
12451  else
12452  break;
12453  (args.argv0)++;
12454  }
12455 
12456  }
12457 
12458  valid = ast_test_flag(&flags, OPT_SILENT);
12459 
12460  if ((context = strchr(args.argv0, '@')))
12461  *context++ = '\0';
12462 
12463  if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
12464  ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
12465  else
12466  ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
12467 
12468  if (!ast_strlen_zero(vms.username)) {
12469  if ((vmu = find_user(&vmus, context ,vms.username))) {
12470  skipuser++;
12471  } else {
12472  ast_log(LOG_WARNING, "Mailbox '%s%s%s' doesn't exist\n", vms.username, context ? "@": "", context ? context : "");
12473  valid = 0;
12474  }
12475  } else {
12476  valid = 0;
12477  }
12478  }
12479 
12480  if (!valid)
12481  res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
12482 
12483  ast_trace(-1, "vm_authenticate user: %s\n", vms.username);
12484 
12485  if (vms.username[0] == '*') {
12486  ast_trace(-1, "user pressed * in context '%s'\n", ast_channel_context(chan));
12487 
12488  /* user entered '*' */
12489  if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
12490  ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
12491  res = 0; /* prevent hangup */
12492  goto out;
12493  }
12494  }
12495 
12496  if (!res) {
12497  valid = 1;
12498  if (!skipuser)
12499  vmu = &vmus;
12500  } else {
12501  res = 0;
12502  }
12503 
12504  /* If ADSI is supported, setup login screen */
12505  adsi_begin(chan, &useadsi);
12506 
12507  if (!valid) {
12508  ast_trace(-1, "Invalid user\n");
12509  goto out;
12510  }
12511  ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
12512 
12513 #ifdef IMAP_STORAGE
12514  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
12515  pthread_setspecific(ts_vmstate.key, &vms);
12516 
12517  vms.interactive = 1;
12518  vms.updated = 1;
12519  if (vmu)
12520  ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
12521  vmstate_insert(&vms);
12522  init_vm_state(&vms);
12523 #endif
12524 
12525  /* Set language from config to override channel language */
12526  if (!ast_strlen_zero(vmu->language)) {
12527  ast_channel_lock(chan);
12528  ast_channel_language_set(chan, vmu->language);
12529  ast_channel_unlock(chan);
12530  }
12531 
12532  /* Retrieve urgent, old and new message counts */
12533  ast_trace(-1, "Before open_mailbox\n");
12534  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
12535  if (res < 0) {
12536  ast_trace(-1, "open mailbox: %d\n", res);
12537  goto out;
12538  }
12539  vms.oldmessages = vms.lastmsg + 1;
12540  ast_trace(-1, "Number of old messages: %d\n", vms.oldmessages);
12541  /* check INBOX */
12542  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12543  if (res < 0) {
12544  ast_trace(-1, "open mailbox: %d\n", res);
12545  goto out;
12546  }
12547  vms.newmessages = vms.lastmsg + 1;
12548  ast_trace(-1, "Number of new messages: %d\n", vms.newmessages);
12549  /* Start in Urgent */
12550  in_urgent = 1;
12551  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11); /*11 is the Urgent folder */
12552  if (res < 0) {
12553  ast_trace(-1, "open mailbox: %d\n", res);
12554  goto out;
12555  }
12556  vms.urgentmessages = vms.lastmsg + 1;
12557  ast_trace(-1, "Number of urgent messages: %d\n", vms.urgentmessages);
12558 
12559  /* Select proper mailbox FIRST!! */
12560  if (play_auto) {
12561  ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
12562  if (vms.urgentmessages) {
12563  in_urgent = 1;
12564  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11);
12565  } else {
12566  in_urgent = 0;
12567  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, play_folder);
12568  }
12569  if (res < 0) {
12570  ast_trace(-1, "open mailbox: %d\n", res);
12571  goto out;
12572  }
12573 
12574  /* If there are no new messages, inform the user and hangup */
12575  if (vms.lastmsg == -1) {
12576  in_urgent = 0;
12577  cmd = vm_browse_messages(chan, &vms, vmu);
12578  res = 0;
12579  goto out;
12580  }
12581  } else {
12582  if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
12583  /* If we only have old messages start here */
12584  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
12585  in_urgent = 0;
12586  play_folder = 1;
12587  if (res < 0)
12588  goto out;
12589  } else if (!vms.urgentmessages && vms.newmessages) {
12590  /* If we have new messages but none are urgent */
12591  in_urgent = 0;
12592  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12593  if (res < 0)
12594  goto out;
12595  }
12596  }
12597 
12598  if (useadsi)
12599  adsi_status(chan, &vms);
12600  res = 0;
12601 
12602  /* Check to see if this is a new user */
12603  if (!strcasecmp(vmu->mailbox, vmu->password) &&
12604  (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
12605  if (ast_play_and_wait(chan, vm_newuser) == -1)
12606  ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
12607  cmd = vm_newuser_setup(chan, vmu, &vms, vmfmts, record_gain);
12608  if ((cmd == 't') || (cmd == '#')) {
12609  /* Timeout */
12610  ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
12611  res = 0;
12612  ast_trace(-1, "Timeout\n");
12613  goto out;
12614  } else if (cmd < 0) {
12615  /* Hangup */
12616  ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
12617  res = -1;
12618  ast_trace(-1, "Hangup\n");
12619  goto out;
12620  }
12621  }
12622 #ifdef IMAP_STORAGE
12623  ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
12624  if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
12625  ast_debug(1, "*** QUOTA EXCEEDED!!\n");
12626  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
12627  }
12628  ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
12629  if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
12630  ast_log(AST_LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
12631  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
12632  }
12633 #endif
12634 
12635  ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
12636  if (play_auto) {
12637  cmd = '1';
12638  } else {
12639  cmd = vm_intro(chan, vmu, &vms);
12640  }
12641  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12642  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12643 
12644  vms.repeats = 0;
12645  vms.starting = 1;
12646  while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
12647  /* Run main menu */
12648  ast_trace(-1, "Main menu: %d %c\n", cmd, (cmd >= 32 && cmd <= 126 ? cmd : ' '));
12649  switch (cmd) {
12650  case '1': /* First message */
12651  vms.curmsg = 0;
12652  /* Fall through */
12653  case '5': /* Play current message */
12654  ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
12655  cmd = vm_browse_messages(chan, &vms, vmu);
12656  break;
12657  case '2': /* Change folders */
12658  ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
12659  if (useadsi)
12660  adsi_folders(chan, 0, "Change to folder...");
12661 
12662  cmd = get_folder2(chan, "vm-changeto", 0);
12663  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12664  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12665  if (cmd == '#') {
12666  cmd = 0;
12667  } else if (cmd > 0) {
12668  cmd = cmd - '0';
12669  res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12670  if (res == ERROR_LOCK_PATH) {
12671  ast_trace(-1, "close mailbox: %d\n", res);
12672  goto out;
12673  }
12674  /* If folder is not urgent, set in_urgent to zero! */
12675  if (cmd != 11) in_urgent = 0;
12676  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, cmd);
12677  if (res < 0) {
12678  ast_trace(-1, "open mailbox: %d\n", res);
12679  goto out;
12680  }
12681  play_folder = cmd;
12682  cmd = 0;
12683  }
12684  if (useadsi)
12685  adsi_status2(chan, &vms);
12686 
12687  if (!cmd) {
12688  cmd = vm_play_folder_name(chan, vms.vmbox);
12689  }
12690 
12691  vms.starting = 1;
12692  vms.curmsg = 0;
12693  break;
12694  case '3': /* Advanced options */
12695  ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
12696  cmd = 0;
12697  vms.repeats = 0;
12698  while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
12699  switch (cmd) {
12700  case '1': /* Reply */
12701  if (vms.lastmsg > -1 && !vms.starting) {
12702  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
12703  if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
12704  res = cmd;
12705  ast_trace(-1, "advanced options: %d\n", cmd);
12706  goto out;
12707  }
12708  } else {
12709  cmd = ast_play_and_wait(chan, "vm-sorry");
12710  }
12711  cmd = 't';
12712  break;
12713  case '2': /* Callback */
12714  if (!vms.starting)
12715  ast_verb(3, "Callback Requested\n");
12716  if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
12717  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
12718  ast_trace(-1, "advanced options: %d\n", cmd);
12719  if (cmd == 9) {
12720  silentexit = 1;
12721  goto out;
12722  } else if (cmd == ERROR_LOCK_PATH) {
12723  res = cmd;
12724  goto out;
12725  }
12726  } else {
12727  cmd = ast_play_and_wait(chan, "vm-sorry");
12728  }
12729  cmd = 't';
12730  break;
12731  case '3': /* Envelope */
12732  if (vms.lastmsg > -1 && !vms.starting) {
12733  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
12734  if (cmd == ERROR_LOCK_PATH) {
12735  res = cmd;
12736  ast_trace(-1, "advanced options: %d\n", cmd);
12737  goto out;
12738  }
12739  } else {
12740  cmd = ast_play_and_wait(chan, "vm-sorry");
12741  }
12742  cmd = 't';
12743  break;
12744  case '4': /* Dialout */
12745  if (!ast_strlen_zero(vmu->dialout)) {
12746  cmd = dialout(chan, vmu, NULL, vmu->dialout);
12747  if (cmd == 9) {
12748  silentexit = 1;
12749  ast_trace(-1, "dialout: %d\n", cmd);
12750  goto out;
12751  }
12752  } else {
12753  cmd = ast_play_and_wait(chan, "vm-sorry");
12754  }
12755  cmd = 't';
12756  break;
12757 
12758  case '5': /* Leave VoiceMail */
12759  if (ast_test_flag(vmu, VM_SVMAIL)) {
12760  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, forward_message, chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
12761  if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
12762  res = cmd;
12763  ast_trace(-1, "forward message: %d\n", cmd);
12764  goto out;
12765  }
12766  } else {
12767  cmd = ast_play_and_wait(chan, "vm-sorry");
12768  }
12769  cmd = 't';
12770  break;
12771 
12772  case '*': /* Return to main menu */
12773  cmd = 't';
12774  break;
12775 
12776  default:
12777  cmd = 0;
12778  if (!vms.starting) {
12779  cmd = ast_play_and_wait(chan, "vm-toreply");
12780  }
12781  if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
12782  cmd = ast_play_and_wait(chan, "vm-tocallback");
12783  }
12784  if (!cmd && !vms.starting) {
12785  cmd = ast_play_and_wait(chan, "vm-tohearenv");
12786  }
12787  if (!ast_strlen_zero(vmu->dialout) && !cmd) {
12788  cmd = ast_play_and_wait(chan, "vm-tomakecall");
12789  }
12790  if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
12791  cmd = ast_play_and_wait(chan, "vm-leavemsg");
12792  }
12793  if (!cmd) {
12794  cmd = ast_play_and_wait(chan, "vm-starmain");
12795  }
12796  if (!cmd) {
12797  cmd = ast_waitfordigit(chan, 6000);
12798  }
12799  if (!cmd) {
12800  vms.repeats++;
12801  }
12802  if (vms.repeats > 3) {
12803  cmd = 't';
12804  }
12805  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12806  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12807  }
12808  }
12809  if (cmd == 't') {
12810  cmd = 0;
12811  vms.repeats = 0;
12812  }
12813  break;
12814  case '4': /* Go to the previous message */
12815  ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
12816  if (vms.curmsg > 0) {
12817  vms.curmsg--;
12818  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12819  } else {
12820  /* Check if we were listening to new
12821  messages. If so, go to Urgent messages
12822  instead of saying "no more messages"
12823  */
12824  if (in_urgent == 0 && vms.urgentmessages > 0) {
12825  /* Check for Urgent messages */
12826  in_urgent = 1;
12827  res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12828  if (res == ERROR_LOCK_PATH) {
12829  ast_trace(-1, "close mailbox: %d\n", res);
12830  goto out;
12831  }
12832  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11); /* Open Urgent folder */
12833  if (res < 0) {
12834  ast_trace(-1, "open mailbox: %d\n", res);
12835  goto out;
12836  }
12837  ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
12838  vms.curmsg = vms.lastmsg;
12839  if (vms.lastmsg < 0) {
12840  cmd = ast_play_and_wait(chan, "vm-nomore");
12841  }
12842  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12843  vms.curmsg = vms.lastmsg;
12844  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12845  } else {
12846  cmd = ast_play_and_wait(chan, "vm-nomore");
12847  }
12848  }
12849  break;
12850  case '6': /* Go to the next message */
12851  ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
12852  if (vms.curmsg < vms.lastmsg) {
12853  vms.curmsg++;
12854  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12855  } else {
12856  if (in_urgent && vms.newmessages > 0) {
12857  /* Check if we were listening to urgent
12858  * messages. If so, go to regular new messages
12859  * instead of saying "no more messages"
12860  */
12861  in_urgent = 0;
12862  res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12863  if (res == ERROR_LOCK_PATH) {
12864  ast_trace(-1, "close mailbox: %d\n", res);
12865  goto out;
12866  }
12867  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12868  if (res < 0) {
12869  ast_trace(-1, "open mailbox: %d\n", res);
12870  goto out;
12871  }
12872  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12873  vms.curmsg = -1;
12874  if (vms.lastmsg < 0) {
12875  cmd = ast_play_and_wait(chan, "vm-nomore");
12876  }
12877  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12878  vms.curmsg = 0;
12879  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12880  } else {
12881  cmd = ast_play_and_wait(chan, "vm-nomore");
12882  }
12883  }
12884  break;
12885  case '7': /* Delete the current message */
12886  if (!nodelete && vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
12887  vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
12888  if (useadsi)
12889  adsi_delete(chan, &vms);
12890  if (vms.deleted[vms.curmsg]) {
12891  if (play_folder == 0) {
12892  if (in_urgent) {
12893  vms.urgentmessages--;
12894  } else {
12895  vms.newmessages--;
12896  }
12897  }
12898  else if (play_folder == 1)
12899  vms.oldmessages--;
12900  cmd = ast_play_and_wait(chan, "vm-deleted");
12901  } else {
12902  if (play_folder == 0) {
12903  if (in_urgent) {
12904  vms.urgentmessages++;
12905  } else {
12906  vms.newmessages++;
12907  }
12908  }
12909  else if (play_folder == 1)
12910  vms.oldmessages++;
12911  cmd = ast_play_and_wait(chan, "vm-undeleted");
12912  }
12913  if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
12914  if (vms.curmsg < vms.lastmsg) {
12915  vms.curmsg++;
12916  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12917  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12918  vms.curmsg = 0;
12919  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12920  } else {
12921  /* Check if we were listening to urgent
12922  messages. If so, go to regular new messages
12923  instead of saying "no more messages"
12924  */
12925  if (in_urgent == 1) {
12926  /* Check for new messages */
12927  in_urgent = 0;
12928  res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12929  if (res == ERROR_LOCK_PATH) {
12930  ast_trace(-1, "close mailbox: %d\n", res);
12931  goto out;
12932  }
12933  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12934  if (res < 0) {
12935  ast_trace(-1, "open mailbox: %d\n", res);
12936  goto out;
12937  }
12938  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12939  vms.curmsg = -1;
12940  if (vms.lastmsg < 0) {
12941  cmd = ast_play_and_wait(chan, "vm-nomore");
12942  }
12943  } else {
12944  cmd = ast_play_and_wait(chan, "vm-nomore");
12945  }
12946  }
12947  }
12948  } else /* Delete not valid if we haven't selected a message */
12949  cmd = 0;
12950 #ifdef IMAP_STORAGE
12951  deleted = 1;
12952 #endif
12953  break;
12954 
12955  case '8': /* Forward the current message */
12956  if (vms.lastmsg > -1) {
12957  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, forward_message, chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
12958  if (cmd == ERROR_LOCK_PATH) {
12959  res = cmd;
12960  ast_trace(-1, "forward message: %d\n", res);
12961  goto out;
12962  }
12963  } else {
12964  /* Check if we were listening to urgent
12965  messages. If so, go to regular new messages
12966  instead of saying "no more messages"
12967  */
12968  if (in_urgent == 1 && vms.newmessages > 0) {
12969  /* Check for new messages */
12970  in_urgent = 0;
12971  res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12972  if (res == ERROR_LOCK_PATH) {
12973  ast_trace(-1, "close mailbox: %d\n", res);
12974  goto out;
12975  }
12976  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12977  if (res < 0) {
12978  ast_trace(-1, "open mailbox: %d\n", res);
12979  goto out;
12980  }
12981  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12982  vms.curmsg = -1;
12983  if (vms.lastmsg < 0) {
12984  cmd = ast_play_and_wait(chan, "vm-nomore");
12985  }
12986  } else {
12987  cmd = ast_play_and_wait(chan, "vm-nomore");
12988  }
12989  }
12990  break;
12991  case '9': /* Save message to folder */
12992  ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
12993  if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
12994  /* No message selected */
12995  cmd = 0;
12996  break;
12997  }
12998  if (useadsi)
12999  adsi_folders(chan, 1, "Save to folder...");
13000  cmd = get_folder2(chan, "vm-savefolder", 1);
13001  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
13002  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
13003  box = 0; /* Shut up compiler */
13004  if (cmd == '#') {
13005  cmd = 0;
13006  break;
13007  } else if (cmd > 0) {
13008  box = cmd = cmd - '0';
13009  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, &vms, vms.curmsg, cmd, NULL, 0);
13010  if (cmd == ERROR_LOCK_PATH) {
13011  res = cmd;
13012  ast_trace(-1, "save to folder: %d\n", res);
13013  goto out;
13014 #ifndef IMAP_STORAGE
13015  } else if (!cmd) {
13016  vms.deleted[vms.curmsg] = 1;
13017 #endif
13018  } else {
13019  vms.deleted[vms.curmsg] = 0;
13020  vms.heard[vms.curmsg] = 0;
13021  }
13022  }
13023  make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
13024  if (useadsi)
13025  adsi_message(chan, &vms);
13026  snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
13027  if (!cmd) {
13028  cmd = ast_play_and_wait(chan, "vm-message");
13029  if (!cmd)
13030  cmd = say_and_wait(chan, vms.curmsg + 1, ast_channel_language(chan));
13031  if (!cmd)
13032  cmd = ast_play_and_wait(chan, "vm-savedto");
13033  if (!cmd)
13034  cmd = vm_play_folder_name(chan, vms.fn);
13035  } else {
13036  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
13037  }
13038  if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
13039  if (vms.curmsg < vms.lastmsg) {
13040  vms.curmsg++;
13041  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
13042  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
13043  vms.curmsg = 0;
13044  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
13045  } else {
13046  /* Check if we were listening to urgent
13047  messages. If so, go to regular new messages
13048  instead of saying "no more messages"
13049  */
13050  if (in_urgent == 1 && vms.newmessages > 0) {
13051  /* Check for new messages */
13052  in_urgent = 0;
13053  res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
13054  if (res == ERROR_LOCK_PATH) {
13055  ast_trace(-1, "close mailbox: %d\n", res);
13056  goto out;
13057  }
13058  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
13059  if (res < 0) {
13060  ast_trace(-1, "open mailbox: %d\n", res);
13061  goto out;
13062  }
13063  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
13064  vms.curmsg = -1;
13065  if (vms.lastmsg < 0) {
13066  cmd = ast_play_and_wait(chan, "vm-nomore");
13067  }
13068  } else {
13069  cmd = ast_play_and_wait(chan, "vm-nomore");
13070  }
13071  }
13072  }
13073  break;
13074  case '*': /* Help */
13075  if (!vms.starting) {
13076  if (!strncasecmp(ast_channel_language(chan), "ja", 2)) {
13077  cmd = vm_play_folder_name(chan, vms.vmbox);
13078  if (!cmd)
13079  cmd = ast_play_and_wait(chan, "jp-wa");
13080  if (!cmd)
13081  cmd = ast_play_and_wait(chan, "digits/1");
13082  if (!cmd)
13083  cmd = ast_play_and_wait(chan, "jp-wo");
13084  if (!cmd)
13085  cmd = ast_play_and_wait(chan, "silence/1");
13086  if (!cmd)
13087  cmd = ast_play_and_wait(chan, "vm-opts");
13088  if (!cmd)
13089  cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent, nodelete);
13090  break;
13091  }
13092  cmd = ast_play_and_wait(chan, "vm-onefor");
13093  if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
13094  cmd = ast_play_and_wait(chan, "vm-for");
13095  }
13096  if (!cmd)
13097  cmd = vm_play_folder_name(chan, vms.vmbox);
13098  if (!cmd)
13099  cmd = ast_play_and_wait(chan, "vm-opts");
13100  if (!cmd)
13101  cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent, nodelete);
13102  } else
13103  cmd = 0;
13104  break;
13105  case '0': /* Mailbox options */
13106  cmd = SCOPE_CALL_WITH_INT_RESULT(-1, vm_options,chan, vmu, &vms, vmfmts, record_gain);
13107  if (useadsi)
13108  adsi_status(chan, &vms);
13109  /* Reopen play_folder */
13110  res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, play_folder);
13111  if (res < 0) {
13112  ast_trace(-1, "open mailbox: %d\n", res);
13113  goto out;
13114  }
13115  vms.starting = 1;
13116  break;
13117  default: /* Nothing */
13118  ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
13119  cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent, nodelete);
13120  break;
13121  }
13122  }
13123  if ((cmd == 't') || (cmd == '#')) {
13124  /* Timeout */
13125  res = 0;
13126  } else {
13127  /* Hangup */
13128  res = -1;
13129  }
13130 
13131 out:
13132  if (res > -1) {
13133  ast_stopstream(chan);
13134  adsi_goodbye(chan);
13135  if (valid && res != OPERATOR_EXIT) {
13136  if (silentexit)
13137  res = ast_play_and_wait(chan, "vm-dialout");
13138  else
13139  res = ast_play_and_wait(chan, "vm-goodbye");
13140  }
13141  if ((valid && res > 0) || res == OPERATOR_EXIT) {
13142  res = 0;
13143  }
13144  if (useadsi)
13145  ast_adsi_unload_session(chan);
13146  }
13147  if (vmu) {
13148  SCOPE_CALL(-1, close_mailbox, &vms, vmu);
13149  }
13150  if (valid) {
13151  int new = 0, old = 0, urgent = 0;
13152  snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
13153  /* Urgent flag not passwd to externnotify here */
13154  run_externnotify(vmu->context, vmu->mailbox, NULL);
13155  ast_app_inboxcount2(ext_context, &urgent, &new, &old);
13156  queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgent, new, old);
13157  }
13158 #ifdef IMAP_STORAGE
13159  /* expunge message - use UID Expunge if supported on IMAP server*/
13160  ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
13161  if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
13162  ast_mutex_lock(&vms.lock);
13163 #ifdef HAVE_IMAP_TK2006
13164  if (LEVELUIDPLUS (vms.mailstream)) {
13165  mail_expunge_full(vms.mailstream, NIL, EX_UID);
13166  } else
13167 #endif
13168  mail_expunge(vms.mailstream);
13169  ast_mutex_unlock(&vms.lock);
13170  }
13171  /* before we delete the state, we should copy pertinent info
13172  * back to the persistent model */
13173  if (vmu) {
13174  vmstate_delete(&vms);
13175  }
13176 #endif
13177  if (vmu)
13178  free_user(vmu);
13179 
13180 #ifdef IMAP_STORAGE
13181  pthread_setspecific(ts_vmstate.key, NULL);
13182 #endif
13183  SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d\n", res);
13184 }
13185 
13186 static int vm_exec(struct ast_channel *chan, const char *data)
13187 {
13188  int res = 0;
13189  char *tmp;
13190  struct leave_vm_options leave_options;
13191  struct ast_flags flags = { 0 };
13192  char *opts[OPT_ARG_ARRAY_SIZE];
13193  AST_DECLARE_APP_ARGS(args,
13194  AST_APP_ARG(argv0);
13195  AST_APP_ARG(argv1);
13196  );
13197  SCOPE_ENTER(3, "%s\n", ast_channel_name(chan));
13198 
13199  memset(&leave_options, 0, sizeof(leave_options));
13200 
13201  if (!ast_strlen_zero(data)) {
13202  tmp = ast_strdupa(data);
13203  AST_STANDARD_APP_ARGS(args, tmp);
13204  if (args.argc == 2) {
13205  if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
13206  SCOPE_EXIT_RTN_VALUE(-1, "parse options failed for '%s'\n", args.argv1);
13207  }
13208  ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_SILENT_IF_GREET | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_MESSAGE_Urgent | OPT_MESSAGE_PRIORITY | OPT_DTMFEXIT);
13209  if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
13210  int gain;
13211 
13212  if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
13213  SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
13214  } else {
13215  leave_options.record_gain = (signed char) gain;
13216  }
13217  }
13218  if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
13219  if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
13220  leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
13221  }
13222  }
13223  if (ast_test_flag(&flags, OPT_BEEP)) { /* Use custom beep (or none at all) */
13224  leave_options.beeptone = opts[OPT_ARG_BEEP_TONE];
13225  } else { /* Use default beep */
13226  leave_options.beeptone = "beep";
13227  }
13228  } else {
13229  char temp[256];
13230  res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
13231  if (res < 0) {
13232  SCOPE_EXIT_RTN_VALUE(res, "getdata failed. RC: %d", res);
13233  }
13234  if (ast_strlen_zero(temp)) {
13235  SCOPE_EXIT_RTN_VALUE(0);
13236  }
13237  args.argv0 = ast_strdupa(temp);
13238  }
13239 
13240  if (ast_channel_state(chan) != AST_STATE_UP) {
13241  if (ast_test_flag(&flags, OPT_EARLYM_GREETING)) {
13243  } else {
13244  ast_answer(chan);
13245  }
13246  }
13247 
13248  res = SCOPE_CALL_WITH_INT_RESULT(-1, leave_voicemail, chan, args.argv0, &leave_options);
13249  if (res == 't') {
13250  ast_play_and_wait(chan, "vm-goodbye");
13251  res = 0;
13252  }
13253 
13254  if (res == OPERATOR_EXIT) {
13255  res = 0;
13256  }
13257 
13258  if (res == ERROR_LOCK_PATH) {
13259  ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
13260  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
13261  res = 0;
13262  }
13263 
13264  SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d", res);
13265 }
13266 
13267 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)
13268 {
13269  struct ast_variable *var;
13270  struct ast_category *cat;
13271  generate_msg_id(id);
13272 
13273  var = ast_variable_new("msg_id", id, "");
13274  if (!var) {
13275  return -1;
13276  }
13277 
13278  cat = ast_category_get(msg_cfg, "message", NULL);
13279  if (!cat) {
13280  ast_log(LOG_ERROR, "Voicemail data file %s/%d.txt has no [message] category?\n", dir, msg);
13281  ast_variables_destroy(var);
13282  return -1;
13283  }
13284 
13285  ast_variable_append(cat, var);
13286 
13287  if (ast_config_text_file_save(filename, msg_cfg, "app_voicemail")) {
13288  ast_log(LOG_WARNING, "Unable to update %s to have a message ID\n", filename);
13289  return -1;
13290  }
13291 
13292  UPDATE_MSG_ID(dir, msg, id, vmu, msg_cfg, folder);
13293  return 0;
13294 }
13295 
13296 static struct ast_vm_user *find_or_create(const char *context, const char *box)
13297 {
13298  struct ast_vm_user *vmu;
13299 
13300  if (!ast_strlen_zero(box) && box[0] == '*') {
13301  ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character. The '*' character,"
13302  "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
13303  "\n\tpredefined extension 'a'. A mailbox or password beginning with '*' is not valid"
13304  "\n\tand will be ignored.\n", box, context);
13305  return NULL;
13306  }
13307 
13308  AST_LIST_TRAVERSE(&users, vmu, list) {
13309  if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
13310  if (strcasecmp(vmu->context, context)) {
13311  ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
13312  \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
13313  \n\tconfiguration creates an ambiguity that you likely do not want. Please\
13314  \n\tamend your voicemail.conf file to avoid this situation.\n", box);
13315  }
13316  ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
13317  return NULL;
13318  }
13319  if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
13320  ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
13321  return NULL;
13322  }
13323  }
13324 
13325  if (!(vmu = ast_calloc(1, sizeof(*vmu))))
13326  return NULL;
13327 
13328  ast_copy_string(vmu->context, context, sizeof(vmu->context));
13329  ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
13330 
13331  AST_LIST_INSERT_TAIL(&users, vmu, list);
13332 
13333  return vmu;
13334 }
13335 
13336 static int append_mailbox(const char *context, const char *box, const char *data)
13337 {
13338  /* Assumes lock is already held */
13339  char *tmp;
13340  char *stringp;
13341  char *s;
13342  struct ast_vm_user *vmu;
13343  char mailbox_full[MAX_VM_MAILBOX_LEN];
13344  int new = 0, old = 0, urgent = 0;
13345  char secretfn[PATH_MAX] = "";
13346 
13347  tmp = ast_strdupa(data);
13348 
13349  if (!(vmu = find_or_create(context, box)))
13350  return -1;
13351 
13352  populate_defaults(vmu);
13353 
13354  stringp = tmp;
13355  if ((s = strsep(&stringp, ","))) {
13356  if (!ast_strlen_zero(s) && s[0] == '*') {
13357  ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
13358  "\n\tmust be reset in voicemail.conf.\n", box);
13359  }
13360  /* assign password regardless of validity to prevent NULL password from being assigned */
13361  ast_copy_string(vmu->password, s, sizeof(vmu->password));
13362  }
13363  if (stringp && (s = strsep(&stringp, ","))) {
13364  ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
13365  }
13366  if (stringp && (s = strsep(&stringp, ","))) {
13367  vmu->email = ast_strdup(s);
13368  }
13369  if (stringp && (s = strsep(&stringp, ","))) {
13370  ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
13371  }
13372  if (stringp) {
13373  apply_options(vmu, stringp);
13374  }
13375 
13376  switch (vmu->passwordlocation) {
13377  case OPT_PWLOC_SPOOLDIR:
13378  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
13379  read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
13380  }
13381 
13382  snprintf(mailbox_full, MAX_VM_MAILBOX_LEN, "%s%s%s",
13383  box,
13384  ast_strlen_zero(context) ? "" : "@",
13385  context);
13386 
13387  inboxcount2(mailbox_full, &urgent, &new, &old);
13388 #ifdef IMAP_STORAGE
13389  imap_logout(mailbox_full);
13390 #endif
13391  queue_mwi_event(NULL, mailbox_full, urgent, new, old);
13392 
13393  return 0;
13394 }
13395 
13396 #ifdef TEST_FRAMEWORK
13397 AST_TEST_DEFINE(test_voicemail_vmuser)
13398 {
13399  int res = 0;
13400  struct ast_vm_user *vmu;
13401  /* language parameter seems to only be used for display in manager action */
13402  static const char options_string[] = "attach=yes|attachfmt=wav49|"
13403  "serveremail=someguy@digium.com|fromstring=Voicemail System|tz=central|delete=yes|saycid=yes|"
13404  "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|leaveurgent=yes|"
13405  "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
13406  "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
13407  "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
13408  "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
13409  "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
13410  "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
13411 #ifdef IMAP_STORAGE
13412  static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
13413  "imapfolder=INBOX|imapvmshareid=6000|imapserver=imapserver|imapport=1234|imapflags=flagged";
13414 #endif
13415 
13416  switch (cmd) {
13417  case TEST_INIT:
13418  info->name = "vmuser";
13419  info->category = "/apps/app_voicemail/";
13420  info->summary = "Vmuser unit test";
13421  info->description =
13422  "This tests passing all supported parameters to apply_options, the voicemail user config parser";
13423  return AST_TEST_NOT_RUN;
13424  case TEST_EXECUTE:
13425  break;
13426  }
13427 
13428  if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
13429  return AST_TEST_NOT_RUN;
13430  }
13431  populate_defaults(vmu);
13432  ast_set_flag(vmu, VM_ALLOCED);
13433 
13434  apply_options(vmu, options_string);
13435 
13436  if (!ast_test_flag(vmu, VM_ATTACH)) {
13437  ast_test_status_update(test, "Parse failure for attach option\n");
13438  res = 1;
13439  }
13440  if (strcasecmp(vmu->attachfmt, "wav49")) {
13441  ast_test_status_update(test, "Parse failure for attachfmt option\n");
13442  res = 1;
13443  }
13444  if (strcasecmp(vmu->fromstring, "Voicemail System")) {
13445  ast_test_status_update(test, "Parse failure for fromstring option\n");
13446  res = 1;
13447  }
13448  if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
13449  ast_test_status_update(test, "Parse failure for serveremail option\n");
13450  res = 1;
13451  }
13452  if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
13453  ast_test_status_update(test, "Parse failure for emailsubject option\n");
13454  res = 1;
13455  }
13456  if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
13457  ast_test_status_update(test, "Parse failure for emailbody option\n");
13458  res = 1;
13459  }
13460  if (strcasecmp(vmu->zonetag, "central")) {
13461  ast_test_status_update(test, "Parse failure for tz option\n");
13462  res = 1;
13463  }
13464  if (!ast_test_flag(vmu, VM_DELETE)) {
13465  ast_test_status_update(test, "Parse failure for delete option\n");
13466  res = 1;
13467  }
13468  if (!ast_test_flag(vmu, VM_SAYCID)) {
13469  ast_test_status_update(test, "Parse failure for saycid option\n");
13470  res = 1;
13471  }
13472  if (!ast_test_flag(vmu, VM_SVMAIL)) {
13473  ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
13474  res = 1;
13475  }
13476  if (!ast_test_flag(vmu, VM_REVIEW)) {
13477  ast_test_status_update(test, "Parse failure for review option\n");
13478  res = 1;
13479  }
13480  if (!ast_test_flag(vmu, VM_MARK_URGENT)) {
13481  ast_test_status_update(test, "Parse failure for leaveurgent option\n");
13482  res = 1;
13483  }
13484  if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
13485  ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
13486  res = 1;
13487  }
13488  if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
13489  ast_test_status_update(test, "Parse failure for messagewrap option\n");
13490  res = 1;
13491  }
13492  if (!ast_test_flag(vmu, VM_OPERATOR)) {
13493  ast_test_status_update(test, "Parse failure for operator option\n");
13494  res = 1;
13495  }
13496  if (!ast_test_flag(vmu, VM_ENVELOPE)) {
13497  ast_test_status_update(test, "Parse failure for envelope option\n");
13498  res = 1;
13499  }
13500  if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
13501  ast_test_status_update(test, "Parse failure for moveheard option\n");
13502  res = 1;
13503  }
13504  if (!ast_test_flag(vmu, VM_SAYDURATION)) {
13505  ast_test_status_update(test, "Parse failure for sayduration option\n");
13506  res = 1;
13507  }
13508  if (vmu->saydurationm != 5) {
13509  ast_test_status_update(test, "Parse failure for saydurationm option\n");
13510  res = 1;
13511  }
13512  if (!ast_test_flag(vmu, VM_FORCENAME)) {
13513  ast_test_status_update(test, "Parse failure for forcename option\n");
13514  res = 1;
13515  }
13516  if (!ast_test_flag(vmu, VM_FORCEGREET)) {
13517  ast_test_status_update(test, "Parse failure for forcegreetings option\n");
13518  res = 1;
13519  }
13520  if (strcasecmp(vmu->callback, "somecontext")) {
13521  ast_test_status_update(test, "Parse failure for callbacks option\n");
13522  res = 1;
13523  }
13524  if (strcasecmp(vmu->dialout, "somecontext2")) {
13525  ast_test_status_update(test, "Parse failure for dialout option\n");
13526  res = 1;
13527  }
13528  if (strcasecmp(vmu->exit, "somecontext3")) {
13529  ast_test_status_update(test, "Parse failure for exitcontext option\n");
13530  res = 1;
13531  }
13532  if (vmu->minsecs != 10) {
13533  ast_test_status_update(test, "Parse failure for minsecs option\n");
13534  res = 1;
13535  }
13536  if (vmu->maxsecs != 100) {
13537  ast_test_status_update(test, "Parse failure for maxsecs option\n");
13538  res = 1;
13539  }
13540  if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
13541  ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
13542  res = 1;
13543  }
13544  if (vmu->maxdeletedmsg != 50) {
13545  ast_test_status_update(test, "Parse failure for backupdeleted option\n");
13546  res = 1;
13547  }
13548  if (vmu->volgain != 1.3) {
13549  ast_test_status_update(test, "Parse failure for volgain option\n");
13550  res = 1;
13551  }
13552  if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
13553  ast_test_status_update(test, "Parse failure for passwordlocation option\n");
13554  res = 1;
13555  }
13556 #ifdef IMAP_STORAGE
13557  apply_options(vmu, option_string2);
13558 
13559  if (strcasecmp(vmu->imapuser, "imapuser")) {
13560  ast_test_status_update(test, "Parse failure for imapuser option\n");
13561  res = 1;
13562  }
13563  if (strcasecmp(vmu->imappassword, "imappasswd")) {
13564  ast_test_status_update(test, "Parse failure for imappasswd option\n");
13565  res = 1;
13566  }
13567  if (strcasecmp(vmu->imapfolder, "INBOX")) {
13568  ast_test_status_update(test, "Parse failure for imapfolder option\n");
13569  res = 1;
13570  }
13571  if (strcasecmp(vmu->imapvmshareid, "6000")) {
13572  ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
13573  res = 1;
13574  }
13575  if (strcasecmp(vmu->imapserver, "imapserver")) {
13576  ast_test_status_update(test, "Parse failure for imapserver option\n");
13577  res = 1;
13578  }
13579  if (strcasecmp(vmu->imapport, "1234")) {
13580  ast_test_status_update(test, "Parse failure for imapport option\n");
13581  res = 1;
13582  }
13583  if (strcasecmp(vmu->imapflags, "flagged")) {
13584  ast_test_status_update(test, "Parse failure for imapflags option\n");
13585  res = 1;
13586  }
13587 #endif
13588 
13589  free_user(vmu);
13590  force_reload_config(); /* Restore original config */
13591  return res ? AST_TEST_FAIL : AST_TEST_PASS;
13592 }
13593 #endif
13594 
13595 static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
13596 {
13597  struct ast_vm_user svm;
13598  struct ast_vm_user *vmu = NULL;
13599  char *parse;
13600  char *mailbox;
13601  char *context;
13602  int res = 0;
13603 
13605  AST_APP_ARG(mailbox_context);
13606  AST_APP_ARG(attribute);
13607  AST_APP_ARG(folder);
13608  );
13609 
13610  buf[0] = '\0';
13611 
13612  if (ast_strlen_zero(args)) {
13613  ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
13614  return -1;
13615  }
13616 
13617  parse = ast_strdupa(args);
13618  AST_STANDARD_APP_ARGS(arg, parse);
13619 
13620  if (ast_strlen_zero(arg.mailbox_context)
13621  || ast_strlen_zero(arg.attribute)
13622  || separate_mailbox(ast_strdupa(arg.mailbox_context), &mailbox, &context)) {
13623  ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
13624  return -1;
13625  }
13626 
13627  memset(&svm, 0, sizeof(svm));
13628  vmu = find_user(&svm, context, mailbox);
13629 
13630  if (!strncasecmp(arg.attribute, "exists", 5)) {
13631  ast_copy_string(buf, vmu ? "1" : "0", len);
13632  free_user(vmu);
13633  return 0;
13634  }
13635 
13636  if (vmu) {
13637  if (!strncasecmp(arg.attribute, "password", 8)) {
13638  ast_copy_string(buf, vmu->password, len);
13639  } else if (!strncasecmp(arg.attribute, "fullname", 8)) {
13640  ast_copy_string(buf, vmu->fullname, len);
13641  } else if (!strncasecmp(arg.attribute, "email", 5)) {
13642  ast_copy_string(buf, vmu->email, len);
13643  } else if (!strncasecmp(arg.attribute, "pager", 5)) {
13644  ast_copy_string(buf, vmu->pager, len);
13645  } else if (!strncasecmp(arg.attribute, "language", 8)) {
13646  ast_copy_string(buf, S_OR(vmu->language, ast_channel_language(chan)), len);
13647  } else if (!strncasecmp(arg.attribute, "locale", 6)) {
13648  ast_copy_string(buf, vmu->locale, len);
13649  } else if (!strncasecmp(arg.attribute, "tz", 2)) {
13650  ast_copy_string(buf, vmu->zonetag, len);
13651  } else if (!strncasecmp(arg.attribute, "count", 5)) {
13652  char *mailbox_id;
13653 
13654  mailbox_id = ast_alloca(strlen(mailbox) + strlen(context) + 2);
13655  sprintf(mailbox_id, "%s@%s", mailbox, context);/* Safe */
13656 
13657  /* If mbxfolder is empty messagecount will default to INBOX */
13658  res = messagecount(mailbox_id, arg.folder);
13659  if (res < 0) {
13660  ast_log(LOG_ERROR, "Unable to retrieve message count for mailbox %s\n", arg.mailbox_context);
13661  free_user(vmu);
13662  return -1;
13663  }
13664  snprintf(buf, len, "%d", res);
13665  } else {
13666  ast_log(LOG_ERROR, "Unknown attribute '%s' for VM_INFO\n", arg.attribute);
13667  free_user(vmu);
13668  return -1;
13669  }
13670  free_user(vmu);
13671  }
13672 
13673  return 0;
13674 }
13675 
13676 static struct ast_custom_function vm_info_acf = {
13677  .name = "VM_INFO",
13678  .read = acf_vm_info,
13679 };
13680 
13681 static int vmauthenticate(struct ast_channel *chan, const char *data)
13682 {
13683  char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
13684  struct ast_vm_user vmus = {{0}};
13685  char *options = NULL;
13686  int silent = 0, skipuser = 0;
13687  int res = -1;
13688 
13689  if (data) {
13690  s = ast_strdupa(data);
13691  user = strsep(&s, ",");
13692  options = strsep(&s, ",");
13693  if (user) {
13694  s = user;
13695  user = strsep(&s, "@");
13696  context = strsep(&s, "");
13697  if (!ast_strlen_zero(user))
13698  skipuser++;
13699  ast_copy_string(mailbox, user, sizeof(mailbox));
13700  }
13701  }
13702 
13703  if (options) {
13704  silent = (strchr(options, 's')) != NULL;
13705  }
13706 
13707  if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
13708  pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
13709  pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
13710  ast_play_and_wait(chan, "auth-thankyou");
13711  res = 0;
13712  } else if (mailbox[0] == '*') {
13713  /* user entered '*' */
13714  if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
13715  res = 0; /* prevent hangup */
13716  }
13717  }
13718 
13719  return res;
13720 }
13721 
13722 static char *show_users_realtime(int fd, const char *context)
13723 {
13724  struct ast_config *cfg;
13725  const char *cat = NULL;
13726 
13727  if (!(cfg = ast_load_realtime_multientry("voicemail",
13728  "context", context, SENTINEL))) {
13729  return CLI_FAILURE;
13730  }
13731 
13732  ast_cli(fd,
13733  "\n"
13734  "=============================================================\n"
13735  "=== Configured Voicemail Users ==============================\n"
13736  "=============================================================\n"
13737  "===\n");
13738 
13739  while ((cat = ast_category_browse(cfg, cat))) {
13740  struct ast_variable *var = NULL;
13741  ast_cli(fd,
13742  "=== Mailbox ...\n"
13743  "===\n");
13744  for (var = ast_variable_browse(cfg, cat); var; var = var->next)
13745  ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
13746  ast_cli(fd,
13747  "===\n"
13748  "=== ---------------------------------------------------------\n"
13749  "===\n");
13750  }
13751 
13752  ast_cli(fd,
13753  "=============================================================\n"
13754  "\n");
13755 
13756  ast_config_destroy(cfg);
13757 
13758  return CLI_SUCCESS;
13759 }
13760 
13761 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
13762 {
13763  int which = 0;
13764  int wordlen;
13765  struct ast_vm_user *vmu;
13766  const char *context = "";
13767  char *ret;
13768 
13769  /* 0 - voicemail; 1 - show; 2 - users; 3 - for; 4 - <context> */
13770  if (pos > 4)
13771  return NULL;
13772  wordlen = strlen(word);
13773  AST_LIST_LOCK(&users);
13774  AST_LIST_TRAVERSE(&users, vmu, list) {
13775  if (!strncasecmp(word, vmu->context, wordlen)) {
13776  if (context && strcmp(context, vmu->context) && ++which > state) {
13777  ret = ast_strdup(vmu->context);
13779  return ret;
13780  }
13781  /* ignore repeated contexts ? */
13782  context = vmu->context;
13783  }
13784  }
13786  return NULL;
13787 }
13788 
13789 /*! \brief Show a list of voicemail users in the CLI */
13790 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13791 {
13792  struct ast_vm_user *vmu;
13793 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
13794  const char *context = NULL;
13795  int users_counter = 0;
13796 
13797  switch (cmd) {
13798  case CLI_INIT:
13799  e->command = "voicemail show users [for]";
13800  e->usage =
13801  "Usage: voicemail show users [for <context>]\n"
13802  " Lists all mailboxes currently set up\n";
13803  return NULL;
13804  case CLI_GENERATE:
13805  return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
13806  }
13807 
13808  if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
13809  return CLI_SHOWUSAGE;
13810  if (a->argc == 5) {
13811  if (strcmp(a->argv[3],"for"))
13812  return CLI_SHOWUSAGE;
13813  context = a->argv[4];
13814  }
13815 
13816  if (ast_check_realtime("voicemail")) {
13817  if (!context) {
13818  ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
13819  return CLI_SHOWUSAGE;
13820  }
13821  return show_users_realtime(a->fd, context);
13822  }
13823 
13824  AST_LIST_LOCK(&users);
13825  if (AST_LIST_EMPTY(&users)) {
13826  ast_cli(a->fd, "There are no voicemail users currently defined\n");
13828  return CLI_FAILURE;
13829  }
13830  if (!context) {
13831  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
13832  } else {
13833  int count = 0;
13834  AST_LIST_TRAVERSE(&users, vmu, list) {
13835  if (!strcmp(context, vmu->context)) {
13836  count++;
13837  break;
13838  }
13839  }
13840  if (count) {
13841  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
13842  } else {
13843  ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
13845  return CLI_FAILURE;
13846  }
13847  }
13848  AST_LIST_TRAVERSE(&users, vmu, list) {
13849  int newmsgs = 0, oldmsgs = 0;
13850  char count[12], tmp[256] = "";
13851 
13852  if (!context || !strcmp(context, vmu->context)) {
13853  snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
13854  inboxcount(tmp, &newmsgs, &oldmsgs);
13855  snprintf(count, sizeof(count), "%d", newmsgs);
13856  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
13857  users_counter++;
13858  }
13859  }
13861  ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
13862  return CLI_SUCCESS;
13863 }
13864 
13865 /*! \brief Show a list of voicemail zones in the CLI */
13866 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13867 {
13868  struct vm_zone *zone;
13869 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
13870  char *res = CLI_SUCCESS;
13871 
13872  switch (cmd) {
13873  case CLI_INIT:
13874  e->command = "voicemail show zones";
13875  e->usage =
13876  "Usage: voicemail show zones\n"
13877  " Lists zone message formats\n";
13878  return NULL;
13879  case CLI_GENERATE:
13880  return NULL;
13881  }
13882 
13883  if (a->argc != 3)
13884  return CLI_SHOWUSAGE;
13885 
13886  AST_LIST_LOCK(&zones);
13887  if (!AST_LIST_EMPTY(&zones)) {
13888  ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
13889  AST_LIST_TRAVERSE(&zones, zone, list) {
13890  ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
13891  }
13892  } else {
13893  ast_cli(a->fd, "There are no voicemail zones currently defined\n");
13894  res = CLI_FAILURE;
13895  }
13897 
13898  return res;
13899 }
13900 
13901 /*! \brief Show a list of voicemail zones in the CLI */
13902 static char *handle_voicemail_show_aliases(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13903 {
13904  struct ao2_iterator aliases;
13905  struct alias_mailbox_mapping *mapping;
13906 #define ALIASES_OUTPUT_FORMAT "%-32s %-32s\n"
13907  char *res = CLI_SUCCESS;
13908 
13909  switch (cmd) {
13910  case CLI_INIT:
13911  e->command = "voicemail show aliases";
13912  e->usage =
13913  "Usage: voicemail show aliases\n"
13914  " Lists mailbox aliases\n";
13915  return NULL;
13916  case CLI_GENERATE:
13917  return NULL;
13918  }
13919 
13920  if (a->argc != 3)
13921  return CLI_SHOWUSAGE;
13922 
13923  if (ast_strlen_zero(aliasescontext)) {
13924  ast_cli(a->fd, "Aliases are not enabled\n");
13925  return res;
13926  }
13927 
13928  ast_cli(a->fd, "Aliases context: %s\n", aliasescontext);
13929  ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, "Alias", "Mailbox");
13930 
13931  aliases = ao2_iterator_init(alias_mailbox_mappings, 0);
13932  while ((mapping = ao2_iterator_next(&aliases))) {
13933  ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, mapping->alias, mapping->mailbox);
13934  ao2_ref(mapping, -1);
13935  }
13936  ao2_iterator_destroy(&aliases);
13937 
13938  return res;
13939 }
13940 
13941 /*! \brief Reload voicemail configuration from the CLI */
13942 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13943 {
13944  switch (cmd) {
13945  case CLI_INIT:
13946  e->command = "voicemail reload";
13947  e->usage =
13948  "Usage: voicemail reload\n"
13949  " Reload voicemail configuration\n";
13950  return NULL;
13951  case CLI_GENERATE:
13952  return NULL;
13953  }
13954 
13955  if (a->argc != 2)
13956  return CLI_SHOWUSAGE;
13957 
13958  ast_cli(a->fd, "Reloading voicemail configuration...\n");
13959  load_config(1);
13960 
13961  return CLI_SUCCESS;
13962 }
13963 
13964 static struct ast_cli_entry cli_voicemail[] = {
13965  AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
13966  AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
13967  AST_CLI_DEFINE(handle_voicemail_show_aliases, "List mailbox aliases"),
13968  AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
13969  AST_CLI_DEFINE(handle_voicemail_show_mailbox, "Display a mailbox's content details"),
13970  AST_CLI_DEFINE(handle_voicemail_forward_message, "Forward message to another folder"),
13971  AST_CLI_DEFINE(handle_voicemail_move_message, "Move message to another folder"),
13972  AST_CLI_DEFINE(handle_voicemail_remove_message, "Remove message"),
13973 };
13974 
13975 static int poll_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
13976 {
13977  int new = 0, old = 0, urgent = 0;
13978 
13979  if (!mwi_state) {
13980  /* This should only occur due to allocation failure of a default mwi state object */
13981  return 0;
13982  }
13983 
13984  inboxcount2(mwi_state->uniqueid, &urgent, &new, &old);
13985 
13986 #ifdef IMAP_STORAGE
13987  if (imap_poll_logout) {
13988  imap_logout(mwi_state->uniqueid);
13989  }
13990 #endif
13991 
13992  if (urgent != mwi_state->urgent_msgs || new != mwi_state->new_msgs || old != mwi_state->old_msgs) {
13993  queue_mwi_event(NULL, mwi_state->uniqueid, urgent, new, old);
13994  run_externnotify(NULL, mwi_state->uniqueid, NULL);
13995  }
13996 
13997  return 0;
13998 }
13999 
14000 static void *mb_poll_thread(void *data)
14001 {
14002  while (poll_thread_run) {
14003  struct timespec ts = { 0, };
14004  struct timeval wait;
14005 
14006  ast_mwi_state_callback_subscribed(poll_subscribed_mailbox, NULL);
14007 
14008  if (!poll_thread_run) {
14009  break;
14010  }
14011 
14012  wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
14013  ts.tv_sec = wait.tv_sec;
14014  ts.tv_nsec = wait.tv_usec * 1000;
14015 
14016  ast_mutex_lock(&poll_lock);
14017  ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
14018  ast_mutex_unlock(&poll_lock);
14019  }
14020 
14021  return NULL;
14022 }
14023 
14024 #ifdef IMAP_STORAGE
14025 static void imap_logout(const char *mailbox_id)
14026 {
14027  char *context;
14028  char *mailbox;
14029  struct ast_vm_user vmus;
14030  RAII_VAR(struct ast_vm_user *, vmu, NULL, free_user);
14031  struct vm_state *vms = NULL;
14032 
14033  if (ast_strlen_zero(mailbox_id)
14034  || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
14035  return;
14036  }
14037 
14038  memset(&vmus, 0, sizeof(vmus));
14039 
14040  if (!(vmu = find_user(&vmus, context, mailbox)) || vmu->imapuser[0] == '\0') {
14041  return;
14042  }
14043 
14044  vms = get_vm_state_by_imapuser(vmu->imapuser, 0);
14045  if (!vms) {
14046  vms = get_vm_state_by_mailbox(mailbox, context, 0);
14047  }
14048  if (!vms) {
14049  return;
14050  }
14051 
14052  ast_mutex_lock(&vms->lock);
14053  vms->mailstream = mail_close(vms->mailstream);
14054  ast_mutex_unlock(&vms->lock);
14055 
14056  vmstate_delete(vms);
14057 }
14058 
14059 static int imap_close_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
14060 {
14061  if (mwi_state && !ast_strlen_zero(mwi_state->uniqueid)) {
14062  imap_logout(mwi_state->uniqueid);
14063  }
14064 
14065  return 0;
14066 }
14067 
14068 #endif
14069 
14070 static int mwi_handle_unsubscribe2(void *data)
14071 {
14072  struct ast_mwi_state *mwi_state = data;
14073 
14074  /*
14075  * Go ahead and clear the implicit MWI publisher here to avoid a leak. If a backing
14076  * configuration is available it'll re-initialize (reset the cached state) on its
14077  * next publish.
14078  */
14079  ast_delete_mwi_state_full(mwi_state->uniqueid, NULL, NULL);
14080 
14081 #ifdef IMAP_STORAGE
14082  imap_close_subscribed_mailbox(mwi_state, NULL);
14083 #endif
14084 
14085  ao2_ref(mwi_state, -1);
14086  return 0;
14087 }
14088 
14089 static void mwi_handle_unsubscribe(const char *id, struct ast_mwi_subscriber *sub)
14090 {
14091  void *data = ast_mwi_subscriber_data(sub);
14092 
14093  /* Don't bump data's reference. We'll just use the one returned above */
14094  if (ast_taskprocessor_push(mwi_subscription_tps, mwi_handle_unsubscribe2, data) < 0) {
14095  /* A reference was returned for data when retrieving, so remove it on error */
14096  ao2_ref(data, -1);
14097  }
14098 }
14099 
14100 static int mwi_handle_subscribe2(void *data)
14101 {
14102  poll_subscribed_mailbox(data, NULL);
14103  ao2_ref(data, -1);
14104  return 0;
14105 }
14106 
14107 static void mwi_handle_subscribe(const char *id, struct ast_mwi_subscriber *sub)
14108 {
14109  void *data = ast_mwi_subscriber_data(sub);
14110 
14111  /* Don't bump data's reference. We'll just use the one returned above */
14112  if (ast_taskprocessor_push(mwi_subscription_tps, mwi_handle_subscribe2, data) < 0) {
14113  /* A reference was returned for data when retrieving, so remove it on error */
14114  ao2_ref(data, -1);
14115  }
14116 }
14117 
14118 struct ast_mwi_observer mwi_observer = {
14119  .on_subscribe = mwi_handle_subscribe,
14120  .on_unsubscribe = mwi_handle_unsubscribe,
14121 };
14122 
14123 static void start_poll_thread(void)
14124 {
14125  int errcode;
14126  ast_mwi_add_observer(&mwi_observer);
14127 
14128  poll_thread_run = 1;
14129 
14130  if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
14131  ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
14132  }
14133 }
14134 
14135 static void stop_poll_thread(void)
14136 {
14137  poll_thread_run = 0;
14138 
14139  ast_mutex_lock(&poll_lock);
14140  ast_cond_signal(&poll_cond);
14141  ast_mutex_unlock(&poll_lock);
14142 
14143  pthread_join(poll_thread, NULL);
14144  poll_thread = AST_PTHREADT_NULL;
14145 
14146  ast_mwi_remove_observer(&mwi_observer);
14147 }
14148 
14149 /*!
14150  * \brief Append vmu info string into given astman with event_name.
14151  * \return 0 failed. 1 otherwise.
14152 */
14154  struct mansession *s,
14155  struct ast_vm_user *vmu,
14156  const char* event_name,
14157  const char* actionid
14158  )
14159 {
14160  int new;
14161  int old;
14162  char *mailbox;
14163  int ret;
14164 
14165  if((s == NULL) || (vmu == NULL) || (event_name == NULL) || (actionid == NULL)) {
14166  ast_log(LOG_ERROR, "Wrong input parameter.");
14167  return 0;
14168  }
14169 
14170  /* create mailbox string */
14171  if (!ast_strlen_zero(vmu->context)) {
14172  ret = ast_asprintf(&mailbox, "%s@%s", vmu->mailbox, vmu->context);
14173  } else {
14174  ret = ast_asprintf(&mailbox, "%s", vmu->mailbox);
14175  }
14176  if (ret == -1) {
14177  ast_log(LOG_ERROR, "Could not create mailbox string. err[%s]\n", strerror(errno));
14178  return 0;
14179  }
14180 
14181  /* get mailbox count */
14182  ret = inboxcount(mailbox, &new, &old);
14183  ast_free(mailbox);
14184  if (ret == -1) {
14185  ast_log(LOG_ERROR, "Could not get mailbox count. user[%s], context[%s]\n",
14186  vmu->mailbox ?: "", vmu->context ?: "");
14187  return 0;
14188  }
14189 
14190  astman_append(s,
14191  "Event: %s\r\n"
14192  "%s"
14193  "VMContext: %s\r\n"
14194  "VoiceMailbox: %s\r\n"
14195  "Fullname: %s\r\n"
14196  "Email: %s\r\n"
14197  "Pager: %s\r\n"
14198  "ServerEmail: %s\r\n"
14199  "FromString: %s\r\n"
14200  "MailCommand: %s\r\n"
14201  "Language: %s\r\n"
14202  "TimeZone: %s\r\n"
14203  "Callback: %s\r\n"
14204  "Dialout: %s\r\n"
14205  "UniqueID: %s\r\n"
14206  "ExitContext: %s\r\n"
14207  "SayDurationMinimum: %d\r\n"
14208  "SayEnvelope: %s\r\n"
14209  "SayCID: %s\r\n"
14210  "AttachMessage: %s\r\n"
14211  "AttachmentFormat: %s\r\n"
14212  "DeleteMessage: %s\r\n"
14213  "VolumeGain: %.2f\r\n"
14214  "CanReview: %s\r\n"
14215  "CanMarkUrgent: %s\r\n"
14216  "CallOperator: %s\r\n"
14217  "MaxMessageCount: %d\r\n"
14218  "MaxMessageLength: %d\r\n"
14219  "NewMessageCount: %d\r\n"
14220  "OldMessageCount: %d\r\n"
14221 #ifdef IMAP_STORAGE
14222  "IMAPUser: %s\r\n"
14223  "IMAPServer: %s\r\n"
14224  "IMAPPort: %s\r\n"
14225  "IMAPFlags: %s\r\n"
14226 #endif
14227  "\r\n",
14228 
14229  event_name,
14230  actionid,
14231  vmu->context,
14232  vmu->mailbox,
14233  vmu->fullname,
14234  vmu->email,
14235  vmu->pager,
14236  ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
14237  ast_strlen_zero(vmu->fromstring) ? fromstring : vmu->fromstring,
14238  mailcmd,
14239  vmu->language,
14240  vmu->zonetag,
14241  vmu->callback,
14242  vmu->dialout,
14243  vmu->uniqueid,
14244  vmu->exit,
14245  vmu->saydurationm,
14246  ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
14247  ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
14248  ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
14249  vmu->attachfmt,
14250  ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
14251  vmu->volgain,
14252  ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
14253  ast_test_flag(vmu, VM_MARK_URGENT) ? "Yes" : "No",
14254  ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
14255  vmu->maxmsg,
14256  vmu->maxsecs,
14257  new,
14258  old
14259 #ifdef IMAP_STORAGE
14260  ,
14261  vmu->imapuser,
14262  vmu->imapserver,
14263  vmu->imapport,
14264  vmu->imapflags
14265 #endif
14266  );
14267 
14268  return 1;
14269 
14270 }
14271 
14272 
14273 /*!
14274  * \brief Append vmbox info string into given astman with event_name.
14275  * \return 0 if unable to append details, 1 otherwise.
14276 */
14278  struct mansession *s,
14279  const struct message *m,
14280  struct ast_vm_user *vmu,
14281  const char* event_name,
14282  const char* actionid)
14283 {
14284  struct ast_vm_mailbox_snapshot *mailbox_snapshot;
14285  struct ast_vm_msg_snapshot *msg;
14286  int nummessages = 0;
14287  int i;
14288 
14289  /* Take a snapshot of the mailbox */
14290  mailbox_snapshot = ast_vm_mailbox_snapshot_create(vmu->mailbox, vmu->context, NULL, 0, AST_VM_SNAPSHOT_SORT_BY_ID, 0);
14291  if (!mailbox_snapshot) {
14292  ast_log(LOG_ERROR, "Could not append voicemail box info for box %s@%s.",
14293  vmu->mailbox, vmu->context);
14294  return 0;
14295  }
14296 
14297  astman_send_listack(s, m, "Voicemail box detail will follow", "start");
14298  /* walk through each folder's contents and append info for each message */
14299  for (i = 0; i < mailbox_snapshot->folders; i++) {
14300  AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
14301  astman_append(s,
14302  "Event: %s\r\n"
14303  "%s"
14304  "Folder: %s\r\n"
14305  "CallerID: %s\r\n"
14306  "Date: %s\r\n"
14307  "Duration: %s\r\n"
14308  "Flag: %s\r\n"
14309  "ID: %s\r\n"
14310  "\r\n",
14311  event_name,
14312  actionid,
14313  msg->folder_name,
14314  msg->callerid,
14315  msg->origdate,
14316  msg->duration,
14317  msg->flag,
14318  msg->msg_id
14319  );
14320  nummessages++;
14321  }
14322  }
14323 
14324  /* done, destroy. */
14325  mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
14326  astman_send_list_complete_start(s, m, "VoicemailBoxDetailComplete", nummessages);
14328 
14329  return 1;
14330 }
14331 
14332 static int manager_match_mailbox(struct ast_mwi_state *mwi_state, void *data)
14333 {
14334  const char *context = astman_get_header(data, "Context");
14335  const char *mailbox = astman_get_header(data, "Mailbox");
14336  const char *at;
14337 
14338  if (!ast_strlen_zero(mwi_state->uniqueid)) {
14339  if (
14340  /* First case: everything matches */
14341  (ast_strlen_zero(context) && ast_strlen_zero(mailbox)) ||
14342  /* Second case: match the mailbox only */
14343  (ast_strlen_zero(context) && !ast_strlen_zero(mailbox) &&
14344  (at = strchr(mwi_state->uniqueid, '@')) &&
14345  strncmp(mailbox, mwi_state->uniqueid, at - mwi_state->uniqueid) == 0) ||
14346  /* Third case: match the context only */
14347  (!ast_strlen_zero(context) && ast_strlen_zero(mailbox) &&
14348  (at = strchr(mwi_state->uniqueid, '@')) &&
14349  strcmp(context, at + 1) == 0) ||
14350  /* Final case: match an exact specified mailbox */
14351  (!ast_strlen_zero(context) && !ast_strlen_zero(mailbox) &&
14352  (at = strchr(mwi_state->uniqueid, '@')) &&
14353  strncmp(mailbox, mwi_state->uniqueid, at - mwi_state->uniqueid) == 0 &&
14354  strcmp(context, at + 1) == 0)
14355  ) {
14356  poll_subscribed_mailbox(mwi_state, NULL);
14357  }
14358  }
14359 
14360  return 0;
14361 }
14362 
14363 static int manager_voicemail_refresh(struct mansession *s, const struct message *m)
14364 {
14365  ast_mwi_state_callback_all(manager_match_mailbox, (void *)m);
14366  astman_send_ack(s, m, "Refresh sent");
14367  return RESULT_SUCCESS;
14368 }
14369 
14370 static int manager_status_voicemail_user(struct mansession *s, const struct message *m)
14371 {
14372  struct ast_vm_user *vmu = NULL;
14373  const char *id = astman_get_header(m, "ActionID");
14374  char actionid[128];
14375  struct ast_vm_user svm;
14376  int ret;
14377 
14378  const char *context = astman_get_header(m, "Context");
14379  const char *mailbox = astman_get_header(m, "Mailbox");
14380 
14381  if ((ast_strlen_zero(context) || ast_strlen_zero(mailbox))) {
14382  astman_send_error(s, m, "Need 'Context' and 'Mailbox' parameters.");
14383  return RESULT_SUCCESS;
14384  }
14385 
14386  actionid[0] = '\0';
14387  if (!ast_strlen_zero(id)) {
14388  snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14389  }
14390 
14391  /* find user */
14392  memset(&svm, 0, sizeof(svm));
14393  vmu = find_user(&svm, context, mailbox);
14394  if (!vmu) {
14395  /* could not find it */
14396  astman_send_ack(s, m, "There is no voicemail user of the given info.");
14397  return RESULT_SUCCESS;
14398  }
14399 
14400  astman_send_listack(s, m, "Voicemail user detail will follow", "start");
14401 
14402  /* append vmu info event */
14403  ret = append_vmu_info_astman(s, vmu, "VoicemailUserDetail", actionid);
14404  free_user(vmu);
14405  if(ret == 0) {
14406  ast_log(LOG_ERROR, "Could not append voicemail user info.");
14407  }
14408 
14409  astman_send_list_complete_start(s, m, "VoicemailUserDetailComplete", 1);
14411 
14412  return RESULT_SUCCESS;
14413 }
14414 
14415 /*! \brief Manager list voicemail users command */
14416 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
14417 {
14418  struct ast_vm_user *vmu = NULL;
14419  const char *id = astman_get_header(m, "ActionID");
14420  char actionid[128];
14421  int num_users = 0;
14422  int ret;
14423 
14424  actionid[0] = '\0';
14425  if (!ast_strlen_zero(id)) {
14426  snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14427  }
14428 
14429  AST_LIST_LOCK(&users);
14430 
14431  if (AST_LIST_EMPTY(&users)) {
14432  astman_send_ack(s, m, "There are no voicemail users currently defined.");
14434  return RESULT_SUCCESS;
14435  }
14436 
14437  astman_send_listack(s, m, "Voicemail user list will follow", "start");
14438 
14439  AST_LIST_TRAVERSE(&users, vmu, list) {
14440  /* append vmu info event */
14441  ret = append_vmu_info_astman(s, vmu, "VoicemailUserEntry", actionid);
14442  if(ret == 0) {
14443  ast_log(LOG_ERROR, "Could not append voicemail user info.");
14444  continue;
14445  }
14446  ++num_users;
14447  }
14448 
14449  astman_send_list_complete_start(s, m, "VoicemailUserEntryComplete", num_users);
14451 
14453 
14454  return RESULT_SUCCESS;
14455 }
14456 
14457 static int manager_get_mailbox_summary(struct mansession *s, const struct message *m)
14458 {
14459  struct ast_vm_user *vmu = NULL;
14460  const char *id = astman_get_header(m, "ActionID");
14461  char actionid[128];
14462  struct ast_vm_user svm;
14463 
14464  const char *context = astman_get_header(m, "Context");
14465  const char *mailbox = astman_get_header(m, "Mailbox");
14466 
14467  if ((ast_strlen_zero(context) || ast_strlen_zero(mailbox))) {
14468  astman_send_error(s, m, "Need 'Context' and 'Mailbox' parameters.");
14469  return 0;
14470  }
14471 
14472  actionid[0] = '\0';
14473  if (!ast_strlen_zero(id)) {
14474  snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14475  }
14476 
14477  /* find user */
14478  memset(&svm, 0, sizeof(svm));
14479  vmu = find_user(&svm, context, mailbox);
14480  if (!vmu) {
14481  /* could not find it */
14482  astman_send_ack(s, m, "There is no voicemail user matching the given user.");
14483  return 0;
14484  }
14485 
14486  /* Append the mailbox details */
14487  if (!append_vmbox_info_astman(s, m, vmu, "VoicemailBoxDetail", actionid)) {
14488  astman_send_error(s, m, "Unable to get mailbox info for the given user.");
14489  }
14490 
14491  free_user(vmu);
14492  return 0;
14493 }
14494 
14495 static int manager_voicemail_move(struct mansession *s, const struct message *m)
14496 {
14497  const char *mailbox = astman_get_header(m, "Mailbox");
14498  const char *context = astman_get_header(m, "Context");
14499  const char *from_folder = astman_get_header(m, "Folder");
14500  const char *id[] = { astman_get_header(m, "ID") };
14501  const char *to_folder = astman_get_header(m, "ToFolder");
14502 
14503  if (ast_strlen_zero(mailbox)) {
14504  astman_send_error(s, m, "Mailbox not specified, required");
14505  return 0;
14506  }
14507  if (ast_strlen_zero(context)) {
14508  astman_send_error(s, m, "Context not specified, required");
14509  return 0;
14510  }
14511  if (ast_strlen_zero(from_folder)) {
14512  astman_send_error(s, m, "Folder not specified, required");
14513  return 0;
14514  }
14515  if (ast_strlen_zero(id[0])) {
14516  astman_send_error(s, m, "ID not specified, required");
14517  return 0;
14518  }
14519  if (ast_strlen_zero(to_folder)) {
14520  astman_send_error(s, m, "ToFolder not specified, required");
14521  return 0;
14522  }
14523 
14524  if (vm_msg_move(mailbox, context, 1, from_folder, id, to_folder)) {
14525  astman_send_ack(s, m, "Message move failed\n");
14526  } else {
14527  astman_send_ack(s, m, "Message move successful\n");
14528  }
14529 
14530  return 0;
14531 }
14532 
14533 static int manager_voicemail_remove(struct mansession *s, const struct message *m)
14534 {
14535  const char *mailbox = astman_get_header(m, "Mailbox");
14536  const char *context = astman_get_header(m, "Context");
14537  const char *folder = astman_get_header(m, "Folder");
14538  const char *id[] = { astman_get_header(m, "ID") };
14539 
14540  if (ast_strlen_zero(mailbox)) {
14541  astman_send_error(s, m, "Mailbox not specified, required");
14542  return 0;
14543  }
14544  if (ast_strlen_zero(context)) {
14545  astman_send_error(s, m, "Context not specified, required");
14546  return 0;
14547  }
14548  if (ast_strlen_zero(folder)) {
14549  astman_send_error(s, m, "Folder not specified, required");
14550  return 0;
14551  }
14552  if (ast_strlen_zero(id[0])) {
14553  astman_send_error(s, m, "ID not specified, required");
14554  return 0;
14555  }
14556 
14557  if (vm_msg_remove(mailbox, context, 1, folder, id)) {
14558  astman_send_ack(s, m, "Message remove failed\n");
14559  } else {
14560  astman_send_ack(s, m, "Message remove successful\n");
14561  }
14562 
14563  return 0;
14564 }
14565 
14566 static int manager_voicemail_forward(struct mansession *s, const struct message *m)
14567 {
14568  const char *from_mailbox = astman_get_header(m, "Mailbox");
14569  const char *from_context = astman_get_header(m, "Context");
14570  const char *from_folder = astman_get_header(m, "Folder");
14571  const char *id[] = { astman_get_header(m, "ID") };
14572  const char *to_mailbox = astman_get_header(m, "ToMailbox");
14573  const char *to_context = astman_get_header(m, "ToContext");
14574  const char *to_folder = astman_get_header(m, "ToFolder");
14575 
14576  if (ast_strlen_zero(from_mailbox)) {
14577  astman_send_error(s, m, "Mailbox not specified, required");
14578  return 0;
14579  }
14580  if (ast_strlen_zero(from_context)) {
14581  astman_send_error(s, m, "Context not specified, required");
14582  return 0;
14583  }
14584  if (ast_strlen_zero(from_folder)) {
14585  astman_send_error(s, m, "Folder not specified, required");
14586  return 0;
14587  }
14588  if (ast_strlen_zero(id[0])) {
14589  astman_send_error(s, m, "ID not specified, required");
14590  return 0;
14591  }
14592  if (ast_strlen_zero(to_mailbox)) {
14593  astman_send_error(s, m, "ToMailbox not specified, required");
14594  return 0;
14595  }
14596  if (ast_strlen_zero(to_context)) {
14597  astman_send_error(s, m, "ToContext not specified, required");
14598  return 0;
14599  }
14600  if (ast_strlen_zero(to_folder)) {
14601  astman_send_error(s, m, "ToFolder not specified, required");
14602  return 0;
14603  }
14604 
14605  if (vm_msg_forward(from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder, 1, id, 0)) {
14606  astman_send_ack(s, m, "Message forward failed\n");
14607  } else {
14608  astman_send_ack(s, m, "Message forward successful\n");
14609  }
14610 
14611  return 0;
14612 }
14613 
14614 /*! \brief Free the users structure. */
14615 static void free_vm_users(void)
14616 {
14617  struct ast_vm_user *current;
14618  AST_LIST_LOCK(&users);
14619  while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
14620  ast_set_flag(current, VM_ALLOCED);
14621  free_user_final(current);
14622  }
14624 }
14625 
14626 /*! \brief Free the zones structure. */
14627 static void free_vm_zones(void)
14628 {
14629  struct vm_zone *zcur;
14630  AST_LIST_LOCK(&zones);
14631  while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
14632  free_zone(zcur);
14634 }
14635 
14636 static const char *substitute_escapes(const char *value)
14637 {
14638  char *current;
14639 
14640  /* Add 16 for fudge factor */
14641  struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
14642 
14643  ast_str_reset(str);
14644 
14645  /* Substitute strings \r, \n, and \t into the appropriate characters */
14646  for (current = (char *) value; *current; current++) {
14647  if (*current == '\\') {
14648  current++;
14649  if (!*current) {
14650  ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
14651  break;
14652  }
14653  switch (*current) {
14654  case '\\':
14655  ast_str_append(&str, 0, "\\");
14656  break;
14657  case 'r':
14658  ast_str_append(&str, 0, "\r");
14659  break;
14660  case 'n':
14661 #ifdef IMAP_STORAGE
14662  if (!str->used || str->str[str->used - 1] != '\r') {
14663  ast_str_append(&str, 0, "\r");
14664  }
14665 #endif
14666  ast_str_append(&str, 0, "\n");
14667  break;
14668  case 't':
14669  ast_str_append(&str, 0, "\t");
14670  break;
14671  default:
14672  ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
14673  break;
14674  }
14675  } else {
14676  ast_str_append(&str, 0, "%c", *current);
14677  }
14678  }
14679 
14680  return ast_str_buffer(str);
14681 }
14682 
14683 static int load_config_force(int reload, int force)
14684 {
14685  struct ast_config *cfg, *ucfg;
14686  struct ast_flags config_flags = { reload && !force ? CONFIG_FLAG_FILEUNCHANGED : 0 };
14687  int res;
14688 
14689  ast_unload_realtime("voicemail");
14690  ast_unload_realtime("voicemail_data");
14691 
14692  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
14693  if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
14694  return 0;
14695  } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
14696  ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
14697  ucfg = NULL;
14698  }
14699  ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
14700  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
14701  ast_config_destroy(ucfg);
14702  ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
14703  return 0;
14704  }
14705  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
14706  ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
14707  return 0;
14708  } else {
14709  ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
14710  if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
14711  ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
14712  ucfg = NULL;
14713  }
14714  }
14715 
14716  res = actual_load_config(reload, cfg, ucfg);
14717 
14718  ast_config_destroy(cfg);
14719  ast_config_destroy(ucfg);
14720 
14721  return res;
14722 }
14723 
14724 static int load_config(int reload)
14725 {
14726  return load_config_force(reload, 0);
14727 }
14728 
14729 #ifdef TEST_FRAMEWORK
14730 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
14731 {
14732  ast_unload_realtime("voicemail");
14733  ast_unload_realtime("voicemail_data");
14734  return actual_load_config(reload, cfg, ucfg);
14735 }
14736 #endif
14737 
14738 static struct alias_mailbox_mapping *alias_mailbox_mapping_create(const char *alias, const char *mailbox)
14739 {
14740  struct alias_mailbox_mapping *mapping;
14741  size_t from_len = strlen(alias) + 1;
14742  size_t to_len = strlen(mailbox) + 1;
14743 
14744  mapping = ao2_alloc(sizeof(*mapping) + from_len + to_len, NULL);
14745  if (!mapping) {
14746  return NULL;
14747  }
14748  mapping->alias = mapping->buf;
14749  mapping->mailbox = mapping->buf + from_len;
14750  ast_copy_string(mapping->alias, alias, from_len); /* Safe */
14751  ast_copy_string(mapping->mailbox, mailbox, to_len); /* Safe */
14752 
14753  return mapping;
14754 }
14755 
14756 static void load_aliases(struct ast_config *cfg)
14757 {
14758  struct ast_variable *var;
14759 
14760  if (ast_strlen_zero(aliasescontext)) {
14761  return;
14762  }
14763  var = ast_variable_browse(cfg, aliasescontext);
14764  while (var) {
14765  struct alias_mailbox_mapping *mapping = alias_mailbox_mapping_create(var->name, var->value);
14766  if (mapping) {
14767  ao2_link(alias_mailbox_mappings, mapping);
14768  ao2_link(mailbox_alias_mappings, mapping);
14769  ao2_ref(mapping, -1);
14770  }
14771  var = var->next;
14772  }
14773 }
14774 
14775 static void load_zonemessages(struct ast_config *cfg)
14776 {
14777  struct ast_variable *var;
14778 
14779  var = ast_variable_browse(cfg, "zonemessages");
14780  while (var) {
14781  if (var->value) {
14782  struct vm_zone *z;
14783  char *msg_format, *tzone;
14784  char storage[strlen(var->value) + 1];
14785 
14786  z = ast_malloc(sizeof(*z));
14787  if (!z) {
14788  return;
14789  }
14790 
14791  strcpy(storage, var->value); /* safe */
14792  msg_format = storage;
14793  tzone = strsep(&msg_format, "|,");
14794  if (msg_format) {
14795  ast_copy_string(z->name, var->name, sizeof(z->name));
14796  ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
14797  ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
14798  AST_LIST_LOCK(&zones);
14799  AST_LIST_INSERT_HEAD(&zones, z, list);
14801  } else {
14802  ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
14803  ast_free(z);
14804  }
14805  }
14806  var = var->next;
14807  }
14808 }
14809 
14810 static void load_users(struct ast_config *cfg)
14811 {
14812  struct ast_variable *var;
14813  char *cat = NULL;
14814 
14815  while ((cat = ast_category_browse(cfg, cat))) {
14816  if (strcasecmp(cat, "general") == 0
14817  || strcasecmp(cat, aliasescontext) == 0
14818  || strcasecmp(cat, "zonemessages") == 0) {
14819  continue;
14820  }
14821 
14822  var = ast_variable_browse(cfg, cat);
14823  while (var) {
14824  append_mailbox(cat, var->name, var->value);
14825  var = var->next;
14826  }
14827  }
14828 }
14829 
14830 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
14831 {
14832  struct ast_vm_user *current;
14833  char *cat;
14834  const char *val;
14835  char *q, *stringp, *tmp;
14836  int x;
14837  unsigned int tmpadsi[4];
14838  char secretfn[PATH_MAX] = "";
14839  long tps_queue_low;
14840  long tps_queue_high;
14841 
14842 #ifdef IMAP_STORAGE
14843  ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
14844 #endif
14845  /* set audio control prompts */
14846  strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
14847  strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
14848  strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
14849  strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
14850  strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
14851 
14852 #ifdef IMAP_STORAGE
14853  ast_mwi_state_callback_all(imap_close_subscribed_mailbox, NULL);
14854 #endif
14855 
14856  /* Free all the users structure */
14857  free_vm_users();
14858 
14859  /* Free all the zones structure */
14860  free_vm_zones();
14861 
14862  /* Remove all aliases */
14863  ao2_callback(alias_mailbox_mappings, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
14864  ao2_callback(mailbox_alias_mappings, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE, NULL, NULL);
14865 
14866  AST_LIST_LOCK(&users);
14867 
14868  memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
14869  memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
14870 
14871  if (cfg) {
14872  /* General settings */
14873 
14874  if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
14875  val = "default";
14876  ast_copy_string(userscontext, val, sizeof(userscontext));
14877 
14878  aliasescontext[0] = '\0';
14879  val = ast_variable_retrieve(cfg, "general", "aliasescontext");
14880  ast_copy_string(aliasescontext, S_OR(val, ""), sizeof(aliasescontext));
14881 
14882  /* Attach voice message to mail message ? */
14883  if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
14884  val = "yes";
14885  ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH);
14886 
14887  if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
14888  val = "no";
14889  ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
14890 
14891  volgain = 0.0;
14892  if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
14893  sscanf(val, "%30lf", &volgain);
14894 
14895 #ifdef ODBC_STORAGE
14896  strcpy(odbc_database, "asterisk");
14897  if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
14898  ast_copy_string(odbc_database, val, sizeof(odbc_database));
14899  }
14900 
14901  strcpy(odbc_table, "voicemessages");
14902  if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
14903  ast_copy_string(odbc_table, val, sizeof(odbc_table));
14904  }
14905  odbc_table_len = strlen(odbc_table);
14906 
14907  ast_set2_flag((&globalflags), 0, VM_ODBC_AUDIO_ON_DISK);
14908  if (!(val = ast_variable_retrieve(cfg, "general", "odbc_audio_on_disk")))
14909  val = "no";
14910  ast_set2_flag((&globalflags), ast_true(val), VM_ODBC_AUDIO_ON_DISK);
14911 
14912 #endif
14913  /* Mail command */
14914  strcpy(mailcmd, SENDMAIL);
14915  if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
14916  ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
14917 
14918  maxsilence = 0;
14919  if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
14920  maxsilence = atoi(val);
14921  if (maxsilence > 0)
14922  maxsilence *= 1000;
14923  }
14924 
14925  if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
14926  maxmsg = MAXMSG;
14927  } else {
14928  maxmsg = atoi(val);
14929  if (maxmsg < 0) {
14930  ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
14931  maxmsg = MAXMSG;
14932  } else if (maxmsg > MAXMSGLIMIT) {
14933  ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
14934  maxmsg = MAXMSGLIMIT;
14935  }
14936  }
14937 
14938  if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
14939  maxdeletedmsg = 0;
14940  } else {
14941  if (sscanf(val, "%30d", &x) == 1)
14942  maxdeletedmsg = x;
14943  else if (ast_true(val))
14944  maxdeletedmsg = MAXMSG;
14945  else
14946  maxdeletedmsg = 0;
14947 
14948  if (maxdeletedmsg < 0) {
14949  ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
14950  maxdeletedmsg = MAXMSG;
14951  } else if (maxdeletedmsg > MAXMSGLIMIT) {
14952  ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
14953  maxdeletedmsg = MAXMSGLIMIT;
14954  }
14955  }
14956 
14957  /* Load date format config for voicemail mail */
14958  if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
14959  ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
14960  }
14961 
14962  /* Load date format config for voicemail pager mail */
14963  if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
14964  ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
14965  }
14966 
14967  /* External password changing command */
14968  if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
14969  ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
14970  pwdchange = PWDCHANGE_EXTERNAL;
14971  } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
14972  ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
14973  pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
14974  }
14975 
14976  /* External password validation command */
14977  if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
14978  ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
14979  ast_debug(1, "found externpasscheck: %s\n", ext_pass_check_cmd);
14980  }
14981 
14982 #ifdef IMAP_STORAGE
14983  /* IMAP server address */
14984  if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
14985  ast_copy_string(imapserver, val, sizeof(imapserver));
14986  } else {
14987  ast_copy_string(imapserver, "localhost", sizeof(imapserver));
14988  }
14989  /* IMAP server port */
14990  if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
14991  ast_copy_string(imapport, val, sizeof(imapport));
14992  } else {
14993  ast_copy_string(imapport, "143", sizeof(imapport));
14994  }
14995  /* IMAP server flags */
14996  if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
14997  ast_copy_string(imapflags, val, sizeof(imapflags));
14998  }
14999  /* IMAP server master username */
15000  if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
15001  ast_copy_string(authuser, val, sizeof(authuser));
15002  }
15003  /* IMAP server master password */
15004  if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
15005  ast_copy_string(authpassword, val, sizeof(authpassword));
15006  }
15007  /* Expunge on exit */
15008  if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
15009  if (ast_false(val))
15010  expungeonhangup = 0;
15011  else
15012  expungeonhangup = 1;
15013  } else {
15014  expungeonhangup = 1;
15015  }
15016  /* IMAP voicemail folder */
15017  if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
15018  ast_copy_string(imapfolder, val, sizeof(imapfolder));
15019  } else {
15020  ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
15021  }
15022  if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
15023  ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
15024  }
15025  if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
15026  imapgreetings = ast_true(val);
15027  } else {
15028  imapgreetings = 0;
15029  }
15030  if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
15031  ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
15032  } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
15033  /* Also support greetingsfolder as documented in voicemail.conf.sample */
15034  ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
15035  } else {
15036  ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
15037  }
15038  if ((val = ast_variable_retrieve(cfg, "general", "imap_poll_logout"))) {
15039  imap_poll_logout = ast_true(val);
15040  } else {
15041  imap_poll_logout = 0;
15042  }
15043 
15044  /* There is some very unorthodox casting done here. This is due
15045  * to the way c-client handles the argument passed in. It expects a
15046  * void pointer and casts the pointer directly to a long without
15047  * first dereferencing it. */
15048  if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
15049  mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
15050  } else {
15051  mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
15052  }
15053 
15054  if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
15055  mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
15056  } else {
15057  mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
15058  }
15059 
15060  if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
15061  mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
15062  } else {
15063  mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
15064  }
15065 
15066  if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
15067  mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
15068  } else {
15069  mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
15070  }
15071 
15072  /* Increment configuration version */
15073  imapversion++;
15074 #endif
15075  /* External voicemail notify application */
15076  if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
15077  ast_copy_string(externnotify, val, sizeof(externnotify));
15078  ast_debug(1, "found externnotify: %s\n", externnotify);
15079  } else {
15080  externnotify[0] = '\0';
15081  }
15082 
15083  /* SMDI voicemail notification */
15084  if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
15085  ast_debug(1, "Enabled SMDI voicemail notification\n");
15086  if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
15087  smdi_iface = ast_smdi_interface_find(val);
15088  } else {
15089  ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
15090  smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
15091  }
15092  if (!smdi_iface) {
15093  ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
15094  }
15095  }
15096 
15097  /* Silence treshold */
15098  silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
15099  if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
15100  silencethreshold = atoi(val);
15101 
15102  if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
15103  val = ASTERISK_USERNAME;
15104  ast_copy_string(serveremail, val, sizeof(serveremail));
15105 
15106  vmmaxsecs = 0;
15107  if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
15108  if (sscanf(val, "%30d", &x) == 1) {
15109  vmmaxsecs = x;
15110  } else {
15111  ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
15112  }
15113  } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
15114  static int maxmessage_deprecate = 0;
15115  if (maxmessage_deprecate == 0) {
15116  maxmessage_deprecate = 1;
15117  ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
15118  }
15119  if (sscanf(val, "%30d", &x) == 1) {
15120  vmmaxsecs = x;
15121  } else {
15122  ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
15123  }
15124  }
15125 
15126  vmminsecs = 0;
15127  if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
15128  if (sscanf(val, "%30d", &x) == 1) {
15129  vmminsecs = x;
15130  if (maxsilence / 1000 >= vmminsecs) {
15131  ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
15132  }
15133  } else {
15134  ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
15135  }
15136  } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
15137  static int maxmessage_deprecate = 0;
15138  if (maxmessage_deprecate == 0) {
15139  maxmessage_deprecate = 1;
15140  ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
15141  }
15142  if (sscanf(val, "%30d", &x) == 1) {
15143  vmminsecs = x;
15144  if (maxsilence / 1000 >= vmminsecs) {
15145  ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
15146  }
15147  } else {
15148  ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
15149  }
15150  }
15151 
15152  val = ast_variable_retrieve(cfg, "general", "format");
15153  if (!val) {
15154  val = "wav";
15155  } else {
15156  tmp = ast_strdupa(val);
15157  val = ast_format_str_reduce(tmp);
15158  if (!val) {
15159  ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
15160  val = "wav";
15161  }
15162  }
15163  ast_copy_string(vmfmts, val, sizeof(vmfmts));
15164 
15165  skipms = 3000;
15166  if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
15167  if (sscanf(val, "%30d", &x) == 1) {
15168  maxgreet = x;
15169  } else {
15170  ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
15171  }
15172  }
15173 
15174  if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
15175  if (sscanf(val, "%30d", &x) == 1) {
15176  skipms = x;
15177  } else {
15178  ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
15179  }
15180  }
15181 
15182  maxlogins = 3;
15183  if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
15184  if (sscanf(val, "%30d", &x) == 1) {
15185  maxlogins = x;
15186  } else {
15187  ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
15188  }
15189  }
15190 
15191  minpassword = MINPASSWORD;
15192  if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
15193  if (sscanf(val, "%30d", &x) == 1) {
15194  minpassword = x;
15195  } else {
15196  ast_log(AST_LOG_WARNING, "Invalid minimum password length. Default to %d\n", minpassword);
15197  }
15198  }
15199 
15200  /* Force new user to record name ? */
15201  if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
15202  val = "no";
15203  ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
15204 
15205  /* Force new user to record greetings ? */
15206  if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
15207  val = "no";
15208  ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
15209 
15210  if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
15211  ast_debug(1, "VM_CID Internal context string: %s\n", val);
15212  stringp = ast_strdupa(val);
15213  for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
15214  if (!ast_strlen_zero(stringp)) {
15215  q = strsep(&stringp, ",");
15216  while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
15217  q++;
15218  ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
15219  ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
15220  } else {
15221  cidinternalcontexts[x][0] = '\0';
15222  }
15223  }
15224  }
15225  if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
15226  ast_debug(1, "VM Review Option disabled globally\n");
15227  val = "no";
15228  }
15229  ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW);
15230 
15231  if (!(val = ast_variable_retrieve(cfg, "general", "leaveurgent"))){
15232  val = "yes";
15233  } else if (ast_false(val)) {
15234  ast_debug(1, "VM leave urgent messages disabled globally\n");
15235  val = "no";
15236  }
15237  ast_set2_flag((&globalflags), ast_true(val), VM_MARK_URGENT);
15238 
15239  /* Temporary greeting reminder */
15240  if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
15241  ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
15242  val = "no";
15243  } else {
15244  ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
15245  }
15246  ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
15247  if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
15248  ast_debug(1, "VM next message wrap disabled globally\n");
15249  val = "no";
15250  }
15251  ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);
15252 
15253  if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
15254  ast_debug(1, "VM Operator break disabled globally\n");
15255  val = "no";
15256  }
15257  ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);
15258 
15259  if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
15260  ast_debug(1, "VM CID Info before msg disabled globally\n");
15261  val = "no";
15262  }
15263  ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID);
15264 
15265  if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
15266  ast_debug(1, "Send Voicemail msg disabled globally\n");
15267  val = "no";
15268  }
15269  ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
15270 
15271  if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
15272  ast_debug(1, "ENVELOPE before msg enabled globally\n");
15273  val = "yes";
15274  }
15275  ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);
15276 
15277  if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
15278  ast_debug(1, "Move Heard enabled globally\n");
15279  val = "yes";
15280  }
15281  ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD);
15282 
15283  if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
15284  ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
15285  val = "no";
15286  }
15287  ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);
15288 
15289  if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
15290  ast_debug(1, "Duration info before msg enabled globally\n");
15291  val = "yes";
15292  }
15293  ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);
15294 
15295  saydurationminfo = 2;
15296  if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
15297  if (sscanf(val, "%30d", &x) == 1) {
15298  saydurationminfo = x;
15299  } else {
15300  ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
15301  }
15302  }
15303 
15304  if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
15305  ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
15306  val = "no";
15307  }
15308  ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
15309 
15310  if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
15311  ast_copy_string(dialcontext, val, sizeof(dialcontext));
15312  ast_debug(1, "found dialout context: %s\n", dialcontext);
15313  } else {
15314  dialcontext[0] = '\0';
15315  }
15316 
15317  if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
15318  ast_copy_string(callcontext, val, sizeof(callcontext));
15319  ast_debug(1, "found callback context: %s\n", callcontext);
15320  } else {
15321  callcontext[0] = '\0';
15322  }
15323 
15324  if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
15325  ast_copy_string(exitcontext, val, sizeof(exitcontext));
15326  ast_debug(1, "found operator context: %s\n", exitcontext);
15327  } else {
15328  exitcontext[0] = '\0';
15329  }
15330 
15331  /* load password sounds configuration */
15332  if ((val = ast_variable_retrieve(cfg, "general", "vm-login")))
15333  ast_copy_string(vm_login, val, sizeof(vm_login));
15334  if ((val = ast_variable_retrieve(cfg, "general", "vm-newuser")))
15335  ast_copy_string(vm_newuser, val, sizeof(vm_newuser));
15336  if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
15337  ast_copy_string(vm_password, val, sizeof(vm_password));
15338  if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
15339  ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
15340  if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
15341  ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
15342  if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
15343  ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
15344  if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
15345  ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
15346  if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
15347  ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
15348  if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
15349  ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
15350  }
15351  if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
15352  ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
15353  }
15354  /* load configurable audio prompts */
15355  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
15356  ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
15357  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
15358  ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
15359  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
15360  ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
15361  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
15362  ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
15363  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
15364  ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
15365 
15366  if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
15367  val = "no";
15368  ast_set2_flag((&globalflags), ast_true(val), VM_DIRECTFORWARD);
15369 
15370  if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
15371  val = "voicemail.conf";
15372  }
15373  if (!(strcmp(val, "spooldir"))) {
15374  passwordlocation = OPT_PWLOC_SPOOLDIR;
15375  } else {
15376  passwordlocation = OPT_PWLOC_VOICEMAILCONF;
15377  }
15378 
15379  poll_freq = DEFAULT_POLL_FREQ;
15380  if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
15381  if (sscanf(val, "%30u", &poll_freq) != 1) {
15382  poll_freq = DEFAULT_POLL_FREQ;
15383  ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
15384  }
15385  }
15386 
15387  poll_mailboxes = 0;
15388  if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
15389  poll_mailboxes = ast_true(val);
15390 
15391  memset(fromstring, 0, sizeof(fromstring));
15392  memset(pagerfromstring, 0, sizeof(pagerfromstring));
15393  strcpy(charset, "ISO-8859-1");
15394  if (emailbody) {
15395  ast_free(emailbody);
15396  emailbody = NULL;
15397  }
15398  if (emailsubject) {
15399  ast_free(emailsubject);
15400  emailsubject = NULL;
15401  }
15402  if (pagerbody) {
15403  ast_free(pagerbody);
15404  pagerbody = NULL;
15405  }
15406  if (pagersubject) {
15407  ast_free(pagersubject);
15408  pagersubject = NULL;
15409  }
15410  if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
15411  ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
15412  if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
15413  ast_copy_string(fromstring, val, sizeof(fromstring));
15414  if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
15415  ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
15416  if ((val = ast_variable_retrieve(cfg, "general", "charset")))
15417  ast_copy_string(charset, val, sizeof(charset));
15418  if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
15419  sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
15420  for (x = 0; x < 4; x++) {
15421  memcpy(&adsifdn[x], &tmpadsi[x], 1);
15422  }
15423  }
15424  if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
15425  sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
15426  for (x = 0; x < 4; x++) {
15427  memcpy(&adsisec[x], &tmpadsi[x], 1);
15428  }
15429  }
15430  if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
15431  if (atoi(val)) {
15432  adsiver = atoi(val);
15433  }
15434  }
15435  if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
15436  ast_copy_string(zonetag, val, sizeof(zonetag));
15437  }
15438  if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
15439  ast_copy_string(locale, val, sizeof(locale));
15440  }
15441  if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
15442  emailsubject = ast_strdup(substitute_escapes(val));
15443  }
15444  if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
15445  emailbody = ast_strdup(substitute_escapes(val));
15446  }
15447  if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
15448  pagersubject = ast_strdup(substitute_escapes(val));
15449  }
15450  if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
15451  pagerbody = ast_strdup(substitute_escapes(val));
15452  }
15453 
15454  tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
15455  if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_high"))) {
15456  if (sscanf(val, "%30ld", &tps_queue_high) != 1 || tps_queue_high <= 0) {
15457  ast_log(AST_LOG_WARNING, "Invalid the taskprocessor high water alert trigger level '%s'\n", val);
15458  tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
15459  }
15460  }
15461  tps_queue_low = -1;
15462  if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_low"))) {
15463  if (sscanf(val, "%30ld", &tps_queue_low) != 1 ||
15464  tps_queue_low < -1 || tps_queue_high < tps_queue_low) {
15465  ast_log(AST_LOG_WARNING, "Invalid the taskprocessor low water clear alert level '%s'\n", val);
15466  tps_queue_low = -1;
15467  }
15468  }
15469  if (ast_taskprocessor_alert_set_levels(mwi_subscription_tps, tps_queue_low, tps_queue_high)) {
15470  ast_log(AST_LOG_WARNING, "Failed to set alert levels for voicemail taskprocessor.\n");
15471  }
15472 
15473  /* load mailboxes from users.conf */
15474  if (ucfg) {
15475  for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
15476  if (!strcasecmp(cat, "general")) {
15477  continue;
15478  }
15479  if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
15480  continue;
15481  if ((current = find_or_create(userscontext, cat))) {
15482  populate_defaults(current);
15483  apply_options_full(current, ast_variable_browse(ucfg, cat));
15484  ast_copy_string(current->context, userscontext, sizeof(current->context));
15485  if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
15486  current->passwordlocation = OPT_PWLOC_USERSCONF;
15487  }
15488 
15489  switch (current->passwordlocation) {
15490  case OPT_PWLOC_SPOOLDIR:
15491  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
15492  read_password_from_file(secretfn, current->password, sizeof(current->password));
15493  }
15494  }
15495  }
15496  }
15497 
15498  /* load mailboxes from voicemail.conf */
15499 
15500  /*
15501  * Aliases must be loaded before users or the aliases won't be notified
15502  * if there's existing voicemail in the user mailbox.
15503  */
15504  load_aliases(cfg);
15505 
15506  load_zonemessages(cfg);
15507 
15508  load_users(cfg);
15509 
15511 
15512  if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
15513  start_poll_thread();
15514  if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
15515  stop_poll_thread();;
15516 
15517  return 0;
15518  } else {
15520  ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
15521  return 0;
15522  }
15523 }
15524 
15525 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
15526 {
15527  int res = -1;
15528  char dir[PATH_MAX];
15529  snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
15530  ast_debug(2, "About to try retrieving name file %s\n", dir);
15531  RETRIEVE(dir, -1, mailbox, context);
15532  if (ast_fileexists(dir, NULL, NULL)) {
15533  res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
15534  }
15535  DISPOSE(dir, -1);
15536  return res;
15537 }
15538 
15539 /*!
15540  * \internal
15541  * \brief Play a recorded user name for the mailbox to the specified channel.
15542  *
15543  * \param chan Where to play the recorded name file.
15544  * \param mailbox_id The mailbox name.
15545  *
15546  * \retval 0 Name played without interruption
15547  * \retval dtmf ASCII value of the DTMF which interrupted playback.
15548  * \retval -1 Unable to locate mailbox or hangup occurred.
15549  */
15550 static int vm_sayname(struct ast_channel *chan, const char *mailbox_id)
15551 {
15552  char *context;
15553  char *mailbox;
15554 
15555  if (ast_strlen_zero(mailbox_id)
15556  || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
15557  return -1;
15558  }
15559  return sayname(chan, mailbox, context);
15560 }
15561 
15562 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
15563  struct ast_config *pwconf;
15564  struct ast_flags config_flags = { 0 };
15565 
15566  pwconf = ast_config_load(secretfn, config_flags);
15567  if (valid_config(pwconf)) {
15568  const char *val = ast_variable_retrieve(pwconf, "general", "password");
15569  if (val) {
15570  ast_copy_string(password, val, passwordlen);
15571  ast_config_destroy(pwconf);
15572  return;
15573  }
15574  ast_config_destroy(pwconf);
15575  }
15576  ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
15577 }
15578 
15579 static int write_password_to_file(const char *secretfn, const char *password) {
15580  struct ast_config *conf;
15581  struct ast_category *cat;
15582  struct ast_variable *var;
15583  int res = -1;
15584 
15585  if (!(conf = ast_config_new())) {
15586  ast_log(LOG_ERROR, "Error creating new config structure\n");
15587  return res;
15588  }
15589  if (!(cat = ast_category_new("general", "", 1))) {
15590  ast_log(LOG_ERROR, "Error creating new category structure\n");
15591  ast_config_destroy(conf);
15592  return res;
15593  }
15594  if (!(var = ast_variable_new("password", password, ""))) {
15595  ast_log(LOG_ERROR, "Error creating new variable structure\n");
15596  ast_config_destroy(conf);
15597  ast_category_destroy(cat);
15598  return res;
15599  }
15600  ast_category_append(conf, cat);
15601  ast_variable_append(cat, var);
15602  if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
15603  res = 0;
15604  } else {
15605  ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
15606  }
15607 
15608  ast_config_destroy(conf);
15609  return res;
15610 }
15611 
15612 static int vmsayname_exec(struct ast_channel *chan, const char *data)
15613 {
15614  char *context;
15615  char *mailbox;
15616  int res;
15617 
15618  if (ast_strlen_zero(data)
15619  || separate_mailbox(ast_strdupa(data), &mailbox, &context)) {
15620  ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
15621  return -1;
15622  }
15623 
15624  if ((res = sayname(chan, mailbox, context)) < 0) {
15625  ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", mailbox, context);
15626  res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
15627  if (!res) {
15628  res = ast_say_character_str(chan, mailbox, AST_DIGIT_ANY, ast_channel_language(chan), AST_SAY_CASE_NONE);
15629  }
15630  }
15631 
15632  return res;
15633 }
15634 
15635 #ifdef TEST_FRAMEWORK
15636 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
15637 {
15638  return 0;
15639 }
15640 
15641 static struct ast_frame *fake_read(struct ast_channel *ast)
15642 {
15643  return &ast_null_frame;
15644 }
15645 
15646 AST_TEST_DEFINE(test_voicemail_vmsayname)
15647 {
15648  char dir[PATH_MAX];
15649  char dir2[PATH_MAX];
15650  static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
15651  static const char TEST_EXTENSION[] = "1234";
15652 
15653  struct ast_channel *test_channel1 = NULL;
15654  int res = -1;
15655  struct ast_format_cap *capabilities;
15656 
15657  static const struct ast_channel_tech fake_tech = {
15658  .write = fake_write,
15659  .read = fake_read,
15660  };
15661 
15662  switch (cmd) {
15663  case TEST_INIT:
15664  info->name = "vmsayname_exec";
15665  info->category = "/apps/app_voicemail/";
15666  info->summary = "Vmsayname unit test";
15667  info->description =
15668  "This tests passing various parameters to vmsayname";
15669  return AST_TEST_NOT_RUN;
15670  case TEST_EXECUTE:
15671  break;
15672  }
15673 
15674  if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL,
15675  NULL, NULL, 0, 0, "TestChannel1"))) {
15676  goto exit_vmsayname_test;
15677  }
15678 
15679  /* normally this is done in the channel driver */
15681  if (!capabilities) {
15682  goto exit_vmsayname_test;
15683  }
15684  ast_format_cap_append(capabilities, ast_format_gsm, 0);
15685  ast_channel_nativeformats_set(test_channel1, capabilities);
15686  ao2_ref(capabilities, -1);
15687  ast_channel_set_writeformat(test_channel1, ast_format_gsm);
15688  ast_channel_set_rawwriteformat(test_channel1, ast_format_gsm);
15689  ast_channel_set_readformat(test_channel1, ast_format_gsm);
15690  ast_channel_set_rawreadformat(test_channel1, ast_format_gsm);
15691  ast_channel_tech_set(test_channel1, &fake_tech);
15692 
15693  ast_channel_unlock(test_channel1);
15694 
15695  ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
15696  snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
15697  if (!(res = vmsayname_exec(test_channel1, dir))) {
15698  snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15699  if (ast_fileexists(dir, NULL, NULL)) {
15700  ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
15701  res = -1;
15702  goto exit_vmsayname_test;
15703  } else {
15704  /* no greeting already exists as expected, let's create one to fully test sayname */
15705  if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
15706  ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
15707  goto exit_vmsayname_test;
15708  }
15709  snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_DATA_DIR);
15710  snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15711  /* we're not going to hear the sound anyway, just use a valid gsm audio file */
15712  if ((res = symlink(dir, dir2))) {
15713  ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
15714  goto exit_vmsayname_test;
15715  }
15716  ast_test_status_update(test, "Test playing created mailbox greeting...\n");
15717  snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
15718  res = vmsayname_exec(test_channel1, dir);
15719 
15720  /* TODO: there may be a better way to do this */
15721  unlink(dir2);
15722  snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15723  rmdir(dir2);
15724  snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
15725  rmdir(dir2);
15726  }
15727  }
15728 
15729 exit_vmsayname_test:
15730 
15731  ast_hangup(test_channel1);
15732 
15733  return res ? AST_TEST_FAIL : AST_TEST_PASS;
15734 }
15735 
15736 struct test_files {
15737  char dir[256];
15738  char file[256];
15739  char txtfile[256];
15740 };
15741 
15742 AST_TEST_DEFINE(test_voicemail_msgcount)
15743 {
15744  int i, j, res = AST_TEST_PASS, syserr;
15745  struct ast_vm_user *vmu;
15746  struct ast_vm_user svm;
15747  struct vm_state vms;
15748 #ifdef IMAP_STORAGE
15749  struct ast_channel *chan = NULL;
15750 #endif
15751  /* Using ast_alloca instead of just declaring tmp as an array is a workaround for a GCC 10 issue with -Wrestrict */
15752  struct test_files *tmp = ast_alloca(sizeof(struct test_files) * 3);
15753  char syscmd[256];
15754  const char origweasels[] = "tt-weasels";
15755  const char testcontext[] = "test";
15756  const char testmailbox[] = "00000000";
15757  const char testspec[] = "00000000@test";
15758  FILE *txt;
15759  int new, old, urgent;
15760  const char *folders[3] = { "Old", "Urgent", "INBOX" };
15761  const int folder2mbox[3] = { 1, 11, 0 };
15762  const int expected_results[3][12] = {
15763  /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
15764  { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
15765  { 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
15766  { 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 2 },
15767  };
15768 
15769  switch (cmd) {
15770  case TEST_INIT:
15771  info->name = "test_voicemail_msgcount";
15772  info->category = "/apps/app_voicemail/";
15773  info->summary = "Test Voicemail status checks";
15774  info->description =
15775  "Verify that message counts are correct when retrieved through the public API";
15776  return AST_TEST_NOT_RUN;
15777  case TEST_EXECUTE:
15778  break;
15779  }
15780 
15781  /* Make sure the original path was completely empty */
15782  snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
15783  if ((syserr = ast_safe_system(syscmd))) {
15784  ast_test_status_update(test, "Unable to clear test directory: %s\n",
15785  syserr > 0 ? strerror(syserr) : "unable to fork()");
15786  return AST_TEST_FAIL;
15787  }
15788 
15789 #ifdef IMAP_STORAGE
15790  if (!(chan = ast_dummy_channel_alloc())) {
15791  ast_test_status_update(test, "Unable to create dummy channel\n");
15792  return AST_TEST_FAIL;
15793  }
15794 #endif
15795 
15796  memset(&svm, 0, sizeof(svm));
15797  if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
15798  !(vmu = find_or_create(testcontext, testmailbox))) {
15799  ast_test_status_update(test, "Cannot create vmu structure\n");
15801 #ifdef IMAP_STORAGE
15802  chan = ast_channel_unref(chan);
15803 #endif
15804  return AST_TEST_FAIL;
15805  }
15806 
15807  populate_defaults(vmu);
15808  memset(&vms, 0, sizeof(vms));
15809 
15810  /* Create temporary voicemail */
15811  for (i = 0; i < 3; i++) {
15812  create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
15813  make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
15814  snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
15815 
15816  if (ast_fileexists(origweasels, "gsm", "en") > 0) {
15817  snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
15818  VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
15819  if ((syserr = ast_safe_system(syscmd))) {
15820  ast_test_status_update(test, "Unable to create test voicemail: %s\n",
15821  syserr > 0 ? strerror(syserr) : "unable to fork()");
15823 #ifdef IMAP_STORAGE
15824  chan = ast_channel_unref(chan);
15825 #endif
15826  free_user(vmu);
15827  return AST_TEST_FAIL;
15828  }
15829  }
15830 
15831  if ((txt = fopen(tmp[i].txtfile, "w+"))) {
15832  fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
15833  fclose(txt);
15834  } else {
15835  ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
15836  res = AST_TEST_FAIL;
15837  break;
15838  }
15839  open_mailbox(&vms, vmu, folder2mbox[i]);
15840  STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent", NULL);
15841 
15842  /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
15843  for (j = 0; j < 3; j++) {
15844  /* folder[2] is INBOX, __has_voicemail will default back to INBOX */
15845  if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
15846  ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
15847  testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
15848  res = AST_TEST_FAIL;
15849  }
15850  }
15851 
15852  new = old = urgent = 0;
15853  if (ast_app_inboxcount(testspec, &new, &old)) {
15854  ast_test_status_update(test, "inboxcount returned failure\n");
15855  res = AST_TEST_FAIL;
15856  } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
15857  ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
15858  testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
15859  res = AST_TEST_FAIL;
15860  }
15861 
15862  new = old = urgent = 0;
15863  if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
15864  ast_test_status_update(test, "inboxcount2 returned failure\n");
15865  res = AST_TEST_FAIL;
15866  } else if (old != expected_results[i][6 + 0] ||
15867  urgent != expected_results[i][6 + 1] ||
15868  new != expected_results[i][6 + 2] ) {
15869  ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
15870  testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
15871  res = AST_TEST_FAIL;
15872  }
15873 
15874  new = old = urgent = 0;
15875  for (j = 0; j < 3; j++) {
15876  if (ast_app_messagecount(testspec, folders[j]) != expected_results[i][9 + j]) {
15877  ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
15878  testspec, folders[j], ast_app_messagecount(testspec, folders[j]), expected_results[i][9 + j]);
15879  res = AST_TEST_FAIL;
15880  }
15881  }
15882  }
15883 
15884  for (i = 0; i < 3; i++) {
15885  /* This is necessary if the voicemails are stored on an ODBC/IMAP
15886  * server, in which case, the rm below will not affect the
15887  * voicemails. */
15888  DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
15889  DISPOSE(tmp[i].dir, 0);
15890  }
15891 
15892  if (vms.deleted) {
15893  ast_free(vms.deleted);
15894  }
15895  if (vms.heard) {
15896  ast_free(vms.heard);
15897  }
15898 
15899 #ifdef IMAP_STORAGE
15900  chan = ast_channel_unref(chan);
15901 #endif
15902 
15903  /* And remove test directory */
15904  snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
15905  if ((syserr = ast_safe_system(syscmd))) {
15906  ast_test_status_update(test, "Unable to clear test directory: %s\n",
15907  syserr > 0 ? strerror(syserr) : "unable to fork()");
15908  }
15909 
15910  free_user(vmu);
15911  force_reload_config(); /* Restore original config */
15912  return res;
15913 }
15914 
15915 AST_TEST_DEFINE(test_voicemail_notify_endl)
15916 {
15917  int res = AST_TEST_PASS;
15918  char testcontext[] = "test";
15919  char testmailbox[] = "00000000";
15920  char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
15921  char attach[256], attach2[256];
15922  char buf[256] = ""; /* No line should actually be longer than 80 */
15923  struct ast_channel *chan = NULL;
15924  struct ast_vm_user *vmu, vmus = {
15925  .flags = 0,
15926  };
15927  FILE *file;
15928  struct {
15929  char *name;
15930  enum { INT, FLAGVAL, STATIC, STRPTR } type;
15931  void *location;
15932  union {
15933  int intval;
15934  char *strval;
15935  } u;
15936  } test_items[] = {
15937  { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
15938  { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
15939  { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
15940  { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
15941  { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
15942  { "attach2", STRPTR, attach2, .u.strval = "" },
15943  { "attach", STRPTR, attach, .u.strval = "" },
15944  };
15945  int which;
15946 
15947  switch (cmd) {
15948  case TEST_INIT:
15949  info->name = "test_voicemail_notify_endl";
15950  info->category = "/apps/app_voicemail/";
15951  info->summary = "Test Voicemail notification end-of-line";
15952  info->description =
15953  "Verify that notification emails use a consistent end-of-line character";
15954  return AST_TEST_NOT_RUN;
15955  case TEST_EXECUTE:
15956  break;
15957  }
15958 
15959  snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_DATA_DIR);
15960  snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_DATA_DIR);
15961 
15962  if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
15963  !(vmu = find_or_create(testcontext, testmailbox))) {
15964  ast_test_status_update(test, "Cannot create vmu structure\n");
15965  return AST_TEST_NOT_RUN;
15966  }
15967 
15968  if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
15969  ast_test_status_update(test, "Cannot find vmu structure?!!\n");
15970  return AST_TEST_NOT_RUN;
15971  }
15972 
15973  populate_defaults(vmu);
15974  vmu->email = ast_strdup("test2@example.net");
15975 #ifdef IMAP_STORAGE
15976  /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
15977 #endif
15978 
15979  file = tmpfile();
15980  for (which = 0; which < ARRAY_LEN(test_items); which++) {
15981  /* Kill previous test, if any */
15982  rewind(file);
15983  if (ftruncate(fileno(file), 0)) {
15984  ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
15985  res = AST_TEST_FAIL;
15986  break;
15987  }
15988 
15989  /* Make each change, in order, to the test mailbox */
15990  if (test_items[which].type == INT) {
15991  *((int *) test_items[which].location) = test_items[which].u.intval;
15992  } else if (test_items[which].type == FLAGVAL) {
15993  if (ast_test_flag(vmu, test_items[which].u.intval)) {
15994  ast_clear_flag(vmu, test_items[which].u.intval);
15995  } else {
15996  ast_set_flag(vmu, test_items[which].u.intval);
15997  }
15998  } else if (test_items[which].type == STATIC) {
15999  strcpy(test_items[which].location, test_items[which].u.strval);
16000  } else if (test_items[which].type == STRPTR) {
16001  test_items[which].location = test_items[which].u.strval;
16002  }
16003 
16004  make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL, NULL);
16005  rewind(file);
16006  while (fgets(buf, sizeof(buf), file)) {
16007  if (
16008  (strlen(buf) > 1 &&
16009 #ifdef IMAP_STORAGE
16010  buf[strlen(buf) - 2] != '\r'
16011 #else
16012  buf[strlen(buf) - 2] == '\r'
16013 #endif
16014  )
16015  || buf[strlen(buf) - 1] != '\n') {
16016  res = AST_TEST_FAIL;
16017  }
16018  }
16019  }
16020  fclose(file);
16021  free_user(vmu);
16022  force_reload_config(); /* Restore original config */
16023  return res;
16024 }
16025 
16026 AST_TEST_DEFINE(test_voicemail_load_config)
16027 {
16028  int res = AST_TEST_PASS;
16029  struct ast_vm_user *vmu;
16030  struct ast_config *cfg;
16031  char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
16032  int fd;
16033  FILE *file;
16034  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
16035 
16036  switch (cmd) {
16037  case TEST_INIT:
16038  info->name = "test_voicemail_load_config";
16039  info->category = "/apps/app_voicemail/";
16040  info->summary = "Test loading Voicemail config";
16041  info->description =
16042  "Verify that configuration is loaded consistently. "
16043  "This is to test regressions of ASTERISK-18838 where it was noticed that "
16044  "some options were loaded after the mailboxes were instantiated, causing "
16045  "those options not to be set correctly.";
16046  return AST_TEST_NOT_RUN;
16047  case TEST_EXECUTE:
16048  break;
16049  }
16050 
16051  /* build a config file by hand... */
16052  if ((fd = mkstemp(config_filename)) < 0) {
16053  return AST_TEST_FAIL;
16054  }
16055  if (!(file = fdopen(fd, "w"))) {
16056  close(fd);
16057  unlink(config_filename);
16058  return AST_TEST_FAIL;
16059  }
16060  fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
16061  fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
16062  fputs("00000002 => 9999,Mrs. Test\n", file);
16063  fclose(file);
16064 
16065  if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
16066  res = AST_TEST_FAIL;
16067  goto cleanup;
16068  }
16069 
16070  load_config_from_memory(1, cfg, NULL);
16071  ast_config_destroy(cfg);
16072 
16073 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
16074  ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
16075  u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
16076 
16077  AST_LIST_LOCK(&users);
16078  AST_LIST_TRAVERSE(&users, vmu, list) {
16079  if (!strcmp(vmu->mailbox, "00000001")) {
16080  if (0); /* trick to get CHECK to work */
16081  CHECK(vmu, callback, "othercontext")
16082  CHECK(vmu, locale, "nl_NL.UTF-8")
16083  CHECK(vmu, zonetag, "central")
16084  } else if (!strcmp(vmu->mailbox, "00000002")) {
16085  if (0); /* trick to get CHECK to work */
16086  CHECK(vmu, callback, "somecontext")
16087  CHECK(vmu, locale, "de_DE.UTF-8")
16088  CHECK(vmu, zonetag, "european")
16089  }
16090  }
16092 
16093 #undef CHECK
16094 
16095  /* Forcibly restore the original config, to reinitialize after test */
16096  force_reload_config(); /* this might say "Failed to load configuration file." */
16097 
16098 cleanup:
16099  unlink(config_filename);
16100  return res;
16101 }
16102 
16103 AST_TEST_DEFINE(test_voicemail_vm_info)
16104 {
16105  struct ast_vm_user *vmu;
16106  struct ast_channel *chan = NULL;
16107  const char testcontext[] = "test";
16108  const char testmailbox[] = "00000000";
16109  const char vminfo_cmd[] = "VM_INFO";
16110  char vminfo_buf[256], vminfo_args[256];
16111  int res = AST_TEST_PASS;
16112  int test_ret = 0;
16113  int test_counter = 0;
16114 
16115  struct {
16116  char *vminfo_test_args;
16117  char *vminfo_expected;
16118  int vminfo_ret;
16119  } test_items[] = {
16120  { "", "", -1 }, /* Missing argument */
16121  { "00000000@test,badparam", "", -1 }, /* Wrong argument */
16122  { "00000000@test", "", -1 }, /* Missing argument */
16123  { "00000000@test,exists", "1", 0 },
16124  { "11111111@test,exists", "0", 0 }, /* Invalid mailbox */
16125  { "00000000@test,email", "vm-info-test@example.net", 0 },
16126  { "11111111@test,email", "", 0 }, /* Invalid mailbox */
16127  { "00000000@test,fullname", "Test Framework Mailbox", 0 },
16128  { "00000000@test,pager", "vm-info-pager-test@example.net", 0 },
16129  { "00000000@test,locale", "en_US", 0 },
16130  { "00000000@test,tz", "central", 0 },
16131  { "00000000@test,language", "en", 0 },
16132  { "00000000@test,password", "9876", 0 },
16133  };
16134 
16135  switch (cmd) {
16136  case TEST_INIT:
16137  info->name = "test_voicemail_vm_info";
16138  info->category = "/apps/app_voicemail/";
16139  info->summary = "VM_INFO unit test";
16140  info->description =
16141  "This tests passing various parameters to VM_INFO";
16142  return AST_TEST_NOT_RUN;
16143  case TEST_EXECUTE:
16144  break;
16145  }
16146 
16147  if (!(chan = ast_dummy_channel_alloc())) {
16148  ast_test_status_update(test, "Unable to create dummy channel\n");
16149  return AST_TEST_FAIL;
16150  }
16151 
16152  if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
16153  !(vmu = find_or_create(testcontext, testmailbox))) {
16154  ast_test_status_update(test, "Cannot create vmu structure\n");
16155  chan = ast_channel_unref(chan);
16156  return AST_TEST_FAIL;
16157  }
16158 
16159  populate_defaults(vmu);
16160 
16161  vmu->email = ast_strdup("vm-info-test@example.net");
16162  if (!vmu->email) {
16163  ast_test_status_update(test, "Cannot create vmu email\n");
16164  chan = ast_channel_unref(chan);
16165  return AST_TEST_FAIL;
16166  }
16167  ast_copy_string(vmu->fullname, "Test Framework Mailbox", sizeof(vmu->fullname));
16168  ast_copy_string(vmu->pager, "vm-info-pager-test@example.net", sizeof(vmu->pager));
16169  ast_copy_string(vmu->language, "en", sizeof(vmu->language));
16170  ast_copy_string(vmu->zonetag, "central", sizeof(vmu->zonetag));
16171  ast_copy_string(vmu->locale, "en_US", sizeof(vmu->zonetag));
16172  ast_copy_string(vmu->password, "9876", sizeof(vmu->password));
16173 
16174  for (test_counter = 0; test_counter < ARRAY_LEN(test_items); test_counter++) {
16175  ast_copy_string(vminfo_args, test_items[test_counter].vminfo_test_args, sizeof(vminfo_args));
16176  test_ret = acf_vm_info(chan, vminfo_cmd, vminfo_args, vminfo_buf, sizeof(vminfo_buf));
16177  if (strcmp(vminfo_buf, test_items[test_counter].vminfo_expected)) {
16178  ast_test_status_update(test, "VM_INFO respose was: '%s', but expected: '%s'\n", vminfo_buf, test_items[test_counter].vminfo_expected);
16179  res = AST_TEST_FAIL;
16180  }
16181  if (!(test_ret == test_items[test_counter].vminfo_ret)) {
16182  ast_test_status_update(test, "VM_INFO return code was: '%i', but expected '%i'\n", test_ret, test_items[test_counter].vminfo_ret);
16183  res = AST_TEST_FAIL;
16184  }
16185  }
16186 
16187  chan = ast_channel_unref(chan);
16188  free_user(vmu);
16189  return res;
16190 }
16191 #endif /* defined(TEST_FRAMEWORK) */
16192 
16193 static const struct ast_vm_functions vm_table = {
16194  .module_version = VM_MODULE_VERSION,
16195  .module_name = AST_MODULE,
16196 
16197  .has_voicemail = has_voicemail,
16198  .inboxcount = inboxcount,
16199  .inboxcount2 = inboxcount2,
16200  .messagecount = messagecount,
16201  .copy_recording_to_vm = msg_create_from_file,
16202  .index_to_foldername = vm_index_to_foldername,
16203  .mailbox_snapshot_create = vm_mailbox_snapshot_create,
16204  .mailbox_snapshot_destroy = vm_mailbox_snapshot_destroy,
16205  .msg_move = vm_msg_move,
16206  .msg_remove = vm_msg_remove,
16207  .msg_forward = vm_msg_forward,
16208  .msg_play = vm_msg_play,
16209 };
16210 
16211 static const struct ast_vm_greeter_functions vm_greeter_table = {
16212  .module_version = VM_GREETER_MODULE_VERSION,
16213  .module_name = AST_MODULE,
16214 
16215  .sayname = vm_sayname,
16216 };
16217 
16218 static int reload(void)
16219 {
16220  return load_config(1);
16221 }
16222 
16223 static int unload_module(void)
16224 {
16225  int res;
16226 
16227  res = ast_unregister_application(voicemail_app);
16228  res |= ast_unregister_application(voicemailmain_app);
16229  res |= ast_unregister_application(vmauthenticate_app);
16230  res |= ast_unregister_application(playmsg_app);
16231  res |= ast_unregister_application(sayname_app);
16232  res |= ast_custom_function_unregister(&vm_info_acf);
16233  res |= ast_manager_unregister("VoicemailUsersList");
16234  res |= ast_manager_unregister("VoicemailUserStatus");
16235  res |= ast_manager_unregister("VoicemailRefresh");
16236  res |= ast_manager_unregister("VoicemailBoxSummary");
16237  res |= ast_manager_unregister("VoicemailMove");
16238  res |= ast_manager_unregister("VoicemailRemove");
16239  res |= ast_manager_unregister("VoicemailForward");
16240 #ifdef TEST_FRAMEWORK
16241  res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
16242  res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
16243  res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
16244  res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
16245  res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
16246  res |= AST_TEST_UNREGISTER(test_voicemail_vm_info);
16247 #endif
16248  ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
16249  ast_vm_unregister(vm_table.module_name);
16250  ast_vm_greeter_unregister(vm_greeter_table.module_name);
16251 #ifdef TEST_FRAMEWORK
16252  ast_uninstall_vm_test_functions();
16253 #endif
16254  ao2_ref(inprocess_container, -1);
16255 
16256  ao2_container_unregister("voicemail_alias_mailbox_mappings");
16257  ao2_cleanup(alias_mailbox_mappings);
16258  ao2_container_unregister("voicemail_mailbox_alias_mappings");
16259  ao2_cleanup(mailbox_alias_mappings);
16260 
16261  if (poll_thread != AST_PTHREADT_NULL)
16262  stop_poll_thread();
16263 
16264  mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
16265  ast_unload_realtime("voicemail");
16266  ast_unload_realtime("voicemail_data");
16267 
16268 #ifdef IMAP_STORAGE
16269  ast_mwi_state_callback_all(imap_close_subscribed_mailbox, NULL);
16270 #endif
16271  free_vm_users();
16272  free_vm_zones();
16273  return res;
16274 }
16275 
16276 static void print_mappings(void *v_obj, void *where, ao2_prnt_fn *prnt)
16277 {
16278  struct alias_mailbox_mapping *mapping = v_obj;
16279 
16280  if (!mapping) {
16281  return;
16282  }
16283  prnt(where, "Alias: %s Mailbox: %s", mapping->alias, mapping->mailbox);
16284 }
16285 
16286 /*!
16287  * \brief Load the module
16288  *
16289  * Module loading including tests for configuration or dependencies.
16290  * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
16291  * or AST_MODULE_LOAD_SUCCESS.
16292  *
16293  * If a dependency, allocation or environment variable fails tests, return AST_MODULE_LOAD_FAILURE.
16294  *
16295  * If the module can't load the configuration file, can't register as a provider or
16296  * has another issue not fatal to Asterisk itself, return AST_MODULE_LOAD_DECLINE.
16297  *
16298  * On success return AST_MODULE_LOAD_SUCCESS.
16299  */
16300 static int load_module(void)
16301 {
16302  int res;
16303  my_umask = umask(0);
16304  umask(my_umask);
16305 
16306  inprocess_container = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 573,
16307  inprocess_hash_fn, NULL, inprocess_cmp_fn);
16308  if (!inprocess_container) {
16309  return AST_MODULE_LOAD_DECLINE;
16310  }
16311 
16312  alias_mailbox_mappings = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, MAPPING_BUCKETS,
16313  alias_mailbox_mapping_hash_fn, NULL, alias_mailbox_mapping_cmp_fn);
16314  if (!alias_mailbox_mappings) {
16315  ast_log(LOG_ERROR, "Unable to create alias_mailbox_mappings container\n");
16316  ao2_cleanup(inprocess_container);
16317  return AST_MODULE_LOAD_DECLINE;
16318  }
16319  res = ao2_container_register("voicemail_alias_mailbox_mappings", alias_mailbox_mappings, print_mappings);
16320  if (res) {
16321  ast_log(LOG_ERROR, "Unable to register alias_mailbox_mappings container\n");
16322  ao2_cleanup(inprocess_container);
16323  ao2_cleanup(alias_mailbox_mappings);
16324  return AST_MODULE_LOAD_DECLINE;
16325  }
16326 
16327  mailbox_alias_mappings = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, MAPPING_BUCKETS,
16328  mailbox_alias_mapping_hash_fn, NULL, mailbox_alias_mapping_cmp_fn);
16329  if (!mailbox_alias_mappings) {
16330  ast_log(LOG_ERROR, "Unable to create mailbox_alias_mappings container\n");
16331  ao2_cleanup(inprocess_container);
16332  ao2_container_unregister("voicemail_alias_mailbox_mappings");
16333  ao2_cleanup(alias_mailbox_mappings);
16334  return AST_MODULE_LOAD_DECLINE;
16335  }
16336  res = ao2_container_register("voicemail_mailbox_alias_mappings", mailbox_alias_mappings, print_mappings);
16337  if (res) {
16338  ast_log(LOG_ERROR, "Unable to register mailbox_alias_mappings container\n");
16339  ao2_cleanup(inprocess_container);
16340  ao2_container_unregister("voicemail_alias_mailbox_mappings");
16341  ao2_cleanup(alias_mailbox_mappings);
16342  ao2_cleanup(mailbox_alias_mappings);
16343  return AST_MODULE_LOAD_DECLINE;
16344  }
16345 
16346  /* compute the location of the voicemail spool directory */
16347  snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
16348 
16349  if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
16350  ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor. MWI will not work\n");
16351  }
16352 
16353  if ((res = load_config(0))) {
16354  unload_module();
16355  return AST_MODULE_LOAD_DECLINE;
16356  }
16357 
16358  res = ast_register_application_xml(voicemail_app, vm_exec);
16359  res |= ast_register_application_xml(voicemailmain_app, vm_execmain);
16360  res |= ast_register_application_xml(vmauthenticate_app, vmauthenticate);
16361  res |= ast_register_application_xml(playmsg_app, vm_playmsgexec);
16362  res |= ast_register_application_xml(sayname_app, vmsayname_exec);
16363  res |= ast_custom_function_register(&vm_info_acf);
16364  res |= ast_manager_register_xml("VoicemailUsersList", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_list_voicemail_users);
16365  res |= ast_manager_register_xml("VoicemailUserStatus", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_status_voicemail_user);
16366  res |= ast_manager_register_xml("VoicemailRefresh", EVENT_FLAG_USER, manager_voicemail_refresh);
16367  res |= ast_manager_register_xml("VoicemailBoxSummary", EVENT_FLAG_CALL | EVENT_FLAG_REPORTING, manager_get_mailbox_summary);
16368  res |= ast_manager_register_xml("VoicemailMove", EVENT_FLAG_USER, manager_voicemail_move);
16369  res |= ast_manager_register_xml("VoicemailRemove", EVENT_FLAG_USER, manager_voicemail_remove);
16370  res |= ast_manager_register_xml("VoicemailForward", EVENT_FLAG_USER, manager_voicemail_forward);
16371 #ifdef TEST_FRAMEWORK
16372  res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
16373  res |= AST_TEST_REGISTER(test_voicemail_msgcount);
16374  res |= AST_TEST_REGISTER(test_voicemail_vmuser);
16375  res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
16376  res |= AST_TEST_REGISTER(test_voicemail_load_config);
16377  res |= AST_TEST_REGISTER(test_voicemail_vm_info);
16378 #endif
16379 
16380  if (res) {
16381  ast_log(LOG_ERROR, "Failure registering applications, functions or tests\n");
16382  unload_module();
16383  return AST_MODULE_LOAD_DECLINE;
16384  }
16385 
16386  /* ast_vm_register may return DECLINE if another module registered for vm */
16387  res = ast_vm_register(&vm_table);
16388  if (res) {
16389  ast_log(LOG_ERROR, "Failure registering as a voicemail provider\n");
16390  unload_module();
16391  return AST_MODULE_LOAD_DECLINE;
16392  }
16393 
16394  /* ast_vm_greeter_register may return DECLINE if another module registered as a greeter */
16395  res = ast_vm_greeter_register(&vm_greeter_table);
16396  if (res) {
16397  ast_log(LOG_ERROR, "Failure registering as a greeter provider\n");
16398  unload_module();
16399  return AST_MODULE_LOAD_DECLINE;
16400  }
16401 
16402  ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
16403 
16404 #ifdef TEST_FRAMEWORK
16405  ast_install_vm_test_functions(vm_test_create_user, vm_test_destroy_user);
16406 #endif
16407 
16408  ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
16409  ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
16410 
16411  return AST_MODULE_LOAD_SUCCESS;
16412 }
16413 
16414 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
16415 {
16416  int cmd = 0;
16417  char destination[80] = "";
16418  int retries = 0;
16419 
16420  if (!num) {
16421  ast_verb(3, "Destination number will be entered manually\n");
16422  while (retries < 3 && cmd != 't') {
16423  destination[1] = '\0';
16424  destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
16425  if (!cmd)
16426  destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
16427  if (!cmd)
16428  destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
16429  if (!cmd) {
16430  cmd = ast_waitfordigit(chan, 6000);
16431  if (cmd)
16432  destination[0] = cmd;
16433  }
16434  if (!cmd) {
16435  retries++;
16436  } else {
16437 
16438  if (cmd < 0)
16439  return 0;
16440  if (cmd == '*') {
16441  ast_verb(3, "User hit '*' to cancel outgoing call\n");
16442  return 0;
16443  }
16444  if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0)
16445  retries++;
16446  else
16447  cmd = 't';
16448  }
16449  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
16450  isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
16451  }
16452  if (retries >= 3) {
16453  return 0;
16454  }
16455 
16456  } else {
16457  ast_verb(3, "Destination number is CID number '%s'\n", num);
16458  ast_copy_string(destination, num, sizeof(destination));
16459  }
16460 
16461  if (!ast_strlen_zero(destination)) {
16462  if (destination[strlen(destination) -1 ] == '*')
16463  return 0;
16464  ast_verb(3, "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, ast_channel_context(chan));
16465  ast_channel_exten_set(chan, destination);
16466  ast_channel_context_set(chan, outgoing_context);
16467  ast_channel_priority_set(chan, 0);
16468  return 9;
16469  }
16470  return 0;
16471 }
16472 
16473 /*!
16474  * \brief The advanced options within a message.
16475  * \param chan
16476  * \param vmu
16477  * \param vms
16478  * \param msg
16479  * \param option
16480  * \param record_gain
16481  *
16482  * Provides handling for the play message envelope, call the person back, or reply to message.
16483  *
16484  * \return zero on success, -1 on error.
16485  */
16486 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)
16487 {
16488  int res = 0;
16489  char filename[PATH_MAX];
16490  struct ast_config *msg_cfg = NULL;
16491  const char *origtime, *context;
16492  char *name, *num;
16493  int retries = 0;
16494  char *cid;
16495  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
16496 
16497  vms->starting = 0;
16498 
16499  make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
16500 
16501  /* Retrieve info from VM attribute file */
16502  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
16503  RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
16504  msg_cfg = ast_config_load(filename, config_flags);
16505  DISPOSE(vms->curdir, vms->curmsg);
16506  if (!valid_config(msg_cfg)) {
16507  ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
16508  return 0;
16509  }
16510 
16511  if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
16512  ast_config_destroy(msg_cfg);
16513  return 0;
16514  }
16515 
16516  cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
16517 
16518  context = ast_variable_retrieve(msg_cfg, "message", "context");
16519  switch (option) {
16520  case 3: /* Play message envelope */
16521  if (!res) {
16522  res = play_message_datetime(chan, vmu, origtime, filename);
16523  }
16524  if (!res) {
16525  res = play_message_callerid(chan, vms, cid, context, 0, 1);
16526  }
16527 
16528  res = 't';
16529  break;
16530 
16531  case 2: /* Call back */
16532 
16533  if (ast_strlen_zero(cid))
16534  break;
16535 
16536  ast_callerid_parse(cid, &name, &num);
16537  while ((res > -1) && (res != 't')) {
16538  switch (res) {
16539  case '1':
16540  if (num) {
16541  /* Dial the CID number */
16542  res = dialout(chan, vmu, num, vmu->callback);
16543  if (res) {
16544  ast_config_destroy(msg_cfg);
16545  return 9;
16546  }
16547  } else {
16548  res = '2';
16549  }
16550  break;
16551 
16552  case '2':
16553  /* Want to enter a different number, can only do this if there's a dialout context for this user */
16554  if (!ast_strlen_zero(vmu->dialout)) {
16555  res = dialout(chan, vmu, NULL, vmu->dialout);
16556  if (res) {
16557  ast_config_destroy(msg_cfg);
16558  return 9;
16559  }
16560  } else {
16561  ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
16562  res = ast_play_and_wait(chan, "vm-sorry");
16563  }
16564  ast_config_destroy(msg_cfg);
16565  return res;
16566  case '*':
16567  res = 't';
16568  break;
16569  case '3':
16570  case '4':
16571  case '5':
16572  case '6':
16573  case '7':
16574  case '8':
16575  case '9':
16576  case '0':
16577 
16578  res = ast_play_and_wait(chan, "vm-sorry");
16579  retries++;
16580  break;
16581  default:
16582  if (num) {
16583  ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
16584  res = ast_play_and_wait(chan, "vm-num-i-have");
16585  if (!res)
16586  res = play_message_callerid(chan, vms, num, vmu->context, 1, 1);
16587  if (!res)
16588  res = ast_play_and_wait(chan, "vm-tocallnum");
16589  /* Only prompt for a caller-specified number if there is a dialout context specified */
16590  if (!ast_strlen_zero(vmu->dialout)) {
16591  if (!res)
16592  res = ast_play_and_wait(chan, "vm-calldiffnum");
16593  }
16594  } else {
16595  res = ast_play_and_wait(chan, "vm-nonumber");
16596  if (!ast_strlen_zero(vmu->dialout)) {
16597  if (!res)
16598  res = ast_play_and_wait(chan, "vm-toenternumber");
16599  }
16600  }
16601  if (!res) {
16602  res = ast_play_and_wait(chan, "vm-star-cancel");
16603  }
16604  if (!res) {
16605  res = ast_waitfordigit(chan, 6000);
16606  }
16607  if (!res) {
16608  retries++;
16609  if (retries > 3) {
16610  res = 't';
16611  }
16612  }
16613  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
16614  isprint(res) ? res : '?', isprint(res) ? res : '?');
16615  break;
16616 
16617  }
16618  if (res == 't')
16619  res = 0;
16620  else if (res == '*')
16621  res = -1;
16622  }
16623  break;
16624 
16625  case 1: /* Reply */
16626  /* Send reply directly to sender */
16627  if (ast_strlen_zero(cid))
16628  break;
16629 
16630  ast_callerid_parse(cid, &name, &num);
16631  if (!num) {
16632  ast_verb(3, "No CID number available, no reply sent\n");
16633  if (!res)
16634  res = ast_play_and_wait(chan, "vm-nonumber");
16635  ast_config_destroy(msg_cfg);
16636  return res;
16637  } else {
16638  struct ast_vm_user vmu2, *vmu3;
16639  memset(&vmu2, 0, sizeof(vmu2));
16640  vmu3 = find_user(&vmu2, vmu->context, num);
16641  if (vmu3) {
16642  struct leave_vm_options leave_options;
16643  char mailbox[AST_MAX_EXTENSION * 2 + 2];
16644  snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
16645 
16646  ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
16647 
16648  memset(&leave_options, 0, sizeof(leave_options));
16649  leave_options.record_gain = record_gain;
16650  leave_options.beeptone = "beep";
16651  res = leave_voicemail(chan, mailbox, &leave_options);
16652  if (!res)
16653  res = 't';
16654  ast_config_destroy(msg_cfg);
16655  free_user(vmu3);
16656  return res;
16657  } else {
16658  /* Sender has no mailbox, can't reply */
16659  ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
16660  ast_play_and_wait(chan, "vm-nobox");
16661  res = 't';
16662  ast_config_destroy(msg_cfg);
16663  return res;
16664  }
16665  }
16666  res = 0;
16667 
16668  break;
16669  }
16670 
16671  ast_config_destroy(msg_cfg);
16672 
16673 #ifndef IMAP_STORAGE
16674  if (!res) {
16675  make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
16676  vms->heard[msg] = 1;
16677  res = wait_file(chan, vms, vms->fn);
16678  }
16679 #endif
16680  return res;
16681 }
16682 
16683 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
16684  int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
16685  signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro)
16686 {
16687  /* Record message & let caller review or re-record it, or set options if applicable */
16688  int res = 0;
16689  int cmd = 0;
16690  int max_attempts = 3;
16691  int attempts = 0;
16692  int recorded = 0;
16693  int msg_exists = 0;
16694  signed char zero_gain = 0;
16695  char tempfile[PATH_MAX];
16696  char *acceptdtmf = "#";
16697  char *canceldtmf = "";
16698  int canceleddtmf = 0;
16699  SCOPE_ENTER(3, "%s: rf: %s fmt: %s type: %s vmu: %s\n",
16700  ast_channel_name(chan), recordfile, fmt, outsidecaller ? "msg" : "greeting",
16701  vmu->mailbox);
16702  /* Note that urgent and private are for flagging messages as such in the future */
16703 
16704  /* barf if no pointer passed to store duration in */
16705  if (duration == NULL) {
16706  SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING, "%s\n", "Error play_record_review called without duration pointer\n");
16707  }
16708 
16709  if (!outsidecaller)
16710  snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
16711  else
16712  ast_copy_string(tempfile, recordfile, sizeof(tempfile));
16713 
16714  cmd = '3'; /* Want to start by recording */
16715 
16716  while ((cmd >= 0) && (cmd != 't')) {
16717  switch (cmd) {
16718  case '1':
16719  if (!msg_exists) {
16720  /* In this case, 1 is to record a message */
16721  cmd = '3';
16722  break;
16723  } else {
16724  /* Otherwise 1 is to save the existing message */
16725  ast_verb(3, "Saving message as is\n");
16726  if (!outsidecaller) {
16727  ast_trace(-1, "Renaming greeting '%s' to '%s'\n", tempfile, recordfile);
16728  ast_filerename(tempfile, recordfile, NULL);
16729  }
16730  if (!forwardintro) {
16731  ast_stream_and_wait(chan, "vm-msgsaved", "");
16732  }
16733  if (!outsidecaller) {
16734  /* Saves to IMAP server only if imapgreeting=yes */
16735  ast_trace(-1, "Saving greeting '%s'\n", recordfile);
16736  SCOPE_CALL(-1, STORE, recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag, msg_id);
16737  SCOPE_CALL(-1, DISPOSE, recordfile, -1);
16738  }
16739  cmd = 't';
16740  SCOPE_EXIT_RTN_VALUE(res, "Message saved to %s\n", recordfile);
16741  }
16742  case '2':
16743  /* Review */
16744  ast_verb(3, "Reviewing the message\n");
16745  ast_trace(-1, "Reviewing '%s'\n", tempfile);
16746  cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
16747  break;
16748  case '3':
16749  msg_exists = 0;
16750  /* Record */
16751  if (recorded == 1)
16752  ast_verb(3, "Re-recording the message\n");
16753  else
16754  ast_verb(3, "Recording the message\n");
16755 
16756  if (recorded && outsidecaller) {
16757  if (forwardintro) {
16758  cmd = ast_play_and_wait(chan, "vm-record-prepend");
16759  } else {
16760  cmd = ast_play_and_wait(chan, INTRO);
16761  }
16762  cmd = ast_play_and_wait(chan, "beep");
16763  }
16764  if (cmd == -1) {
16765  /* User has hung up, no options to give */
16766  ast_filedelete(tempfile, NULL);
16767  SCOPE_EXIT_RTN_VALUE(cmd, "User hung up before message could be rerecorded. Deleted '%s'\n", tempfile);
16768  }
16769  recorded = 1;
16770  /* 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 */
16771  if (record_gain)
16772  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
16773  if (ast_test_flag(vmu, VM_OPERATOR))
16774  canceldtmf = "0";
16775  ast_trace(-1, "Recording '%s'\n", tempfile);
16776  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);
16777  if (strchr(canceldtmf, cmd)) {
16778  /* need this flag here to distinguish between pressing '0' during message recording or after */
16779  canceleddtmf = 1;
16780  }
16781  if (record_gain)
16782  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
16783  if (cmd == -1) {
16784  /* User has hung up, no options to give */
16785  if (!outsidecaller) {
16786  /* user was recording a greeting and they hung up, so let's delete the recording. */
16787  ast_filedelete(tempfile, NULL);
16788  }
16789  SCOPE_EXIT_RTN_VALUE(cmd, "User hung up after recording. %s %s\n",
16790  outsidecaller ? "Saved message " : "Deleted greeting \n", tempfile);
16791  }
16792  if (cmd == '0') {
16793  break;
16794  } else if (cmd == '*') {
16795  break;
16796 #if 0
16797  } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
16798  /* Message is too short */
16799  ast_verb(3, "Message too short\n");
16800  cmd = ast_play_and_wait(chan, "vm-tooshort");
16801  cmd = ast_filedelete(tempfile, NULL);
16802  break;
16803  } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
16804  /* Message is all silence */
16805  ast_verb(3, "Nothing recorded\n");
16806  cmd = ast_filedelete(tempfile, NULL);
16807  cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
16808  if (!cmd)
16809  cmd = ast_play_and_wait(chan, "vm-speakup");
16810  break;
16811 #endif
16812  } else {
16813  /* If all is well, a message exists */
16814  msg_exists = 1;
16815  cmd = 0;
16816  }
16817  break;
16818  case '4':
16819  if (outsidecaller && ast_test_flag(vmu, VM_MARK_URGENT)) { /* only mark vm messages */
16820  /* Mark Urgent */
16821  if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
16822  ast_verb(3, "marking message as Urgent\n");
16823  res = ast_play_and_wait(chan, "vm-marked-urgent");
16824  strcpy(flag, "Urgent");
16825  } else if (flag) {
16826  ast_verb(3, "UNmarking message as Urgent\n");
16827  res = ast_play_and_wait(chan, "vm-marked-nonurgent");
16828  strcpy(flag, "");
16829  } else {
16830  ast_play_and_wait(chan, "vm-sorry");
16831  }
16832  cmd = 0;
16833  } else {
16834  cmd = ast_play_and_wait(chan, "vm-sorry");
16835  }
16836  break;
16837  case '5':
16838  case '6':
16839  case '7':
16840  case '8':
16841  case '9':
16842  case '*':
16843  case '#':
16844  cmd = ast_play_and_wait(chan, "vm-sorry");
16845  break;
16846 #if 0
16847 /* XXX Commented out for the moment because of the dangers of deleting
16848  a message while recording (can put the message numbers out of sync) */
16849  case '*':
16850  /* Cancel recording, delete message, offer to take another message*/
16851  cmd = ast_play_and_wait(chan, "vm-deleted");
16852  cmd = ast_filedelete(tempfile, NULL);
16853  if (outsidecaller) {
16854  res = vm_exec(chan, NULL);
16855  return res;
16856  }
16857  else
16858  return 1;
16859 #endif
16860  case '0':
16861  if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
16862  cmd = ast_play_and_wait(chan, "vm-sorry");
16863  break;
16864  }
16865  if (msg_exists || recorded) {
16866  ast_trace(-1, "Reviewing '%s'\n", tempfile);
16867  cmd = ast_play_and_wait(chan, "vm-saveoper");
16868  if (!cmd)
16869  cmd = ast_waitfordigit(chan, 3000);
16870  if (cmd == '1') {
16871  ast_trace(-1, "Saving '%s' to '%s'\n", tempfile, recordfile);
16872  ast_filerename(tempfile, recordfile, NULL);
16873  ast_play_and_wait(chan, "vm-msgsaved");
16874  cmd = '0';
16875  } else if (cmd == '4') {
16876  if (flag) {
16877  ast_play_and_wait(chan, "vm-marked-urgent");
16878  strcpy(flag, "Urgent");
16879  }
16880  ast_play_and_wait(chan, "vm-msgsaved");
16881  cmd = '0';
16882  } else {
16883  ast_trace(-1, "Deleting '%s'\n", tempfile);
16884  ast_play_and_wait(chan, "vm-deleted");
16885  SCOPE_CALL(-1, DELETE, tempfile, -1, tempfile, vmu);
16886  SCOPE_CALL(-1, DISPOSE, tempfile, -1);
16887  cmd = '0';
16888  }
16889  }
16890  return cmd;
16891  default:
16892  /* If the caller is an outside caller and the review option is enabled or it's forward intro
16893  allow them to review the message, but let the owner of the box review
16894  their OGM's */
16895  if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW) && !forwardintro) {
16896  SCOPE_EXIT_RTN_VALUE(cmd, "Done. Outside caller, review not set, no forwardintro\n");
16897  }
16898  if (msg_exists) {
16899  cmd = ast_play_and_wait(chan, "vm-review");
16900  if (!cmd && outsidecaller && ast_test_flag(vmu, VM_MARK_URGENT)) {
16901  if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
16902  cmd = ast_play_and_wait(chan, "vm-review-urgent");
16903  } else if (flag) {
16904  cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
16905  }
16906  }
16907  } else {
16908  cmd = ast_play_and_wait(chan, "vm-torerecord");
16909  if (!cmd)
16910  cmd = ast_waitfordigit(chan, 600);
16911  }
16912 
16913  if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
16914  cmd = ast_play_and_wait(chan, "vm-reachoper");
16915  if (!cmd)
16916  cmd = ast_waitfordigit(chan, 600);
16917  }
16918 #if 0
16919  if (!cmd)
16920  cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
16921 #endif
16922  if (!cmd)
16923  cmd = ast_waitfordigit(chan, 6000);
16924  if (!cmd) {
16925  attempts++;
16926  }
16927  if (attempts > max_attempts) {
16928  cmd = 't';
16929  }
16930  }
16931  }
16932  if (!outsidecaller && (cmd == -1 || cmd == 't')) {
16933  /* Hang up or timeout, so delete the recording. */
16934  ast_trace(-1, "Deleting '%s' on hangup or timeout\n", tempfile);
16935  ast_filedelete(tempfile, NULL);
16936  }
16937 
16938  if (cmd != 't' && outsidecaller)
16939  ast_play_and_wait(chan, "vm-goodbye");
16940 
16941  SCOPE_EXIT_RTN_VALUE(cmd, "Done\n");
16942 }
16943 
16944 static struct ast_vm_msg_snapshot *vm_msg_snapshot_alloc(void)
16945 {
16946  struct ast_vm_msg_snapshot *msg_snapshot;
16947 
16948  if (!(msg_snapshot = ast_calloc(1, sizeof(*msg_snapshot)))) {
16949  return NULL;
16950  }
16951 
16952  if (ast_string_field_init(msg_snapshot, 512)) {
16953  ast_free(msg_snapshot);
16954  return NULL;
16955  }
16956 
16957  return msg_snapshot;
16958 }
16959 
16960 static struct ast_vm_msg_snapshot *vm_msg_snapshot_destroy(struct ast_vm_msg_snapshot *msg_snapshot)
16961 {
16962  ast_string_field_free_memory(msg_snapshot);
16963  ast_free(msg_snapshot);
16964 
16965  return NULL;
16966 }
16967 
16968 #ifdef TEST_FRAMEWORK
16969 
16970 static int vm_test_destroy_user(const char *context, const char *mailbox)
16971 {
16972  struct ast_vm_user *vmu;
16973 
16974  AST_LIST_LOCK(&users);
16975  AST_LIST_TRAVERSE_SAFE_BEGIN(&users, vmu, list) {
16976  if (!strcmp(context, vmu->context)
16977  && !strcmp(mailbox, vmu->mailbox)) {
16979  ast_free(vmu);
16980  break;
16981  }
16982  }
16985  return 0;
16986 }
16987 
16988 static int vm_test_create_user(const char *context, const char *mailbox)
16989 {
16990  struct ast_vm_user *vmu;
16991 
16992  if (!(vmu = find_or_create(context, mailbox))) {
16993  return -1;
16994  }
16995  populate_defaults(vmu);
16996  return 0;
16997 }
16998 
16999 #endif
17000 
17001 /*!
17002  * \brief Create and store off all the msgs in an open mailbox
17003  *
17004  * \note TODO XXX This function should work properly for all
17005  * voicemail storage options, but is far more expensive for
17006  * ODBC at the moment. This is because the RETRIEVE macro
17007  * not only pulls out the message's meta data file from the
17008  * database, but also the actual audio for each message, temporarily
17009  * writing it to the file system. This is an area that needs
17010  * to be made more efficient.
17011  */
17012 static int vm_msg_snapshot_create(struct ast_vm_user *vmu,
17013  struct vm_state *vms,
17014  struct ast_vm_mailbox_snapshot *mailbox_snapshot,
17015  int snapshot_index,
17016  int mailbox_index,
17017  int descending,
17018  enum ast_vm_snapshot_sort_val sort_val)
17019 {
17020  struct ast_vm_msg_snapshot *msg_snapshot;
17021  struct ast_vm_msg_snapshot *msg_snapshot_tmp;
17022  struct ast_config *msg_cfg;
17023  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17024  char filename[PATH_MAX];
17025  const char *value;
17026 
17027  for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
17028  int inserted = 0;
17029  /* Find the msg */
17030  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
17031  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
17032  RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
17033  msg_cfg = ast_config_load(filename, config_flags);
17034  if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17035  DISPOSE(vms->curdir, vms->curmsg);
17036  continue;
17037  }
17038 
17039  /* Create the snapshot object */
17040  if (!(msg_snapshot = vm_msg_snapshot_alloc())) {
17041  ast_config_destroy(msg_cfg);
17042  return -1;
17043  }
17044 
17045  /* Fill in the snapshot object */
17046  if ((value = ast_variable_retrieve(msg_cfg, "message", "msg_id"))) {
17047  ast_string_field_set(msg_snapshot, msg_id, value);
17048  } else {
17049  /* Message snapshots *really* should have a
17050  * message ID. Add one to the message config
17051  * if it does not already exist
17052  */
17053  char id[MSG_ID_LEN];
17054  if (!(add_message_id(msg_cfg, vms->curdir, vms->curmsg,
17055  filename, id, sizeof(id), vmu, mailbox_index))) {
17056  ast_string_field_set(msg_snapshot, msg_id, id);
17057  } else {
17058  ast_log(LOG_WARNING, "Unable to create a message ID for message %s/%d\n", vms->curdir, vms->curmsg);
17059  }
17060  }
17061  if ((value = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
17062  ast_string_field_set(msg_snapshot, callerid, value);
17063  }
17064  if ((value = ast_variable_retrieve(msg_cfg, "message", "callerchan"))) {
17065  ast_string_field_set(msg_snapshot, callerchan, value);
17066  }
17067  if ((value = ast_variable_retrieve(msg_cfg, "message", "exten"))) {
17068  ast_string_field_set(msg_snapshot, exten, value);
17069  }
17070  if ((value = ast_variable_retrieve(msg_cfg, "message", "origdate"))) {
17071  ast_string_field_set(msg_snapshot, origdate, value);
17072  }
17073  if ((value = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
17074  ast_string_field_set(msg_snapshot, origtime, value);
17075  }
17076  if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17077  ast_string_field_set(msg_snapshot, duration, value);
17078  }
17079  if ((value = ast_variable_retrieve(msg_cfg, "message", "flag"))) {
17080  ast_string_field_set(msg_snapshot, flag, value);
17081  }
17082  msg_snapshot->msg_number = vms->curmsg;
17083  ast_string_field_set(msg_snapshot, folder_name, mailbox_folders[mailbox_index]);
17084 
17085  /* store msg snapshot in mailbox snapshot */
17086  switch (sort_val) {
17087  default:
17088  case AST_VM_SNAPSHOT_SORT_BY_ID:
17089  if (descending) {
17090  AST_LIST_INSERT_HEAD(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17091  } else {
17092  AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17093  }
17094  inserted = 1;
17095  break;
17096  case AST_VM_SNAPSHOT_SORT_BY_TIME:
17097  AST_LIST_TRAVERSE_SAFE_BEGIN(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot_tmp, msg) {
17098  int val = strcmp(msg_snapshot->origtime, msg_snapshot_tmp->origtime);
17099  if (descending && val >= 0) {
17100  AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
17101  inserted = 1;
17102  break;
17103  } else if (!descending && val <= 0) {
17104  AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
17105  inserted = 1;
17106  break;
17107  }
17108  }
17110  break;
17111  }
17112 
17113  if (!inserted) {
17114  AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17115  }
17116 
17117  mailbox_snapshot->total_msg_num++;
17118 
17119  /* cleanup configs and msg */
17120  ast_config_destroy(msg_cfg);
17121  DISPOSE(vms->curdir, vms->curmsg);
17122  }
17123 
17124  return 0;
17125 }
17126 
17127 static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_create(const char *mailbox,
17128  const char *context,
17129  const char *folder,
17130  int descending,
17131  enum ast_vm_snapshot_sort_val sort_val,
17132  int combine_INBOX_and_OLD)
17133 {
17134  struct ast_vm_mailbox_snapshot *mailbox_snapshot;
17135  struct vm_state vms;
17136  struct ast_vm_user *vmu = NULL, vmus;
17137  int res;
17138  int i;
17139  int this_index_only = -1;
17140  int open = 0;
17141  int inbox_index = get_folder_by_name("INBOX");
17142  int old_index = get_folder_by_name("Old");
17143  int urgent_index = get_folder_by_name("Urgent");
17144 
17145  if (ast_strlen_zero(mailbox)) {
17146  ast_log(LOG_WARNING, "Cannot create a mailbox snapshot since no mailbox was specified\n");
17147  return NULL;
17148  }
17149 
17150  memset(&vmus, 0, sizeof(vmus));
17151 
17152  if (!(ast_strlen_zero(folder))) {
17153  /* find the folder index */
17154  for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
17155  if (!strcasecmp(mailbox_folders[i], folder)) {
17156  this_index_only = i;
17157  break;
17158  }
17159  }
17160  if (this_index_only == -1) {
17161  /* Folder was specified and it did not match any folder in our list */
17162  return NULL;
17163  }
17164  }
17165 
17166  if (!(vmu = find_user(&vmus, context, mailbox))) {
17167  ast_log(AST_LOG_WARNING, "Failed to create mailbox snapshot for unknown voicemail user %s@%s\n", mailbox, context);
17168  return NULL;
17169  }
17170 
17171  if (!(mailbox_snapshot = ast_calloc(1, sizeof(*mailbox_snapshot)))) {
17172  ast_log(AST_LOG_ERROR, "Failed to allocate memory for mailbox snapshot\n");
17173  free_user(vmu);
17174  return NULL;
17175  }
17176 
17177  if (!(mailbox_snapshot->snapshots = ast_calloc(ARRAY_LEN(mailbox_folders), sizeof(*mailbox_snapshot->snapshots)))) {
17178  ast_free(mailbox_snapshot);
17179  free_user(vmu);
17180  return NULL;
17181  }
17182 
17183  mailbox_snapshot->folders = ARRAY_LEN(mailbox_folders);
17184 
17185  for (i = 0; i < mailbox_snapshot->folders; i++) {
17186  int msg_folder_index = i;
17187 
17188  /* We want this message in the snapshot if any of the following:
17189  * No folder was specified.
17190  * The specified folder matches the current folder.
17191  * The specified folder is INBOX AND we were asked to combine messages AND the current folder is either Old or Urgent.
17192  */
17193  if (!(this_index_only == -1 || this_index_only == i || (this_index_only == inbox_index && combine_INBOX_and_OLD && (i == old_index || i == urgent_index)))) {
17194  continue;
17195  }
17196 
17197  /* Make sure that Old or Urgent messages are marked as being in INBOX. */
17198  if (combine_INBOX_and_OLD && (i == old_index || i == urgent_index)) {
17199  msg_folder_index = inbox_index;
17200  }
17201 
17202  memset(&vms, 0, sizeof(vms));
17203  ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17204  vms.lastmsg = -1;
17205  open = 0;
17206 
17207  /* open the mailbox state */
17208  if ((res = open_mailbox(&vms, vmu, i)) < 0) {
17209  ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17210  goto snapshot_cleanup;
17211  }
17212  open = 1;
17213 
17214  /* Iterate through each msg, storing off info */
17215  if (vms.lastmsg != -1) {
17216  if ((vm_msg_snapshot_create(vmu, &vms, mailbox_snapshot, msg_folder_index, i, descending, sort_val))) {
17217  ast_log(LOG_WARNING, "Failed to create msg snapshots for %s@%s\n", mailbox, context);
17218  goto snapshot_cleanup;
17219  }
17220  }
17221 
17222  /* close mailbox */
17223  if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17224  goto snapshot_cleanup;
17225  }
17226  open = 0;
17227  }
17228 
17229 snapshot_cleanup:
17230  if (vmu && open) {
17231  close_mailbox(&vms, vmu);
17232  }
17233 
17234 #ifdef IMAP_STORAGE
17235  if (vmu) {
17236  vmstate_delete(&vms);
17237  }
17238 #endif
17239 
17240  free_user(vmu);
17241  return mailbox_snapshot;
17242 }
17243 
17244 static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot)
17245 {
17246  int i;
17247  struct ast_vm_msg_snapshot *msg_snapshot;
17248 
17249  for (i = 0; i < mailbox_snapshot->folders; i++) {
17250  while ((msg_snapshot = AST_LIST_REMOVE_HEAD(&mailbox_snapshot->snapshots[i], msg))) {
17251  msg_snapshot = vm_msg_snapshot_destroy(msg_snapshot);
17252  }
17253  }
17254  ast_free(mailbox_snapshot->snapshots);
17255  ast_free(mailbox_snapshot);
17256  return NULL;
17257 }
17258 
17259 /*!
17260  * \brief common bounds checking and existence check for Voicemail API functions.
17261  *
17262  * \details
17263  * This is called by vm_msg_move, vm_msg_remove, and vm_msg_forward to
17264  * ensure that data passed in are valid. This ensures that given the
17265  * desired message IDs, they can be found.
17266  *
17267  * \param vms The voicemail state corresponding to an open mailbox
17268  * \param msg_ids An array of message identifiers
17269  * \param num_msgs The number of identifiers in msg_ids
17270  * \param[out] msg_nums The message indexes corresponding to the given
17271  * \param vmu
17272  * message IDs
17273  * \pre vms must have open_mailbox() called on it prior to this function.
17274  *
17275  * \retval -1 Failure
17276  * \retval 0 Success
17277  */
17278 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)
17279 {
17280  int i;
17281  int res = 0;
17282  for (i = 0; i < num_msgs; ++i) {
17283  const char *msg_id = msg_ids[i];
17284  int found = 0;
17285  for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
17286  const char *other_msg_id;
17287  char filename[PATH_MAX];
17288  struct ast_config *msg_cfg;
17289  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17290 
17291  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
17292  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
17293  RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
17294  msg_cfg = ast_config_load(filename, config_flags);
17295  if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17296  DISPOSE(vms->curdir, vms->curmsg);
17297  res = -1;
17298  goto done;
17299  }
17300 
17301  other_msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
17302 
17303  if (!ast_strlen_zero(other_msg_id) && !strcmp(other_msg_id, msg_id)) {
17304  /* Message found. We can get out of this inner loop
17305  * and move on to the next message to find
17306  */
17307  found = 1;
17308  msg_nums[i] = vms->curmsg;
17309  ast_config_destroy(msg_cfg);
17310  DISPOSE(vms->curdir, vms->curmsg);
17311  break;
17312  }
17313  ast_config_destroy(msg_cfg);
17314  DISPOSE(vms->curdir, vms->curmsg);
17315  }
17316  if (!found) {
17317  /* If we can't find one of the message IDs requested, then OH NO! */
17318  res = -1;
17319  goto done;
17320  }
17321  }
17322 
17323 done:
17324  return res;
17325 }
17326 
17327 static void notify_new_state(struct ast_vm_user *vmu)
17328 {
17329  int new = 0, old = 0, urgent = 0;
17330  char ext_context[1024];
17331 
17332  snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
17333  run_externnotify(vmu->context, vmu->mailbox, NULL);
17334  ast_app_inboxcount2(ext_context, &urgent, &new, &old);
17335  queue_mwi_event(NULL, ext_context, urgent, new, old);
17336 }
17337 
17338 static int vm_msg_forward(const char *from_mailbox,
17339  const char *from_context,
17340  const char *from_folder,
17341  const char *to_mailbox,
17342  const char *to_context,
17343  const char *to_folder,
17344  size_t num_msgs,
17345  const char *msg_ids [],
17346  int delete_old)
17347 {
17348  struct vm_state from_vms;
17349  struct ast_vm_user *vmu = NULL, vmus;
17350  struct ast_vm_user *to_vmu = NULL, to_vmus;
17351  struct ast_config *msg_cfg;
17352  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17353  char filename[PATH_MAX];
17354  int from_folder_index;
17355  int open = 0;
17356  int res = 0;
17357  int i;
17358  int *msg_nums;
17359 
17360  if (ast_strlen_zero(from_mailbox) || ast_strlen_zero(to_mailbox)) {
17361  ast_log(LOG_WARNING, "Cannot forward message because either the from or to mailbox was not specified\n");
17362  return -1;
17363  }
17364 
17365  if (!num_msgs) {
17366  ast_log(LOG_WARNING, "Invalid number of messages specified to forward: %zu\n", num_msgs);
17367  return -1;
17368  }
17369 
17370  if (ast_strlen_zero(from_folder) || ast_strlen_zero(to_folder)) {
17371  ast_log(LOG_WARNING, "Cannot forward message because the from_folder or to_folder was not specified\n");
17372  return -1;
17373  }
17374 
17375  memset(&vmus, 0, sizeof(vmus));
17376  memset(&to_vmus, 0, sizeof(to_vmus));
17377  memset(&from_vms, 0, sizeof(from_vms));
17378 
17379  from_folder_index = get_folder_by_name(from_folder);
17380  if (from_folder_index == -1) {
17381  return -1;
17382  }
17383 
17384  if (get_folder_by_name(to_folder) == -1) {
17385  return -1;
17386  }
17387 
17388  if (!(vmu = find_user(&vmus, from_context, from_mailbox))) {
17389  ast_log(LOG_WARNING, "Can't find voicemail user to forward from (%s@%s)\n", from_mailbox, from_context);
17390  return -1;
17391  }
17392 
17393  if (!(to_vmu = find_user(&to_vmus, to_context, to_mailbox))) {
17394  ast_log(LOG_WARNING, "Can't find voicemail user to forward to (%s@%s)\n", to_mailbox, to_context);
17395  free_user(vmu);
17396  return -1;
17397  }
17398 
17399  ast_copy_string(from_vms.username, from_mailbox, sizeof(from_vms.username));
17400  from_vms.lastmsg = -1;
17401  open = 0;
17402 
17403  /* open the mailbox state */
17404  if ((res = open_mailbox(&from_vms, vmu, from_folder_index)) < 0) {
17405  ast_log(LOG_WARNING, "Could not open mailbox %s\n", from_mailbox);
17406  res = -1;
17407  goto vm_forward_cleanup;
17408  }
17409 
17410  open = 1;
17411 
17412  if ((from_vms.lastmsg + 1) < num_msgs) {
17413  ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", from_folder, num_msgs);
17414  res = -1;
17415  goto vm_forward_cleanup;
17416  }
17417 
17418  msg_nums = ast_alloca(sizeof(int) * num_msgs);
17419 
17420  if ((res = message_range_and_existence_check(&from_vms, msg_ids, num_msgs, msg_nums, vmu) < 0)) {
17421  goto vm_forward_cleanup;
17422  }
17423 
17424  /* Now we actually forward the messages */
17425  for (i = 0; i < num_msgs; i++) {
17426  int cur_msg = msg_nums[i];
17427  int duration = 0;
17428  const char *value;
17429 
17430  make_file(from_vms.fn, sizeof(from_vms.fn), from_vms.curdir, cur_msg);
17431  snprintf(filename, sizeof(filename), "%s.txt", from_vms.fn);
17432  RETRIEVE(from_vms.curdir, cur_msg, vmu->mailbox, vmu->context);
17433  msg_cfg = ast_config_load(filename, config_flags);
17434  /* XXX This likely will not fail since we previously ensured that the
17435  * message we are looking for exists. However, there still could be some
17436  * circumstance where this fails, so atomicity is not guaranteed.
17437  */
17438  if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17439  DISPOSE(from_vms.curdir, cur_msg);
17440  continue;
17441  }
17442  if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17443  duration = atoi(value);
17444  }
17445 
17446  copy_message(NULL, vmu, from_folder_index, cur_msg, duration, to_vmu, vmfmts, from_vms.curdir, "", to_folder);
17447 
17448  if (delete_old) {
17449  from_vms.deleted[cur_msg] = 1;
17450  }
17451  ast_config_destroy(msg_cfg);
17452  DISPOSE(from_vms.curdir, cur_msg);
17453  }
17454 
17455  /* close mailbox */
17456  if ((res = close_mailbox(&from_vms, vmu) == ERROR_LOCK_PATH)) {
17457  res = -1;
17458  goto vm_forward_cleanup;
17459  }
17460  open = 0;
17461 
17462 vm_forward_cleanup:
17463  if (vmu && open) {
17464  close_mailbox(&from_vms, vmu);
17465  }
17466 #ifdef IMAP_STORAGE
17467  if (vmu) {
17468  vmstate_delete(&from_vms);
17469  }
17470 #endif
17471 
17472  if (!res) {
17473  notify_new_state(to_vmu);
17474  }
17475 
17476  free_user(vmu);
17477  free_user(to_vmu);
17478  return res;
17479 }
17480 
17481 static int vm_msg_move(const char *mailbox,
17482  const char *context,
17483  size_t num_msgs,
17484  const char *oldfolder,
17485  const char *old_msg_ids [],
17486  const char *newfolder)
17487 {
17488  struct vm_state vms;
17489  struct ast_vm_user *vmu = NULL, vmus;
17490  int old_folder_index;
17491  int new_folder_index;
17492  int open = 0;
17493  int res = 0;
17494  int i;
17495  int *old_msg_nums;
17496 
17497  if (ast_strlen_zero(mailbox)) {
17498  ast_log(LOG_WARNING, "Cannot move message because no mailbox was specified\n");
17499  return -1;
17500  }
17501 
17502  if (!num_msgs) {
17503  ast_log(LOG_WARNING, "Invalid number of messages specified to move: %zu\n", num_msgs);
17504  return -1;
17505  }
17506 
17507  if (ast_strlen_zero(oldfolder) || ast_strlen_zero(newfolder)) {
17508  ast_log(LOG_WARNING, "Cannot move message because either oldfolder or newfolder was not specified\n");
17509  return -1;
17510  }
17511 
17512  old_folder_index = get_folder_by_name(oldfolder);
17513  new_folder_index = get_folder_by_name(newfolder);
17514 
17515  memset(&vmus, 0, sizeof(vmus));
17516  memset(&vms, 0, sizeof(vms));
17517 
17518  if (old_folder_index == -1 || new_folder_index == -1) {
17519  return -1;
17520  }
17521 
17522  if (!(vmu = find_user(&vmus, context, mailbox))) {
17523  return -1;
17524  }
17525 
17526  ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17527  vms.lastmsg = -1;
17528  open = 0;
17529 
17530  /* open the mailbox state */
17531  if ((res = open_mailbox(&vms, vmu, old_folder_index)) < 0) {
17532  ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17533  res = -1;
17534  goto vm_move_cleanup;
17535  }
17536 
17537  open = 1;
17538 
17539  if ((vms.lastmsg + 1) < num_msgs) {
17540  ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", oldfolder, num_msgs);
17541  res = -1;
17542  goto vm_move_cleanup;
17543  }
17544 
17545  old_msg_nums = ast_alloca(sizeof(int) * num_msgs);
17546 
17547  if ((res = message_range_and_existence_check(&vms, old_msg_ids, num_msgs, old_msg_nums, vmu)) < 0) {
17548  goto vm_move_cleanup;
17549  }
17550 
17551  /* Now actually move the message */
17552  for (i = 0; i < num_msgs; ++i) {
17553  if (save_to_folder(vmu, &vms, old_msg_nums[i], new_folder_index, NULL, 0)) {
17554  res = -1;
17555  goto vm_move_cleanup;
17556  }
17557  vms.deleted[old_msg_nums[i]] = 1;
17558  }
17559 
17560  /* close mailbox */
17561  if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17562  res = -1;
17563  goto vm_move_cleanup;
17564  }
17565  open = 0;
17566 
17567 vm_move_cleanup:
17568  if (vmu && open) {
17569  close_mailbox(&vms, vmu);
17570  }
17571 #ifdef IMAP_STORAGE
17572  if (vmu) {
17573  vmstate_delete(&vms);
17574  }
17575 #endif
17576 
17577  if (!res) {
17578  notify_new_state(vmu);
17579  }
17580 
17581  free_user(vmu);
17582  return res;
17583 }
17584 
17585 static int vm_msg_remove(const char *mailbox,
17586  const char *context,
17587  size_t num_msgs,
17588  const char *folder,
17589  const char *msgs[])
17590 {
17591  struct vm_state vms;
17592  struct ast_vm_user *vmu = NULL, vmus;
17593  int folder_index;
17594  int open = 0;
17595  int res = 0;
17596  int i;
17597  int *msg_nums;
17598 
17599  if (ast_strlen_zero(mailbox)) {
17600  ast_log(LOG_WARNING, "Cannot remove message because no mailbox was specified\n");
17601  return -1;
17602  }
17603 
17604  if (!num_msgs) {
17605  ast_log(LOG_WARNING, "Invalid number of messages specified to remove: %zu\n", num_msgs);
17606  return -1;
17607  }
17608 
17609  if (ast_strlen_zero(folder)) {
17610  ast_log(LOG_WARNING, "Cannot remove message because no folder was specified\n");
17611  return -1;
17612  }
17613 
17614  memset(&vmus, 0, sizeof(vmus));
17615  memset(&vms, 0, sizeof(vms));
17616 
17617  folder_index = get_folder_by_name(folder);
17618  if (folder_index == -1) {
17619  ast_log(LOG_WARNING, "Could not remove msgs from unknown folder %s\n", folder);
17620  return -1;
17621  }
17622 
17623  if (!(vmu = find_user(&vmus, context, mailbox))) {
17624  ast_log(LOG_WARNING, "Can't find voicemail user to remove msg from (%s@%s)\n", mailbox, context);
17625  return -1;
17626  }
17627 
17628  ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17629  vms.lastmsg = -1;
17630  open = 0;
17631 
17632  /* open the mailbox state */
17633  if ((res = open_mailbox(&vms, vmu, folder_index)) < 0) {
17634  ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17635  res = -1;
17636  goto vm_remove_cleanup;
17637  }
17638 
17639  open = 1;
17640 
17641  if ((vms.lastmsg + 1) < num_msgs) {
17642  ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", folder, num_msgs);
17643  res = -1;
17644  goto vm_remove_cleanup;
17645  }
17646 
17647  msg_nums = ast_alloca(sizeof(int) * num_msgs);
17648 
17649  if ((res = message_range_and_existence_check(&vms, msgs, num_msgs, msg_nums, vmu)) < 0) {
17650  goto vm_remove_cleanup;
17651  }
17652 
17653  for (i = 0; i < num_msgs; i++) {
17654  vms.deleted[msg_nums[i]] = 1;
17655  }
17656 
17657  /* close mailbox */
17658  if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17659  res = -1;
17660  ast_log(AST_LOG_ERROR, "Failed to close mailbox folder %s while removing msgs\n", folder);
17661  goto vm_remove_cleanup;
17662  }
17663  open = 0;
17664 
17665 vm_remove_cleanup:
17666  if (vmu && open) {
17667  close_mailbox(&vms, vmu);
17668  }
17669 #ifdef IMAP_STORAGE
17670  if (vmu) {
17671  vmstate_delete(&vms);
17672  }
17673 #endif
17674 
17675  if (!res) {
17676  notify_new_state(vmu);
17677  }
17678 
17679  free_user(vmu);
17680  return res;
17681 }
17682 
17683 static int vm_msg_play(struct ast_channel *chan,
17684  const char *mailbox,
17685  const char *context,
17686  const char *folder,
17687  const char *msg_id,
17688  ast_vm_msg_play_cb cb)
17689 {
17690  struct vm_state vms;
17691  struct ast_vm_user *vmu = NULL, vmus;
17692  int res = 0;
17693  int open = 0;
17694  int i;
17695  char filename[PATH_MAX];
17696  struct ast_config *msg_cfg;
17697  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17698  int duration = 0;
17699  const char *value;
17700 
17701  if (ast_strlen_zero(mailbox)) {
17702  ast_log(LOG_WARNING, "Cannot play message because no mailbox was specified\n");
17703  return -1;
17704  }
17705 
17706  if (ast_strlen_zero(folder)) {
17707  ast_log(LOG_WARNING, "Cannot play message because no folder was specified\n");
17708  return -1;
17709  }
17710 
17711  if (ast_strlen_zero(msg_id)) {
17712  ast_log(LOG_WARNING, "Cannot play message because no message number was specified\n");
17713  return -1;
17714  }
17715 
17716  memset(&vmus, 0, sizeof(vmus));
17717  memset(&vms, 0, sizeof(vms));
17718 
17719  if (ast_strlen_zero(context)) {
17720  context = "default";
17721  }
17722 
17723  if (!(vmu = find_user(&vmus, context, mailbox))) {
17724  return -1;
17725  }
17726 
17727  i = get_folder_by_name(folder);
17728  ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17729  vms.lastmsg = -1;
17730  if ((res = open_mailbox(&vms, vmu, i)) < 0) {
17731  ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17732  goto play2_msg_cleanup;
17733  }
17734  open = 1;
17735 
17736  if (message_range_and_existence_check(&vms, &msg_id, 1, &vms.curmsg, vmu)) {
17737  res = -1;
17738  goto play2_msg_cleanup;
17739  }
17740 
17741  /* Find the msg */
17742  make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
17743  snprintf(filename, sizeof(filename), "%s.txt", vms.fn);
17744  RETRIEVE(vms.curdir, vms.curmsg, vmu->mailbox, vmu->context);
17745 
17746  msg_cfg = ast_config_load(filename, config_flags);
17747  if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17748  DISPOSE(vms.curdir, vms.curmsg);
17749  res = -1;
17750  goto play2_msg_cleanup;
17751  }
17752  if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17753  duration = atoi(value);
17754  }
17755  ast_config_destroy(msg_cfg);
17756 
17757 #ifdef IMAP_STORAGE
17758  /*IMAP storage stores any prepended message from a forward
17759  * as a separate file from the rest of the message
17760  */
17761  if (!ast_strlen_zero(vms.introfn) && ast_fileexists(vms.introfn, NULL, NULL) > 0) {
17762  wait_file(chan, &vms, vms.introfn);
17763  }
17764 #endif
17765  if (cb) {
17766  cb(chan, vms.fn, duration);
17767  } else if ((wait_file(chan, &vms, vms.fn)) < 0) {
17768  ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms.fn);
17769  } else {
17770  res = 0;
17771  }
17772 
17773  vms.heard[vms.curmsg] = 1;
17774 
17775  /* cleanup configs and msg */
17776  DISPOSE(vms.curdir, vms.curmsg);
17777 
17778 play2_msg_cleanup:
17779  if (vmu && open) {
17780  close_mailbox(&vms, vmu);
17781  }
17782 
17783 #ifdef IMAP_STORAGE
17784  if (vmu) {
17785  vmstate_delete(&vms);
17786  }
17787 #endif
17788 
17789  if (!res) {
17790  notify_new_state(vmu);
17791  }
17792 
17793  free_user(vmu);
17794  return res;
17795 }
17796 
17797 /* This is a workaround so that menuselect displays a proper description
17798  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
17799  */
17800 
17801 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
17802  .support_level = AST_MODULE_SUPPORT_CORE,
17803  .load = load_module,
17804  .unload = unload_module,
17805  .reload = reload,
17806  .optional_modules = "res_adsi,res_smdi",
17807 );
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:1151
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:2788
struct ast_variable * next
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
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.
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
enum ast_getdata_result 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:188
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:404
char * str
Subscriber phone number (Malloced)
Definition: channel.h:291
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:2701
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
void( ao2_prnt_fn)(void *where, const char *fmt,...)
Print output.
Definition: astobj2.h:1435
static void free_vm_zones(void)
Free the zones structure.
Asterisk locking-related definitions:
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:3310
Asterisk main include file. File version handling, generic pbx functions.
AO2_STRING_FIELD_HASH_FN(transport_monitor, key)
Hashing function for struct transport_monitor.
#define ast_realloc(p, len)
A wrapper for realloc()
Definition: astmm.h:226
#define VM_MARK_URGENT
static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
Loads the options specific to a voicemail user.
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Default English syntax for 'You have N messages' greeting.
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition: pbx_app.c:471
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.
static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
Manager list voicemail users command.
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
int ast_adsi_data_mode(unsigned char *buf)
Puts CPE in data mode.
Definition: adsi.c:219
static void free_vm_users(void)
Free the users structure.
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:750
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2958
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
basically mkdir -p $dest/$context/$ext/$folder
int urgent_msgs
Definition: mwi.h:464
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.
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition: channel.c:4277
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
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_party_name name
Subscriber name.
Definition: channel.h:340
static int last_message_index(char *dir)
Determines the highest message number in use for a given user and mailbox folder. ...
Convenient Signal Processing routines.
#define ast_odbc_request_obj(name, check)
Get a ODBC connection object.
Definition: res_odbc.h:120
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 'standard' argument separation process for an application.
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:3467
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.
static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Greek syntax for 'You have N messages' greeting.
#define VM_SAYDURATION
descriptor for a cli entry.
Definition: cli.h:171
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
int ast_file_is_readable(const char *filename)
Test that a file exists and is readable by the effective user.
Definition: utils.c:3107
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
Voicemail greeter function table definition.
static int debug
Global debug status.
Definition: res_xmpp.c:441
#define AST_TASKPROCESSOR_HIGH_WATER_LEVEL
Definition: taskprocessor.h:64
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container, as described below.
Definition: astobj2.h:1693
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:3576
static struct stasis_rest_handlers mailboxes
REST handler for /api-docs/mailboxes.json.
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
int ast_odbc_prepare(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Prepares a SQL query on a statement.
Definition: res_odbc.c:454
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
static void copy_plain_file(char *frompath, char *topath)
Copies a voicemail information (envelope) file.
static int vm_browse_messages_ja(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Japanese syntax for 'You have N messages' greeting.
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3530
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:3202
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.
static int count_messages(struct ast_vm_user *vmu, char *dir)
Find all .txt files - even if they are not in sequence from 0000.
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:439
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)
Test Framework API.
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:8259
#define VM_MESSAGEWRAP
void( ast_vm_msg_play_cb)(struct ast_channel *chan, const char *playfile, int duration)
Voicemail playback callback function definition.
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.
int ast_adsi_download_disconnect(unsigned char *buf)
Disconnects (and hopefully saves) a downloaded script.
Definition: adsi.c:208
AO2_STRING_FIELD_CMP_FN(transport_monitor, key)
Comparison function for struct transport_monitor.
ast_channel_state
ast_channel states
Definition: channelstate.h:35
char * str
Subscriber name (Malloced)
Definition: channel.h:264
static void free_zone(struct minivm_zone *z)
Free Mini Voicemail timezone.
Definition: app_minivm.c:2538
#define VM_ATTACH
#define VM_ODBC_AUDIO_ON_DISK
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:1139
#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:400
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:159
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 'prepend' mode which works a li...
Definition: main/app.c:2159
char pager[80]
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:3421
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 ...
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
static int check_mime(const char *str)
Check if the string would need encoding within the MIME standard, to avoid confusing certain mail sof...
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:348
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.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
int ast_unlock_path(const char *path)
Unlock a path.
Definition: main/app.c:2630
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:269
char password[80]
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:1465
Definitions to aid in the use of thread local storage.
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1141
ADSI Support (built upon Caller*ID)
int ast_mwi_add_observer(struct ast_mwi_observer *observer)
Add an observer to receive MWI state related events.
Definition: mwi.c:301
static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
Check the given mailbox'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:7422
list of users found in the config file
void(* on_subscribe)(const char *mailbox, struct ast_mwi_subscriber *sub)
Raised when MWI is being subscribed.
Definition: mwi.h:255
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
Definition: main/config.c:3659
char context[MAX_VM_CONTEXT_LEN]
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:757
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:4190
#define VM_SAYCID
static int get_date(char *s, int len)
Gets the current date and time, as formatted string.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
#define MAX_LANGUAGE
Definition: channel.h:172
#define VM_SVMAIL
Utility functions.
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
int ast_base64_encode_file_path(const char *filename, FILE *outputfile, const char *endl)
Performs a base 64 encode algorithm on the contents of a File.
Definition: utils.c:702
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:3050
int ast_update2_realtime(const char *family,...) attribute_sentinel
Update realtime configuration.
Definition: main/config.c:3695
All configuration options for http media cache.
off_t ast_tellstream(struct ast_filestream *fs)
Tell where we are in a stream.
Definition: file.c:1085
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...
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.
Number structure.
Definition: app_followme.c:154
void ast_category_append(struct ast_config *config, struct ast_category *category)
Appends a category to a config.
Definition: extconf.c:2833
Custom localtime functions for multiple timezones.
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:1113
struct ast_party_id id
Caller party ID.
Definition: channel.h:420
enum AST_LOCK_RESULT ast_lock_path(const char *path)
Lock a filesystem path.
Definition: main/app.c:2614
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 'You have N messages' greeting.
#define VM_SEARCH
#define force_reload_config()
Forcibly reload voicemail.conf, even if it has not changed. This is necessary after running unit test...
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:619
int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
Parse a time (integer) string.
Definition: utils.c:2446
#define VM_ENVELOPE
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 vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Italian syntax for 'You have N messages' greeting.
int ast_adsi_set_line(unsigned char *buf, int page, int line)
Sets the current line and page.
Definition: adsi.c:285
#define MINPASSWORD
static void cleanup(void)
Clean up any old apps that we don't need any more.
Definition: res_stasis.c:327
char locale[20]
#define ast_config_load(filename, flags)
Load a config file.
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 int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Spanish syntax for 'You have N messages' greeting.
int ast_store_realtime(const char *family,...) attribute_sentinel
Create realtime configuration.
Definition: main/config.c:3740
int ast_build_string(char **buffer, size_t *space, const char *fmt,...)
Build a string in a buffer, designed to be called repeatedly.
Definition: utils.c:2167
ast_mutex_t lock
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.
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:3549
#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:3475
ODBC container.
Definition: res_odbc.h:46
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
char zonetag[80]
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
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 ast_dummy_channel_alloc()
Create a fake channel structure.
Definition: channel.h:1282
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 'You have N messages' greeting.
#define VM_EMAIL_EXT_RECS
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define AST_MAX_EXTENSION
Definition: channel.h:134
int passwordlocation
Caller Party information.
Definition: channel.h:418
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
structure to hold extensions
struct ast_format * ast_format_gsm
Built-in cached gsm format.
Definition: format_cache.c:96
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:1616
#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:87
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:2149
In case you didn't read that giant block of text above the mansession_session struct, the 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:1785
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:282
static int check_password(struct ast_vm_user *vmu, char *password)
Check that password meets minimum required length.
static void generate_msg_id(char *dst)
Sets the destination string to a uniquely identifying msg_id string.
#define VM_MOVEHEARD
#define ast_format_cap_append(cap, format, framing)
Add format capability to capabilities structure.
Definition: format_cap.h:99
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
char * ast_format_str_reduce(char *fmts)
Definition: file.c:1894
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_debug(level,...)
Log a DEBUG message.
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 AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
ODBC resource manager.
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:8271
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:4175
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
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:3066
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:3274
int ast_ratestream(struct ast_filestream *fs)
Return the sample rate of the stream's format.
Definition: file.c:1090
struct ast_vm_mailbox_snapshot * ast_vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot)
destroy a snapshot
Definition: main/app.c:675
Structure to describe a channel "technology", ie a channel driver See for examples: ...
Definition: channel.h:628
static void apply_options(struct ast_vm_user *vmu, const char *options)
Destructively Parse options and apply.
Core PBX routines and definitions.
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
void ast_vm_greeter_unregister(const char *module_name)
Unregister the specified voicemail greeter provider.
Definition: main/app.c:511
#define VM_DIRECTFORWARD
#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:291
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:189
struct ast_str * ast_odbc_print_errors(SQLSMALLINT handle_type, SQLHANDLE handle, const char *operation)
Shortcut for printing errors to logs after a failed SQL operation.
Definition: res_odbc.c:515
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
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()
#define ASTERISK_USERNAME
Definition: app_minivm.c:553
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:8057
#define DEFAULT_POLL_FREQ
#define VM_PBXSKIP
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
char fromstring[100]
#define VM_OPERATOR
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3622
#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:346
int new_msgs
Definition: mwi.h:459
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
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.
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:360
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: utils.c:2199
char * emailbody
Support for dynamic strings.
Definition: strings.h:623
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:307
#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.
struct ast_vm_mailbox_snapshot * ast_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)
Create a snapshot of a mailbox which contains information about every msg.
Definition: main/app.c:661
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
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.
const char * module_name
The name of the module that provides the voicemail greeter functionality.
static unsigned int poll_mailboxes
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the 'nonstandard' argument separation process for an application.
#define VM_REVIEW
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.
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:3207
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2282
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 ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:8781
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
Seeks into stream.
Definition: file.c:1075
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
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 ESS(x)
Definition: cli.h:59
#define MSGFILE_LEN
#define AST_MAX_CONTEXT
Definition: channel.h:135
char * command
Definition: cli.h:186
static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move)
void ast_mwi_state_callback_all(on_mwi_state handler, void *data)
For each managed mailbox call the given handler.
Definition: mwi.c:338
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Portuguese syntax for 'You have N messages' greeting.
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:1878
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1111
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2541
static int load_module(void)
Load the module.
static int append_vmbox_info_astman(struct mansession *s, const struct message *m, struct ast_vm_user *vmu, const char *event_name, const char *actionid)
Append vmbox info string into given astman with event_name.
SMDI support for Asterisk.
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
Support for logging to various files, console and syslog Configuration in file logger.conf.
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:1533
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
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:395
An API for managing task processing threads that can be shared across modules.
char language[MAX_LANGUAGE]
structure to hold users read from users.conf
Structure used to handle boolean flags.
Definition: utils.h:199
const ast_string_field uniqueid
Definition: mwi.h:458
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:582
Options for leaving voicemail with the voicemail() application.
Definition: app_minivm.c:654
const char * usage
Definition: cli.h:177
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:645
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...
#define SENDMAIL
Default mail command to mail voicemail. Change it with the mailcmd= command in voicemail.conf.
Definition: app_minivm.c:541
struct ast_frame ast_null_frame
Definition: main/frame.c:79
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
Definition: channel.c:3175
const char * module_name
The name of the module that provides the voicemail functionality.
SQLRETURN ast_odbc_execute_sql(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Execute a unprepared SQL query.
Definition: res_odbc.c:469
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:693
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
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:398
int ast_adsi_available(struct ast_channel *chan)
Returns non-zero if Channel does or might support ADSI.
Definition: adsi.c:263
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
char attachfmt[20]
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
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:8235
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:3776
int old_msgs
Definition: mwi.h:460
static int has_voicemail(const char *mailbox, const char *folder)
Determines if the given folder has messages.
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:80
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
FILE * ast_file_mkftemp(char *template_name, mode_t mode)
same as mkstemp, but return a FILE
Definition: file.c:187
ast_app: A registered application
Definition: pbx_app.c:45
char uniqueid[80]
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"...
Definition: utils.c:2216
static int vm_lock_path(const char *path)
Lock file path only return failure if ast_lock_path returns 'timeout', not if the path does not exist...
MWI state event interface.
Definition: mwi.h:248
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1840
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1129
static char * handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Reload voicemail configuration from the CLI.
Asterisk MWI API.
#define VM_SKIPAFTERCMD
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2805
char * emailsubject
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:773
static int load_users(void)
Callback that loads the users from phoneprov sections.
Data structure associated with a single frame of data.
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:1371
#define VM_ALLOCED
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
Abstract JSON element (object, array, string, int, ...).
unsigned int module_version
The version of this function table.
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.
int ast_filerename(const char *oldname, const char *newname, const char *fmt)
Renames a file.
Definition: file.c:1146
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 AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
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]
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:909
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:279
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
int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders)
Reads multiple digits.
Definition: channel.c:6558
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
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:1111
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:599
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
static void run_externnotify(struct ast_channel *chan, struct minivm_account *vmu)
Run external notification for voicemail message.
Definition: app_minivm.c:1643
static int load_config_force(int reload, int force)
Reload voicemail.conf.
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:804
The structure that contains MWI state.
Definition: mwi.h:455
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:191
Say numbers and dates (maybe words one day too)
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
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.
#define ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, assignedids, requestor, amaflag,...)
Create a channel structure.
Definition: channel.h:1258
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.
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:2009
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:3389
Asterisk module definitions.
Voicemail function table definition.
#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:941
static struct ast_flags globalflags
Definition: app_minivm.c:699
char mailbox[MAX_VM_MBOX_ID_LEN]
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
#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 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:297
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.
static int valid_config(const struct ast_config *cfg)
Check if configuration file is valid.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Structure for mutex and tracking information.
Definition: lock.h:135
An SMDI message waiting indicator message.
Definition: smdi.h:51
static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
Resets a user password to a specified password.
void ast_manager_publish_event(const char *type, int class_type, struct ast_json *obj)
Publish an event to AMI.
Definition: manager.c:2063
#define VM_FORCEGREET
static unsigned int poll_freq
Media Format Cache API.
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_app_inboxcount(const char *mailboxes, int *newmsgs, int *oldmsgs)
Determine number of new/old messages in a mailbox.
Definition: main/app.c:604
static void populate_defaults(struct ast_vm_user *vmu)
Sets default voicemail system options to a voicemail user.
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 'record a temporary greeting'.
#define AST_APP_ARG(name)
Define an application argument.
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2479
int ast_callerid_parse(char *instr, char **name, char **location)
Destructively parse inbuf into name and location (or number)
Definition: callerid.c:1162
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
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:3431
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:342
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532