Wed Jan 8 2020 09:49:40

Asterisk developer's documentation


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  * \extref unixODBC (http://www.unixodbc.org/)
25  * \extref A source distribution of University of Washington's IMAP c-client
26  * (http://www.washington.edu/imap/)
27  *
28  * \par See also
29  * \arg \ref Config_vm
30  * \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
31  * \ingroup applications
32  * \note This module requires res_adsi to load. This needs to be optional
33  * during compilation.
34  *
35  * \note 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 /*** MODULEINFO
41  <use>res_adsi</use>
42  <use>res_smdi</use>
43  <support_level>core</support_level>
44  ***/
45 
46 /*** MAKEOPTS
47 <category name="MENUSELECT_OPTS_app_voicemail_odbcstorage" displayname="Voicemail ODBC Storage Build Options" positive_output="yes" touch_on_change="apps/app_voicemail_odbcstorage.c apps/app_directory_odbcstorage.c">
48  <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
49  <depend>generic_odbc</depend>
50  <depend>ltdl</depend>
51  <defaultenabled>yes</defaultenabled>
52  <support_level>core</support_level>
53  </member>
54 </category>
55 <category name="MENUSELECT_OPTS_app_voicemail_imapstorage" displayname="Voicemail IMAP Storage Build Options" positive_output="yes" touch_on_change="apps/app_voicemail_imapstorage.c apps/app_directory_imapstorage.c">
56  <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
57  <depend>imap_tk</depend>
58  <use>openssl</use>
59  <defaultenabled>yes</defaultenabled>
60  <support_level>core</support_level>
61  </member>
62 </category>
63 ***/
64 
65 #include "asterisk.h"
66 
67 #ifdef IMAP_STORAGE
68 #include <ctype.h>
69 #include <signal.h>
70 #include <pwd.h>
71 #ifdef USE_SYSTEM_IMAP
72 #include <imap/c-client.h>
73 #include <imap/imap4r1.h>
74 #include <imap/linkage.h>
75 #elif defined (USE_SYSTEM_CCLIENT)
76 #include <c-client/c-client.h>
77 #include <c-client/imap4r1.h>
78 #include <c-client/linkage.h>
79 #else
80 #include "c-client.h"
81 #include "imap4r1.h"
82 #include "linkage.h"
83 #endif
84 #endif
85 
86 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426691 $")
87 
88 #include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
89 #include <sys/time.h>
90 #include <sys/stat.h>
91 #include <sys/mman.h>
92 #include <time.h>
93 #include <dirent.h>
94 #if defined(__FreeBSD__) || defined(__OpenBSD__)
95 #include <sys/wait.h>
96 #endif
97 
98 #include "asterisk/logger.h"
99 #include "asterisk/lock.h"
100 #include "asterisk/file.h"
101 #include "asterisk/channel.h"
102 #include "asterisk/pbx.h"
103 #include "asterisk/config.h"
104 #include "asterisk/say.h"
105 #include "asterisk/module.h"
106 #include "asterisk/adsi.h"
107 #include "asterisk/app.h"
108 #include "asterisk/manager.h"
109 #include "asterisk/dsp.h"
110 #include "asterisk/localtime.h"
111 #include "asterisk/cli.h"
112 #include "asterisk/utils.h"
113 #include "asterisk/stringfields.h"
114 #include "asterisk/smdi.h"
115 #include "asterisk/astobj2.h"
116 #include "asterisk/event.h"
117 #include "asterisk/taskprocessor.h"
118 #include "asterisk/test.h"
119 
120 #ifdef ODBC_STORAGE
121 #include "asterisk/res_odbc.h"
122 #endif
123 
124 #ifdef IMAP_STORAGE
125 #include "asterisk/threadstorage.h"
126 #endif
127 
128 /*** DOCUMENTATION
129  <application name="VoiceMail" language="en_US">
130  <synopsis>
131  Leave a Voicemail message.
132  </synopsis>
133  <syntax>
134  <parameter name="mailboxs" argsep="&amp;" required="true">
135  <argument name="mailbox1" argsep="@" required="true">
136  <argument name="mailbox" required="true" />
137  <argument name="context" />
138  </argument>
139  <argument name="mailbox2" argsep="@" multiple="true">
140  <argument name="mailbox" required="true" />
141  <argument name="context" />
142  </argument>
143  </parameter>
144  <parameter name="options">
145  <optionlist>
146  <option name="b">
147  <para>Play the <literal>busy</literal> greeting to the calling party.</para>
148  </option>
149  <option name="d">
150  <argument name="c" />
151  <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
152  if played during the greeting. Context defaults to the current context.</para>
153  </option>
154  <option name="g">
155  <argument name="#" required="true" />
156  <para>Use the specified amount of gain when recording the voicemail
157  message. The units are whole-number decibels (dB). Only works on supported
158  technologies, which is DAHDI only.</para>
159  </option>
160  <option name="s">
161  <para>Skip the playback of instructions for leaving a message to the
162  calling party.</para>
163  </option>
164  <option name="u">
165  <para>Play the <literal>unavailable</literal> greeting.</para>
166  </option>
167  <option name="U">
168  <para>Mark message as <literal>URGENT</literal>.</para>
169  </option>
170  <option name="P">
171  <para>Mark message as <literal>PRIORITY</literal>.</para>
172  </option>
173  </optionlist>
174  </parameter>
175  </syntax>
176  <description>
177  <para>This application allows the calling party to leave a message for the specified
178  list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
179  the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
180  exist.</para>
181  <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
182  <enumlist>
183  <enum name="0">
184  <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
185  </enum>
186  <enum name="*">
187  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
188  </enum>
189  </enumlist>
190  <para>This application will set the following channel variable upon completion:</para>
191  <variablelist>
192  <variable name="VMSTATUS">
193  <para>This indicates the status of the execution of the VoiceMail application.</para>
194  <value name="SUCCESS" />
195  <value name="USEREXIT" />
196  <value name="FAILED" />
197  </variable>
198  </variablelist>
199  </description>
200  <see-also>
201  <ref type="application">VoiceMailMain</ref>
202  </see-also>
203  </application>
204  <application name="VoiceMailMain" language="en_US">
205  <synopsis>
206  Check Voicemail messages.
207  </synopsis>
208  <syntax>
209  <parameter name="mailbox" required="true" argsep="@">
210  <argument name="mailbox" />
211  <argument name="context" />
212  </parameter>
213  <parameter name="options">
214  <optionlist>
215  <option name="p">
216  <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
217  the mailbox that is entered by the caller.</para>
218  </option>
219  <option name="g">
220  <argument name="#" required="true" />
221  <para>Use the specified amount of gain when recording a voicemail message.
222  The units are whole-number decibels (dB).</para>
223  </option>
224  <option name="s">
225  <para>Skip checking the passcode for the mailbox.</para>
226  </option>
227  <option name="a">
228  <argument name="folder" required="true" />
229  <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
230  Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
231  <enumlist>
232  <enum name="0"><para>INBOX</para></enum>
233  <enum name="1"><para>Old</para></enum>
234  <enum name="2"><para>Work</para></enum>
235  <enum name="3"><para>Family</para></enum>
236  <enum name="4"><para>Friends</para></enum>
237  <enum name="5"><para>Cust1</para></enum>
238  <enum name="6"><para>Cust2</para></enum>
239  <enum name="7"><para>Cust3</para></enum>
240  <enum name="8"><para>Cust4</para></enum>
241  <enum name="9"><para>Cust5</para></enum>
242  </enumlist>
243  </option>
244  </optionlist>
245  </parameter>
246  </syntax>
247  <description>
248  <para>This application allows the calling party to check voicemail messages. A specific
249  <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
250  may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
251  be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
252  <literal>default</literal> context will be used.</para>
253  <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
254  or Password, and the extension exists:</para>
255  <enumlist>
256  <enum name="*">
257  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
258  </enum>
259  </enumlist>
260  </description>
261  <see-also>
262  <ref type="application">VoiceMail</ref>
263  </see-also>
264  </application>
265  <application name="MailboxExists" language="en_US">
266  <synopsis>
267  Check to see if Voicemail mailbox exists.
268  </synopsis>
269  <syntax>
270  <parameter name="mailbox" required="true" argsep="@">
271  <argument name="mailbox" required="true" />
272  <argument name="context" />
273  </parameter>
274  <parameter name="options">
275  <para>None options.</para>
276  </parameter>
277  </syntax>
278  <description>
279  <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
280  <replaceable>context</replaceable> is specified, the <literal>default</literal> context
281  will be used.</para>
282  <para>This application will set the following channel variable upon completion:</para>
283  <variablelist>
284  <variable name="VMBOXEXISTSSTATUS">
285  <para>This will contain the status of the execution of the MailboxExists application.
286  Possible values include:</para>
287  <value name="SUCCESS" />
288  <value name="FAILED" />
289  </variable>
290  </variablelist>
291  </description>
292  </application>
293  <application name="VMAuthenticate" language="en_US">
294  <synopsis>
295  Authenticate with Voicemail passwords.
296  </synopsis>
297  <syntax>
298  <parameter name="mailbox" required="true" argsep="@">
299  <argument name="mailbox" />
300  <argument name="context" />
301  </parameter>
302  <parameter name="options">
303  <optionlist>
304  <option name="s">
305  <para>Skip playing the initial prompts.</para>
306  </option>
307  </optionlist>
308  </parameter>
309  </syntax>
310  <description>
311  <para>This application behaves the same way as the Authenticate application, but the passwords
312  are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
313  specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
314  is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
315  mailbox.</para>
316  <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
317  or Password, and the extension exists:</para>
318  <enumlist>
319  <enum name="*">
320  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
321  </enum>
322  </enumlist>
323  </description>
324  </application>
325  <application name="VMSayName" language="en_US">
326  <synopsis>
327  Play the name of a voicemail user
328  </synopsis>
329  <syntax>
330  <parameter name="mailbox" required="true" argsep="@">
331  <argument name="mailbox" />
332  <argument name="context" />
333  </parameter>
334  </syntax>
335  <description>
336  <para>This application will say the recorded name of the voicemail user specified as the
337  argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
338  </description>
339  </application>
340  <function name="MAILBOX_EXISTS" language="en_US">
341  <synopsis>
342  Tell if a mailbox is configured.
343  </synopsis>
344  <syntax argsep="@">
345  <parameter name="mailbox" required="true" />
346  <parameter name="context" />
347  </syntax>
348  <description>
349  <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
350  If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
351  context.</para>
352  </description>
353  </function>
354  <manager name="VoicemailUsersList" language="en_US">
355  <synopsis>
356  List All Voicemail User Information.
357  </synopsis>
358  <syntax>
359  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
360  </syntax>
361  <description>
362  </description>
363  </manager>
364  ***/
365 
366 #ifdef IMAP_STORAGE
367 static char imapserver[48];
368 static char imapport[8];
369 static char imapflags[128];
370 static char imapfolder[64];
371 static char imapparentfolder[64] = "\0";
372 static char greetingfolder[64];
373 static char authuser[32];
374 static char authpassword[42];
375 static int imapversion = 1;
376 
377 static int expungeonhangup = 1;
378 static int imapgreetings = 0;
379 static char delimiter = '\0';
380 
381 struct vm_state;
382 struct ast_vm_user;
383 
384 AST_THREADSTORAGE(ts_vmstate);
385 
386 /* Forward declarations for IMAP */
387 static int init_mailstream(struct vm_state *vms, int box);
388 static void write_file(char *filename, char *buffer, unsigned long len);
389 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
390 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
391 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
392 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
393 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
394 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
395 static void vmstate_insert(struct vm_state *vms);
396 static void vmstate_delete(struct vm_state *vms);
397 static void set_update(MAILSTREAM * stream);
398 static void init_vm_state(struct vm_state *vms);
399 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
400 static void get_mailbox_delimiter(MAILSTREAM *stream);
401 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
402 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
403 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);
404 static void update_messages_by_imapuser(const char *user, unsigned long number);
405 static int vm_delete(char *file);
406 
407 static int imap_remove_file (char *dir, int msgnum);
408 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
409 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
410 static void check_quota(struct vm_state *vms, char *mailbox);
411 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
412 struct vmstate {
413  struct vm_state *vms;
414  AST_LIST_ENTRY(vmstate) list;
415 };
416 
417 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
418 
419 #endif
420 
421 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
422 
423 #define COMMAND_TIMEOUT 5000
424 /* Don't modify these here; set your umask at runtime instead */
425 #define VOICEMAIL_DIR_MODE 0777
426 #define VOICEMAIL_FILE_MODE 0666
427 #define CHUNKSIZE 65536
428 
429 #define VOICEMAIL_CONFIG "voicemail.conf"
430 #define ASTERISK_USERNAME "asterisk"
431 
432 /* Define fast-forward, pause, restart, and reverse keys
433  * while listening to a voicemail message - these are
434  * strings, not characters */
435 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
436 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
437 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
438 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
439 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
440 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
441 
442 /* Default mail command to mail voicemail. Change it with the
443  * mailcmd= command in voicemail.conf */
444 #define SENDMAIL "/usr/sbin/sendmail -t"
445 
446 #define INTRO "vm-intro"
447 
448 #define MAXMSG 100
449 #define MAXMSGLIMIT 9999
450 
451 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
452 
453 #define BASELINELEN 72
454 #define BASEMAXINLINE 256
455 #ifdef IMAP_STORAGE
456 #define ENDL "\r\n"
457 #else
458 #define ENDL "\n"
459 #endif
460 
461 #define MAX_DATETIME_FORMAT 512
462 #define MAX_NUM_CID_CONTEXTS 10
463 
464 #define VM_REVIEW (1 << 0) /*!< After recording, permit the caller to review the recording before saving */
465 #define VM_OPERATOR (1 << 1) /*!< Allow 0 to be pressed to go to 'o' extension */
466 #define VM_SAYCID (1 << 2) /*!< Repeat the CallerID info during envelope playback */
467 #define VM_SVMAIL (1 << 3) /*!< Allow the user to compose a new VM from within VoicemailMain */
468 #define VM_ENVELOPE (1 << 4) /*!< Play the envelope information (who-from, time received, etc.) */
469 #define VM_SAYDURATION (1 << 5) /*!< Play the length of the message during envelope playback */
470 #define VM_SKIPAFTERCMD (1 << 6) /*!< After deletion, assume caller wants to go to the next message */
471 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
472 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
473 #define VM_PBXSKIP (1 << 9) /*!< Skip the [PBX] preamble in the Subject line of emails */
474 #define VM_DIRECFORWARD (1 << 10) /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
475 #define VM_ATTACH (1 << 11) /*!< Attach message to voicemail notifications? */
476 #define VM_DELETE (1 << 12) /*!< Delete message after sending notification */
477 #define VM_ALLOCED (1 << 13) /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
478 #define VM_SEARCH (1 << 14) /*!< Search all contexts for a matching mailbox */
479 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
480 #define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
481 #define VM_MESSAGEWRAP (1 << 17) /*!< Wrap around from the last message to the first, and vice-versa */
482 #define VM_FWDURGAUTO (1 << 18) /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
483 #define ERROR_LOCK_PATH -100
484 #define OPERATOR_EXIT 300
485 
486 
487 enum vm_box {
494 };
495 
497  OPT_SILENT = (1 << 0),
498  OPT_BUSY_GREETING = (1 << 1),
500  OPT_RECORDGAIN = (1 << 3),
502  OPT_AUTOPLAY = (1 << 6),
503  OPT_DTMFEXIT = (1 << 7),
504  OPT_MESSAGE_Urgent = (1 << 8),
506 };
507 
512  /* This *must* be the last value in this enum! */
514 };
515 
520 };
521 
532 });
533 
534 static int load_config(int reload);
535 #ifdef TEST_FRAMEWORK
536 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
537 #endif
538 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
539 
540 /*! \page vmlang Voicemail Language Syntaxes Supported
541 
542  \par Syntaxes supported, not really language codes.
543  \arg \b en - English
544  \arg \b de - German
545  \arg \b es - Spanish
546  \arg \b fr - French
547  \arg \b it - Italian
548  \arg \b nl - Dutch
549  \arg \b pt - Portuguese
550  \arg \b pt_BR - Portuguese (Brazil)
551  \arg \b gr - Greek
552  \arg \b no - Norwegian
553  \arg \b se - Swedish
554  \arg \b tw - Chinese (Taiwan)
555  \arg \b ua - Ukrainian
556 
557 German requires the following additional soundfile:
558 \arg \b 1F einE (feminine)
559 
560 Spanish requires the following additional soundfile:
561 \arg \b 1M un (masculine)
562 
563 Dutch, Greek, Portuguese & Spanish require the following additional soundfiles:
564 \arg \b vm-INBOXs singular of 'new'
565 \arg \b vm-Olds singular of 'old/heard/read'
566 
567 NB these are plural:
568 \arg \b vm-INBOX nieuwe (nl)
569 \arg \b vm-Old oude (nl)
570 
571 Polish uses:
572 \arg \b vm-new-a 'new', feminine singular accusative
573 \arg \b vm-new-e 'new', feminine plural accusative
574 \arg \b vm-new-ych 'new', feminine plural genitive
575 \arg \b vm-old-a 'old', feminine singular accusative
576 \arg \b vm-old-e 'old', feminine plural accusative
577 \arg \b vm-old-ych 'old', feminine plural genitive
578 \arg \b digits/1-a 'one', not always same as 'digits/1'
579 \arg \b digits/2-ie 'two', not always same as 'digits/2'
580 
581 Swedish uses:
582 \arg \b vm-nytt singular of 'new'
583 \arg \b vm-nya plural of 'new'
584 \arg \b vm-gammalt singular of 'old'
585 \arg \b vm-gamla plural of 'old'
586 \arg \b digits/ett 'one', not always same as 'digits/1'
587 
588 Norwegian uses:
589 \arg \b vm-ny singular of 'new'
590 \arg \b vm-nye plural of 'new'
591 \arg \b vm-gammel singular of 'old'
592 \arg \b vm-gamle plural of 'old'
593 
594 Dutch also uses:
595 \arg \b nl-om 'at'?
596 
597 Greek, Portuguese & Spanish also uses:
598 \arg \b vm-youhaveno
599 
600 Italian requires the following additional soundfile:
601 
602 For vm_intro_it:
603 \arg \b vm-nuovo new
604 \arg \b vm-nuovi new plural
605 \arg \b vm-vecchio old
606 \arg \b vm-vecchi old plural
607 
608 Chinese (Taiwan) requires the following additional soundfile:
609 \arg \b vm-tong A class-word for call (tong1)
610 \arg \b vm-ri A class-word for day (ri4)
611 \arg \b vm-you You (ni3)
612 \arg \b vm-haveno Have no (mei2 you3)
613 \arg \b vm-have Have (you3)
614 \arg \b vm-listen To listen (yao4 ting1)
615 
616 
617 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
618 spelled among others when you have to change folder. For the above reasons, vm-INBOX
619 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
620 
621 */
622 
623 struct baseio {
624  int iocp;
625  int iolen;
627  int ateof;
628  unsigned char iobuf[BASEMAXINLINE];
629 };
630 
631 /*! Structure for linked list of users
632  * Use ast_vm_user_destroy() to free one of these structures. */
633 struct ast_vm_user {
634  char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
635  char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
636  char password[80]; /*!< Secret pin code, numbers only */
637  char fullname[80]; /*!< Full name, for directory app */
638  char email[80]; /*!< E-mail address */
639  char *emailsubject; /*!< E-mail subject */
640  char *emailbody; /*!< E-mail body */
641  char pager[80]; /*!< E-mail address to pager (no attachment) */
642  char serveremail[80]; /*!< From: Mail address */
643  char language[MAX_LANGUAGE]; /*!< Config: Language setting */
644  char zonetag[80]; /*!< Time zone */
645  char locale[20]; /*!< The locale (for presentation of date/time) */
646  char callback[80];
647  char dialout[80];
648  char uniqueid[80]; /*!< Unique integer identifier */
649  char exit[80];
650  char attachfmt[20]; /*!< Attachment format */
651  unsigned int flags; /*!< VM_ flags */
653  int minsecs; /*!< Minimum number of seconds per message for this mailbox */
654  int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
655  int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
656  int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
657  int passwordlocation; /*!< Storage location of the password */
658 #ifdef IMAP_STORAGE
659  char imapuser[80]; /*!< IMAP server login */
660  char imappassword[80]; /*!< IMAP server password if authpassword not defined */
661  char imapfolder[64]; /*!< IMAP voicemail folder */
662  char imapvmshareid[80]; /*!< Shared mailbox ID to use rather than the dialed one */
663  int imapversion; /*!< If configuration changes, use the new values */
664 #endif
665  double volgain; /*!< Volume gain for voicemails sent via email */
667 };
668 
669 /*! Voicemail time zones */
670 struct vm_zone {
671  AST_LIST_ENTRY(vm_zone) list;
672  char name[80];
673  char timezone[80];
674  char msg_format[512];
675 };
676 
677 #define VMSTATE_MAX_MSG_ARRAY 256
678 
679 /*! Voicemail mailbox state */
680 struct vm_state {
681  char curbox[80];
682  char username[80];
683  char context[80];
684  char curdir[PATH_MAX];
685  char vmbox[PATH_MAX];
686  char fn[PATH_MAX];
687  char intro[PATH_MAX];
688  int *deleted;
689  int *heard;
690  int dh_arraysize; /* used for deleted / heard allocation */
691  int curmsg;
692  int lastmsg;
696  int starting;
697  int repeats;
698 #ifdef IMAP_STORAGE
700  int updated; /*!< decremented on each mail check until 1 -allows delay */
701  long *msgArray;
702  unsigned msg_array_max;
703  MAILSTREAM *mailstream;
704  int vmArrayIndex;
705  char imapuser[80]; /*!< IMAP server login */
706  char imapfolder[64]; /*!< IMAP voicemail folder */
707  int imapversion;
708  int interactive;
709  char introfn[PATH_MAX]; /*!< Name of prepended file */
710  unsigned int quota_limit;
711  unsigned int quota_usage;
712  struct vm_state *persist_vms;
713 #endif
714 };
715 
716 #ifdef ODBC_STORAGE
717 static char odbc_database[80];
718 static char odbc_table[80];
719 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
720 #define DISPOSE(a,b) remove_file(a,b)
721 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
722 #define EXISTS(a,b,c,d) (message_exists(a,b))
723 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
724 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
725 #define DELETE(a,b,c,d) (delete_file(a,b))
726 #else
727 #ifdef IMAP_STORAGE
728 #define DISPOSE(a,b) (imap_remove_file(a,b))
729 #define STORE(a,b,c,d,e,f,g,h,i,j) (imap_store_file(a,b,c,d,e,f,g,h,i,j))
730 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
731 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
732 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
733 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
734 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
735 #else
736 #define RETRIEVE(a,b,c,d)
737 #define DISPOSE(a,b)
738 #define STORE(a,b,c,d,e,f,g,h,i,j)
739 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
740 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
741 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
742 #define DELETE(a,b,c,d) (vm_delete(c))
743 #endif
744 #endif
745 
746 static char VM_SPOOL_DIR[PATH_MAX];
747 
748 static char ext_pass_cmd[128];
749 static char ext_pass_check_cmd[128];
750 
751 static int my_umask;
752 
753 #define PWDCHANGE_INTERNAL (1 << 1)
754 #define PWDCHANGE_EXTERNAL (1 << 2)
756 
757 #ifdef ODBC_STORAGE
758 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
759 #else
760 # ifdef IMAP_STORAGE
761 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
762 # else
763 # define tdesc "Comedian Mail (Voicemail System)"
764 # endif
765 #endif
766 
767 static char userscontext[AST_MAX_EXTENSION] = "default";
768 
769 static char *addesc = "Comedian Mail";
770 
771 /* Leave a message */
772 static char *app = "VoiceMail";
773 
774 /* Check mail, control, etc */
775 static char *app2 = "VoiceMailMain";
776 
777 static char *app3 = "MailboxExists";
778 static char *app4 = "VMAuthenticate";
779 
780 static char *sayname_app = "VMSayName";
781 
783 static AST_LIST_HEAD_STATIC(zones, vm_zone);
784 static char zonetag[80];
785 static char locale[20];
786 static int maxsilence;
787 static int maxmsg;
788 static int maxdeletedmsg;
789 static int silencethreshold = 128;
790 static char serveremail[80];
791 static char mailcmd[160]; /* Configurable mail cmd */
792 static char externnotify[160];
793 static struct ast_smdi_interface *smdi_iface = NULL;
794 static char vmfmts[80];
795 static double volgain;
796 static int vmminsecs;
797 static int vmmaxsecs;
798 static int maxgreet;
799 static int skipms;
800 static int maxlogins;
801 static int minpassword;
802 static int passwordlocation;
803 
804 /*! Poll mailboxes for changes since there is something external to
805  * app_voicemail that may change them. */
806 static unsigned int poll_mailboxes;
807 
808 /*! Polling frequency */
809 static unsigned int poll_freq;
810 /*! By default, poll every 30 seconds */
811 #define DEFAULT_POLL_FREQ 30
812 
814 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
815 static pthread_t poll_thread = AST_PTHREADT_NULL;
816 static unsigned char poll_thread_run;
817 
818 /*! Subscription to ... MWI event subscriptions */
819 static struct ast_event_sub *mwi_sub_sub;
820 /*! Subscription to ... MWI event un-subscriptions */
822 
823 /*!
824  * \brief An MWI subscription
825  *
826  * This is so we can keep track of which mailboxes are subscribed to.
827  * This way, we know which mailboxes to poll when the pollmailboxes
828  * option is being used.
829  */
830 struct mwi_sub {
831  AST_RWLIST_ENTRY(mwi_sub) entry;
832  int old_urgent;
833  int old_new;
834  int old_old;
835  uint32_t uniqueid;
836  char mailbox[1];
837 };
838 
839 struct mwi_sub_task {
840  const char *mailbox;
841  const char *context;
842  uint32_t uniqueid;
843 };
844 
846 
848 
849 /* custom audio control prompts for voicemail playback */
852 static char listen_control_pause_key[12];
854 static char listen_control_stop_key[12];
855 
856 /* custom password sounds */
857 static char vm_password[80] = "vm-password";
858 static char vm_newpassword[80] = "vm-newpassword";
859 static char vm_passchanged[80] = "vm-passchanged";
860 static char vm_reenterpassword[80] = "vm-reenterpassword";
861 static char vm_mismatch[80] = "vm-mismatch";
862 static char vm_invalid_password[80] = "vm-invalid-password";
863 static char vm_pls_try_again[80] = "vm-pls-try-again";
864 
865 /*
866  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
867  * 1. create a sound along the lines of "Please try again. When done, press the pound key" which could be spliced
868  * from existing sound clips. This would require some programming changes in the area of vm_forward options and also
869  * app.c's __ast_play_and_record function
870  * 2. create a sound prompt saying "Please try again. When done recording, press any key to stop and send the prepended
871  * message." At the time of this comment, I think this would require new voice work to be commissioned.
872  * 3. Something way different like providing instructions before a time out or a post-recording menu. This would require
873  * more effort than either of the other two.
874  */
875 static char vm_prepend_timeout[80] = "vm-then-pound";
876 
877 static struct ast_flags globalflags = {0};
878 
879 static int saydurationminfo;
880 
881 static char dialcontext[AST_MAX_CONTEXT] = "";
882 static char callcontext[AST_MAX_CONTEXT] = "";
883 static char exitcontext[AST_MAX_CONTEXT] = "";
884 
886 
887 
888 static char *emailbody = NULL;
889 static char *emailsubject = NULL;
890 static char *pagerbody = NULL;
891 static char *pagersubject = NULL;
892 static char fromstring[100];
893 static char pagerfromstring[100];
894 static char charset[32] = "ISO-8859-1";
895 
896 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
897 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
898 static int adsiver = 1;
899 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
900 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
901 
902 /* Forward declarations - generic */
903 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
904 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);
905 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
906 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
907  char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
908  signed char record_gain, struct vm_state *vms, char *flag);
909 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
910 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
911 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);
912 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);
913 static void apply_options(struct ast_vm_user *vmu, const char *options);
914 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);
915 static int is_valid_dtmf(const char *key);
916 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
917 static int write_password_to_file(const char *secretfn, const char *password);
918 static const char *substitute_escapes(const char *value);
919 static void free_user(struct ast_vm_user *vmu);
920 
922 
923 struct inprocess {
924  int count;
925  char *context;
926  char mailbox[0];
927 };
928 
929 static int inprocess_hash_fn(const void *obj, const int flags)
930 {
931  const struct inprocess *i = obj;
932  return atoi(i->mailbox);
933 }
934 
935 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
936 {
937  struct inprocess *i = obj, *j = arg;
938  if (strcmp(i->mailbox, j->mailbox)) {
939  return 0;
940  }
941  return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
942 }
943 
944 static int inprocess_count(const char *context, const char *mailbox, int delta)
945 {
946  struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
947  arg->context = arg->mailbox + strlen(mailbox) + 1;
948  strcpy(arg->mailbox, mailbox); /* SAFE */
949  strcpy(arg->context, context); /* SAFE */
950  ao2_lock(inprocess_container);
951  if ((i = ao2_find(inprocess_container, arg, 0))) {
952  int ret = ast_atomic_fetchadd_int(&i->count, delta);
953  ao2_unlock(inprocess_container);
954  ao2_ref(i, -1);
955  return ret;
956  }
957  if (delta < 0) {
958  ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
959  }
960  if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
961  ao2_unlock(inprocess_container);
962  return 0;
963  }
964  i->context = i->mailbox + strlen(mailbox) + 1;
965  strcpy(i->mailbox, mailbox); /* SAFE */
966  strcpy(i->context, context); /* SAFE */
967  i->count = delta;
968  ao2_link(inprocess_container, i);
969  ao2_unlock(inprocess_container);
970  ao2_ref(i, -1);
971  return 0;
972 }
973 
974 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
975 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
976 #endif
977 
978 /*!
979  * \brief Strips control and non 7-bit clean characters from input string.
980  *
981  * \note To map control and none 7-bit characters to a 7-bit clean characters
982  * please use ast_str_encode_mine().
983  */
984 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
985 {
986  char *bufptr = buf;
987  for (; *input; input++) {
988  if (*input < 32) {
989  continue;
990  }
991  *bufptr++ = *input;
992  if (bufptr == buf + buflen - 1) {
993  break;
994  }
995  }
996  *bufptr = '\0';
997  return buf;
998 }
999 
1000 
1001 /*!
1002  * \brief Sets default voicemail system options to a voicemail user.
1003  *
1004  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
1005  * - all the globalflags
1006  * - the saydurationminfo
1007  * - the callcontext
1008  * - the dialcontext
1009  * - the exitcontext
1010  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
1011  * - volume gain
1012  * - emailsubject, emailbody set to NULL
1013  */
1014 static void populate_defaults(struct ast_vm_user *vmu)
1015 {
1016  ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
1018  if (saydurationminfo) {
1020  }
1021  ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
1022  ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
1023  ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
1024  ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
1025  ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
1026  if (vmminsecs) {
1027  vmu->minsecs = vmminsecs;
1028  }
1029  if (vmmaxsecs) {
1030  vmu->maxsecs = vmmaxsecs;
1031  }
1032  if (maxmsg) {
1033  vmu->maxmsg = maxmsg;
1034  }
1035  if (maxdeletedmsg) {
1037  }
1038  vmu->volgain = volgain;
1039  ast_free(vmu->emailsubject);
1040  vmu->emailsubject = NULL;
1041  ast_free(vmu->emailbody);
1042  vmu->emailbody = NULL;
1043 #ifdef IMAP_STORAGE
1044  ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
1045 #endif
1046 }
1047 
1048 /*!
1049  * \brief Sets a a specific property value.
1050  * \param vmu The voicemail user object to work with.
1051  * \param var The name of the property to be set.
1052  * \param value The value to be set to the property.
1053  *
1054  * The property name must be one of the understood properties. See the source for details.
1055  */
1056 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
1057 {
1058  int x;
1059  if (!strcasecmp(var, "attach")) {
1060  ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
1061  } else if (!strcasecmp(var, "attachfmt")) {
1062  ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
1063  } else if (!strcasecmp(var, "serveremail")) {
1064  ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
1065  } else if (!strcasecmp(var, "emailbody")) {
1066  vmu->emailbody = ast_strdup(substitute_escapes(value));
1067  } else if (!strcasecmp(var, "emailsubject")) {
1069  } else if (!strcasecmp(var, "language")) {
1070  ast_copy_string(vmu->language, value, sizeof(vmu->language));
1071  } else if (!strcasecmp(var, "tz")) {
1072  ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
1073  } else if (!strcasecmp(var, "locale")) {
1074  ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
1075 #ifdef IMAP_STORAGE
1076  } else if (!strcasecmp(var, "imapuser")) {
1077  ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
1078  vmu->imapversion = imapversion;
1079  } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
1080  ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
1081  vmu->imapversion = imapversion;
1082  } else if (!strcasecmp(var, "imapfolder")) {
1083  ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
1084  } else if (!strcasecmp(var, "imapvmshareid")) {
1085  ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
1086  vmu->imapversion = imapversion;
1087 #endif
1088  } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
1089  ast_set2_flag(vmu, ast_true(value), VM_DELETE);
1090  } else if (!strcasecmp(var, "saycid")){
1091  ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
1092  } else if (!strcasecmp(var, "sendvoicemail")){
1093  ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
1094  } else if (!strcasecmp(var, "review")){
1095  ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
1096  } else if (!strcasecmp(var, "tempgreetwarn")){
1097  ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
1098  } else if (!strcasecmp(var, "messagewrap")){
1099  ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);
1100  } else if (!strcasecmp(var, "operator")) {
1101  ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
1102  } else if (!strcasecmp(var, "envelope")){
1103  ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
1104  } else if (!strcasecmp(var, "moveheard")){
1105  ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
1106  } else if (!strcasecmp(var, "sayduration")){
1107  ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
1108  } else if (!strcasecmp(var, "saydurationm")){
1109  if (sscanf(value, "%30d", &x) == 1) {
1110  vmu->saydurationm = x;
1111  } else {
1112  ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
1113  }
1114  } else if (!strcasecmp(var, "forcename")){
1115  ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
1116  } else if (!strcasecmp(var, "forcegreetings")){
1117  ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
1118  } else if (!strcasecmp(var, "callback")) {
1119  ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
1120  } else if (!strcasecmp(var, "dialout")) {
1121  ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
1122  } else if (!strcasecmp(var, "exitcontext")) {
1123  ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
1124  } else if (!strcasecmp(var, "minsecs")) {
1125  if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
1126  vmu->minsecs = x;
1127  } else {
1128  ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
1129  vmu->minsecs = vmminsecs;
1130  }
1131  } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
1132  vmu->maxsecs = atoi(value);
1133  if (vmu->maxsecs <= 0) {
1134  ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
1135  vmu->maxsecs = vmmaxsecs;
1136  } else {
1137  vmu->maxsecs = atoi(value);
1138  }
1139  if (!strcasecmp(var, "maxmessage"))
1140  ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
1141  } else if (!strcasecmp(var, "maxmsg")) {
1142  vmu->maxmsg = atoi(value);
1143  /* Accept maxmsg=0 (Greetings only voicemail) */
1144  if (vmu->maxmsg < 0) {
1145  ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
1146  vmu->maxmsg = MAXMSG;
1147  } else if (vmu->maxmsg > MAXMSGLIMIT) {
1148  ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
1149  vmu->maxmsg = MAXMSGLIMIT;
1150  }
1151  } else if (!strcasecmp(var, "nextaftercmd")) {
1152  ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
1153  } else if (!strcasecmp(var, "backupdeleted")) {
1154  if (sscanf(value, "%30d", &x) == 1)
1155  vmu->maxdeletedmsg = x;
1156  else if (ast_true(value))
1157  vmu->maxdeletedmsg = MAXMSG;
1158  else
1159  vmu->maxdeletedmsg = 0;
1160 
1161  if (vmu->maxdeletedmsg < 0) {
1162  ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
1163  vmu->maxdeletedmsg = MAXMSG;
1164  } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
1165  ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
1166  vmu->maxdeletedmsg = MAXMSGLIMIT;
1167  }
1168  } else if (!strcasecmp(var, "volgain")) {
1169  sscanf(value, "%30lf", &vmu->volgain);
1170  } else if (!strcasecmp(var, "passwordlocation")) {
1171  if (!strcasecmp(value, "spooldir")) {
1173  } else {
1175  }
1176  } else if (!strcasecmp(var, "options")) {
1177  apply_options(vmu, value);
1178  }
1179 }
1180 
1181 static char *vm_check_password_shell(char *command, char *buf, size_t len)
1182 {
1183  int fds[2], pid = 0;
1184 
1185  memset(buf, 0, len);
1186 
1187  if (pipe(fds)) {
1188  snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
1189  } else {
1190  /* good to go*/
1191  pid = ast_safe_fork(0);
1192 
1193  if (pid < 0) {
1194  /* ok maybe not */
1195  close(fds[0]);
1196  close(fds[1]);
1197  snprintf(buf, len, "FAILURE: Fork failed");
1198  } else if (pid) {
1199  /* parent */
1200  close(fds[1]);
1201  if (read(fds[0], buf, len) < 0) {
1202  ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
1203  }
1204  close(fds[0]);
1205  } else {
1206  /* child */
1208  AST_APP_ARG(v)[20];
1209  );
1210  char *mycmd = ast_strdupa(command);
1211 
1212  close(fds[0]);
1213  dup2(fds[1], STDOUT_FILENO);
1214  close(fds[1]);
1215  ast_close_fds_above_n(STDOUT_FILENO);
1216 
1217  AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
1218 
1219  execv(arg.v[0], arg.v);
1220  printf("FAILURE: %s", strerror(errno));
1221  _exit(0);
1222  }
1223  }
1224  return buf;
1225 }
1226 
1227 /*!
1228  * \brief Check that password meets minimum required length
1229  * \param vmu The voicemail user to change the password for.
1230  * \param password The password string to check
1231  *
1232  * \return zero on ok, 1 on not ok.
1233  */
1234 static int check_password(struct ast_vm_user *vmu, char *password)
1235 {
1236  /* check minimum length */
1237  if (strlen(password) < minpassword)
1238  return 1;
1239  /* check that password does not contain '*' character */
1240  if (!ast_strlen_zero(password) && password[0] == '*')
1241  return 1;
1242  if (!ast_strlen_zero(ext_pass_check_cmd)) {
1243  char cmd[255], buf[255];
1244 
1245  ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
1246 
1247  snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
1248  if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
1249  ast_debug(5, "Result: %s\n", buf);
1250  if (!strncasecmp(buf, "VALID", 5)) {
1251  ast_debug(3, "Passed password check: '%s'\n", buf);
1252  return 0;
1253  } else if (!strncasecmp(buf, "FAILURE", 7)) {
1254  ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
1255  return 0;
1256  } else {
1257  ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
1258  return 1;
1259  }
1260  }
1261  }
1262  return 0;
1263 }
1264 
1265 /*!
1266  * \brief Performs a change of the voicemail passowrd in the realtime engine.
1267  * \param vmu The voicemail user to change the password for.
1268  * \param password The new value to be set to the password for this user.
1269  *
1270  * This only works if there is a realtime engine configured.
1271  * This is called from the (top level) vm_change_password.
1272  *
1273  * \return zero on success, -1 on error.
1274  */
1275 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
1276 {
1277  int res = -1;
1278  if (!strcmp(vmu->password, password)) {
1279  /* No change (but an update would return 0 rows updated, so we opt out here) */
1280  return 0;
1281  }
1282 
1283  if (strlen(password) > 10) {
1284  ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
1285  }
1286  if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
1287  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
1288  ast_copy_string(vmu->password, password, sizeof(vmu->password));
1289  res = 0;
1290  }
1291  return res;
1292 }
1293 
1294 /*!
1295  * \brief Destructively Parse options and apply.
1296  */
1297 static void apply_options(struct ast_vm_user *vmu, const char *options)
1298 {
1299  char *stringp;
1300  char *s;
1301  char *var, *value;
1302  stringp = ast_strdupa(options);
1303  while ((s = strsep(&stringp, "|"))) {
1304  value = s;
1305  if ((var = strsep(&value, "=")) && value) {
1306  apply_option(vmu, var, value);
1307  }
1308  }
1309 }
1310 
1311 /*!
1312  * \brief Loads the options specific to a voicemail user.
1313  *
1314  * This is called when a vm_user structure is being set up, such as from load_options.
1315  */
1316 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
1317 {
1318  for (; var; var = var->next) {
1319  if (!strcasecmp(var->name, "vmsecret")) {
1320  ast_copy_string(retval->password, var->value, sizeof(retval->password));
1321  } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
1322  if (ast_strlen_zero(retval->password)) {
1323  if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
1324  ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
1325  "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
1326  } else {
1327  ast_copy_string(retval->password, var->value, sizeof(retval->password));
1328  }
1329  }
1330  } else if (!strcasecmp(var->name, "uniqueid")) {
1331  ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
1332  } else if (!strcasecmp(var->name, "pager")) {
1333  ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
1334  } else if (!strcasecmp(var->name, "email")) {
1335  ast_copy_string(retval->email, var->value, sizeof(retval->email));
1336  } else if (!strcasecmp(var->name, "fullname")) {
1337  ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
1338  } else if (!strcasecmp(var->name, "context")) {
1339  ast_copy_string(retval->context, var->value, sizeof(retval->context));
1340  } else if (!strcasecmp(var->name, "emailsubject")) {
1341  ast_free(retval->emailsubject);
1343  } else if (!strcasecmp(var->name, "emailbody")) {
1344  ast_free(retval->emailbody);
1345  retval->emailbody = ast_strdup(substitute_escapes(var->value));
1346 #ifdef IMAP_STORAGE
1347  } else if (!strcasecmp(var->name, "imapuser")) {
1348  ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
1349  retval->imapversion = imapversion;
1350  } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
1351  ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
1352  retval->imapversion = imapversion;
1353  } else if (!strcasecmp(var->name, "imapfolder")) {
1354  ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
1355  } else if (!strcasecmp(var->name, "imapvmshareid")) {
1356  ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
1357  retval->imapversion = imapversion;
1358 #endif
1359  } else
1360  apply_option(retval, var->name, var->value);
1361  }
1362 }
1363 
1364 /*!
1365  * \brief Determines if a DTMF key entered is valid.
1366  * \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.
1367  *
1368  * Tests the character entered against the set of valid DTMF characters.
1369  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1370  */
1371 static int is_valid_dtmf(const char *key)
1372 {
1373  int i;
1374  char *local_key = ast_strdupa(key);
1375 
1376  for (i = 0; i < strlen(key); ++i) {
1377  if (!strchr(VALID_DTMF, *local_key)) {
1378  ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1379  return 0;
1380  }
1381  local_key++;
1382  }
1383  return 1;
1384 }
1385 
1386 /*!
1387  * \brief Finds a voicemail user from the realtime engine.
1388  * \param ivm
1389  * \param context
1390  * \param mailbox
1391  *
1392  * 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.
1393  *
1394  * \return The ast_vm_user structure for the user that was found.
1395  */
1396 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1397 {
1398  struct ast_variable *var;
1399  struct ast_vm_user *retval;
1400 
1401  if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1402  if (ivm) {
1403  memset(retval, 0, sizeof(*retval));
1404  }
1405  populate_defaults(retval);
1406  if (!ivm) {
1407  ast_set_flag(retval, VM_ALLOCED);
1408  }
1409  if (mailbox) {
1410  ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1411  }
1412  if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
1413  var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1414  } else {
1415  var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1416  }
1417  if (var) {
1418  apply_options_full(retval, var);
1419  ast_variables_destroy(var);
1420  } else {
1421  if (!ivm)
1422  free_user(retval);
1423  retval = NULL;
1424  }
1425  }
1426  return retval;
1427 }
1428 
1429 /*!
1430  * \brief Finds a voicemail user from the users file or the realtime engine.
1431  * \param ivm
1432  * \param context
1433  * \param mailbox
1434  *
1435  * \return The ast_vm_user structure for the user that was found.
1436  */
1437 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1438 {
1439  /* This function could be made to generate one from a database, too */
1440  struct ast_vm_user *vmu = NULL, *cur;
1441  AST_LIST_LOCK(&users);
1442 
1443  if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
1444  context = "default";
1445 
1446  AST_LIST_TRAVERSE(&users, cur, list) {
1447 #ifdef IMAP_STORAGE
1448  if (cur->imapversion != imapversion) {
1449  continue;
1450  }
1451 #endif
1452  if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1453  break;
1454  if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1455  break;
1456  }
1457  if (cur) {
1458  /* Make a copy, so that on a reload, we have no race */
1459  if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
1460  *vmu = *cur;
1461  if (!ivm) {
1462  vmu->emailbody = ast_strdup(cur->emailbody);
1463  vmu->emailsubject = ast_strdup(cur->emailsubject);
1464  }
1465  ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1466  AST_LIST_NEXT(vmu, list) = NULL;
1467  }
1468  } else
1469  vmu = find_user_realtime(ivm, context, mailbox);
1471  return vmu;
1472 }
1473 
1474 /*!
1475  * \brief Resets a user password to a specified password.
1476  * \param context
1477  * \param mailbox
1478  * \param newpass
1479  *
1480  * This does the actual change password work, called by the vm_change_password() function.
1481  *
1482  * \return zero on success, -1 on error.
1483  */
1484 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
1485 {
1486  /* This function could be made to generate one from a database, too */
1487  struct ast_vm_user *cur;
1488  int res = -1;
1489  AST_LIST_LOCK(&users);
1490  AST_LIST_TRAVERSE(&users, cur, list) {
1491  if ((!context || !strcasecmp(context, cur->context)) &&
1492  (!strcasecmp(mailbox, cur->mailbox)))
1493  break;
1494  }
1495  if (cur) {
1496  ast_copy_string(cur->password, newpass, sizeof(cur->password));
1497  res = 0;
1498  }
1500  return res;
1501 }
1502 
1503 /*!
1504  * \brief Check if configuration file is valid
1505  */
1506 static inline int valid_config(const struct ast_config *cfg)
1507 {
1508  return cfg && cfg != CONFIG_STATUS_FILEINVALID;
1509 }
1510 
1511 /*!
1512  * \brief The handler for the change password option.
1513  * \param vmu The voicemail user to work with.
1514  * \param newpassword The new password (that has been gathered from the appropriate prompting).
1515  * 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.
1516  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
1517  */
1518 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
1519 {
1520  struct ast_config *cfg = NULL;
1521  struct ast_variable *var = NULL;
1522  struct ast_category *cat = NULL;
1523  char *category = NULL, *value = NULL, *new = NULL;
1524  const char *tmp = NULL;
1525  struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
1526  char secretfn[PATH_MAX] = "";
1527  int found = 0;
1528 
1529  if (!change_password_realtime(vmu, newpassword))
1530  return;
1531 
1532  /* check if we should store the secret in the spool directory next to the messages */
1533  switch (vmu->passwordlocation) {
1534  case OPT_PWLOC_SPOOLDIR:
1535  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
1536  if (write_password_to_file(secretfn, newpassword) == 0) {
1537  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
1538  ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
1539  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1540  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1541  break;
1542  } else {
1543  ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
1544  }
1545  /* Fall-through */
1547  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
1548  while ((category = ast_category_browse(cfg, category))) {
1549  if (!strcasecmp(category, vmu->context)) {
1550  if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
1551  ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
1552  break;
1553  }
1554  value = strstr(tmp, ",");
1555  if (!value) {
1556  new = ast_alloca(strlen(newpassword)+1);
1557  sprintf(new, "%s", newpassword);
1558  } else {
1559  new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
1560  sprintf(new, "%s%s", newpassword, value);
1561  }
1562  if (!(cat = ast_category_get(cfg, category))) {
1563  ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
1564  break;
1565  }
1566  ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
1567  found = 1;
1568  }
1569  }
1570  /* save the results */
1571  if (found) {
1572  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
1573  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1574  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1575  ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
1576  ast_config_destroy(cfg);
1577  break;
1578  }
1579 
1580  ast_config_destroy(cfg);
1581  }
1582  /* Fall-through */
1583  case OPT_PWLOC_USERSCONF:
1584  /* check users.conf and update the password stored for the mailbox */
1585  /* if no vmsecret entry exists create one. */
1586  if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
1587  ast_debug(4, "we are looking for %s\n", vmu->mailbox);
1588  for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
1589  ast_debug(4, "users.conf: %s\n", category);
1590  if (!strcasecmp(category, vmu->mailbox)) {
1591  if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
1592  ast_debug(3, "looks like we need to make vmsecret!\n");
1593  var = ast_variable_new("vmsecret", newpassword, "");
1594  } else {
1595  var = NULL;
1596  }
1597  new = ast_alloca(strlen(newpassword) + 1);
1598  sprintf(new, "%s", newpassword);
1599  if (!(cat = ast_category_get(cfg, category))) {
1600  ast_debug(4, "failed to get category!\n");
1601  ast_free(var);
1602  break;
1603  }
1604  if (!var) {
1605  ast_variable_update(cat, "vmsecret", new, NULL, 0);
1606  } else {
1607  ast_variable_append(cat, var);
1608  }
1609  found = 1;
1610  break;
1611  }
1612  }
1613  /* save the results and clean things up */
1614  if (found) {
1615  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
1616  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1617  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1618  ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
1619  }
1620 
1621  ast_config_destroy(cfg);
1622  }
1623  }
1624 }
1625 
1626 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
1627 {
1628  char buf[255];
1629  snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
1630  ast_debug(1, "External password: %s\n",buf);
1631  if (!ast_safe_system(buf)) {
1632  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
1633  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1634  /* Reset the password in memory, too */
1635  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1636  }
1637 }
1638 
1639 /*!
1640  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1641  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1642  * \param len The length of the path string that was written out.
1643  * \param context
1644  * \param ext
1645  * \param folder
1646  *
1647  * The path is constructed as
1648  * VM_SPOOL_DIRcontext/ext/folder
1649  *
1650  * \return zero on success, -1 on error.
1651  */
1652 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
1653 {
1654  return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
1655 }
1656 
1657 /*!
1658  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1659  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1660  * \param len The length of the path string that was written out.
1661  * \param dir
1662  * \param num
1663  *
1664  * The path is constructed as
1665  * VM_SPOOL_DIRcontext/ext/folder
1666  *
1667  * \return zero on success, -1 on error.
1668  */
1669 static int make_file(char *dest, const int len, const char *dir, const int num)
1670 {
1671  return snprintf(dest, len, "%s/msg%04d", dir, num);
1672 }
1673 
1674 /* same as mkstemp, but return a FILE * */
1675 static FILE *vm_mkftemp(char *template)
1676 {
1677  FILE *p = NULL;
1678  int pfd = mkstemp(template);
1679  chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1680  if (pfd > -1) {
1681  p = fdopen(pfd, "w+");
1682  if (!p) {
1683  close(pfd);
1684  pfd = -1;
1685  }
1686  }
1687  return p;
1688 }
1689 
1690 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1691  * \param dest String. base directory.
1692  * \param len Length of dest.
1693  * \param context String. Ignored if is null or empty string.
1694  * \param ext String. Ignored if is null or empty string.
1695  * \param folder String. Ignored if is null or empty string.
1696  * \return -1 on failure, 0 on success.
1697  */
1698 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1699 {
1700  mode_t mode = VOICEMAIL_DIR_MODE;
1701  int res;
1702 
1703  make_dir(dest, len, context, ext, folder);
1704  if ((res = ast_mkdir(dest, mode))) {
1705  ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1706  return -1;
1707  }
1708  return 0;
1709 }
1710 
1711 static const char * const mailbox_folders[] = {
1712 #ifdef IMAP_STORAGE
1713  imapfolder,
1714 #else
1715  "INBOX",
1716 #endif
1717  "Old",
1718  "Work",
1719  "Family",
1720  "Friends",
1721  "Cust1",
1722  "Cust2",
1723  "Cust3",
1724  "Cust4",
1725  "Cust5",
1726  "Deleted",
1727  "Urgent",
1728 };
1729 
1730 static const char *mbox(struct ast_vm_user *vmu, int id)
1731 {
1732 #ifdef IMAP_STORAGE
1733  if (vmu && id == 0) {
1734  return vmu->imapfolder;
1735  }
1736 #endif
1737  return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
1738 }
1739 
1740 static int get_folder_by_name(const char *name)
1741 {
1742  size_t i;
1743 
1744  for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
1745  if (strcasecmp(name, mailbox_folders[i]) == 0) {
1746  return i;
1747  }
1748  }
1749 
1750  return -1;
1751 }
1752 
1753 static void free_user(struct ast_vm_user *vmu)
1754 {
1755  if (ast_test_flag(vmu, VM_ALLOCED)) {
1756 
1757  ast_free(vmu->emailbody);
1758  vmu->emailbody = NULL;
1759 
1760  ast_free(vmu->emailsubject);
1761  vmu->emailsubject = NULL;
1762 
1763  ast_free(vmu);
1764  }
1765 }
1766 
1767 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
1768 
1769  int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
1770 
1771  /* remove old allocation */
1772  if (vms->deleted) {
1773  ast_free(vms->deleted);
1774  vms->deleted = NULL;
1775  }
1776  if (vms->heard) {
1777  ast_free(vms->heard);
1778  vms->heard = NULL;
1779  }
1780  vms->dh_arraysize = 0;
1781 
1782  if (arraysize > 0) {
1783  if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
1784  return -1;
1785  }
1786  if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
1787  ast_free(vms->deleted);
1788  vms->deleted = NULL;
1789  return -1;
1790  }
1791  vms->dh_arraysize = arraysize;
1792  }
1793 
1794  return 0;
1795 }
1796 
1797 /* All IMAP-specific functions should go in this block. This
1798  * keeps them from being spread out all over the code */
1799 #ifdef IMAP_STORAGE
1800 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
1801 {
1802  char arg[10];
1803  struct vm_state *vms;
1804  unsigned long messageNum;
1805 
1806  /* If greetings aren't stored in IMAP, just delete the file */
1807  if (msgnum < 0 && !imapgreetings) {
1808  ast_filedelete(file, NULL);
1809  return;
1810  }
1811 
1812  if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1813  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);
1814  return;
1815  }
1816 
1817  if (msgnum < 0) {
1818  imap_delete_old_greeting(file, vms);
1819  return;
1820  }
1821 
1822  /* find real message number based on msgnum */
1823  /* this may be an index into vms->msgArray based on the msgnum. */
1824  messageNum = vms->msgArray[msgnum];
1825  if (messageNum == 0) {
1826  ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
1827  return;
1828  }
1829  if (option_debug > 2)
1830  ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
1831  /* delete message */
1832  snprintf (arg, sizeof(arg), "%lu", messageNum);
1833  ast_mutex_lock(&vms->lock);
1834  mail_setflag (vms->mailstream, arg, "\\DELETED");
1835  mail_expunge(vms->mailstream);
1836  ast_mutex_unlock(&vms->lock);
1837 }
1838 
1839 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
1840 {
1841  struct vm_state *vms_p;
1842  char *file, *filename;
1843  char *attachment;
1844  int i;
1845  BODY *body;
1846 
1847  /* This function is only used for retrieval of IMAP greetings
1848  * regular messages are not retrieved this way, nor are greetings
1849  * if they are stored locally*/
1850  if (msgnum > -1 || !imapgreetings) {
1851  return 0;
1852  } else {
1853  file = strrchr(ast_strdupa(dir), '/');
1854  if (file)
1855  *file++ = '\0';
1856  else {
1857  ast_debug (1, "Failed to procure file name from directory passed.\n");
1858  return -1;
1859  }
1860  }
1861 
1862  /* check if someone is accessing this box right now... */
1863  if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) &&
1864  !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1865  /* Unlike when retrieving a message, it is reasonable not to be able to find a
1866  * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
1867  * that's all we need to do.
1868  */
1869  if (!(vms_p = create_vm_state_from_user(vmu))) {
1870  ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
1871  return -1;
1872  }
1873  }
1874 
1875  /* Greetings will never have a prepended message */
1876  *vms_p->introfn = '\0';
1877 
1878  ast_mutex_lock(&vms_p->lock);
1879  init_mailstream(vms_p, GREETINGS_FOLDER);
1880  if (!vms_p->mailstream) {
1881  ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
1882  ast_mutex_unlock(&vms_p->lock);
1883  return -1;
1884  }
1885 
1886  /*XXX Yuck, this could probably be done a lot better */
1887  for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
1888  mail_fetchstructure(vms_p->mailstream, i + 1, &body);
1889  /* We have the body, now we extract the file name of the first attachment. */
1890  if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1891  attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
1892  } else {
1893  ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
1894  ast_mutex_unlock(&vms_p->lock);
1895  return -1;
1896  }
1897  filename = strsep(&attachment, ".");
1898  if (!strcmp(filename, file)) {
1899  ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
1900  vms_p->msgArray[vms_p->curmsg] = i + 1;
1901  save_body(body, vms_p, "2", attachment, 0);
1902  ast_mutex_unlock(&vms_p->lock);
1903  return 0;
1904  }
1905  }
1906  ast_mutex_unlock(&vms_p->lock);
1907 
1908  return -1;
1909 }
1910 
1911 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
1912 {
1913  BODY *body;
1914  char *header_content;
1915  char *attachedfilefmt;
1916  char buf[80];
1917  struct vm_state *vms;
1918  char text_file[PATH_MAX];
1919  FILE *text_file_ptr;
1920  int res = 0;
1921  struct ast_vm_user *vmu;
1922 
1923  if (!(vmu = find_user(NULL, context, mailbox))) {
1924  ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
1925  return -1;
1926  }
1927 
1928  if (msgnum < 0) {
1929  if (imapgreetings) {
1930  res = imap_retrieve_greeting(dir, msgnum, vmu);
1931  goto exit;
1932  } else {
1933  res = 0;
1934  goto exit;
1935  }
1936  }
1937 
1938  /* Before anything can happen, we need a vm_state so that we can
1939  * actually access the imap server through the vms->mailstream
1940  */
1941  if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1942  /* This should not happen. If it does, then I guess we'd
1943  * need to create the vm_state, extract which mailbox to
1944  * open, and then set up the msgArray so that the correct
1945  * IMAP message could be accessed. If I have seen correctly
1946  * though, the vms should be obtainable from the vmstates list
1947  * and should have its msgArray properly set up.
1948  */
1949  ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
1950  res = -1;
1951  goto exit;
1952  }
1953 
1954  make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
1955  snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
1956 
1957  /* Don't try to retrieve a message from IMAP if it already is on the file system */
1958  if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
1959  res = 0;
1960  goto exit;
1961  }
1962 
1963  if (option_debug > 2)
1964  ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
1965  if (vms->msgArray[msgnum] == 0) {
1966  ast_log(LOG_WARNING, "Trying to access unknown message\n");
1967  res = -1;
1968  goto exit;
1969  }
1970 
1971  /* This will only work for new messages... */
1972  ast_mutex_lock(&vms->lock);
1973  header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
1974  ast_mutex_unlock(&vms->lock);
1975  /* empty string means no valid header */
1976  if (ast_strlen_zero(header_content)) {
1977  ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
1978  res = -1;
1979  goto exit;
1980  }
1981 
1982  ast_mutex_lock(&vms->lock);
1983  mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
1984  ast_mutex_unlock(&vms->lock);
1985 
1986  /* We have the body, now we extract the file name of the first attachment. */
1987  if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1988  attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
1989  } else {
1990  ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
1991  res = -1;
1992  goto exit;
1993  }
1994 
1995  /* Find the format of the attached file */
1996 
1997  strsep(&attachedfilefmt, ".");
1998  if (!attachedfilefmt) {
1999  ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
2000  res = -1;
2001  goto exit;
2002  }
2003 
2004  save_body(body, vms, "2", attachedfilefmt, 0);
2005  if (save_body(body, vms, "3", attachedfilefmt, 1)) {
2006  *vms->introfn = '\0';
2007  }
2008 
2009  /* Get info from headers!! */
2010  snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
2011 
2012  if (!(text_file_ptr = fopen(text_file, "w"))) {
2013  ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
2014  }
2015 
2016  fprintf(text_file_ptr, "%s\n", "[message]");
2017 
2018  get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
2019  fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
2020  get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
2021  fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
2022  get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
2023  fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
2024  get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
2025  fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
2026  get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
2027  fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
2028  get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
2029  fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
2030  get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
2031  fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
2032  fclose(text_file_ptr);
2033 
2034 exit:
2035  free_user(vmu);
2036  return res;
2037 }
2038 
2039 static int folder_int(const char *folder)
2040 {
2041  /*assume a NULL folder means INBOX*/
2042  if (!folder) {
2043  return 0;
2044  }
2045  if (!strcasecmp(folder, imapfolder)) {
2046  return 0;
2047  } else if (!strcasecmp(folder, "Old")) {
2048  return 1;
2049  } else if (!strcasecmp(folder, "Work")) {
2050  return 2;
2051  } else if (!strcasecmp(folder, "Family")) {
2052  return 3;
2053  } else if (!strcasecmp(folder, "Friends")) {
2054  return 4;
2055  } else if (!strcasecmp(folder, "Cust1")) {
2056  return 5;
2057  } else if (!strcasecmp(folder, "Cust2")) {
2058  return 6;
2059  } else if (!strcasecmp(folder, "Cust3")) {
2060  return 7;
2061  } else if (!strcasecmp(folder, "Cust4")) {
2062  return 8;
2063  } else if (!strcasecmp(folder, "Cust5")) {
2064  return 9;
2065  } else if (!strcasecmp(folder, "Urgent")) {
2066  return 11;
2067  } else { /*assume they meant INBOX if folder is not found otherwise*/
2068  return 0;
2069  }
2070 }
2071 
2072 static int __messagecount(const char *context, const char *mailbox, const char *folder)
2073 {
2074  SEARCHPGM *pgm;
2075  SEARCHHEADER *hdr;
2076 
2077  struct ast_vm_user *vmu, vmus;
2078  struct vm_state *vms_p;
2079  int ret = 0;
2080  int fold = folder_int(folder);
2081  int urgent = 0;
2082 
2083  /* If URGENT, then look at INBOX */
2084  if (fold == 11) {
2085  fold = NEW_FOLDER;
2086  urgent = 1;
2087  }
2088 
2089  if (ast_strlen_zero(mailbox))
2090  return 0;
2091 
2092  /* We have to get the user before we can open the stream! */
2093  vmu = find_user(&vmus, context, mailbox);
2094  if (!vmu) {
2095  ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
2096  return -1;
2097  } else {
2098  /* No IMAP account available */
2099  if (vmu->imapuser[0] == '\0') {
2100  ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2101  return -1;
2102  }
2103  }
2104 
2105  /* No IMAP account available */
2106  if (vmu->imapuser[0] == '\0') {
2107  ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2108  free_user(vmu);
2109  return -1;
2110  }
2111  ast_assert(msgnum < vms->msg_array_max);
2112 
2113  /* check if someone is accessing this box right now... */
2114  vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
2115  if (!vms_p) {
2116  vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
2117  }
2118  if (vms_p) {
2119  ast_debug(3, "Returning before search - user is logged in\n");
2120  if (fold == 0) { /* INBOX */
2121  return urgent ? vms_p->urgentmessages : vms_p->newmessages;
2122  }
2123  if (fold == 1) { /* Old messages */
2124  return vms_p->oldmessages;
2125  }
2126  }
2127 
2128  /* add one if not there... */
2129  vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
2130  if (!vms_p) {
2131  vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
2132  }
2133 
2134  if (!vms_p) {
2135  vms_p = create_vm_state_from_user(vmu);
2136  }
2137  ret = init_mailstream(vms_p, fold);
2138  if (!vms_p->mailstream) {
2139  ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
2140  return -1;
2141  }
2142  if (ret == 0) {
2143  ast_mutex_lock(&vms_p->lock);
2144  pgm = mail_newsearchpgm ();
2145  hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
2146  hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
2147  pgm->header = hdr;
2148  if (fold != OLD_FOLDER) {
2149  pgm->unseen = 1;
2150  pgm->seen = 0;
2151  }
2152  /* In the special case where fold is 1 (old messages) we have to do things a bit
2153  * differently. Old messages are stored in the INBOX but are marked as "seen"
2154  */
2155  else {
2156  pgm->unseen = 0;
2157  pgm->seen = 1;
2158  }
2159  /* look for urgent messages */
2160  if (fold == NEW_FOLDER) {
2161  if (urgent) {
2162  pgm->flagged = 1;
2163  pgm->unflagged = 0;
2164  } else {
2165  pgm->flagged = 0;
2166  pgm->unflagged = 1;
2167  }
2168  }
2169  pgm->undeleted = 1;
2170  pgm->deleted = 0;
2171 
2172  vms_p->vmArrayIndex = 0;
2173  mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2174  if (fold == 0 && urgent == 0)
2175  vms_p->newmessages = vms_p->vmArrayIndex;
2176  if (fold == 1)
2177  vms_p->oldmessages = vms_p->vmArrayIndex;
2178  if (fold == 0 && urgent == 1)
2179  vms_p->urgentmessages = vms_p->vmArrayIndex;
2180  /*Freeing the searchpgm also frees the searchhdr*/
2181  mail_free_searchpgm(&pgm);
2182  ast_mutex_unlock(&vms_p->lock);
2183  vms_p->updated = 0;
2184  return vms_p->vmArrayIndex;
2185  } else {
2186  ast_mutex_lock(&vms_p->lock);
2187  mail_ping(vms_p->mailstream);
2188  ast_mutex_unlock(&vms_p->lock);
2189  }
2190  return 0;
2191 }
2192 
2193 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
2194 {
2195  /* Check if mailbox is full */
2196  check_quota(vms, vmu->imapfolder);
2197  if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
2198  ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
2199  ast_play_and_wait(chan, "vm-mailboxfull");
2200  return -1;
2201  }
2202 
2203  /* Check if we have exceeded maxmsg */
2204  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));
2205  if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
2206  ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
2207  ast_play_and_wait(chan, "vm-mailboxfull");
2208  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2209  return -1;
2210  }
2211 
2212  return 0;
2213 }
2214 
2215 /*!
2216  * \brief Gets the number of messages that exist in a mailbox folder.
2217  * \param context
2218  * \param mailbox
2219  * \param folder
2220  *
2221  * This method is used when IMAP backend is used.
2222  * \return The number of messages in this mailbox folder (zero or more).
2223  */
2224 static int messagecount(const char *context, const char *mailbox, const char *folder)
2225 {
2226  if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
2227  return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
2228  } else {
2229  return __messagecount(context, mailbox, folder);
2230  }
2231 }
2232 
2233 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)
2234 {
2235  char *myserveremail = serveremail;
2236  char fn[PATH_MAX];
2237  char introfn[PATH_MAX];
2238  char mailbox[256];
2239  char *stringp;
2240  FILE *p = NULL;
2241  char tmp[80] = "/tmp/astmail-XXXXXX";
2242  long len;
2243  void *buf;
2244  int tempcopy = 0;
2245  STRING str;
2246  int ret; /* for better error checking */
2247  char *imap_flags = NIL;
2248  int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
2249  int box = NEW_FOLDER;
2250 
2251  /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
2252  if (msgnum < 0) {
2253  if(!imapgreetings) {
2254  return 0;
2255  } else {
2256  box = GREETINGS_FOLDER;
2257  }
2258  }
2259 
2260  if (imap_check_limits(chan, vms, vmu, msgcount)) {
2261  return -1;
2262  }
2263 
2264  /* Set urgent flag for IMAP message */
2265  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
2266  ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
2267  imap_flags = "\\FLAGGED";
2268  }
2269 
2270  /* Attach only the first format */
2271  fmt = ast_strdupa(fmt);
2272  stringp = fmt;
2273  strsep(&stringp, "|");
2274 
2275  if (!ast_strlen_zero(vmu->serveremail))
2276  myserveremail = vmu->serveremail;
2277 
2278  if (msgnum > -1)
2279  make_file(fn, sizeof(fn), dir, msgnum);
2280  else
2281  ast_copy_string (fn, dir, sizeof(fn));
2282 
2283  snprintf(introfn, sizeof(introfn), "%sintro", fn);
2284  if (ast_fileexists(introfn, NULL, NULL) <= 0) {
2285  *introfn = '\0';
2286  }
2287 
2288  if (ast_strlen_zero(vmu->email)) {
2289  /* We need the vmu->email to be set when we call make_email_file, but
2290  * if we keep it set, a duplicate e-mail will be created. So at the end
2291  * of this function, we will revert back to an empty string if tempcopy
2292  * is 1.
2293  */
2294  ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2295  tempcopy = 1;
2296  }
2297 
2298  if (!strcmp(fmt, "wav49"))
2299  fmt = "WAV";
2300  ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2301 
2302  /* Make a temporary file instead of piping directly to sendmail, in case the mail
2303  command hangs. */
2304  if (!(p = vm_mkftemp(tmp))) {
2305  ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2306  if (tempcopy)
2307  *(vmu->email) = '\0';
2308  return -1;
2309  }
2310 
2311  if (msgnum < 0 && imapgreetings) {
2312  if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
2313  ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
2314  return -1;
2315  }
2316  imap_delete_old_greeting(fn, vms);
2317  }
2318 
2319  make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
2320  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
2321  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
2322  fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
2323  /* read mail file to memory */
2324  len = ftell(p);
2325  rewind(p);
2326  if (!(buf = ast_malloc(len + 1))) {
2327  ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
2328  fclose(p);
2329  if (tempcopy)
2330  *(vmu->email) = '\0';
2331  return -1;
2332  }
2333  if (fread(buf, len, 1, p) < len) {
2334  if (ferror(p)) {
2335  ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
2336  return -1;
2337  }
2338  }
2339  ((char *) buf)[len] = '\0';
2340  INIT(&str, mail_string, buf, len);
2341  ret = init_mailstream(vms, box);
2342  if (ret == 0) {
2343  imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
2344  ast_mutex_lock(&vms->lock);
2345  if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
2346  ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2347  ast_mutex_unlock(&vms->lock);
2348  fclose(p);
2349  unlink(tmp);
2350  ast_free(buf);
2351  } else {
2352  ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
2353  fclose(p);
2354  unlink(tmp);
2355  ast_free(buf);
2356  return -1;
2357  }
2358  ast_debug(3, "%s stored\n", fn);
2359 
2360  if (tempcopy)
2361  *(vmu->email) = '\0';
2362  inprocess_count(vmu->mailbox, vmu->context, -1);
2363  return 0;
2364 
2365 }
2366 
2367 /*!
2368  * \brief Gets the number of messages that exist in the inbox folder.
2369  * \param mailbox_context
2370  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
2371  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
2372  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
2373  *
2374  * This method is used when IMAP backend is used.
2375  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
2376  *
2377  * \return zero on success, -1 on error.
2378  */
2379 
2380 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
2381 {
2382  char tmp[PATH_MAX] = "";
2383  char *mailboxnc;
2384  char *context;
2385  char *mb;
2386  char *cur;
2387  if (newmsgs)
2388  *newmsgs = 0;
2389  if (oldmsgs)
2390  *oldmsgs = 0;
2391  if (urgentmsgs)
2392  *urgentmsgs = 0;
2393 
2394  ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
2395  /* If no mailbox, return immediately */
2396  if (ast_strlen_zero(mailbox_context))
2397  return 0;
2398 
2399  ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2400  context = strchr(tmp, '@');
2401  if (strchr(mailbox_context, ',')) {
2402  int tmpnew, tmpold, tmpurgent;
2403  ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2404  mb = tmp;
2405  while ((cur = strsep(&mb, ", "))) {
2406  if (!ast_strlen_zero(cur)) {
2407  if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2408  return -1;
2409  else {
2410  if (newmsgs)
2411  *newmsgs += tmpnew;
2412  if (oldmsgs)
2413  *oldmsgs += tmpold;
2414  if (urgentmsgs)
2415  *urgentmsgs += tmpurgent;
2416  }
2417  }
2418  }
2419  return 0;
2420  }
2421  if (context) {
2422  *context = '\0';
2423  mailboxnc = tmp;
2424  context++;
2425  } else {
2426  context = "default";
2427  mailboxnc = (char *) mailbox_context;
2428  }
2429 
2430  if (newmsgs) {
2431  struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
2432  if (!vmu) {
2433  ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2434  return -1;
2435  }
2436  if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
2437  free_user(vmu);
2438  return -1;
2439  }
2440  free_user(vmu);
2441  }
2442  if (oldmsgs) {
2443  if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
2444  return -1;
2445  }
2446  }
2447  if (urgentmsgs) {
2448  if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
2449  return -1;
2450  }
2451  }
2452  return 0;
2453 }
2454 
2455 /**
2456  * \brief Determines if the given folder has messages.
2457  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
2458  * \param folder the folder to look in
2459  *
2460  * This function is used when the mailbox is stored in an IMAP back end.
2461  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
2462  * \return 1 if the folder has one or more messages. zero otherwise.
2463  */
2464 
2465 static int has_voicemail(const char *mailbox, const char *folder)
2466 {
2467  char tmp[256], *tmp2, *box, *context;
2468  ast_copy_string(tmp, mailbox, sizeof(tmp));
2469  tmp2 = tmp;
2470  if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
2471  while ((box = strsep(&tmp2, ",&"))) {
2472  if (!ast_strlen_zero(box)) {
2473  if (has_voicemail(box, folder)) {
2474  return 1;
2475  }
2476  }
2477  }
2478  }
2479  if ((context = strchr(tmp, '@'))) {
2480  *context++ = '\0';
2481  } else {
2482  context = "default";
2483  }
2484  return __messagecount(context, tmp, folder) ? 1 : 0;
2485 }
2486 
2487 /*!
2488  * \brief Copies a message from one mailbox to another.
2489  * \param chan
2490  * \param vmu
2491  * \param imbox
2492  * \param msgnum
2493  * \param duration
2494  * \param recip
2495  * \param fmt
2496  * \param dir
2497  *
2498  * This works with IMAP storage based mailboxes.
2499  *
2500  * \return zero on success, -1 on error.
2501  */
2502 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)
2503 {
2504  struct vm_state *sendvms = NULL, *destvms = NULL;
2505  char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2506  if (msgnum >= recip->maxmsg) {
2507  ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
2508  return -1;
2509  }
2510  if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
2511  ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2512  return -1;
2513  }
2514  if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
2515  ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2516  return -1;
2517  }
2518  snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2519  ast_mutex_lock(&sendvms->lock);
2520  if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
2521  ast_mutex_unlock(&sendvms->lock);
2522  return 0;
2523  }
2524  ast_mutex_unlock(&sendvms->lock);
2525  ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2526  return -1;
2527 }
2528 
2529 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
2530 {
2531  char tmp[256], *t = tmp;
2532  size_t left = sizeof(tmp);
2533 
2534  if (box == OLD_FOLDER) {
2535  ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
2536  } else {
2537  ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
2538  }
2539 
2540  if (box == NEW_FOLDER) {
2541  ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
2542  } else {
2543  snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
2544  }
2545 
2546  /* Build up server information */
2547  ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
2548 
2549  /* Add authentication user if present */
2550  if (!ast_strlen_zero(authuser))
2551  ast_build_string(&t, &left, "/authuser=%s", authuser);
2552 
2553  /* Add flags if present */
2554  if (!ast_strlen_zero(imapflags))
2555  ast_build_string(&t, &left, "/%s", imapflags);
2556 
2557  /* End with username */
2558 #if 1
2559  ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
2560 #else
2561  ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
2562 #endif
2563  if (box == NEW_FOLDER || box == OLD_FOLDER)
2564  snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
2565  else if (box == GREETINGS_FOLDER)
2566  snprintf(spec, len, "%s%s", tmp, greetingfolder);
2567  else { /* Other folders such as Friends, Family, etc... */
2568  if (!ast_strlen_zero(imapparentfolder)) {
2569  /* imapparentfolder would typically be set to INBOX */
2570  snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
2571  } else {
2572  snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
2573  }
2574  }
2575 }
2576 
2577 static int init_mailstream(struct vm_state *vms, int box)
2578 {
2579  MAILSTREAM *stream = NIL;
2580  long debug;
2581  char tmp[256];
2582 
2583  if (!vms) {
2584  ast_log(LOG_ERROR, "vm_state is NULL!\n");
2585  return -1;
2586  }
2587  if (option_debug > 2)
2588  ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
2589  if (vms->mailstream == NIL || !vms->mailstream) {
2590  if (option_debug)
2591  ast_log(LOG_DEBUG, "mailstream not set.\n");
2592  } else {
2593  stream = vms->mailstream;
2594  }
2595  /* debug = T; user wants protocol telemetry? */
2596  debug = NIL; /* NO protocol telemetry? */
2597 
2598  if (delimiter == '\0') { /* did not probe the server yet */
2599  char *cp;
2600 #ifdef USE_SYSTEM_IMAP
2601 #include <imap/linkage.c>
2602 #elif defined(USE_SYSTEM_CCLIENT)
2603 #include <c-client/linkage.c>
2604 #else
2605 #include "linkage.c"
2606 #endif
2607  /* Connect to INBOX first to get folders delimiter */
2608  imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
2609  ast_mutex_lock(&vms->lock);
2610  stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2611  ast_mutex_unlock(&vms->lock);
2612  if (stream == NIL) {
2613  ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
2614  return -1;
2615  }
2616  get_mailbox_delimiter(stream);
2617  /* update delimiter in imapfolder */
2618  for (cp = vms->imapfolder; *cp; cp++)
2619  if (*cp == '/')
2620  *cp = delimiter;
2621  }
2622  /* Now connect to the target folder */
2623  imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
2624  if (option_debug > 2)
2625  ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
2626  ast_mutex_lock(&vms->lock);
2627  vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2628  ast_mutex_unlock(&vms->lock);
2629  if (vms->mailstream == NIL) {
2630  return -1;
2631  } else {
2632  return 0;
2633  }
2634 }
2635 
2636 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
2637 {
2638  SEARCHPGM *pgm;
2639  SEARCHHEADER *hdr;
2640  int ret, urgent = 0;
2641 
2642  /* If Urgent, then look at INBOX */
2643  if (box == 11) {
2644  box = NEW_FOLDER;
2645  urgent = 1;
2646  }
2647 
2648  ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
2649  ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
2650  vms->imapversion = vmu->imapversion;
2651  ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
2652 
2653  if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
2654  ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
2655  return -1;
2656  }
2657 
2658  create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2659 
2660  /* Check Quota */
2661  if (box == 0) {
2662  ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
2663  check_quota(vms, (char *) mbox(vmu, box));
2664  }
2665 
2666  ast_mutex_lock(&vms->lock);
2667  pgm = mail_newsearchpgm();
2668 
2669  /* Check IMAP folder for Asterisk messages only... */
2670  hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
2671  hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
2672  pgm->header = hdr;
2673  pgm->deleted = 0;
2674  pgm->undeleted = 1;
2675 
2676  /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
2677  if (box == NEW_FOLDER && urgent == 1) {
2678  pgm->unseen = 1;
2679  pgm->seen = 0;
2680  pgm->flagged = 1;
2681  pgm->unflagged = 0;
2682  } else if (box == NEW_FOLDER && urgent == 0) {
2683  pgm->unseen = 1;
2684  pgm->seen = 0;
2685  pgm->flagged = 0;
2686  pgm->unflagged = 1;
2687  } else if (box == OLD_FOLDER) {
2688  pgm->seen = 1;
2689  pgm->unseen = 0;
2690  }
2691 
2692  ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
2693 
2694  vms->vmArrayIndex = 0;
2695  mail_search_full (vms->mailstream, NULL, pgm, NIL);
2696  vms->lastmsg = vms->vmArrayIndex - 1;
2697  mail_free_searchpgm(&pgm);
2698  /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
2699  * ensure to allocate enough space to account for all of them. Warn if old messages
2700  * have not been checked first as that is required.
2701  */
2702  if (box == 0 && !vms->dh_arraysize) {
2703  ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
2704  }
2705  if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
2706  ast_mutex_unlock(&vms->lock);
2707  return -1;
2708  }
2709 
2710  ast_mutex_unlock(&vms->lock);
2711  return 0;
2712 }
2713 
2714 static void write_file(char *filename, char *buffer, unsigned long len)
2715 {
2716  FILE *output;
2717 
2718  output = fopen (filename, "w");
2719  if (fwrite(buffer, len, 1, output) != 1) {
2720  if (ferror(output)) {
2721  ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
2722  }
2723  }
2724  fclose (output);
2725 }
2726 
2727 static void update_messages_by_imapuser(const char *user, unsigned long number)
2728 {
2729  struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
2730 
2731  if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
2732  return;
2733  }
2734 
2735  ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
2736 
2737  /* Ensure we have room for the next message. */
2738  if (vms->vmArrayIndex >= vms->msg_array_max) {
2739  long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
2740  if (!new_mem) {
2741  return;
2742  }
2743  vms->msgArray = new_mem;
2744  vms->msg_array_max *= 2;
2745  }
2746 
2747  vms->msgArray[vms->vmArrayIndex++] = number;
2748 }
2749 
2750 void mm_searched(MAILSTREAM *stream, unsigned long number)
2751 {
2752  char *mailbox = stream->mailbox, buf[1024] = "", *user;
2753 
2754  if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
2755  return;
2756 
2757  update_messages_by_imapuser(user, number);
2758 }
2759 
2760 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
2761 {
2762  struct ast_variable *var;
2763  struct ast_vm_user *vmu;
2764 
2765  vmu = ast_calloc(1, sizeof *vmu);
2766  if (!vmu)
2767  return NULL;
2768 
2769  populate_defaults(vmu);
2770  ast_set_flag(vmu, VM_ALLOCED);
2771 
2772  var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
2773  if (var) {
2774  apply_options_full(vmu, var);
2775  ast_variables_destroy(var);
2776  return vmu;
2777  } else {
2778  ast_free(vmu);
2779  return NULL;
2780  }
2781 }
2782 
2783 /* Interfaces to C-client */
2784 
2785 void mm_exists(MAILSTREAM * stream, unsigned long number)
2786 {
2787  /* mail_ping will callback here if new mail! */
2788  ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
2789  if (number == 0) return;
2790  set_update(stream);
2791 }
2792 
2793 
2794 void mm_expunged(MAILSTREAM * stream, unsigned long number)
2795 {
2796  /* mail_ping will callback here if expunged mail! */
2797  ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
2798  if (number == 0) return;
2799  set_update(stream);
2800 }
2801 
2802 
2803 void mm_flags(MAILSTREAM * stream, unsigned long number)
2804 {
2805  /* mail_ping will callback here if read mail! */
2806  ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
2807  if (number == 0) return;
2808  set_update(stream);
2809 }
2810 
2811 
2812 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
2813 {
2814  ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
2815  mm_log (string, errflg);
2816 }
2817 
2818 
2819 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2820 {
2821  if (delimiter == '\0') {
2822  delimiter = delim;
2823  }
2824 
2825  ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
2826  if (attributes & LATT_NOINFERIORS)
2827  ast_debug(5, "no inferiors\n");
2828  if (attributes & LATT_NOSELECT)
2829  ast_debug(5, "no select\n");
2830  if (attributes & LATT_MARKED)
2831  ast_debug(5, "marked\n");
2832  if (attributes & LATT_UNMARKED)
2833  ast_debug(5, "unmarked\n");
2834 }
2835 
2836 
2837 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2838 {
2839  ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
2840  if (attributes & LATT_NOINFERIORS)
2841  ast_debug(5, "no inferiors\n");
2842  if (attributes & LATT_NOSELECT)
2843  ast_debug(5, "no select\n");
2844  if (attributes & LATT_MARKED)
2845  ast_debug(5, "marked\n");
2846  if (attributes & LATT_UNMARKED)
2847  ast_debug(5, "unmarked\n");
2848 }
2849 
2850 
2851 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
2852 {
2853  ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
2854  if (status->flags & SA_MESSAGES)
2855  ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
2856  if (status->flags & SA_RECENT)
2857  ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
2858  if (status->flags & SA_UNSEEN)
2859  ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
2860  if (status->flags & SA_UIDVALIDITY)
2861  ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
2862  if (status->flags & SA_UIDNEXT)
2863  ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
2864  ast_log(AST_LOG_NOTICE, "\n");
2865 }
2866 
2867 
2868 void mm_log(char *string, long errflg)
2869 {
2870  switch ((short) errflg) {
2871  case NIL:
2872  ast_debug(1, "IMAP Info: %s\n", string);
2873  break;
2874  case PARSE:
2875  case WARN:
2876  ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
2877  break;
2878  case ERROR:
2879  ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
2880  break;
2881  }
2882 }
2883 
2884 
2885 void mm_dlog(char *string)
2886 {
2887  ast_log(AST_LOG_NOTICE, "%s\n", string);
2888 }
2889 
2890 
2891 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
2892 {
2893  struct ast_vm_user *vmu;
2894 
2895  ast_debug(4, "Entering callback mm_login\n");
2896 
2897  ast_copy_string(user, mb->user, MAILTMPLEN);
2898 
2899  /* We should only do this when necessary */
2900  if (!ast_strlen_zero(authpassword)) {
2901  ast_copy_string(pwd, authpassword, MAILTMPLEN);
2902  } else {
2903  AST_LIST_TRAVERSE(&users, vmu, list) {
2904  if (!strcasecmp(mb->user, vmu->imapuser)) {
2905  ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2906  break;
2907  }
2908  }
2909  if (!vmu) {
2910  if ((vmu = find_user_realtime_imapuser(mb->user))) {
2911  ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2912  free_user(vmu);
2913  }
2914  }
2915  }
2916 }
2917 
2918 
2919 void mm_critical(MAILSTREAM * stream)
2920 {
2921 }
2922 
2923 
2924 void mm_nocritical(MAILSTREAM * stream)
2925 {
2926 }
2927 
2928 
2929 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
2930 {
2931  kill (getpid (), SIGSTOP);
2932  return NIL;
2933 }
2934 
2935 
2936 void mm_fatal(char *string)
2937 {
2938  ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
2939 }
2940 
2941 /* C-client callback to handle quota */
2942 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
2943 {
2944  struct vm_state *vms;
2945  char *mailbox = stream->mailbox, *user;
2946  char buf[1024] = "";
2947  unsigned long usage = 0, limit = 0;
2948 
2949  while (pquota) {
2950  usage = pquota->usage;
2951  limit = pquota->limit;
2952  pquota = pquota->next;
2953  }
2954 
2955  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)))) {
2956  ast_log(AST_LOG_ERROR, "No state found.\n");
2957  return;
2958  }
2959 
2960  ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
2961 
2962  vms->quota_usage = usage;
2963  vms->quota_limit = limit;
2964 }
2965 
2966 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
2967 {
2968  char *start, *eol_pnt;
2969  int taglen;
2970 
2971  if (ast_strlen_zero(header) || ast_strlen_zero(tag))
2972  return NULL;
2973 
2974  taglen = strlen(tag) + 1;
2975  if (taglen < 1)
2976  return NULL;
2977 
2978  if (!(start = strstr(header, tag)))
2979  return NULL;
2980 
2981  /* Since we can be called multiple times we should clear our buffer */
2982  memset(buf, 0, len);
2983 
2984  ast_copy_string(buf, start+taglen, len);
2985  if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
2986  *eol_pnt = '\0';
2987  return buf;
2988 }
2989 
2990 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
2991 {
2992  char *start, *quote, *eol_pnt;
2993 
2994  if (ast_strlen_zero(mailbox))
2995  return NULL;
2996 
2997  if (!(start = strstr(mailbox, "/user=")))
2998  return NULL;
2999 
3000  ast_copy_string(buf, start+6, len);
3001 
3002  if (!(quote = strchr(buf, '\"'))) {
3003  if (!(eol_pnt = strchr(buf, '/')))
3004  eol_pnt = strchr(buf,'}');
3005  *eol_pnt = '\0';
3006  return buf;
3007  } else {
3008  eol_pnt = strchr(buf+1,'\"');
3009  *eol_pnt = '\0';
3010  return buf+1;
3011  }
3012 }
3013 
3014 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
3015 {
3016  struct vm_state *vms_p;
3017 
3018  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3019  if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
3020  return vms_p;
3021  }
3022  if (option_debug > 4)
3023  ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
3024  /* XXX: Is this correctly freed always? */
3025  if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
3026  return NULL;
3027  ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
3028  ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
3029  ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
3030  ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
3031  vms_p->mailstream = NIL; /* save for access from interactive entry point */
3032  vms_p->imapversion = vmu->imapversion;
3033  if (option_debug > 4)
3034  ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
3035  vms_p->updated = 1;
3036  /* set mailbox to INBOX! */
3037  ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
3038  init_vm_state(vms_p);
3039  vmstate_insert(vms_p);
3040  return vms_p;
3041 }
3042 
3043 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
3044 {
3045  struct vmstate *vlist = NULL;
3046 
3047  if (interactive) {
3048  struct vm_state *vms;
3049  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3050  vms = pthread_getspecific(ts_vmstate.key);
3051  return vms;
3052  }
3053 
3054  AST_LIST_LOCK(&vmstates);
3055  AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3056  if (!vlist->vms) {
3057  ast_debug(3, "error: vms is NULL for %s\n", user);
3058  continue;
3059  }
3060  if (vlist->vms->imapversion != imapversion) {
3061  continue;
3062  }
3063  if (!vlist->vms->imapuser) {
3064  ast_debug(3, "error: imapuser is NULL for %s\n", user);
3065  continue;
3066  }
3067 
3068  if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
3069  AST_LIST_UNLOCK(&vmstates);
3070  return vlist->vms;
3071  }
3072  }
3073  AST_LIST_UNLOCK(&vmstates);
3074 
3075  ast_debug(3, "%s not found in vmstates\n", user);
3076 
3077  return NULL;
3078 }
3079 
3080 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
3081 {
3082 
3083  struct vmstate *vlist = NULL;
3084  const char *local_context = S_OR(context, "default");
3085 
3086  if (interactive) {
3087  struct vm_state *vms;
3088  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3089  vms = pthread_getspecific(ts_vmstate.key);
3090  return vms;
3091  }
3092 
3093  AST_LIST_LOCK(&vmstates);
3094  AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3095  if (!vlist->vms) {
3096  ast_debug(3, "error: vms is NULL for %s\n", mailbox);
3097  continue;
3098  }
3099  if (vlist->vms->imapversion != imapversion) {
3100  continue;
3101  }
3102  if (!vlist->vms->username || !vlist->vms->context) {
3103  ast_debug(3, "error: username is NULL for %s\n", mailbox);
3104  continue;
3105  }
3106 
3107  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);
3108 
3109  if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
3110  ast_debug(3, "Found it!\n");
3111  AST_LIST_UNLOCK(&vmstates);
3112  return vlist->vms;
3113  }
3114  }
3115  AST_LIST_UNLOCK(&vmstates);
3116 
3117  ast_debug(3, "%s not found in vmstates\n", mailbox);
3118 
3119  return NULL;
3120 }
3121 
3122 static void vmstate_insert(struct vm_state *vms)
3123 {
3124  struct vmstate *v;
3125  struct vm_state *altvms;
3126 
3127  /* If interactive, it probably already exists, and we should
3128  use the one we already have since it is more up to date.
3129  We can compare the username to find the duplicate */
3130  if (vms->interactive == 1) {
3131  altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
3132  if (altvms) {
3133  ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3134  vms->newmessages = altvms->newmessages;
3135  vms->oldmessages = altvms->oldmessages;
3136  vms->vmArrayIndex = altvms->vmArrayIndex;
3137  /* XXX: no msgArray copying? */
3138  vms->lastmsg = altvms->lastmsg;
3139  vms->curmsg = altvms->curmsg;
3140  /* get a pointer to the persistent store */
3141  vms->persist_vms = altvms;
3142  /* Reuse the mailstream? */
3143 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
3144  vms->mailstream = altvms->mailstream;
3145 #else
3146  vms->mailstream = NIL;
3147 #endif
3148  }
3149  return;
3150  }
3151 
3152  if (!(v = ast_calloc(1, sizeof(*v))))
3153  return;
3154 
3155  v->vms = vms;
3156 
3157  ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3158 
3159  AST_LIST_LOCK(&vmstates);
3160  AST_LIST_INSERT_TAIL(&vmstates, v, list);
3161  AST_LIST_UNLOCK(&vmstates);
3162 }
3163 
3164 static void vmstate_delete(struct vm_state *vms)
3165 {
3166  struct vmstate *vc = NULL;
3167  struct vm_state *altvms = NULL;
3168 
3169  /* If interactive, we should copy pertinent info
3170  back to the persistent state (to make update immediate) */
3171  if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
3172  ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3173  altvms->newmessages = vms->newmessages;
3174  altvms->oldmessages = vms->oldmessages;
3175  altvms->updated = 1;
3176  vms->mailstream = mail_close(vms->mailstream);
3177 
3178  /* Interactive states are not stored within the persistent list */
3179  return;
3180  }
3181 
3182  ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3183 
3184  AST_LIST_LOCK(&vmstates);
3185  AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
3186  if (vc->vms == vms) {
3188  break;
3189  }
3190  }
3192  AST_LIST_UNLOCK(&vmstates);
3193 
3194  if (vc) {
3195  ast_mutex_destroy(&vc->vms->lock);
3196  ast_free(vc->vms->msgArray);
3197  vc->vms->msgArray = NULL;
3198  vc->vms->msg_array_max = 0;
3199  /* XXX: is no one supposed to free vms itself? */
3200  ast_free(vc);
3201  } else {
3202  ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3203  }
3204 }
3205 
3206 static void set_update(MAILSTREAM * stream)
3207 {
3208  struct vm_state *vms;
3209  char *mailbox = stream->mailbox, *user;
3210  char buf[1024] = "";
3211 
3212  if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
3213  if (user && option_debug > 2)
3214  ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
3215  return;
3216  }
3217 
3218  ast_debug(3, "User %s mailbox set for update.\n", user);
3219 
3220  vms->updated = 1; /* Set updated flag since mailbox changed */
3221 }
3222 
3223 static void init_vm_state(struct vm_state *vms)
3224 {
3225  vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
3226  vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
3227  if (!vms->msgArray) {
3228  /* Out of mem? This can't be good. */
3229  vms->msg_array_max = 0;
3230  }
3231  vms->vmArrayIndex = 0;
3232  ast_mutex_init(&vms->lock);
3233 }
3234 
3235 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro)
3236 {
3237  char *body_content;
3238  char *body_decoded;
3239  char *fn = is_intro ? vms->introfn : vms->fn;
3240  unsigned long len;
3241  unsigned long newlen;
3242  char filename[256];
3243 
3244  if (!body || body == NIL)
3245  return -1;
3246 
3247  ast_mutex_lock(&vms->lock);
3248  body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
3249  ast_mutex_unlock(&vms->lock);
3250  if (body_content != NIL) {
3251  snprintf(filename, sizeof(filename), "%s.%s", fn, format);
3252  /* ast_debug(1,body_content); */
3253  body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
3254  /* If the body of the file is empty, return an error */
3255  if (!newlen) {
3256  return -1;
3257  }
3258  write_file(filename, (char *) body_decoded, newlen);
3259  } else {
3260  ast_debug(5, "Body of message is NULL.\n");
3261  return -1;
3262  }
3263  return 0;
3264 }
3265 
3266 /*!
3267  * \brief Get delimiter via mm_list callback
3268  * \param stream
3269  *
3270  * Determines the delimiter character that is used by the underlying IMAP based mail store.
3271  */
3272 /* MUTEX should already be held */
3273 static void get_mailbox_delimiter(MAILSTREAM *stream) {
3274  char tmp[50];
3275  snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
3276  mail_list(stream, tmp, "*");
3277 }
3278 
3279 /*!
3280  * \brief Check Quota for user
3281  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
3282  * \param mailbox the mailbox to check the quota for.
3283  *
3284  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
3285  */
3286 static void check_quota(struct vm_state *vms, char *mailbox) {
3287  ast_mutex_lock(&vms->lock);
3288  mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
3289  ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
3290  if (vms && vms->mailstream != NULL) {
3291  imap_getquotaroot(vms->mailstream, mailbox);
3292  } else {
3293  ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
3294  }
3295  ast_mutex_unlock(&vms->lock);
3296 }
3297 
3298 #endif /* IMAP_STORAGE */
3299 
3300 /*! \brief Lock file path
3301  * only return failure if ast_lock_path returns 'timeout',
3302  * not if the path does not exist or any other reason
3303  */
3304 static int vm_lock_path(const char *path)
3305 {
3306  switch (ast_lock_path(path)) {
3307  case AST_LOCK_TIMEOUT:
3308  return -1;
3309  default:
3310  return 0;
3311  }
3312 }
3313 
3314 
3315 #ifdef ODBC_STORAGE
3316 struct generic_prepare_struct {
3317  char *sql;
3318  int argc;
3319  char **argv;
3320 };
3321 
3322 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
3323 {
3324  struct generic_prepare_struct *gps = data;
3325  int res, i;
3326  SQLHSTMT stmt;
3327 
3328  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
3329  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3330  ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
3331  return NULL;
3332  }
3333  res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
3334  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3335  ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
3336  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3337  return NULL;
3338  }
3339  for (i = 0; i < gps->argc; i++)
3340  SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
3341 
3342  return stmt;
3343 }
3344 
3345 /*!
3346  * \brief Retrieves a file from an ODBC data store.
3347  * \param dir the path to the file to be retreived.
3348  * \param msgnum the message number, such as within a mailbox folder.
3349  *
3350  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
3351  * 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.
3352  *
3353  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
3354  * The output is the message information file with the name msgnum and the extension .txt
3355  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
3356  *
3357  * \return 0 on success, -1 on error.
3358  */
3359 static int retrieve_file(char *dir, int msgnum)
3360 {
3361  int x = 0;
3362  int res;
3363  int fd = -1;
3364  size_t fdlen = 0;
3365  void *fdm = MAP_FAILED;
3366  SQLSMALLINT colcount = 0;
3367  SQLHSTMT stmt;
3368  char sql[PATH_MAX];
3369  char fmt[80]="";
3370  char *c;
3371  char coltitle[256];
3372  SQLSMALLINT collen;
3373  SQLSMALLINT datatype;
3374  SQLSMALLINT decimaldigits;
3375  SQLSMALLINT nullable;
3376  SQLULEN colsize;
3377  SQLLEN colsize2;
3378  FILE *f = NULL;
3379  char rowdata[80];
3380  char fn[PATH_MAX];
3381  char full_fn[PATH_MAX];
3382  char msgnums[80];
3383  char *argv[] = { dir, msgnums };
3384  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3385 
3386  struct odbc_obj *obj;
3387  obj = ast_odbc_request_obj(odbc_database, 0);
3388  if (obj) {
3389  ast_copy_string(fmt, vmfmts, sizeof(fmt));
3390  c = strchr(fmt, '|');
3391  if (c)
3392  *c = '\0';
3393  if (!strcasecmp(fmt, "wav49"))
3394  strcpy(fmt, "WAV");
3395  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3396  if (msgnum > -1)
3397  make_file(fn, sizeof(fn), dir, msgnum);
3398  else
3399  ast_copy_string(fn, dir, sizeof(fn));
3400 
3401  /* Create the information file */
3402  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3403 
3404  if (!(f = fopen(full_fn, "w+"))) {
3405  ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
3406  goto yuck;
3407  }
3408 
3409  snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
3410  snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
3411  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3412  if (!stmt) {
3413  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3414  ast_odbc_release_obj(obj);
3415  goto yuck;
3416  }
3417  res = SQLFetch(stmt);
3418  if (res == SQL_NO_DATA) {
3419  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3420  ast_odbc_release_obj(obj);
3421  goto yuck;
3422  } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3423  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3424  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3425  ast_odbc_release_obj(obj);
3426  goto yuck;
3427  }
3428  fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
3429  if (fd < 0) {
3430  ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
3431  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3432  ast_odbc_release_obj(obj);
3433  goto yuck;
3434  }
3435  res = SQLNumResultCols(stmt, &colcount);
3436  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3437  ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
3438  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3439  ast_odbc_release_obj(obj);
3440  goto yuck;
3441  }
3442  if (f)
3443  fprintf(f, "[message]\n");
3444  for (x = 0; x < colcount; x++) {
3445  rowdata[0] = '\0';
3446  colsize = 0;
3447  collen = sizeof(coltitle);
3448  res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen,
3449  &datatype, &colsize, &decimaldigits, &nullable);
3450  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3451  ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
3452  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3453  ast_odbc_release_obj(obj);
3454  goto yuck;
3455  }
3456  if (!strcasecmp(coltitle, "recording")) {
3457  off_t offset;
3458  res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
3459  fdlen = colsize2;
3460  if (fd > -1) {
3461  char tmp[1]="";
3462  lseek(fd, fdlen - 1, SEEK_SET);
3463  if (write(fd, tmp, 1) != 1) {
3464  close(fd);
3465  fd = -1;
3466  continue;
3467  }
3468  /* Read out in small chunks */
3469  for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
3470  if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
3471  ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
3472  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3473  ast_odbc_release_obj(obj);
3474  goto yuck;
3475  } else {
3476  res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
3477  munmap(fdm, CHUNKSIZE);
3478  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3479  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3480  unlink(full_fn);
3481  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3482  ast_odbc_release_obj(obj);
3483  goto yuck;
3484  }
3485  }
3486  }
3487  if (truncate(full_fn, fdlen) < 0) {
3488  ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
3489  }
3490  }
3491  } else {
3492  res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3493  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3494  ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
3495  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3496  ast_odbc_release_obj(obj);
3497  goto yuck;
3498  }
3499  if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
3500  fprintf(f, "%s=%s\n", coltitle, rowdata);
3501  }
3502  }
3503  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3504  ast_odbc_release_obj(obj);
3505  } else
3506  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3507 yuck:
3508  if (f)
3509  fclose(f);
3510  if (fd > -1)
3511  close(fd);
3512  return x - 1;
3513 }
3514 
3515 /*!
3516  * \brief Determines the highest message number in use for a given user and mailbox folder.
3517  * \param vmu
3518  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
3519  *
3520  * This method is used when mailboxes are stored in an ODBC back end.
3521  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
3522  *
3523  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
3524 
3525  */
3526 static int last_message_index(struct ast_vm_user *vmu, char *dir)
3527 {
3528  int x = 0;
3529  int res;
3530  SQLHSTMT stmt;
3531  char sql[PATH_MAX];
3532  char rowdata[20];
3533  char *argv[] = { dir };
3534  struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
3535 
3536  struct odbc_obj *obj;
3537  obj = ast_odbc_request_obj(odbc_database, 0);
3538  if (obj) {
3539  snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
3540 
3541  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3542  if (!stmt) {
3543  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3544  ast_odbc_release_obj(obj);
3545  goto yuck;
3546  }
3547  res = SQLFetch(stmt);
3548  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3549  if (res == SQL_NO_DATA) {
3550  ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
3551  } else {
3552  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3553  }
3554 
3555  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3556  ast_odbc_release_obj(obj);
3557  goto yuck;
3558  }
3559  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3560  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3561  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3562  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3563  ast_odbc_release_obj(obj);
3564  goto yuck;
3565  }
3566  if (sscanf(rowdata, "%30d", &x) != 1)
3567  ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
3568  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3569  ast_odbc_release_obj(obj);
3570  return x;
3571  } else
3572  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3573 yuck:
3574  return x - 1;
3575 }
3576 
3577 /*!
3578  * \brief Determines if the specified message exists.
3579  * \param dir the folder the mailbox folder to look for messages.
3580  * \param msgnum the message index to query for.
3581  *
3582  * This method is used when mailboxes are stored in an ODBC back end.
3583  *
3584  * \return greater than zero if the message exists, zero when the message does not exist or on error.
3585  */
3586 static int message_exists(char *dir, int msgnum)
3587 {
3588  int x = 0;
3589  int res;
3590  SQLHSTMT stmt;
3591  char sql[PATH_MAX];
3592  char rowdata[20];
3593  char msgnums[20];
3594  char *argv[] = { dir, msgnums };
3595  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3596 
3597  struct odbc_obj *obj;
3598  obj = ast_odbc_request_obj(odbc_database, 0);
3599  if (obj) {
3600  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3601  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
3602  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3603  if (!stmt) {
3604  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3605  ast_odbc_release_obj(obj);
3606  goto yuck;
3607  }
3608  res = SQLFetch(stmt);
3609  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3610  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3611  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3612  ast_odbc_release_obj(obj);
3613  goto yuck;
3614  }
3615  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3616  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3617  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3618  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3619  ast_odbc_release_obj(obj);
3620  goto yuck;
3621  }
3622  if (sscanf(rowdata, "%30d", &x) != 1)
3623  ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
3624  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3625  ast_odbc_release_obj(obj);
3626  } else
3627  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3628 yuck:
3629  return x;
3630 }
3631 
3632 /*!
3633  * \brief returns the number of messages found.
3634  * \param vmu
3635  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
3636  *
3637  * This method is used when mailboxes are stored in an ODBC back end.
3638  *
3639  * \return The count of messages being zero or more, less than zero on error.
3640  */
3641 static int count_messages(struct ast_vm_user *vmu, char *dir)
3642 {
3643  int x = 0;
3644  int res;
3645  SQLHSTMT stmt;
3646  char sql[PATH_MAX];
3647  char rowdata[20];
3648  char *argv[] = { dir };
3649  struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
3650 
3651  struct odbc_obj *obj;
3652  obj = ast_odbc_request_obj(odbc_database, 0);
3653  if (obj) {
3654  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
3655  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3656  if (!stmt) {
3657  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3658  ast_odbc_release_obj(obj);
3659  goto yuck;
3660  }
3661  res = SQLFetch(stmt);
3662  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3663  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3664  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3665  ast_odbc_release_obj(obj);
3666  goto yuck;
3667  }
3668  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3669  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3670  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3671  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3672  ast_odbc_release_obj(obj);
3673  goto yuck;
3674  }
3675  if (sscanf(rowdata, "%30d", &x) != 1)
3676  ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
3677  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3678  ast_odbc_release_obj(obj);
3679  return x;
3680  } else
3681  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3682 yuck:
3683  return x - 1;
3684 
3685 }
3686 
3687 /*!
3688  * \brief Deletes a message from the mailbox folder.
3689  * \param sdir The mailbox folder to work in.
3690  * \param smsg The message index to be deleted.
3691  *
3692  * This method is used when mailboxes are stored in an ODBC back end.
3693  * The specified message is directly deleted from the database 'voicemessages' table.
3694  *
3695  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
3696  */
3697 static void delete_file(const char *sdir, int smsg)
3698 {
3699  SQLHSTMT stmt;
3700  char sql[PATH_MAX];
3701  char msgnums[20];
3702  char *argv[] = { NULL, msgnums };
3703  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3704  struct odbc_obj *obj;
3705 
3706  argv[0] = ast_strdupa(sdir);
3707 
3708  obj = ast_odbc_request_obj(odbc_database, 0);
3709  if (obj) {
3710  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3711  snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
3712  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3713  if (!stmt)
3714  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3715  else
3716  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3717  ast_odbc_release_obj(obj);
3718  } else
3719  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3720  return;
3721 }
3722 
3723 /*!
3724  * \brief Copies a voicemail from one mailbox to another.
3725  * \param sdir the folder for which to look for the message to be copied.
3726  * \param smsg the index of the message to be copied.
3727  * \param ddir the destination folder to copy the message into.
3728  * \param dmsg the index to be used for the copied message.
3729  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
3730  * \param dmailboxcontext The context for the destination user.
3731  *
3732  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
3733  */
3734 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
3735 {
3736  SQLHSTMT stmt;
3737  char sql[512];
3738  char msgnums[20];
3739  char msgnumd[20];
3740  struct odbc_obj *obj;
3741  char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
3742  struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
3743 
3744  delete_file(ddir, dmsg);
3745  obj = ast_odbc_request_obj(odbc_database, 0);
3746  if (obj) {
3747  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3748  snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
3749  snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, flag, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,flag,?,? FROM %s WHERE dir=? AND msgnum=?", odbc_table, odbc_table);
3750  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3751  if (!stmt)
3752  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
3753  else
3754  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3755  ast_odbc_release_obj(obj);
3756  } else
3757  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3758  return;
3759 }
3760 
3761 struct insert_data {
3762  char *sql;
3763  const char *dir;
3764  const char *msgnums;
3765  void *data;
3766  SQLLEN datalen;
3767  SQLLEN indlen;
3768  const char *context;
3769  const char *macrocontext;
3770  const char *callerid;
3771  const char *origtime;
3772  const char *duration;
3773  const char *mailboxuser;
3774  const char *mailboxcontext;
3775  const char *category;
3776  const char *flag;
3777 };
3778 
3779 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
3780 {
3781  struct insert_data *data = vdata;
3782  int res;
3783  SQLHSTMT stmt;
3784 
3785  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
3786  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3787  ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
3788  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3789  return NULL;
3790  }
3791 
3792  SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
3793  SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
3794  SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
3795  SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
3796  SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
3797  SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
3798  SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
3799  SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
3800  SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
3801  SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
3802  SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
3803  if (!ast_strlen_zero(data->category)) {
3804  SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
3805  }
3806  res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
3807  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3808  ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
3809  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3810  return NULL;
3811  }
3812 
3813  return stmt;
3814 }
3815 
3816 /*!
3817  * \brief Stores a voicemail into the database.
3818  * \param dir the folder the mailbox folder to store the message.
3819  * \param mailboxuser the user owning the mailbox folder.
3820  * \param mailboxcontext
3821  * \param msgnum the message index for the message to be stored.
3822  *
3823  * This method is used when mailboxes are stored in an ODBC back end.
3824  * The message sound file and information file is looked up on the file system.
3825  * A SQL query is invoked to store the message into the (MySQL) database.
3826  *
3827  * \return the zero on success -1 on error.
3828  */
3829 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
3830 {
3831  int res = 0;
3832  int fd = -1;
3833  void *fdm = MAP_FAILED;
3834  off_t fdlen = -1;
3835  SQLHSTMT stmt;
3836  char sql[PATH_MAX];
3837  char msgnums[20];
3838  char fn[PATH_MAX];
3839  char full_fn[PATH_MAX];
3840  char fmt[80]="";
3841  char *c;
3842  struct ast_config *cfg = NULL;
3843  struct odbc_obj *obj;
3844  struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
3845  .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
3846  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
3847 
3848  delete_file(dir, msgnum);
3849  if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
3850  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3851  return -1;
3852  }
3853 
3854  do {
3855  ast_copy_string(fmt, vmfmts, sizeof(fmt));
3856  c = strchr(fmt, '|');
3857  if (c)
3858  *c = '\0';
3859  if (!strcasecmp(fmt, "wav49"))
3860  strcpy(fmt, "WAV");
3861  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3862  if (msgnum > -1)
3863  make_file(fn, sizeof(fn), dir, msgnum);
3864  else
3865  ast_copy_string(fn, dir, sizeof(fn));
3866  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3867  cfg = ast_config_load(full_fn, config_flags);
3868  snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
3869  fd = open(full_fn, O_RDWR);
3870  if (fd < 0) {
3871  ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
3872  res = -1;
3873  break;
3874  }
3875  if (valid_config(cfg)) {
3876  if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
3877  idata.context = "";
3878  }
3879  if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
3880  idata.macrocontext = "";
3881  }
3882  if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
3883  idata.callerid = "";
3884  }
3885  if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
3886  idata.origtime = "";
3887  }
3888  if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
3889  idata.duration = "";
3890  }
3891  if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
3892  idata.category = "";
3893  }
3894  if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
3895  idata.flag = "";
3896  }
3897  }
3898  fdlen = lseek(fd, 0, SEEK_END);
3899  if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
3900  ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
3901  res = -1;
3902  break;
3903  }
3904  fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
3905  if (fdm == MAP_FAILED) {
3906  ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
3907  res = -1;
3908  break;
3909  }
3910  idata.data = fdm;
3911  idata.datalen = idata.indlen = fdlen;
3912 
3913  if (!ast_strlen_zero(idata.category))
3914  snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
3915  else
3916  snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
3917 
3918  if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
3919  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3920  } else {
3921  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3922  res = -1;
3923  }
3924  } while (0);
3925  if (obj) {
3926  ast_odbc_release_obj(obj);
3927  }
3928  if (valid_config(cfg))
3929  ast_config_destroy(cfg);
3930  if (fdm != MAP_FAILED)
3931  munmap(fdm, fdlen);
3932  if (fd > -1)
3933  close(fd);
3934  return res;
3935 }
3936 
3937 /*!
3938  * \brief Renames a message in a mailbox folder.
3939  * \param sdir The folder of the message to be renamed.
3940  * \param smsg The index of the message to be renamed.
3941  * \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.
3942  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
3943  * \param ddir The destination folder for the message to be renamed into
3944  * \param dmsg The destination message for the message to be renamed.
3945  *
3946  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
3947  * 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.
3948  * 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.
3949  */
3950 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
3951 {
3952  SQLHSTMT stmt;
3953  char sql[PATH_MAX];
3954  char msgnums[20];
3955  char msgnumd[20];
3956  struct odbc_obj *obj;
3957  char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
3958  struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
3959 
3960  delete_file(ddir, dmsg);
3961  obj = ast_odbc_request_obj(odbc_database, 0);
3962  if (obj) {
3963  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3964  snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
3965  snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
3966  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3967  if (!stmt)
3968  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3969  else
3970  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3971  ast_odbc_release_obj(obj);
3972  } else
3973  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3974  return;
3975 }
3976 
3977 /*!
3978  * \brief Removes a voicemail message file.
3979  * \param dir the path to the message file.
3980  * \param msgnum the unique number for the message within the mailbox.
3981  *
3982  * Removes the message content file and the information file.
3983  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
3984  * Typical use is to clean up after a RETRIEVE operation.
3985  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
3986  * \return zero on success, -1 on error.
3987  */
3988 static int remove_file(char *dir, int msgnum)
3989 {
3990  char fn[PATH_MAX];
3991  char full_fn[PATH_MAX];
3992  char msgnums[80];
3993 
3994  if (msgnum > -1) {
3995  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3996  make_file(fn, sizeof(fn), dir, msgnum);
3997  } else
3998  ast_copy_string(fn, dir, sizeof(fn));
3999  ast_filedelete(fn, NULL);
4000  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
4001  unlink(full_fn);
4002  return 0;
4003 }
4004 #else
4005 #ifndef IMAP_STORAGE
4006 /*!
4007  * \brief Find all .txt files - even if they are not in sequence from 0000.
4008  * \param vmu
4009  * \param dir
4010  *
4011  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
4012  *
4013  * \return the count of messages, zero or more.
4014  */
4015 static int count_messages(struct ast_vm_user *vmu, char *dir)
4016 {
4017 
4018  int vmcount = 0;
4019  DIR *vmdir = NULL;
4020  struct dirent *vment = NULL;
4021 
4022  if (vm_lock_path(dir))
4023  return ERROR_LOCK_PATH;
4024 
4025  if ((vmdir = opendir(dir))) {
4026  while ((vment = readdir(vmdir))) {
4027  if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
4028  vmcount++;
4029  }
4030  }
4031  closedir(vmdir);
4032  }
4033  ast_unlock_path(dir);
4034 
4035  return vmcount;
4036 }
4037 
4038 /*!
4039  * \brief Renames a message in a mailbox folder.
4040  * \param sfn The path to the mailbox information and data file to be renamed.
4041  * \param dfn The path for where the message data and information files will be renamed to.
4042  *
4043  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
4044  */
4045 static void rename_file(char *sfn, char *dfn)
4046 {
4047  char stxt[PATH_MAX];
4048  char dtxt[PATH_MAX];
4049  ast_filerename(sfn, dfn, NULL);
4050  snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
4051  snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
4052  if (ast_check_realtime("voicemail_data")) {
4053  ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
4054  }
4055  rename(stxt, dtxt);
4056 }
4057 
4058 /*!
4059  * \brief Determines the highest message number in use for a given user and mailbox folder.
4060  * \param vmu
4061  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
4062  *
4063  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
4064  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
4065  *
4066  * \note Should always be called with a lock already set on dir.
4067  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
4068  */
4069 static int last_message_index(struct ast_vm_user *vmu, char *dir)
4070 {
4071  int x;
4072  unsigned char map[MAXMSGLIMIT] = "";
4073  DIR *msgdir;
4074  struct dirent *msgdirent;
4075  int msgdirint;
4076  char extension[4];
4077  int stopcount = 0;
4078 
4079  /* Reading the entire directory into a file map scales better than
4080  * doing a stat repeatedly on a predicted sequence. I suspect this
4081  * is partially due to stat(2) internally doing a readdir(2) itself to
4082  * find each file. */
4083  if (!(msgdir = opendir(dir))) {
4084  return -1;
4085  }
4086 
4087  while ((msgdirent = readdir(msgdir))) {
4088  if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
4089  map[msgdirint] = 1;
4090  stopcount++;
4091  ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
4092  }
4093  }
4094  closedir(msgdir);
4095 
4096  for (x = 0; x < vmu->maxmsg; x++) {
4097  if (map[x] == 1) {
4098  stopcount--;
4099  } else if (map[x] == 0 && !stopcount) {
4100  break;
4101  }
4102  }
4103 
4104  return x - 1;
4105 }
4106 
4107 #endif /* #ifndef IMAP_STORAGE */
4108 #endif /* #else of #ifdef ODBC_STORAGE */
4109 #ifndef IMAP_STORAGE
4110 /*!
4111  * \brief Utility function to copy a file.
4112  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
4113  * \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.
4114  *
4115  * 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.
4116  * The copy operation copies up to 4096 bytes at once.
4117  *
4118  * \return zero on success, -1 on error.
4119  */
4120 static int copy(char *infile, char *outfile)
4121 {
4122  int ifd;
4123  int ofd;
4124  int res;
4125  int len;
4126  char buf[4096];
4127 
4128 #ifdef HARDLINK_WHEN_POSSIBLE
4129  /* Hard link if possible; saves disk space & is faster */
4130  if (link(infile, outfile)) {
4131 #endif
4132  if ((ifd = open(infile, O_RDONLY)) < 0) {
4133  ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
4134  return -1;
4135  }
4136  if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
4137  ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
4138  close(ifd);
4139  return -1;
4140  }
4141  do {
4142  len = read(ifd, buf, sizeof(buf));
4143  if (len < 0) {
4144  ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
4145  close(ifd);
4146  close(ofd);
4147  unlink(outfile);
4148  } else if (len) {
4149  res = write(ofd, buf, len);
4150  if (errno == ENOMEM || errno == ENOSPC || res != len) {
4151  ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
4152  close(ifd);
4153  close(ofd);
4154  unlink(outfile);
4155  }
4156  }
4157  } while (len);
4158  close(ifd);
4159  close(ofd);
4160  return 0;
4161 #ifdef HARDLINK_WHEN_POSSIBLE
4162  } else {
4163  /* Hard link succeeded */
4164  return 0;
4165  }
4166 #endif
4167 }
4168 
4169 /*!
4170  * \brief Copies a voicemail information (envelope) file.
4171  * \param frompath
4172  * \param topath
4173  *
4174  * Every voicemail has the data (.wav) file, and the information file.
4175  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
4176  * This is used by the COPY macro when not using IMAP storage.
4177  */
4178 static void copy_plain_file(char *frompath, char *topath)
4179 {
4180  char frompath2[PATH_MAX], topath2[PATH_MAX];
4181  struct ast_variable *tmp,*var = NULL;
4182  const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
4183  ast_filecopy(frompath, topath, NULL);
4184  snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
4185  snprintf(topath2, sizeof(topath2), "%s.txt", topath);
4186  if (ast_check_realtime("voicemail_data")) {
4187  var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
4188  /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
4189  for (tmp = var; tmp; tmp = tmp->next) {
4190  if (!strcasecmp(tmp->name, "origmailbox")) {
4191  origmailbox = tmp->value;
4192  } else if (!strcasecmp(tmp->name, "context")) {
4193  context = tmp->value;
4194  } else if (!strcasecmp(tmp->name, "macrocontext")) {
4195  macrocontext = tmp->value;
4196  } else if (!strcasecmp(tmp->name, "exten")) {
4197  exten = tmp->value;
4198  } else if (!strcasecmp(tmp->name, "priority")) {
4199  priority = tmp->value;
4200  } else if (!strcasecmp(tmp->name, "callerchan")) {
4201  callerchan = tmp->value;
4202  } else if (!strcasecmp(tmp->name, "callerid")) {
4203  callerid = tmp->value;
4204  } else if (!strcasecmp(tmp->name, "origdate")) {
4205  origdate = tmp->value;
4206  } else if (!strcasecmp(tmp->name, "origtime")) {
4207  origtime = tmp->value;
4208  } else if (!strcasecmp(tmp->name, "category")) {
4209  category = tmp->value;
4210  } else if (!strcasecmp(tmp->name, "duration")) {
4211  duration = tmp->value;
4212  }
4213  }
4214  ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "macrocontext", macrocontext, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, SENTINEL);
4215  }
4216  copy(frompath2, topath2);
4217  ast_variables_destroy(var);
4218 }
4219 #endif
4220 
4221 /*!
4222  * \brief Removes the voicemail sound and information file.
4223  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
4224  *
4225  * This is used by the DELETE macro when voicemails are stored on the file system.
4226  *
4227  * \return zero on success, -1 on error.
4228  */
4229 static int vm_delete(char *file)
4230 {
4231  char *txt;
4232  int txtsize = 0;
4233 
4234  txtsize = (strlen(file) + 5)*sizeof(char);
4235  txt = ast_alloca(txtsize);
4236  /* Sprintf here would safe because we alloca'd exactly the right length,
4237  * but trying to eliminate all sprintf's anyhow
4238  */
4239  if (ast_check_realtime("voicemail_data")) {
4240  ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
4241  }
4242  snprintf(txt, txtsize, "%s.txt", file);
4243  unlink(txt);
4244  return ast_filedelete(file, NULL);
4245 }
4246 
4247 /*!
4248  * \brief utility used by inchar(), for base_encode()
4249  */
4250 static int inbuf(struct baseio *bio, FILE *fi)
4251 {
4252  int l;
4253 
4254  if (bio->ateof)
4255  return 0;
4256 
4257  if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
4258  if (ferror(fi))
4259  return -1;
4260 
4261  bio->ateof = 1;
4262  return 0;
4263  }
4264 
4265  bio->iolen = l;
4266  bio->iocp = 0;
4267 
4268  return 1;
4269 }
4270 
4271 /*!
4272  * \brief utility used by base_encode()
4273  */
4274 static int inchar(struct baseio *bio, FILE *fi)
4275 {
4276  if (bio->iocp>=bio->iolen) {
4277  if (!inbuf(bio, fi))
4278  return EOF;
4279  }
4280 
4281  return bio->iobuf[bio->iocp++];
4282 }
4283 
4284 /*!
4285  * \brief utility used by base_encode()
4286  */
4287 static int ochar(struct baseio *bio, int c, FILE *so)
4288 {
4289  if (bio->linelength >= BASELINELEN) {
4290  if (fputs(ENDL, so) == EOF) {
4291  return -1;
4292  }
4293 
4294  bio->linelength = 0;
4295  }
4296 
4297  if (putc(((unsigned char) c), so) == EOF) {
4298  return -1;
4299  }
4300 
4301  bio->linelength++;
4302 
4303  return 1;
4304 }
4305 
4306 /*!
4307  * \brief Performs a base 64 encode algorithm on the contents of a File
4308  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
4309  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
4310  *
4311  * TODO: shouldn't this (and the above 3 support functions) be put into some kind of external utility location, such as funcs/func_base64.c ?
4312  *
4313  * \return zero on success, -1 on error.
4314  */
4315 static int base_encode(char *filename, FILE *so)
4316 {
4317  static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
4318  'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
4319  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
4320  '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
4321  int i, hiteof = 0;
4322  FILE *fi;
4323  struct baseio bio;
4324 
4325  memset(&bio, 0, sizeof(bio));
4326  bio.iocp = BASEMAXINLINE;
4327 
4328  if (!(fi = fopen(filename, "rb"))) {
4329  ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
4330  return -1;
4331  }
4332 
4333  while (!hiteof){
4334  unsigned char igroup[3], ogroup[4];
4335  int c, n;
4336 
4337  memset(igroup, 0, sizeof(igroup));
4338 
4339  for (n = 0; n < 3; n++) {
4340  if ((c = inchar(&bio, fi)) == EOF) {
4341  hiteof = 1;
4342  break;
4343  }
4344 
4345  igroup[n] = (unsigned char) c;
4346  }
4347 
4348  if (n > 0) {
4349  ogroup[0]= dtable[igroup[0] >> 2];
4350  ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
4351  ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
4352  ogroup[3]= dtable[igroup[2] & 0x3F];
4353 
4354  if (n < 3) {
4355  ogroup[3] = '=';
4356 
4357  if (n < 2)
4358  ogroup[2] = '=';
4359  }
4360 
4361  for (i = 0; i < 4; i++)
4362  ochar(&bio, ogroup[i], so);
4363  }
4364  }
4365 
4366  fclose(fi);
4367 
4368  if (fputs(ENDL, so) == EOF) {
4369  return 0;
4370  }
4371 
4372  return 1;
4373 }
4374 
4375 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)
4376 {
4377  char callerid[256];
4378  char num[12];
4379  char fromdir[256], fromfile[256];
4380  struct ast_config *msg_cfg;
4381  const char *origcallerid, *origtime;
4382  char origcidname[80], origcidnum[80], origdate[80];
4383  int inttime;
4384  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
4385 
4386  /* Prepare variables for substitution in email body and subject */
4387  pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
4388  pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
4389  snprintf(num, sizeof(num), "%d", msgnum);
4390  pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
4391  pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
4392  pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
4393  pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
4394  ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
4395  pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
4396  pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
4397  pbx_builtin_setvar_helper(ast, "VM_DATE", date);
4398  pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
4399  pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
4400 
4401  /* Retrieve info from VM attribute file */
4402  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
4403  make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
4404  if (strlen(fromfile) < sizeof(fromfile) - 5) {
4405  strcat(fromfile, ".txt");
4406  }
4407  if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
4408  if (option_debug > 0) {
4409  ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
4410  }
4411  return;
4412  }
4413 
4414  if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
4415  pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
4416  ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
4417  pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
4418  pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
4419  }
4420 
4421  if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
4422  struct timeval tv = { inttime, };
4423  struct ast_tm tm;
4424  ast_localtime(&tv, &tm, NULL);
4425  ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
4426  pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
4427  }
4428  ast_config_destroy(msg_cfg);
4429 }
4430 
4431 /*!
4432  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
4433  * \param from The string to work with.
4434  * \param buf The buffer into which to write the modified quoted string.
4435  * \param maxlen Always zero, but see \see ast_str
4436  *
4437  * \return The destination string with quotes wrapped on it (the to field).
4438  */
4439 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
4440 {
4441  const char *ptr;
4442 
4443  /* We're only ever passing 0 to maxlen, so short output isn't possible */
4444  ast_str_set(buf, maxlen, "\"");
4445  for (ptr = from; *ptr; ptr++) {
4446  if (*ptr == '"' || *ptr == '\\') {
4447  ast_str_append(buf, maxlen, "\\%c", *ptr);
4448  } else {
4449  ast_str_append(buf, maxlen, "%c", *ptr);
4450  }
4451  }
4452  ast_str_append(buf, maxlen, "\"");
4453 
4454  return ast_str_buffer(*buf);
4455 }
4456 
4457 /*! \brief
4458  * fill in *tm for current time according to the proper timezone, if any.
4459  * \return tm so it can be used as a function argument.
4460  */
4461 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
4462 {
4463  const struct vm_zone *z = NULL;
4464  struct timeval t = ast_tvnow();
4465 
4466  /* Does this user have a timezone specified? */
4467  if (!ast_strlen_zero(vmu->zonetag)) {
4468  /* Find the zone in the list */
4469  AST_LIST_LOCK(&zones);
4470  AST_LIST_TRAVERSE(&zones, z, list) {
4471  if (!strcmp(z->name, vmu->zonetag))
4472  break;
4473  }
4475  }
4476  ast_localtime(&t, tm, z ? z->timezone : NULL);
4477  return tm;
4478 }
4479 
4480 /*!\brief Check if the string would need encoding within the MIME standard, to
4481  * avoid confusing certain mail software that expects messages to be 7-bit
4482  * clean.
4483  */
4484 static int check_mime(const char *str)
4485 {
4486  for (; *str; str++) {
4487  if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
4488  return 1;
4489  }
4490  }
4491  return 0;
4492 }
4493 
4494 /*!\brief Encode a string according to the MIME rules for encoding strings
4495  * that are not 7-bit clean or contain control characters.
4496  *
4497  * Additionally, if the encoded string would exceed the MIME limit of 76
4498  * characters per line, then the encoding will be broken up into multiple
4499  * sections, separated by a space character, in order to facilitate
4500  * breaking up the associated header across multiple lines.
4501  *
4502  * \param end An expandable buffer for holding the result
4503  * \param maxlen Always zero, but see \see ast_str
4504  * \param start A string to be encoded
4505  * \param preamble The length of the first line already used for this string,
4506  * to ensure that each line maintains a maximum length of 76 chars.
4507  * \param postamble the length of any additional characters appended to the
4508  * line, used to ensure proper field wrapping.
4509  * \retval The encoded string.
4510  */
4511 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
4512 {
4513  struct ast_str *tmp = ast_str_alloca(80);
4514  int first_section = 1;
4515 
4516  ast_str_reset(*end);
4517  ast_str_set(&tmp, -1, "=?%s?Q?", charset);
4518  for (; *start; start++) {
4519  int need_encoding = 0;
4520  if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
4521  need_encoding = 1;
4522  }
4523  if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
4524  (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
4525  (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
4526  (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
4527  /* Start new line */
4528  ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
4529  ast_str_set(&tmp, -1, "=?%s?Q?", charset);
4530  first_section = 0;
4531  }
4532  if (need_encoding && *start == ' ') {
4533  ast_str_append(&tmp, -1, "_");
4534  } else if (need_encoding) {
4535  ast_str_append(&tmp, -1, "=%hhX", *start);
4536  } else {
4537  ast_str_append(&tmp, -1, "%c", *start);
4538  }
4539  }
4540  ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
4541  return ast_str_buffer(*end);
4542 }
4543 
4544 /*!
4545  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
4546  * \param p The output file to generate the email contents into.
4547  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
4548  * \param vmu The voicemail user who is sending the voicemail.
4549  * \param msgnum The message index in the mailbox folder.
4550  * \param context
4551  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
4552  * \param fromfolder
4553  * \param cidnum The caller ID number.
4554  * \param cidname The caller ID name.
4555  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
4556  * \param attach2
4557  * \param format The message sound file format. i.e. .wav
4558  * \param duration The time of the message content, in seconds.
4559  * \param attach_user_voicemail if 1, the sound file is attached to the email.
4560  * \param chan
4561  * \param category
4562  * \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.
4563  * \param flag
4564  *
4565  * The email body, and base 64 encoded attachement (if any) are stored to the file identified by *p. This method does not actually send the email. That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function.
4566  */
4567 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)
4568 {
4569  char date[256];
4570  char host[MAXHOSTNAMELEN] = "";
4571  char who[256];
4572  char bound[256];
4573  char dur[256];
4574  struct ast_tm tm;
4575  char enc_cidnum[256] = "", enc_cidname[256] = "";
4576  struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
4577  char *greeting_attachment;
4578  char filename[256];
4579 
4580  if (!str1 || !str2) {
4581  ast_free(str1);
4582  ast_free(str2);
4583  return;
4584  }
4585 
4586  if (cidnum) {
4587  strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
4588  }
4589  if (cidname) {
4590  strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
4591  }
4592  gethostname(host, sizeof(host) - 1);
4593 
4594  if (strchr(srcemail, '@')) {
4595  ast_copy_string(who, srcemail, sizeof(who));
4596  } else {
4597  snprintf(who, sizeof(who), "%s@%s", srcemail, host);
4598  }
4599 
4600  greeting_attachment = strrchr(ast_strdupa(attach), '/');
4601  if (greeting_attachment) {
4602  *greeting_attachment++ = '\0';
4603  }
4604 
4605  snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
4606  ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
4607  fprintf(p, "Date: %s" ENDL, date);
4608 
4609  /* Set date format for voicemail mail */
4610  ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
4611 
4612  if (!ast_strlen_zero(fromstring)) {
4613  struct ast_channel *ast;
4614  if ((ast = ast_dummy_channel_alloc())) {
4615  char *ptr;
4616  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
4617  ast_str_substitute_variables(&str1, 0, ast, fromstring);
4618 
4619  if (check_mime(ast_str_buffer(str1))) {
4620  int first_line = 1;
4621  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
4622  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
4623  *ptr = '\0';
4624  fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
4625  first_line = 0;
4626  /* Substring is smaller, so this will never grow */
4627  ast_str_set(&str2, 0, "%s", ptr + 1);
4628  }
4629  fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
4630  } else {
4631  fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
4632  }
4633  ast = ast_channel_unref(ast);
4634  } else {
4635  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4636  }
4637  } else {
4638  fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
4639  }
4640 
4641  if (check_mime(vmu->fullname)) {
4642  int first_line = 1;
4643  char *ptr;
4644  ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
4645  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
4646  *ptr = '\0';
4647  fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
4648  first_line = 0;
4649  /* Substring is smaller, so this will never grow */
4650  ast_str_set(&str2, 0, "%s", ptr + 1);
4651  }
4652  fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
4653  } else {
4654  fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
4655  }
4656 
4657  if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
4658  char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
4659  struct ast_channel *ast;
4660  if ((ast = ast_dummy_channel_alloc())) {
4661  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
4662  ast_str_substitute_variables(&str1, 0, ast, e_subj);
4663  if (check_mime(ast_str_buffer(str1))) {
4664  int first_line = 1;
4665  char *ptr;
4666  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
4667  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
4668  *ptr = '\0';
4669  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
4670  first_line = 0;
4671  /* Substring is smaller, so this will never grow */
4672  ast_str_set(&str2, 0, "%s", ptr + 1);
4673  }
4674  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
4675  } else {
4676  fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
4677  }
4678  ast = ast_channel_unref(ast);
4679  } else {
4680  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4681  }
4682  } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
4683  if (ast_strlen_zero(flag)) {
4684  fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
4685  } else {
4686  fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
4687  }
4688  } else {
4689  if (ast_strlen_zero(flag)) {
4690  fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
4691  } else {
4692  fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
4693  }
4694  }
4695 
4696  fprintf(p, "Message-ID: <Asterisk-%d-%u-%s-%d@%s>" ENDL, msgnum + 1,
4697  (unsigned int) ast_random(), mailbox, (int) getpid(), host);
4698  if (imap) {
4699  /* additional information needed for IMAP searching */
4700  fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
4701  /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
4702  fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
4703  fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
4704 #ifdef IMAP_STORAGE
4705  fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
4706 #else
4707  fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
4708 #endif
4709  /* flag added for Urgent */
4710  fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
4711  fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
4712  fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
4713  fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
4714  fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
4715  fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
4716  if (!ast_strlen_zero(category)) {
4717  fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
4718  } else {
4719  fprintf(p, "X-Asterisk-VM-Category: " ENDL);
4720  }
4721  fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
4722  fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
4723  fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
4724  }
4725  if (!ast_strlen_zero(cidnum)) {
4726  fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
4727  }
4728  if (!ast_strlen_zero(cidname)) {
4729  fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
4730  }
4731  fprintf(p, "MIME-Version: 1.0" ENDL);
4732  if (attach_user_voicemail) {
4733  /* Something unique. */
4734  snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%u", msgnum + 1, mailbox,
4735  (int) getpid(), (unsigned int) ast_random());
4736 
4737  fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
4738  fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
4739  fprintf(p, "--%s" ENDL, bound);
4740  }
4741  fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
4742  if (emailbody || vmu->emailbody) {
4743  char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
4744  struct ast_channel *ast;
4745  if ((ast = ast_dummy_channel_alloc())) {
4746  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
4747  ast_str_substitute_variables(&str1, 0, ast, e_body);
4748 #ifdef IMAP_STORAGE
4749  {
4750  /* Convert body to native line terminators for IMAP backend */
4751  char *line = ast_str_buffer(str1), *next;
4752  do {
4753  /* Terminate line before outputting it to the file */
4754  if ((next = strchr(line, '\n'))) {
4755  *next++ = '\0';
4756  }
4757  fprintf(p, "%s" ENDL, line);
4758  line = next;
4759  } while (!ast_strlen_zero(line));
4760  }
4761 #else
4762  fprintf(p, "%s" ENDL, ast_str_buffer(str1));
4763 #endif
4764  ast = ast_channel_unref(ast);
4765  } else {
4766  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4767  }
4768  } else if (msgnum > -1) {
4769  if (strcmp(vmu->mailbox, mailbox)) {
4770  /* Forwarded type */
4771  struct ast_config *msg_cfg;
4772  const char *v;
4773  int inttime;
4774  char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
4775  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
4776  /* Retrieve info from VM attribute file */
4777  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
4778  make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
4779  if (strlen(fromfile) < sizeof(fromfile) - 5) {
4780  strcat(fromfile, ".txt");
4781  }
4782  if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
4783  if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
4784  ast_copy_string(origcallerid, v, sizeof(origcallerid));
4785  }
4786 
4787  /* You might be tempted to do origdate, except that a) it's in the wrong
4788  * format, and b) it's missing for IMAP recordings. */
4789  if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
4790  struct timeval tv = { inttime, };
4791  struct ast_tm tm;
4792  ast_localtime(&tv, &tm, NULL);
4793  ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
4794  }
4795  fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
4796  " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
4797  "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
4798  " chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
4799  msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
4800  date, origcallerid, origdate);
4801  ast_config_destroy(msg_cfg);
4802  } else {
4803  goto plain_message;
4804  }
4805  } else {
4806 plain_message:
4807  fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
4808  "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
4809  "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
4810  ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
4811  (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
4812  }
4813  } else {
4814  fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
4815  "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
4816  }
4817 
4818  if (imap || attach_user_voicemail) {
4819  if (!ast_strlen_zero(attach2)) {
4820  snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
4821  ast_debug(5, "creating second attachment filename %s\n", filename);
4822  add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
4823  snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
4824  ast_debug(5, "creating attachment filename %s\n", filename);
4825  add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
4826  } else {
4827  snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
4828  ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
4829  add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
4830  }
4831  }
4832  ast_free(str1);
4833  ast_free(str2);
4834 }
4835 
4836 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)
4837 {
4838  char tmpdir[256], newtmp[256];
4839  char fname[256];
4840  char tmpcmd[256];
4841  int tmpfd = -1;
4842  int soxstatus = 0;
4843 
4844  /* Eww. We want formats to tell us their own MIME type */
4845  char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
4846 
4847  if (vmu->volgain < -.001 || vmu->volgain > .001) {
4848  create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
4849  snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
4850  tmpfd = mkstemp(newtmp);
4851  chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
4852  ast_debug(3, "newtmp: %s\n", newtmp);
4853  if (tmpfd > -1) {
4854  snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
4855  if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
4856  attach = newtmp;
4857  ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
4858  } else {
4859  ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
4860  soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
4861  ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
4862  }
4863  }
4864  }
4865  fprintf(p, "--%s" ENDL, bound);
4866  if (msgnum > -1)
4867  fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
4868  else
4869  fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
4870  fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
4871  fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
4872  if (msgnum > -1)
4873  fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
4874  else
4875  fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
4876  snprintf(fname, sizeof(fname), "%s.%s", attach, format);
4877  base_encode(fname, p);
4878  if (last)
4879  fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
4880  if (tmpfd > -1) {
4881  if (soxstatus == 0) {
4882  unlink(fname);
4883  }
4884  close(tmpfd);
4885  unlink(newtmp);
4886  }
4887  return 0;
4888 }
4889 
4890 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag)
4891 {
4892  FILE *p = NULL;
4893  char tmp[80] = "/tmp/astmail-XXXXXX";
4894  char tmp2[256];
4895  char *stringp;
4896 
4897  if (vmu && ast_strlen_zero(vmu->email)) {
4898  ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
4899  return(0);
4900  }
4901 
4902  /* Mail only the first format */
4903  format = ast_strdupa(format);
4904  stringp = format;
4905  strsep(&stringp, "|");
4906 
4907  if (!strcmp(format, "wav49"))
4908  format = "WAV";
4909  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));
4910  /* Make a temporary file instead of piping directly to sendmail, in case the mail
4911  command hangs */
4912  if ((p = vm_mkftemp(tmp)) == NULL) {
4913  ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
4914  return -1;
4915  } else {
4916  make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
4917  fclose(p);
4918  snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
4919  ast_safe_system(tmp2);
4920  ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
4921  }
4922  return 0;
4923 }
4924 
4925 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)
4926 {
4927  char enc_cidnum[256], enc_cidname[256];
4928  char date[256];
4929  char host[MAXHOSTNAMELEN] = "";
4930  char who[256];
4931  char dur[PATH_MAX];
4932  char tmp[80] = "/tmp/astmail-XXXXXX";
4933  char tmp2[PATH_MAX];
4934  struct ast_tm tm;
4935  FILE *p;
4936  struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
4937 
4938  if (!str1 || !str2) {
4939  ast_free(str1);
4940  ast_free(str2);
4941  return -1;
4942  }
4943 
4944  if (cidnum) {
4945  strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
4946  }
4947  if (cidname) {
4948  strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
4949  }
4950 
4951  if ((p = vm_mkftemp(tmp)) == NULL) {
4952  ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
4953  ast_free(str1);
4954  ast_free(str2);
4955  return -1;
4956  }
4957  gethostname(host, sizeof(host)-1);
4958  if (strchr(srcemail, '@')) {
4959  ast_copy_string(who, srcemail, sizeof(who));
4960  } else {
4961  snprintf(who, sizeof(who), "%s@%s", srcemail, host);
4962  }
4963  snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
4964  ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
4965  fprintf(p, "Date: %s\n", date);
4966 
4967  /* Reformat for custom pager format */
4968  ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
4969 
4970  if (!ast_strlen_zero(pagerfromstring)) {
4971  struct ast_channel *ast;
4972  if ((ast = ast_dummy_channel_alloc())) {
4973  char *ptr;
4974  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
4975  ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
4976 
4977  if (check_mime(ast_str_buffer(str1))) {
4978  int first_line = 1;
4979  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
4980  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
4981  *ptr = '\0';
4982  fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
4983  first_line = 0;
4984  /* Substring is smaller, so this will never grow */
4985  ast_str_set(&str2, 0, "%s", ptr + 1);
4986  }
4987  fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
4988  } else {
4989  fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
4990  }
4991  ast = ast_channel_unref(ast);
4992  } else {
4993  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4994  }
4995  } else {
4996  fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
4997  }
4998 
4999  if (check_mime(vmu->fullname)) {
5000  int first_line = 1;
5001  char *ptr;
5002  ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
5003  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5004  *ptr = '\0';
5005  fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
5006  first_line = 0;
5007  /* Substring is smaller, so this will never grow */
5008  ast_str_set(&str2, 0, "%s", ptr + 1);
5009  }
5010  fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
5011  } else {
5012  fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
5013  }
5014 
5015  if (!ast_strlen_zero(pagersubject)) {
5016  struct ast_channel *ast;
5017  if ((ast = ast_dummy_channel_alloc())) {
5018  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5019  ast_str_substitute_variables(&str1, 0, ast, pagersubject);
5020  if (check_mime(ast_str_buffer(str1))) {
5021  int first_line = 1;
5022  char *ptr;
5023  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
5024  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5025  *ptr = '\0';
5026  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5027  first_line = 0;
5028  /* Substring is smaller, so this will never grow */
5029  ast_str_set(&str2, 0, "%s", ptr + 1);
5030  }
5031  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5032  } else {
5033  fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
5034  }
5035  ast = ast_channel_unref(ast);
5036  } else {
5037  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5038  }
5039  } else {
5040  if (ast_strlen_zero(flag)) {
5041  fprintf(p, "Subject: New VM\n\n");
5042  } else {
5043  fprintf(p, "Subject: New %s VM\n\n", flag);
5044  }
5045  }
5046 
5047  if (pagerbody) {
5048  struct ast_channel *ast;
5049  if ((ast = ast_dummy_channel_alloc())) {
5050  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5051  ast_str_substitute_variables(&str1, 0, ast, pagerbody);
5052  fprintf(p, "%s" ENDL, ast_str_buffer(str1));
5053  ast = ast_channel_unref(ast);
5054  } else {
5055  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5056  }
5057  } else {
5058  fprintf(p, "New %s long %s msg in box %s\n"
5059  "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
5060  }
5061 
5062  fclose(p);
5063  snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
5064  ast_safe_system(tmp2);
5065  ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
5066  ast_free(str1);
5067  ast_free(str2);
5068  return 0;
5069 }
5070 
5071 /*!
5072  * \brief Gets the current date and time, as formatted string.
5073  * \param s The buffer to hold the output formatted date.
5074  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
5075  *
5076  * The date format string used is "%a %b %e %r UTC %Y".
5077  *
5078  * \return zero on success, -1 on error.
5079  */
5080 static int get_date(char *s, int len)
5081 {
5082  struct ast_tm tm;
5083  struct timeval t = ast_tvnow();
5084 
5085  ast_localtime(&t, &tm, "UTC");
5086 
5087  return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
5088 }
5089 
5090 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
5091 {
5092  int res;
5093  char fn[PATH_MAX];
5094  char dest[PATH_MAX];
5095 
5096  snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
5097 
5098  if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
5099  ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
5100  return -1;
5101  }
5102 
5103  RETRIEVE(fn, -1, ext, context);
5104  if (ast_fileexists(fn, NULL, NULL) > 0) {
5105  res = ast_stream_and_wait(chan, fn, ecodes);
5106  if (res) {
5107  DISPOSE(fn, -1);
5108  return res;
5109  }
5110  } else {
5111  /* Dispose just in case */
5112  DISPOSE(fn, -1);
5113  res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
5114  if (res)
5115  return res;
5116  res = ast_say_digit_str(chan, ext, ecodes, chan->language);
5117  if (res)
5118  return res;
5119  }
5120  res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
5121  return res;
5122 }
5123 
5124 static void free_zone(struct vm_zone *z)
5125 {
5126  ast_free(z);
5127 }
5128 
5129 #ifdef ODBC_STORAGE
5130 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
5131 {
5132  int x = -1;
5133  int res;
5134  SQLHSTMT stmt = NULL;
5135  char sql[PATH_MAX];
5136  char rowdata[20];
5137  char tmp[PATH_MAX] = "";
5138  struct odbc_obj *obj = NULL;
5139  char *context;
5140  struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
5141 
5142  if (newmsgs)
5143  *newmsgs = 0;
5144  if (oldmsgs)
5145  *oldmsgs = 0;
5146  if (urgentmsgs)
5147  *urgentmsgs = 0;
5148 
5149  /* If no mailbox, return immediately */
5150  if (ast_strlen_zero(mailbox))
5151  return 0;
5152 
5153  ast_copy_string(tmp, mailbox, sizeof(tmp));
5154 
5155  if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
5156  int u, n, o;
5157  char *next, *remaining = tmp;
5158  while ((next = strsep(&remaining, " ,"))) {
5159  if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
5160  return -1;
5161  }
5162  if (urgentmsgs) {
5163  *urgentmsgs += u;
5164  }
5165  if (newmsgs) {
5166  *newmsgs += n;
5167  }
5168  if (oldmsgs) {
5169  *oldmsgs += o;
5170  }
5171  }
5172  return 0;
5173  }
5174 
5175  context = strchr(tmp, '@');
5176  if (context) {
5177  *context = '\0';
5178  context++;
5179  } else
5180  context = "default";
5181 
5182  if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
5183  do {
5184  if (newmsgs) {
5185  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
5186  if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
5187  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5188  break;
5189  }
5190  res = SQLFetch(stmt);
5191  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5192  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5193  break;
5194  }
5195  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5196  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5197  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5198  break;
5199  }
5200  *newmsgs = atoi(rowdata);
5201  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5202  }
5203 
5204  if (oldmsgs) {
5205  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
5206  if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
5207  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5208  break;
5209  }
5210  res = SQLFetch(stmt);
5211  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5212  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5213  break;
5214  }
5215  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5216  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5217  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5218  break;
5219  }
5220  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
5221  *oldmsgs = atoi(rowdata);
5222  }
5223 
5224  if (urgentmsgs) {
5225  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
5226  if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
5227  ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5228  break;
5229  }
5230  res = SQLFetch(stmt);
5231  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5232  ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5233  break;
5234  }
5235  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5236  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5237  ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5238  break;
5239  }
5240  *urgentmsgs = atoi(rowdata);
5241  }
5242 
5243  x = 0;
5244  } while (0);
5245  } else {
5246  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
5247  }
5248 
5249  if (stmt) {
5250  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5251  }
5252  if (obj) {
5253  ast_odbc_release_obj(obj);
5254  }
5255  return x;
5256 }
5257 
5258 /*!
5259  * \brief Gets the number of messages that exist in a mailbox folder.
5260  * \param context
5261  * \param mailbox
5262  * \param folder
5263  *
5264  * This method is used when ODBC backend is used.
5265  * \return The number of messages in this mailbox folder (zero or more).
5266  */
5267 static int messagecount(const char *context, const char *mailbox, const char *folder)
5268 {
5269  struct odbc_obj *obj = NULL;
5270  int nummsgs = 0;
5271  int res;
5272  SQLHSTMT stmt = NULL;
5273  char sql[PATH_MAX];
5274  char rowdata[20];
5275  struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
5276  if (!folder)
5277  folder = "INBOX";
5278  /* If no mailbox, return immediately */
5279  if (ast_strlen_zero(mailbox))
5280  return 0;
5281 
5282  obj = ast_odbc_request_obj(odbc_database, 0);
5283  if (obj) {
5284  if (!strcmp(folder, "INBOX")) {
5285  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/INBOX' OR dir = '%s%s/%s/Urgent'", odbc_table, VM_SPOOL_DIR, context, mailbox, VM_SPOOL_DIR, context, mailbox);
5286  } else {
5287  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
5288  }
5289  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
5290  if (!stmt) {
5291  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5292  goto yuck;
5293  }
5294  res = SQLFetch(stmt);
5295  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5296  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5297  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5298  goto yuck;
5299  }
5300  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5301  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5302  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5303  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5304  goto yuck;
5305  }
5306  nummsgs = atoi(rowdata);
5307  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5308  } else
5309  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
5310 
5311 yuck:
5312  if (obj)
5313  ast_odbc_release_obj(obj);
5314  return nummsgs;
5315 }
5316 
5317 /**
5318  * \brief Determines if the given folder has messages.
5319  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
5320  *
5321  * This function is used when the mailbox is stored in an ODBC back end.
5322  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
5323  * \return 1 if the folder has one or more messages. zero otherwise.
5324  */
5325 static int has_voicemail(const char *mailbox, const char *folder)
5326 {
5327  char tmp[256], *tmp2 = tmp, *box, *context;
5328  ast_copy_string(tmp, mailbox, sizeof(tmp));
5329  while ((context = box = strsep(&tmp2, ",&"))) {
5330  strsep(&context, "@");
5331  if (ast_strlen_zero(context))
5332  context = "default";
5333  if (messagecount(context, box, folder))
5334  return 1;
5335  }
5336  return 0;
5337 }
5338 #endif
5339 #ifndef IMAP_STORAGE
5340 /*!
5341  * \brief Copies a message from one mailbox to another.
5342  * \param chan
5343  * \param vmu
5344  * \param imbox
5345  * \param msgnum
5346  * \param duration
5347  * \param recip
5348  * \param fmt
5349  * \param dir
5350  * \param flag
5351  *
5352  * This is only used by file storage based mailboxes.
5353  *
5354  * \return zero on success, -1 on error.
5355  */
5356 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)
5357 {
5358  char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
5359  const char *frombox = mbox(vmu, imbox);
5360  const char *userfolder;
5361  int recipmsgnum;
5362  int res = 0;
5363 
5364  ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
5365 
5366  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
5367  userfolder = "Urgent";
5368  } else {
5369  userfolder = "INBOX";
5370  }
5371 
5372  create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
5373 
5374  if (!dir)
5375  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
5376  else
5377  ast_copy_string(fromdir, dir, sizeof(fromdir));
5378 
5379  make_file(frompath, sizeof(frompath), fromdir, msgnum);
5380  make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
5381 
5382  if (vm_lock_path(todir))
5383  return ERROR_LOCK_PATH;
5384 
5385  recipmsgnum = last_message_index(recip, todir) + 1;
5386  if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
5387  make_file(topath, sizeof(topath), todir, recipmsgnum);
5388 #ifndef ODBC_STORAGE
5389  if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
5390  COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
5391  } else {
5392 #endif
5393  /* If we are prepending a message for ODBC, then the message already
5394  * exists in the database, but we want to force copying from the
5395  * filesystem (since only the FS contains the prepend). */
5396  copy_plain_file(frompath, topath);
5397  STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
5398  vm_delete(topath);
5399 #ifndef ODBC_STORAGE
5400  }
5401 #endif
5402  } else {
5403  ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
5404  res = -1;
5405  }
5406  ast_unlock_path(todir);
5407  notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
5408  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
5409  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
5410  flag);
5411 
5412  return res;
5413 }
5414 #endif
5415 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
5416 
5417 static int messagecount(const char *context, const char *mailbox, const char *folder)
5418 {
5419  return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
5420 }
5421 
5422 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
5423 {
5424  DIR *dir;
5425  struct dirent *de;
5426  char fn[256];
5427  int ret = 0;
5428 
5429  /* If no mailbox, return immediately */
5430  if (ast_strlen_zero(mailbox))
5431  return 0;
5432 
5433  if (ast_strlen_zero(folder))
5434  folder = "INBOX";
5435  if (ast_strlen_zero(context))
5436  context = "default";
5437 
5438  snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
5439 
5440  if (!(dir = opendir(fn)))
5441  return 0;
5442 
5443  while ((de = readdir(dir))) {
5444  if (!strncasecmp(de->d_name, "msg", 3)) {
5445  if (shortcircuit) {
5446  ret = 1;
5447  break;
5448  } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
5449  ret++;
5450  }
5451  }
5452  }
5453 
5454  closedir(dir);
5455 
5456  return ret;
5457 }
5458 
5459 /**
5460  * \brief Determines if the given folder has messages.
5461  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
5462  * \param folder the folder to look in
5463  *
5464  * This function is used when the mailbox is stored in a filesystem back end.
5465  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
5466  * \return 1 if the folder has one or more messages. zero otherwise.
5467  */
5468 static int has_voicemail(const char *mailbox, const char *folder)
5469 {
5470  char tmp[256], *tmp2 = tmp, *box, *context;
5471  ast_copy_string(tmp, mailbox, sizeof(tmp));
5472  if (ast_strlen_zero(folder)) {
5473  folder = "INBOX";
5474  }
5475  while ((box = strsep(&tmp2, ",&"))) {
5476  if ((context = strchr(box, '@')))
5477  *context++ = '\0';
5478  else
5479  context = "default";
5480  if (__has_voicemail(context, box, folder, 1))
5481  return 1;
5482  /* If we are checking INBOX, we should check Urgent as well */
5483  if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
5484  return 1;
5485  }
5486  }
5487  return 0;
5488 }
5489 
5490 
5491 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
5492 {
5493  char tmp[256];
5494  char *context;
5495 
5496  /* If no mailbox, return immediately */
5497  if (ast_strlen_zero(mailbox))
5498  return 0;
5499 
5500  if (newmsgs)
5501  *newmsgs = 0;
5502  if (oldmsgs)
5503  *oldmsgs = 0;
5504  if (urgentmsgs)
5505  *urgentmsgs = 0;
5506 
5507  if (strchr(mailbox, ',')) {
5508  int tmpnew, tmpold, tmpurgent;
5509  char *mb, *cur;
5510 
5511  ast_copy_string(tmp, mailbox, sizeof(tmp));
5512  mb = tmp;
5513  while ((cur = strsep(&mb, ", "))) {
5514  if (!ast_strlen_zero(cur)) {
5515  if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
5516  return -1;
5517  else {
5518  if (newmsgs)
5519  *newmsgs += tmpnew;
5520  if (oldmsgs)
5521  *oldmsgs += tmpold;
5522  if (urgentmsgs)
5523  *urgentmsgs += tmpurgent;
5524  }
5525  }
5526  }
5527  return 0;
5528  }
5529 
5530  ast_copy_string(tmp, mailbox, sizeof(tmp));
5531 
5532  if ((context = strchr(tmp, '@')))
5533  *context++ = '\0';
5534  else
5535  context = "default";
5536 
5537  if (newmsgs)
5538  *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
5539  if (oldmsgs)
5540  *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
5541  if (urgentmsgs)
5542  *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
5543 
5544  return 0;
5545 }
5546 
5547 #endif
5548 
5549 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
5550 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
5551 {
5552  int urgentmsgs = 0;
5553  int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
5554  if (newmsgs) {
5555  *newmsgs += urgentmsgs;
5556  }
5557  return res;
5558 }
5559 
5560 static void run_externnotify(char *context, char *extension, const char *flag)
5561 {
5562  char arguments[255];
5563  char ext_context[256] = "";
5564  int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
5565  struct ast_smdi_mwi_message *mwi_msg;
5566 
5567  if (!ast_strlen_zero(context))
5568  snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
5569  else
5570  ast_copy_string(ext_context, extension, sizeof(ext_context));
5571 
5572  if (smdi_iface) {
5573  if (ast_app_has_voicemail(ext_context, NULL))
5574  ast_smdi_mwi_set(smdi_iface, extension);
5575  else
5576  ast_smdi_mwi_unset(smdi_iface, extension);
5577 
5578  if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
5579  ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
5580  if (!strncmp(mwi_msg->cause, "INV", 3))
5581  ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
5582  else if (!strncmp(mwi_msg->cause, "BLK", 3))
5583  ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
5584  ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
5586  } else {
5587  ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
5588  }
5589  }
5590 
5591  if (!ast_strlen_zero(externnotify)) {
5592  if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
5593  ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
5594  } else {
5595  snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
5596  externnotify, S_OR(context, "\"\""),
5597  extension, newvoicemails,
5598  oldvoicemails, urgentvoicemails);
5599  ast_debug(1, "Executing %s\n", arguments);
5600  ast_safe_system(arguments);
5601  }
5602  }
5603 }
5604 
5605 /*!
5606  * \brief Variables used for saving a voicemail.
5607  *
5608  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
5609  */
5610 struct leave_vm_options {
5611  unsigned int flags;
5612  signed char record_gain;
5614 };
5615 
5616 /*!
5617  * \brief Prompts the user and records a voicemail to a mailbox.
5618  * \param chan
5619  * \param ext
5620  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
5621  *
5622  *
5623  *
5624  * \return zero on success, -1 on error.
5625  */
5626 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
5627 {
5628 #ifdef IMAP_STORAGE
5629  int newmsgs, oldmsgs;
5630 #else
5631  char urgdir[PATH_MAX];
5632 #endif
5633  char txtfile[PATH_MAX];
5634  char tmptxtfile[PATH_MAX];
5635  struct vm_state *vms = NULL;
5636  char callerid[256];
5637  FILE *txt;
5638  char date[256];
5639  int txtdes;
5640  int res = 0;
5641  int msgnum;
5642  int duration = 0;
5643  int sound_duration = 0;
5644  int ausemacro = 0;
5645  int ousemacro = 0;
5646  int ouseexten = 0;
5647  char tmpdur[16];
5648  char priority[16];
5649  char origtime[16];
5650  char dir[PATH_MAX];
5651  char tmpdir[PATH_MAX];
5652  char fn[PATH_MAX];
5653  char prefile[PATH_MAX] = "";
5654  char tempfile[PATH_MAX] = "";
5655  char ext_context[256] = "";
5656  char fmt[80];
5657  char *context;
5658  char ecodes[17] = "#";
5659  struct ast_str *tmp = ast_str_create(16);
5660  char *tmpptr;
5661  struct ast_vm_user *vmu;
5662  struct ast_vm_user svm;
5663  const char *category = NULL;
5664  const char *code;
5665  const char *alldtmf = "0123456789ABCD*#";
5666  char flag[80];
5667 
5668  if (!tmp) {
5669  return -1;
5670  }
5671 
5672  ast_str_set(&tmp, 0, "%s", ext);
5673  ext = ast_str_buffer(tmp);
5674  if ((context = strchr(ext, '@'))) {
5675  *context++ = '\0';
5676  tmpptr = strchr(context, '&');
5677  } else {
5678  tmpptr = strchr(ext, '&');
5679  }
5680 
5681  if (tmpptr)
5682  *tmpptr++ = '\0';
5683 
5684  ast_channel_lock(chan);
5685  if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
5686  category = ast_strdupa(category);
5687  }
5688  ast_channel_unlock(chan);
5689 
5690  if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
5691  ast_copy_string(flag, "Urgent", sizeof(flag));
5692  } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
5693  ast_copy_string(flag, "PRIORITY", sizeof(flag));
5694  } else {
5695  flag[0] = '\0';
5696  }
5697 
5698  ast_debug(3, "Before find_user\n");
5699  if (!(vmu = find_user(&svm, context, ext))) {
5700  ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
5701  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5702  ast_free(tmp);
5703  return res;
5704  }
5705  /* Setup pre-file if appropriate */
5706  if (strcmp(vmu->context, "default"))
5707  snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
5708  else
5709  ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
5710 
5711  /* Set the path to the prefile. Will be one of
5712  VM_SPOOL_DIRcontext/ext/busy
5713  VM_SPOOL_DIRcontext/ext/unavail
5714  Depending on the flag set in options.
5715  */
5716  if (ast_test_flag(options, OPT_BUSY_GREETING)) {
5717  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
5718  } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
5719  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
5720  }
5721  /* Set the path to the tmpfile as
5722  VM_SPOOL_DIR/context/ext/temp
5723  and attempt to create the folder structure.
5724  */
5725  snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
5726  if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
5727  ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
5728  ast_free(tmp);
5729  return -1;
5730  }
5731  RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
5732  if (ast_fileexists(tempfile, NULL, NULL) > 0)
5733  ast_copy_string(prefile, tempfile, sizeof(prefile));
5734 
5735  DISPOSE(tempfile, -1);
5736  /* It's easier just to try to make it than to check for its existence */
5737 #ifndef IMAP_STORAGE
5738  create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
5739 #else
5740  snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
5741  if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
5742  ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
5743  }
5744 #endif
5745 
5746  /* Check current or macro-calling context for special extensions */
5747  if (ast_test_flag(vmu, VM_OPERATOR)) {
5748  if (!ast_strlen_zero(vmu->exit)) {
5749  if (ast_exists_extension(chan, vmu->exit, "o", 1,
5750  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5751  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
5752  ouseexten = 1;
5753  }
5754  } else if (ast_exists_extension(chan, chan->context, "o", 1,
5755  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5756  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
5757  ouseexten = 1;
5758  } else if (!ast_strlen_zero(chan->macrocontext)
5759  && ast_exists_extension(chan, chan->macrocontext, "o", 1,
5760  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5761  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
5762  ousemacro = 1;
5763  }
5764  }
5765 
5766  if (!ast_strlen_zero(vmu->exit)) {
5767  if (ast_exists_extension(chan, vmu->exit, "a", 1,
5768  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5769  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
5770  }
5771  } else if (ast_exists_extension(chan, chan->context, "a", 1,
5772  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5773  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
5774  } else if (!ast_strlen_zero(chan->macrocontext)
5775  && ast_exists_extension(chan, chan->macrocontext, "a", 1,
5776  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5777  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
5778  ausemacro = 1;
5779  }
5780 
5781  if (ast_test_flag(options, OPT_DTMFEXIT)) {
5782  for (code = alldtmf; *code; code++) {
5783  char e[2] = "";
5784  e[0] = *code;
5785  if (strchr(ecodes, e[0]) == NULL
5786  && ast_canmatch_extension(chan,
5787  (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : chan->context),
5788  e, 1, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5789  strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
5790  }
5791  }
5792  }
5793 
5794  /* Play the beginning intro if desired */
5795  if (!ast_strlen_zero(prefile)) {
5796 #ifdef ODBC_STORAGE
5797  int success =
5798 #endif
5799  RETRIEVE(prefile, -1, ext, context);
5800  if (ast_fileexists(prefile, NULL, NULL) > 0) {
5801  if (ast_streamfile(chan, prefile, chan->language) > -1)
5802  res = ast_waitstream(chan, ecodes);
5803 #ifdef ODBC_STORAGE
5804  if (success == -1) {
5805  /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
5806  ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
5807  store_file(prefile, vmu->mailbox, vmu->context, -1);
5808  }
5809 #endif
5810  } else {
5811  ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
5812  res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
5813  }
5814  DISPOSE(prefile, -1);
5815  if (res < 0) {
5816  ast_debug(1, "Hang up during prefile playback\n");
5817  free_user(vmu);
5818  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5819  ast_free(tmp);
5820  return -1;
5821  }
5822  }
5823  if (res == '#') {
5824  /* On a '#' we skip the instructions */
5825  ast_set_flag(options, OPT_SILENT);
5826  res = 0;
5827  }
5828  /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
5829  if (vmu->maxmsg == 0) {
5830  if (option_debug > 2)
5831  ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
5832  pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
5833  goto leave_vm_out;
5834  }
5835  if (!res && !ast_test_flag(options, OPT_SILENT)) {
5836  res = ast_stream_and_wait(chan, INTRO, ecodes);
5837  if (res == '#') {
5838  ast_set_flag(options, OPT_SILENT);
5839  res = 0;
5840  }
5841  }
5842  if (res > 0)
5843  ast_stopstream(chan);
5844  /* Check for a '*' here in case the caller wants to escape from voicemail to something
5845  other than the operator -- an automated attendant or mailbox login for example */
5846  if (res == '*') {
5847  chan->exten[0] = 'a';
5848  chan->exten[1] = '\0';
5849  if (!ast_strlen_zero(vmu->exit)) {
5850  ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
5851  } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
5852  ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
5853  }
5854  chan->priority = 0;
5855  free_user(vmu);
5856  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
5857  ast_free(tmp);
5858  return 0;
5859  }
5860 
5861  /* Check for a '0' here */
5862  if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
5863  transfer:
5864  if (ouseexten || ousemacro) {
5865  chan->exten[0] = 'o';
5866  chan->exten[1] = '\0';
5867  if (!ast_strlen_zero(vmu->exit)) {
5868  ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
5869  } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
5870  ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
5871  }
5872  ast_play_and_wait(chan, "transfer");
5873  chan->priority = 0;
5874  free_user(vmu);
5875  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
5876  }
5877  ast_free(tmp);
5878  return OPERATOR_EXIT;
5879  }
5880 
5881  /* Allow all other digits to exit Voicemail and return to the dialplan */
5882  if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
5883  if (!ast_strlen_zero(options->exitcontext)) {
5884  ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
5885  }
5886  free_user(vmu);
5887  ast_free(tmp);
5888  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
5889  return res;
5890  }
5891 
5892  if (res < 0) {
5893  free_user(vmu);
5894  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5895  ast_free(tmp);
5896  return -1;
5897  }
5898  /* The meat of recording the message... All the announcements and beeps have been played*/
5899  ast_copy_string(fmt, vmfmts, sizeof(fmt));
5900  if (!ast_strlen_zero(fmt)) {
5901  msgnum = 0;
5902 
5903 #ifdef IMAP_STORAGE
5904  /* Is ext a mailbox? */
5905  /* must open stream for this user to get info! */
5906  res = inboxcount(ext_context, &newmsgs, &oldmsgs);
5907  if (res < 0) {
5908  ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
5909  ast_free(tmp);
5910  return -1;
5911  }
5912  if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
5913  /* It is possible under certain circumstances that inboxcount did not
5914  * create a vm_state when it was needed. This is a catchall which will
5915  * rarely be used.
5916  */
5917  if (!(vms = create_vm_state_from_user(vmu))) {
5918  ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
5919  ast_free(tmp);
5920  return -1;
5921  }
5922  }
5923  vms->newmessages++;
5924 
5925  /* here is a big difference! We add one to it later */
5926  msgnum = newmsgs + oldmsgs;
5927  ast_debug(3, "Messagecount set to %d\n", msgnum);
5928  snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
5929  /* set variable for compatibility */
5930  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
5931 
5932  if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
5933  goto leave_vm_out;
5934  }
5935 #else
5936  if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
5937  res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
5938  if (!res)
5939  res = ast_waitstream(chan, "");
5940  ast_log(AST_LOG_WARNING, "No more messages possible\n");
5941  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5942  inprocess_count(vmu->mailbox, vmu->context, -1);
5943  goto leave_vm_out;
5944  }
5945 
5946 #endif
5947  snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
5948  txtdes = mkstemp(tmptxtfile);
5949  chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
5950  if (txtdes < 0) {
5951  res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
5952  if (!res)
5953  res = ast_waitstream(chan, "");
5954  ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
5955  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5956  inprocess_count(vmu->mailbox, vmu->context, -1);
5957  goto leave_vm_out;
5958  }
5959 
5960  /* Now play the beep once we have the message number for our next message. */
5961  if (res >= 0) {
5962  /* Unless we're *really* silent, try to send the beep */
5963  res = ast_stream_and_wait(chan, "beep", "");
5964  }
5965 
5966  /* Store information in real-time storage */
5967  if (ast_check_realtime("voicemail_data")) {
5968  snprintf(priority, sizeof(priority), "%d", chan->priority);
5969  snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
5970  get_date(date, sizeof(date));
5971  ast_callerid_merge(callerid, sizeof(callerid),
5972  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
5973  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
5974  "Unknown");
5975  ast_store_realtime("voicemail_data",
5976  "origmailbox", ext,
5977  "context", chan->context,
5978  "macrocontext", chan->macrocontext,
5979  "exten", chan->exten,
5980  "priority", priority,
5981  "callerchan", chan->name,
5982  "callerid", callerid,
5983  "origdate", date,
5984  "origtime", origtime,
5985  "category", S_OR(category, ""),
5986  "filename", tmptxtfile,
5987  SENTINEL);
5988  }
5989 
5990  /* Store information */
5991  txt = fdopen(txtdes, "w+");
5992  if (txt) {
5993  get_date(date, sizeof(date));
5994  ast_callerid_merge(callerid, sizeof(callerid),
5995  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
5996  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
5997  "Unknown");
5998  fprintf(txt,
5999  ";\n"
6000  "; Message Information file\n"
6001  ";\n"
6002  "[message]\n"
6003  "origmailbox=%s\n"
6004  "context=%s\n"
6005  "macrocontext=%s\n"
6006  "exten=%s\n"
6007  "rdnis=%s\n"
6008  "priority=%d\n"
6009  "callerchan=%s\n"
6010  "callerid=%s\n"
6011  "origdate=%s\n"
6012  "origtime=%ld\n"
6013  "category=%s\n",
6014  ext,
6015  chan->context,
6016  chan->macrocontext,
6017  chan->exten,
6019  chan->redirecting.from.number.str, "unknown"),
6020  chan->priority,
6021  chan->name,
6022  callerid,
6023  date, (long) time(NULL),
6024  category ? category : "");
6025  } else {
6026  ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
6027  inprocess_count(vmu->mailbox, vmu->context, -1);
6028  if (ast_check_realtime("voicemail_data")) {
6029  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6030  }
6031  res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
6032  goto leave_vm_out;
6033  }
6034  res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
6035 
6036  if (txt) {
6037  fprintf(txt, "flag=%s\n", flag);
6038  if (sound_duration < vmu->minsecs) {
6039  fclose(txt);
6040  ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
6041  ast_filedelete(tmptxtfile, NULL);
6042  unlink(tmptxtfile);
6043  if (ast_check_realtime("voicemail_data")) {
6044  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6045  }
6046  inprocess_count(vmu->mailbox, vmu->context, -1);
6047  } else {
6048  fprintf(txt, "duration=%d\n", duration);
6049  fclose(txt);
6050  if (vm_lock_path(dir)) {
6051  ast_log(AST_LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
6052  /* Delete files */
6053  ast_filedelete(tmptxtfile, NULL);
6054  unlink(tmptxtfile);
6055  inprocess_count(vmu->mailbox, vmu->context, -1);
6056  } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
6057  ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
6058  unlink(tmptxtfile);
6059  ast_unlock_path(dir);
6060  inprocess_count(vmu->mailbox, vmu->context, -1);
6061  if (ast_check_realtime("voicemail_data")) {
6062  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6063  }
6064  } else {
6065 #ifndef IMAP_STORAGE
6066  msgnum = last_message_index(vmu, dir) + 1;
6067 #endif
6068  make_file(fn, sizeof(fn), dir, msgnum);
6069 
6070  /* assign a variable with the name of the voicemail file */
6071 #ifndef IMAP_STORAGE
6072  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
6073 #else
6074  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
6075 #endif
6076 
6077  snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
6078  ast_filerename(tmptxtfile, fn, NULL);
6079  rename(tmptxtfile, txtfile);
6080  inprocess_count(vmu->mailbox, vmu->context, -1);
6081 
6082  /* Properly set permissions on voicemail text descriptor file.
6083  Unfortunately mkstemp() makes this file 0600 on most unix systems. */
6084  if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
6085  ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
6086 
6087  ast_unlock_path(dir);
6088  if (ast_check_realtime("voicemail_data")) {
6089  snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
6090  ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
6091  }
6092  /* We must store the file first, before copying the message, because
6093  * ODBC storage does the entire copy with SQL.
6094  */
6095  if (ast_fileexists(fn, NULL, NULL) > 0) {
6096  STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
6097  }
6098 
6099  /* Are there to be more recipients of this message? */
6100  while (tmpptr) {
6101  struct ast_vm_user recipu, *recip;
6102  char *exten, *cntx;
6103 
6104  exten = strsep(&tmpptr, "&");
6105  cntx = strchr(exten, '@');
6106  if (cntx) {
6107  *cntx = '\0';
6108  cntx++;
6109  }
6110  if ((recip = find_user(&recipu, cntx, exten))) {
6111  copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
6112  free_user(recip);
6113  }
6114  }
6115 #ifndef IMAP_STORAGE
6116  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
6117  /* Move the message from INBOX to Urgent folder if this is urgent! */
6118  char sfn[PATH_MAX];
6119  char dfn[PATH_MAX];
6120  int x;
6121  /* It's easier just to try to make it than to check for its existence */
6122  create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
6123  x = last_message_index(vmu, urgdir) + 1;
6124  make_file(sfn, sizeof(sfn), dir, msgnum);
6125  make_file(dfn, sizeof(dfn), urgdir, x);
6126  ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
6127  RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
6128  /* Notification must happen for this new message in Urgent folder, not INBOX */
6129  ast_copy_string(fn, dfn, sizeof(fn));
6130  msgnum = x;
6131  }
6132 #endif
6133  /* Notification needs to happen after the copy, though. */
6134  if (ast_fileexists(fn, NULL, NULL)) {
6135 #ifdef IMAP_STORAGE
6136  notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
6137  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
6138  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
6139  flag);
6140 #else
6141  notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
6142  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
6143  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
6144  flag);
6145 #endif
6146  }
6147 
6148  /* Disposal needs to happen after the optional move and copy */
6149  if (ast_fileexists(fn, NULL, NULL)) {
6150  DISPOSE(dir, msgnum);
6151  }
6152  }
6153  }
6154  } else {
6155  inprocess_count(vmu->mailbox, vmu->context, -1);
6156  }
6157  if (res == '0') {
6158  goto transfer;
6159  } else if (res > 0 && res != 't')
6160  res = 0;
6161 
6162  if (sound_duration < vmu->minsecs)
6163  /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
6164  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
6165  else
6166  pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
6167  } else
6168  ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
6169 leave_vm_out:
6170  free_user(vmu);
6171 
6172 #ifdef IMAP_STORAGE
6173  /* expunge message - use UID Expunge if supported on IMAP server*/
6174  ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
6175  if (expungeonhangup == 1) {
6176  ast_mutex_lock(&vms->lock);
6177 #ifdef HAVE_IMAP_TK2006
6178  if (LEVELUIDPLUS (vms->mailstream)) {
6179  mail_expunge_full(vms->mailstream, NIL, EX_UID);
6180  } else
6181 #endif
6182  mail_expunge(vms->mailstream);
6183  ast_mutex_unlock(&vms->lock);
6184  }
6185 #endif
6186 
6187  ast_free(tmp);
6188  return res;
6189 }
6190 
6191 #if !defined(IMAP_STORAGE)
6192 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
6193 {
6194  /* we know the actual number of messages, so stop process when number is hit */
6195 
6196  int x, dest;
6197  char sfn[PATH_MAX];
6198  char dfn[PATH_MAX];
6199 
6200  if (vm_lock_path(dir)) {
6201  return ERROR_LOCK_PATH;
6202  }
6203 
6204  for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
6205  make_file(sfn, sizeof(sfn), dir, x);
6206  if (EXISTS(dir, x, sfn, NULL)) {
6207 
6208  if (x != dest) {
6209  make_file(dfn, sizeof(dfn), dir, dest);
6210  RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
6211  }
6212 
6213  dest++;
6214  }
6215  }
6216  ast_unlock_path(dir);
6217 
6218  return dest;
6219 }
6220 #endif
6221 
6222 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
6223 {
6224  int d;
6225  d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
6226  return d;
6227 }
6228 
6229 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
6230 {
6231 #ifdef IMAP_STORAGE
6232  /* we must use mbox(x) folder names, and copy the message there */
6233  /* simple. huh? */
6234  char sequence[10];
6235  char mailbox[256];
6236  int res;
6237 
6238  /* get the real IMAP message number for this message */
6239  snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
6240 
6241  ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
6242  ast_mutex_lock(&vms->lock);
6243  /* if save to Old folder, put in INBOX as read */
6244  if (box == OLD_FOLDER) {
6245  mail_setflag(vms->mailstream, sequence, "\\Seen");
6246  mail_clearflag(vms->mailstream, sequence, "\\Unseen");
6247  } else if (box == NEW_FOLDER) {
6248  mail_setflag(vms->mailstream, sequence, "\\Unseen");
6249  mail_clearflag(vms->mailstream, sequence, "\\Seen");
6250  }
6251  if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
6252  ast_mutex_unlock(&vms->lock);
6253  return 0;
6254  }
6255  /* Create the folder if it don't exist */
6256  imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
6257  ast_debug(5, "Checking if folder exists: %s\n", mailbox);
6258  if (mail_create(vms->mailstream, mailbox) == NIL)
6259  ast_debug(5, "Folder exists.\n");
6260  else
6261  ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
6262  res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
6263  ast_mutex_unlock(&vms->lock);
6264  return res;
6265 #else
6266  char *dir = vms->curdir;
6267  char *username = vms->username;
6268  char *context = vmu->context;
6269  char sfn[PATH_MAX];
6270  char dfn[PATH_MAX];
6271  char ddir[PATH_MAX];
6272  const char *dbox = mbox(vmu, box);
6273  int x, i;
6274  create_dirpath(ddir, sizeof(ddir), context, username, dbox);
6275 
6276  if (vm_lock_path(ddir))
6277  return ERROR_LOCK_PATH;
6278 
6279  x = last_message_index(vmu, ddir) + 1;
6280 
6281  if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
6282  x--;
6283  for (i = 1; i <= x; i++) {
6284  /* Push files down a "slot". The oldest file (msg0000) will be deleted. */
6285  make_file(sfn, sizeof(sfn), ddir, i);
6286  make_file(dfn, sizeof(dfn), ddir, i - 1);
6287  if (EXISTS(ddir, i, sfn, NULL)) {
6288  RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
6289  } else
6290  break;
6291  }
6292  } else {
6293  if (x >= vmu->maxmsg) {
6294  ast_unlock_path(ddir);
6295  return -1;
6296  }
6297  }
6298  make_file(sfn, sizeof(sfn), dir, msg);
6299  make_file(dfn, sizeof(dfn), ddir, x);
6300  if (strcmp(sfn, dfn)) {
6301  COPY(dir, msg, ddir, x, username, context, sfn, dfn);
6302  }
6303  ast_unlock_path(ddir);
6304 #endif
6305  return 0;
6306 }
6307 
6308 static int adsi_logo(unsigned char *buf)
6309 {
6310  int bytes = 0;
6311  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
6312  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
6313  return bytes;
6314 }
6315 
6316 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
6317 {
6318  unsigned char buf[256];
6319  int bytes = 0;
6320  int x;
6321  char num[5];
6322 
6323  *useadsi = 0;
6324  bytes += ast_adsi_data_mode(buf + bytes);
6325  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6326 
6327  bytes = 0;
6328  bytes += adsi_logo(buf);
6329  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
6330 #ifdef DISPLAY
6331  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
6332 #endif
6333  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6334  bytes += ast_adsi_data_mode(buf + bytes);
6335  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6336 
6337  if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
6338  bytes = 0;
6339  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
6340  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
6341  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6342  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6343  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6344  return 0;
6345  }
6346 
6347 #ifdef DISPLAY
6348  /* Add a dot */
6349  bytes = 0;
6350  bytes += ast_adsi_logo(buf);
6351  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
6352  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
6353  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6354  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6355 #endif
6356  bytes = 0;
6357  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
6358  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
6359  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
6360  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
6361  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
6362  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
6363  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
6364 
6365 #ifdef DISPLAY
6366  /* Add another dot */
6367  bytes = 0;
6368  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
6369  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6370 
6371  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6372  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6373 #endif
6374 
6375  bytes = 0;
6376  /* These buttons we load but don't use yet */
6377  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
6378  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
6379  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
6380  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
6381  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
6382  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
6383  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
6384 
6385 #ifdef DISPLAY
6386  /* Add another dot */
6387  bytes = 0;
6388  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
6389  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6390  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6391 #endif
6392 
6393  bytes = 0;
6394  for (x = 0; x < 5; x++) {
6395  snprintf(num, sizeof(num), "%d", x);
6396  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
6397  }
6398  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
6399  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
6400 
6401 #ifdef DISPLAY
6402  /* Add another dot */
6403  bytes = 0;
6404  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
6405  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6406  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6407 #endif
6408 
6409  if (ast_adsi_end_download(chan)) {
6410  bytes = 0;
6411  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
6412  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
6413  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6414  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6415  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6416  return 0;
6417  }
6418  bytes = 0;
6419  bytes += ast_adsi_download_disconnect(buf + bytes);
6420  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6421  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
6422 
6423  ast_debug(1, "Done downloading scripts...\n");
6424 
6425 #ifdef DISPLAY
6426  /* Add last dot */
6427  bytes = 0;
6428  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
6429  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6430 #endif
6431  ast_debug(1, "Restarting session...\n");
6432 
6433  bytes = 0;
6434  /* Load the session now */
6435  if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
6436  *useadsi = 1;
6437  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
6438  } else
6439  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
6440 
6441  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6442  return 0;
6443 }
6444 
6445 static void adsi_begin(struct ast_channel *chan, int *useadsi)
6446 {
6447  int x;
6448  if (!ast_adsi_available(chan))
6449  return;
6450  x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
6451  if (x < 0)
6452  return;
6453  if (!x) {
6454  if (adsi_load_vmail(chan, useadsi)) {
6455  ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
6456  return;
6457  }
6458  } else
6459  *useadsi = 1;
6460 }
6461 
6462 static void adsi_login(struct ast_channel *chan)
6463 {
6464  unsigned char buf[256];
6465  int bytes = 0;
6466  unsigned char keys[8];
6467  int x;
6468  if (!ast_adsi_available(chan))
6469  return;
6470 
6471  for (x = 0; x < 8; x++)
6472  keys[x] = 0;
6473  /* Set one key for next */
6474  keys[3] = ADSI_KEY_APPS + 3;
6475 
6476  bytes += adsi_logo(buf + bytes);
6477  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
6478  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
6479  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6480  bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
6481  bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
6482  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
6483  bytes += ast_adsi_set_keys(buf + bytes, keys);
6484  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6485  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6486 }
6487 
6488 static void adsi_password(struct ast_channel *chan)
6489 {
6490  unsigned char buf[256];
6491  int bytes = 0;
6492  unsigned char keys[8];
6493  int x;
6494  if (!ast_adsi_available(chan))
6495  return;
6496 
6497  for (x = 0; x < 8; x++)
6498  keys[x] = 0;
6499  /* Set one key for next */
6500  keys[3] = ADSI_KEY_APPS + 3;
6501 
6502  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6503  bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
6504  bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
6505  bytes += ast_adsi_set_keys(buf + bytes, keys);
6506  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6507  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6508 }
6509 
6510 static void adsi_folders(struct ast_channel *chan, int start, char *label)
6511 {
6512  unsigned char buf[256];
6513  int bytes = 0;
6514  unsigned char keys[8];
6515  int x, y;
6516 
6517  if (!ast_adsi_available(chan))
6518  return;
6519 
6520  for (x = 0; x < 5; x++) {
6521  y = ADSI_KEY_APPS + 12 + start + x;
6522  if (y > ADSI_KEY_APPS + 12 + 4)
6523  y = 0;
6524  keys[x] = ADSI_KEY_SKT | y;
6525  }
6526  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
6527  keys[6] = 0;
6528  keys[7] = 0;
6529 
6530  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
6531  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
6532  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6533  bytes += ast_adsi_set_keys(buf + bytes, keys);
6534  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6535 
6536  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6537 }
6538 
6539 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
6540 {
6541  int bytes = 0;
6542  unsigned char buf[256];
6543  char buf1[256], buf2[256];
6544  char fn2[PATH_MAX];
6545 
6546  char cid[256] = "";
6547  char *val;
6548  char *name, *num;
6549  char datetime[21] = "";
6550  FILE *f;
6551 
6552  unsigned char keys[8];
6553 
6554  int x;
6555 
6556  if (!ast_adsi_available(chan))
6557  return;
6558 
6559  /* Retrieve important info */
6560  snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
6561  f = fopen(fn2, "r");
6562  if (f) {
6563  while (!feof(f)) {
6564  if (!fgets((char *) buf, sizeof(buf), f)) {
6565  continue;
6566  }
6567  if (!feof(f)) {
6568  char *stringp = NULL;
6569  stringp = (char *) buf;
6570  strsep(&stringp, "=");
6571  val = strsep(&stringp, "=");
6572  if (!ast_strlen_zero(val)) {
6573  if (!strcmp((char *) buf, "callerid"))
6574  ast_copy_string(cid, val, sizeof(cid));
6575  if (!strcmp((char *) buf, "origdate"))
6576  ast_copy_string(datetime, val, sizeof(datetime));
6577  }
6578  }
6579  }
6580  fclose(f);
6581  }
6582  /* New meaning for keys */
6583  for (x = 0; x < 5; x++)
6584  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
6585  keys[6] = 0x0;
6586  keys[7] = 0x0;
6587 
6588  if (!vms->curmsg) {
6589  /* No prev key, provide "Folder" instead */
6590  keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6591  }
6592  if (vms->curmsg >= vms->lastmsg) {
6593  /* If last message ... */
6594  if (vms->curmsg) {
6595  /* but not only message, provide "Folder" instead */
6596  keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6597  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6598 
6599  } else {
6600  /* Otherwise if only message, leave blank */
6601  keys[3] = 1;
6602  }
6603  }
6604 
6605  if (!ast_strlen_zero(cid)) {
6606  ast_callerid_parse(cid, &name, &num);
6607  if (!name)
6608  name = num;
6609  } else
6610  name = "Unknown Caller";
6611 
6612  /* If deleted, show "undeleted" */
6613 #ifdef IMAP_STORAGE
6614  ast_mutex_lock(&vms->lock);
6615 #endif
6616  if (vms->deleted[vms->curmsg]) {
6617  keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
6618  }
6619 #ifdef IMAP_STORAGE
6620  ast_mutex_unlock(&vms->lock);
6621 #endif
6622 
6623  /* Except "Exit" */
6624  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
6625  snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
6626  strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
6627  snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
6628 
6629  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
6630  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
6631  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
6632  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
6633  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6634  bytes += ast_adsi_set_keys(buf + bytes, keys);
6635  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6636 
6637  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6638 }
6639 
6640 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
6641 {
6642  int bytes = 0;
6643  unsigned char buf[256];
6644  unsigned char keys[8];
6645 
6646  int x;
6647 
6648  if (!ast_adsi_available(chan))
6649  return;
6650 
6651  /* New meaning for keys */
6652  for (x = 0; x < 5; x++)
6653  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
6654 
6655  keys[6] = 0x0;
6656  keys[7] = 0x0;
6657 
6658  if (!vms->curmsg) {
6659  /* No prev key, provide "Folder" instead */
6660  keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6661  }
6662  if (vms->curmsg >= vms->lastmsg) {
6663  /* If last message ... */
6664  if (vms->curmsg) {
6665  /* but not only message, provide "Folder" instead */
6666  keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6667  } else {
6668  /* Otherwise if only message, leave blank */
6669  keys[3] = 1;
6670  }
6671  }
6672 
6673  /* If deleted, show "undeleted" */
6674 #ifdef IMAP_STORAGE
6675  ast_mutex_lock(&vms->lock);
6676 #endif
6677  if (vms->deleted[vms->curmsg]) {
6678  keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
6679  }
6680 #ifdef IMAP_STORAGE
6681  ast_mutex_unlock(&vms->lock);
6682 #endif
6683 
6684  /* Except "Exit" */
6685  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
6686  bytes += ast_adsi_set_keys(buf + bytes, keys);
6687  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6688 
6689  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6690 }
6691 
6692 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
6693 {
6694  unsigned char buf[256] = "";
6695  char buf1[256] = "", buf2[256] = "";
6696  int bytes = 0;
6697  unsigned char keys[8];
6698  int x;
6699 
6700  char *newm = (vms->newmessages == 1) ? "message" : "messages";
6701  char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
6702  if (!ast_adsi_available(chan))
6703  return;
6704  if (vms->newmessages) {
6705  snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
6706  if (vms->oldmessages) {
6707  strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
6708  snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
6709  } else {
6710  snprintf(buf2, sizeof(buf2), "%s.", newm);
6711  }
6712  } else if (vms->oldmessages) {
6713  snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
6714  snprintf(buf2, sizeof(buf2), "%s.", oldm);
6715  } else {
6716  strcpy(buf1, "You have no messages.");
6717  buf2[0] = ' ';
6718  buf2[1] = '\0';
6719  }
6720  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
6721  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
6722  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6723 
6724  for (x = 0; x < 6; x++)
6725  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
6726  keys[6] = 0;
6727  keys[7] = 0;
6728 
6729  /* Don't let them listen if there are none */
6730  if (vms->lastmsg < 0)
6731  keys[0] = 1;
6732  bytes += ast_adsi_set_keys(buf + bytes, keys);
6733 
6734  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6735 
6736  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6737 }
6738 
6739 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
6740 {
6741  unsigned char buf[256] = "";
6742  char buf1[256] = "", buf2[256] = "";
6743  int bytes = 0;
6744  unsigned char keys[8];
6745  int x;
6746 
6747  char *mess = (vms->lastmsg == 0) ? "message" : "messages";
6748 
6749  if (!ast_adsi_available(chan))
6750  return;
6751 
6752  /* Original command keys */
6753  for (x = 0; x < 6; x++)
6754  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
6755 
6756  keys[6] = 0;
6757  keys[7] = 0;
6758 
6759  if ((vms->lastmsg + 1) < 1)
6760  keys[0] = 0;
6761 
6762  snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
6763  strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
6764 
6765  if (vms->lastmsg + 1)
6766  snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
6767  else
6768  strcpy(buf2, "no messages.");
6769  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
6770  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
6771  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
6772  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6773  bytes += ast_adsi_set_keys(buf + bytes, keys);
6774 
6775  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6776 
6777  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6778 
6779 }
6780 
6781 /*
6782 static void adsi_clear(struct ast_channel *chan)
6783 {
6784  char buf[256];
6785  int bytes=0;
6786  if (!ast_adsi_available(chan))
6787  return;
6788  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6789  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6790 
6791  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6792 }
6793 */
6794 
6795 static void adsi_goodbye(struct ast_channel *chan)
6796 {
6797  unsigned char buf[256];
6798  int bytes = 0;
6799 
6800  if (!ast_adsi_available(chan))
6801  return;
6802  bytes += adsi_logo(buf + bytes);
6803  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
6804  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
6805  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6806  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6807 
6808  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6809 }
6810 
6811 /*!\brief get_folder: Folder menu
6812  * Plays "press 1 for INBOX messages" etc.
6813  * Should possibly be internationalized
6814  */
6815 static int get_folder(struct ast_channel *chan, int start)
6816 {
6817  int x;
6818  int d;
6819  char fn[PATH_MAX];
6820  d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
6821  if (d)
6822  return d;
6823  for (x = start; x < 5; x++) { /* For all folders */
6824  if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
6825  return d;
6826  d = ast_play_and_wait(chan, "vm-for"); /* "for" */
6827  if (d)
6828  return d;
6829  snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
6830 
6831  /* The inbox folder can have its name changed under certain conditions
6832  * so this checks if the sound file exists for the inbox folder name and
6833  * if it doesn't, plays the default name instead. */
6834  if (x == 0) {
6835  if (ast_fileexists(fn, NULL, NULL)) {
6836  d = vm_play_folder_name(chan, fn);
6837  } else {
6838  ast_verb(1, "failed to find %s\n", fn);
6839  d = vm_play_folder_name(chan, "vm-INBOX");
6840  }
6841  } else {
6842  ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
6843  d = vm_play_folder_name(chan, fn);
6844  }
6845 
6846  if (d)
6847  return d;
6848  d = ast_waitfordigit(chan, 500);
6849  if (d)
6850  return d;
6851  }
6852 
6853  d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
6854  if (d)
6855  return d;
6856  d = ast_waitfordigit(chan, 4000);
6857  return d;
6858 }
6859 
6860 /*!
6861  * \brief plays a prompt and waits for a keypress.
6862  * \param chan
6863  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
6864  * \param start Does not appear to be used at this time.
6865  *
6866  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
6867  * After playing the message identified by the fn parameter value, it calls get_folder(), which plays the
6868  * prompting for the number inputs that correspond to the available folders.
6869  *
6870  * \return zero on success, or -1 on error.
6871  */
6872 static int get_folder2(struct ast_channel *chan, char *fn, int start)
6873 {
6874  int res = 0;
6875  int loops = 0;
6876 
6877  res = ast_play_and_wait(chan, fn); /* Folder name */
6878  while (((res < '0') || (res > '9')) &&
6879  (res != '#') && (res >= 0) &&
6880  loops < 4) {
6881  res = get_folder(chan, 0);
6882  loops++;
6883  }
6884  if (loops == 4) { /* give up */
6885  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
6886  return '#';
6887  }
6888  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
6889  return res;
6890 }
6891 
6892 /*!
6893  * \brief presents the option to prepend to an existing message when forwarding it.
6894  * \param chan
6895  * \param vmu
6896  * \param curdir
6897  * \param curmsg
6898  * \param vm_fmts
6899  * \param context
6900  * \param record_gain
6901  * \param duration
6902  * \param vms
6903  * \param flag
6904  *
6905  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
6906  *
6907  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
6908  * \return zero on success, -1 on error.
6909  */
6910 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
6911  char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
6912 {
6913  int cmd = 0;
6914  int retries = 0, prepend_duration = 0, already_recorded = 0;
6915  char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
6916  char textfile[PATH_MAX];
6917  struct ast_config *msg_cfg;
6918  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
6919 #ifndef IMAP_STORAGE
6920  signed char zero_gain = 0;
6921 #endif
6922  const char *duration_str;
6923 
6924  /* Must always populate duration correctly */
6925  make_file(msgfile, sizeof(msgfile), curdir, curmsg);
6926  strcpy(textfile, msgfile);
6927  strcpy(backup, msgfile);
6928  strcpy(backup_textfile, msgfile);
6929  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
6930  strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
6931  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
6932 
6933  if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
6934  *duration = atoi(duration_str);
6935  } else {
6936  *duration = 0;
6937  }
6938 
6939  while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
6940  if (cmd)
6941  retries = 0;
6942  switch (cmd) {
6943  case '1':
6944 
6945 #ifdef IMAP_STORAGE
6946  /* Record new intro file */
6947  make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
6948  strncat(vms->introfn, "intro", sizeof(vms->introfn));
6949  ast_play_and_wait(chan, INTRO);
6950  ast_play_and_wait(chan, "beep");
6951  cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
6952  if (cmd == -1) {
6953  break;
6954  }
6955  cmd = 't';
6956 #else
6957 
6958  /* prepend a message to the current message, update the metadata and return */
6959 
6960  make_file(msgfile, sizeof(msgfile), curdir, curmsg);
6961  strcpy(textfile, msgfile);
6962  strncat(textfile, ".txt", sizeof(textfile) - 1);
6963  *duration = 0;
6964 
6965  /* if we can't read the message metadata, stop now */
6966  if (!valid_config(msg_cfg)) {
6967  cmd = 0;
6968  break;
6969  }
6970 
6971  /* Back up the original file, so we can retry the prepend and restore it after forward. */
6972 #ifndef IMAP_STORAGE
6973  if (already_recorded) {
6974  ast_filecopy(backup, msgfile, NULL);
6975  copy(backup_textfile, textfile);
6976  }
6977  else {
6978  ast_filecopy(msgfile, backup, NULL);
6979  copy(textfile, backup_textfile);
6980  }
6981 #endif
6982  already_recorded = 1;
6983 
6984  if (record_gain)
6985  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
6986 
6987  cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
6988 
6989  if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
6990  ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
6991  ast_stream_and_wait(chan, vm_prepend_timeout, "");
6992  ast_filerename(backup, msgfile, NULL);
6993  }
6994 
6995  if (record_gain)
6996  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
6997 
6998 
6999  if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
7000  *duration = atoi(duration_str);
7001 
7002  if (prepend_duration) {
7003  struct ast_category *msg_cat;
7004  /* need enough space for a maximum-length message duration */
7005  char duration_buf[12];
7006 
7007  *duration += prepend_duration;
7008  msg_cat = ast_category_get(msg_cfg, "message");
7009  snprintf(duration_buf, 11, "%ld", *duration);
7010  if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
7011  ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
7012  }
7013  }
7014 
7015 #endif
7016  break;
7017  case '2':
7018  /* NULL out introfile so we know there is no intro! */
7019 #ifdef IMAP_STORAGE
7020  *vms->introfn = '\0';
7021 #endif
7022  cmd = 't';
7023  break;
7024  case '*':
7025  cmd = '*';
7026  break;
7027  default:
7028  /* If time_out and return to menu, reset already_recorded */
7029  already_recorded = 0;
7030 
7031  cmd = ast_play_and_wait(chan, "vm-forwardoptions");
7032  /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
7033  if (!cmd) {
7034  cmd = ast_play_and_wait(chan, "vm-starmain");
7035  /* "press star to return to the main menu" */
7036  }
7037  if (!cmd) {
7038  cmd = ast_waitfordigit(chan, 6000);
7039  }
7040  if (!cmd) {
7041  retries++;
7042  }
7043  if (retries > 3) {
7044  cmd = '*'; /* Let's cancel this beast */
7045  }
7046  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
7047  }
7048  }
7049 
7050  if (valid_config(msg_cfg))
7051  ast_config_destroy(msg_cfg);
7052  if (prepend_duration)
7053  *duration = prepend_duration;
7054 
7055  if (already_recorded && cmd == -1) {
7056  /* restore original message if prepention cancelled */
7057  ast_filerename(backup, msgfile, NULL);
7058  rename(backup_textfile, textfile);
7059  }
7060 
7061  if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
7062  cmd = 0;
7063  return cmd;
7064 }
7065 
7066 static void queue_mwi_event(const char *box, int urgent, int new, int old)
7067 {
7068  struct ast_event *event;
7069  char *mailbox, *context;
7070 
7071  /* Strip off @default */
7072  context = mailbox = ast_strdupa(box);
7073  strsep(&context, "@");
7074  if (ast_strlen_zero(context))
7075  context = "default";
7076 
7077  if (!(event = ast_event_new(AST_EVENT_MWI,
7082  AST_EVENT_IE_END))) {
7083  return;
7084  }
7085 
7087 }
7088 
7089 /*!
7090  * \brief Sends email notification that a user has a new voicemail waiting for them.
7091  * \param chan
7092  * \param vmu
7093  * \param vms
7094  * \param msgnum
7095  * \param duration
7096  * \param fmt
7097  * \param cidnum The Caller ID phone number value.
7098  * \param cidname The Caller ID name value.
7099  * \param flag
7100  *
7101  * \return zero on success, -1 on error.
7102  */
7103 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)
7104 {
7105  char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
7106  int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
7107  const char *category;
7108  char *myserveremail = serveremail;
7109 
7110  ast_channel_lock(chan);
7111  if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
7112  category = ast_strdupa(category);
7113  }
7114  ast_channel_unlock(chan);
7115 
7116 #ifndef IMAP_STORAGE
7117  make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
7118 #else
7119  snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
7120 #endif
7121  make_file(fn, sizeof(fn), todir, msgnum);
7122  snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
7123 
7124  if (!ast_strlen_zero(vmu->attachfmt)) {
7125  if (strstr(fmt, vmu->attachfmt))
7126  fmt = vmu->attachfmt;
7127  else
7128  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);
7129  }
7130 
7131  /* Attach only the first format */
7132  fmt = ast_strdupa(fmt);
7133  stringp = fmt;
7134  strsep(&stringp, "|");
7135 
7136  if (!ast_strlen_zero(vmu->serveremail))
7137  myserveremail = vmu->serveremail;
7138 
7139  if (!ast_strlen_zero(vmu->email)) {
7140  int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
7141 
7142  if (attach_user_voicemail)
7143  RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
7144 
7145  /* XXX possible imap issue, should category be NULL XXX */
7146  sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
7147 
7148  if (attach_user_voicemail)
7149  DISPOSE(todir, msgnum);
7150  }
7151 
7152  if (!ast_strlen_zero(vmu->pager)) {
7153  sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
7154  }
7155 
7156  if (ast_test_flag(vmu, VM_DELETE))
7157  DELETE(todir, msgnum, fn, vmu);
7158 
7159  /* Leave voicemail for someone */
7160  if (ast_app_has_voicemail(ext_context, NULL))
7161  ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
7162 
7163  queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
7164 
7165  ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
7166  run_externnotify(vmu->context, vmu->mailbox, flag);
7167 
7168 #ifdef IMAP_STORAGE
7169  vm_delete(fn); /* Delete the file, but not the IMAP message */
7170  if (ast_test_flag(vmu, VM_DELETE)) { /* Delete the IMAP message if delete = yes */
7171  vm_imap_delete(NULL, vms->curmsg, vmu);
7172  vms->newmessages--; /* Fix new message count */
7173  }
7174 #endif
7175 
7176  return 0;
7177 }
7178 
7179 /*!
7180  * \brief Sends a voicemail message to a mailbox recipient.
7181  * \param chan
7182  * \param context
7183  * \param vms
7184  * \param sender
7185  * \param fmt
7186  * \param is_new_message Used to indicate the mode for which this method was invoked.
7187  * Will be 0 when called to forward an existing message (option 8)
7188  * Will be 1 when called to leave a message (option 3->5)
7189  * \param record_gain
7190  * \param urgent
7191  *
7192  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
7193  *
7194  * When in the leave message mode (is_new_message == 1):
7195  * - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
7196  * - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
7197  *
7198  * When in the forward message mode (is_new_message == 0):
7199  * - retreives the current message to be forwarded
7200  * - copies the original message to a temporary file, so updates to the envelope can be done.
7201  * - determines the target mailbox and folders
7202  * - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
7203  *
7204  * \return zero on success, -1 on error.
7205  */
7206 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)
7207 {
7208 #ifdef IMAP_STORAGE
7209  int todircount = 0;
7210  struct vm_state *dstvms;
7211 #endif
7212  char username[70]="";
7213  char fn[PATH_MAX]; /* for playback of name greeting */
7214  char ecodes[16] = "#";
7215  int res = 0, cmd = 0;
7216  struct ast_vm_user *receiver = NULL, *vmtmp;
7218  char *stringp;
7219  const char *s;
7220  int saved_messages = 0;
7221  int valid_extensions = 0;
7222  char *dir;
7223  int curmsg;
7224  char urgent_str[7] = "";
7225  int prompt_played = 0;
7226 #ifndef IMAP_STORAGE
7227  char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
7228 #endif
7229  if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
7230  ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
7231  }
7232 
7233  if (vms == NULL) return -1;
7234  dir = vms->curdir;
7235  curmsg = vms->curmsg;
7236 
7237  ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
7238  while (!res && !valid_extensions) {
7239  int use_directory = 0;
7240  if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
7241  int done = 0;
7242  int retries = 0;
7243  cmd = 0;
7244  while ((cmd >= 0) && !done ){
7245  if (cmd)
7246  retries = 0;
7247  switch (cmd) {
7248  case '1':
7249  use_directory = 0;
7250  done = 1;
7251  break;
7252  case '2':
7253  use_directory = 1;
7254  done = 1;
7255  break;
7256  case '*':
7257  cmd = 't';
7258  done = 1;
7259  break;
7260  default:
7261  /* Press 1 to enter an extension press 2 to use the directory */
7262  cmd = ast_play_and_wait(chan, "vm-forward");
7263  if (!cmd) {
7264  cmd = ast_waitfordigit(chan, 3000);
7265  }
7266  if (!cmd) {
7267  retries++;
7268  }
7269  if (retries > 3) {
7270  cmd = 't';
7271  done = 1;
7272  }
7273  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
7274  }
7275  }
7276  if (cmd < 0 || cmd == 't')
7277  break;
7278  }
7279 
7280  if (use_directory) {
7281  /* use app_directory */
7282 
7283  char old_context[sizeof(chan->context)];
7284  char old_exten[sizeof(chan->exten)];
7285  int old_priority;
7286  struct ast_app* directory_app;
7287 
7288  directory_app = pbx_findapp("Directory");
7289  if (directory_app) {
7290  char vmcontext[256];
7291  /* make backup copies */
7292  memcpy(old_context, chan->context, sizeof(chan->context));
7293  memcpy(old_exten, chan->exten, sizeof(chan->exten));
7294  old_priority = chan->priority;
7295 
7296  /* call the the Directory, changes the channel */
7297  snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
7298  res = pbx_exec(chan, directory_app, vmcontext);
7299 
7300  ast_copy_string(username, chan->exten, sizeof(username));
7301 
7302  /* restore the old context, exten, and priority */
7303  memcpy(chan->context, old_context, sizeof(chan->context));
7304  memcpy(chan->exten, old_exten, sizeof(chan->exten));
7305  chan->priority = old_priority;
7306  } else {
7307  ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
7308  ast_clear_flag((&globalflags), VM_DIRECFORWARD);
7309  }
7310  } else {
7311  /* Ask for an extension */
7312  res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
7313  prompt_played++;
7314  if (res || prompt_played > 4)
7315  break;
7316  if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
7317  break;
7318  }
7319 
7320  /* start all over if no username */
7321  if (ast_strlen_zero(username))
7322  continue;
7323  stringp = username;
7324  s = strsep(&stringp, "*");
7325  /* start optimistic */
7326  valid_extensions = 1;
7327  while (s) {
7328  if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
7329  int oldmsgs;
7330  int newmsgs;
7331  int capacity;
7332  if (inboxcount(s, &newmsgs, &oldmsgs)) {
7333  ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
7334  /* Shouldn't happen, but allow trying another extension if it does */
7335  res = ast_play_and_wait(chan, "pbx-invalid");
7336  valid_extensions = 0;
7337  break;
7338  }
7339  capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
7340  if ((newmsgs + oldmsgs) >= capacity) {
7341  ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
7342  res = ast_play_and_wait(chan, "vm-mailboxfull");
7343  valid_extensions = 0;
7344  while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
7345  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
7346  free_user(vmtmp);
7347  }
7348  inprocess_count(receiver->mailbox, receiver->context, -1);
7349  break;
7350  }
7351  AST_LIST_INSERT_HEAD(&extensions, receiver, list);
7352  } else {
7353  /* XXX Optimization for the future. When we encounter a single bad extension,
7354  * bailing out on all of the extensions may not be the way to go. We should
7355  * probably just bail on that single extension, then allow the user to enter
7356  * several more. XXX
7357  */
7358  while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
7359  free_user(receiver);
7360  }
7361  ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
7362  /* "I am sorry, that's not a valid extension. Please try again." */
7363  res = ast_play_and_wait(chan, "pbx-invalid");
7364  valid_extensions = 0;
7365  break;
7366  }
7367 
7368  /* play name if available, else play extension number */
7369  snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
7370  RETRIEVE(fn, -1, s, receiver->context);
7371  if (ast_fileexists(fn, NULL, NULL) > 0) {
7372  res = ast_stream_and_wait(chan, fn, ecodes);
7373  if (res) {
7374  DISPOSE(fn, -1);
7375  return res;
7376  }
7377  } else {
7378  res = ast_say_digit_str(chan, s, ecodes, chan->language);
7379  }
7380  DISPOSE(fn, -1);
7381 
7382  s = strsep(&stringp, "*");
7383  }
7384  /* break from the loop of reading the extensions */
7385  if (valid_extensions)
7386  break;
7387  }
7388  /* check if we're clear to proceed */
7389  if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
7390  return res;
7391  if (is_new_message == 1) {
7392  struct leave_vm_options leave_options;
7393  char mailbox[AST_MAX_EXTENSION * 2 + 2];
7394  snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
7395 
7396  /* Send VoiceMail */
7397  memset(&leave_options, 0, sizeof(leave_options));
7398  leave_options.record_gain = record_gain;
7399  cmd = leave_voicemail(chan, mailbox, &leave_options);
7400  } else {
7401  /* Forward VoiceMail */
7402  long duration = 0;
7403  struct vm_state vmstmp;
7404  int copy_msg_result = 0;
7405  memcpy(&vmstmp, vms, sizeof(vmstmp));
7406 
7407  RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
7408 
7409  cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
7410  if (!cmd) {
7411  AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
7412 #ifdef IMAP_STORAGE
7413  int attach_user_voicemail;
7414  char *myserveremail = serveremail;
7415 
7416  /* get destination mailbox */
7417  dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
7418  if (!dstvms) {
7419  dstvms = create_vm_state_from_user(vmtmp);
7420  }
7421  if (dstvms) {
7422  init_mailstream(dstvms, 0);
7423  if (!dstvms->mailstream) {
7424  ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
7425  } else {
7426  copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
7427  run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str);
7428  }
7429  } else {
7430  ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
7431  }
7432  if (!ast_strlen_zero(vmtmp->serveremail))
7433  myserveremail = vmtmp->serveremail;
7434  attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
7435  /* NULL category for IMAP storage */
7436  sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
7437  dstvms->curbox,
7438  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
7439  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
7440  vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
7441  NULL, urgent_str);
7442 #else
7443  copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
7444 #endif
7445  saved_messages++;
7447  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
7448  free_user(vmtmp);
7449  if (res)
7450  break;
7451  }
7453  if (saved_messages > 0 && !copy_msg_result) {
7454  /* give confirmation that the message was saved */
7455  /* commented out since we can't forward batches yet
7456  if (saved_messages == 1)
7457  res = ast_play_and_wait(chan, "vm-message");
7458  else
7459  res = ast_play_and_wait(chan, "vm-messages");
7460  if (!res)
7461  res = ast_play_and_wait(chan, "vm-saved"); */
7462 #ifdef IMAP_STORAGE
7463  /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
7464  if (ast_strlen_zero(vmstmp.introfn))
7465 #endif
7466  res = ast_play_and_wait(chan, "vm-msgsaved");
7467  }
7468 #ifndef IMAP_STORAGE
7469  else {
7470  /* with IMAP, mailbox full warning played by imap_check_limits */
7471  res = ast_play_and_wait(chan, "vm-mailboxfull");
7472  }
7473  /* Restore original message without prepended message if backup exists */
7474  make_file(msgfile, sizeof(msgfile), dir, curmsg);
7475  strcpy(textfile, msgfile);
7476  strcpy(backup, msgfile);
7477  strcpy(backup_textfile, msgfile);
7478  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
7479  strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
7480  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
7481  if (ast_fileexists(backup, NULL, NULL) > 0) {
7482  ast_filerename(backup, msgfile, NULL);
7483  rename(backup_textfile, textfile);
7484  }
7485 #endif
7486  }
7487  DISPOSE(dir, curmsg);
7488 #ifndef IMAP_STORAGE
7489  if (cmd) { /* assuming hangup, cleanup backup file */
7490  make_file(msgfile, sizeof(msgfile), dir, curmsg);
7491  strcpy(textfile, msgfile);
7492  strcpy(backup_textfile, msgfile);
7493  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
7494  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
7495  rename(backup_textfile, textfile);
7496  }
7497 #endif
7498  }
7499 
7500  /* If anything failed above, we still have this list to free */
7501  while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
7502  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
7503  free_user(vmtmp);
7504  }
7505  return res ? res : cmd;
7506 }
7507 
7508 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
7509 {
7510  int res;
7511  if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
7512  ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file);
7513  return res;
7514 }
7515 
7516 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
7517 {
7518  ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
7519  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);
7520 }
7521 
7522 static int play_message_category(struct ast_channel *chan, const char *category)
7523 {
7524  int res = 0;
7525 
7526  if (!ast_strlen_zero(category))
7527  res = ast_play_and_wait(chan, category);
7528 
7529  if (res) {
7530  ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
7531  res = 0;
7532  }
7533 
7534  return res;
7535 }
7536 
7537 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
7538 {
7539  int res = 0;
7540  struct vm_zone *the_zone = NULL;
7541  time_t t;
7542 
7543  if (ast_get_time_t(origtime, &t, 0, NULL)) {
7544  ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
7545  return 0;
7546  }
7547 
7548  /* Does this user have a timezone specified? */
7549  if (!ast_strlen_zero(vmu->zonetag)) {
7550  /* Find the zone in the list */
7551  struct vm_zone *z;
7552  AST_LIST_LOCK(&zones);
7553  AST_LIST_TRAVERSE(&zones, z, list) {
7554  if (!strcmp(z->name, vmu->zonetag)) {
7555  the_zone = z;
7556  break;
7557  }
7558  }
7560  }
7561 
7562 /* No internal variable parsing for now, so we'll comment it out for the time being */
7563 #if 0
7564  /* Set the DIFF_* variables */
7565  ast_localtime(&t, &time_now, NULL);
7566  tv_now = ast_tvnow();
7567  ast_localtime(&tv_now, &time_then, NULL);
7568 
7569  /* Day difference */
7570  if (time_now.tm_year == time_then.tm_year)
7571  snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
7572  else
7573  snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
7574  pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
7575 
7576  /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
7577 #endif
7578  if (the_zone) {
7579  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
7580  } else if (!strncasecmp(chan->language, "de", 2)) { /* GERMAN syntax */
7581  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
7582  } else if (!strncasecmp(chan->language, "gr", 2)) { /* GREEK syntax */
7583  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
7584  } else if (!strncasecmp(chan->language, "it", 2)) { /* ITALIAN syntax */
7585  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
7586  } else if (!strncasecmp(chan->language, "nl", 2)) { /* DUTCH syntax */
7587  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
7588  } else if (!strncasecmp(chan->language, "no", 2)) { /* NORWEGIAN syntax */
7589  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
7590  } else if (!strncasecmp(chan->language, "pl", 2)) { /* POLISH syntax */
7591  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
7592  } else if (!strncasecmp(chan->language, "pt_BR", 5)) { /* Brazillian PORTUGUESE syntax */
7593  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
7594  } else if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
7595  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
7596  } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
7597  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
7598  } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
7599  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' A 'digits/day' dB 'digits/year' Y 'digits/at' k 'hours' M 'minutes'", NULL);
7600  } else {
7601  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
7602  }
7603 #if 0
7604  pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
7605 #endif
7606  return res;
7607 }
7608 
7609 
7610 
7611 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
7612 {
7613  int res = 0;
7614  int i;
7615  char *callerid, *name;
7616  char prefile[PATH_MAX] = "";
7617 
7618 
7619  /* If voicemail cid is not enabled, or we didn't get cid or context from
7620  * the attribute file, leave now.
7621  *
7622  * TODO Still need to change this so that if this function is called by the
7623  * message envelope (and someone is explicitly requesting to hear the CID),
7624  * it does not check to see if CID is enabled in the config file.
7625  */
7626  if ((cid == NULL)||(context == NULL))
7627  return res;
7628 
7629  /* Strip off caller ID number from name */
7630  ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
7631  ast_callerid_parse(cid, &name, &callerid);
7632  if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
7633  /* Check for internal contexts and only */
7634  /* say extension when the call didn't come from an internal context in the list */
7635  for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
7636  ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
7637  if ((strcmp(cidinternalcontexts[i], context) == 0))
7638  break;
7639  }
7640  if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
7641  if (!res) {
7642  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
7643  if (!ast_strlen_zero(prefile)) {
7644  /* See if we can find a recorded name for this person instead of their extension number */
7645  if (ast_fileexists(prefile, NULL, NULL) > 0) {
7646  ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
7647  if (!callback)
7648  res = wait_file2(chan, vms, "vm-from");
7649  res = ast_stream_and_wait(chan, prefile, "");
7650  } else {
7651  ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
7652  /* Say "from extension" as one saying to sound smoother */
7653  if (!callback)
7654  res = wait_file2(chan, vms, "vm-from-extension");
7655  res = ast_say_digit_str(chan, callerid, "", chan->language);
7656  }
7657  }
7658  }
7659  } else if (!res) {
7660  ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
7661  /* Since this is all nicely figured out, why not say "from phone number" in this case? */
7662  if (!callback)
7663  res = wait_file2(chan, vms, "vm-from-phonenumber");
7664  res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
7665  }
7666  } else {
7667  /* Number unknown */
7668  ast_debug(1, "VM-CID: From an unknown number\n");
7669  /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
7670  res = wait_file2(chan, vms, "vm-unknown-caller");
7671  }
7672  return res;
7673 }
7674 
7675 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
7676 {
7677  int res = 0;
7678  int durationm;
7679  int durations;
7680  /* Verify that we have a duration for the message */
7681  if (duration == NULL)
7682  return res;
7683 
7684  /* Convert from seconds to minutes */
7685  durations = atoi(duration);
7686  durationm = (durations / 60);
7687 
7688  ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
7689 
7690  if ((!res) && (durationm >= minduration)) {
7691  res = wait_file2(chan, vms, "vm-duration");
7692 
7693  /* POLISH syntax */
7694  if (!strncasecmp(chan->language, "pl", 2)) {
7695  div_t num = div(durationm, 10);
7696 
7697  if (durationm == 1) {
7698  res = ast_play_and_wait(chan, "digits/1z");
7699  res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
7700  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
7701  if (num.rem == 2) {
7702  if (!num.quot) {
7703  res = ast_play_and_wait(chan, "digits/2-ie");
7704  } else {
7705  res = say_and_wait(chan, durationm - 2 , chan->language);
7706  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
7707  }
7708  } else {
7709  res = say_and_wait(chan, durationm, chan->language);
7710  }
7711  res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
7712  } else {
7713  res = say_and_wait(chan, durationm, chan->language);
7714  res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
7715  }
7716  /* DEFAULT syntax */
7717  } else {
7718  res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
7719  res = wait_file2(chan, vms, "vm-minutes");
7720  }
7721  }
7722  return res;
7723 }
7724 
7725 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
7726 {
7727  int res = 0;
7728  char filename[256], *cid;
7729  const char *origtime, *context, *category, *duration, *flag;
7730  struct ast_config *msg_cfg;
7731  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
7732 
7733  vms->starting = 0;
7734  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
7735  adsi_message(chan, vms);
7736  if (!vms->curmsg) {
7737  res = wait_file2(chan, vms, "vm-first"); /* "First" */
7738  } else if (vms->curmsg == vms->lastmsg) {
7739  res = wait_file2(chan, vms, "vm-last"); /* "last" */
7740  }
7741 
7742  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
7743  RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
7744  msg_cfg = ast_config_load(filename, config_flags);
7745  if (!valid_config(msg_cfg)) {
7746  ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
7747  return 0;
7748  }
7749  flag = ast_variable_retrieve(msg_cfg, "message", "flag");
7750 
7751  /* Play the word urgent if we are listening to urgent messages */
7752  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
7753  res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
7754  }
7755 
7756  if (!res) {
7757  /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
7758  /* POLISH syntax */
7759  if (!strncasecmp(chan->language, "pl", 2)) {
7760  if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
7761  int ten, one;
7762  char nextmsg[256];
7763  ten = (vms->curmsg + 1) / 10;
7764  one = (vms->curmsg + 1) % 10;
7765 
7766  if (vms->curmsg < 20) {
7767  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
7768  res = wait_file2(chan, vms, nextmsg);
7769  } else {
7770  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
7771  res = wait_file2(chan, vms, nextmsg);
7772  if (one > 0) {
7773  if (!res) {
7774  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
7775  res = wait_file2(chan, vms, nextmsg);
7776  }
7777  }
7778  }
7779  }
7780  if (!res)
7781  res = wait_file2(chan, vms, "vm-message");
7782  /* HEBREW syntax */
7783  } else if (!strncasecmp(chan->language, "he", 2)) {
7784  if (!vms->curmsg) {
7785  res = wait_file2(chan, vms, "vm-message");
7786  res = wait_file2(chan, vms, "vm-first");
7787  } else if (vms->curmsg == vms->lastmsg) {
7788  res = wait_file2(chan, vms, "vm-message");
7789  res = wait_file2(chan, vms, "vm-last");
7790  } else {
7791  res = wait_file2(chan, vms, "vm-message");
7792  res = wait_file2(chan, vms, "vm-number");
7793  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
7794  }
7795  /* VIETNAMESE syntax */
7796  } else if (!strncasecmp(chan->language, "vi", 2)) {
7797  if (!vms->curmsg) {
7798  res = wait_file2(chan, vms, "vm-message");
7799  res = wait_file2(chan, vms, "vm-first");
7800  } else if (vms->curmsg == vms->lastmsg) {
7801  res = wait_file2(chan, vms, "vm-message");
7802  res = wait_file2(chan, vms, "vm-last");
7803  } else {
7804  res = wait_file2(chan, vms, "vm-message");
7805  res = wait_file2(chan, vms, "vm-number");
7806  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
7807  }
7808  } else {
7809  if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
7810  res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
7811  } else { /* DEFAULT syntax */
7812  res = wait_file2(chan, vms, "vm-message");
7813  }
7814  if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
7815  if (!res) {
7816  ast_test_suite_event_notify("PLAYBACK", "Message: message number");
7817  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
7818  }
7819  }
7820  }
7821  }
7822 
7823  if (!valid_config(msg_cfg)) {
7824  ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
7825  return 0;
7826  }
7827 
7828  if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
7829  ast_log(AST_LOG_WARNING, "No origtime?!\n");
7830  DISPOSE(vms->curdir, vms->curmsg);
7831  ast_config_destroy(msg_cfg);
7832  return 0;
7833  }
7834 
7835  cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
7836  duration = ast_variable_retrieve(msg_cfg, "message", "duration");
7837  category = ast_variable_retrieve(msg_cfg, "message", "category");
7838 
7839  context = ast_variable_retrieve(msg_cfg, "message", "context");
7840  if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
7841  context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
7842  if (!res) {
7843  res = play_message_category(chan, category);
7844  }
7845  if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
7846  res = play_message_datetime(chan, vmu, origtime, filename);
7847  }
7848  if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
7849  res = play_message_callerid(chan, vms, cid, context, 0);
7850  }
7851  if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
7852  res = play_message_duration(chan, vms, duration, vmu->saydurationm);
7853  }
7854  /* Allow pressing '1' to skip envelope / callerid */
7855  if (res == '1') {
7856  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
7857  res = 0;
7858  }
7859  ast_config_destroy(msg_cfg);
7860 
7861  if (!res) {
7862  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
7863 #ifdef IMAP_STORAGE
7864  ast_mutex_lock(&vms->lock);
7865 #endif
7866  vms->heard[vms->curmsg] = 1;
7867 #ifdef IMAP_STORAGE
7868  ast_mutex_unlock(&vms->lock);
7869  /*IMAP storage stores any prepended message from a forward
7870  * as a separate file from the rest of the message
7871  */
7872  if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
7873  wait_file(chan, vms, vms->introfn);
7874  }
7875 #endif
7876  if ((res = wait_file(chan, vms, vms->fn)) < 0) {
7877  ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
7878  res = 0;
7879  }
7880  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
7881  }
7882  DISPOSE(vms->curdir, vms->curmsg);
7883  return res;
7884 }
7885 
7886 #ifdef IMAP_STORAGE
7887 static int imap_remove_file(char *dir, int msgnum)
7888 {
7889  char fn[PATH_MAX];
7890  char full_fn[PATH_MAX];
7891  char intro[PATH_MAX] = {0,};
7892 
7893  if (msgnum > -1) {
7894  make_file(fn, sizeof(fn), dir, msgnum);
7895  snprintf(intro, sizeof(intro), "%sintro", fn);
7896  } else
7897  ast_copy_string(fn, dir, sizeof(fn));
7898 
7899  if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
7900  ast_filedelete(fn, NULL);
7901  if (!ast_strlen_zero(intro)) {
7902  ast_filedelete(intro, NULL);
7903  }
7904  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
7905  unlink(full_fn);
7906  }
7907  return 0;
7908 }
7909 
7910 
7911 
7912 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
7913 {
7914  char *file, *filename;
7915  char *attachment;
7916  char arg[10];
7917  int i;
7918  BODY* body;
7919 
7920  file = strrchr(ast_strdupa(dir), '/');
7921  if (file) {
7922  *file++ = '\0';
7923  } else {
7924  ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
7925  return -1;
7926  }
7927 
7928  ast_mutex_lock(&vms->lock);
7929  for (i = 0; i < vms->mailstream->nmsgs; i++) {
7930  mail_fetchstructure(vms->mailstream, i + 1, &body);
7931  /* We have the body, now we extract the file name of the first attachment. */
7932  if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
7933  attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
7934  } else {
7935  ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
7936  ast_mutex_unlock(&vms->lock);
7937  return -1;
7938  }
7939  filename = strsep(&attachment, ".");
7940  if (!strcmp(filename, file)) {
7941  sprintf(arg, "%d", i + 1);
7942  mail_setflag(vms->mailstream, arg, "\\DELETED");
7943  }
7944  }
7945  mail_expunge(vms->mailstream);
7946  ast_mutex_unlock(&vms->lock);
7947  return 0;
7948 }
7949 
7950 #elif !defined(IMAP_STORAGE)
7951 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
7952 {
7953  int count_msg, last_msg;
7954 
7955  ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
7956 
7957  /* Rename the member vmbox HERE so that we don't try to return before
7958  * we know what's going on.
7959  */
7960  snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
7961 
7962  /* Faster to make the directory than to check if it exists. */
7963  create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
7964 
7965  /* traverses directory using readdir (or select query for ODBC) */
7966  count_msg = count_messages(vmu, vms->curdir);
7967  if (count_msg < 0) {
7968  return count_msg;
7969  } else {
7970  vms->lastmsg = count_msg - 1;
7971  }
7972 
7973  if (vm_allocate_dh(vms, vmu, count_msg)) {
7974  return -1;
7975  }
7976 
7977  /*
7978  The following test is needed in case sequencing gets messed up.
7979  There appears to be more than one way to mess up sequence, so
7980  we will not try to find all of the root causes--just fix it when
7981  detected.
7982  */
7983 
7984  if (vm_lock_path(vms->curdir)) {
7985  ast_log(AST_LOG_ERROR, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
7986  return ERROR_LOCK_PATH;
7987  }
7988 
7989  /* for local storage, checks directory for messages up to maxmsg limit */
7990  last_msg = last_message_index(vmu, vms->curdir);
7991  ast_unlock_path(vms->curdir);
7992 
7993  if (last_msg < -1) {
7994  return last_msg;
7995  } else if (vms->lastmsg != last_msg) {
7996  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);
7997  resequence_mailbox(vmu, vms->curdir, count_msg);
7998  }
7999 
8000  return 0;
8001 }
8002 #endif
8003 
8004 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
8005 {
8006  int x = 0;
8007  int last_msg_idx = 0;
8008 
8009 #ifndef IMAP_STORAGE
8010  int res = 0, nummsg;
8011  char fn2[PATH_MAX];
8012 #endif
8013 
8014  if (vms->lastmsg <= -1) {
8015  goto done;
8016  }
8017 
8018  vms->curmsg = -1;
8019 #ifndef IMAP_STORAGE
8020  /* Get the deleted messages fixed */
8021  if (vm_lock_path(vms->curdir)) {
8022  return ERROR_LOCK_PATH;
8023  }
8024 
8025  /* update count as message may have arrived while we've got mailbox open */
8026  last_msg_idx = last_message_index(vmu, vms->curdir);
8027  if (last_msg_idx != vms->lastmsg) {
8028  ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
8029  }
8030 
8031  /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
8032  for (x = 0; x < last_msg_idx + 1; x++) {
8033  if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
8034  /* Save this message. It's not in INBOX or hasn't been heard */
8035  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
8036  if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
8037  break;
8038  }
8039  vms->curmsg++;
8040  make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
8041  if (strcmp(vms->fn, fn2)) {
8042  RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
8043  }
8044  } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
8045  /* Move to old folder before deleting */
8046  res = save_to_folder(vmu, vms, x, 1);
8047  if (res == ERROR_LOCK_PATH) {
8048  /* If save failed do not delete the message */
8049  ast_log(AST_LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
8050  vms->deleted[x] = 0;
8051  vms->heard[x] = 0;
8052  --x;
8053  }
8054  } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
8055  /* Move to deleted folder */
8056  res = save_to_folder(vmu, vms, x, 10);
8057  if (res == ERROR_LOCK_PATH) {
8058  /* If save failed do not delete the message */
8059  vms->deleted[x] = 0;
8060  vms->heard[x] = 0;
8061  --x;
8062  }
8063  } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
8064  /* If realtime storage enabled - we should explicitly delete this message,
8065  cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
8066  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
8067  if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
8068  DELETE(vms->curdir, x, vms->fn, vmu);
8069  }
8070  }
8071  }
8072 
8073  /* Delete ALL remaining messages */
8074  nummsg = x - 1;
8075  for (x = vms->curmsg + 1; x <= nummsg; x++) {
8076  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
8077  if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
8078  DELETE(vms->curdir, x, vms->fn, vmu);
8079  }
8080  }
8081  ast_unlock_path(vms->curdir);
8082 #else /* defined(IMAP_STORAGE) */
8083  ast_mutex_lock(&vms->lock);
8084  if (vms->deleted) {
8085  /* Since we now expunge after each delete, deleting in reverse order
8086  * ensures that no reordering occurs between each step. */
8087  last_msg_idx = vms->dh_arraysize;
8088  for (x = last_msg_idx - 1; x >= 0; x--) {
8089  if (vms->deleted[x]) {
8090  ast_debug(3, "IMAP delete of %d\n", x);
8091  DELETE(vms->curdir, x, vms->fn, vmu);
8092  }
8093  }
8094  }
8095 #endif
8096 
8097 done:
8098  if (vms->deleted) {
8099  ast_free(vms->deleted);
8100  vms->deleted = NULL;
8101  }
8102  if (vms->heard) {
8103  ast_free(vms->heard);
8104  vms->heard = NULL;
8105  }
8106  vms->dh_arraysize = 0;
8107 #ifdef IMAP_STORAGE
8108  ast_mutex_unlock(&vms->lock);
8109 #endif
8110 
8111  return 0;
8112 }
8113 
8114 /* In Greek even though we CAN use a syntax like "friends messages"
8115  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
8116  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
8117  * syntax for the above three categories which is more elegant.
8118  */
8119 
8120 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
8121 {
8122  int cmd;
8123  char *buf;
8124 
8125  buf = ast_alloca(strlen(box) + 2);
8126  strcpy(buf, box);
8127  strcat(buf, "s");
8128 
8129  if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
8130  cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
8131  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
8132  } else {
8133  cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
8134  return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
8135  }
8136 }
8137 
8138 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
8139 {
8140  int cmd;
8141 
8142  if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
8143  if (!strcasecmp(box, "vm-INBOX"))
8144  cmd = ast_play_and_wait(chan, "vm-new-e");
8145  else
8146  cmd = ast_play_and_wait(chan, "vm-old-e");
8147  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
8148  } else {
8149  cmd = ast_play_and_wait(chan, "vm-messages");
8150  return cmd ? cmd : ast_play_and_wait(chan, box);
8151  }
8152 }
8153 
8154 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
8155 {
8156  int cmd;
8157 
8158  if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
8159  cmd = ast_play_and_wait(chan, "vm-messages");
8160  return cmd ? cmd : ast_play_and_wait(chan, box);
8161  } else {
8162  cmd = ast_play_and_wait(chan, box);
8163  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
8164  }
8165 }
8166 
8167 static int vm_play_folder_name(struct ast_channel *chan, char *box)
8168 {
8169  int cmd;
8170 
8171  if ( !strncasecmp(chan->language, "it", 2) ||
8172  !strncasecmp(chan->language, "es", 2) ||
8173  !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
8174  cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
8175  return cmd ? cmd : ast_play_and_wait(chan, box);
8176  } else if (!strncasecmp(chan->language, "gr", 2)) {
8177  return vm_play_folder_name_gr(chan, box);
8178  } else if (!strncasecmp(chan->language, "he", 2)) { /* Hebrew syntax */
8179  return ast_play_and_wait(chan, box);
8180  } else if (!strncasecmp(chan->language, "pl", 2)) {
8181  return vm_play_folder_name_pl(chan, box);
8182  } else if (!strncasecmp(chan->language, "ua", 2)) { /* Ukrainian syntax */
8183  return vm_play_folder_name_ua(chan, box);
8184  } else if (!strncasecmp(chan->language, "vi", 2)) {
8185  return ast_play_and_wait(chan, box);
8186  } else { /* Default English */
8187  cmd = ast_play_and_wait(chan, box);
8188  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
8189  }
8190 }
8191 
8192 /* GREEK SYNTAX
8193  In greek the plural for old/new is
8194  different so we need the following files
8195  We also need vm-denExeteMynhmata because
8196  this syntax is different.
8197 
8198  -> vm-Olds.wav : "Palia"
8199  -> vm-INBOXs.wav : "Nea"
8200  -> vm-denExeteMynhmata : "den exete mynhmata"
8201 */
8202 
8203 
8204 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
8205 {
8206  int res = 0;
8207 
8208  if (vms->newmessages) {
8209  res = ast_play_and_wait(chan, "vm-youhave");
8210  if (!res)
8211  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
8212  if (!res) {
8213  if ((vms->newmessages == 1)) {
8214  res = ast_play_and_wait(chan, "vm-INBOX");
8215  if (!res)
8216  res = ast_play_and_wait(chan, "vm-message");
8217  } else {
8218  res = ast_play_and_wait(chan, "vm-INBOXs");
8219  if (!res)
8220  res = ast_play_and_wait(chan, "vm-messages");
8221  }
8222  }
8223  } else if (vms->oldmessages){
8224  res = ast_play_and_wait(chan, "vm-youhave");
8225  if (!res)
8226  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
8227  if ((vms->oldmessages == 1)){
8228  res = ast_play_and_wait(chan, "vm-Old");
8229  if (!res)
8230  res = ast_play_and_wait(chan, "vm-message");
8231  } else {
8232  res = ast_play_and_wait(chan, "vm-Olds");
8233  if (!res)
8234  res = ast_play_and_wait(chan, "vm-messages");
8235  }
8236  } else if (!vms->oldmessages && !vms->newmessages)
8237  res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
8238  return res;
8239 }
8240 
8241 /* Version of vm_intro() designed to work for many languages.
8242  *
8243  * It is hoped that this function can prevent the proliferation of
8244  * language-specific vm_intro() functions and in time replace the language-
8245  * specific functions which already exist. An examination of the language-
8246  * specific functions revealed that they all corrected the same deficiencies
8247  * in vm_intro_en() (which was the default function). Namely:
8248  *
8249  * 1) The vm-Old and vm-INBOX sound files were overloaded. The English
8250  * wording of the voicemail greeting hides this problem. For example,
8251  * vm-INBOX contains only the word "new". This means that both of these
8252  * sequences produce valid utterances:
8253  * * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
8254  * * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
8255  * However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
8256  * in many languages) the first utterance becomes "you have 1 the new message".
8257  * 2) The function contains hardcoded rules for pluralizing the word "message".
8258  * These rules are correct for English, but not for many other languages.
8259  * 3) No attempt is made to pluralize the adjectives ("old" and "new") as
8260  * required in many languages.
8261  * 4) The gender of the word for "message" is not specified. This is a problem
8262  * because in many languages the gender of the number in phrases such
8263  * as "you have one new message" must match the gender of the word
8264  * meaning "message".
8265  *
8266  * Fixing these problems for each new language has meant duplication of effort.
8267  * This new function solves the problems in the following general ways:
8268  * 1) Add new sound files vm-new and vm-old. These can be linked to vm-INBOX
8269  * and vm-Old respectively for those languages where it makes sense.
8270  * 2) Call ast_say_counted_noun() to put the proper gender and number prefix
8271  * on vm-message.
8272  * 3) Call ast_say_counted_adjective() to put the proper gender and number
8273  * prefix on vm-new and vm-old (none for English).
8274  * 4) Pass the gender of the language's word for "message" as an agument to
8275  * this function which is can in turn pass on to the functions which
8276  * say numbers and put endings on nounds and adjectives.
8277  *
8278  * All languages require these messages:
8279  * vm-youhave "You have..."
8280  * vm-and "and"
8281  * vm-no "no" (in the sense of "none", as in "you have no messages")
8282  *
8283  * To use it for English, you will need these additional sound files:
8284  * vm-new "new"
8285  * vm-message "message", singular
8286  * vm-messages "messages", plural
8287  *
8288  * If you use it for Russian and other slavic languages, you will need these additional sound files:
8289  *
8290  * vm-newn "novoye" (singular, neuter)
8291  * vm-newx "novikh" (counting plural form, genative plural)
8292  * vm-message "sobsheniye" (singular form)
8293  * vm-messagex1 "sobsheniya" (first counting plural form, genative singular)
8294  * vm-messagex2 "sobsheniy" (second counting plural form, genative plural)
8295  * digits/1n "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
8296  * digits/2n "dva" (neuter singular)
8297  */
8298 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
8299 {
8300  int res;
8301  int lastnum = 0;
8302 
8303  res = ast_play_and_wait(chan, "vm-youhave");
8304 
8305  if (!res && vms->newmessages) {
8306  lastnum = vms->newmessages;
8307 
8308  if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
8309  res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
8310  }
8311 
8312  if (!res && vms->oldmessages) {
8313  res = ast_play_and_wait(chan, "vm-and");
8314  }
8315  }
8316 
8317  if (!res && vms->oldmessages) {
8318  lastnum = vms->oldmessages;
8319 
8320  if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
8321  res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
8322  }
8323  }
8324 
8325  if (!res) {
8326  if (lastnum == 0) {
8327  res = ast_play_and_wait(chan, "vm-no");
8328  }
8329  if (!res) {
8330  res = ast_say_counted_noun(chan, lastnum, "vm-message");
8331  }
8332  }
8333 
8334  return res;
8335 }
8336 
8337 /* Default Hebrew syntax */
8338 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
8339 {
8340  int res = 0;
8341 
8342  /* Introduce messages they have */
8343  if (!res) {
8344  if ((vms->newmessages) || (vms->oldmessages)) {
8345  res = ast_play_and_wait(chan, "vm-youhave");
8346  }
8347  /*
8348  * The word "shtei" refers to the number 2 in hebrew when performing a count
8349  * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
8350  * an element, this is one of them.
8351  */
8352  if (vms->newmessages) {
8353  if (!res) {
8354  if (vms->newmessages == 1) {
8355  res = ast_play_and_wait(chan, "vm-INBOX1");
8356  } else {
8357  if (vms->newmessages == 2) {
8358  res = ast_play_and_wait(chan, "vm-shtei");
8359  } else {
8360  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
8361  }
8362  res = ast_play_and_wait(chan, "vm-INBOX");
8363  }
8364  }
8365  if (vms->oldmessages && !res) {
8366  res = ast_play_and_wait(chan, "vm-and");
8367  if (vms->oldmessages == 1) {
8368  res = ast_play_and_wait(chan, "vm-Old1");
8369  } else {
8370  if (vms->oldmessages == 2) {
8371  res = ast_play_and_wait(chan, "vm-shtei");
8372  } else {
8373  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
8374  }
8375  res = ast_play_and_wait(chan, "vm-Old");
8376  }
8377  }
8378  }
8379  if (!res && vms->oldmessages && !vms->newmessages) {
8380  if (!res) {
8381  if (vms->oldmessages == 1) {
8382  res = ast_play_and_wait(chan, "vm-Old1");
8383  } else {
8384  if (vms->oldmessages == 2) {
8385  res = ast_play_and_wait(chan, "vm-shtei");
8386  } else {
8387  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
8388  }
8389  res = ast_play_and_wait(chan, "vm-Old");
8390  }
8391  }
8392  }
8393  if (!res) {
8394  if (!vms->oldmessages && !vms->newmessages) {
8395  if (!res) {
8396  res = ast_play_and_wait(chan, "vm-nomessages");
8397  }
8398  }
8399  }
8400  }
8401  return res;
8402 }
8403 
8404 /* Default English syntax */
8405 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
8406 {
8407  int res;
8408 
8409  /* Introduce messages they have */
8410  res = ast_play_and_wait(chan, "vm-youhave");
8411  if (!res) {
8412  if (vms->urgentmessages) {
8413  res = say_and_wait(chan, vms->urgentmessages, chan->language);
8414  if (!res)
8415  res = ast_play_and_wait(chan, "vm-Urgent");
8416  if ((vms->oldmessages || vms->newmessages) && !res) {
8417  res = ast_play_and_wait(chan, "vm-and");
8418  } else if (!res) {
8419  if ((vms->urgentmessages == 1))
8420  res = ast_play_and_wait(chan, "vm-message");
8421  else
8422  res = ast_play_and_wait(chan, "vm-messages");
8423  }
8424  }
8425  if (vms->newmessages) {
8426  res = say_and_wait(chan, vms->newmessages, chan->language);
8427  if (!res)
8428  res = ast_play_and_wait(chan, "vm-INBOX");
8429  if (vms->oldmessages && !res)
8430  res = ast_play_and_wait(chan, "vm-and");
8431  else if (!res) {
8432  if ((vms->newmessages == 1))
8433  res = ast_play_and_wait(chan, "vm-message");
8434  else
8435  res = ast_play_and_wait(chan, "vm-messages");
8436  }
8437 
8438  }
8439  if (!res && vms->oldmessages) {
8440  res = say_and_wait(chan, vms->oldmessages, chan->language);
8441  if (!res)
8442  res = ast_play_and_wait(chan, "vm-Old");
8443  if (!res) {
8444  if (vms->oldmessages == 1)
8445  res = ast_play_and_wait(chan, "vm-message");
8446  else
8447  res = ast_play_and_wait(chan, "vm-messages");
8448  }
8449  }
8450  if (!res) {
8451  if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
8452  res = ast_play_and_wait(chan, "vm-no");
8453  if (!res)
8454  res = ast_play_and_wait(chan, "vm-messages");
8455  }
8456  }
8457  }
8458  return res;
8459 }
8460 
8461 /* ITALIAN syntax */
8462 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
8463 {
8464  /* Introduce messages they have */
8465  int res;
8466  if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
8467  res = ast_play_and_wait(chan, "vm-no") ||
8468  ast_play_and_wait(chan, "vm-message");
8469  else
8470  res = ast_play_and_wait(chan, "vm-youhave");
8471  if (!res && vms->newmessages) {
8472  res = (vms->newmessages == 1) ?
8473  ast_play_and_wait(chan, "digits/un") ||
8474  ast_play_and_wait(chan, "vm-nuovo") ||
8475  ast_play_and_wait(chan, "vm-message") :
8476  /* 2 or more new messages */
8477  say_and_wait(chan, vms->newmessages, chan->language) ||
8478  ast_play_and_wait(chan, "vm-nuovi") ||
8479  ast_play_and_wait(chan, "vm-messages");
8480  if (!res && vms->oldmessages)
8481  res = ast_play_and_wait(chan, "vm-and");
8482  }
8483  if (!res && vms->oldmessages) {
8484  res = (vms->oldmessages == 1) ?
8485  ast_play_and_wait(chan, "digits/un") ||
8486  ast_play_and_wait(chan, "vm-vecchio") ||
8487  ast_play_and_wait(chan, "vm-message") :
8488  /* 2 or more old messages */
8489  say_and_wait(chan, vms->oldmessages, chan->language) ||
8490  ast_play_and_wait(chan, "vm-vecchi") ||
8491  ast_play_and_wait(chan, "vm-messages");
8492  }
8493  return res;
8494 }
8495 
8496 /* POLISH syntax */
8497 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
8498 {
8499  /* Introduce messages they have */
8500  int res;
8501  div_t num;
8502 
8503  if (!vms->oldmessages && !vms->newmessages) {
8504  res = ast_play_and_wait(chan, "vm-no");
8505  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8506  return res;
8507  } else {
8508  res = ast_play_and_wait(chan, "vm-youhave");
8509  }
8510 
8511  if (vms->newmessages) {
8512  num = div(vms->newmessages, 10);
8513  if (vms->newmessages == 1) {
8514  res = ast_play_and_wait(chan, "digits/1-a");
8515  res = res ? res : ast_play_and_wait(chan, "vm-new-a");
8516  res = res ? res : ast_play_and_wait(chan, "vm-message");
8517  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
8518  if (num.rem == 2) {
8519  if (!num.quot) {
8520  res = ast_play_and_wait(chan, "digits/2-ie");
8521  } else {
8522  res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
8523  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
8524  }
8525  } else {
8526  res = say_and_wait(chan, vms->newmessages, chan->language);
8527  }
8528  res = res ? res : ast_play_and_wait(chan, "vm-new-e");
8529  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8530  } else {
8531  res = say_and_wait(chan, vms->newmessages, chan->language);
8532  res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
8533  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8534  }
8535  if (!res && vms->oldmessages)
8536  res = ast_play_and_wait(chan, "vm-and");
8537  }
8538  if (!res && vms->oldmessages) {
8539  num = div(vms->oldmessages, 10);
8540  if (vms->oldmessages == 1) {
8541  res = ast_play_and_wait(chan, "digits/1-a");
8542  res = res ? res : ast_play_and_wait(chan, "vm-old-a");
8543  res = res ? res : ast_play_and_wait(chan, "vm-message");
8544  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
8545  if (num.rem == 2) {
8546  if (!num.quot) {
8547  res = ast_play_and_wait(chan, "digits/2-ie");
8548  } else {
8549  res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
8550  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
8551  }
8552  } else {
8553  res = say_and_wait(chan, vms->oldmessages, chan->language);
8554  }
8555  res = res ? res : ast_play_and_wait(chan, "vm-old-e");
8556  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8557  } else {
8558  res = say_and_wait(chan, vms->oldmessages, chan->language);
8559  res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
8560  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8561  }
8562  }
8563 
8564  return res;
8565 }
8566 
8567 /* SWEDISH syntax */
8568 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
8569 {
8570  /* Introduce messages they have */
8571  int res;
8572 
8573  res = ast_play_and_wait(chan, "vm-youhave");
8574  if (res)
8575  return res;
8576 
8577  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8578  res = ast_play_and_wait(chan, "vm-no");
8579  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8580  return res;
8581  }
8582 
8583  if (vms->newmessages) {
8584  if ((vms->newmessages == 1)) {
8585  res = ast_play_and_wait(chan, "digits/ett");
8586  res = res ? res : ast_play_and_wait(chan, "vm-nytt");
8587  res = res ? res : ast_play_and_wait(chan, "vm-message");
8588  } else {
8589  res = say_and_wait(chan, vms->newmessages, chan->language);
8590  res = res ? res : ast_play_and_wait(chan, "vm-nya");
8591  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8592  }
8593  if (!res && vms->oldmessages)
8594  res = ast_play_and_wait(chan, "vm-and");
8595  }
8596  if (!res && vms->oldmessages) {
8597  if (vms->oldmessages == 1) {
8598  res = ast_play_and_wait(chan, "digits/ett");
8599  res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
8600  res = res ? res : ast_play_and_wait(chan, "vm-message");
8601  } else {
8602  res = say_and_wait(chan, vms->oldmessages, chan->language);
8603  res = res ? res : ast_play_and_wait(chan, "vm-gamla");
8604  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8605  }
8606  }
8607 
8608  return res;
8609 }
8610 
8611 /* NORWEGIAN syntax */
8612 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
8613 {
8614  /* Introduce messages they have */
8615  int res;
8616 
8617  res = ast_play_and_wait(chan, "vm-youhave");
8618  if (res)
8619  return res;
8620 
8621  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8622  res = ast_play_and_wait(chan, "vm-no");
8623  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8624  return res;
8625  }
8626 
8627  if (vms->newmessages) {
8628  if ((vms->newmessages == 1)) {
8629  res = ast_play_and_wait(chan, "digits/1");
8630  res = res ? res : ast_play_and_wait(chan, "vm-ny");
8631  res = res ? res : ast_play_and_wait(chan, "vm-message");
8632  } else {
8633  res = say_and_wait(chan, vms->newmessages, chan->language);
8634  res = res ? res : ast_play_and_wait(chan, "vm-nye");
8635  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8636  }
8637  if (!res && vms->oldmessages)
8638  res = ast_play_and_wait(chan, "vm-and");
8639  }
8640  if (!res && vms->oldmessages) {
8641  if (vms->oldmessages == 1) {
8642  res = ast_play_and_wait(chan, "digits/1");
8643  res = res ? res : ast_play_and_wait(chan, "vm-gamel");
8644  res = res ? res : ast_play_and_wait(chan, "vm-message");
8645  } else {
8646  res = say_and_wait(chan, vms->oldmessages, chan->language);
8647  res = res ? res : ast_play_and_wait(chan, "vm-gamle");
8648  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8649  }
8650  }
8651 
8652  return res;
8653 }
8654 
8655 /* GERMAN syntax */
8656 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
8657 {
8658  /* Introduce messages they have */
8659  int res;
8660  res = ast_play_and_wait(chan, "vm-youhave");
8661  if (!res) {
8662  if (vms->newmessages) {
8663  if ((vms->newmessages == 1))
8664  res = ast_play_and_wait(chan, "digits/1F");
8665  else
8666  res = say_and_wait(chan, vms->newmessages, chan->language);
8667  if (!res)
8668  res = ast_play_and_wait(chan, "vm-INBOX");
8669  if (vms->oldmessages && !res)
8670  res = ast_play_and_wait(chan, "vm-and");
8671  else if (!res) {
8672  if ((vms->newmessages == 1))
8673  res = ast_play_and_wait(chan, "vm-message");
8674  else
8675  res = ast_play_and_wait(chan, "vm-messages");
8676  }
8677 
8678  }
8679  if (!res && vms->oldmessages) {
8680  if (vms->oldmessages == 1)
8681  res = ast_play_and_wait(chan, "digits/1F");
8682  else
8683  res = say_and_wait(chan, vms->oldmessages, chan->language);
8684  if (!res)
8685  res = ast_play_and_wait(chan, "vm-Old");
8686  if (!res) {
8687  if (vms->oldmessages == 1)
8688  res = ast_play_and_wait(chan, "vm-message");
8689  else
8690  res = ast_play_and_wait(chan, "vm-messages");
8691  }
8692  }
8693  if (!res) {
8694  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8695  res = ast_play_and_wait(chan, "vm-no");
8696  if (!res)
8697  res = ast_play_and_wait(chan, "vm-messages");
8698  }
8699  }
8700  }
8701  return res;
8702 }
8703 
8704 /* SPANISH syntax */
8705 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
8706 {
8707  /* Introduce messages they have */
8708  int res;
8709  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8710  res = ast_play_and_wait(chan, "vm-youhaveno");
8711  if (!res)
8712  res = ast_play_and_wait(chan, "vm-messages");
8713  } else {
8714  res = ast_play_and_wait(chan, "vm-youhave");
8715  }
8716  if (!res) {
8717  if (vms->newmessages) {
8718  if (!res) {
8719  if ((vms->newmessages == 1)) {
8720  res = ast_play_and_wait(chan, "digits/1");
8721  if (!res)
8722  res = ast_play_and_wait(chan, "vm-message");
8723  if (!res)
8724  res = ast_play_and_wait(chan, "vm-INBOXs");
8725  } else {
8726  res = say_and_wait(chan, vms->newmessages, chan->language);
8727  if (!res)
8728  res = ast_play_and_wait(chan, "vm-messages");
8729  if (!res)
8730  res = ast_play_and_wait(chan, "vm-INBOX");
8731  }
8732  }
8733  if (vms->oldmessages && !res)
8734  res = ast_play_and_wait(chan, "vm-and");
8735  }
8736  if (vms->oldmessages) {
8737  if (!res) {
8738  if (vms->oldmessages == 1) {
8739  res = ast_play_and_wait(chan, "digits/1");
8740  if (!res)
8741  res = ast_play_and_wait(chan, "vm-message");
8742  if (!res)
8743  res = ast_play_and_wait(chan, "vm-Olds");
8744  } else {
8745  res = say_and_wait(chan, vms->oldmessages, chan->language);
8746  if (!res)
8747  res = ast_play_and_wait(chan, "vm-messages");
8748  if (!res)
8749  res = ast_play_and_wait(chan, "vm-Old");
8750  }
8751  }
8752  }
8753  }
8754 return res;
8755 }
8756 
8757 /* BRAZILIAN PORTUGUESE syntax */
8758 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
8759  /* Introduce messages they have */
8760  int res;
8761  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8762  res = ast_play_and_wait(chan, "vm-nomessages");
8763  return res;
8764  } else {
8765  res = ast_play_and_wait(chan, "vm-youhave");
8766  }
8767  if (vms->newmessages) {
8768  if (!res)
8769  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
8770  if ((vms->newmessages == 1)) {
8771  if (!res)
8772  res = ast_play_and_wait(chan, "vm-message");
8773  if (!res)
8774  res = ast_play_and_wait(chan, "vm-INBOXs");
8775  } else {
8776  if (!res)
8777  res = ast_play_and_wait(chan, "vm-messages");
8778  if (!res)
8779  res = ast_play_and_wait(chan, "vm-INBOX");
8780  }
8781  if (vms->oldmessages && !res)
8782  res = ast_play_and_wait(chan, "vm-and");
8783  }
8784  if (vms->oldmessages) {
8785  if (!res)
8786  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
8787  if (vms->oldmessages == 1) {
8788  if (!res)
8789  res = ast_play_and_wait(chan, "vm-message");
8790  if (!res)
8791  res = ast_play_and_wait(chan, "vm-Olds");
8792  } else {
8793  if (!res)
8794  res = ast_play_and_wait(chan, "vm-messages");
8795  if (!res)
8796  res = ast_play_and_wait(chan, "vm-Old");
8797  }
8798  }
8799  return res;
8800 }
8801 
8802 /* FRENCH syntax */
8803 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
8804 {
8805  /* Introduce messages they have */
8806  int res;
8807  res = ast_play_and_wait(chan, "vm-youhave");
8808  if (!res) {
8809  if (vms->newmessages) {
8810  res = say_and_wait(chan, vms->newmessages, chan->language);
8811  if (!res)
8812  res = ast_play_and_wait(chan, "vm-INBOX");
8813  if (vms->oldmessages && !res)
8814  res = ast_play_and_wait(chan, "vm-and");
8815  else if (!res) {
8816  if ((vms->newmessages == 1))
8817  res = ast_play_and_wait(chan, "vm-message");
8818  else
8819  res = ast_play_and_wait(chan, "vm-messages");
8820  }
8821 
8822  }
8823  if (!res && vms->oldmessages) {
8824  res = say_and_wait(chan, vms->oldmessages, chan->language);
8825  if (!res)
8826  res = ast_play_and_wait(chan, "vm-Old");
8827  if (!res) {
8828  if (vms->oldmessages == 1)
8829  res = ast_play_and_wait(chan, "vm-message");
8830  else
8831  res = ast_play_and_wait(chan, "vm-messages");
8832  }
8833  }
8834  if (!res) {
8835  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8836  res = ast_play_and_wait(chan, "vm-no");
8837  if (!res)
8838  res = ast_play_and_wait(chan, "vm-messages");
8839  }
8840  }
8841  }
8842  return res;
8843 }
8844 
8845 /* DUTCH syntax */
8846 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
8847 {
8848  /* Introduce messages they have */
8849  int res;
8850  res = ast_play_and_wait(chan, "vm-youhave");
8851  if (!res) {
8852  if (vms->newmessages) {
8853  res = say_and_wait(chan, vms->newmessages, chan->language);
8854  if (!res) {
8855  if (vms->newmessages == 1)
8856  res = ast_play_and_wait(chan, "vm-INBOXs");
8857  else
8858  res = ast_play_and_wait(chan, "vm-INBOX");
8859  }
8860  if (vms->oldmessages && !res)
8861  res = ast_play_and_wait(chan, "vm-and");
8862  else if (!res) {
8863  if ((vms->newmessages == 1))
8864  res = ast_play_and_wait(chan, "vm-message");
8865  else
8866  res = ast_play_and_wait(chan, "vm-messages");
8867  }
8868 
8869  }
8870  if (!res && vms->oldmessages) {
8871  res = say_and_wait(chan, vms->oldmessages, chan->language);
8872  if (!res) {
8873  if (vms->oldmessages == 1)
8874  res = ast_play_and_wait(chan, "vm-Olds");
8875  else
8876  res = ast_play_and_wait(chan, "vm-Old");
8877  }
8878  if (!res) {
8879  if (vms->oldmessages == 1)
8880  res = ast_play_and_wait(chan, "vm-message");
8881  else
8882  res = ast_play_and_wait(chan, "vm-messages");
8883  }
8884  }
8885  if (!res) {
8886  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8887  res = ast_play_and_wait(chan, "vm-no");
8888  if (!res)
8889  res = ast_play_and_wait(chan, "vm-messages");
8890  }
8891  }
8892  }
8893  return res;
8894 }
8895 
8896 /* PORTUGUESE syntax */
8897 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
8898 {
8899  /* Introduce messages they have */
8900  int res;
8901  res = ast_play_and_wait(chan, "vm-youhave");
8902  if (!res) {
8903  if (vms->newmessages) {
8904  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
8905  if (!res) {
8906  if ((vms->newmessages == 1)) {
8907  res = ast_play_and_wait(chan, "vm-message");
8908  if (!res)
8909  res = ast_play_and_wait(chan, "vm-INBOXs");
8910  } else {
8911  res = ast_play_and_wait(chan, "vm-messages");
8912  if (!res)
8913  res = ast_play_and_wait(chan, "vm-INBOX");
8914  }
8915  }
8916  if (vms->oldmessages && !res)
8917  res = ast_play_and_wait(chan, "vm-and");
8918  }
8919  if (!res && vms->oldmessages) {
8920  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
8921  if (!res) {
8922  if (vms->oldmessages == 1) {
8923  res = ast_play_and_wait(chan, "vm-message");
8924  if (!res)
8925  res = ast_play_and_wait(chan, "vm-Olds");
8926  } else {
8927  res = ast_play_and_wait(chan, "vm-messages");
8928  if (!res)
8929  res = ast_play_and_wait(chan, "vm-Old");
8930  }
8931  }
8932  }
8933  if (!res) {
8934  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8935  res = ast_play_and_wait(chan, "vm-no");
8936  if (!res)
8937  res = ast_play_and_wait(chan, "vm-messages");
8938  }
8939  }
8940  }
8941  return res;
8942 }
8943 
8944 
8945 /* CZECH syntax */
8946 /* in czech there must be declension of word new and message
8947  * czech : english : czech : english
8948  * --------------------------------------------------------
8949  * vm-youhave : you have
8950  * vm-novou : one new : vm-zpravu : message
8951  * vm-nove : 2-4 new : vm-zpravy : messages
8952  * vm-novych : 5-infinite new : vm-zprav : messages
8953  * vm-starou : one old
8954  * vm-stare : 2-4 old
8955  * vm-starych : 5-infinite old
8956  * jednu : one - falling 4.
8957  * vm-no : no ( no messages )
8958  */
8959 
8960 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
8961 {
8962  int res;
8963  res = ast_play_and_wait(chan, "vm-youhave");
8964  if (!res) {
8965  if (vms->newmessages) {
8966  if (vms->newmessages == 1) {
8967  res = ast_play_and_wait(chan, "digits/jednu");
8968  } else {
8969  res = say_and_wait(chan, vms->newmessages, chan->language);
8970  }
8971  if (!res) {
8972  if ((vms->newmessages == 1))
8973  res = ast_play_and_wait(chan, "vm-novou");
8974  if ((vms->newmessages) > 1 && (vms->newmessages < 5))
8975  res = ast_play_and_wait(chan, "vm-nove");
8976  if (vms->newmessages > 4)
8977  res = ast_play_and_wait(chan, "vm-novych");
8978  }
8979  if (vms->oldmessages && !res)
8980  res = ast_play_and_wait(chan, "vm-and");
8981  else if (!res) {
8982  if ((vms->newmessages == 1))
8983  res = ast_play_and_wait(chan, "vm-zpravu");
8984  if ((vms->newmessages) > 1 && (vms->newmessages < 5))
8985  res = ast_play_and_wait(chan, "vm-zpravy");
8986  if (vms->newmessages > 4)
8987  res = ast_play_and_wait(chan, "vm-zprav");
8988  }
8989  }
8990  if (!res && vms->oldmessages) {
8991  res = say_and_wait(chan, vms->oldmessages, chan->language);
8992  if (!res) {
8993  if ((vms->oldmessages == 1))
8994  res = ast_play_and_wait(chan, "vm-starou");
8995  if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
8996  res = ast_play_and_wait(chan, "vm-stare");
8997  if (vms->oldmessages > 4)
8998  res = ast_play_and_wait(chan, "vm-starych");
8999  }
9000  if (!res) {
9001  if ((vms->oldmessages == 1))
9002  res = ast_play_and_wait(chan, "vm-zpravu");
9003  if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
9004  res = ast_play_and_wait(chan, "vm-zpravy");
9005  if (vms->oldmessages > 4)
9006  res = ast_play_and_wait(chan, "vm-zprav");
9007  }
9008  }
9009  if (!res) {
9010  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
9011  res = ast_play_and_wait(chan, "vm-no");
9012  if (!res)
9013  res = ast_play_and_wait(chan, "vm-zpravy");
9014  }
9015  }
9016  }
9017  return res;
9018 }
9019 
9020 /* CHINESE (Taiwan) syntax */
9021 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
9022 {
9023  int res;
9024  /* Introduce messages they have */
9025  res = ast_play_and_wait(chan, "vm-you");
9026 
9027  if (!res && vms->newmessages) {
9028  res = ast_play_and_wait(chan, "vm-have");
9029  if (!res)
9030  res = say_and_wait(chan, vms->newmessages, chan->language);
9031  if (!res)
9032  res = ast_play_and_wait(chan, "vm-tong");
9033  if (!res)
9034  res = ast_play_and_wait(chan, "vm-INBOX");
9035  if (vms->oldmessages && !res)
9036  res = ast_play_and_wait(chan, "vm-and");
9037  else if (!res)
9038  res = ast_play_and_wait(chan, "vm-messages");
9039  }
9040  if (!res && vms->oldmessages) {
9041  res = ast_play_and_wait(chan, "vm-have");
9042  if (!res)
9043  res = say_and_wait(chan, vms->oldmessages, chan->language);
9044  if (!res)
9045  res = ast_play_and_wait(chan, "vm-tong");
9046  if (!res)
9047  res = ast_play_and_wait(chan, "vm-Old");
9048  if (!res)
9049  res = ast_play_and_wait(chan, "vm-messages");
9050  }
9051  if (!res && !vms->oldmessages && !vms->newmessages) {
9052  res = ast_play_and_wait(chan, "vm-haveno");
9053  if (!res)
9054  res = ast_play_and_wait(chan, "vm-messages");
9055  }
9056  return res;
9057 }
9058 
9059 /* Vietnamese syntax */
9060 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
9061 {
9062  int res;
9063 
9064  /* Introduce messages they have */
9065  res = ast_play_and_wait(chan, "vm-youhave");
9066  if (!res) {
9067  if (vms->newmessages) {
9068  res = say_and_wait(chan, vms->newmessages, chan->language);
9069  if (!res)
9070  res = ast_play_and_wait(chan, "vm-INBOX");
9071  if (vms->oldmessages && !res)
9072  res = ast_play_and_wait(chan, "vm-and");
9073  }
9074  if (!res && vms->oldmessages) {
9075  res = say_and_wait(chan, vms->oldmessages, chan->language);
9076  if (!res)
9077  res = ast_play_and_wait(chan, "vm-Old");
9078  }
9079  if (!res) {
9080  if (!vms->oldmessages && !vms->newmessages) {
9081  res = ast_play_and_wait(chan, "vm-no");
9082  if (!res)
9083  res = ast_play_and_wait(chan, "vm-message");
9084  }
9085  }
9086  }
9087  return res;
9088 }
9089 
9090 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
9091 {
9092  char prefile[256];
9093 
9094  /* Notify the user that the temp greeting is set and give them the option to remove it */
9095  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
9096  if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
9097  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
9098  if (ast_fileexists(prefile, NULL, NULL) > 0) {
9099  ast_play_and_wait(chan, "vm-tempgreetactive");
9100  }
9101  DISPOSE(prefile, -1);
9102  }
9103 
9104  /* Play voicemail intro - syntax is different for different languages */
9105  if (0) {
9106  return 0;
9107  } else if (!strncasecmp(chan->language, "cs", 2)) { /* CZECH syntax */
9108  return vm_intro_cs(chan, vms);
9109  } else if (!strncasecmp(chan->language, "cz", 2)) { /* deprecated CZECH syntax */
9110  static int deprecation_warning = 0;
9111  if (deprecation_warning++ % 10 == 0) {
9112  ast_log(LOG_WARNING, "cz is not a standard language code. Please switch to using cs instead.\n");
9113  }
9114  return vm_intro_cs(chan, vms);
9115  } else if (!strncasecmp(chan->language, "de", 2)) { /* GERMAN syntax */
9116  return vm_intro_de(chan, vms);
9117  } else if (!strncasecmp(chan->language, "es", 2)) { /* SPANISH syntax */
9118  return vm_intro_es(chan, vms);
9119  } else if (!strncasecmp(chan->language, "fr", 2)) { /* FRENCH syntax */
9120  return vm_intro_fr(chan, vms);
9121  } else if (!strncasecmp(chan->language, "gr", 2)) { /* GREEK syntax */
9122  return vm_intro_gr(chan, vms);
9123  } else if (!strncasecmp(chan->language, "he", 2)) { /* HEBREW syntax */
9124  return vm_intro_he(chan, vms);
9125  } else if (!strncasecmp(chan->language, "it", 2)) { /* ITALIAN syntax */
9126  return vm_intro_it(chan, vms);
9127  } else if (!strncasecmp(chan->language, "nl", 2)) { /* DUTCH syntax */
9128  return vm_intro_nl(chan, vms);
9129  } else if (!strncasecmp(chan->language, "no", 2)) { /* NORWEGIAN syntax */
9130  return vm_intro_no(chan, vms);
9131  } else if (!strncasecmp(chan->language, "pl", 2)) { /* POLISH syntax */
9132  return vm_intro_pl(chan, vms);
9133  } else if (!strncasecmp(chan->language, "pt_BR", 5)) { /* BRAZILIAN PORTUGUESE syntax */
9134  return vm_intro_pt_BR(chan, vms);
9135  } else if (!strncasecmp(chan->language, "pt", 2)) { /* PORTUGUESE syntax */
9136  return vm_intro_pt(chan, vms);
9137  } else if (!strncasecmp(chan->language, "ru", 2)) { /* RUSSIAN syntax */
9138  return vm_intro_multilang(chan, vms, "n");
9139  } else if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
9140  return vm_intro_se(chan, vms);
9141  } else if (!strncasecmp(chan->language, "ua", 2)) { /* UKRAINIAN syntax */
9142  return vm_intro_multilang(chan, vms, "n");
9143  } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
9144  return vm_intro_vi(chan, vms);
9145  } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
9146  return vm_intro_zh(chan, vms);
9147  } else { /* Default to ENGLISH */
9148  return vm_intro_en(chan, vms);
9149  }
9150 }
9151 
9152 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
9153 {
9154  int res = 0;
9155  /* Play instructions and wait for new command */
9156  while (!res) {
9157  if (vms->starting) {
9158  if (vms->lastmsg > -1) {
9159  if (skipadvanced)
9160  res = ast_play_and_wait(chan, "vm-onefor-full");
9161  else
9162  res = ast_play_and_wait(chan, "vm-onefor");
9163  if (!res)
9164  res = vm_play_folder_name(chan, vms->vmbox);
9165  }
9166  if (!res) {
9167  if (skipadvanced)
9168  res = ast_play_and_wait(chan, "vm-opts-full");
9169  else
9170  res = ast_play_and_wait(chan, "vm-opts");
9171  }
9172  } else {
9173  /* Added for additional help */
9174  if (skipadvanced) {
9175  res = ast_play_and_wait(chan, "vm-onefor-full");
9176  if (!res)
9177  res = vm_play_folder_name(chan, vms->vmbox);
9178  res = ast_play_and_wait(chan, "vm-opts-full");
9179  }
9180  /* Logic:
9181  * If the current message is not the first OR
9182  * if we're listening to the first new message and there are
9183  * also urgent messages, then prompt for navigation to the
9184  * previous message
9185  */
9186  if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
9187  res = ast_play_and_wait(chan, "vm-prev");
9188  }
9189  if (!res && !skipadvanced)
9190  res = ast_play_and_wait(chan, "vm-advopts");
9191  if (!res)
9192  res = ast_play_and_wait(chan, "vm-repeat");
9193  /* Logic:
9194  * If we're not listening to the last message OR
9195  * we're listening to the last urgent message and there are
9196  * also new non-urgent messages, then prompt for navigation
9197  * to the next message
9198  */
9199  if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
9200  (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
9201  res = ast_play_and_wait(chan, "vm-next");
9202  }
9203  if (!res) {
9204  int curmsg_deleted;
9205 #ifdef IMAP_STORAGE
9206  ast_mutex_lock(&vms->lock);
9207 #endif
9208  curmsg_deleted = vms->deleted[vms->curmsg];
9209 #ifdef IMAP_STORAGE
9210  ast_mutex_unlock(&vms->lock);
9211 #endif
9212  if (!curmsg_deleted) {
9213  res = ast_play_and_wait(chan, "vm-delete");
9214  } else {
9215  res = ast_play_and_wait(chan, "vm-undelete");
9216  }
9217  if (!res) {
9218  res = ast_play_and_wait(chan, "vm-toforward");
9219  }
9220  if (!res) {
9221  res = ast_play_and_wait(chan, "vm-savemessage");
9222  }
9223  }
9224  }
9225  if (!res) {
9226  res = ast_play_and_wait(chan, "vm-helpexit");
9227  }
9228  if (!res)
9229  res = ast_waitfordigit(chan, 6000);
9230  if (!res) {
9231  vms->repeats++;
9232  if (vms->repeats > 2) {
9233  res = 't';
9234  }
9235  }
9236  }
9237  return res;
9238 }
9239 
9240 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
9241 {
9242  int res = 0;
9243  /* Play instructions and wait for new command */
9244  while (!res) {
9245  if (vms->lastmsg > -1) {
9246  res = ast_play_and_wait(chan, "vm-listen");
9247  if (!res)
9248  res = vm_play_folder_name(chan, vms->vmbox);
9249  if (!res)
9250  res = ast_play_and_wait(chan, "press");
9251  if (!res)
9252  res = ast_play_and_wait(chan, "digits/1");
9253  }
9254  if (!res)
9255  res = ast_play_and_wait(chan, "vm-opts");
9256  if (!res) {
9257  vms->starting = 0;
9258  return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
9259  }
9260  }
9261  return res;
9262 }
9263 
9264 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
9265 {
9266  if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
9267  return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
9268  } else { /* Default to ENGLISH */
9269  return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
9270  }
9271 }
9272 
9273 
9274 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
9275 {
9276  int cmd = 0;
9277  int duration = 0;
9278  int tries = 0;
9279  char newpassword[80] = "";
9280  char newpassword2[80] = "";
9281  char prefile[PATH_MAX] = "";
9282  unsigned char buf[256];
9283  int bytes = 0;
9284 
9285  ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
9286  if (ast_adsi_available(chan)) {
9287  bytes += adsi_logo(buf + bytes);
9288  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
9289  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
9290  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
9291  bytes += ast_adsi_voice_mode(buf + bytes, 0);
9292  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
9293  }
9294 
9295  /* If forcename is set, have the user record their name */
9296  if (ast_test_flag(vmu, VM_FORCENAME)) {
9297  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
9298  if (ast_fileexists(prefile, NULL, NULL) < 1) {
9299  cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9300  if (cmd < 0 || cmd == 't' || cmd == '#')
9301  return cmd;
9302  }
9303  }
9304 
9305  /* If forcegreetings is set, have the user record their greetings */
9306  if (ast_test_flag(vmu, VM_FORCEGREET)) {
9307  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
9308  if (ast_fileexists(prefile, NULL, NULL) < 1) {
9309  cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9310  if (cmd < 0 || cmd == 't' || cmd == '#')
9311  return cmd;
9312  }
9313 
9314  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
9315  if (ast_fileexists(prefile, NULL, NULL) < 1) {
9316  cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9317  if (cmd < 0 || cmd == 't' || cmd == '#')
9318  return cmd;
9319  }
9320  }
9321 
9322  /*
9323  * Change the password last since new users will be able to skip over any steps this one comes before
9324  * by hanging up and calling back to voicemail main since the password is used to verify new user status.
9325  */
9326  for (;;) {
9327  newpassword[1] = '\0';
9328  newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
9329  if (cmd == '#')
9330  newpassword[0] = '\0';
9331  if (cmd < 0 || cmd == 't' || cmd == '#')
9332  return cmd;
9333  cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
9334  if (cmd < 0 || cmd == 't' || cmd == '#')
9335  return cmd;
9336  cmd = check_password(vmu, newpassword); /* perform password validation */
9337  if (cmd != 0) {
9338  ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
9339  cmd = ast_play_and_wait(chan, vm_invalid_password);
9340  } else {
9341  newpassword2[1] = '\0';
9342  newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
9343  if (cmd == '#')
9344  newpassword2[0] = '\0';
9345  if (cmd < 0 || cmd == 't' || cmd == '#')
9346  return cmd;
9347  cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
9348  if (cmd < 0 || cmd == 't' || cmd == '#')
9349  return cmd;
9350  if (!strcmp(newpassword, newpassword2))
9351  break;
9352  ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
9353  cmd = ast_play_and_wait(chan, vm_mismatch);
9354  }
9355  if (++tries == 3)
9356  return -1;
9357  if (cmd != 0) {
9358  cmd = ast_play_and_wait(chan, vm_pls_try_again);
9359  }
9360  }
9361  if (pwdchange & PWDCHANGE_INTERNAL)
9362  vm_change_password(vmu, newpassword);
9363  if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
9364  vm_change_password_shell(vmu, newpassword);
9365 
9366  ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
9367  cmd = ast_play_and_wait(chan, vm_passchanged);
9368 
9369  return cmd;
9370 }
9371 
9372 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
9373 {
9374  int cmd = 0;
9375  int retries = 0;
9376  int duration = 0;
9377  char newpassword[80] = "";
9378  char newpassword2[80] = "";
9379  char prefile[PATH_MAX] = "";
9380  unsigned char buf[256];
9381  int bytes = 0;
9382 
9383  ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
9384  if (ast_adsi_available(chan)) {
9385  bytes += adsi_logo(buf + bytes);
9386  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
9387  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
9388  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
9389  bytes += ast_adsi_voice_mode(buf + bytes, 0);
9390  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
9391  }
9392  while ((cmd >= 0) && (cmd != 't')) {
9393  if (cmd)
9394  retries = 0;
9395  switch (cmd) {
9396  case '1': /* Record your unavailable message */
9397  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
9398  cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9399  break;
9400  case '2': /* Record your busy message */
9401  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
9402  cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9403  break;
9404  case '3': /* Record greeting */
9405  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
9406  cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9407  break;
9408  case '4': /* manage the temporary greeting */
9409  cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
9410  break;
9411  case '5': /* change password */
9412  if (vmu->password[0] == '-') {
9413  cmd = ast_play_and_wait(chan, "vm-no");
9414  break;
9415  }
9416  newpassword[1] = '\0';
9417  newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
9418  if (cmd == '#')
9419  newpassword[0] = '\0';
9420  else {
9421  if (cmd < 0)
9422  break;
9423  if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
9424  break;
9425  }
9426  }
9427  cmd = check_password(vmu, newpassword); /* perform password validation */
9428  if (cmd != 0) {
9429  ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
9430  cmd = ast_play_and_wait(chan, vm_invalid_password);
9431  if (!cmd) {
9432  cmd = ast_play_and_wait(chan, vm_pls_try_again);
9433  }
9434  break;
9435  }
9436  newpassword2[1] = '\0';
9437  newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
9438  if (cmd == '#')
9439  newpassword2[0] = '\0';
9440  else {
9441  if (cmd < 0)
9442  break;
9443 
9444  if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
9445  break;
9446  }
9447  }
9448  if (strcmp(newpassword, newpassword2)) {
9449  ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
9450  cmd = ast_play_and_wait(chan, vm_mismatch);
9451  if (!cmd) {
9452  cmd = ast_play_and_wait(chan, vm_pls_try_again);
9453  }
9454  break;
9455  }
9456 
9457  if (pwdchange & PWDCHANGE_INTERNAL) {
9458  vm_change_password(vmu, newpassword);
9459  }
9460  if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
9461  vm_change_password_shell(vmu, newpassword);
9462  }
9463 
9464  ast_debug(1, "User %s set password to %s of length %d\n",
9465  vms->username, newpassword, (int) strlen(newpassword));
9466  cmd = ast_play_and_wait(chan, vm_passchanged);
9467  break;
9468  case '*':
9469  cmd = 't';
9470  break;
9471  default:
9472  cmd = 0;
9473  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
9474  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
9475  if (ast_fileexists(prefile, NULL, NULL)) {
9476  cmd = ast_play_and_wait(chan, "vm-tmpexists");
9477  }
9478  DISPOSE(prefile, -1);
9479  if (!cmd) {
9480  cmd = ast_play_and_wait(chan, "vm-options");
9481  }
9482  if (!cmd) {
9483  cmd = ast_waitfordigit(chan, 6000);
9484  }
9485  if (!cmd) {
9486  retries++;
9487  }
9488  if (retries > 3) {
9489  cmd = 't';
9490  }
9491  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
9492  }
9493  }
9494  if (cmd == 't')
9495  cmd = 0;
9496  return cmd;
9497 }
9498 
9499 /*!
9500  * \brief The handler for 'record a temporary greeting'.
9501  * \param chan
9502  * \param vmu
9503  * \param vms
9504  * \param fmtc
9505  * \param record_gain
9506  *
9507  * This is option 4 from the mailbox options menu.
9508  * This function manages the following promptings:
9509  * 1: play / record / review the temporary greeting. : invokes play_record_review().
9510  * 2: remove (delete) the temporary greeting.
9511  * *: return to the main menu.
9512  *
9513  * \return zero on success, -1 on error.
9514  */
9515 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
9516 {
9517  int cmd = 0;
9518  int retries = 0;
9519  int duration = 0;
9520  char prefile[PATH_MAX] = "";
9521  unsigned char buf[256];
9522  int bytes = 0;
9523 
9524  if (ast_adsi_available(chan)) {
9525  bytes += adsi_logo(buf + bytes);
9526  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
9527  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
9528  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
9529  bytes += ast_adsi_voice_mode(buf + bytes, 0);
9530  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
9531  }
9532 
9533  ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
9534  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
9535  while ((cmd >= 0) && (cmd != 't')) {
9536  if (cmd)
9537  retries = 0;
9538  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
9539  if (ast_fileexists(prefile, NULL, NULL) <= 0) {
9540  cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9541  if (cmd == -1) {
9542  break;
9543  }
9544  cmd = 't';
9545  } else {
9546  switch (cmd) {
9547  case '1':
9548  cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9549  break;
9550  case '2':
9551  DELETE(prefile, -1, prefile, vmu);
9552  ast_play_and_wait(chan, "vm-tempremoved");
9553  cmd = 't';
9554  break;
9555  case '*':
9556  cmd = 't';
9557  break;
9558  default:
9559  cmd = ast_play_and_wait(chan,
9560  ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
9561  "vm-tempgreeting2" : "vm-tempgreeting");
9562  if (!cmd) {
9563  cmd = ast_waitfordigit(chan, 6000);
9564  }
9565  if (!cmd) {
9566  retries++;
9567  }
9568  if (retries > 3) {
9569  cmd = 't';
9570  }
9571  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
9572  }
9573  }
9574  DISPOSE(prefile, -1);
9575  }
9576  if (cmd == 't')
9577  cmd = 0;
9578  return cmd;
9579 }
9580 
9581 
9582 /* Hebrew Syntax */
9583 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9584 {
9585  int cmd = 0;
9586 
9587  if (vms->lastmsg > -1) {
9588  cmd = play_message(chan, vmu, vms);
9589  } else {
9590  if (!strcasecmp(vms->fn, "INBOX")) {
9591  cmd = ast_play_and_wait(chan, "vm-nonewmessages");
9592  } else {
9593  cmd = ast_play_and_wait(chan, "vm-nomessages");
9594  }
9595  }
9596  return cmd;
9597 }
9598 
9599 /*!
9600  * \brief Default English syntax for 'You have N messages' greeting.
9601  * \param chan
9602  * \param vms
9603  * \param vmu
9604  *
9605  * \return zero on success, -1 on error.
9606  */
9607 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9608 {
9609  int cmd = 0;
9610 
9611  if (vms->lastmsg > -1) {
9612  cmd = play_message(chan, vmu, vms);
9613  } else {
9614  cmd = ast_play_and_wait(chan, "vm-youhave");
9615  if (!cmd)
9616  cmd = ast_play_and_wait(chan, "vm-no");
9617  if (!cmd) {
9618  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
9619  cmd = ast_play_and_wait(chan, vms->fn);
9620  }
9621  if (!cmd)
9622  cmd = ast_play_and_wait(chan, "vm-messages");
9623  }
9624  return cmd;
9625 }
9626 
9627 
9628 /*!
9629  * \brief Common LATIN languages syntax for 'You have N messages' greeting.
9630  * \param chan
9631  * \param vms
9632  * \param vmu
9633  *
9634  * \return zero on success, -1 on error.
9635  */
9636 static int vm_browse_messages_latin(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9637 {
9638  int cmd;
9639 
9640  if (vms->lastmsg > -1) {
9641  cmd = play_message(chan, vmu, vms);
9642  } else {
9643  cmd = ast_play_and_wait(chan, "vm-youhaveno");
9644  if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
9645  if (!cmd) {
9646  snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
9647  cmd = ast_play_and_wait(chan, vms->fn);
9648  }
9649  if (!cmd)
9650  cmd = ast_play_and_wait(chan, "vm-messages");
9651  } else {
9652  if (!cmd)
9653  cmd = ast_play_and_wait(chan, "vm-messages");
9654  if (!cmd) {
9655  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
9656  cmd = ast_play_and_wait(chan, vms->fn);
9657  }
9658  }
9659  }
9660  return cmd;
9661 }
9662 
9663 /*!
9664  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
9665  * \param chan
9666  * \param vms
9667  * \param vmu
9668  *
9669  * \return zero on success, -1 on error.
9670  */
9671 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9672 {
9673  int cmd;
9674 
9675  if (vms->lastmsg > -1) {
9676  cmd = play_message(chan, vmu, vms);
9677  } else {
9678  cmd = ast_play_and_wait(chan, "vm-you");
9679  if (!cmd)
9680  cmd = ast_play_and_wait(chan, "vm-haveno");
9681  if (!cmd)
9682  cmd = ast_play_and_wait(chan, "vm-messages");
9683  if (!cmd) {
9684  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
9685  cmd = ast_play_and_wait(chan, vms->fn);
9686  }
9687  }
9688  return cmd;
9689 }
9690 
9691 /*!
9692  * \brief Vietnamese syntax for 'You have N messages' greeting.
9693  * \param chan
9694  * \param vms
9695  * \param vmu
9696  *
9697  * \return zero on success, -1 on error.
9698  */
9699 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9700 {
9701  int cmd = 0;
9702 
9703  if (vms->lastmsg > -1) {
9704  cmd = play_message(chan, vmu, vms);
9705  } else {
9706  cmd = ast_play_and_wait(chan, "vm-no");
9707  if (!cmd) {
9708  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
9709  cmd = ast_play_and_wait(chan, vms->fn);
9710  }
9711  }
9712  return cmd;
9713 }
9714 
9715 /*!
9716  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
9717  * \param chan The channel for the current user. We read the language property from this.
9718  * \param vms passed into the language-specific vm_browse_messages function.
9719  * \param vmu passed into the language-specific vm_browse_messages function.
9720  *
9721  * The method to be invoked is determined by the value of language code property in the user's channel.
9722  * The default (when unable to match) is to use english.
9723  *
9724  * \return zero on success, -1 on error.
9725  */
9726 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9727 {
9728  if (!strncasecmp(chan->language, "es", 2) ||
9729  !strncasecmp(chan->language, "it", 2) ||
9730  !strncasecmp(chan->language, "pt", 2) ||
9731  !strncasecmp(chan->language, "gr", 2)) { /* SPANISH, ITALIAN, PORTUGUESE or GREEK */
9732  return vm_browse_messages_latin(chan, vms, vmu);
9733  } else if (!strncasecmp(chan->language, "he", 2)) { /* HEBREW */
9734  return vm_browse_messages_he(chan, vms, vmu);
9735  } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE */
9736  return vm_browse_messages_vi(chan, vms, vmu);
9737  } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) */
9738  return vm_browse_messages_zh(chan, vms, vmu);
9739  } else { /* Default to English syntax */
9740  return vm_browse_messages_en(chan, vms, vmu);
9741  }
9742 }
9743 
9744 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
9745  struct ast_vm_user *res_vmu, const char *context, const char *prefix,
9746  int skipuser, int max_logins, int silent)
9747 {
9748  int useadsi = 0, valid = 0, logretries = 0;
9749  char password[AST_MAX_EXTENSION]="", *passptr;
9750  struct ast_vm_user vmus, *vmu = NULL;
9751 
9752  /* If ADSI is supported, setup login screen */
9753  adsi_begin(chan, &useadsi);
9754  if (!skipuser && useadsi)
9755  adsi_login(chan);
9756  if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
9757  ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
9758  return -1;
9759  }
9760 
9761  /* Authenticate them and get their mailbox/password */
9762 
9763  while (!valid && (logretries < max_logins)) {
9764  /* Prompt for, and read in the username */
9765  if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
9766  ast_log(AST_LOG_WARNING, "Couldn't read username\n");
9767  return -1;
9768  }
9769  if (ast_strlen_zero(mailbox)) {
9770  if (chan->caller.id.number.valid && chan->caller.id.number.str) {
9771  ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
9772  } else {
9773  ast_verb(3, "Username not entered\n");
9774  return -1;
9775  }
9776  } else if (mailbox[0] == '*') {
9777  /* user entered '*' */
9778  ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
9779  if (ast_exists_extension(chan, chan->context, "a", 1,
9780  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
9781  return -1;
9782  }
9783  ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
9784  mailbox[0] = '\0';
9785  }
9786 
9787  if (useadsi)
9788  adsi_password(chan);
9789 
9790  if (!ast_strlen_zero(prefix)) {
9791  char fullusername[80] = "";
9792  ast_copy_string(fullusername, prefix, sizeof(fullusername));
9793  strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
9794  ast_copy_string(mailbox, fullusername, mailbox_size);
9795  }
9796 
9797  ast_debug(1, "Before find user for mailbox %s\n", mailbox);
9798  vmu = find_user(&vmus, context, mailbox);
9799  if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
9800  /* saved password is blank, so don't bother asking */
9801  password[0] = '\0';
9802  } else {
9803  if (ast_streamfile(chan, vm_password, chan->language)) {
9804  ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
9805  return -1;
9806  }
9807  if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
9808  ast_log(AST_LOG_WARNING, "Unable to read password\n");
9809  return -1;
9810  } else if (password[0] == '*') {
9811  /* user entered '*' */
9812  ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
9813  if (ast_exists_extension(chan, chan->context, "a", 1,
9814  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
9815  mailbox[0] = '*';
9816  return -1;
9817  }
9818  ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
9819  mailbox[0] = '\0';
9820  /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
9821  vmu = NULL;
9822  }
9823  }
9824 
9825  if (vmu) {
9826  passptr = vmu->password;
9827  if (passptr[0] == '-') passptr++;
9828  }
9829  if (vmu && !strcmp(passptr, password))
9830  valid++;
9831  else {
9832  ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
9833  if (!ast_strlen_zero(prefix))
9834  mailbox[0] = '\0';
9835  }
9836  logretries++;
9837  if (!valid) {
9838  if (skipuser || logretries >= max_logins) {
9839  if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
9840  ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
9841  return -1;
9842  }
9843  } else {
9844  if (useadsi)
9845  adsi_login(chan);
9846  if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
9847  ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
9848  return -1;
9849  }
9850  }
9851  if (ast_waitstream(chan, "")) /* Channel is hung up */
9852  return -1;
9853  }
9854  }
9855  if (!valid && (logretries >= max_logins)) {
9856  ast_stopstream(chan);
9857  ast_play_and_wait(chan, "vm-goodbye");
9858  return -1;
9859  }
9860  if (vmu && !skipuser) {
9861  memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
9862  }
9863  return 0;
9864 }
9865 
9866 static int vm_execmain(struct ast_channel *chan, const char *data)
9867 {
9868  /* XXX This is, admittedly, some pretty horrendous code. For some
9869  reason it just seemed a lot easier to do with GOTO's. I feel
9870  like I'm back in my GWBASIC days. XXX */
9871  int res = -1;
9872  int cmd = 0;
9873  int valid = 0;
9874  char prefixstr[80] ="";
9875  char ext_context[256]="";
9876  int box;
9877  int useadsi = 0;
9878  int skipuser = 0;
9879  struct vm_state vms;
9880  struct ast_vm_user *vmu = NULL, vmus;
9881  char *context = NULL;
9882  int silentexit = 0;
9883  struct ast_flags flags = { 0 };
9884  signed char record_gain = 0;
9885  int play_auto = 0;
9886  int play_folder = 0;
9887  int in_urgent = 0;
9888 #ifdef IMAP_STORAGE
9889  int deleted = 0;
9890 #endif
9891 
9892  /* Add the vm_state to the active list and keep it active */
9893  memset(&vms, 0, sizeof(vms));
9894 
9895  vms.lastmsg = -1;
9896 
9897  memset(&vmus, 0, sizeof(vmus));
9898 
9899  ast_test_suite_event_notify("START", "Message: vm_execmain started");
9900  if (chan->_state != AST_STATE_UP) {
9901  ast_debug(1, "Before ast_answer\n");
9902  ast_answer(chan);
9903  }
9904 
9905  if (!ast_strlen_zero(data)) {
9906  char *opts[OPT_ARG_ARRAY_SIZE];
9907  char *parse;
9909  AST_APP_ARG(argv0);
9910  AST_APP_ARG(argv1);
9911  );
9912 
9913  parse = ast_strdupa(data);
9914 
9915  AST_STANDARD_APP_ARGS(args, parse);
9916 
9917  if (args.argc == 2) {
9918  if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
9919  return -1;
9920  if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
9921  int gain;
9922  if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
9923  if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
9924  ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
9925  return -1;
9926  } else {
9927  record_gain = (signed char) gain;
9928  }
9929  } else {
9930  ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
9931  }
9932  }
9933  if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
9934  play_auto = 1;
9935  if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
9936  /* See if it is a folder name first */
9937  if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
9938  if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
9939  play_folder = -1;
9940  }
9941  } else {
9942  play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
9943  }
9944  } else {
9945  ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
9946  }
9947  if (play_folder > 9 || play_folder < 0) {
9949  "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
9950  opts[OPT_ARG_PLAYFOLDER]);
9951  play_folder = 0;
9952  }
9953  }
9954  } else {
9955  /* old style options parsing */
9956  while (*(args.argv0)) {
9957  if (*(args.argv0) == 's')
9958  ast_set_flag(&flags, OPT_SILENT);
9959  else if (*(args.argv0) == 'p')
9961  else
9962  break;
9963  (args.argv0)++;
9964  }
9965 
9966  }
9967 
9968  valid = ast_test_flag(&flags, OPT_SILENT);
9969 
9970  if ((context = strchr(args.argv0, '@')))
9971  *context++ = '\0';
9972 
9973  if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
9974  ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
9975  else
9976  ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
9977 
9978  if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
9979  skipuser++;
9980  else
9981  valid = 0;
9982  }
9983 
9984  if (!valid)
9985  res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
9986 
9987  ast_debug(1, "After vm_authenticate\n");
9988 
9989  if (vms.username[0] == '*') {
9990  ast_debug(1, "user pressed * in context '%s'\n", chan->context);
9991 
9992  /* user entered '*' */
9993  if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
9994  ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
9995  res = 0; /* prevent hangup */
9996  goto out;
9997  }
9998  }
9999 
10000  if (!res) {
10001  valid = 1;
10002  if (!skipuser)
10003  vmu = &vmus;
10004  } else {
10005  res = 0;
10006  }
10007 
10008  /* If ADSI is supported, setup login screen */
10009  adsi_begin(chan, &useadsi);
10010 
10011  ast_test_suite_assert(valid);
10012  if (!valid) {
10013  goto out;
10014  }
10015  ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10016 
10017 #ifdef IMAP_STORAGE
10018  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10019  pthread_setspecific(ts_vmstate.key, &vms);
10020 
10021  vms.interactive = 1;
10022  vms.updated = 1;
10023  if (vmu)
10024  ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10025  vmstate_insert(&vms);
10026  init_vm_state(&vms);
10027 #endif
10028 
10029  /* Set language from config to override channel language */
10030  if (!ast_strlen_zero(vmu->language))
10031  ast_string_field_set(chan, language, vmu->language);
10032 
10033  /* Retrieve urgent, old and new message counts */
10034  ast_debug(1, "Before open_mailbox\n");
10035  res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10036  if (res < 0)
10037  goto out;
10038  vms.oldmessages = vms.lastmsg + 1;
10039  ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10040  /* check INBOX */
10041  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10042  if (res < 0)
10043  goto out;
10044  vms.newmessages = vms.lastmsg + 1;
10045  ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10046  /* Start in Urgent */
10047  in_urgent = 1;
10048  res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10049  if (res < 0)
10050  goto out;
10051  vms.urgentmessages = vms.lastmsg + 1;
10052  ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10053 
10054  /* Select proper mailbox FIRST!! */
10055  if (play_auto) {
10056  ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10057  if (vms.urgentmessages) {
10058  in_urgent = 1;
10059  res = open_mailbox(&vms, vmu, 11);
10060  } else {
10061  in_urgent = 0;
10062  res = open_mailbox(&vms, vmu, play_folder);
10063  }
10064  if (res < 0)
10065  goto out;
10066 
10067  /* If there are no new messages, inform the user and hangup */
10068  if (vms.lastmsg == -1) {
10069  in_urgent = 0;
10070  cmd = vm_browse_messages(chan, &vms, vmu);
10071  res = 0;
10072  goto out;
10073  }
10074  } else {
10075  if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10076  /* If we only have old messages start here */
10077  res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10078  in_urgent = 0;
10079  play_folder = 1;
10080  if (res < 0)
10081  goto out;
10082  } else if (!vms.urgentmessages && vms.newmessages) {
10083  /* If we have new messages but none are urgent */
10084  in_urgent = 0;
10085  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10086  if (res < 0)
10087  goto out;
10088  }
10089  }
10090 
10091  if (useadsi)
10092  adsi_status(chan, &vms);
10093  res = 0;
10094 
10095  /* Check to see if this is a new user */
10096  if (!strcasecmp(vmu->mailbox, vmu->password) &&
10098  if (ast_play_and_wait(chan, "vm-newuser") == -1)
10099  ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10100  cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10101  if ((cmd == 't') || (cmd == '#')) {
10102  /* Timeout */
10103  ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10104  res = 0;
10105  goto out;
10106  } else if (cmd < 0) {
10107  /* Hangup */
10108  ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10109  res = -1;
10110  goto out;
10111  }
10112  }
10113 #ifdef IMAP_STORAGE
10114  ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10115  if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10116  ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10117  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10118  }
10119  ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10120  if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10121  ast_log(AST_LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10122  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10123  }
10124 #endif
10125 
10126  ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10127  if (play_auto) {
10128  cmd = '1';
10129  } else {
10130  cmd = vm_intro(chan, vmu, &vms);
10131  }
10132  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10133 
10134  vms.repeats = 0;
10135  vms.starting = 1;
10136  while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10137  /* Run main menu */
10138  switch (cmd) {
10139  case '1': /* First message */
10140  vms.curmsg = 0;
10141  /* Fall through */
10142  case '5': /* Play current message */
10143  ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10144  cmd = vm_browse_messages(chan, &vms, vmu);
10145  break;
10146  case '2': /* Change folders */
10147  ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10148  if (useadsi)
10149  adsi_folders(chan, 0, "Change to folder...");
10150 
10151  cmd = get_folder2(chan, "vm-changeto", 0);
10152  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10153  if (cmd == '#') {
10154  cmd = 0;
10155  } else if (cmd > 0) {
10156  cmd = cmd - '0';
10157  res = close_mailbox(&vms, vmu);
10158  if (res == ERROR_LOCK_PATH)
10159  goto out;
10160  /* If folder is not urgent, set in_urgent to zero! */
10161  if (cmd != 11) in_urgent = 0;
10162  res = open_mailbox(&vms, vmu, cmd);
10163  if (res < 0)
10164  goto out;
10165  play_folder = cmd;
10166  cmd = 0;
10167  }
10168  if (useadsi)
10169  adsi_status2(chan, &vms);
10170 
10171  if (!cmd) {
10172  cmd = vm_play_folder_name(chan, vms.vmbox);
10173  }
10174 
10175  vms.starting = 1;
10176  vms.curmsg = 0;
10177  break;
10178  case '3': /* Advanced options */
10179  ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10180  cmd = 0;
10181  vms.repeats = 0;
10182  while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10183  switch (cmd) {
10184  case '1': /* Reply */
10185  if (vms.lastmsg > -1 && !vms.starting) {
10186  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10187  if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10188  res = cmd;
10189  goto out;
10190  }
10191  } else {
10192  cmd = ast_play_and_wait(chan, "vm-sorry");
10193  }
10194  cmd = 't';
10195  break;
10196  case '2': /* Callback */
10197  if (!vms.starting)
10198  ast_verb(3, "Callback Requested\n");
10199  if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10200  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10201  if (cmd == 9) {
10202  silentexit = 1;
10203  goto out;
10204  } else if (cmd == ERROR_LOCK_PATH) {
10205  res = cmd;
10206  goto out;
10207  }
10208  } else {
10209  cmd = ast_play_and_wait(chan, "vm-sorry");
10210  }
10211  cmd = 't';
10212  break;
10213  case '3': /* Envelope */
10214  if (vms.lastmsg > -1 && !vms.starting) {
10215  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10216  if (cmd == ERROR_LOCK_PATH) {
10217  res = cmd;
10218  goto out;
10219  }
10220  } else {
10221  cmd = ast_play_and_wait(chan, "vm-sorry");
10222  }
10223  cmd = 't';
10224  break;
10225  case '4': /* Dialout */
10226  if (!ast_strlen_zero(vmu->dialout)) {
10227  cmd = dialout(chan, vmu, NULL, vmu->dialout);
10228  if (cmd == 9) {
10229  silentexit = 1;
10230  goto out;
10231  }
10232  } else {
10233  cmd = ast_play_and_wait(chan, "vm-sorry");
10234  }
10235  cmd = 't';
10236  break;
10237 
10238  case '5': /* Leave VoiceMail */
10239  if (ast_test_flag(vmu, VM_SVMAIL)) {
10240  cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10241  if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10242  res = cmd;
10243  goto out;
10244  }
10245  } else {
10246  cmd = ast_play_and_wait(chan, "vm-sorry");
10247  }
10248  cmd = 't';
10249  break;
10250 
10251  case '*': /* Return to main menu */
10252  cmd = 't';
10253  break;
10254 
10255  default:
10256  cmd = 0;
10257  if (!vms.starting) {
10258  cmd = ast_play_and_wait(chan, "vm-toreply");
10259  }
10260  if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10261  cmd = ast_play_and_wait(chan, "vm-tocallback");
10262  }
10263  if (!cmd && !vms.starting) {
10264  cmd = ast_play_and_wait(chan, "vm-tohearenv");
10265  }
10266  if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10267  cmd = ast_play_and_wait(chan, "vm-tomakecall");
10268  }
10269  if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10270  cmd = ast_play_and_wait(chan, "vm-leavemsg");
10271  }
10272  if (!cmd) {
10273  cmd = ast_play_and_wait(chan, "vm-starmain");
10274  }
10275  if (!cmd) {
10276  cmd = ast_waitfordigit(chan, 6000);
10277  }
10278  if (!cmd) {
10279  vms.repeats++;
10280  }
10281  if (vms.repeats > 3) {
10282  cmd = 't';
10283  }
10284  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10285  }
10286  }
10287  if (cmd == 't') {
10288  cmd = 0;
10289  vms.repeats = 0;
10290  }
10291  break;
10292  case '4': /* Go to the previous message */
10293  ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10294  if (vms.curmsg > 0) {
10295  vms.curmsg--;
10296  cmd = play_message(chan, vmu, &vms);
10297  } else {
10298  /* Check if we were listening to new
10299  messages. If so, go to Urgent messages
10300  instead of saying "no more messages"
10301  */
10302  if (in_urgent == 0 && vms.urgentmessages > 0) {
10303  /* Check for Urgent messages */
10304  in_urgent = 1;
10305  res = close_mailbox(&vms, vmu);
10306  if (res == ERROR_LOCK_PATH)
10307  goto out;
10308  res = open_mailbox(&vms, vmu, 11); /* Open Urgent folder */
10309  if (res < 0)
10310  goto out;
10311  ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10312  vms.curmsg = vms.lastmsg;
10313  if (vms.lastmsg < 0) {
10314  cmd = ast_play_and_wait(chan, "vm-nomore");
10315  }
10316  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10317  vms.curmsg = vms.lastmsg;
10318  cmd = play_message(chan, vmu, &vms);
10319  } else {
10320  cmd = ast_play_and_wait(chan, "vm-nomore");
10321  }
10322  }
10323  break;
10324  case '6': /* Go to the next message */
10325  ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10326  if (vms.curmsg < vms.lastmsg) {
10327  vms.curmsg++;
10328  cmd = play_message(chan, vmu, &vms);
10329  } else {
10330  if (in_urgent && vms.newmessages > 0) {
10331  /* Check if we were listening to urgent
10332  * messages. If so, go to regular new messages
10333  * instead of saying "no more messages"
10334  */
10335  in_urgent = 0;
10336  res = close_mailbox(&vms, vmu);
10337  if (res == ERROR_LOCK_PATH)
10338  goto out;
10339  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10340  if (res < 0)
10341  goto out;
10342  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10343  vms.curmsg = -1;
10344  if (vms.lastmsg < 0) {
10345  cmd = ast_play_and_wait(chan, "vm-nomore");
10346  }
10347  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10348  vms.curmsg = 0;
10349  cmd = play_message(chan, vmu, &vms);
10350  } else {
10351  cmd = ast_play_and_wait(chan, "vm-nomore");
10352  }
10353  }
10354  break;
10355  case '7': /* Delete the current message */
10356  if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10357  vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10358  if (useadsi)
10359  adsi_delete(chan, &vms);
10360  if (vms.deleted[vms.curmsg]) {
10361  if (play_folder == 0) {
10362  if (in_urgent) {
10363  vms.urgentmessages--;
10364  } else {
10365  vms.newmessages--;
10366  }
10367  }
10368  else if (play_folder == 1)
10369  vms.oldmessages--;
10370  cmd = ast_play_and_wait(chan, "vm-deleted");
10371  } else {
10372  if (play_folder == 0) {
10373  if (in_urgent) {
10374  vms.urgentmessages++;
10375  } else {
10376  vms.newmessages++;
10377  }
10378  }
10379  else if (play_folder == 1)
10380  vms.oldmessages++;
10381  cmd = ast_play_and_wait(chan, "vm-undeleted");
10382  }
10383  if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10384  if (vms.curmsg < vms.lastmsg) {
10385  vms.curmsg++;
10386  cmd = play_message(chan, vmu, &vms);
10387  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10388  vms.curmsg = 0;
10389  cmd = play_message(chan, vmu, &vms);
10390  } else {
10391  /* Check if we were listening to urgent
10392  messages. If so, go to regular new messages
10393  instead of saying "no more messages"
10394  */
10395  if (in_urgent == 1) {
10396  /* Check for new messages */
10397  in_urgent = 0;
10398  res = close_mailbox(&vms, vmu);
10399  if (res == ERROR_LOCK_PATH)
10400  goto out;
10401  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10402  if (res < 0)
10403  goto out;
10404  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10405  vms.curmsg = -1;
10406  if (vms.lastmsg < 0) {
10407  cmd = ast_play_and_wait(chan, "vm-nomore");
10408  }
10409  } else {
10410  cmd = ast_play_and_wait(chan, "vm-nomore");
10411  }
10412  }
10413  }
10414  } else /* Delete not valid if we haven't selected a message */
10415  cmd = 0;
10416 #ifdef IMAP_STORAGE
10417  deleted = 1;
10418 #endif
10419  break;
10420 
10421  case '8': /* Forward the current message */
10422  if (vms.lastmsg > -1) {
10423  cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10424  if (cmd == ERROR_LOCK_PATH) {
10425  res = cmd;
10426  goto out;
10427  }
10428  } else {
10429  /* Check if we were listening to urgent
10430  messages. If so, go to regular new messages
10431  instead of saying "no more messages"
10432  */
10433  if (in_urgent == 1 && vms.newmessages > 0) {
10434  /* Check for new messages */
10435  in_urgent = 0;
10436  res = close_mailbox(&vms, vmu);
10437  if (res == ERROR_LOCK_PATH)
10438  goto out;
10439  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10440  if (res < 0)
10441  goto out;
10442  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10443  vms.curmsg = -1;
10444  if (vms.lastmsg < 0) {
10445  cmd = ast_play_and_wait(chan, "vm-nomore");
10446  }
10447  } else {
10448  cmd = ast_play_and_wait(chan, "vm-nomore");
10449  }
10450  }
10451  break;
10452  case '9': /* Save message to folder */
10453  ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10454  if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10455  /* No message selected */
10456  cmd = 0;
10457  break;
10458  }
10459  if (useadsi)
10460  adsi_folders(chan, 1, "Save to folder...");
10461  cmd = get_folder2(chan, "vm-savefolder", 1);
10462  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10463  box = 0; /* Shut up compiler */
10464  if (cmd == '#') {
10465  cmd = 0;
10466  break;
10467  } else if (cmd > 0) {
10468  box = cmd = cmd - '0';
10469  cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10470  if (cmd == ERROR_LOCK_PATH) {
10471  res = cmd;
10472  goto out;
10473 #ifndef IMAP_STORAGE
10474  } else if (!cmd) {
10475  vms.deleted[vms.curmsg] = 1;
10476 #endif
10477  } else {
10478  vms.deleted[vms.curmsg] = 0;
10479  vms.heard[vms.curmsg] = 0;
10480  }
10481  }
10482  make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10483  if (useadsi)
10484  adsi_message(chan, &vms);
10485  snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10486  if (!cmd) {
10487  cmd = ast_play_and_wait(chan, "vm-message");
10488  if (!cmd)
10489  cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10490  if (!cmd)
10491  cmd = ast_play_and_wait(chan, "vm-savedto");
10492  if (!cmd)
10493  cmd = vm_play_folder_name(chan, vms.fn);
10494  } else {
10495  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10496  }
10497  if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10498  if (vms.curmsg < vms.lastmsg) {
10499  vms.curmsg++;
10500  cmd = play_message(chan, vmu, &vms);
10501  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10502  vms.curmsg = 0;
10503  cmd = play_message(chan, vmu, &vms);
10504  } else {
10505  /* Check if we were listening to urgent
10506  messages. If so, go to regular new messages
10507  instead of saying "no more messages"
10508  */
10509  if (in_urgent == 1 && vms.newmessages > 0) {
10510  /* Check for new messages */
10511  in_urgent = 0;
10512  res = close_mailbox(&vms, vmu);
10513  if (res == ERROR_LOCK_PATH)
10514  goto out;
10515  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10516  if (res < 0)
10517  goto out;
10518  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10519  vms.curmsg = -1;
10520  if (vms.lastmsg < 0) {
10521  cmd = ast_play_and_wait(chan, "vm-nomore");
10522  }
10523  } else {
10524  cmd = ast_play_and_wait(chan, "vm-nomore");
10525  }
10526  }
10527  }
10528  break;
10529  case '*': /* Help */
10530  if (!vms.starting) {
10531  cmd = ast_play_and_wait(chan, "vm-onefor");
10532  if (!strncasecmp(chan->language, "he", 2)) {
10533  cmd = ast_play_and_wait(chan, "vm-for");
10534  }
10535  if (!cmd)
10536  cmd = vm_play_folder_name(chan, vms.vmbox);
10537  if (!cmd)
10538  cmd = ast_play_and_wait(chan, "vm-opts");
10539  if (!cmd)
10540  cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10541  } else
10542  cmd = 0;
10543  break;
10544  case '0': /* Mailbox options */
10545  cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10546  if (useadsi)
10547  adsi_status(chan, &vms);
10548  break;
10549  default: /* Nothing */
10550  ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10551  cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10552  break;
10553  }
10554  }
10555  if ((cmd == 't') || (cmd == '#')) {
10556  /* Timeout */
10557  res = 0;
10558  } else {
10559  /* Hangup */
10560  res = -1;
10561  }
10562 
10563 out:
10564  if (res > -1) {
10565  ast_stopstream(chan);
10566  adsi_goodbye(chan);
10567  if (valid && res != OPERATOR_EXIT) {
10568  if (silentexit)
10569  res = ast_play_and_wait(chan, "vm-dialout");
10570  else
10571  res = ast_play_and_wait(chan, "vm-goodbye");
10572  }
10573  if ((valid && res > 0) || res == OPERATOR_EXIT) {
10574  res = 0;
10575  }
10576  if (useadsi)
10578  }
10579  if (vmu)
10580  close_mailbox(&vms, vmu);
10581  if (valid) {
10582  int new = 0, old = 0, urgent = 0;
10583  snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10584  ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10585  /* Urgent flag not passwd to externnotify here */
10586  run_externnotify(vmu->context, vmu->mailbox, NULL);
10587  ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10588  queue_mwi_event(ext_context, urgent, new, old);
10589  }
10590 #ifdef IMAP_STORAGE
10591  /* expunge message - use UID Expunge if supported on IMAP server*/
10592  ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10593  if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10594  ast_mutex_lock(&vms.lock);
10595 #ifdef HAVE_IMAP_TK2006
10596  if (LEVELUIDPLUS (vms.mailstream)) {
10597  mail_expunge_full(vms.mailstream, NIL, EX_UID);
10598  } else
10599 #endif
10600  mail_expunge(vms.mailstream);
10601  ast_mutex_unlock(&vms.lock);
10602  }
10603  /* before we delete the state, we should copy pertinent info
10604  * back to the persistent model */
10605  if (vmu) {
10606  vmstate_delete(&vms);
10607  }
10608 #endif
10609  if (vmu)
10610  free_user(vmu);
10611 
10612 #ifdef IMAP_STORAGE
10613  pthread_setspecific(ts_vmstate.key, NULL);
10614 #endif
10615  return res;
10616 }
10617 
10618 static int vm_exec(struct ast_channel *chan, const char *data)
10619 {
10620  int res = 0;
10621  char *tmp;
10622  struct leave_vm_options leave_options;
10623  struct ast_flags flags = { 0 };
10624  char *opts[OPT_ARG_ARRAY_SIZE];
10626  AST_APP_ARG(argv0);
10627  AST_APP_ARG(argv1);
10628  );
10629 
10630  memset(&leave_options, 0, sizeof(leave_options));
10631 
10632  if (chan->_state != AST_STATE_UP)
10633  ast_answer(chan);
10634 
10635  if (!ast_strlen_zero(data)) {
10636  tmp = ast_strdupa(data);
10638  if (args.argc == 2) {
10639  if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10640  return -1;
10642  if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10643  int gain;
10644 
10645  if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10646  ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10647  return -1;
10648  } else {
10649  leave_options.record_gain = (signed char) gain;
10650  }
10651  }
10652  if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10653  if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10654  leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10655  }
10656  }
10657  } else {
10658  char temp[256];
10659  res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10660  if (res < 0)
10661  return res;
10662  if (ast_strlen_zero(temp))
10663  return 0;
10664  args.argv0 = ast_strdupa(temp);
10665  }
10666 
10667  res = leave_voicemail(chan, args.argv0, &leave_options);
10668  if (res == 't') {
10669  ast_play_and_wait(chan, "vm-goodbye");
10670  res = 0;
10671  }
10672 
10673  if (res == OPERATOR_EXIT) {
10674  res = 0;
10675  }
10676 
10677  if (res == ERROR_LOCK_PATH) {
10678  ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10679  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10680  res = 0;
10681  }
10682 
10683  return res;
10684 }
10685 
10686 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10687 {
10688  struct ast_vm_user *vmu;
10689 
10690  if (!ast_strlen_zero(box) && box[0] == '*') {
10691  ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character. The '*' character,"
10692  "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10693  "\n\tpredefined extension 'a'. A mailbox or password beginning with '*' is not valid"
10694  "\n\tand will be ignored.\n", box, context);
10695  return NULL;
10696  }
10697 
10698  AST_LIST_TRAVERSE(&users, vmu, list) {
10699  if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10700  if (strcasecmp(vmu->context, context)) {
10701  ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10702  \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10703  \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10704  \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10705  }
10706  ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10707  return NULL;
10708  }
10709  if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10710  ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10711  return NULL;
10712  }
10713  }
10714 
10715  if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10716  return NULL;
10717 
10718  ast_copy_string(vmu->context, context, sizeof(vmu->context));
10719  ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10720 
10721  AST_LIST_INSERT_TAIL(&users, vmu, list);
10722 
10723  return vmu;
10724 }
10725 
10726 static int append_mailbox(const char *context, const char *box, const char *data)
10727 {
10728  /* Assumes lock is already held */
10729  char *tmp;
10730  char *stringp;
10731  char *s;
10732  struct ast_vm_user *vmu;
10733  char *mailbox_full;
10734  int new = 0, old = 0, urgent = 0;
10735  char secretfn[PATH_MAX] = "";
10736 
10737  tmp = ast_strdupa(data);
10738 
10739  if (!(vmu = find_or_create(context, box)))
10740  return -1;
10741 
10742  populate_defaults(vmu);
10743 
10744  stringp = tmp;
10745  if ((s = strsep(&stringp, ","))) {
10746  if (!ast_strlen_zero(s) && s[0] == '*') {
10747  ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
10748  "\n\tmust be reset in voicemail.conf.\n", box);
10749  }
10750  /* assign password regardless of validity to prevent NULL password from being assigned */
10751  ast_copy_string(vmu->password, s, sizeof(vmu->password));
10752  }
10753  if (stringp && (s = strsep(&stringp, ","))) {
10754  ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10755  }
10756  if (stringp && (s = strsep(&stringp, ","))) {
10757  ast_copy_string(vmu->email, s, sizeof(vmu->email));
10758  }
10759  if (stringp && (s = strsep(&stringp, ","))) {
10760  ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10761  }
10762  if (stringp && (s = strsep(&stringp, ","))) {
10763  apply_options(vmu, s);
10764  }
10765 
10766  switch (vmu->passwordlocation) {
10767  case OPT_PWLOC_SPOOLDIR:
10768  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10769  read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10770  }
10771 
10772  mailbox_full = ast_alloca(strlen(box) + strlen(context) + 1);
10773  strcpy(mailbox_full, box);
10774  strcat(mailbox_full, "@");
10775  strcat(mailbox_full, context);
10776 
10777  inboxcount2(mailbox_full, &urgent, &new, &old);
10778  queue_mwi_event(mailbox_full, urgent, new, old);
10779 
10780  return 0;
10781 }
10782 
10783 AST_TEST_DEFINE(test_voicemail_vmuser)
10784 {
10785  int res = 0;
10786  struct ast_vm_user *vmu;
10787  /* language parameter seems to only be used for display in manager action */
10788  static const char options_string[] = "attach=yes|attachfmt=wav49|"
10789  "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10790  "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10791  "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10792  "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10793  "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10794  "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10795  "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10796  "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10797 #ifdef IMAP_STORAGE
10798  static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10799  "imapfolder=INBOX|imapvmshareid=6000";
10800 #endif
10801 
10802  switch (cmd) {
10803  case TEST_INIT:
10804  info->name = "vmuser";
10805  info->category = "/apps/app_voicemail/";
10806  info->summary = "Vmuser unit test";
10807  info->description =
10808  "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10809  return AST_TEST_NOT_RUN;
10810  case TEST_EXECUTE:
10811  break;
10812  }
10813 
10814  if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10815  return AST_TEST_NOT_RUN;
10816  }
10817  populate_defaults(vmu);
10818  ast_set_flag(vmu, VM_ALLOCED);
10819 
10820  apply_options(vmu, options_string);
10821 
10822  if (!ast_test_flag(vmu, VM_ATTACH)) {
10823  ast_test_status_update(test, "Parse failure for attach option\n");
10824  res = 1;
10825  }
10826  if (strcasecmp(vmu->attachfmt, "wav49")) {
10827  ast_test_status_update(test, "Parse failure for attachftm option\n");
10828  res = 1;
10829  }
10830  if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10831  ast_test_status_update(test, "Parse failure for serveremail option\n");
10832  res = 1;
10833  }
10834  if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10835  ast_test_status_update(test, "Parse failure for emailsubject option\n");
10836  res = 1;
10837  }
10838  if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10839  ast_test_status_update(test, "Parse failure for emailbody option\n");
10840  res = 1;
10841  }
10842  if (strcasecmp(vmu->zonetag, "central")) {
10843  ast_test_status_update(test, "Parse failure for tz option\n");
10844  res = 1;
10845  }
10846  if (!ast_test_flag(vmu, VM_DELETE)) {
10847  ast_test_status_update(test, "Parse failure for delete option\n");
10848  res = 1;
10849  }
10850  if (!ast_test_flag(vmu, VM_SAYCID)) {
10851  ast_test_status_update(test, "Parse failure for saycid option\n");
10852  res = 1;
10853  }
10854  if (!ast_test_flag(vmu, VM_SVMAIL)) {
10855  ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10856  res = 1;
10857  }
10858  if (!ast_test_flag(vmu, VM_REVIEW)) {
10859  ast_test_status_update(test, "Parse failure for review option\n");
10860  res = 1;
10861  }
10862  if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10863  ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10864  res = 1;
10865  }
10866  if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10867  ast_test_status_update(test, "Parse failure for messagewrap option\n");
10868  res = 1;
10869  }
10870  if (!ast_test_flag(vmu, VM_OPERATOR)) {
10871  ast_test_status_update(test, "Parse failure for operator option\n");
10872  res = 1;
10873  }
10874  if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10875  ast_test_status_update(test, "Parse failure for envelope option\n");
10876  res = 1;
10877  }
10878  if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10879  ast_test_status_update(test, "Parse failure for moveheard option\n");
10880  res = 1;
10881  }
10882  if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10883  ast_test_status_update(test, "Parse failure for sayduration option\n");
10884  res = 1;
10885  }
10886  if (vmu->saydurationm != 5) {
10887  ast_test_status_update(test, "Parse failure for saydurationm option\n");
10888  res = 1;
10889  }
10890  if (!ast_test_flag(vmu, VM_FORCENAME)) {
10891  ast_test_status_update(test, "Parse failure for forcename option\n");
10892  res = 1;
10893  }
10894  if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10895  ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10896  res = 1;
10897  }
10898  if (strcasecmp(vmu->callback, "somecontext")) {
10899  ast_test_status_update(test, "Parse failure for callbacks option\n");
10900  res = 1;
10901  }
10902  if (strcasecmp(vmu->dialout, "somecontext2")) {
10903  ast_test_status_update(test, "Parse failure for dialout option\n");
10904  res = 1;
10905  }
10906  if (strcasecmp(vmu->exit, "somecontext3")) {
10907  ast_test_status_update(test, "Parse failure for exitcontext option\n");
10908  res = 1;
10909  }
10910  if (vmu->minsecs != 10) {
10911  ast_test_status_update(test, "Parse failure for minsecs option\n");
10912  res = 1;
10913  }
10914  if (vmu->maxsecs != 100) {
10915  ast_test_status_update(test, "Parse failure for maxsecs option\n");
10916  res = 1;
10917  }
10918  if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10919  ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10920  res = 1;
10921  }
10922  if (vmu->maxdeletedmsg != 50) {
10923  ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10924  res = 1;
10925  }
10926  if (vmu->volgain != 1.3) {
10927  ast_test_status_update(test, "Parse failure for volgain option\n");
10928  res = 1;
10929  }
10930  if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10931  ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10932  res = 1;
10933  }
10934 #ifdef IMAP_STORAGE
10935  apply_options(vmu, option_string2);
10936 
10937  if (strcasecmp(vmu->imapuser, "imapuser")) {
10938  ast_test_status_update(test, "Parse failure for imapuser option\n");
10939  res = 1;
10940  }
10941  if (strcasecmp(vmu->imappassword, "imappasswd")) {
10942  ast_test_status_update(test, "Parse failure for imappasswd option\n");
10943  res = 1;
10944  }
10945  if (strcasecmp(vmu->imapfolder, "INBOX")) {
10946  ast_test_status_update(test, "Parse failure for imapfolder option\n");
10947  res = 1;
10948  }
10949  if (strcasecmp(vmu->imapvmshareid, "6000")) {
10950  ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10951  res = 1;
10952  }
10953 #endif
10954 
10955  free_user(vmu);
10956  return res ? AST_TEST_FAIL : AST_TEST_PASS;
10957 }
10958 
10959 static int vm_box_exists(struct ast_channel *chan, const char *data)
10960 {
10961  struct ast_vm_user svm;
10962  char *context, *box;
10964  AST_APP_ARG(mbox);
10965  AST_APP_ARG(options);
10966  );
10967  static int dep_warning = 0;
10968 
10969  if (ast_strlen_zero(data)) {
10970  ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10971  return -1;
10972  }
10973 
10974  if (!dep_warning) {
10975  dep_warning = 1;
10976  ast_log(AST_LOG_WARNING, "MailboxExists is deprecated. Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10977  }
10978 
10979  box = ast_strdupa(data);
10980 
10982 
10983  if (args.options) {
10984  }
10985 
10986  if ((context = strchr(args.mbox, '@'))) {
10987  *context = '\0';
10988  context++;
10989  }
10990 
10991  if (find_user(&svm, context, args.mbox)) {
10992  pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10993  } else
10994  pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10995 
10996  return 0;
10997 }
10998 
10999 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11000 {
11001  struct ast_vm_user svm;
11003  AST_APP_ARG(mbox);
11004  AST_APP_ARG(context);
11005  );
11006 
11007  AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11008 
11009  if (ast_strlen_zero(arg.mbox)) {
11010  ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11011  return -1;
11012  }
11013 
11014  ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11015  return 0;
11016 }
11017 
11019  .name = "MAILBOX_EXISTS",
11020  .read = acf_mailbox_exists,
11021 };
11022 
11023 static int vmauthenticate(struct ast_channel *chan, const char *data)
11024 {
11025  char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11026  struct ast_vm_user vmus;
11027  char *options = NULL;
11028  int silent = 0, skipuser = 0;
11029  int res = -1;
11030 
11031  if (data) {
11032  s = ast_strdupa(data);
11033  user = strsep(&s, ",");
11034  options = strsep(&s, ",");
11035  if (user) {
11036  s = user;
11037  user = strsep(&s, "@");
11038  context = strsep(&s, "");
11039  if (!ast_strlen_zero(user))
11040  skipuser++;
11041  ast_copy_string(mailbox, user, sizeof(mailbox));
11042  }
11043  }
11044 
11045  if (options) {
11046  silent = (strchr(options, 's')) != NULL;
11047  }
11048 
11049  if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11050  pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11051  pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11052  ast_play_and_wait(chan, "auth-thankyou");
11053  res = 0;
11054  } else if (mailbox[0] == '*') {
11055  /* user entered '*' */
11056  if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11057  res = 0; /* prevent hangup */
11058  }
11059  }
11060 
11061  return res;
11062 }
11063 
11064 static char *show_users_realtime(int fd, const char *context)
11065 {
11066  struct ast_config *cfg;
11067  const char *cat = NULL;
11068 
11069  if (!(cfg = ast_load_realtime_multientry("voicemail",
11070  "context", context, SENTINEL))) {
11071  return CLI_FAILURE;
11072  }
11073 
11074  ast_cli(fd,
11075  "\n"
11076  "=============================================================\n"
11077  "=== Configured Voicemail Users ==============================\n"
11078  "=============================================================\n"
11079  "===\n");
11080 
11081  while ((cat = ast_category_browse(cfg, cat))) {
11082  struct ast_variable *var = NULL;
11083  ast_cli(fd,
11084  "=== Mailbox ...\n"
11085  "===\n");
11086  for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11087  ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11088  ast_cli(fd,
11089  "===\n"
11090  "=== ---------------------------------------------------------\n"
11091  "===\n");
11092  }
11093 
11094  ast_cli(fd,
11095  "=============================================================\n"
11096  "\n");
11097 
11098  ast_config_destroy(cfg);
11099 
11100  return CLI_SUCCESS;
11101 }
11102 
11103 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11104 {
11105  int which = 0;
11106  int wordlen;
11107  struct ast_vm_user *vmu;
11108  const char *context = "";
11109 
11110  /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11111  if (pos > 4)
11112  return NULL;
11113  if (pos == 3)
11114  return (state == 0) ? ast_strdup("for") : NULL;
11115  wordlen = strlen(word);
11116  AST_LIST_TRAVERSE(&users, vmu, list) {
11117  if (!strncasecmp(word, vmu->context, wordlen)) {
11118  if (context && strcmp(context, vmu->context) && ++which > state)
11119  return ast_strdup(vmu->context);
11120  /* ignore repeated contexts ? */
11121  context = vmu->context;
11122  }
11123  }
11124  return NULL;
11125 }
11126 
11127 /*! \brief Show a list of voicemail users in the CLI */
11128 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11129 {
11130  struct ast_vm_user *vmu;
11131 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11132  const char *context = NULL;
11133  int users_counter = 0;
11134 
11135  switch (cmd) {
11136  case CLI_INIT:
11137  e->command = "voicemail show users";
11138  e->usage =
11139  "Usage: voicemail show users [for <context>]\n"
11140  " Lists all mailboxes currently set up\n";
11141  return NULL;
11142  case CLI_GENERATE:
11143  return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11144  }
11145 
11146  if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11147  return CLI_SHOWUSAGE;
11148  if (a->argc == 5) {
11149  if (strcmp(a->argv[3],"for"))
11150  return CLI_SHOWUSAGE;
11151  context = a->argv[4];
11152  }
11153 
11154  if (ast_check_realtime("voicemail")) {
11155  if (!context) {
11156  ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11157  return CLI_SHOWUSAGE;
11158  }
11159  return show_users_realtime(a->fd, context);
11160  }
11161 
11162  AST_LIST_LOCK(&users);
11163  if (AST_LIST_EMPTY(&users)) {
11164  ast_cli(a->fd, "There are no voicemail users currently defined\n");
11166  return CLI_FAILURE;
11167  }
11168  if (!context) {
11169  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11170  } else {
11171  int count = 0;
11172  AST_LIST_TRAVERSE(&users, vmu, list) {
11173  if (!strcmp(context, vmu->context)) {
11174  count++;
11175  break;
11176  }
11177  }
11178  if (count) {
11179  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11180  } else {
11181  ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11183  return CLI_FAILURE;
11184  }
11185  }
11186  AST_LIST_TRAVERSE(&users, vmu, list) {
11187  int newmsgs = 0, oldmsgs = 0;
11188  char count[12], tmp[256] = "";
11189 
11190  if (!context || !strcmp(context, vmu->context)) {
11191  snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11192  inboxcount(tmp, &newmsgs, &oldmsgs);
11193  snprintf(count, sizeof(count), "%d", newmsgs);
11194  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11195  users_counter++;
11196  }
11197  }
11199  ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11200  return CLI_SUCCESS;
11201 }
11202 
11203 /*! \brief Show a list of voicemail zones in the CLI */
11204 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11205 {
11206  struct vm_zone *zone;
11207 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11208  char *res = CLI_SUCCESS;
11209 
11210  switch (cmd) {
11211  case CLI_INIT:
11212  e->command = "voicemail show zones";
11213  e->usage =
11214  "Usage: voicemail show zones\n"
11215  " Lists zone message formats\n";
11216  return NULL;
11217  case CLI_GENERATE:
11218  return NULL;
11219  }
11220 
11221  if (a->argc != 3)
11222  return CLI_SHOWUSAGE;
11223 
11224  AST_LIST_LOCK(&zones);
11225  if (!AST_LIST_EMPTY(&zones)) {
11226  ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11227  AST_LIST_TRAVERSE(&zones, zone, list) {
11228  ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11229  }
11230  } else {
11231  ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11232  res = CLI_FAILURE;
11233  }
11235 
11236  return res;
11237 }
11238 
11239 /*! \brief Reload voicemail configuration from the CLI */
11240 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11241 {
11242  switch (cmd) {
11243  case CLI_INIT:
11244  e->command = "voicemail reload";
11245  e->usage =
11246  "Usage: voicemail reload\n"
11247  " Reload voicemail configuration\n";
11248  return NULL;
11249  case CLI_GENERATE:
11250  return NULL;
11251  }
11252 
11253  if (a->argc != 2)
11254  return CLI_SHOWUSAGE;
11255 
11256  ast_cli(a->fd, "Reloading voicemail configuration...\n");
11257  load_config(1);
11258 
11259  return CLI_SUCCESS;
11260 }
11261 
11262 static struct ast_cli_entry cli_voicemail[] = {
11263  AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11264  AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11265  AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11266 };
11267 
11268 #ifdef IMAP_STORAGE
11269  #define DATA_EXPORT_VM_USERS(USER) \
11270  USER(ast_vm_user, context, AST_DATA_STRING) \
11271  USER(ast_vm_user, mailbox, AST_DATA_STRING) \
11272  USER(ast_vm_user, password, AST_DATA_PASSWORD) \
11273  USER(ast_vm_user, fullname, AST_DATA_STRING) \
11274  USER(ast_vm_user, email, AST_DATA_STRING) \
11275  USER(ast_vm_user, emailsubject, AST_DATA_STRING) \
11276  USER(ast_vm_user, emailbody, AST_DATA_STRING) \
11277  USER(ast_vm_user, pager, AST_DATA_STRING) \
11278  USER(ast_vm_user, serveremail, AST_DATA_STRING) \
11279  USER(ast_vm_user, language, AST_DATA_STRING) \
11280  USER(ast_vm_user, zonetag, AST_DATA_STRING) \
11281  USER(ast_vm_user, callback, AST_DATA_STRING) \
11282  USER(ast_vm_user, dialout, AST_DATA_STRING) \
11283  USER(ast_vm_user, uniqueid, AST_DATA_STRING) \
11284  USER(ast_vm_user, exit, AST_DATA_STRING) \
11285  USER(ast_vm_user, attachfmt, AST_DATA_STRING) \
11286  USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER) \
11287  USER(ast_vm_user, saydurationm, AST_DATA_INTEGER) \
11288  USER(ast_vm_user, maxmsg, AST_DATA_INTEGER) \
11289  USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER) \
11290  USER(ast_vm_user, maxsecs, AST_DATA_INTEGER) \
11291  USER(ast_vm_user, imapuser, AST_DATA_STRING) \
11292  USER(ast_vm_user, imappassword, AST_DATA_STRING) \
11293  USER(ast_vm_user, imapvmshareid, AST_DATA_STRING) \
11294  USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11295 #else
11296  #define DATA_EXPORT_VM_USERS(USER) \
11297  USER(ast_vm_user, context, AST_DATA_STRING) \
11298  USER(ast_vm_user, mailbox, AST_DATA_STRING) \
11299  USER(ast_vm_user, password, AST_DATA_PASSWORD) \
11300  USER(ast_vm_user, fullname, AST_DATA_STRING) \
11301  USER(ast_vm_user, email, AST_DATA_STRING) \
11302  USER(ast_vm_user, emailsubject, AST_DATA_STRING) \
11303  USER(ast_vm_user, emailbody, AST_DATA_STRING) \
11304  USER(ast_vm_user, pager, AST_DATA_STRING) \
11305  USER(ast_vm_user, serveremail, AST_DATA_STRING) \
11306  USER(ast_vm_user, language, AST_DATA_STRING) \
11307  USER(ast_vm_user, zonetag, AST_DATA_STRING) \
11308  USER(ast_vm_user, callback, AST_DATA_STRING) \
11309  USER(ast_vm_user, dialout, AST_DATA_STRING) \
11310  USER(ast_vm_user, uniqueid, AST_DATA_STRING) \
11311  USER(ast_vm_user, exit, AST_DATA_STRING) \
11312  USER(ast_vm_user, attachfmt, AST_DATA_STRING) \
11313  USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER) \
11314  USER(ast_vm_user, saydurationm, AST_DATA_INTEGER) \
11315  USER(ast_vm_user, maxmsg, AST_DATA_INTEGER) \
11316  USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER) \
11317  USER(ast_vm_user, maxsecs, AST_DATA_INTEGER) \
11318  USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11319 #endif
11320 
11322 
11323 #define DATA_EXPORT_VM_ZONES(ZONE) \
11324  ZONE(vm_zone, name, AST_DATA_STRING) \
11325  ZONE(vm_zone, timezone, AST_DATA_STRING) \
11326  ZONE(vm_zone, msg_format, AST_DATA_STRING)
11327 
11329 
11330 /*!
11331  * \internal
11332  * \brief Add voicemail user to the data_root.
11333  * \param[in] search The search tree.
11334  * \param[in] data_root The main result node.
11335  * \param[in] user The voicemail user.
11336  */
11337 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11338  struct ast_data *data_root, struct ast_vm_user *user)
11339 {
11340  struct ast_data *data_user, *data_zone;
11341  struct ast_data *data_state;
11342  struct vm_zone *zone = NULL;
11343  int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11344  char ext_context[256] = "";
11345 
11346  data_user = ast_data_add_node(data_root, "user");
11347  if (!data_user) {
11348  return -1;
11349  }
11350 
11351  ast_data_add_structure(ast_vm_user, data_user, user);
11352 
11353  AST_LIST_LOCK(&zones);
11354  AST_LIST_TRAVERSE(&zones, zone, list) {
11355  if (!strcmp(zone->name, user->zonetag)) {
11356  break;
11357  }
11358  }
11360 
11361  /* state */
11362  data_state = ast_data_add_node(data_user, "state");
11363  if (!data_state) {
11364  return -1;
11365  }
11366  snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11367  inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11368  ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11369  ast_data_add_int(data_state, "newmsg", newmsg);
11370  ast_data_add_int(data_state, "oldmsg", oldmsg);
11371 
11372  if (zone) {
11373  data_zone = ast_data_add_node(data_user, "zone");
11374  ast_data_add_structure(vm_zone, data_zone, zone);
11375  }
11376 
11377  if (!ast_data_search_match(search, data_user)) {
11378  ast_data_remove_node(data_root, data_user);
11379  }
11380 
11381  return 0;
11382 }
11383 
11384 static int vm_users_data_provider_get(const struct ast_data_search *search,
11385  struct ast_data *data_root)
11386 {
11387  struct ast_vm_user *user;
11388 
11389  AST_LIST_LOCK(&users);
11390  AST_LIST_TRAVERSE(&users, user, list) {
11391  vm_users_data_provider_get_helper(search, data_root, user);
11392  }
11394 
11395  return 0;
11396 }
11397 
11401 };
11402 
11403 static const struct ast_data_entry vm_data_providers[] = {
11404  AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11405 };
11406 
11408 {
11409  int new = 0, old = 0, urgent = 0;
11410 
11411  inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11412 
11413  if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11414  mwi_sub->old_urgent = urgent;
11415  mwi_sub->old_new = new;
11416  mwi_sub->old_old = old;
11417  queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11418  run_externnotify(NULL, mwi_sub->mailbox, NULL);
11419  }
11420 }
11421 
11422 static void poll_subscribed_mailboxes(void)
11423 {
11424  struct mwi_sub *mwi_sub;
11425 
11427  AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11428  if (!ast_strlen_zero(mwi_sub->mailbox)) {
11429  poll_subscribed_mailbox(mwi_sub);
11430  }
11431  }
11433 }
11434 
11435 static void *mb_poll_thread(void *data)
11436 {
11437  while (poll_thread_run) {
11438  struct timespec ts = { 0, };
11439  struct timeval wait;
11440 
11441  wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11442  ts.tv_sec = wait.tv_sec;
11443  ts.tv_nsec = wait.tv_usec * 1000;
11444 
11446  ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11448 
11449  if (!poll_thread_run)
11450  break;
11451 
11453  }
11454 
11455  return NULL;
11456 }
11457 
11458 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11459 {
11460  ast_free(mwi_sub);
11461 }
11462 
11463 static int handle_unsubscribe(void *datap)
11464 {
11465  struct mwi_sub *mwi_sub;
11466  uint32_t *uniqueid = datap;
11467 
11469  AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11470  if (mwi_sub->uniqueid == *uniqueid) {
11471  AST_LIST_REMOVE_CURRENT(entry);
11472  break;
11473  }
11474  }
11477 
11478  if (mwi_sub)
11479  mwi_sub_destroy(mwi_sub);
11480 
11481  ast_free(uniqueid);
11482  return 0;
11483 }
11484 
11485 static int handle_subscribe(void *datap)
11486 {
11487  unsigned int len;
11488  struct mwi_sub *mwi_sub;
11489  struct mwi_sub_task *p = datap;
11490 
11491  len = sizeof(*mwi_sub);
11492  if (!ast_strlen_zero(p->mailbox))
11493  len += strlen(p->mailbox);
11494 
11495  if (!ast_strlen_zero(p->context))
11496  len += strlen(p->context) + 1; /* Allow for seperator */
11497 
11498  if (!(mwi_sub = ast_calloc(1, len)))
11499  return -1;
11500 
11501  mwi_sub->uniqueid = p->uniqueid;
11502  if (!ast_strlen_zero(p->mailbox))
11503  strcpy(mwi_sub->mailbox, p->mailbox);
11504 
11505  if (!ast_strlen_zero(p->context)) {
11506  strcat(mwi_sub->mailbox, "@");
11507  strcat(mwi_sub->mailbox, p->context);
11508  }
11509 
11511  AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11513  ast_free((void *) p->mailbox);
11514  ast_free((void *) p->context);
11515  ast_free(p);
11516  poll_subscribed_mailbox(mwi_sub);
11517  return 0;
11518 }
11519 
11520 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11521 {
11522  uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11523 
11524  if (!uniqueid) {
11525  ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11526  return;
11527  }
11528 
11529  if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11530  ast_free(uniqueid);
11531  return;
11532  }
11533 
11535  ast_free(uniqueid);
11536  return;
11537  }
11538 
11540  *uniqueid = u;
11541  if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11542  ast_free(uniqueid);
11543  }
11544 }
11545 
11546 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11547 {
11548  struct mwi_sub_task *mwist;
11549 
11550  if (ast_event_get_type(event) != AST_EVENT_SUB)
11551  return;
11552 
11554  return;
11555 
11556  if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11557  ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11558  return;
11559  }
11563 
11564  if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11565  ast_free(mwist);
11566  }
11567 }
11568 
11569 static void start_poll_thread(void)
11570 {
11571  int errcode;
11572  mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11575 
11576  mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11579 
11580  if (mwi_sub_sub)
11581  ast_event_report_subs(mwi_sub_sub);
11582 
11583  poll_thread_run = 1;
11584 
11585  if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11586  ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11587  }
11588 }
11589 
11590 static void stop_poll_thread(void)
11591 {
11592  poll_thread_run = 0;
11593 
11594  if (mwi_sub_sub) {
11595  ast_event_unsubscribe(mwi_sub_sub);
11596  mwi_sub_sub = NULL;
11597  }
11598 
11599  if (mwi_unsub_sub) {
11600  ast_event_unsubscribe(mwi_unsub_sub);
11601  mwi_unsub_sub = NULL;
11602  }
11603 
11605  ast_cond_signal(&poll_cond);
11607 
11608  pthread_join(poll_thread, NULL);
11609 
11610  poll_thread = AST_PTHREADT_NULL;
11611 }
11612 
11613 /*! \brief Manager list voicemail users command */
11614 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11615 {
11616  struct ast_vm_user *vmu = NULL;
11617  const char *id = astman_get_header(m, "ActionID");
11618  char actionid[128] = "";
11619 
11620  if (!ast_strlen_zero(id))
11621  snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11622 
11623  AST_LIST_LOCK(&users);
11624 
11625  if (AST_LIST_EMPTY(&users)) {
11626  astman_send_ack(s, m, "There are no voicemail users currently defined.");
11628  return RESULT_SUCCESS;
11629  }
11630 
11631  astman_send_ack(s, m, "Voicemail user list will follow");
11632 
11633  AST_LIST_TRAVERSE(&users, vmu, list) {
11634  char dirname[256];
11635 
11636 #ifdef IMAP_STORAGE
11637  int new, old;
11638  inboxcount(vmu->mailbox, &new, &old);
11639 #endif
11640 
11641  make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11642  astman_append(s,
11643  "%s"
11644  "Event: VoicemailUserEntry\r\n"
11645  "VMContext: %s\r\n"
11646  "VoiceMailbox: %s\r\n"
11647  "Fullname: %s\r\n"
11648  "Email: %s\r\n"
11649  "Pager: %s\r\n"
11650  "ServerEmail: %s\r\n"
11651  "MailCommand: %s\r\n"
11652  "Language: %s\r\n"
11653  "TimeZone: %s\r\n"
11654  "Callback: %s\r\n"
11655  "Dialout: %s\r\n"
11656  "UniqueID: %s\r\n"
11657  "ExitContext: %s\r\n"
11658  "SayDurationMinimum: %d\r\n"
11659  "SayEnvelope: %s\r\n"
11660  "SayCID: %s\r\n"
11661  "AttachMessage: %s\r\n"
11662  "AttachmentFormat: %s\r\n"
11663  "DeleteMessage: %s\r\n"
11664  "VolumeGain: %.2f\r\n"
11665  "CanReview: %s\r\n"
11666  "CallOperator: %s\r\n"
11667  "MaxMessageCount: %d\r\n"
11668  "MaxMessageLength: %d\r\n"
11669  "NewMessageCount: %d\r\n"
11670 #ifdef IMAP_STORAGE
11671  "OldMessageCount: %d\r\n"
11672  "IMAPUser: %s\r\n"
11673 #endif
11674  "\r\n",
11675  actionid,
11676  vmu->context,
11677  vmu->mailbox,
11678  vmu->fullname,
11679  vmu->email,
11680  vmu->pager,
11681  ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
11682  mailcmd,
11683  vmu->language,
11684  vmu->zonetag,
11685  vmu->callback,
11686  vmu->dialout,
11687  vmu->uniqueid,
11688  vmu->exit,
11689  vmu->saydurationm,
11690  ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11691  ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11692  ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11693  vmu->attachfmt,
11694  ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11695  vmu->volgain,
11696  ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11697  ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11698  vmu->maxmsg,
11699  vmu->maxsecs,
11700 #ifdef IMAP_STORAGE
11701  new, old, vmu->imapuser
11702 #else
11703  count_messages(vmu, dirname)
11704 #endif
11705  );
11706  }
11707  astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11708 
11710 
11711  return RESULT_SUCCESS;
11712 }
11713 
11714 /*! \brief Free the users structure. */
11715 static void free_vm_users(void)
11716 {
11717  struct ast_vm_user *current;
11718  AST_LIST_LOCK(&users);
11719  while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11720  ast_set_flag(current, VM_ALLOCED);
11721  free_user(current);
11722  }
11724 }
11725 
11726 /*! \brief Free the zones structure. */
11727 static void free_vm_zones(void)
11728 {
11729  struct vm_zone *zcur;
11730  AST_LIST_LOCK(&zones);
11731  while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11732  free_zone(zcur);
11734 }
11735 
11736 static const char *substitute_escapes(const char *value)
11737 {
11738  char *current;
11739 
11740  /* Add 16 for fudge factor */
11741  struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11742 
11743  ast_str_reset(str);
11744 
11745  /* Substitute strings \r, \n, and \t into the appropriate characters */
11746  for (current = (char *) value; *current; current++) {
11747  if (*current == '\\') {
11748  current++;
11749  if (!*current) {
11750  ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11751  break;
11752  }
11753  switch (*current) {
11754  case '\\':
11755  ast_str_append(&str, 0, "\\");
11756  break;
11757  case 'r':
11758  ast_str_append(&str, 0, "\r");
11759  break;
11760  case 'n':
11761 #ifdef IMAP_STORAGE
11762  if (!str->used || str->str[str->used - 1] != '\r') {
11763  ast_str_append(&str, 0, "\r");
11764  }
11765 #endif
11766  ast_str_append(&str, 0, "\n");
11767  break;
11768  case 't':
11769  ast_str_append(&str, 0, "\t");
11770  break;
11771  default:
11772  ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11773  break;
11774  }
11775  } else {
11776  ast_str_append(&str, 0, "%c", *current);
11777  }
11778  }
11779 
11780  return ast_str_buffer(str);
11781 }
11782 
11783 static int load_config(int reload)
11784 {
11785  struct ast_config *cfg, *ucfg;
11786  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11787  int res;
11788 
11789  ast_unload_realtime("voicemail");
11790  ast_unload_realtime("voicemail_data");
11791 
11792  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11793  if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11794  return 0;
11795  } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11796  ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
11797  ucfg = NULL;
11798  }
11799  ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11800  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11801  ast_config_destroy(ucfg);
11802  ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
11803  return 0;
11804  }
11805  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11806  ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
11807  return 0;
11808  } else {
11809  ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11810  if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11811  ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
11812  ucfg = NULL;
11813  }
11814  }
11815 
11816  res = actual_load_config(reload, cfg, ucfg);
11817 
11818  ast_config_destroy(cfg);
11819  ast_config_destroy(ucfg);
11820 
11821  return res;
11822 }
11823 
11824 #ifdef TEST_FRAMEWORK
11825 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11826 {
11827  ast_unload_realtime("voicemail");
11828  ast_unload_realtime("voicemail_data");
11829  return actual_load_config(reload, cfg, ucfg);
11830 }
11831 #endif
11832 
11833 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11834 {
11835  struct ast_vm_user *current;
11836  char *cat;
11837  struct ast_variable *var;
11838  const char *val;
11839  char *q, *stringp, *tmp;
11840  int x;
11841  unsigned int tmpadsi[4];
11842  char secretfn[PATH_MAX] = "";
11843 
11844 #ifdef IMAP_STORAGE
11845  ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11846 #endif
11847  /* set audio control prompts */
11848  strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11849  strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11850  strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11851  strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11852  strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11853 
11854  /* Free all the users structure */
11855  free_vm_users();
11856 
11857  /* Free all the zones structure */
11858  free_vm_zones();
11859 
11860  AST_LIST_LOCK(&users);
11861 
11862  memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11863  memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11864 
11865  if (cfg) {
11866  /* General settings */
11867 
11868  if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11869  val = "default";
11870  ast_copy_string(userscontext, val, sizeof(userscontext));
11871  /* Attach voice message to mail message ? */
11872  if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
11873  val = "yes";
11874  ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH);
11875 
11876  if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11877  val = "no";
11878  ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11879 
11880  volgain = 0.0;
11881  if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11882  sscanf(val, "%30lf", &volgain);
11883 
11884 #ifdef ODBC_STORAGE
11885  strcpy(odbc_database, "asterisk");
11886  if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11887  ast_copy_string(odbc_database, val, sizeof(odbc_database));
11888  }
11889  strcpy(odbc_table, "voicemessages");
11890  if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11891  ast_copy_string(odbc_table, val, sizeof(odbc_table));
11892  }
11893 #endif
11894  /* Mail command */
11895  strcpy(mailcmd, SENDMAIL);
11896  if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11897  ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11898 
11899  maxsilence = 0;
11900  if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11901  maxsilence = atoi(val);
11902  if (maxsilence > 0)
11903  maxsilence *= 1000;
11904  }
11905 
11906  if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11907  maxmsg = MAXMSG;
11908  } else {
11909  maxmsg = atoi(val);
11910  if (maxmsg < 0) {
11911  ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11912  maxmsg = MAXMSG;
11913  } else if (maxmsg > MAXMSGLIMIT) {
11914  ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11915  maxmsg = MAXMSGLIMIT;
11916  }
11917  }
11918 
11919  if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11920  maxdeletedmsg = 0;
11921  } else {
11922  if (sscanf(val, "%30d", &x) == 1)
11923  maxdeletedmsg = x;
11924  else if (ast_true(val))
11925  maxdeletedmsg = MAXMSG;
11926  else
11927  maxdeletedmsg = 0;
11928 
11929  if (maxdeletedmsg < 0) {
11930  ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11931  maxdeletedmsg = MAXMSG;
11932  } else if (maxdeletedmsg > MAXMSGLIMIT) {
11933  ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11934  maxdeletedmsg = MAXMSGLIMIT;
11935  }
11936  }
11937 
11938  /* Load date format config for voicemail mail */
11939  if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11940  ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11941  }
11942 
11943  /* Load date format config for voicemail pager mail */
11944  if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11945  ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11946  }
11947 
11948  /* External password changing command */
11949  if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11950  ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11951  pwdchange = PWDCHANGE_EXTERNAL;
11952  } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11953  ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11954  pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11955  }
11956 
11957  /* External password validation command */
11958  if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11959  ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11960  ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11961  }
11962 
11963 #ifdef IMAP_STORAGE
11964  /* IMAP server address */
11965  if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11966  ast_copy_string(imapserver, val, sizeof(imapserver));
11967  } else {
11968  ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11969  }
11970  /* IMAP server port */
11971  if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11972  ast_copy_string(imapport, val, sizeof(imapport));
11973  } else {
11974  ast_copy_string(imapport, "143", sizeof(imapport));
11975  }
11976  /* IMAP server flags */
11977  if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11978  ast_copy_string(imapflags, val, sizeof(imapflags));
11979  }
11980  /* IMAP server master username */
11981  if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11982  ast_copy_string(authuser, val, sizeof(authuser));
11983  }
11984  /* IMAP server master password */
11985  if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11986  ast_copy_string(authpassword, val, sizeof(authpassword));
11987  }
11988  /* Expunge on exit */
11989  if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11990  if (ast_false(val))
11991  expungeonhangup = 0;
11992  else
11993  expungeonhangup = 1;
11994  } else {
11995  expungeonhangup = 1;
11996  }
11997  /* IMAP voicemail folder */
11998  if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11999  ast_copy_string(imapfolder, val, sizeof(imapfolder));
12000  } else {
12001  ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12002  }
12003  if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12004  ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12005  }
12006  if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12007  imapgreetings = ast_true(val);
12008  } else {
12009  imapgreetings = 0;
12010  }
12011  if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12012  ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12013  } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12014  /* Also support greetingsfolder as documented in voicemail.conf.sample */
12015  ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12016  } else {
12017  ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12018  }
12019 
12020  /* There is some very unorthodox casting done here. This is due
12021  * to the way c-client handles the argument passed in. It expects a
12022  * void pointer and casts the pointer directly to a long without
12023  * first dereferencing it. */
12024  if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12025  mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12026  } else {
12027  mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12028  }
12029 
12030  if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12031  mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12032  } else {
12033  mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12034  }
12035 
12036  if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12037  mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12038  } else {
12039  mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12040  }
12041 
12042  if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12043  mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12044  } else {
12045  mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12046  }
12047 
12048  /* Increment configuration version */
12049  imapversion++;
12050 #endif
12051  /* External voicemail notify application */
12052  if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12053  ast_copy_string(externnotify, val, sizeof(externnotify));
12054  ast_debug(1, "found externnotify: %s\n", externnotify);
12055  } else {
12056  externnotify[0] = '\0';
12057  }
12058 
12059  /* SMDI voicemail notification */
12060  if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12061  ast_debug(1, "Enabled SMDI voicemail notification\n");
12062  if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12063  smdi_iface = ast_smdi_interface_find(val);
12064  } else {
12065  ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12066  smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12067  }
12068  if (!smdi_iface) {
12069  ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12070  }
12071  }
12072 
12073  /* Silence treshold */
12075  if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12076  silencethreshold = atoi(val);
12077 
12078  if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
12079  val = ASTERISK_USERNAME;
12080  ast_copy_string(serveremail, val, sizeof(serveremail));
12081 
12082  vmmaxsecs = 0;
12083  if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12084  if (sscanf(val, "%30d", &x) == 1) {
12085  vmmaxsecs = x;
12086  } else {
12087  ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12088  }
12089  } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12090  static int maxmessage_deprecate = 0;
12091  if (maxmessage_deprecate == 0) {
12092  maxmessage_deprecate = 1;
12093  ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12094  }
12095  if (sscanf(val, "%30d", &x) == 1) {
12096  vmmaxsecs = x;
12097  } else {
12098  ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12099  }
12100  }
12101 
12102  vmminsecs = 0;
12103  if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12104  if (sscanf(val, "%30d", &x) == 1) {
12105  vmminsecs = x;
12106  if (maxsilence / 1000 >= vmminsecs) {
12107  ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12108  }
12109  } else {
12110  ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12111  }
12112  } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12113  static int maxmessage_deprecate = 0;
12114  if (maxmessage_deprecate == 0) {
12115  maxmessage_deprecate = 1;
12116  ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12117  }
12118  if (sscanf(val, "%30d", &x) == 1) {
12119  vmminsecs = x;
12120  if (maxsilence / 1000 >= vmminsecs) {
12121  ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12122  }
12123  } else {
12124  ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12125  }
12126  }
12127 
12128  val = ast_variable_retrieve(cfg, "general", "format");
12129  if (!val) {
12130  val = "wav";
12131  } else {
12132  tmp = ast_strdupa(val);
12133  val = ast_format_str_reduce(tmp);
12134  if (!val) {
12135  ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12136  val = "wav";
12137  }
12138  }
12139  ast_copy_string(vmfmts, val, sizeof(vmfmts));
12140 
12141  skipms = 3000;
12142  if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12143  if (sscanf(val, "%30d", &x) == 1) {
12144  maxgreet = x;
12145  } else {
12146  ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12147  }
12148  }
12149 
12150  if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12151  if (sscanf(val, "%30d", &x) == 1) {
12152  skipms = x;
12153  } else {
12154  ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12155  }
12156  }
12157 
12158  maxlogins = 3;
12159  if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12160  if (sscanf(val, "%30d", &x) == 1) {
12161  maxlogins = x;
12162  } else {
12163  ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12164  }
12165  }
12166 
12167  minpassword = MINPASSWORD;
12168  if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12169  if (sscanf(val, "%30d", &x) == 1) {
12170  minpassword = x;
12171  } else {
12172  ast_log(AST_LOG_WARNING, "Invalid minimum password length. Default to %d\n", minpassword);
12173  }
12174  }
12175 
12176  /* Force new user to record name ? */
12177  if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
12178  val = "no";
12179  ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12180 
12181  /* Force new user to record greetings ? */
12182  if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
12183  val = "no";
12184  ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12185 
12186  if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12187  ast_debug(1, "VM_CID Internal context string: %s\n", val);
12188  stringp = ast_strdupa(val);
12189  for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12190  if (!ast_strlen_zero(stringp)) {
12191  q = strsep(&stringp, ",");
12192  while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12193  q++;
12194  ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12195  ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12196  } else {
12197  cidinternalcontexts[x][0] = '\0';
12198  }
12199  }
12200  }
12201  if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12202  ast_debug(1, "VM Review Option disabled globally\n");
12203  val = "no";
12204  }
12205  ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW);
12206 
12207  /* Temporary greeting reminder */
12208  if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12209  ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12210  val = "no";
12211  } else {
12212  ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12213  }
12214  ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12215  if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12216  ast_debug(1, "VM next message wrap disabled globally\n");
12217  val = "no";
12218  }
12219  ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);
12220 
12221  if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12222  ast_debug(1, "VM Operator break disabled globally\n");
12223  val = "no";
12224  }
12225  ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);
12226 
12227  if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12228  ast_debug(1, "VM CID Info before msg disabled globally\n");
12229  val = "no";
12230  }
12231  ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID);
12232 
12233  if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12234  ast_debug(1, "Send Voicemail msg disabled globally\n");
12235  val = "no";
12236  }
12237  ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12238 
12239  if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12240  ast_debug(1, "ENVELOPE before msg enabled globally\n");
12241  val = "yes";
12242  }
12243  ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);
12244 
12245  if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12246  ast_debug(1, "Move Heard enabled globally\n");
12247  val = "yes";
12248  }
12249  ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD);
12250 
12251  if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12252  ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12253  val = "no";
12254  }
12255  ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);
12256 
12257  if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12258  ast_debug(1, "Duration info before msg enabled globally\n");
12259  val = "yes";
12260  }
12261  ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);
12262 
12263  saydurationminfo = 2;
12264  if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12265  if (sscanf(val, "%30d", &x) == 1) {
12266  saydurationminfo = x;
12267  } else {
12268  ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12269  }
12270  }
12271 
12272  if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12273  ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12274  val = "no";
12275  }
12276  ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12277 
12278  if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12279  ast_copy_string(dialcontext, val, sizeof(dialcontext));
12280  ast_debug(1, "found dialout context: %s\n", dialcontext);
12281  } else {
12282  dialcontext[0] = '\0';
12283  }
12284 
12285  if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12286  ast_copy_string(callcontext, val, sizeof(callcontext));
12287  ast_debug(1, "found callback context: %s\n", callcontext);
12288  } else {
12289  callcontext[0] = '\0';
12290  }
12291 
12292  if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12293  ast_copy_string(exitcontext, val, sizeof(exitcontext));
12294  ast_debug(1, "found operator context: %s\n", exitcontext);
12295  } else {
12296  exitcontext[0] = '\0';
12297  }
12298 
12299  /* load password sounds configuration */
12300  if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12301  ast_copy_string(vm_password, val, sizeof(vm_password));
12302  if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12303  ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12304  if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12305  ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12306  if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12307  ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12308  if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12309  ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12310  if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12311  ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12312  if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12313  ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12314  }
12315  if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12316  ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12317  }
12318  /* load configurable audio prompts */
12319  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12320  ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12321  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12322  ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12323  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12324  ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12325  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12326  ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12327  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12328  ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12329 
12330  if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
12331  val = "no";
12332  ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD);
12333 
12334  if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12335  val = "voicemail.conf";
12336  }
12337  if (!(strcmp(val, "spooldir"))) {
12338  passwordlocation = OPT_PWLOC_SPOOLDIR;
12339  } else {
12340  passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12341  }
12342 
12343  poll_freq = DEFAULT_POLL_FREQ;
12344  if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12345  if (sscanf(val, "%30u", &poll_freq) != 1) {
12346  poll_freq = DEFAULT_POLL_FREQ;
12347  ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12348  }
12349  }
12350 
12351  poll_mailboxes = 0;
12352  if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12353  poll_mailboxes = ast_true(val);
12354 
12355  memset(fromstring, 0, sizeof(fromstring));
12356  memset(pagerfromstring, 0, sizeof(pagerfromstring));
12357  strcpy(charset, "ISO-8859-1");
12358  if (emailbody) {
12359  ast_free(emailbody);
12360  emailbody = NULL;
12361  }
12362  if (emailsubject) {
12363  ast_free(emailsubject);
12364  emailsubject = NULL;
12365  }
12366  if (pagerbody) {
12367  ast_free(pagerbody);
12368  pagerbody = NULL;
12369  }
12370  if (pagersubject) {
12371  ast_free(pagersubject);
12372  pagersubject = NULL;
12373  }
12374  if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12375  ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12376  if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12377  ast_copy_string(fromstring, val, sizeof(fromstring));
12378  if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12379  ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12380  if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12381  ast_copy_string(charset, val, sizeof(charset));
12382  if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12383  sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12384  for (x = 0; x < 4; x++) {
12385  memcpy(&adsifdn[x], &tmpadsi[x], 1);
12386  }
12387  }
12388  if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12389  sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12390  for (x = 0; x < 4; x++) {
12391  memcpy(&adsisec[x], &tmpadsi[x], 1);
12392  }
12393  }
12394  if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12395  if (atoi(val)) {
12396  adsiver = atoi(val);
12397  }
12398  }
12399  if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12400  ast_copy_string(zonetag, val, sizeof(zonetag));
12401  }
12402  if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12403  ast_copy_string(locale, val, sizeof(locale));
12404  }
12405  if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12406  emailsubject = ast_strdup(substitute_escapes(val));
12407  }
12408  if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12409  emailbody = ast_strdup(substitute_escapes(val));
12410  }
12411  if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12412  pagersubject = ast_strdup(substitute_escapes(val));
12413  }
12414  if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12415  pagerbody = ast_strdup(substitute_escapes(val));
12416  }
12417 
12418  /* load mailboxes from users.conf */
12419  if (ucfg) {
12420  for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12421  if (!strcasecmp(cat, "general")) {
12422  continue;
12423  }
12424  if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12425  continue;
12426  if ((current = find_or_create(userscontext, cat))) {
12427  populate_defaults(current);
12428  apply_options_full(current, ast_variable_browse(ucfg, cat));
12429  ast_copy_string(current->context, userscontext, sizeof(current->context));
12430  if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12432  }
12433 
12434  switch (current->passwordlocation) {
12435  case OPT_PWLOC_SPOOLDIR:
12436  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12437  read_password_from_file(secretfn, current->password, sizeof(current->password));
12438  }
12439  }
12440  }
12441  }
12442 
12443  /* load mailboxes from voicemail.conf */
12444  cat = ast_category_browse(cfg, NULL);
12445  while (cat) {
12446  if (strcasecmp(cat, "general")) {
12447  var = ast_variable_browse(cfg, cat);
12448  if (strcasecmp(cat, "zonemessages")) {
12449  /* Process mailboxes in this context */
12450  while (var) {
12451  append_mailbox(cat, var->name, var->value);
12452  var = var->next;
12453  }
12454  } else {
12455  /* Timezones in this context */
12456  while (var) {
12457  struct vm_zone *z;
12458  if ((z = ast_malloc(sizeof(*z)))) {
12459  char *msg_format, *tzone;
12460  msg_format = ast_strdupa(var->value);
12461  tzone = strsep(&msg_format, "|,");
12462  if (msg_format) {
12463  ast_copy_string(z->name, var->name, sizeof(z->name));
12464  ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12465  ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12466  AST_LIST_LOCK(&zones);
12467  AST_LIST_INSERT_HEAD(&zones, z, list);
12469  } else {
12470  ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12471  ast_free(z);
12472  }
12473  } else {
12475  return -1;
12476  }
12477  var = var->next;
12478  }
12479  }
12480  }
12481  cat = ast_category_browse(cfg, cat);
12482  }
12483 
12485 
12486  if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12488  if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12489  stop_poll_thread();;
12490 
12491  return 0;
12492  } else {
12494  ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12495  return 0;
12496  }
12497 }
12498 
12499 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12500 {
12501  int res = -1;
12502  char dir[PATH_MAX];
12503  snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12504  ast_debug(2, "About to try retrieving name file %s\n", dir);
12505  RETRIEVE(dir, -1, mailbox, context);
12506  if (ast_fileexists(dir, NULL, NULL)) {
12507  res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12508  }
12509  DISPOSE(dir, -1);
12510  return res;
12511 }
12512 
12513 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12514  struct ast_config *pwconf;
12515  struct ast_flags config_flags = { 0 };
12516 
12517  pwconf = ast_config_load(secretfn, config_flags);
12518  if (valid_config(pwconf)) {
12519  const char *val = ast_variable_retrieve(pwconf, "general", "password");
12520  if (val) {
12521  ast_copy_string(password, val, passwordlen);
12522  ast_config_destroy(pwconf);
12523  return;
12524  }
12525  ast_config_destroy(pwconf);
12526  }
12527  ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12528 }
12529 
12530 static int write_password_to_file(const char *secretfn, const char *password) {
12531  struct ast_config *conf;
12532  struct ast_category *cat;
12533  struct ast_variable *var;
12534  int res = -1;
12535 
12536  if (!(conf = ast_config_new())) {
12537  ast_log(LOG_ERROR, "Error creating new config structure\n");
12538  return res;
12539  }
12540  if (!(cat = ast_category_new("general", "", 1))) {
12541  ast_log(LOG_ERROR, "Error creating new category structure\n");
12542  ast_config_destroy(conf);
12543  return res;
12544  }
12545  if (!(var = ast_variable_new("password", password, ""))) {
12546  ast_log(LOG_ERROR, "Error creating new variable structure\n");
12547  ast_config_destroy(conf);
12548  ast_category_destroy(cat);
12549  return res;
12550  }
12551  ast_category_append(conf, cat);
12552  ast_variable_append(cat, var);
12553  if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12554  res = 0;
12555  } else {
12556  ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12557  }
12558 
12559  ast_config_destroy(conf);
12560  return res;
12561 }
12562 
12563 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12564 {
12565  char *context;
12566  char *args_copy;
12567  int res;
12568 
12569  if (ast_strlen_zero(data)) {
12570  ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12571  return -1;
12572  }
12573 
12574  args_copy = ast_strdupa(data);
12575  if ((context = strchr(args_copy, '@'))) {
12576  *context++ = '\0';
12577  } else {
12578  context = "default";
12579  }
12580 
12581  if ((res = sayname(chan, args_copy, context) < 0)) {
12582  ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12583  res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12584  if (!res) {
12585  res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12586  }
12587  }
12588 
12589  return res;
12590 }
12591 
12592 #ifdef TEST_FRAMEWORK
12593 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12594 {
12595  return 0;
12596 }
12597 
12598 static struct ast_frame *fake_read(struct ast_channel *ast)
12599 {
12600  return &ast_null_frame;
12601 }
12602 
12603 AST_TEST_DEFINE(test_voicemail_vmsayname)
12604 {
12605  char dir[PATH_MAX];
12606  char dir2[PATH_MAX];
12607  static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12608  static const char TEST_EXTENSION[] = "1234";
12609 
12610  struct ast_channel *test_channel1 = NULL;
12611  int res = -1;
12612 
12613  static const struct ast_channel_tech fake_tech = {
12614  .write = fake_write,
12615  .read = fake_read,
12616  };
12617 
12618  switch (cmd) {
12619  case TEST_INIT:
12620  info->name = "vmsayname_exec";
12621  info->category = "/apps/app_voicemail/";
12622  info->summary = "Vmsayname unit test";
12623  info->description =
12624  "This tests passing various parameters to vmsayname";
12625  return AST_TEST_NOT_RUN;
12626  case TEST_EXECUTE:
12627  break;
12628  }
12629 
12630  if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12631  NULL, NULL, 0, 0, "TestChannel1"))) {
12632  goto exit_vmsayname_test;
12633  }
12634 
12635  /* normally this is done in the channel driver */
12636  test_channel1->nativeformats = AST_FORMAT_GSM;
12637  test_channel1->writeformat = AST_FORMAT_GSM;
12638  test_channel1->rawwriteformat = AST_FORMAT_GSM;
12639  test_channel1->readformat = AST_FORMAT_GSM;
12640  test_channel1->rawreadformat = AST_FORMAT_GSM;
12641  test_channel1->tech = &fake_tech;
12642 
12643  ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12644  snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12645  if (!(res = vmsayname_exec(test_channel1, dir))) {
12646  snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12647  if (ast_fileexists(dir, NULL, NULL)) {
12648  ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12649  res = -1;
12650  goto exit_vmsayname_test;
12651  } else {
12652  /* no greeting already exists as expected, let's create one to fully test sayname */
12653  if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12654  ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12655  goto exit_vmsayname_test;
12656  }
12657  snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12658  snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12659  /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12660  if ((res = symlink(dir, dir2))) {
12661  ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12662  goto exit_vmsayname_test;
12663  }
12664  ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12665  snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12666  res = vmsayname_exec(test_channel1, dir);
12667 
12668  /* TODO: there may be a better way to do this */
12669  unlink(dir2);
12670  snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12671  rmdir(dir2);
12672  snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12673  rmdir(dir2);
12674  }
12675  }
12676 
12677 exit_vmsayname_test:
12678 
12679  if (test_channel1) {
12680  ast_hangup(test_channel1);
12681  }
12682 
12683  return res ? AST_TEST_FAIL : AST_TEST_PASS;
12684 }
12685 
12686 AST_TEST_DEFINE(test_voicemail_msgcount)
12687 {
12688  int i, j, res = AST_TEST_PASS, syserr;
12689  struct ast_vm_user *vmu;
12690  struct ast_vm_user svm;
12691  struct vm_state vms;
12692 #ifdef IMAP_STORAGE
12693  struct ast_channel *chan = NULL;
12694 #endif
12695  struct {
12696  char dir[256];
12697  char file[256];
12698  char txtfile[256];
12699  } tmp[3];
12700  char syscmd[256];
12701  const char origweasels[] = "tt-weasels";
12702  const char testcontext[] = "test";
12703  const char testmailbox[] = "00000000";
12704  const char testspec[] = "00000000@test";
12705  FILE *txt;
12706  int new, old, urgent;
12707  const char *folders[3] = { "Old", "Urgent", "INBOX" };
12708  const int folder2mbox[3] = { 1, 11, 0 };
12709  const int expected_results[3][12] = {
12710  /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12711  { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
12712  { 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
12713  { 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 2 },
12714  };
12715 
12716  switch (cmd) {
12717  case TEST_INIT:
12718  info->name = "test_voicemail_msgcount";
12719  info->category = "/apps/app_voicemail/";
12720  info->summary = "Test Voicemail status checks";
12721  info->description =
12722  "Verify that message counts are correct when retrieved through the public API";
12723  return AST_TEST_NOT_RUN;
12724  case TEST_EXECUTE:
12725  break;
12726  }
12727 
12728  /* Make sure the original path was completely empty */
12729  snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12730  if ((syserr = ast_safe_system(syscmd))) {
12731  ast_test_status_update(test, "Unable to clear test directory: %s\n",
12732  syserr > 0 ? strerror(syserr) : "unable to fork()");
12733  return AST_TEST_FAIL;
12734  }
12735 
12736 #ifdef IMAP_STORAGE
12737  if (!(chan = ast_dummy_channel_alloc())) {
12738  ast_test_status_update(test, "Unable to create dummy channel\n");
12739  return AST_TEST_FAIL;
12740  }
12741 #endif
12742 
12743  if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
12744  !(vmu = find_or_create(testcontext, testmailbox))) {
12745  ast_test_status_update(test, "Cannot create vmu structure\n");
12747 #ifdef IMAP_STORAGE
12748  chan = ast_channel_unref(chan);
12749 #endif
12750  return AST_TEST_FAIL;
12751  }
12752 
12753  populate_defaults(vmu);
12754  memset(&vms, 0, sizeof(vms));
12755 
12756  /* Create temporary voicemail */
12757  for (i = 0; i < 3; i++) {
12758  create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12759  make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12760  snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12761 
12762  if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12763  snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12764  VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12765  if ((syserr = ast_safe_system(syscmd))) {
12766  ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12767  syserr > 0 ? strerror(syserr) : "unable to fork()");
12769 #ifdef IMAP_STORAGE
12770  chan = ast_channel_unref(chan);
12771 #endif
12772  return AST_TEST_FAIL;
12773  }
12774  }
12775 
12776  if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12777  fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12778  fclose(txt);
12779  } else {
12780  ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12781  res = AST_TEST_FAIL;
12782  break;
12783  }
12784  open_mailbox(&vms, vmu, folder2mbox[i]);
12785  STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12786 
12787  /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12788  for (j = 0; j < 3; j++) {
12789  /* folder[2] is INBOX, __has_voicemail will default back to INBOX */
12790  if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12791  ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12792  testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12793  res = AST_TEST_FAIL;
12794  }
12795  }
12796 
12797  new = old = urgent = 0;
12798  if (ast_app_inboxcount(testspec, &new, &old)) {
12799  ast_test_status_update(test, "inboxcount returned failure\n");
12800  res = AST_TEST_FAIL;
12801  } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12802  ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12803  testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12804  res = AST_TEST_FAIL;
12805  }
12806 
12807  new = old = urgent = 0;
12808  if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12809  ast_test_status_update(test, "inboxcount2 returned failure\n");
12810  res = AST_TEST_FAIL;
12811  } else if (old != expected_results[i][6 + 0] ||
12812  urgent != expected_results[i][6 + 1] ||
12813  new != expected_results[i][6 + 2] ) {
12814  ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12815  testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12816  res = AST_TEST_FAIL;
12817  }
12818 
12819  new = old = urgent = 0;
12820  for (j = 0; j < 3; j++) {
12821  if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12822  ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12823  testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12824  res = AST_TEST_FAIL;
12825  }
12826  }
12827  }
12828 
12829  for (i = 0; i < 3; i++) {
12830  /* This is necessary if the voicemails are stored on an ODBC/IMAP
12831  * server, in which case, the rm below will not affect the
12832  * voicemails. */
12833  DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12834  DISPOSE(tmp[i].dir, 0);
12835  }
12836 
12837  if (vms.deleted) {
12838  ast_free(vms.deleted);
12839  }
12840  if (vms.heard) {
12841  ast_free(vms.heard);
12842  }
12843 
12844 #ifdef IMAP_STORAGE
12845  chan = ast_channel_unref(chan);
12846 #endif
12847 
12848  /* And remove test directory */
12849  snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12850  if ((syserr = ast_safe_system(syscmd))) {
12851  ast_test_status_update(test, "Unable to clear test directory: %s\n",
12852  syserr > 0 ? strerror(syserr) : "unable to fork()");
12853  }
12854 
12855  return res;
12856 }
12857 
12858 AST_TEST_DEFINE(test_voicemail_notify_endl)
12859 {
12860  int res = AST_TEST_PASS;
12861  char testcontext[] = "test";
12862  char testmailbox[] = "00000000";
12863  char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12864  char attach[256], attach2[256];
12865  char buf[256] = ""; /* No line should actually be longer than 80 */
12866  struct ast_channel *chan = NULL;
12867  struct ast_vm_user *vmu, vmus = {
12868  .flags = 0,
12869  };
12870  FILE *file;
12871  struct {
12872  char *name;
12873  enum { INT, FLAGVAL, STATIC, STRPTR } type;
12874  void *location;
12875  union {
12876  int intval;
12877  char *strval;
12878  } u;
12879  } test_items[] = {
12880  { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12881  { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12882  { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12883  { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12884  { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12885  { "attach2", STRPTR, attach2, .u.strval = "" },
12886  { "attach", STRPTR, attach, .u.strval = "" },
12887  };
12888  int which;
12889 
12890  switch (cmd) {
12891  case TEST_INIT:
12892  info->name = "test_voicemail_notify_endl";
12893  info->category = "/apps/app_voicemail/";
12894  info->summary = "Test Voicemail notification end-of-line";
12895  info->description =
12896  "Verify that notification emails use a consistent end-of-line character";
12897  return AST_TEST_NOT_RUN;
12898  case TEST_EXECUTE:
12899  break;
12900  }
12901 
12902  snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12903  snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12904 
12905  if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12906  !(vmu = find_or_create(testcontext, testmailbox))) {
12907  ast_test_status_update(test, "Cannot create vmu structure\n");
12908  return AST_TEST_NOT_RUN;
12909  }
12910 
12911  if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12912  ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12913  return AST_TEST_NOT_RUN;
12914  }
12915 
12916  populate_defaults(vmu);
12917  ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12918 #ifdef IMAP_STORAGE
12919  /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12920 #endif
12921 
12922  file = tmpfile();
12923  for (which = 0; which < ARRAY_LEN(test_items); which++) {
12924  /* Kill previous test, if any */
12925  rewind(file);
12926  if (ftruncate(fileno(file), 0)) {
12927  ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12928  res = AST_TEST_FAIL;
12929  break;
12930  }
12931 
12932  /* Make each change, in order, to the test mailbox */
12933  if (test_items[which].type == INT) {
12934  *((int *) test_items[which].location) = test_items[which].u.intval;
12935  } else if (test_items[which].type == FLAGVAL) {
12936  if (ast_test_flag(vmu, test_items[which].u.intval)) {
12937  ast_clear_flag(vmu, test_items[which].u.intval);
12938  } else {
12939  ast_set_flag(vmu, test_items[which].u.intval);
12940  }
12941  } else if (test_items[which].type == STATIC) {
12942  strcpy(test_items[which].location, test_items[which].u.strval);
12943  } else if (test_items[which].type == STRPTR) {
12944  test_items[which].location = test_items[which].u.strval;
12945  }
12946 
12947  make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12948  rewind(file);
12949  while (fgets(buf, sizeof(buf), file)) {
12950  if (
12951 #ifdef IMAP_STORAGE
12952  buf[strlen(buf) - 2] != '\r'
12953 #else
12954  buf[strlen(buf) - 2] == '\r'
12955 #endif
12956  || buf[strlen(buf) - 1] != '\n') {
12957  res = AST_TEST_FAIL;
12958  }
12959  }
12960  }
12961  fclose(file);
12962  return res;
12963 }
12964 
12965 AST_TEST_DEFINE(test_voicemail_load_config)
12966 {
12967  int res = AST_TEST_PASS;
12968  struct ast_vm_user *vmu;
12969  struct ast_config *cfg;
12970  char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
12971  int fd;
12972  FILE *file;
12973  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
12974 
12975  switch (cmd) {
12976  case TEST_INIT:
12977  info->name = "test_voicemail_load_config";
12978  info->category = "/apps/app_voicemail/";
12979  info->summary = "Test loading Voicemail config";
12980  info->description =
12981  "Verify that configuration is loaded consistently. "
12982  "This is to test regressions of ASTERISK-18838 where it was noticed that "
12983  "some options were loaded after the mailboxes were instantiated, causing "
12984  "those options not to be set correctly.";
12985  return AST_TEST_NOT_RUN;
12986  case TEST_EXECUTE:
12987  break;
12988  }
12989 
12990  /* build a config file by hand... */
12991  if ((fd = mkstemp(config_filename)) < 0) {
12992  return AST_TEST_FAIL;
12993  }
12994  if (!(file = fdopen(fd, "w"))) {
12995  close(fd);
12996  unlink(config_filename);
12997  return AST_TEST_FAIL;
12998  }
12999  fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13000  fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13001  fputs("00000002 => 9999,Mrs. Test\n", file);
13002  fclose(file);
13003 
13004  if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
13005  res = AST_TEST_FAIL;
13006  goto cleanup;
13007  }
13008 
13009  load_config_from_memory(1, cfg, NULL);
13010  ast_config_destroy(cfg);
13011 
13012 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13013  ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13014  u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13015 
13016  AST_LIST_LOCK(&users);
13017  AST_LIST_TRAVERSE(&users, vmu, list) {
13018  if (!strcmp(vmu->mailbox, "00000001")) {
13019  if (0); /* trick to get CHECK to work */
13020  CHECK(vmu, callback, "othercontext")
13021  CHECK(vmu, locale, "nl_NL.UTF-8")
13022  CHECK(vmu, zonetag, "central")
13023  } else if (!strcmp(vmu->mailbox, "00000002")) {
13024  if (0); /* trick to get CHECK to work */
13025  CHECK(vmu, callback, "somecontext")
13026  CHECK(vmu, locale, "de_DE.UTF-8")
13027  CHECK(vmu, zonetag, "european")
13028  }
13029  }
13031 
13032 #undef CHECK
13033 
13034  /* restore config */
13035  load_config(1); /* this might say "Failed to load configuration file." */
13036 
13037 cleanup:
13038  unlink(config_filename);
13039  return res;
13040 }
13041 
13042 #endif /* defined(TEST_FRAMEWORK) */
13043 
13044 static int reload(void)
13045 {
13046  return load_config(1);
13047 }
13048 
13049 static int unload_module(void)
13050 {
13051  int res;
13052 
13053  res = ast_unregister_application(app);
13054  res |= ast_unregister_application(app2);
13055  res |= ast_unregister_application(app3);
13056  res |= ast_unregister_application(app4);
13057  res |= ast_unregister_application(sayname_app);
13058  res |= ast_custom_function_unregister(&mailbox_exists_acf);
13059  res |= ast_manager_unregister("VoicemailUsersList");
13060  res |= ast_data_unregister(NULL);
13061 #ifdef TEST_FRAMEWORK
13062  res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13063  res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13064  res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13065  res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13066  res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13067 #endif
13068  ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13070  ao2_ref(inprocess_container, -1);
13071 
13072  if (poll_thread != AST_PTHREADT_NULL)
13073  stop_poll_thread();
13074 
13075  mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13076  ast_unload_realtime("voicemail");
13077  ast_unload_realtime("voicemail_data");
13078 
13079  free_vm_users();
13080  free_vm_zones();
13081  return res;
13082 }
13083 
13084 static int load_module(void)
13085 {
13086  int res;
13087  my_umask = umask(0);
13088  umask(my_umask);
13089 
13090  if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13091  return AST_MODULE_LOAD_DECLINE;
13092  }
13093 
13094  /* compute the location of the voicemail spool directory */
13095  snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13096 
13097  if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13098  ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor. MWI will not work\n");
13099  }
13100 
13101  if ((res = load_config(0)))
13102  return res;
13103 
13108  res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13109  res |= ast_custom_function_register(&mailbox_exists_acf);
13111 #ifdef TEST_FRAMEWORK
13112  res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13113  res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13114  res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13115  res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13116  res |= AST_TEST_REGISTER(test_voicemail_load_config);
13117 #endif
13118 
13119  if (res)
13120  return res;
13121 
13122  ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13123  ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13124 
13126  ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13127  ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13128 
13129  return res;
13130 }
13131 
13132 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
13133 {
13134  int cmd = 0;
13135  char destination[80] = "";
13136  int retries = 0;
13137 
13138  if (!num) {
13139  ast_verb(3, "Destination number will be entered manually\n");
13140  while (retries < 3 && cmd != 't') {
13141  destination[1] = '\0';
13142  destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13143  if (!cmd)
13144  destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13145  if (!cmd)
13146  destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13147  if (!cmd) {
13148  cmd = ast_waitfordigit(chan, 6000);
13149  if (cmd)
13150  destination[0] = cmd;
13151  }
13152  if (!cmd) {
13153  retries++;
13154  } else {
13155 
13156  if (cmd < 0)
13157  return 0;
13158  if (cmd == '*') {
13159  ast_verb(3, "User hit '*' to cancel outgoing call\n");
13160  return 0;
13161  }
13162  if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0)
13163  retries++;
13164  else
13165  cmd = 't';
13166  }
13167  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13168  }
13169  if (retries >= 3) {
13170  return 0;
13171  }
13172 
13173  } else {
13174  if (option_verbose > 2)
13175  ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13176  ast_copy_string(destination, num, sizeof(destination));
13177  }
13178 
13179  if (!ast_strlen_zero(destination)) {
13180  if (destination[strlen(destination) -1 ] == '*')
13181  return 0;
13182  if (option_verbose > 2)
13183  ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13184  ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13185  ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13186  chan->priority = 0;
13187  return 9;
13188  }
13189  return 0;
13190 }
13191 
13192 /*!
13193  * \brief The advanced options within a message.
13194  * \param chan
13195  * \param vmu
13196  * \param vms
13197  * \param msg
13198  * \param option
13199  * \param record_gain
13200  *
13201  * Provides handling for the play message envelope, call the person back, or reply to message.
13202  *
13203  * \return zero on success, -1 on error.
13204  */
13205 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)
13206 {
13207  int res = 0;
13208  char filename[PATH_MAX];
13209  struct ast_config *msg_cfg = NULL;
13210  const char *origtime, *context;
13211  char *name, *num;
13212  int retries = 0;
13213  char *cid;
13214  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13215 
13216  vms->starting = 0;
13217 
13218  make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13219 
13220  /* Retrieve info from VM attribute file */
13221  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13222  RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13223  msg_cfg = ast_config_load(filename, config_flags);
13224  DISPOSE(vms->curdir, vms->curmsg);
13225  if (!valid_config(msg_cfg)) {
13226  ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13227  return 0;
13228  }
13229 
13230  if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13231  ast_config_destroy(msg_cfg);
13232  return 0;
13233  }
13234 
13235  cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13236 
13237  context = ast_variable_retrieve(msg_cfg, "message", "context");
13238  if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13239  context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13240  switch (option) {
13241  case 3: /* Play message envelope */
13242  if (!res)
13243  res = play_message_datetime(chan, vmu, origtime, filename);
13244  if (!res)
13245  res = play_message_callerid(chan, vms, cid, context, 0);
13246 
13247  res = 't';
13248  break;
13249 
13250  case 2: /* Call back */
13251 
13252  if (ast_strlen_zero(cid))
13253  break;
13254 
13255  ast_callerid_parse(cid, &name, &num);
13256  while ((res > -1) && (res != 't')) {
13257  switch (res) {
13258  case '1':
13259  if (num) {
13260  /* Dial the CID number */
13261  res = dialout(chan, vmu, num, vmu->callback);
13262  if (res) {
13263  ast_config_destroy(msg_cfg);
13264  return 9;
13265  }
13266  } else {
13267  res = '2';
13268  }
13269  break;
13270 
13271  case '2':
13272  /* Want to enter a different number, can only do this if there's a dialout context for this user */
13273  if (!ast_strlen_zero(vmu->dialout)) {
13274  res = dialout(chan, vmu, NULL, vmu->dialout);
13275  if (res) {
13276  ast_config_destroy(msg_cfg);
13277  return 9;
13278  }
13279  } else {
13280  ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13281  res = ast_play_and_wait(chan, "vm-sorry");
13282  }
13283  ast_config_destroy(msg_cfg);
13284  return res;
13285  case '*':
13286  res = 't';
13287  break;
13288  case '3':
13289  case '4':
13290  case '5':
13291  case '6':
13292  case '7':
13293  case '8':
13294  case '9':
13295  case '0':
13296 
13297  res = ast_play_and_wait(chan, "vm-sorry");
13298  retries++;
13299  break;
13300  default:
13301  if (num) {
13302  ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13303  res = ast_play_and_wait(chan, "vm-num-i-have");
13304  if (!res)
13305  res = play_message_callerid(chan, vms, num, vmu->context, 1);
13306  if (!res)
13307  res = ast_play_and_wait(chan, "vm-tocallnum");
13308  /* Only prompt for a caller-specified number if there is a dialout context specified */
13309  if (!ast_strlen_zero(vmu->dialout)) {
13310  if (!res)
13311  res = ast_play_and_wait(chan, "vm-calldiffnum");
13312  }
13313  } else {
13314  res = ast_play_and_wait(chan, "vm-nonumber");
13315  if (!ast_strlen_zero(vmu->dialout)) {
13316  if (!res)
13317  res = ast_play_and_wait(chan, "vm-toenternumber");
13318  }
13319  }
13320  if (!res) {
13321  res = ast_play_and_wait(chan, "vm-star-cancel");
13322  }
13323  if (!res) {
13324  res = ast_waitfordigit(chan, 6000);
13325  }
13326  if (!res) {
13327  retries++;
13328  if (retries > 3) {
13329  res = 't';
13330  }
13331  }
13332  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13333  break;
13334 
13335  }
13336  if (res == 't')
13337  res = 0;
13338  else if (res == '*')
13339  res = -1;
13340  }
13341  break;
13342 
13343  case 1: /* Reply */
13344  /* Send reply directly to sender */
13345  if (ast_strlen_zero(cid))
13346  break;
13347 
13348  ast_callerid_parse(cid, &name, &num);
13349  if (!num) {
13350  ast_verb(3, "No CID number available, no reply sent\n");
13351  if (!res)
13352  res = ast_play_and_wait(chan, "vm-nonumber");
13353  ast_config_destroy(msg_cfg);
13354  return res;
13355  } else {
13356  struct ast_vm_user vmu2;
13357  if (find_user(&vmu2, vmu->context, num)) {
13358  struct leave_vm_options leave_options;
13359  char mailbox[AST_MAX_EXTENSION * 2 + 2];
13360  snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13361 
13362  ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13363 
13364  memset(&leave_options, 0, sizeof(leave_options));
13365  leave_options.record_gain = record_gain;
13366  res = leave_voicemail(chan, mailbox, &leave_options);
13367  if (!res)
13368  res = 't';
13369  ast_config_destroy(msg_cfg);
13370  return res;
13371  } else {
13372  /* Sender has no mailbox, can't reply */
13373  ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13374  ast_play_and_wait(chan, "vm-nobox");
13375  res = 't';
13376  ast_config_destroy(msg_cfg);
13377  return res;
13378  }
13379  }
13380  res = 0;
13381 
13382  break;
13383  }
13384 
13385  ast_config_destroy(msg_cfg);
13386 
13387 #ifndef IMAP_STORAGE
13388  if (!res) {
13389  make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13390  vms->heard[msg] = 1;
13391  res = wait_file(chan, vms, vms->fn);
13392  }
13393 #endif
13394  return res;
13395 }
13396 
13397 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13398  int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13399  signed char record_gain, struct vm_state *vms, char *flag)
13400 {
13401  /* Record message & let caller review or re-record it, or set options if applicable */
13402  int res = 0;
13403  int cmd = 0;
13404  int max_attempts = 3;
13405  int attempts = 0;
13406  int recorded = 0;
13407  int msg_exists = 0;
13408  signed char zero_gain = 0;
13409  char tempfile[PATH_MAX];
13410  char *acceptdtmf = "#";
13411  char *canceldtmf = "";
13412  int canceleddtmf = 0;
13413 
13414  /* Note that urgent and private are for flagging messages as such in the future */
13415 
13416  /* barf if no pointer passed to store duration in */
13417  if (duration == NULL) {
13418  ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13419  return -1;
13420  }
13421 
13422  if (!outsidecaller)
13423  snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13424  else
13425  ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13426 
13427  cmd = '3'; /* Want to start by recording */
13428 
13429  while ((cmd >= 0) && (cmd != 't')) {
13430  switch (cmd) {
13431  case '1':
13432  if (!msg_exists) {
13433  /* In this case, 1 is to record a message */
13434  cmd = '3';
13435  break;
13436  } else {
13437  /* Otherwise 1 is to save the existing message */
13438  ast_verb(3, "Saving message as is\n");
13439  if (!outsidecaller)
13440  ast_filerename(tempfile, recordfile, NULL);
13441  ast_stream_and_wait(chan, "vm-msgsaved", "");
13442  if (!outsidecaller) {
13443  /* Saves to IMAP server only if imapgreeting=yes */
13444  STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13445  DISPOSE(recordfile, -1);
13446  }
13447  cmd = 't';
13448  return res;
13449  }
13450  case '2':
13451  /* Review */
13452  ast_verb(3, "Reviewing the message\n");
13453  cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13454  break;
13455  case '3':
13456  msg_exists = 0;
13457  /* Record */
13458  if (recorded == 1)
13459  ast_verb(3, "Re-recording the message\n");
13460  else
13461  ast_verb(3, "Recording the message\n");
13462 
13463  if (recorded && outsidecaller) {
13464  cmd = ast_play_and_wait(chan, INTRO);
13465  cmd = ast_play_and_wait(chan, "beep");
13466  }
13467  recorded = 1;
13468  /* 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 */
13469  if (record_gain)
13470  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13471  if (ast_test_flag(vmu, VM_OPERATOR))
13472  canceldtmf = "0";
13473  cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13474  if (strchr(canceldtmf, cmd)) {
13475  /* need this flag here to distinguish between pressing '0' during message recording or after */
13476  canceleddtmf = 1;
13477  }
13478  if (record_gain)
13479  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13480  if (cmd == -1) {
13481  /* User has hung up, no options to give */
13482  if (!outsidecaller) {
13483  /* user was recording a greeting and they hung up, so let's delete the recording. */
13484  ast_filedelete(tempfile, NULL);
13485  }
13486  return cmd;
13487  }
13488  if (cmd == '0') {
13489  break;
13490  } else if (cmd == '*') {
13491  break;
13492 #if 0
13493  } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13494  /* Message is too short */
13495  ast_verb(3, "Message too short\n");
13496  cmd = ast_play_and_wait(chan, "vm-tooshort");
13497  cmd = ast_filedelete(tempfile, NULL);
13498  break;
13499  } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13500  /* Message is all silence */
13501  ast_verb(3, "Nothing recorded\n");
13502  cmd = ast_filedelete(tempfile, NULL);
13503  cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13504  if (!cmd)
13505  cmd = ast_play_and_wait(chan, "vm-speakup");
13506  break;
13507 #endif
13508  } else {
13509  /* If all is well, a message exists */
13510  msg_exists = 1;
13511  cmd = 0;
13512  }
13513  break;
13514  case '4':
13515  if (outsidecaller) { /* only mark vm messages */
13516  /* Mark Urgent */
13517  if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13518  ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13519  res = ast_play_and_wait(chan, "vm-marked-urgent");
13520  strcpy(flag, "Urgent");
13521  } else if (flag) {
13522  ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13523  res = ast_play_and_wait(chan, "vm-marked-nonurgent");
13524  strcpy(flag, "");
13525  } else {
13526  ast_play_and_wait(chan, "vm-sorry");
13527  }
13528  cmd = 0;
13529  } else {
13530  cmd = ast_play_and_wait(chan, "vm-sorry");
13531  }
13532  break;
13533  case '5':
13534  case '6':
13535  case '7':
13536  case '8':
13537  case '9':
13538  case '*':
13539  case '#':
13540  cmd = ast_play_and_wait(chan, "vm-sorry");
13541  break;
13542 #if 0
13543 /* XXX Commented out for the moment because of the dangers of deleting
13544  a message while recording (can put the message numbers out of sync) */
13545  case '*':
13546  /* Cancel recording, delete message, offer to take another message*/
13547  cmd = ast_play_and_wait(chan, "vm-deleted");
13548  cmd = ast_filedelete(tempfile, NULL);
13549  if (outsidecaller) {
13550  res = vm_exec(chan, NULL);
13551  return res;
13552  }
13553  else
13554  return 1;
13555 #endif
13556  case '0':
13557  if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13558  cmd = ast_play_and_wait(chan, "vm-sorry");
13559  break;
13560  }
13561  if (msg_exists || recorded) {
13562  cmd = ast_play_and_wait(chan, "vm-saveoper");
13563  if (!cmd)
13564  cmd = ast_waitfordigit(chan, 3000);
13565  if (cmd == '1') {
13566  ast_filerename(tempfile, recordfile, NULL);
13567  ast_play_and_wait(chan, "vm-msgsaved");
13568  cmd = '0';
13569  } else if (cmd == '4') {
13570  if (flag) {
13571  ast_play_and_wait(chan, "vm-marked-urgent");
13572  strcpy(flag, "Urgent");
13573  }
13574  ast_play_and_wait(chan, "vm-msgsaved");
13575  cmd = '0';
13576  } else {
13577  ast_play_and_wait(chan, "vm-deleted");
13578  DELETE(tempfile, -1, tempfile, vmu);
13579  cmd = '0';
13580  }
13581  }
13582  return cmd;
13583  default:
13584  /* If the caller is an ouside caller, and the review option is enabled,
13585  allow them to review the message, but let the owner of the box review
13586  their OGM's */
13587  if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13588  return cmd;
13589  if (msg_exists) {
13590  cmd = ast_play_and_wait(chan, "vm-review");
13591  if (!cmd && outsidecaller) {
13592  if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13593  cmd = ast_play_and_wait(chan, "vm-review-urgent");
13594  } else if (flag) {
13595  cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13596  }
13597  }
13598  } else {
13599  cmd = ast_play_and_wait(chan, "vm-torerecord");
13600  if (!cmd)
13601  cmd = ast_waitfordigit(chan, 600);
13602  }
13603 
13604  if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13605  cmd = ast_play_and_wait(chan, "vm-reachoper");
13606  if (!cmd)
13607  cmd = ast_waitfordigit(chan, 600);
13608  }
13609 #if 0
13610  if (!cmd)
13611  cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13612 #endif
13613  if (!cmd)
13614  cmd = ast_waitfordigit(chan, 6000);
13615  if (!cmd) {
13616  attempts++;
13617  }
13618  if (attempts > max_attempts) {
13619  cmd = 't';
13620  }
13621  }
13622  }
13623  if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13624  /* Hang up or timeout, so delete the recording. */
13625  ast_filedelete(tempfile, NULL);
13626  }
13627 
13628  if (cmd != 't' && outsidecaller)
13629  ast_play_and_wait(chan, "vm-goodbye");
13630 
13631  return cmd;
13632 }
13633 
13634 /* This is a workaround so that menuselect displays a proper description
13635  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13636  */
13637 
13639  .load = load_module,
13640  .unload = unload_module,
13641  .reload = reload,
13642  .nonoptreq = "res_adsi,res_smdi",
13643  );
static char user[512]
double volgain
#define VM_TEMPGREETWARN
int ast_filecopy(const char *oldname, const char *newname, const char *fmt)
Copies a file.
Definition: file.c:941
SQLHDBC con
Definition: res_odbc.h:48
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:81
int ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2804
static int saydurationminfo
static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag)
#define ast_channel_lock(chan)
Definition: channel.h:2466
static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
static char exten[AST_MAX_EXTENSION]
Definition: chan_alsa.c:109
static char userscontext[AST_MAX_EXTENSION]
Main Channel structure associated with a channel.
Definition: channel.h:742
int ast_adsi_voice_mode(unsigned char *buf, int when)
Puts CPE in voice mode.
Definition: adsi.c:252
static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:191
static char * vm_check_password_shell(char *command, char *buf, size_t len)
An event.
Definition: event.c:85
char * str
Subscriber phone number (Malloced)
Definition: channel.h:241
static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
Definition: app.h:732
int ast_store_realtime(const char *family,...) attribute_sentinel
Create realtime configuration.
Definition: config.c:2726
int oldmessages
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:946
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
char uniqueid[80]
static void free_vm_zones(void)
Free the zones structure.
static char ext_pass_cmd[128]
int ast_safe_system(const char *s)
Safely spawn an external program while closing file descriptors.
Definition: asterisk.c:1077
Asterisk locking-related definitions:
char fullname[80]
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:2068
Asterisk main include file. File version handling, generic pbx functions.
#define ao2_link(arg1, arg2)
Definition: astobj2.h:785
static const char * substitute_escapes(const char *value)
static int minpassword
const char * ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
Gets a variable.
Definition: config.c:625
int ast_adsi_begin_download(struct ast_channel *chan, char *service, unsigned char *fdn, unsigned char *sec, int version)
Definition: adsi.c:32
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 silencethreshold, int maxsilence_ms, const char *path, const char *acceptdtmf, const char *canceldtmf)
Record a file based on input from a channel This function will play &quot;auth-thankyou&quot; upon successful r...
Definition: app.c:1178
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
Definition: app.h:712
static int load_config(int reload)
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
The data tree to be returned by the callbacks and managed by functions local to this file...
Definition: data.c:85
static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
Loads the options specific to a voicemail user.
int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
Plays a stream and gets DTMF data from a channel.
Definition: app.c:178
struct ast_frame ast_null_frame
Definition: frame.c:131
struct ast_party_caller caller
Channel Caller ID information.
Definition: channel.h:804
char * strsep(char **str, const char *delims)
static char mailcmd[160]
struct ast_app * pbx_findapp(const char *app)
Look up an application.
Definition: pbx.c:1537
static FILE * vm_mkftemp(char *template)
char msg_format[512]
#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY
int ast_callerid_split(const char *src, char *name, int namelen, char *num, int numlen)
Definition: callerid.c:1093
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: app.c:2242
#define MAX_NUM_CID_CONTEXTS
int priority
Definition: channel.h:841
#define AST_RWLIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a read/write list of specified type, statically initialized...
Definition: linkedlists.h:332
static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Default English syntax for &#39;You have N messages&#39; greeting.
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition: pbx.c:1497
static void rename_file(char *sfn, char *dfn)
Renames a message in a mailbox folder.
enum ast_event_type ast_event_get_type(const struct ast_event *event)
Get the type for an event.
Definition: event.c:1070
#define ADSI_MSG_DISPLAY
Definition: adsi.h:32
static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
static unsigned char adsifdn[4]
static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
Manager list voicemail users command.
#define ast_strdup(a)
Definition: astmm.h:109
int ast_adsi_data_mode(unsigned char *buf)
Puts CPE in data mode.
Definition: adsi.c:219
char attachfmt[20]
Definition: ast_expr2.c:325
format_t writeformat
Definition: channel.h:854
static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir, signed char record_gain, struct vm_state *vms, char *flag)
static void free_vm_users(void)
Free the users structure.
char name[80]
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: utils.h:653
static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
#define AST_DIGIT_ANY
Definition: file.h:47
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: cli.c:2177
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2502
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: app.c:683
static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
basically mkdir -p $dest/$context/$ext/$folder
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define ast_test_flag(p, flag)
Definition: utils.h:63
int option_debug
Definition: asterisk.c:182
static char pagerdateformat[32]
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
Definition: config.c:2630
static int adsi_logo(unsigned char *buf)
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
struct ast_party_name name
Subscriber name.
Definition: channel.h:290
struct ast_party_id from
Who is redirecting the call (Sent to the party the call is redirected toward)
Definition: channel.h:449
char fn[PATH_MAX]
static int * map
Definition: misdn_config.c:434
#define SMDI_MWI_WAIT_TIMEOUT
int ast_taskprocessor_push(struct ast_taskprocessor *tps, int(*task_exe)(void *datap), void *datap)
Push a task into the specified taskprocessor queue and signal the taskprocessor thread.
Convenient Signal Processing routines.
#define AST_RWLIST_WRLOCK(head)
Write locks a list.
Definition: linkedlists.h:51
char context[AST_MAX_CONTEXT]
Definition: channel.h:868
static int adsiver
static int inprocess_cmp_fn(void *obj, void *arg, int flags)
static int reload(void)
static int handle_subscribe(void *datap)
int ast_app_has_voicemail(const char *mailbox, const char *folder)
Determine if a given mailbox has any voicemail If folder is NULL, defaults to &quot;INBOX&quot;. If folder is &quot;INBOX&quot;, includes the number of messages in the &quot;Urgent&quot; folder.
Definition: app.c:421
#define ast_set_flag(p, flag)
Definition: utils.h:70
static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag)
Sends email notification that a user has a new voicemail waiting for them.
int ast_event_queue_and_cache(struct ast_event *event)
Queue and cache an event.
Definition: event.c:1465
#define ERROR_LOCK_PATH
static char * handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail users in the CLI.
vm_option_flags
void ast_event_report_subs(const struct ast_event_sub *sub)
Report current subscriptions to a subscription subscriber.
Definition: event.c:701
#define VERBOSE_PREFIX_3
Definition: logger.h:43
#define VM_SAYDURATION
descriptor for a cli entry.
Definition: cli.h:165
const int argc
Definition: cli.h:154
#define LOG_WARNING
Definition: logger.h:144
static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
int ast_update2_realtime(const char *family,...) attribute_sentinel
Update realtime configuration.
Definition: config.c:2703
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
static int maxsilence
static struct ast_data_handler vm_users_data_provider
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
Definition: config.c:597
static 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.
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:497
static char intro[ADSI_MAX_INTRO][20]
Definition: res_adsi.c:66
char timezone[80]
const char * context
static int pwdchange
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...
static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
int lineno
Definition: config.h:87
void ast_verbose(const char *fmt,...)
Definition: logger.c:1568
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: app.c:2101
static int maxdeletedmsg
struct ast_smdi_interface * ast_smdi_interface_find(const char *iface_name)
Find an SMDI interface with the specified name.
Definition: res_smdi.c:627
#define AST_RWLIST_UNLOCK(head)
Attempts to unlock a read/write based list.
Definition: linkedlists.h:150
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:1570
#define tdesc
static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
static void copy_plain_file(char *frompath, char *topath)
Copies a voicemail information (envelope) file.
int ast_unlock_path(const char *path)
Unlock a path.
Definition: app.c:1668
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
static char email[80]
Definition: pbx_dundi.c:197
#define DATA_EXPORT_VM_USERS(USER)
This entries are for multiple registers.
Definition: data.h:253
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
vm_option_args
#define AST_LOG_WARNING
Definition: logger.h:149
#define var
Definition: ast_expr2f.c:606
static unsigned char adsisec[4]
static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
static int count_messages(struct ast_vm_user *vmu, char *dir)
Find all .txt files - even if they are not in sequence from 0000.
char curdir[PATH_MAX]
#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY
int ast_adsi_set_keys(unsigned char *buf, unsigned char *keys)
Set which soft keys should be displayed.
Definition: adsi.c:307
format_t rawwriteformat
Definition: channel.h:856
static struct ast_data_entry vm_data_providers[]
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:438
int ast_adsi_load_session(struct ast_channel *chan, unsigned char *app, int ver, int data)
Check if scripts for a given app are already loaded. Version may be -1, if any version is okay...
Definition: adsi.c:76
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
Definition: pbx.c:4468
Test Framework API.
static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
static int vm_box_exists(struct ast_channel *chan, const char *data)
struct ast_party_redirecting redirecting
Redirecting/Diversion information.
Definition: channel.h:814
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:8415
#define VM_MESSAGEWRAP
An MWI subscription.
#define EVENT_FLAG_CALL
Definition: manager.h:72
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
Definition: cli.h:146
static int maxgreet
Configuration File Parser.
int ast_adsi_download_disconnect(unsigned char *buf)
Disconnects (and hopefully saves) a downloaded script.
Definition: adsi.c:208
int option_verbose
Definition: asterisk.c:181
char * str
Subscriber name (Malloced)
Definition: channel.h:214
static void adsi_login(struct ast_channel *chan)
static pthread_t poll_thread
static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
int ast_config_text_file_save(const char *filename, const struct ast_config *cfg, const char *generator)
Definition: config.c:1977
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: config.c:2548
static char listen_control_reverse_key[12]
#define VM_ATTACH
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:900
char context[80]
static struct ast_threadstorage buf2
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:142
Number of new messages Used by: AST_EVENT_MWI Payload type: UINT.
Definition: event_defs.h:71
struct ast_str * ast_str_create(size_t init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:420
#define ADSI_JUST_LEFT
Definition: adsi.h:112
Number of Used by: AST_EVENT_MWI Payload type: UINT.
Definition: event_defs.h:77
#define INTRO
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
static char locale[20]
#define ast_assert(a)
Definition: utils.h:738
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:2135
#define ast_mutex_lock(a)
Definition: lock.h:155
static int get_folder(struct ast_channel *chan, int start)
get_folder: Folder menu Plays &quot;press 1 for INBOX messages&quot; etc. Should possibly be internationalized ...
unsigned char iobuf[BASEMAXINLINE]
static int get_folder_by_name(const char *name)
#define ao2_unlock(a)
Definition: astobj2.h:497
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
#define ast_copy_flags(dest, src, flagz)
Definition: utils.h:84
format_t nativeformats
Definition: channel.h:852
#define ast_str_alloca(init_len)
Definition: strings.h:608
#define MAXHOSTNAMELEN
Definition: network.h:69
static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg)
static int unload_module(void)
const char * ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
Retrieve a configuration variable within the configuration set.
Definition: config.c:614
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_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: config.c:586
const char * str
Definition: app_jack.c:144
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
static ast_cond_t poll_cond
static char externnotify[160]
static int inprocess_count(const char *context, const char *mailbox, int delta)
static int handle_unsubscribe(void *datap)
static int inbuf(struct baseio *bio, FILE *fi)
utility used by inchar(), for base_encode()
static int vmminsecs
static char * emailsubject
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:931
#define DISPOSE(a, b)
int value
Definition: syslog.c:39
void ast_cli(int fd, const char *fmt,...)
Definition: cli.c:105
#define CHUNKSIZE
ADSI Support (built upon Caller*ID)
static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
static int input(yyscan_t yyscanner)
Definition: ast_expr2f.c:1575
#define LOG_DEBUG
Definition: logger.h:122
static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
#define AST_DATA_ENTRY(__path, __handler)
Definition: data.h:260
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:7795
list of users found in the config file
char locale[20]
const char * ext
Definition: http.c:112
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx.c:7705
format_t rawreadformat
Definition: channel.h:855
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:600
#define ast_cond_signal(cond)
Definition: lock.h:169
int urgentmessages
static struct ast_smdi_interface * smdi_iface
static char fromstring[100]
static int transfer
Definition: chan_mgcp.c:187
#define ast_verb(level,...)
Definition: logger.h:243
Definition: ael.tab.c:203
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
char * emailsubject
void ast_unreplace_sigchld(void)
Restore the SIGCHLD handler.
Definition: asterisk.c:1062
static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return * the previous value of *p. This can be used to handle reference co...
Definition: lock.h:603
#define ast_test_suite_assert(exp)
Definition: test.h:185
const char * line
Definition: cli.h:156
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Definition: pbx.c:3814
static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
struct ast_channel * ast_channel_alloc(int needqueue, int state, const char *cid_num, const char *cid_name, const char *acctcode, const char *exten, const char *context, const char *linkedid, const int amaflag, const char *name_fmt,...)
Definition: channel.c:9825
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:5415
#define VM_SAYCID
static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
#define ADSI_COMM_PAGE
Definition: adsi.h:107
static int get_date(char *s, int len)
Gets the current date and time, as formatted string.
char vmbox[PATH_MAX]
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
Definition: pbx.c:10475
int dh_arraysize
#define MAX_LANGUAGE
Definition: channel.h:138
String fields in structures.
#define VM_SVMAIL
Utility functions.
const char * astman_get_header(const struct message *m, char *var)
Get header from mananger transaction.
Definition: manager.c:1860
pthread_cond_t ast_cond_t
Definition: lock.h:144
static const char * mbox(struct ast_vm_user *vmu, int id)
char cause[SMDI_MWI_FAIL_CAUSE_LEN+1]
Definition: smdi.h:58
#define ast_manager_event(chan, category, event, contents,...)
Definition: manager.h:221
static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
const char * mailbox
static int make_file(char *dest, const int len, const char *dir, const int num)
Creates a file system path expression for a folder within the voicemail data folder and the appropria...
static char * emailbody
struct ast_category * ast_category_new(const char *name, const char *in_file, int lineno)
Create a category structure.
Definition: config.c:673
int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
Set the MWI indicator for a mailbox.
Definition: res_smdi.c:319
static int messagecount(const char *context, const char *mailbox, const char *folder)
Number structure.
Definition: app_followme.c:109
#define ADSI_DIR_FROM_LEFT
Definition: adsi.h:120
Custom localtime functions for multiple timezones.
static void adsi_begin(struct ast_channel *chan, int *useadsi)
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:874
#define DEFAULT_LISTEN_CONTROL_PAUSE_KEY
struct ast_party_id id
Caller party ID.
Definition: channel.h:370
static void run_externnotify(char *context, char *extension, const char *flag)
static char pagerfromstring[100]
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name)
Retrieve a category if it exists.
Definition: config.c:709
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
Definition: config.c:2679
char * context
static char * pagerbody
#define AST_LOG_NOTICE
Definition: logger.h:138
static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
The handler for the change password option.
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Vietnamese syntax for &#39;You have N messages&#39; greeting.
#define VM_SEARCH
static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
get values from config variables.
Definition: utils.c:2118
#define VM_ENVELOPE
static int passwordlocation
static char exitcontext[AST_MAX_CONTEXT]
Definition: config.h:68
static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
static char * sayname_app
int ast_adsi_set_line(unsigned char *buf, int page, int line)
Sets the current line and page.
Definition: adsi.c:285
static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
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: app.c:822
#define MINPASSWORD
static int inprocess_hash_fn(const void *obj, const int flags)
enum AST_LOCK_RESULT ast_lock_path(const char *path)
Lock a filesystem path.
Definition: app.c:1652
#define SENTINEL
Definition: compiler.h:75
Context IE Used by AST_EVENT_MWI Payload type: str.
Definition: event_defs.h:121
#define AST_DATA_HANDLER_VERSION
The Data API structures version.
Definition: data.h:204
void ast_category_destroy(struct ast_category *cat)
Definition: config.c:762
const char * value
Definition: config.h:79
int ast_adsi_input_format(unsigned char *buf, int num, int dir, int wrap, char *format1, char *format2)
Set input format.
Definition: adsi.c:329
static char * complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
#define AST_LOG_ERROR
Definition: logger.h:160
static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
int ast_say_counted_noun(struct ast_channel *chan, int num, const char *noun)
static struct ast_custom_function mailbox_exists_acf
static int vmsayname_exec(struct ast_channel *chan, const char *data)
int ast_build_string(char **buffer, size_t *space, const char *fmt,...)
Build a string in a buffer, designed to be called repeatedly.
Definition: utils.c:1521
char * ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown)
Definition: callerid.c:1074
#define MAXMSG
static char * strip_control_and_high(const char *input, char *buf, size_t buflen)
Strips control and non 7-bit clean characters from input string.
static char * app
static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
General Asterisk PBX channel definitions.
int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Destroy realtime configuration.
Definition: config.c:2750
static char dialcontext[AST_MAX_CONTEXT]
#define VM_FORCENAME
#define VM_DELETE
ODBC container.
Definition: res_odbc.h:46
static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static const char *const mailbox_folders[]
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define VOICEMAIL_CONFIG
const int fd
Definition: cli.h:153
static int vm_execmain(struct ast_channel *chan, const char *data)
#define ast_config_load(filename, flags)
Load a config file.
Definition: config.h:170
static struct ast_flags globalflags
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_manager_register_xml(a, b, c)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:172
static struct ast_event_sub * mwi_sub
Definition: res_jabber.c:394
#define DEFAULT_LISTEN_CONTROL_STOP_KEY
#define AST_PTHREADT_NULL
Definition: lock.h:65
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
const int n
Definition: cli.h:159
struct sla_ringing_trunk * last
Definition: app_meetme.c:965
Data structure associated with a custom dialplan function.
Definition: pbx.h:95
static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Chinese (Taiwan)syntax for &#39;You have N messages&#39; greeting.
static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
ast_mutex_t lock
Definition: app_meetme.c:964
struct ast_data * ast_data_add_node(struct ast_data *root, const char *childname)
Add a container child.
Definition: data.c:2317
static int silencethreshold
static void * mb_poll_thread(void *data)
char mailbox[AST_MAX_EXTENSION]
char username[80]
#define AST_MAX_EXTENSION
Definition: channel.h:135
#define AST_RWLIST_TRAVERSE
Definition: linkedlists.h:493
static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, const char *category, const char *flag)
int passwordlocation
#define ast_data_unregister(path)
Definition: data.h:394
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:554
static char vm_reenterpassword[80]
static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
static int my_umask
char * ast_category_browse(struct ast_config *config, const char *prev)
Goes through categories.
Definition: config.c:810
#define ao2_ref(o, delta)
Definition: astobj2.h:472
#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:83
char context[AST_MAX_CONTEXT]
long int ast_random(void)
Definition: utils.c:1640
static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64]
static void free_zone(struct vm_zone *z)
#define ao2_lock(a)
Definition: astobj2.h:488
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:191
static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
static int check_password(struct ast_vm_user *vmu, char *password)
Check that password meets minimum required length.
#define VM_MOVEHEARD
static char * addesc
static int say_and_wait(struct ast_channel *chan, int num, const char *language)
#define COPY(a, b, c, d, e, f, g, h)
uint32_t uniqueid
static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
static char language[MAX_LANGUAGE]
Definition: chan_alsa.c:108
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
const char * name
Definition: config.h:77
int ast_strftime_locale(char *buf, size_t len, const char *format, const struct ast_tm *tm, const char *locale)
Definition: localtime.c:2280
char * ast_format_str_reduce(char *fmts)
Definition: file.c:1382
static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size, struct ast_vm_user *res_vmu, const char *context, const char *prefix, int skipuser, int max_logins, int silent)
static char zonetag[80]
static ast_mutex_t poll_lock
#define ENDL
static const char * ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
Wraps a character sequence in double quotes, escaping occurences of quotes within the string...
static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain)
The advanced options within a message.
#define DELETE(a, b, c, d)
static int write_password_to_file(const char *secretfn, const char *password)
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
static double volgain
int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
Determine number of new/old messages in a mailbox.
Definition: app.c:435
ODBC resource manager.
char zonetag[80]
static char VM_SPOOL_DIR[PATH_MAX]
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:5400
#define ast_data_register_multiple(data_entries, entries)
Definition: data.h:377
static int last_message_index(struct ast_vm_user *vmu, char *dir)
Determines the highest message number in use for a given user and mailbox folder. ...
static void free_user(struct ast_vm_user *vmu)
char mailbox[0]
static int vm_delete(char *file)
Removes the voicemail sound and information file.
#define PWDCHANGE_INTERNAL
static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
Structure to describe a channel &quot;technology&quot;, ie a channel driver See for examples: ...
Definition: channel.h:507
static void apply_options(struct ast_vm_user *vmu, const char *options)
Destructively Parse options and apply.
Core PBX routines and definitions.
static char vm_mismatch[80]
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:542
static char vm_pls_try_again[80]
#define ast_data_add_structure(structure_name, root, structure)
Definition: data.h:620
int * heard
int ast_app_inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
Determine number of urgent/new/old messages in a mailbox.
Definition: app.c:455
Event type Used by: AST_EVENT_SUB, AST_EVENT_UNSUB Payload type: UINT.
Definition: event_defs.h:95
The list of nodes with their search requirement.
Definition: data.c:122
static struct ast_taskprocessor * mwi_subscription_tps
#define ASTOBJ_UNREF(object, destructor)
Decrement the reference count on an object.
Definition: astobj.h:218
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:290
static char vm_invalid_password[80]
static int get_folder2(struct ast_channel *chan, char *fn, int start)
plays a prompt and waits for a keypress.
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:184
const char *const * argv
Definition: cli.h:155
static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
Sets a a specific property value.
static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
const char * ast_config_AST_DATA_DIR
Definition: asterisk.c:262
#define DEFAULT_POLL_FREQ
#define VM_PBXSKIP
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define HVSU_OUTPUT_FORMAT
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
int ast_adsi_transmit_message(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype)
Definition: adsi.c:98
#define VM_OPERATOR
static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
static char callcontext[AST_MAX_CONTEXT]
static int skipms
static void adsi_password(struct ast_channel *chan)
char pager[80]
static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
#define AST_LIST_HEAD_NOLOCK_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:345
#define LOG_ERROR
Definition: logger.h:155
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:716
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:594
static char ext_pass_check_cmd[128]
static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is &quot;true&quot;. This function checks to see whether a string passed to it is an indication of an &quot;true&quot; value. It checks to see if the string is &quot;yes&quot;, &quot;true&quot;, &quot;y&quot;, &quot;t&quot;, &quot;on&quot; or &quot;1&quot;.
Definition: utils.c:1533
static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:364
static struct @350 args
#define RENAME(a, b, c, d, e, f, g, h)
#define CLI_SHOWUSAGE
Definition: cli.h:44
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.
void ast_data_remove_node(struct ast_data *root, struct ast_data *child)
Remove a node that was added using ast_data_add_.
Definition: data.c:2486
static unsigned int poll_mailboxes
static char listen_control_restart_key[12]
char * emailbody
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: config.c:483
static int vm_users_data_provider_get(const struct ast_data_search *search, struct ast_data *data_root)
#define OPERATOR_EXIT
Event subscription.
Definition: event.c:124
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
#define VM_REVIEW
enum ast_channel_state _state
Definition: channel.h:839
static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Top level method to invoke the language variant vm_browse_messages_XX function.
static void poll_subscribed_mailboxes(void)
static int vm_browse_messages_latin(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Common LATIN languages syntax for &#39;You have N messages&#39; greeting.
static void adsi_folders(struct ast_channel *chan, int start, char *label)
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: utils.c:1587
#define ADSI_KEY_SKT
Definition: adsi.h:117
static int vm_users_data_provider_get_helper(const struct ast_data_search *search, struct ast_data *data_root, struct ast_vm_user *user)
const ast_string_field name
Definition: channel.h:787
static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
Prompts the user and records a voicemail to a mailbox.
int linelength
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:430
char exit[80]
static int ochar(struct baseio *bio, int c, FILE *so)
utility used by base_encode()
#define LOG_NOTICE
Definition: logger.h:133
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:11261
#define VOICEMAIL_DIR_MODE
static void adsi_goodbye(struct ast_channel *chan)
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)
Copies a message from one mailbox to another.
static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
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)
Creates the email file to be sent to indicate a new voicemail exists for a user.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
int ast_say_counted_adjective(struct ast_channel *chan, int num, const char *adjective, const char *gender)
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
static struct ast_threadstorage buf1
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:696
#define ao2_find(arg1, arg2, arg3)
Definition: astobj2.h:964
#define ast_channel_unlock(chan)
Definition: channel.h:2467
static struct ast_vm_user * find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
Finds a voicemail user from the users file or the realtime engine.
#define CLI_FAILURE
Definition: cli.h:45
int errno
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1858
static const char name[]
#define AST_MAX_CONTEXT
Definition: channel.h:136
#define ast_free(a)
Definition: astmm.h:97
char * command
Definition: cli.h:180
static char emaildateformat[32]
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:418
char macrocontext[AST_MAX_CONTEXT]
Definition: channel.h:870
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: config.c:888
int ast_adsi_end_download(struct ast_channel *chan)
Definition: adsi.c:43
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1370
#define AST_FLAGS_ALL
Definition: utils.h:196
int ast_say_character_str(struct ast_channel *chan, const char *num, const char *ints, const char *lang)
Definition: channel.c:8421
#define ast_odbc_request_obj(a, b)
Definition: res_odbc.h:123
static int inchar(struct baseio *bio, FILE *fi)
utility used by base_encode()
static int load_module(void)
void ast_close_fds_above_n(int n)
Common routine for child processes, to close all fds prior to exec(2)
Definition: app.c:2237
int * deleted
#define BASELINELEN
static struct ast_format f[]
Definition: format_g726.c:181
static char vm_passchanged[80]
SMDI support for Asterisk.
const char * word
Definition: cli.h:157
int ast_adsi_load_soft_key(unsigned char *buf, int key, const char *llabel, const char *slabel, char *ret, int data)
Creates &quot;load soft key&quot; parameters.
Definition: adsi.c:296
static int maxlogins
static int base_encode(char *filename, FILE *so)
Performs a base 64 encode algorithm on the contents of a File.
static struct ast_cli_entry cli_voicemail[]
#define STORE(a, b, c, d, e, f, g, h, i, j)
const char * ast_config_AST_SPOOL_DIR
Definition: asterisk.c:259
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:2351
static char acceptdtmf
Definition: chan_agent.c:227
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
#define ADSI_MSG_DOWNLOAD
Definition: adsi.h:33
An API for managing task processing threads that can be shared across modules.
int ast_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: config.c:942
char fwd_st[SMDI_MAX_STATION_NUM_LEN+1]
Definition: smdi.h:56
static char * app2
static char listen_control_pause_key[12]
if(yyss+yystacksize-1<=yyssp)
Definition: ast_expr2.c:1874
static const char type[]
Definition: chan_nbs.c:57
#define SENDMAIL
structure to hold users read from users.conf
static void * cleanup(void *unused)
Definition: pbx_realtime.c:125
static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
Structure used to handle boolean flags.
Definition: utils.h:200
static char charset[32]
static char listen_control_forward_key[12]
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
Definition: config.c:2606
#define ast_clear_flag(p, flag)
Definition: utils.h:77
Support for logging to various files, console and syslog Configuration in file logger.conf.
void ast_install_vm_functions(int(*has_voicemail_func)(const char *mailbox, const char *folder), int(*inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs), int(*inboxcount2_func)(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs), int(*messagecount_func)(const char *context, const char *mailbox, const char *folder), int(*sayname_func)(struct ast_channel *chan, const char *mailbox, const char *context))
Set voicemail function callbacks.
Definition: app.c:399
#define AST_RWLIST_ENTRY
Definition: linkedlists.h:414
Options for leaving voicemail with the voicemail() application.
Definition: app_minivm.c:635
const char * usage
Definition: cli.h:171
static void read_password_from_file(const char *secretfn, char *password, int passwordlen)
static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
Definition: pbx.c:10546
uint32_t ast_event_get_ie_uint(const struct ast_event *event, enum ast_event_ie_type ie_type)
Get the value of an information element that has an integer payload.
Definition: event.c:1075
int(*const write)(struct ast_channel *chan, struct ast_frame *frame)
Write a frame, in standard format (see frame.h)
Definition: channel.h:554
static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms)
#define RETRIEVE(a, b, c, d)
#define VALID_DTMF
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
Definition: channel.c:3552
char language[MAX_LANGUAGE]
int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime_sec, char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence_ms)
Record a file based on input frm a channel. Recording is performed in &#39;prepend&#39; mode which works a li...
Definition: app.c:1188
signed char record_gain
Definition: app_minivm.c:637
vm_passwordlocation
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:436
#define EVENT_FLAG_REPORTING
Definition: manager.h:80
#define CLI_SUCCESS
Definition: cli.h:43
static char * app3
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:471
#define AST_RWLIST_INSERT_TAIL
Definition: linkedlists.h:726
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:622
int ast_adsi_available(struct ast_channel *chan)
Returns non-zero if Channel does or might support ADSI.
Definition: adsi.c:263
A ast_taskprocessor structure is a singleton by name.
Definition: taskprocessor.c:67
static int vmmaxsecs
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:8397
static struct ast_event_sub * mwi_unsub_sub
Standard Command Line Interface.
format_t readformat
Definition: channel.h:853
struct ast_event * ast_event_new(enum ast_event_type event_type,...)
Create a new event.
Definition: event.c:1202
#define ast_calloc(a, b)
Definition: astmm.h:82
static int has_voicemail(const char *mailbox, const char *folder)
Determines if the given folder has messages.
struct ast_event_sub * ast_event_subscribe(enum ast_event_type event_type, ast_event_cb_t cb, const char *description, void *userdata,...)
Subscribe to events.
Definition: event.c:909
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
static void start_poll_thread(void)
#define ao2_container_alloc(arg1, arg2, arg3)
Definition: astobj2.h:734
static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:77
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
#define AST_LOG_DEBUG
Definition: logger.h:127
static struct ast_app_option vm_app_options[128]
ast_app: A registered application
Definition: pbx.c:971
char dialout[80]
static struct ast_vm_user * find_or_create(const char *context, const char *box)
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is &quot;false&quot;...
Definition: utils.c:1550
static int vm_lock_path(const char *path)
Lock file path only return failure if ast_lock_path returns &#39;timeout&#39;, not if the path does not exist...
int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
Register multiple commands.
Definition: cli.c:2167
const int pos
Definition: cli.h:158
SAY_EXTERN int(* ast_say_date_with_format)(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *timezone) SAY_INIT(ast_say_date_with_format)
Definition: say.h:168
#define AST_OPTION_RXGAIN
Definition: frame.h:463
#define ast_realloc(a, b)
Definition: astmm.h:103
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1343
static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:919
static char * handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Reload voicemail configuration from the CLI.
#define EXISTS(a, b, c, d)
#define VM_SKIPAFTERCMD
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:3086
static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
static char * pagersubject
static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
#define BASEMAXINLINE
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
static char * show_users_realtime(int fd, const char *context)
static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
static int append_mailbox(const char *context, const char *box, const char *data)
void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
ast_smdi_mwi_message destructor.
Definition: res_smdi.c:829
vm_box
static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
Data structure associated with a single frame of data.
Definition: frame.h:142
#define AST_DATA_STRUCTURE(__struct, __name)
Definition: data.h:300
#define MAXMSGLIMIT
char curbox[80]
#define VM_ALLOCED
#define VMSTATE_MAX_MSG_ARRAY
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
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)
static char vm_password[80]
char callback[80]
static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
static int play_message_category(struct ast_channel *chan, const char *category)
static char * app4
Unique ID Used by: AST_EVENT_SUB, AST_EVENT_UNSUB Payload type: UINT.
Definition: event_defs.h:89
static char * handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail zones in the CLI.
#define ASTERISK_USERNAME
#define VM_DIRECFORWARD
#define VOICEMAIL_FILE_MODE
int ast_filerename(const char *oldname, const char *newname, const char *fmt)
Renames a file.
Definition: file.c:936
const char * name
Definition: pbx.h:96
#define PWDCHANGE_EXTERNAL
static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts, char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
presents the option to prepend to an existing message when forwarding it.
static struct ast_event_sub * mwi_sub_sub
static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
static int is_valid_dtmf(const char *key)
Determines if a DTMF key entered is valid.
enum queue_result id
Definition: app_queue.c:1090
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:528
struct ast_variable * next
Definition: config.h:82
static int vm_exec(struct ast_channel *chan, const char *data)
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
#define HVSZ_OUTPUT_FORMAT
#define ast_mutex_init(pmutex)
Definition: lock.h:152
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:669
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:229
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
void ast_category_append(struct ast_config *config, struct ast_category *cat)
Definition: config.c:719
#define CONFIG_STATUS_FILEINVALID
Definition: config.h:52
int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
Unset the MWI indicator for a mailbox.
Definition: res_smdi.c:324
#define VM_FWDURGAUTO
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:107
int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders)
Reads multiple digits.
Definition: channel.c:5837
#define ast_mutex_destroy(a)
Definition: lock.h:154
static unsigned char poll_thread_run
char serveremail[80]
int ast_adsi_unload_session(struct ast_channel *chan)
Definition: adsi.c:87
static int maxmsg
const char * ast_event_get_ie_str(const struct ast_event *event, enum ast_event_ie_type ie_type)
Get the value of an information element that has a string payload.
Definition: event.c:1102
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the &#39;nonstandard&#39; argument separation process for an application.
Definition: app.h:619
struct ast_channel * ast_dummy_channel_alloc(void)
Create a fake channel structure.
Definition: channel.c:1391
else
Definition: ast_expr2.c:1975
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:1112
Say numbers and dates (maybe words one day too)
static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
int ast_app_messagecount(const char *context, const char *mailbox, const char *folder)
Check number of messages in a given context, mailbox, and folder.
Definition: app.c:486
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
static char vm_newpassword[80]
#define RESULT_SUCCESS
Definition: cli.h:39
struct ast_smdi_mwi_message * ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout, const char *station)
Definition: res_smdi.c:620
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:1880
#define ast_malloc(a)
Definition: astmm.h:91
Asterisk module definitions.
const char * ast_config_AST_VAR_DIR
Definition: asterisk.c:261
static void delete_file(struct phoneprov_file *file)
static char vmfmts[80]
static snd_pcm_format_t format
Definition: chan_alsa.c:93
struct ast_channel_tech * tech
Definition: channel.h:743
char email[80]
#define ADSI_JUST_CENT
Definition: adsi.h:114
struct ast_data * ast_data_add_int(struct ast_data *root, const char *childname, int value)
Add an integer node type.
Definition: data.c:2322
static int vmauthenticate(struct ast_channel *chan, const char *data)
static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
struct ast_event_sub * ast_event_unsubscribe(struct ast_event_sub *event_sub)
Un-subscribe from events.
Definition: event.c:987
static char serveremail[80]
static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
int newmessages
#define AST_FORMAT_GSM
Definition: frame.h:244
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:247
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 ...
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:172
#define ADSI_KEY_APPS
Definition: adsi.h:109
static int valid_config(const struct ast_config *cfg)
Check if configuration file is valid.
static char listen_control_stop_key[12]
int ast_data_search_match(const struct ast_data_search *search, struct ast_data *data)
Check the current generated node to know if it matches the search condition.
Definition: data.c:1458
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1164
#define AST_RWLIST_TRAVERSE_SAFE_END
Definition: linkedlists.h:602
const ast_string_field language
Definition: channel.h:787
uint32_t version
Structure version.
Definition: data.h:247
char exten[AST_MAX_EXTENSION]
Definition: channel.h:869
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:526
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:128
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:437
struct ast_variable * ast_variable_new(const char *name, const char *value, const char *filename)
Definition: config.c:278
static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
Structure for mutex and tracking information.
Definition: lock.h:121
An SMDI message waiting indicator message.
Definition: smdi.h:55
static char mailbox[AST_MAX_EXTENSION]
Definition: chan_mgcp.c:197
The structure of the node handler.
Definition: data.h:245
char password[80]
int ast_manager_unregister(char *action)
Unregister a registered manager command.
Definition: manager.c:5355
jack_status_t status
Definition: app_jack.c:143
#define DATA_EXPORT_VM_ZONES(ZONE)
static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
Resets a user password to a specified password.
yylloc first_line
Definition: ast_expr2.c:1857
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
Mailbox name.
Definition: event_defs.h:83
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: config.c:2650
#define VM_FORCEGREET
#define DEFAULT_LISTEN_CONTROL_RESTART_KEY
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
Definition: app.h:721
#define CONFIG_STATUS_FILEUNCHANGED
Definition: config.h:51
static unsigned int poll_freq
struct ao2_container * inprocess_container
#define ast_mutex_unlock(a)
Definition: lock.h:156
static void queue_mwi_event(const char *box, int urgent, int new, int old)
static void stop_poll_thread(void)
static char prefix[MAX_PREFIX]
Definition: http.c:107
static void populate_defaults(struct ast_vm_user *vmu)
Sets default voicemail system options to a voicemail user.
static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
The handler for &#39;record a temporary greeting&#39;.
static char vm_prepend_timeout[80]
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2151
int ast_callerid_parse(char *instr, char **name, char **location)
Destructively parse inbuf into name and location (or number)
Definition: callerid.c:1009
void ast_uninstall_vm_functions(void)
Definition: app.c:412
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:344
static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: config.c:2587
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:292