Wed Jan 8 2020 09:49:41

Asterisk developer's documentation


app_voicemail_imapstorage.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" displayname="Voicemail Build Options" positive_output="yes" touch_on_change="apps/app_voicemail.c apps/app_directory.c">
48  <member name="FILE_STORAGE" displayname="Storage of Voicemail using filesystem">
49  <conflict>ODBC_STORAGE</conflict>
50  <conflict>IMAP_STORAGE</conflict>
51  <defaultenabled>yes</defaultenabled>
52  <support_level>core</support_level>
53  </member>
54  <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
55  <depend>generic_odbc</depend>
56  <depend>ltdl</depend>
57  <conflict>IMAP_STORAGE</conflict>
58  <conflict>FILE_STORAGE</conflict>
59  <defaultenabled>no</defaultenabled>
60  <support_level>core</support_level>
61  </member>
62  <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
63  <depend>imap_tk</depend>
64  <conflict>ODBC_STORAGE</conflict>
65  <conflict>FILE_STORAGE</conflict>
66  <use>openssl</use>
67  <defaultenabled>no</defaultenabled>
68  <support_level>core</support_level>
69  </member>
70 </category>
71 ***/
72 
73 #include "asterisk.h"
74 
75 #ifdef IMAP_STORAGE
76 #include <ctype.h>
77 #include <signal.h>
78 #include <pwd.h>
79 #ifdef USE_SYSTEM_IMAP
80 #include <imap/c-client.h>
81 #include <imap/imap4r1.h>
82 #include <imap/linkage.h>
83 #elif defined (USE_SYSTEM_CCLIENT)
84 #include <c-client/c-client.h>
85 #include <c-client/imap4r1.h>
86 #include <c-client/linkage.h>
87 #else
88 #include "c-client.h"
89 #include "imap4r1.h"
90 #include "linkage.h"
91 #endif
92 #endif
93 
94 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426691 $")
95 
96 #include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
97 #include <sys/time.h>
98 #include <sys/stat.h>
99 #include <sys/mman.h>
100 #include <time.h>
101 #include <dirent.h>
102 #if defined(__FreeBSD__) || defined(__OpenBSD__)
103 #include <sys/wait.h>
104 #endif
105 
106 #include "asterisk/logger.h"
107 #include "asterisk/lock.h"
108 #include "asterisk/file.h"
109 #include "asterisk/channel.h"
110 #include "asterisk/pbx.h"
111 #include "asterisk/config.h"
112 #include "asterisk/say.h"
113 #include "asterisk/module.h"
114 #include "asterisk/adsi.h"
115 #include "asterisk/app.h"
116 #include "asterisk/manager.h"
117 #include "asterisk/dsp.h"
118 #include "asterisk/localtime.h"
119 #include "asterisk/cli.h"
120 #include "asterisk/utils.h"
121 #include "asterisk/stringfields.h"
122 #include "asterisk/smdi.h"
123 #include "asterisk/astobj2.h"
124 #include "asterisk/event.h"
125 #include "asterisk/taskprocessor.h"
126 #include "asterisk/test.h"
127 
128 #ifdef ODBC_STORAGE
129 #include "asterisk/res_odbc.h"
130 #endif
131 
132 #ifdef IMAP_STORAGE
133 #include "asterisk/threadstorage.h"
134 #endif
135 
136 /*** DOCUMENTATION
137  <application name="VoiceMail" language="en_US">
138  <synopsis>
139  Leave a Voicemail message.
140  </synopsis>
141  <syntax>
142  <parameter name="mailboxs" argsep="&amp;" required="true">
143  <argument name="mailbox1" argsep="@" required="true">
144  <argument name="mailbox" required="true" />
145  <argument name="context" />
146  </argument>
147  <argument name="mailbox2" argsep="@" multiple="true">
148  <argument name="mailbox" required="true" />
149  <argument name="context" />
150  </argument>
151  </parameter>
152  <parameter name="options">
153  <optionlist>
154  <option name="b">
155  <para>Play the <literal>busy</literal> greeting to the calling party.</para>
156  </option>
157  <option name="d">
158  <argument name="c" />
159  <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
160  if played during the greeting. Context defaults to the current context.</para>
161  </option>
162  <option name="g">
163  <argument name="#" required="true" />
164  <para>Use the specified amount of gain when recording the voicemail
165  message. The units are whole-number decibels (dB). Only works on supported
166  technologies, which is DAHDI only.</para>
167  </option>
168  <option name="s">
169  <para>Skip the playback of instructions for leaving a message to the
170  calling party.</para>
171  </option>
172  <option name="u">
173  <para>Play the <literal>unavailable</literal> greeting.</para>
174  </option>
175  <option name="U">
176  <para>Mark message as <literal>URGENT</literal>.</para>
177  </option>
178  <option name="P">
179  <para>Mark message as <literal>PRIORITY</literal>.</para>
180  </option>
181  </optionlist>
182  </parameter>
183  </syntax>
184  <description>
185  <para>This application allows the calling party to leave a message for the specified
186  list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
187  the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
188  exist.</para>
189  <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
190  <enumlist>
191  <enum name="0">
192  <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
193  </enum>
194  <enum name="*">
195  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
196  </enum>
197  </enumlist>
198  <para>This application will set the following channel variable upon completion:</para>
199  <variablelist>
200  <variable name="VMSTATUS">
201  <para>This indicates the status of the execution of the VoiceMail application.</para>
202  <value name="SUCCESS" />
203  <value name="USEREXIT" />
204  <value name="FAILED" />
205  </variable>
206  </variablelist>
207  </description>
208  <see-also>
209  <ref type="application">VoiceMailMain</ref>
210  </see-also>
211  </application>
212  <application name="VoiceMailMain" language="en_US">
213  <synopsis>
214  Check Voicemail messages.
215  </synopsis>
216  <syntax>
217  <parameter name="mailbox" required="true" argsep="@">
218  <argument name="mailbox" />
219  <argument name="context" />
220  </parameter>
221  <parameter name="options">
222  <optionlist>
223  <option name="p">
224  <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
225  the mailbox that is entered by the caller.</para>
226  </option>
227  <option name="g">
228  <argument name="#" required="true" />
229  <para>Use the specified amount of gain when recording a voicemail message.
230  The units are whole-number decibels (dB).</para>
231  </option>
232  <option name="s">
233  <para>Skip checking the passcode for the mailbox.</para>
234  </option>
235  <option name="a">
236  <argument name="folder" required="true" />
237  <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
238  Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
239  <enumlist>
240  <enum name="0"><para>INBOX</para></enum>
241  <enum name="1"><para>Old</para></enum>
242  <enum name="2"><para>Work</para></enum>
243  <enum name="3"><para>Family</para></enum>
244  <enum name="4"><para>Friends</para></enum>
245  <enum name="5"><para>Cust1</para></enum>
246  <enum name="6"><para>Cust2</para></enum>
247  <enum name="7"><para>Cust3</para></enum>
248  <enum name="8"><para>Cust4</para></enum>
249  <enum name="9"><para>Cust5</para></enum>
250  </enumlist>
251  </option>
252  </optionlist>
253  </parameter>
254  </syntax>
255  <description>
256  <para>This application allows the calling party to check voicemail messages. A specific
257  <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
258  may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
259  be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
260  <literal>default</literal> context will be used.</para>
261  <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
262  or Password, and the extension exists:</para>
263  <enumlist>
264  <enum name="*">
265  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
266  </enum>
267  </enumlist>
268  </description>
269  <see-also>
270  <ref type="application">VoiceMail</ref>
271  </see-also>
272  </application>
273  <application name="MailboxExists" language="en_US">
274  <synopsis>
275  Check to see if Voicemail mailbox exists.
276  </synopsis>
277  <syntax>
278  <parameter name="mailbox" required="true" argsep="@">
279  <argument name="mailbox" required="true" />
280  <argument name="context" />
281  </parameter>
282  <parameter name="options">
283  <para>None options.</para>
284  </parameter>
285  </syntax>
286  <description>
287  <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
288  <replaceable>context</replaceable> is specified, the <literal>default</literal> context
289  will be used.</para>
290  <para>This application will set the following channel variable upon completion:</para>
291  <variablelist>
292  <variable name="VMBOXEXISTSSTATUS">
293  <para>This will contain the status of the execution of the MailboxExists application.
294  Possible values include:</para>
295  <value name="SUCCESS" />
296  <value name="FAILED" />
297  </variable>
298  </variablelist>
299  </description>
300  </application>
301  <application name="VMAuthenticate" language="en_US">
302  <synopsis>
303  Authenticate with Voicemail passwords.
304  </synopsis>
305  <syntax>
306  <parameter name="mailbox" required="true" argsep="@">
307  <argument name="mailbox" />
308  <argument name="context" />
309  </parameter>
310  <parameter name="options">
311  <optionlist>
312  <option name="s">
313  <para>Skip playing the initial prompts.</para>
314  </option>
315  </optionlist>
316  </parameter>
317  </syntax>
318  <description>
319  <para>This application behaves the same way as the Authenticate application, but the passwords
320  are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
321  specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
322  is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
323  mailbox.</para>
324  <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
325  or Password, and the extension exists:</para>
326  <enumlist>
327  <enum name="*">
328  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
329  </enum>
330  </enumlist>
331  </description>
332  </application>
333  <application name="VMSayName" language="en_US">
334  <synopsis>
335  Play the name of a voicemail user
336  </synopsis>
337  <syntax>
338  <parameter name="mailbox" required="true" argsep="@">
339  <argument name="mailbox" />
340  <argument name="context" />
341  </parameter>
342  </syntax>
343  <description>
344  <para>This application will say the recorded name of the voicemail user specified as the
345  argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
346  </description>
347  </application>
348  <function name="MAILBOX_EXISTS" language="en_US">
349  <synopsis>
350  Tell if a mailbox is configured.
351  </synopsis>
352  <syntax argsep="@">
353  <parameter name="mailbox" required="true" />
354  <parameter name="context" />
355  </syntax>
356  <description>
357  <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
358  If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
359  context.</para>
360  </description>
361  </function>
362  <manager name="VoicemailUsersList" language="en_US">
363  <synopsis>
364  List All Voicemail User Information.
365  </synopsis>
366  <syntax>
367  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
368  </syntax>
369  <description>
370  </description>
371  </manager>
372  ***/
373 
374 #ifdef IMAP_STORAGE
375 static char imapserver[48];
376 static char imapport[8];
377 static char imapflags[128];
378 static char imapfolder[64];
379 static char imapparentfolder[64] = "\0";
380 static char greetingfolder[64];
381 static char authuser[32];
382 static char authpassword[42];
383 static int imapversion = 1;
384 
385 static int expungeonhangup = 1;
386 static int imapgreetings = 0;
387 static char delimiter = '\0';
388 
389 struct vm_state;
390 struct ast_vm_user;
391 
392 AST_THREADSTORAGE(ts_vmstate);
393 
394 /* Forward declarations for IMAP */
395 static int init_mailstream(struct vm_state *vms, int box);
396 static void write_file(char *filename, char *buffer, unsigned long len);
397 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
398 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
399 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
400 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
401 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
402 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
403 static void vmstate_insert(struct vm_state *vms);
404 static void vmstate_delete(struct vm_state *vms);
405 static void set_update(MAILSTREAM * stream);
406 static void init_vm_state(struct vm_state *vms);
407 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
408 static void get_mailbox_delimiter(MAILSTREAM *stream);
409 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
410 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
411 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);
412 static void update_messages_by_imapuser(const char *user, unsigned long number);
413 static int vm_delete(char *file);
414 
415 static int imap_remove_file (char *dir, int msgnum);
416 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
417 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
418 static void check_quota(struct vm_state *vms, char *mailbox);
419 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
420 struct vmstate {
421  struct vm_state *vms;
422  AST_LIST_ENTRY(vmstate) list;
423 };
424 
425 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
426 
427 #endif
428 
429 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
430 
431 #define COMMAND_TIMEOUT 5000
432 /* Don't modify these here; set your umask at runtime instead */
433 #define VOICEMAIL_DIR_MODE 0777
434 #define VOICEMAIL_FILE_MODE 0666
435 #define CHUNKSIZE 65536
436 
437 #define VOICEMAIL_CONFIG "voicemail.conf"
438 #define ASTERISK_USERNAME "asterisk"
439 
440 /* Define fast-forward, pause, restart, and reverse keys
441  * while listening to a voicemail message - these are
442  * strings, not characters */
443 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
444 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
445 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
446 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
447 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
448 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
449 
450 /* Default mail command to mail voicemail. Change it with the
451  * mailcmd= command in voicemail.conf */
452 #define SENDMAIL "/usr/sbin/sendmail -t"
453 
454 #define INTRO "vm-intro"
455 
456 #define MAXMSG 100
457 #define MAXMSGLIMIT 9999
458 
459 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
460 
461 #define BASELINELEN 72
462 #define BASEMAXINLINE 256
463 #ifdef IMAP_STORAGE
464 #define ENDL "\r\n"
465 #else
466 #define ENDL "\n"
467 #endif
468 
469 #define MAX_DATETIME_FORMAT 512
470 #define MAX_NUM_CID_CONTEXTS 10
471 
472 #define VM_REVIEW (1 << 0) /*!< After recording, permit the caller to review the recording before saving */
473 #define VM_OPERATOR (1 << 1) /*!< Allow 0 to be pressed to go to 'o' extension */
474 #define VM_SAYCID (1 << 2) /*!< Repeat the CallerID info during envelope playback */
475 #define VM_SVMAIL (1 << 3) /*!< Allow the user to compose a new VM from within VoicemailMain */
476 #define VM_ENVELOPE (1 << 4) /*!< Play the envelope information (who-from, time received, etc.) */
477 #define VM_SAYDURATION (1 << 5) /*!< Play the length of the message during envelope playback */
478 #define VM_SKIPAFTERCMD (1 << 6) /*!< After deletion, assume caller wants to go to the next message */
479 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
480 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
481 #define VM_PBXSKIP (1 << 9) /*!< Skip the [PBX] preamble in the Subject line of emails */
482 #define VM_DIRECFORWARD (1 << 10) /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
483 #define VM_ATTACH (1 << 11) /*!< Attach message to voicemail notifications? */
484 #define VM_DELETE (1 << 12) /*!< Delete message after sending notification */
485 #define VM_ALLOCED (1 << 13) /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
486 #define VM_SEARCH (1 << 14) /*!< Search all contexts for a matching mailbox */
487 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
488 #define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
489 #define VM_MESSAGEWRAP (1 << 17) /*!< Wrap around from the last message to the first, and vice-versa */
490 #define VM_FWDURGAUTO (1 << 18) /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
491 #define ERROR_LOCK_PATH -100
492 #define OPERATOR_EXIT 300
493 
494 
495 enum vm_box {
502 };
503 
505  OPT_SILENT = (1 << 0),
506  OPT_BUSY_GREETING = (1 << 1),
508  OPT_RECORDGAIN = (1 << 3),
510  OPT_AUTOPLAY = (1 << 6),
511  OPT_DTMFEXIT = (1 << 7),
512  OPT_MESSAGE_Urgent = (1 << 8),
514 };
515 
520  /* This *must* be the last value in this enum! */
522 };
523 
528 };
529 
540 });
541 
542 static int load_config(int reload);
543 #ifdef TEST_FRAMEWORK
544 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
545 #endif
546 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
547 
548 /*! \page vmlang Voicemail Language Syntaxes Supported
549 
550  \par Syntaxes supported, not really language codes.
551  \arg \b en - English
552  \arg \b de - German
553  \arg \b es - Spanish
554  \arg \b fr - French
555  \arg \b it - Italian
556  \arg \b nl - Dutch
557  \arg \b pt - Portuguese
558  \arg \b pt_BR - Portuguese (Brazil)
559  \arg \b gr - Greek
560  \arg \b no - Norwegian
561  \arg \b se - Swedish
562  \arg \b tw - Chinese (Taiwan)
563  \arg \b ua - Ukrainian
564 
565 German requires the following additional soundfile:
566 \arg \b 1F einE (feminine)
567 
568 Spanish requires the following additional soundfile:
569 \arg \b 1M un (masculine)
570 
571 Dutch, Greek, Portuguese & Spanish require the following additional soundfiles:
572 \arg \b vm-INBOXs singular of 'new'
573 \arg \b vm-Olds singular of 'old/heard/read'
574 
575 NB these are plural:
576 \arg \b vm-INBOX nieuwe (nl)
577 \arg \b vm-Old oude (nl)
578 
579 Polish uses:
580 \arg \b vm-new-a 'new', feminine singular accusative
581 \arg \b vm-new-e 'new', feminine plural accusative
582 \arg \b vm-new-ych 'new', feminine plural genitive
583 \arg \b vm-old-a 'old', feminine singular accusative
584 \arg \b vm-old-e 'old', feminine plural accusative
585 \arg \b vm-old-ych 'old', feminine plural genitive
586 \arg \b digits/1-a 'one', not always same as 'digits/1'
587 \arg \b digits/2-ie 'two', not always same as 'digits/2'
588 
589 Swedish uses:
590 \arg \b vm-nytt singular of 'new'
591 \arg \b vm-nya plural of 'new'
592 \arg \b vm-gammalt singular of 'old'
593 \arg \b vm-gamla plural of 'old'
594 \arg \b digits/ett 'one', not always same as 'digits/1'
595 
596 Norwegian uses:
597 \arg \b vm-ny singular of 'new'
598 \arg \b vm-nye plural of 'new'
599 \arg \b vm-gammel singular of 'old'
600 \arg \b vm-gamle plural of 'old'
601 
602 Dutch also uses:
603 \arg \b nl-om 'at'?
604 
605 Greek, Portuguese & Spanish also uses:
606 \arg \b vm-youhaveno
607 
608 Italian requires the following additional soundfile:
609 
610 For vm_intro_it:
611 \arg \b vm-nuovo new
612 \arg \b vm-nuovi new plural
613 \arg \b vm-vecchio old
614 \arg \b vm-vecchi old plural
615 
616 Chinese (Taiwan) requires the following additional soundfile:
617 \arg \b vm-tong A class-word for call (tong1)
618 \arg \b vm-ri A class-word for day (ri4)
619 \arg \b vm-you You (ni3)
620 \arg \b vm-haveno Have no (mei2 you3)
621 \arg \b vm-have Have (you3)
622 \arg \b vm-listen To listen (yao4 ting1)
623 
624 
625 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
626 spelled among others when you have to change folder. For the above reasons, vm-INBOX
627 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
628 
629 */
630 
631 struct baseio {
632  int iocp;
633  int iolen;
634  int linelength;
635  int ateof;
636  unsigned char iobuf[BASEMAXINLINE];
637 };
638 
639 /*! Structure for linked list of users
640  * Use ast_vm_user_destroy() to free one of these structures. */
641 struct ast_vm_user {
642  char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
643  char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
644  char password[80]; /*!< Secret pin code, numbers only */
645  char fullname[80]; /*!< Full name, for directory app */
646  char email[80]; /*!< E-mail address */
647  char *emailsubject; /*!< E-mail subject */
648  char *emailbody; /*!< E-mail body */
649  char pager[80]; /*!< E-mail address to pager (no attachment) */
650  char serveremail[80]; /*!< From: Mail address */
651  char language[MAX_LANGUAGE]; /*!< Config: Language setting */
652  char zonetag[80]; /*!< Time zone */
653  char locale[20]; /*!< The locale (for presentation of date/time) */
654  char callback[80];
655  char dialout[80];
656  char uniqueid[80]; /*!< Unique integer identifier */
657  char exit[80];
658  char attachfmt[20]; /*!< Attachment format */
659  unsigned int flags; /*!< VM_ flags */
660  int saydurationm;
661  int minsecs; /*!< Minimum number of seconds per message for this mailbox */
662  int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
663  int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
664  int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
665  int passwordlocation; /*!< Storage location of the password */
666 #ifdef IMAP_STORAGE
667  char imapuser[80]; /*!< IMAP server login */
668  char imappassword[80]; /*!< IMAP server password if authpassword not defined */
669  char imapfolder[64]; /*!< IMAP voicemail folder */
670  char imapvmshareid[80]; /*!< Shared mailbox ID to use rather than the dialed one */
671  int imapversion; /*!< If configuration changes, use the new values */
672 #endif
673  double volgain; /*!< Volume gain for voicemails sent via email */
675 };
676 
677 /*! Voicemail time zones */
678 struct vm_zone {
679  AST_LIST_ENTRY(vm_zone) list;
680  char name[80];
681  char timezone[80];
682  char msg_format[512];
683 };
684 
685 #define VMSTATE_MAX_MSG_ARRAY 256
686 
687 /*! Voicemail mailbox state */
688 struct vm_state {
689  char curbox[80];
690  char username[80];
691  char context[80];
692  char curdir[PATH_MAX];
693  char vmbox[PATH_MAX];
694  char fn[PATH_MAX];
695  char intro[PATH_MAX];
696  int *deleted;
697  int *heard;
698  int dh_arraysize; /* used for deleted / heard allocation */
699  int curmsg;
700  int lastmsg;
701  int newmessages;
702  int oldmessages;
703  int urgentmessages;
704  int starting;
705  int repeats;
706 #ifdef IMAP_STORAGE
708  int updated; /*!< decremented on each mail check until 1 -allows delay */
709  long *msgArray;
710  unsigned msg_array_max;
711  MAILSTREAM *mailstream;
712  int vmArrayIndex;
713  char imapuser[80]; /*!< IMAP server login */
714  char imapfolder[64]; /*!< IMAP voicemail folder */
715  int imapversion;
716  int interactive;
717  char introfn[PATH_MAX]; /*!< Name of prepended file */
718  unsigned int quota_limit;
719  unsigned int quota_usage;
720  struct vm_state *persist_vms;
721 #endif
722 };
723 
724 #ifdef ODBC_STORAGE
725 static char odbc_database[80];
726 static char odbc_table[80];
727 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
728 #define DISPOSE(a,b) remove_file(a,b)
729 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
730 #define EXISTS(a,b,c,d) (message_exists(a,b))
731 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
732 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
733 #define DELETE(a,b,c,d) (delete_file(a,b))
734 #else
735 #ifdef IMAP_STORAGE
736 #define DISPOSE(a,b) (imap_remove_file(a,b))
737 #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))
738 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
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_file(g,h));
742 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
743 #else
744 #define RETRIEVE(a,b,c,d)
745 #define DISPOSE(a,b)
746 #define STORE(a,b,c,d,e,f,g,h,i,j)
747 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
748 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
749 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
750 #define DELETE(a,b,c,d) (vm_delete(c))
751 #endif
752 #endif
753 
754 static char VM_SPOOL_DIR[PATH_MAX];
755 
756 static char ext_pass_cmd[128];
757 static char ext_pass_check_cmd[128];
758 
759 static int my_umask;
760 
761 #define PWDCHANGE_INTERNAL (1 << 1)
762 #define PWDCHANGE_EXTERNAL (1 << 2)
764 
765 #ifdef ODBC_STORAGE
766 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
767 #else
768 # ifdef IMAP_STORAGE
769 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
770 # else
771 # define tdesc "Comedian Mail (Voicemail System)"
772 # endif
773 #endif
774 
775 static char userscontext[AST_MAX_EXTENSION] = "default";
776 
777 static char *addesc = "Comedian Mail";
778 
779 /* Leave a message */
780 static char *app = "VoiceMail";
781 
782 /* Check mail, control, etc */
783 static char *app2 = "VoiceMailMain";
784 
785 static char *app3 = "MailboxExists";
786 static char *app4 = "VMAuthenticate";
787 
788 static char *sayname_app = "VMSayName";
789 
791 static AST_LIST_HEAD_STATIC(zones, vm_zone);
792 static char zonetag[80];
793 static char locale[20];
794 static int maxsilence;
795 static int maxmsg;
796 static int maxdeletedmsg;
797 static int silencethreshold = 128;
798 static char serveremail[80];
799 static char mailcmd[160]; /* Configurable mail cmd */
800 static char externnotify[160];
801 static struct ast_smdi_interface *smdi_iface = NULL;
802 static char vmfmts[80];
803 static double volgain;
804 static int vmminsecs;
805 static int vmmaxsecs;
806 static int maxgreet;
807 static int skipms;
808 static int maxlogins;
809 static int minpassword;
810 static int passwordlocation;
811 
812 /*! Poll mailboxes for changes since there is something external to
813  * app_voicemail that may change them. */
814 static unsigned int poll_mailboxes;
815 
816 /*! Polling frequency */
817 static unsigned int poll_freq;
818 /*! By default, poll every 30 seconds */
819 #define DEFAULT_POLL_FREQ 30
820 
822 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
823 static pthread_t poll_thread = AST_PTHREADT_NULL;
824 static unsigned char poll_thread_run;
825 
826 /*! Subscription to ... MWI event subscriptions */
827 static struct ast_event_sub *mwi_sub_sub;
828 /*! Subscription to ... MWI event un-subscriptions */
830 
831 /*!
832  * \brief An MWI subscription
833  *
834  * This is so we can keep track of which mailboxes are subscribed to.
835  * This way, we know which mailboxes to poll when the pollmailboxes
836  * option is being used.
837  */
838 struct mwi_sub {
839  AST_RWLIST_ENTRY(mwi_sub) entry;
840  int old_urgent;
841  int old_new;
842  int old_old;
843  uint32_t uniqueid;
844  char mailbox[1];
845 };
846 
847 struct mwi_sub_task {
848  const char *mailbox;
849  const char *context;
850  uint32_t uniqueid;
851 };
852 
854 
856 
857 /* custom audio control prompts for voicemail playback */
860 static char listen_control_pause_key[12];
862 static char listen_control_stop_key[12];
863 
864 /* custom password sounds */
865 static char vm_password[80] = "vm-password";
866 static char vm_newpassword[80] = "vm-newpassword";
867 static char vm_passchanged[80] = "vm-passchanged";
868 static char vm_reenterpassword[80] = "vm-reenterpassword";
869 static char vm_mismatch[80] = "vm-mismatch";
870 static char vm_invalid_password[80] = "vm-invalid-password";
871 static char vm_pls_try_again[80] = "vm-pls-try-again";
872 
873 /*
874  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
875  * 1. create a sound along the lines of "Please try again. When done, press the pound key" which could be spliced
876  * from existing sound clips. This would require some programming changes in the area of vm_forward options and also
877  * app.c's __ast_play_and_record function
878  * 2. create a sound prompt saying "Please try again. When done recording, press any key to stop and send the prepended
879  * message." At the time of this comment, I think this would require new voice work to be commissioned.
880  * 3. Something way different like providing instructions before a time out or a post-recording menu. This would require
881  * more effort than either of the other two.
882  */
883 static char vm_prepend_timeout[80] = "vm-then-pound";
884 
885 static struct ast_flags globalflags = {0};
886 
887 static int saydurationminfo;
888 
889 static char dialcontext[AST_MAX_CONTEXT] = "";
890 static char callcontext[AST_MAX_CONTEXT] = "";
891 static char exitcontext[AST_MAX_CONTEXT] = "";
892 
894 
895 
896 static char *emailbody = NULL;
897 static char *emailsubject = NULL;
898 static char *pagerbody = NULL;
899 static char *pagersubject = NULL;
900 static char fromstring[100];
901 static char pagerfromstring[100];
902 static char charset[32] = "ISO-8859-1";
903 
904 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
905 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
906 static int adsiver = 1;
907 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
908 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
909 
910 /* Forward declarations - generic */
911 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
912 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);
913 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
914 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
915  char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
916  signed char record_gain, struct vm_state *vms, char *flag);
917 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
918 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
919 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);
920 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);
921 static void apply_options(struct ast_vm_user *vmu, const char *options);
922 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);
923 static int is_valid_dtmf(const char *key);
924 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
925 static int write_password_to_file(const char *secretfn, const char *password);
926 static const char *substitute_escapes(const char *value);
927 static void free_user(struct ast_vm_user *vmu);
928 
930 
931 struct inprocess {
932  int count;
933  char *context;
934  char mailbox[0];
935 };
936 
937 static int inprocess_hash_fn(const void *obj, const int flags)
938 {
939  const struct inprocess *i = obj;
940  return atoi(i->mailbox);
941 }
942 
943 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
944 {
945  struct inprocess *i = obj, *j = arg;
946  if (strcmp(i->mailbox, j->mailbox)) {
947  return 0;
948  }
949  return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
950 }
951 
952 static int inprocess_count(const char *context, const char *mailbox, int delta)
953 {
954  struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
955  arg->context = arg->mailbox + strlen(mailbox) + 1;
956  strcpy(arg->mailbox, mailbox); /* SAFE */
957  strcpy(arg->context, context); /* SAFE */
958  ao2_lock(inprocess_container);
959  if ((i = ao2_find(inprocess_container, arg, 0))) {
960  int ret = ast_atomic_fetchadd_int(&i->count, delta);
961  ao2_unlock(inprocess_container);
962  ao2_ref(i, -1);
963  return ret;
964  }
965  if (delta < 0) {
966  ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
967  }
968  if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
969  ao2_unlock(inprocess_container);
970  return 0;
971  }
972  i->context = i->mailbox + strlen(mailbox) + 1;
973  strcpy(i->mailbox, mailbox); /* SAFE */
974  strcpy(i->context, context); /* SAFE */
975  i->count = delta;
976  ao2_link(inprocess_container, i);
977  ao2_unlock(inprocess_container);
978  ao2_ref(i, -1);
979  return 0;
980 }
981 
982 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
983 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
984 #endif
985 
986 /*!
987  * \brief Strips control and non 7-bit clean characters from input string.
988  *
989  * \note To map control and none 7-bit characters to a 7-bit clean characters
990  * please use ast_str_encode_mine().
991  */
992 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
993 {
994  char *bufptr = buf;
995  for (; *input; input++) {
996  if (*input < 32) {
997  continue;
998  }
999  *bufptr++ = *input;
1000  if (bufptr == buf + buflen - 1) {
1001  break;
1002  }
1003  }
1004  *bufptr = '\0';
1005  return buf;
1006 }
1007 
1008 
1009 /*!
1010  * \brief Sets default voicemail system options to a voicemail user.
1011  *
1012  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
1013  * - all the globalflags
1014  * - the saydurationminfo
1015  * - the callcontext
1016  * - the dialcontext
1017  * - the exitcontext
1018  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
1019  * - volume gain
1020  * - emailsubject, emailbody set to NULL
1021  */
1022 static void populate_defaults(struct ast_vm_user *vmu)
1023 {
1024  ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
1026  if (saydurationminfo) {
1028  }
1029  ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
1030  ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
1031  ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
1032  ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
1033  ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
1034  if (vmminsecs) {
1035  vmu->minsecs = vmminsecs;
1036  }
1037  if (vmmaxsecs) {
1038  vmu->maxsecs = vmmaxsecs;
1039  }
1040  if (maxmsg) {
1041  vmu->maxmsg = maxmsg;
1042  }
1043  if (maxdeletedmsg) {
1045  }
1046  vmu->volgain = volgain;
1047  ast_free(vmu->emailsubject);
1048  vmu->emailsubject = NULL;
1049  ast_free(vmu->emailbody);
1050  vmu->emailbody = NULL;
1051 #ifdef IMAP_STORAGE
1052  ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
1053 #endif
1054 }
1055 
1056 /*!
1057  * \brief Sets a a specific property value.
1058  * \param vmu The voicemail user object to work with.
1059  * \param var The name of the property to be set.
1060  * \param value The value to be set to the property.
1061  *
1062  * The property name must be one of the understood properties. See the source for details.
1063  */
1064 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
1065 {
1066  int x;
1067  if (!strcasecmp(var, "attach")) {
1068  ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
1069  } else if (!strcasecmp(var, "attachfmt")) {
1070  ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
1071  } else if (!strcasecmp(var, "serveremail")) {
1072  ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
1073  } else if (!strcasecmp(var, "emailbody")) {
1074  vmu->emailbody = ast_strdup(substitute_escapes(value));
1075  } else if (!strcasecmp(var, "emailsubject")) {
1077  } else if (!strcasecmp(var, "language")) {
1078  ast_copy_string(vmu->language, value, sizeof(vmu->language));
1079  } else if (!strcasecmp(var, "tz")) {
1080  ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
1081  } else if (!strcasecmp(var, "locale")) {
1082  ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
1083 #ifdef IMAP_STORAGE
1084  } else if (!strcasecmp(var, "imapuser")) {
1085  ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
1086  vmu->imapversion = imapversion;
1087  } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
1088  ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
1089  vmu->imapversion = imapversion;
1090  } else if (!strcasecmp(var, "imapfolder")) {
1091  ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
1092  } else if (!strcasecmp(var, "imapvmshareid")) {
1093  ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
1094  vmu->imapversion = imapversion;
1095 #endif
1096  } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
1097  ast_set2_flag(vmu, ast_true(value), VM_DELETE);
1098  } else if (!strcasecmp(var, "saycid")){
1099  ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
1100  } else if (!strcasecmp(var, "sendvoicemail")){
1101  ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
1102  } else if (!strcasecmp(var, "review")){
1103  ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
1104  } else if (!strcasecmp(var, "tempgreetwarn")){
1105  ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
1106  } else if (!strcasecmp(var, "messagewrap")){
1107  ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);
1108  } else if (!strcasecmp(var, "operator")) {
1109  ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
1110  } else if (!strcasecmp(var, "envelope")){
1111  ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
1112  } else if (!strcasecmp(var, "moveheard")){
1113  ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
1114  } else if (!strcasecmp(var, "sayduration")){
1115  ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
1116  } else if (!strcasecmp(var, "saydurationm")){
1117  if (sscanf(value, "%30d", &x) == 1) {
1118  vmu->saydurationm = x;
1119  } else {
1120  ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
1121  }
1122  } else if (!strcasecmp(var, "forcename")){
1123  ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
1124  } else if (!strcasecmp(var, "forcegreetings")){
1125  ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
1126  } else if (!strcasecmp(var, "callback")) {
1127  ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
1128  } else if (!strcasecmp(var, "dialout")) {
1129  ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
1130  } else if (!strcasecmp(var, "exitcontext")) {
1131  ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
1132  } else if (!strcasecmp(var, "minsecs")) {
1133  if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
1134  vmu->minsecs = x;
1135  } else {
1136  ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
1137  vmu->minsecs = vmminsecs;
1138  }
1139  } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
1140  vmu->maxsecs = atoi(value);
1141  if (vmu->maxsecs <= 0) {
1142  ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
1143  vmu->maxsecs = vmmaxsecs;
1144  } else {
1145  vmu->maxsecs = atoi(value);
1146  }
1147  if (!strcasecmp(var, "maxmessage"))
1148  ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
1149  } else if (!strcasecmp(var, "maxmsg")) {
1150  vmu->maxmsg = atoi(value);
1151  /* Accept maxmsg=0 (Greetings only voicemail) */
1152  if (vmu->maxmsg < 0) {
1153  ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
1154  vmu->maxmsg = MAXMSG;
1155  } else if (vmu->maxmsg > MAXMSGLIMIT) {
1156  ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
1157  vmu->maxmsg = MAXMSGLIMIT;
1158  }
1159  } else if (!strcasecmp(var, "nextaftercmd")) {
1160  ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
1161  } else if (!strcasecmp(var, "backupdeleted")) {
1162  if (sscanf(value, "%30d", &x) == 1)
1163  vmu->maxdeletedmsg = x;
1164  else if (ast_true(value))
1165  vmu->maxdeletedmsg = MAXMSG;
1166  else
1167  vmu->maxdeletedmsg = 0;
1168 
1169  if (vmu->maxdeletedmsg < 0) {
1170  ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
1171  vmu->maxdeletedmsg = MAXMSG;
1172  } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
1173  ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
1174  vmu->maxdeletedmsg = MAXMSGLIMIT;
1175  }
1176  } else if (!strcasecmp(var, "volgain")) {
1177  sscanf(value, "%30lf", &vmu->volgain);
1178  } else if (!strcasecmp(var, "passwordlocation")) {
1179  if (!strcasecmp(value, "spooldir")) {
1181  } else {
1183  }
1184  } else if (!strcasecmp(var, "options")) {
1185  apply_options(vmu, value);
1186  }
1187 }
1188 
1189 static char *vm_check_password_shell(char *command, char *buf, size_t len)
1190 {
1191  int fds[2], pid = 0;
1192 
1193  memset(buf, 0, len);
1194 
1195  if (pipe(fds)) {
1196  snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
1197  } else {
1198  /* good to go*/
1199  pid = ast_safe_fork(0);
1200 
1201  if (pid < 0) {
1202  /* ok maybe not */
1203  close(fds[0]);
1204  close(fds[1]);
1205  snprintf(buf, len, "FAILURE: Fork failed");
1206  } else if (pid) {
1207  /* parent */
1208  close(fds[1]);
1209  if (read(fds[0], buf, len) < 0) {
1210  ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
1211  }
1212  close(fds[0]);
1213  } else {
1214  /* child */
1216  AST_APP_ARG(v)[20];
1217  );
1218  char *mycmd = ast_strdupa(command);
1219 
1220  close(fds[0]);
1221  dup2(fds[1], STDOUT_FILENO);
1222  close(fds[1]);
1223  ast_close_fds_above_n(STDOUT_FILENO);
1224 
1225  AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
1226 
1227  execv(arg.v[0], arg.v);
1228  printf("FAILURE: %s", strerror(errno));
1229  _exit(0);
1230  }
1231  }
1232  return buf;
1233 }
1234 
1235 /*!
1236  * \brief Check that password meets minimum required length
1237  * \param vmu The voicemail user to change the password for.
1238  * \param password The password string to check
1239  *
1240  * \return zero on ok, 1 on not ok.
1241  */
1242 static int check_password(struct ast_vm_user *vmu, char *password)
1243 {
1244  /* check minimum length */
1245  if (strlen(password) < minpassword)
1246  return 1;
1247  /* check that password does not contain '*' character */
1248  if (!ast_strlen_zero(password) && password[0] == '*')
1249  return 1;
1250  if (!ast_strlen_zero(ext_pass_check_cmd)) {
1251  char cmd[255], buf[255];
1252 
1253  ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
1254 
1255  snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
1256  if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
1257  ast_debug(5, "Result: %s\n", buf);
1258  if (!strncasecmp(buf, "VALID", 5)) {
1259  ast_debug(3, "Passed password check: '%s'\n", buf);
1260  return 0;
1261  } else if (!strncasecmp(buf, "FAILURE", 7)) {
1262  ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
1263  return 0;
1264  } else {
1265  ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
1266  return 1;
1267  }
1268  }
1269  }
1270  return 0;
1271 }
1272 
1273 /*!
1274  * \brief Performs a change of the voicemail passowrd in the realtime engine.
1275  * \param vmu The voicemail user to change the password for.
1276  * \param password The new value to be set to the password for this user.
1277  *
1278  * This only works if there is a realtime engine configured.
1279  * This is called from the (top level) vm_change_password.
1280  *
1281  * \return zero on success, -1 on error.
1282  */
1283 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
1284 {
1285  int res = -1;
1286  if (!strcmp(vmu->password, password)) {
1287  /* No change (but an update would return 0 rows updated, so we opt out here) */
1288  return 0;
1289  }
1290 
1291  if (strlen(password) > 10) {
1292  ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
1293  }
1294  if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
1295  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
1296  ast_copy_string(vmu->password, password, sizeof(vmu->password));
1297  res = 0;
1298  }
1299  return res;
1300 }
1301 
1302 /*!
1303  * \brief Destructively Parse options and apply.
1304  */
1305 static void apply_options(struct ast_vm_user *vmu, const char *options)
1306 {
1307  char *stringp;
1308  char *s;
1309  char *var, *value;
1310  stringp = ast_strdupa(options);
1311  while ((s = strsep(&stringp, "|"))) {
1312  value = s;
1313  if ((var = strsep(&value, "=")) && value) {
1314  apply_option(vmu, var, value);
1315  }
1316  }
1317 }
1318 
1319 /*!
1320  * \brief Loads the options specific to a voicemail user.
1321  *
1322  * This is called when a vm_user structure is being set up, such as from load_options.
1323  */
1324 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
1325 {
1326  for (; var; var = var->next) {
1327  if (!strcasecmp(var->name, "vmsecret")) {
1328  ast_copy_string(retval->password, var->value, sizeof(retval->password));
1329  } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
1330  if (ast_strlen_zero(retval->password)) {
1331  if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
1332  ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
1333  "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
1334  } else {
1335  ast_copy_string(retval->password, var->value, sizeof(retval->password));
1336  }
1337  }
1338  } else if (!strcasecmp(var->name, "uniqueid")) {
1339  ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
1340  } else if (!strcasecmp(var->name, "pager")) {
1341  ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
1342  } else if (!strcasecmp(var->name, "email")) {
1343  ast_copy_string(retval->email, var->value, sizeof(retval->email));
1344  } else if (!strcasecmp(var->name, "fullname")) {
1345  ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
1346  } else if (!strcasecmp(var->name, "context")) {
1347  ast_copy_string(retval->context, var->value, sizeof(retval->context));
1348  } else if (!strcasecmp(var->name, "emailsubject")) {
1349  ast_free(retval->emailsubject);
1351  } else if (!strcasecmp(var->name, "emailbody")) {
1352  ast_free(retval->emailbody);
1353  retval->emailbody = ast_strdup(substitute_escapes(var->value));
1354 #ifdef IMAP_STORAGE
1355  } else if (!strcasecmp(var->name, "imapuser")) {
1356  ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
1357  retval->imapversion = imapversion;
1358  } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
1359  ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
1360  retval->imapversion = imapversion;
1361  } else if (!strcasecmp(var->name, "imapfolder")) {
1362  ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
1363  } else if (!strcasecmp(var->name, "imapvmshareid")) {
1364  ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
1365  retval->imapversion = imapversion;
1366 #endif
1367  } else
1368  apply_option(retval, var->name, var->value);
1369  }
1370 }
1371 
1372 /*!
1373  * \brief Determines if a DTMF key entered is valid.
1374  * \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.
1375  *
1376  * Tests the character entered against the set of valid DTMF characters.
1377  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1378  */
1379 static int is_valid_dtmf(const char *key)
1380 {
1381  int i;
1382  char *local_key = ast_strdupa(key);
1383 
1384  for (i = 0; i < strlen(key); ++i) {
1385  if (!strchr(VALID_DTMF, *local_key)) {
1386  ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1387  return 0;
1388  }
1389  local_key++;
1390  }
1391  return 1;
1392 }
1393 
1394 /*!
1395  * \brief Finds a voicemail user from the realtime engine.
1396  * \param ivm
1397  * \param context
1398  * \param mailbox
1399  *
1400  * 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.
1401  *
1402  * \return The ast_vm_user structure for the user that was found.
1403  */
1404 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1405 {
1406  struct ast_variable *var;
1407  struct ast_vm_user *retval;
1408 
1409  if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1410  if (ivm) {
1411  memset(retval, 0, sizeof(*retval));
1412  }
1413  populate_defaults(retval);
1414  if (!ivm) {
1415  ast_set_flag(retval, VM_ALLOCED);
1416  }
1417  if (mailbox) {
1418  ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1419  }
1420  if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
1421  var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1422  } else {
1423  var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1424  }
1425  if (var) {
1426  apply_options_full(retval, var);
1427  ast_variables_destroy(var);
1428  } else {
1429  if (!ivm)
1430  free_user(retval);
1431  retval = NULL;
1432  }
1433  }
1434  return retval;
1435 }
1436 
1437 /*!
1438  * \brief Finds a voicemail user from the users file or the realtime engine.
1439  * \param ivm
1440  * \param context
1441  * \param mailbox
1442  *
1443  * \return The ast_vm_user structure for the user that was found.
1444  */
1445 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1446 {
1447  /* This function could be made to generate one from a database, too */
1448  struct ast_vm_user *vmu = NULL, *cur;
1449  AST_LIST_LOCK(&users);
1450 
1451  if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
1452  context = "default";
1453 
1454  AST_LIST_TRAVERSE(&users, cur, list) {
1455 #ifdef IMAP_STORAGE
1456  if (cur->imapversion != imapversion) {
1457  continue;
1458  }
1459 #endif
1460  if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1461  break;
1462  if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1463  break;
1464  }
1465  if (cur) {
1466  /* Make a copy, so that on a reload, we have no race */
1467  if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
1468  *vmu = *cur;
1469  if (!ivm) {
1470  vmu->emailbody = ast_strdup(cur->emailbody);
1471  vmu->emailsubject = ast_strdup(cur->emailsubject);
1472  }
1473  ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1474  AST_LIST_NEXT(vmu, list) = NULL;
1475  }
1476  } else
1477  vmu = find_user_realtime(ivm, context, mailbox);
1479  return vmu;
1480 }
1481 
1482 /*!
1483  * \brief Resets a user password to a specified password.
1484  * \param context
1485  * \param mailbox
1486  * \param newpass
1487  *
1488  * This does the actual change password work, called by the vm_change_password() function.
1489  *
1490  * \return zero on success, -1 on error.
1491  */
1492 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
1493 {
1494  /* This function could be made to generate one from a database, too */
1495  struct ast_vm_user *cur;
1496  int res = -1;
1497  AST_LIST_LOCK(&users);
1498  AST_LIST_TRAVERSE(&users, cur, list) {
1499  if ((!context || !strcasecmp(context, cur->context)) &&
1500  (!strcasecmp(mailbox, cur->mailbox)))
1501  break;
1502  }
1503  if (cur) {
1504  ast_copy_string(cur->password, newpass, sizeof(cur->password));
1505  res = 0;
1506  }
1508  return res;
1509 }
1510 
1511 /*!
1512  * \brief Check if configuration file is valid
1513  */
1514 static inline int valid_config(const struct ast_config *cfg)
1515 {
1516  return cfg && cfg != CONFIG_STATUS_FILEINVALID;
1517 }
1518 
1519 /*!
1520  * \brief The handler for the change password option.
1521  * \param vmu The voicemail user to work with.
1522  * \param newpassword The new password (that has been gathered from the appropriate prompting).
1523  * 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.
1524  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
1525  */
1526 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
1527 {
1528  struct ast_config *cfg = NULL;
1529  struct ast_variable *var = NULL;
1530  struct ast_category *cat = NULL;
1531  char *category = NULL, *value = NULL, *new = NULL;
1532  const char *tmp = NULL;
1533  struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
1534  char secretfn[PATH_MAX] = "";
1535  int found = 0;
1536 
1537  if (!change_password_realtime(vmu, newpassword))
1538  return;
1539 
1540  /* check if we should store the secret in the spool directory next to the messages */
1541  switch (vmu->passwordlocation) {
1542  case OPT_PWLOC_SPOOLDIR:
1543  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
1544  if (write_password_to_file(secretfn, newpassword) == 0) {
1545  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
1546  ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
1547  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1548  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1549  break;
1550  } else {
1551  ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
1552  }
1553  /* Fall-through */
1555  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
1556  while ((category = ast_category_browse(cfg, category))) {
1557  if (!strcasecmp(category, vmu->context)) {
1558  if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
1559  ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
1560  break;
1561  }
1562  value = strstr(tmp, ",");
1563  if (!value) {
1564  new = ast_alloca(strlen(newpassword)+1);
1565  sprintf(new, "%s", newpassword);
1566  } else {
1567  new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
1568  sprintf(new, "%s%s", newpassword, value);
1569  }
1570  if (!(cat = ast_category_get(cfg, category))) {
1571  ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
1572  break;
1573  }
1574  ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
1575  found = 1;
1576  }
1577  }
1578  /* save the results */
1579  if (found) {
1580  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
1581  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1582  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1583  ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
1584  ast_config_destroy(cfg);
1585  break;
1586  }
1587 
1588  ast_config_destroy(cfg);
1589  }
1590  /* Fall-through */
1591  case OPT_PWLOC_USERSCONF:
1592  /* check users.conf and update the password stored for the mailbox */
1593  /* if no vmsecret entry exists create one. */
1594  if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
1595  ast_debug(4, "we are looking for %s\n", vmu->mailbox);
1596  for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
1597  ast_debug(4, "users.conf: %s\n", category);
1598  if (!strcasecmp(category, vmu->mailbox)) {
1599  if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
1600  ast_debug(3, "looks like we need to make vmsecret!\n");
1601  var = ast_variable_new("vmsecret", newpassword, "");
1602  } else {
1603  var = NULL;
1604  }
1605  new = ast_alloca(strlen(newpassword) + 1);
1606  sprintf(new, "%s", newpassword);
1607  if (!(cat = ast_category_get(cfg, category))) {
1608  ast_debug(4, "failed to get category!\n");
1609  ast_free(var);
1610  break;
1611  }
1612  if (!var) {
1613  ast_variable_update(cat, "vmsecret", new, NULL, 0);
1614  } else {
1615  ast_variable_append(cat, var);
1616  }
1617  found = 1;
1618  break;
1619  }
1620  }
1621  /* save the results and clean things up */
1622  if (found) {
1623  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
1624  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1625  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1626  ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
1627  }
1628 
1629  ast_config_destroy(cfg);
1630  }
1631  }
1632 }
1633 
1634 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
1635 {
1636  char buf[255];
1637  snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
1638  ast_debug(1, "External password: %s\n",buf);
1639  if (!ast_safe_system(buf)) {
1640  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
1641  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1642  /* Reset the password in memory, too */
1643  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1644  }
1645 }
1646 
1647 /*!
1648  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1649  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1650  * \param len The length of the path string that was written out.
1651  * \param context
1652  * \param ext
1653  * \param folder
1654  *
1655  * The path is constructed as
1656  * VM_SPOOL_DIRcontext/ext/folder
1657  *
1658  * \return zero on success, -1 on error.
1659  */
1660 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
1661 {
1662  return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
1663 }
1664 
1665 /*!
1666  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1667  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1668  * \param len The length of the path string that was written out.
1669  * \param dir
1670  * \param num
1671  *
1672  * The path is constructed as
1673  * VM_SPOOL_DIRcontext/ext/folder
1674  *
1675  * \return zero on success, -1 on error.
1676  */
1677 static int make_file(char *dest, const int len, const char *dir, const int num)
1678 {
1679  return snprintf(dest, len, "%s/msg%04d", dir, num);
1680 }
1681 
1682 /* same as mkstemp, but return a FILE * */
1683 static FILE *vm_mkftemp(char *template)
1684 {
1685  FILE *p = NULL;
1686  int pfd = mkstemp(template);
1687  chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1688  if (pfd > -1) {
1689  p = fdopen(pfd, "w+");
1690  if (!p) {
1691  close(pfd);
1692  pfd = -1;
1693  }
1694  }
1695  return p;
1696 }
1697 
1698 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1699  * \param dest String. base directory.
1700  * \param len Length of dest.
1701  * \param context String. Ignored if is null or empty string.
1702  * \param ext String. Ignored if is null or empty string.
1703  * \param folder String. Ignored if is null or empty string.
1704  * \return -1 on failure, 0 on success.
1705  */
1706 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1707 {
1708  mode_t mode = VOICEMAIL_DIR_MODE;
1709  int res;
1710 
1711  make_dir(dest, len, context, ext, folder);
1712  if ((res = ast_mkdir(dest, mode))) {
1713  ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1714  return -1;
1715  }
1716  return 0;
1717 }
1718 
1719 static const char * const mailbox_folders[] = {
1720 #ifdef IMAP_STORAGE
1721  imapfolder,
1722 #else
1723  "INBOX",
1724 #endif
1725  "Old",
1726  "Work",
1727  "Family",
1728  "Friends",
1729  "Cust1",
1730  "Cust2",
1731  "Cust3",
1732  "Cust4",
1733  "Cust5",
1734  "Deleted",
1735  "Urgent",
1736 };
1737 
1738 static const char *mbox(struct ast_vm_user *vmu, int id)
1739 {
1740 #ifdef IMAP_STORAGE
1741  if (vmu && id == 0) {
1742  return vmu->imapfolder;
1743  }
1744 #endif
1745  return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
1746 }
1747 
1748 static int get_folder_by_name(const char *name)
1749 {
1750  size_t i;
1751 
1752  for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
1753  if (strcasecmp(name, mailbox_folders[i]) == 0) {
1754  return i;
1755  }
1756  }
1757 
1758  return -1;
1759 }
1760 
1761 static void free_user(struct ast_vm_user *vmu)
1762 {
1763  if (ast_test_flag(vmu, VM_ALLOCED)) {
1764 
1765  ast_free(vmu->emailbody);
1766  vmu->emailbody = NULL;
1767 
1768  ast_free(vmu->emailsubject);
1769  vmu->emailsubject = NULL;
1770 
1771  ast_free(vmu);
1772  }
1773 }
1774 
1775 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
1776 
1777  int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
1778 
1779  /* remove old allocation */
1780  if (vms->deleted) {
1781  ast_free(vms->deleted);
1782  vms->deleted = NULL;
1783  }
1784  if (vms->heard) {
1785  ast_free(vms->heard);
1786  vms->heard = NULL;
1787  }
1788  vms->dh_arraysize = 0;
1789 
1790  if (arraysize > 0) {
1791  if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
1792  return -1;
1793  }
1794  if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
1795  ast_free(vms->deleted);
1796  vms->deleted = NULL;
1797  return -1;
1798  }
1799  vms->dh_arraysize = arraysize;
1800  }
1801 
1802  return 0;
1803 }
1804 
1805 /* All IMAP-specific functions should go in this block. This
1806  * keeps them from being spread out all over the code */
1807 #ifdef IMAP_STORAGE
1808 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
1809 {
1810  char arg[10];
1811  struct vm_state *vms;
1812  unsigned long messageNum;
1813 
1814  /* If greetings aren't stored in IMAP, just delete the file */
1815  if (msgnum < 0 && !imapgreetings) {
1816  ast_filedelete(file, NULL);
1817  return;
1818  }
1819 
1820  if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1821  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);
1822  return;
1823  }
1824 
1825  if (msgnum < 0) {
1826  imap_delete_old_greeting(file, vms);
1827  return;
1828  }
1829 
1830  /* find real message number based on msgnum */
1831  /* this may be an index into vms->msgArray based on the msgnum. */
1832  messageNum = vms->msgArray[msgnum];
1833  if (messageNum == 0) {
1834  ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
1835  return;
1836  }
1837  if (option_debug > 2)
1838  ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
1839  /* delete message */
1840  snprintf (arg, sizeof(arg), "%lu", messageNum);
1841  ast_mutex_lock(&vms->lock);
1842  mail_setflag (vms->mailstream, arg, "\\DELETED");
1843  mail_expunge(vms->mailstream);
1844  ast_mutex_unlock(&vms->lock);
1845 }
1846 
1847 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
1848 {
1849  struct vm_state *vms_p;
1850  char *file, *filename;
1851  char *attachment;
1852  int i;
1853  BODY *body;
1854 
1855  /* This function is only used for retrieval of IMAP greetings
1856  * regular messages are not retrieved this way, nor are greetings
1857  * if they are stored locally*/
1858  if (msgnum > -1 || !imapgreetings) {
1859  return 0;
1860  } else {
1861  file = strrchr(ast_strdupa(dir), '/');
1862  if (file)
1863  *file++ = '\0';
1864  else {
1865  ast_debug (1, "Failed to procure file name from directory passed.\n");
1866  return -1;
1867  }
1868  }
1869 
1870  /* check if someone is accessing this box right now... */
1871  if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) &&
1872  !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1873  /* Unlike when retrieving a message, it is reasonable not to be able to find a
1874  * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
1875  * that's all we need to do.
1876  */
1877  if (!(vms_p = create_vm_state_from_user(vmu))) {
1878  ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
1879  return -1;
1880  }
1881  }
1882 
1883  /* Greetings will never have a prepended message */
1884  *vms_p->introfn = '\0';
1885 
1886  ast_mutex_lock(&vms_p->lock);
1887  init_mailstream(vms_p, GREETINGS_FOLDER);
1888  if (!vms_p->mailstream) {
1889  ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
1890  ast_mutex_unlock(&vms_p->lock);
1891  return -1;
1892  }
1893 
1894  /*XXX Yuck, this could probably be done a lot better */
1895  for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
1896  mail_fetchstructure(vms_p->mailstream, i + 1, &body);
1897  /* We have the body, now we extract the file name of the first attachment. */
1898  if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1899  attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
1900  } else {
1901  ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
1902  ast_mutex_unlock(&vms_p->lock);
1903  return -1;
1904  }
1905  filename = strsep(&attachment, ".");
1906  if (!strcmp(filename, file)) {
1907  ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
1908  vms_p->msgArray[vms_p->curmsg] = i + 1;
1909  save_body(body, vms_p, "2", attachment, 0);
1910  ast_mutex_unlock(&vms_p->lock);
1911  return 0;
1912  }
1913  }
1914  ast_mutex_unlock(&vms_p->lock);
1915 
1916  return -1;
1917 }
1918 
1919 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
1920 {
1921  BODY *body;
1922  char *header_content;
1923  char *attachedfilefmt;
1924  char buf[80];
1925  struct vm_state *vms;
1926  char text_file[PATH_MAX];
1927  FILE *text_file_ptr;
1928  int res = 0;
1929  struct ast_vm_user *vmu;
1930 
1931  if (!(vmu = find_user(NULL, context, mailbox))) {
1932  ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
1933  return -1;
1934  }
1935 
1936  if (msgnum < 0) {
1937  if (imapgreetings) {
1938  res = imap_retrieve_greeting(dir, msgnum, vmu);
1939  goto exit;
1940  } else {
1941  res = 0;
1942  goto exit;
1943  }
1944  }
1945 
1946  /* Before anything can happen, we need a vm_state so that we can
1947  * actually access the imap server through the vms->mailstream
1948  */
1949  if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1950  /* This should not happen. If it does, then I guess we'd
1951  * need to create the vm_state, extract which mailbox to
1952  * open, and then set up the msgArray so that the correct
1953  * IMAP message could be accessed. If I have seen correctly
1954  * though, the vms should be obtainable from the vmstates list
1955  * and should have its msgArray properly set up.
1956  */
1957  ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
1958  res = -1;
1959  goto exit;
1960  }
1961 
1962  make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
1963  snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
1964 
1965  /* Don't try to retrieve a message from IMAP if it already is on the file system */
1966  if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
1967  res = 0;
1968  goto exit;
1969  }
1970 
1971  if (option_debug > 2)
1972  ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
1973  if (vms->msgArray[msgnum] == 0) {
1974  ast_log(LOG_WARNING, "Trying to access unknown message\n");
1975  res = -1;
1976  goto exit;
1977  }
1978 
1979  /* This will only work for new messages... */
1980  ast_mutex_lock(&vms->lock);
1981  header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
1982  ast_mutex_unlock(&vms->lock);
1983  /* empty string means no valid header */
1984  if (ast_strlen_zero(header_content)) {
1985  ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
1986  res = -1;
1987  goto exit;
1988  }
1989 
1990  ast_mutex_lock(&vms->lock);
1991  mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
1992  ast_mutex_unlock(&vms->lock);
1993 
1994  /* We have the body, now we extract the file name of the first attachment. */
1995  if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1996  attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
1997  } else {
1998  ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
1999  res = -1;
2000  goto exit;
2001  }
2002 
2003  /* Find the format of the attached file */
2004 
2005  strsep(&attachedfilefmt, ".");
2006  if (!attachedfilefmt) {
2007  ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
2008  res = -1;
2009  goto exit;
2010  }
2011 
2012  save_body(body, vms, "2", attachedfilefmt, 0);
2013  if (save_body(body, vms, "3", attachedfilefmt, 1)) {
2014  *vms->introfn = '\0';
2015  }
2016 
2017  /* Get info from headers!! */
2018  snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
2019 
2020  if (!(text_file_ptr = fopen(text_file, "w"))) {
2021  ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
2022  }
2023 
2024  fprintf(text_file_ptr, "%s\n", "[message]");
2025 
2026  get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
2027  fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
2028  get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
2029  fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
2030  get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
2031  fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
2032  get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
2033  fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
2034  get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
2035  fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
2036  get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
2037  fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
2038  get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
2039  fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
2040  fclose(text_file_ptr);
2041 
2042 exit:
2043  free_user(vmu);
2044  return res;
2045 }
2046 
2047 static int folder_int(const char *folder)
2048 {
2049  /*assume a NULL folder means INBOX*/
2050  if (!folder) {
2051  return 0;
2052  }
2053  if (!strcasecmp(folder, imapfolder)) {
2054  return 0;
2055  } else if (!strcasecmp(folder, "Old")) {
2056  return 1;
2057  } else if (!strcasecmp(folder, "Work")) {
2058  return 2;
2059  } else if (!strcasecmp(folder, "Family")) {
2060  return 3;
2061  } else if (!strcasecmp(folder, "Friends")) {
2062  return 4;
2063  } else if (!strcasecmp(folder, "Cust1")) {
2064  return 5;
2065  } else if (!strcasecmp(folder, "Cust2")) {
2066  return 6;
2067  } else if (!strcasecmp(folder, "Cust3")) {
2068  return 7;
2069  } else if (!strcasecmp(folder, "Cust4")) {
2070  return 8;
2071  } else if (!strcasecmp(folder, "Cust5")) {
2072  return 9;
2073  } else if (!strcasecmp(folder, "Urgent")) {
2074  return 11;
2075  } else { /*assume they meant INBOX if folder is not found otherwise*/
2076  return 0;
2077  }
2078 }
2079 
2080 static int __messagecount(const char *context, const char *mailbox, const char *folder)
2081 {
2082  SEARCHPGM *pgm;
2083  SEARCHHEADER *hdr;
2084 
2085  struct ast_vm_user *vmu, vmus;
2086  struct vm_state *vms_p;
2087  int ret = 0;
2088  int fold = folder_int(folder);
2089  int urgent = 0;
2090 
2091  /* If URGENT, then look at INBOX */
2092  if (fold == 11) {
2093  fold = NEW_FOLDER;
2094  urgent = 1;
2095  }
2096 
2097  if (ast_strlen_zero(mailbox))
2098  return 0;
2099 
2100  /* We have to get the user before we can open the stream! */
2101  vmu = find_user(&vmus, context, mailbox);
2102  if (!vmu) {
2103  ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
2104  return -1;
2105  } else {
2106  /* No IMAP account available */
2107  if (vmu->imapuser[0] == '\0') {
2108  ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2109  return -1;
2110  }
2111  }
2112 
2113  /* No IMAP account available */
2114  if (vmu->imapuser[0] == '\0') {
2115  ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2116  free_user(vmu);
2117  return -1;
2118  }
2119  ast_assert(msgnum < vms->msg_array_max);
2120 
2121  /* check if someone is accessing this box right now... */
2122  vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
2123  if (!vms_p) {
2124  vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
2125  }
2126  if (vms_p) {
2127  ast_debug(3, "Returning before search - user is logged in\n");
2128  if (fold == 0) { /* INBOX */
2129  return urgent ? vms_p->urgentmessages : vms_p->newmessages;
2130  }
2131  if (fold == 1) { /* Old messages */
2132  return vms_p->oldmessages;
2133  }
2134  }
2135 
2136  /* add one if not there... */
2137  vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
2138  if (!vms_p) {
2139  vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
2140  }
2141 
2142  if (!vms_p) {
2143  vms_p = create_vm_state_from_user(vmu);
2144  }
2145  ret = init_mailstream(vms_p, fold);
2146  if (!vms_p->mailstream) {
2147  ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
2148  return -1;
2149  }
2150  if (ret == 0) {
2151  ast_mutex_lock(&vms_p->lock);
2152  pgm = mail_newsearchpgm ();
2153  hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
2154  hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
2155  pgm->header = hdr;
2156  if (fold != OLD_FOLDER) {
2157  pgm->unseen = 1;
2158  pgm->seen = 0;
2159  }
2160  /* In the special case where fold is 1 (old messages) we have to do things a bit
2161  * differently. Old messages are stored in the INBOX but are marked as "seen"
2162  */
2163  else {
2164  pgm->unseen = 0;
2165  pgm->seen = 1;
2166  }
2167  /* look for urgent messages */
2168  if (fold == NEW_FOLDER) {
2169  if (urgent) {
2170  pgm->flagged = 1;
2171  pgm->unflagged = 0;
2172  } else {
2173  pgm->flagged = 0;
2174  pgm->unflagged = 1;
2175  }
2176  }
2177  pgm->undeleted = 1;
2178  pgm->deleted = 0;
2179 
2180  vms_p->vmArrayIndex = 0;
2181  mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2182  if (fold == 0 && urgent == 0)
2183  vms_p->newmessages = vms_p->vmArrayIndex;
2184  if (fold == 1)
2185  vms_p->oldmessages = vms_p->vmArrayIndex;
2186  if (fold == 0 && urgent == 1)
2187  vms_p->urgentmessages = vms_p->vmArrayIndex;
2188  /*Freeing the searchpgm also frees the searchhdr*/
2189  mail_free_searchpgm(&pgm);
2190  ast_mutex_unlock(&vms_p->lock);
2191  vms_p->updated = 0;
2192  return vms_p->vmArrayIndex;
2193  } else {
2194  ast_mutex_lock(&vms_p->lock);
2195  mail_ping(vms_p->mailstream);
2196  ast_mutex_unlock(&vms_p->lock);
2197  }
2198  return 0;
2199 }
2200 
2201 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
2202 {
2203  /* Check if mailbox is full */
2204  check_quota(vms, vmu->imapfolder);
2205  if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
2206  ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
2207  ast_play_and_wait(chan, "vm-mailboxfull");
2208  return -1;
2209  }
2210 
2211  /* Check if we have exceeded maxmsg */
2212  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));
2213  if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
2214  ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
2215  ast_play_and_wait(chan, "vm-mailboxfull");
2216  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2217  return -1;
2218  }
2219 
2220  return 0;
2221 }
2222 
2223 /*!
2224  * \brief Gets the number of messages that exist in a mailbox folder.
2225  * \param context
2226  * \param mailbox
2227  * \param folder
2228  *
2229  * This method is used when IMAP backend is used.
2230  * \return The number of messages in this mailbox folder (zero or more).
2231  */
2232 static int messagecount(const char *context, const char *mailbox, const char *folder)
2233 {
2234  if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
2235  return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
2236  } else {
2237  return __messagecount(context, mailbox, folder);
2238  }
2239 }
2240 
2241 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)
2242 {
2243  char *myserveremail = serveremail;
2244  char fn[PATH_MAX];
2245  char introfn[PATH_MAX];
2246  char mailbox[256];
2247  char *stringp;
2248  FILE *p = NULL;
2249  char tmp[80] = "/tmp/astmail-XXXXXX";
2250  long len;
2251  void *buf;
2252  int tempcopy = 0;
2253  STRING str;
2254  int ret; /* for better error checking */
2255  char *imap_flags = NIL;
2256  int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
2257  int box = NEW_FOLDER;
2258 
2259  /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
2260  if (msgnum < 0) {
2261  if(!imapgreetings) {
2262  return 0;
2263  } else {
2264  box = GREETINGS_FOLDER;
2265  }
2266  }
2267 
2268  if (imap_check_limits(chan, vms, vmu, msgcount)) {
2269  return -1;
2270  }
2271 
2272  /* Set urgent flag for IMAP message */
2273  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
2274  ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
2275  imap_flags = "\\FLAGGED";
2276  }
2277 
2278  /* Attach only the first format */
2279  fmt = ast_strdupa(fmt);
2280  stringp = fmt;
2281  strsep(&stringp, "|");
2282 
2283  if (!ast_strlen_zero(vmu->serveremail))
2284  myserveremail = vmu->serveremail;
2285 
2286  if (msgnum > -1)
2287  make_file(fn, sizeof(fn), dir, msgnum);
2288  else
2289  ast_copy_string (fn, dir, sizeof(fn));
2290 
2291  snprintf(introfn, sizeof(introfn), "%sintro", fn);
2292  if (ast_fileexists(introfn, NULL, NULL) <= 0) {
2293  *introfn = '\0';
2294  }
2295 
2296  if (ast_strlen_zero(vmu->email)) {
2297  /* We need the vmu->email to be set when we call make_email_file, but
2298  * if we keep it set, a duplicate e-mail will be created. So at the end
2299  * of this function, we will revert back to an empty string if tempcopy
2300  * is 1.
2301  */
2302  ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2303  tempcopy = 1;
2304  }
2305 
2306  if (!strcmp(fmt, "wav49"))
2307  fmt = "WAV";
2308  ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2309 
2310  /* Make a temporary file instead of piping directly to sendmail, in case the mail
2311  command hangs. */
2312  if (!(p = vm_mkftemp(tmp))) {
2313  ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2314  if (tempcopy)
2315  *(vmu->email) = '\0';
2316  return -1;
2317  }
2318 
2319  if (msgnum < 0 && imapgreetings) {
2320  if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
2321  ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
2322  return -1;
2323  }
2324  imap_delete_old_greeting(fn, vms);
2325  }
2326 
2327  make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
2328  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
2329  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
2330  fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
2331  /* read mail file to memory */
2332  len = ftell(p);
2333  rewind(p);
2334  if (!(buf = ast_malloc(len + 1))) {
2335  ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
2336  fclose(p);
2337  if (tempcopy)
2338  *(vmu->email) = '\0';
2339  return -1;
2340  }
2341  if (fread(buf, len, 1, p) < len) {
2342  if (ferror(p)) {
2343  ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
2344  return -1;
2345  }
2346  }
2347  ((char *) buf)[len] = '\0';
2348  INIT(&str, mail_string, buf, len);
2349  ret = init_mailstream(vms, box);
2350  if (ret == 0) {
2351  imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
2352  ast_mutex_lock(&vms->lock);
2353  if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
2354  ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2355  ast_mutex_unlock(&vms->lock);
2356  fclose(p);
2357  unlink(tmp);
2358  ast_free(buf);
2359  } else {
2360  ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
2361  fclose(p);
2362  unlink(tmp);
2363  ast_free(buf);
2364  return -1;
2365  }
2366  ast_debug(3, "%s stored\n", fn);
2367 
2368  if (tempcopy)
2369  *(vmu->email) = '\0';
2370  inprocess_count(vmu->mailbox, vmu->context, -1);
2371  return 0;
2372 
2373 }
2374 
2375 /*!
2376  * \brief Gets the number of messages that exist in the inbox folder.
2377  * \param mailbox_context
2378  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
2379  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
2380  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
2381  *
2382  * This method is used when IMAP backend is used.
2383  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
2384  *
2385  * \return zero on success, -1 on error.
2386  */
2387 
2388 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
2389 {
2390  char tmp[PATH_MAX] = "";
2391  char *mailboxnc;
2392  char *context;
2393  char *mb;
2394  char *cur;
2395  if (newmsgs)
2396  *newmsgs = 0;
2397  if (oldmsgs)
2398  *oldmsgs = 0;
2399  if (urgentmsgs)
2400  *urgentmsgs = 0;
2401 
2402  ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
2403  /* If no mailbox, return immediately */
2404  if (ast_strlen_zero(mailbox_context))
2405  return 0;
2406 
2407  ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2408  context = strchr(tmp, '@');
2409  if (strchr(mailbox_context, ',')) {
2410  int tmpnew, tmpold, tmpurgent;
2411  ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2412  mb = tmp;
2413  while ((cur = strsep(&mb, ", "))) {
2414  if (!ast_strlen_zero(cur)) {
2415  if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2416  return -1;
2417  else {
2418  if (newmsgs)
2419  *newmsgs += tmpnew;
2420  if (oldmsgs)
2421  *oldmsgs += tmpold;
2422  if (urgentmsgs)
2423  *urgentmsgs += tmpurgent;
2424  }
2425  }
2426  }
2427  return 0;
2428  }
2429  if (context) {
2430  *context = '\0';
2431  mailboxnc = tmp;
2432  context++;
2433  } else {
2434  context = "default";
2435  mailboxnc = (char *) mailbox_context;
2436  }
2437 
2438  if (newmsgs) {
2439  struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
2440  if (!vmu) {
2441  ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2442  return -1;
2443  }
2444  if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
2445  free_user(vmu);
2446  return -1;
2447  }
2448  free_user(vmu);
2449  }
2450  if (oldmsgs) {
2451  if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
2452  return -1;
2453  }
2454  }
2455  if (urgentmsgs) {
2456  if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
2457  return -1;
2458  }
2459  }
2460  return 0;
2461 }
2462 
2463 /**
2464  * \brief Determines if the given folder has messages.
2465  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
2466  * \param folder the folder to look in
2467  *
2468  * This function is used when the mailbox is stored in an IMAP back end.
2469  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
2470  * \return 1 if the folder has one or more messages. zero otherwise.
2471  */
2472 
2473 static int has_voicemail(const char *mailbox, const char *folder)
2474 {
2475  char tmp[256], *tmp2, *box, *context;
2476  ast_copy_string(tmp, mailbox, sizeof(tmp));
2477  tmp2 = tmp;
2478  if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
2479  while ((box = strsep(&tmp2, ",&"))) {
2480  if (!ast_strlen_zero(box)) {
2481  if (has_voicemail(box, folder)) {
2482  return 1;
2483  }
2484  }
2485  }
2486  }
2487  if ((context = strchr(tmp, '@'))) {
2488  *context++ = '\0';
2489  } else {
2490  context = "default";
2491  }
2492  return __messagecount(context, tmp, folder) ? 1 : 0;
2493 }
2494 
2495 /*!
2496  * \brief Copies a message from one mailbox to another.
2497  * \param chan
2498  * \param vmu
2499  * \param imbox
2500  * \param msgnum
2501  * \param duration
2502  * \param recip
2503  * \param fmt
2504  * \param dir
2505  *
2506  * This works with IMAP storage based mailboxes.
2507  *
2508  * \return zero on success, -1 on error.
2509  */
2510 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)
2511 {
2512  struct vm_state *sendvms = NULL, *destvms = NULL;
2513  char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2514  if (msgnum >= recip->maxmsg) {
2515  ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
2516  return -1;
2517  }
2518  if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
2519  ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2520  return -1;
2521  }
2522  if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
2523  ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2524  return -1;
2525  }
2526  snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2527  ast_mutex_lock(&sendvms->lock);
2528  if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
2529  ast_mutex_unlock(&sendvms->lock);
2530  return 0;
2531  }
2532  ast_mutex_unlock(&sendvms->lock);
2533  ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2534  return -1;
2535 }
2536 
2537 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
2538 {
2539  char tmp[256], *t = tmp;
2540  size_t left = sizeof(tmp);
2541 
2542  if (box == OLD_FOLDER) {
2543  ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
2544  } else {
2545  ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
2546  }
2547 
2548  if (box == NEW_FOLDER) {
2549  ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
2550  } else {
2551  snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
2552  }
2553 
2554  /* Build up server information */
2555  ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
2556 
2557  /* Add authentication user if present */
2558  if (!ast_strlen_zero(authuser))
2559  ast_build_string(&t, &left, "/authuser=%s", authuser);
2560 
2561  /* Add flags if present */
2562  if (!ast_strlen_zero(imapflags))
2563  ast_build_string(&t, &left, "/%s", imapflags);
2564 
2565  /* End with username */
2566 #if 1
2567  ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
2568 #else
2569  ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
2570 #endif
2571  if (box == NEW_FOLDER || box == OLD_FOLDER)
2572  snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
2573  else if (box == GREETINGS_FOLDER)
2574  snprintf(spec, len, "%s%s", tmp, greetingfolder);
2575  else { /* Other folders such as Friends, Family, etc... */
2576  if (!ast_strlen_zero(imapparentfolder)) {
2577  /* imapparentfolder would typically be set to INBOX */
2578  snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
2579  } else {
2580  snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
2581  }
2582  }
2583 }
2584 
2585 static int init_mailstream(struct vm_state *vms, int box)
2586 {
2587  MAILSTREAM *stream = NIL;
2588  long debug;
2589  char tmp[256];
2590 
2591  if (!vms) {
2592  ast_log(LOG_ERROR, "vm_state is NULL!\n");
2593  return -1;
2594  }
2595  if (option_debug > 2)
2596  ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
2597  if (vms->mailstream == NIL || !vms->mailstream) {
2598  if (option_debug)
2599  ast_log(LOG_DEBUG, "mailstream not set.\n");
2600  } else {
2601  stream = vms->mailstream;
2602  }
2603  /* debug = T; user wants protocol telemetry? */
2604  debug = NIL; /* NO protocol telemetry? */
2605 
2606  if (delimiter == '\0') { /* did not probe the server yet */
2607  char *cp;
2608 #ifdef USE_SYSTEM_IMAP
2609 #include <imap/linkage.c>
2610 #elif defined(USE_SYSTEM_CCLIENT)
2611 #include <c-client/linkage.c>
2612 #else
2613 #include "linkage.c"
2614 #endif
2615  /* Connect to INBOX first to get folders delimiter */
2616  imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
2617  ast_mutex_lock(&vms->lock);
2618  stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2619  ast_mutex_unlock(&vms->lock);
2620  if (stream == NIL) {
2621  ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
2622  return -1;
2623  }
2624  get_mailbox_delimiter(stream);
2625  /* update delimiter in imapfolder */
2626  for (cp = vms->imapfolder; *cp; cp++)
2627  if (*cp == '/')
2628  *cp = delimiter;
2629  }
2630  /* Now connect to the target folder */
2631  imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
2632  if (option_debug > 2)
2633  ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
2634  ast_mutex_lock(&vms->lock);
2635  vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2636  ast_mutex_unlock(&vms->lock);
2637  if (vms->mailstream == NIL) {
2638  return -1;
2639  } else {
2640  return 0;
2641  }
2642 }
2643 
2644 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
2645 {
2646  SEARCHPGM *pgm;
2647  SEARCHHEADER *hdr;
2648  int ret, urgent = 0;
2649 
2650  /* If Urgent, then look at INBOX */
2651  if (box == 11) {
2652  box = NEW_FOLDER;
2653  urgent = 1;
2654  }
2655 
2656  ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
2657  ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
2658  vms->imapversion = vmu->imapversion;
2659  ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
2660 
2661  if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
2662  ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
2663  return -1;
2664  }
2665 
2666  create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2667 
2668  /* Check Quota */
2669  if (box == 0) {
2670  ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
2671  check_quota(vms, (char *) mbox(vmu, box));
2672  }
2673 
2674  ast_mutex_lock(&vms->lock);
2675  pgm = mail_newsearchpgm();
2676 
2677  /* Check IMAP folder for Asterisk messages only... */
2678  hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
2679  hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
2680  pgm->header = hdr;
2681  pgm->deleted = 0;
2682  pgm->undeleted = 1;
2683 
2684  /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
2685  if (box == NEW_FOLDER && urgent == 1) {
2686  pgm->unseen = 1;
2687  pgm->seen = 0;
2688  pgm->flagged = 1;
2689  pgm->unflagged = 0;
2690  } else if (box == NEW_FOLDER && urgent == 0) {
2691  pgm->unseen = 1;
2692  pgm->seen = 0;
2693  pgm->flagged = 0;
2694  pgm->unflagged = 1;
2695  } else if (box == OLD_FOLDER) {
2696  pgm->seen = 1;
2697  pgm->unseen = 0;
2698  }
2699 
2700  ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
2701 
2702  vms->vmArrayIndex = 0;
2703  mail_search_full (vms->mailstream, NULL, pgm, NIL);
2704  vms->lastmsg = vms->vmArrayIndex - 1;
2705  mail_free_searchpgm(&pgm);
2706  /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
2707  * ensure to allocate enough space to account for all of them. Warn if old messages
2708  * have not been checked first as that is required.
2709  */
2710  if (box == 0 && !vms->dh_arraysize) {
2711  ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
2712  }
2713  if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
2714  ast_mutex_unlock(&vms->lock);
2715  return -1;
2716  }
2717 
2718  ast_mutex_unlock(&vms->lock);
2719  return 0;
2720 }
2721 
2722 static void write_file(char *filename, char *buffer, unsigned long len)
2723 {
2724  FILE *output;
2725 
2726  output = fopen (filename, "w");
2727  if (fwrite(buffer, len, 1, output) != 1) {
2728  if (ferror(output)) {
2729  ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
2730  }
2731  }
2732  fclose (output);
2733 }
2734 
2735 static void update_messages_by_imapuser(const char *user, unsigned long number)
2736 {
2737  struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
2738 
2739  if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
2740  return;
2741  }
2742 
2743  ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
2744 
2745  /* Ensure we have room for the next message. */
2746  if (vms->vmArrayIndex >= vms->msg_array_max) {
2747  long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
2748  if (!new_mem) {
2749  return;
2750  }
2751  vms->msgArray = new_mem;
2752  vms->msg_array_max *= 2;
2753  }
2754 
2755  vms->msgArray[vms->vmArrayIndex++] = number;
2756 }
2757 
2758 void mm_searched(MAILSTREAM *stream, unsigned long number)
2759 {
2760  char *mailbox = stream->mailbox, buf[1024] = "", *user;
2761 
2762  if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
2763  return;
2764 
2765  update_messages_by_imapuser(user, number);
2766 }
2767 
2768 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
2769 {
2770  struct ast_variable *var;
2771  struct ast_vm_user *vmu;
2772 
2773  vmu = ast_calloc(1, sizeof *vmu);
2774  if (!vmu)
2775  return NULL;
2776 
2777  populate_defaults(vmu);
2778  ast_set_flag(vmu, VM_ALLOCED);
2779 
2780  var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
2781  if (var) {
2782  apply_options_full(vmu, var);
2783  ast_variables_destroy(var);
2784  return vmu;
2785  } else {
2786  ast_free(vmu);
2787  return NULL;
2788  }
2789 }
2790 
2791 /* Interfaces to C-client */
2792 
2793 void mm_exists(MAILSTREAM * stream, unsigned long number)
2794 {
2795  /* mail_ping will callback here if new mail! */
2796  ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
2797  if (number == 0) return;
2798  set_update(stream);
2799 }
2800 
2801 
2802 void mm_expunged(MAILSTREAM * stream, unsigned long number)
2803 {
2804  /* mail_ping will callback here if expunged mail! */
2805  ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
2806  if (number == 0) return;
2807  set_update(stream);
2808 }
2809 
2810 
2811 void mm_flags(MAILSTREAM * stream, unsigned long number)
2812 {
2813  /* mail_ping will callback here if read mail! */
2814  ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
2815  if (number == 0) return;
2816  set_update(stream);
2817 }
2818 
2819 
2820 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
2821 {
2822  ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
2823  mm_log (string, errflg);
2824 }
2825 
2826 
2827 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2828 {
2829  if (delimiter == '\0') {
2830  delimiter = delim;
2831  }
2832 
2833  ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
2834  if (attributes & LATT_NOINFERIORS)
2835  ast_debug(5, "no inferiors\n");
2836  if (attributes & LATT_NOSELECT)
2837  ast_debug(5, "no select\n");
2838  if (attributes & LATT_MARKED)
2839  ast_debug(5, "marked\n");
2840  if (attributes & LATT_UNMARKED)
2841  ast_debug(5, "unmarked\n");
2842 }
2843 
2844 
2845 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2846 {
2847  ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
2848  if (attributes & LATT_NOINFERIORS)
2849  ast_debug(5, "no inferiors\n");
2850  if (attributes & LATT_NOSELECT)
2851  ast_debug(5, "no select\n");
2852  if (attributes & LATT_MARKED)
2853  ast_debug(5, "marked\n");
2854  if (attributes & LATT_UNMARKED)
2855  ast_debug(5, "unmarked\n");
2856 }
2857 
2858 
2859 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
2860 {
2861  ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
2862  if (status->flags & SA_MESSAGES)
2863  ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
2864  if (status->flags & SA_RECENT)
2865  ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
2866  if (status->flags & SA_UNSEEN)
2867  ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
2868  if (status->flags & SA_UIDVALIDITY)
2869  ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
2870  if (status->flags & SA_UIDNEXT)
2871  ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
2872  ast_log(AST_LOG_NOTICE, "\n");
2873 }
2874 
2875 
2876 void mm_log(char *string, long errflg)
2877 {
2878  switch ((short) errflg) {
2879  case NIL:
2880  ast_debug(1, "IMAP Info: %s\n", string);
2881  break;
2882  case PARSE:
2883  case WARN:
2884  ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
2885  break;
2886  case ERROR:
2887  ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
2888  break;
2889  }
2890 }
2891 
2892 
2893 void mm_dlog(char *string)
2894 {
2895  ast_log(AST_LOG_NOTICE, "%s\n", string);
2896 }
2897 
2898 
2899 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
2900 {
2901  struct ast_vm_user *vmu;
2902 
2903  ast_debug(4, "Entering callback mm_login\n");
2904 
2905  ast_copy_string(user, mb->user, MAILTMPLEN);
2906 
2907  /* We should only do this when necessary */
2908  if (!ast_strlen_zero(authpassword)) {
2909  ast_copy_string(pwd, authpassword, MAILTMPLEN);
2910  } else {
2911  AST_LIST_TRAVERSE(&users, vmu, list) {
2912  if (!strcasecmp(mb->user, vmu->imapuser)) {
2913  ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2914  break;
2915  }
2916  }
2917  if (!vmu) {
2918  if ((vmu = find_user_realtime_imapuser(mb->user))) {
2919  ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2920  free_user(vmu);
2921  }
2922  }
2923  }
2924 }
2925 
2926 
2927 void mm_critical(MAILSTREAM * stream)
2928 {
2929 }
2930 
2931 
2932 void mm_nocritical(MAILSTREAM * stream)
2933 {
2934 }
2935 
2936 
2937 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
2938 {
2939  kill (getpid (), SIGSTOP);
2940  return NIL;
2941 }
2942 
2943 
2944 void mm_fatal(char *string)
2945 {
2946  ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
2947 }
2948 
2949 /* C-client callback to handle quota */
2950 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
2951 {
2952  struct vm_state *vms;
2953  char *mailbox = stream->mailbox, *user;
2954  char buf[1024] = "";
2955  unsigned long usage = 0, limit = 0;
2956 
2957  while (pquota) {
2958  usage = pquota->usage;
2959  limit = pquota->limit;
2960  pquota = pquota->next;
2961  }
2962 
2963  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)))) {
2964  ast_log(AST_LOG_ERROR, "No state found.\n");
2965  return;
2966  }
2967 
2968  ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
2969 
2970  vms->quota_usage = usage;
2971  vms->quota_limit = limit;
2972 }
2973 
2974 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
2975 {
2976  char *start, *eol_pnt;
2977  int taglen;
2978 
2979  if (ast_strlen_zero(header) || ast_strlen_zero(tag))
2980  return NULL;
2981 
2982  taglen = strlen(tag) + 1;
2983  if (taglen < 1)
2984  return NULL;
2985 
2986  if (!(start = strstr(header, tag)))
2987  return NULL;
2988 
2989  /* Since we can be called multiple times we should clear our buffer */
2990  memset(buf, 0, len);
2991 
2992  ast_copy_string(buf, start+taglen, len);
2993  if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
2994  *eol_pnt = '\0';
2995  return buf;
2996 }
2997 
2998 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
2999 {
3000  char *start, *quote, *eol_pnt;
3001 
3002  if (ast_strlen_zero(mailbox))
3003  return NULL;
3004 
3005  if (!(start = strstr(mailbox, "/user=")))
3006  return NULL;
3007 
3008  ast_copy_string(buf, start+6, len);
3009 
3010  if (!(quote = strchr(buf, '\"'))) {
3011  if (!(eol_pnt = strchr(buf, '/')))
3012  eol_pnt = strchr(buf,'}');
3013  *eol_pnt = '\0';
3014  return buf;
3015  } else {
3016  eol_pnt = strchr(buf+1,'\"');
3017  *eol_pnt = '\0';
3018  return buf+1;
3019  }
3020 }
3021 
3022 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
3023 {
3024  struct vm_state *vms_p;
3025 
3026  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3027  if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
3028  return vms_p;
3029  }
3030  if (option_debug > 4)
3031  ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
3032  /* XXX: Is this correctly freed always? */
3033  if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
3034  return NULL;
3035  ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
3036  ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
3037  ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
3038  ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
3039  vms_p->mailstream = NIL; /* save for access from interactive entry point */
3040  vms_p->imapversion = vmu->imapversion;
3041  if (option_debug > 4)
3042  ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
3043  vms_p->updated = 1;
3044  /* set mailbox to INBOX! */
3045  ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
3046  init_vm_state(vms_p);
3047  vmstate_insert(vms_p);
3048  return vms_p;
3049 }
3050 
3051 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
3052 {
3053  struct vmstate *vlist = NULL;
3054 
3055  if (interactive) {
3056  struct vm_state *vms;
3057  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3058  vms = pthread_getspecific(ts_vmstate.key);
3059  return vms;
3060  }
3061 
3062  AST_LIST_LOCK(&vmstates);
3063  AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3064  if (!vlist->vms) {
3065  ast_debug(3, "error: vms is NULL for %s\n", user);
3066  continue;
3067  }
3068  if (vlist->vms->imapversion != imapversion) {
3069  continue;
3070  }
3071  if (!vlist->vms->imapuser) {
3072  ast_debug(3, "error: imapuser is NULL for %s\n", user);
3073  continue;
3074  }
3075 
3076  if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
3077  AST_LIST_UNLOCK(&vmstates);
3078  return vlist->vms;
3079  }
3080  }
3081  AST_LIST_UNLOCK(&vmstates);
3082 
3083  ast_debug(3, "%s not found in vmstates\n", user);
3084 
3085  return NULL;
3086 }
3087 
3088 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
3089 {
3090 
3091  struct vmstate *vlist = NULL;
3092  const char *local_context = S_OR(context, "default");
3093 
3094  if (interactive) {
3095  struct vm_state *vms;
3096  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3097  vms = pthread_getspecific(ts_vmstate.key);
3098  return vms;
3099  }
3100 
3101  AST_LIST_LOCK(&vmstates);
3102  AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3103  if (!vlist->vms) {
3104  ast_debug(3, "error: vms is NULL for %s\n", mailbox);
3105  continue;
3106  }
3107  if (vlist->vms->imapversion != imapversion) {
3108  continue;
3109  }
3110  if (!vlist->vms->username || !vlist->vms->context) {
3111  ast_debug(3, "error: username is NULL for %s\n", mailbox);
3112  continue;
3113  }
3114 
3115  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);
3116 
3117  if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
3118  ast_debug(3, "Found it!\n");
3119  AST_LIST_UNLOCK(&vmstates);
3120  return vlist->vms;
3121  }
3122  }
3123  AST_LIST_UNLOCK(&vmstates);
3124 
3125  ast_debug(3, "%s not found in vmstates\n", mailbox);
3126 
3127  return NULL;
3128 }
3129 
3130 static void vmstate_insert(struct vm_state *vms)
3131 {
3132  struct vmstate *v;
3133  struct vm_state *altvms;
3134 
3135  /* If interactive, it probably already exists, and we should
3136  use the one we already have since it is more up to date.
3137  We can compare the username to find the duplicate */
3138  if (vms->interactive == 1) {
3139  altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
3140  if (altvms) {
3141  ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3142  vms->newmessages = altvms->newmessages;
3143  vms->oldmessages = altvms->oldmessages;
3144  vms->vmArrayIndex = altvms->vmArrayIndex;
3145  /* XXX: no msgArray copying? */
3146  vms->lastmsg = altvms->lastmsg;
3147  vms->curmsg = altvms->curmsg;
3148  /* get a pointer to the persistent store */
3149  vms->persist_vms = altvms;
3150  /* Reuse the mailstream? */
3151 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
3152  vms->mailstream = altvms->mailstream;
3153 #else
3154  vms->mailstream = NIL;
3155 #endif
3156  }
3157  return;
3158  }
3159 
3160  if (!(v = ast_calloc(1, sizeof(*v))))
3161  return;
3162 
3163  v->vms = vms;
3164 
3165  ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3166 
3167  AST_LIST_LOCK(&vmstates);
3168  AST_LIST_INSERT_TAIL(&vmstates, v, list);
3169  AST_LIST_UNLOCK(&vmstates);
3170 }
3171 
3172 static void vmstate_delete(struct vm_state *vms)
3173 {
3174  struct vmstate *vc = NULL;
3175  struct vm_state *altvms = NULL;
3176 
3177  /* If interactive, we should copy pertinent info
3178  back to the persistent state (to make update immediate) */
3179  if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
3180  ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3181  altvms->newmessages = vms->newmessages;
3182  altvms->oldmessages = vms->oldmessages;
3183  altvms->updated = 1;
3184  vms->mailstream = mail_close(vms->mailstream);
3185 
3186  /* Interactive states are not stored within the persistent list */
3187  return;
3188  }
3189 
3190  ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3191 
3192  AST_LIST_LOCK(&vmstates);
3193  AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
3194  if (vc->vms == vms) {
3196  break;
3197  }
3198  }
3200  AST_LIST_UNLOCK(&vmstates);
3201 
3202  if (vc) {
3203  ast_mutex_destroy(&vc->vms->lock);
3204  ast_free(vc->vms->msgArray);
3205  vc->vms->msgArray = NULL;
3206  vc->vms->msg_array_max = 0;
3207  /* XXX: is no one supposed to free vms itself? */
3208  ast_free(vc);
3209  } else {
3210  ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3211  }
3212 }
3213 
3214 static void set_update(MAILSTREAM * stream)
3215 {
3216  struct vm_state *vms;
3217  char *mailbox = stream->mailbox, *user;
3218  char buf[1024] = "";
3219 
3220  if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
3221  if (user && option_debug > 2)
3222  ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
3223  return;
3224  }
3225 
3226  ast_debug(3, "User %s mailbox set for update.\n", user);
3227 
3228  vms->updated = 1; /* Set updated flag since mailbox changed */
3229 }
3230 
3231 static void init_vm_state(struct vm_state *vms)
3232 {
3233  vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
3234  vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
3235  if (!vms->msgArray) {
3236  /* Out of mem? This can't be good. */
3237  vms->msg_array_max = 0;
3238  }
3239  vms->vmArrayIndex = 0;
3240  ast_mutex_init(&vms->lock);
3241 }
3242 
3243 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro)
3244 {
3245  char *body_content;
3246  char *body_decoded;
3247  char *fn = is_intro ? vms->introfn : vms->fn;
3248  unsigned long len;
3249  unsigned long newlen;
3250  char filename[256];
3251 
3252  if (!body || body == NIL)
3253  return -1;
3254 
3255  ast_mutex_lock(&vms->lock);
3256  body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
3257  ast_mutex_unlock(&vms->lock);
3258  if (body_content != NIL) {
3259  snprintf(filename, sizeof(filename), "%s.%s", fn, format);
3260  /* ast_debug(1,body_content); */
3261  body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
3262  /* If the body of the file is empty, return an error */
3263  if (!newlen) {
3264  return -1;
3265  }
3266  write_file(filename, (char *) body_decoded, newlen);
3267  } else {
3268  ast_debug(5, "Body of message is NULL.\n");
3269  return -1;
3270  }
3271  return 0;
3272 }
3273 
3274 /*!
3275  * \brief Get delimiter via mm_list callback
3276  * \param stream
3277  *
3278  * Determines the delimiter character that is used by the underlying IMAP based mail store.
3279  */
3280 /* MUTEX should already be held */
3281 static void get_mailbox_delimiter(MAILSTREAM *stream) {
3282  char tmp[50];
3283  snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
3284  mail_list(stream, tmp, "*");
3285 }
3286 
3287 /*!
3288  * \brief Check Quota for user
3289  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
3290  * \param mailbox the mailbox to check the quota for.
3291  *
3292  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
3293  */
3294 static void check_quota(struct vm_state *vms, char *mailbox) {
3295  ast_mutex_lock(&vms->lock);
3296  mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
3297  ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
3298  if (vms && vms->mailstream != NULL) {
3299  imap_getquotaroot(vms->mailstream, mailbox);
3300  } else {
3301  ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
3302  }
3303  ast_mutex_unlock(&vms->lock);
3304 }
3305 
3306 #endif /* IMAP_STORAGE */
3307 
3308 /*! \brief Lock file path
3309  * only return failure if ast_lock_path returns 'timeout',
3310  * not if the path does not exist or any other reason
3311  */
3312 static int vm_lock_path(const char *path)
3313 {
3314  switch (ast_lock_path(path)) {
3315  case AST_LOCK_TIMEOUT:
3316  return -1;
3317  default:
3318  return 0;
3319  }
3320 }
3321 
3322 
3323 #ifdef ODBC_STORAGE
3324 struct generic_prepare_struct {
3325  char *sql;
3326  int argc;
3327  char **argv;
3328 };
3329 
3330 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
3331 {
3332  struct generic_prepare_struct *gps = data;
3333  int res, i;
3334  SQLHSTMT stmt;
3335 
3336  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
3337  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3338  ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
3339  return NULL;
3340  }
3341  res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
3342  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3343  ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
3344  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3345  return NULL;
3346  }
3347  for (i = 0; i < gps->argc; i++)
3348  SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
3349 
3350  return stmt;
3351 }
3352 
3353 /*!
3354  * \brief Retrieves a file from an ODBC data store.
3355  * \param dir the path to the file to be retreived.
3356  * \param msgnum the message number, such as within a mailbox folder.
3357  *
3358  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
3359  * 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.
3360  *
3361  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
3362  * The output is the message information file with the name msgnum and the extension .txt
3363  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
3364  *
3365  * \return 0 on success, -1 on error.
3366  */
3367 static int retrieve_file(char *dir, int msgnum)
3368 {
3369  int x = 0;
3370  int res;
3371  int fd = -1;
3372  size_t fdlen = 0;
3373  void *fdm = MAP_FAILED;
3374  SQLSMALLINT colcount = 0;
3375  SQLHSTMT stmt;
3376  char sql[PATH_MAX];
3377  char fmt[80]="";
3378  char *c;
3379  char coltitle[256];
3380  SQLSMALLINT collen;
3381  SQLSMALLINT datatype;
3382  SQLSMALLINT decimaldigits;
3383  SQLSMALLINT nullable;
3384  SQLULEN colsize;
3385  SQLLEN colsize2;
3386  FILE *f = NULL;
3387  char rowdata[80];
3388  char fn[PATH_MAX];
3389  char full_fn[PATH_MAX];
3390  char msgnums[80];
3391  char *argv[] = { dir, msgnums };
3392  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3393 
3394  struct odbc_obj *obj;
3395  obj = ast_odbc_request_obj(odbc_database, 0);
3396  if (obj) {
3397  ast_copy_string(fmt, vmfmts, sizeof(fmt));
3398  c = strchr(fmt, '|');
3399  if (c)
3400  *c = '\0';
3401  if (!strcasecmp(fmt, "wav49"))
3402  strcpy(fmt, "WAV");
3403  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3404  if (msgnum > -1)
3405  make_file(fn, sizeof(fn), dir, msgnum);
3406  else
3407  ast_copy_string(fn, dir, sizeof(fn));
3408 
3409  /* Create the information file */
3410  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3411 
3412  if (!(f = fopen(full_fn, "w+"))) {
3413  ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
3414  goto yuck;
3415  }
3416 
3417  snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
3418  snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
3419  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3420  if (!stmt) {
3421  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3422  ast_odbc_release_obj(obj);
3423  goto yuck;
3424  }
3425  res = SQLFetch(stmt);
3426  if (res == SQL_NO_DATA) {
3427  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3428  ast_odbc_release_obj(obj);
3429  goto yuck;
3430  } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3431  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3432  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3433  ast_odbc_release_obj(obj);
3434  goto yuck;
3435  }
3436  fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
3437  if (fd < 0) {
3438  ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
3439  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3440  ast_odbc_release_obj(obj);
3441  goto yuck;
3442  }
3443  res = SQLNumResultCols(stmt, &colcount);
3444  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3445  ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
3446  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3447  ast_odbc_release_obj(obj);
3448  goto yuck;
3449  }
3450  if (f)
3451  fprintf(f, "[message]\n");
3452  for (x = 0; x < colcount; x++) {
3453  rowdata[0] = '\0';
3454  colsize = 0;
3455  collen = sizeof(coltitle);
3456  res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen,
3457  &datatype, &colsize, &decimaldigits, &nullable);
3458  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3459  ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
3460  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3461  ast_odbc_release_obj(obj);
3462  goto yuck;
3463  }
3464  if (!strcasecmp(coltitle, "recording")) {
3465  off_t offset;
3466  res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
3467  fdlen = colsize2;
3468  if (fd > -1) {
3469  char tmp[1]="";
3470  lseek(fd, fdlen - 1, SEEK_SET);
3471  if (write(fd, tmp, 1) != 1) {
3472  close(fd);
3473  fd = -1;
3474  continue;
3475  }
3476  /* Read out in small chunks */
3477  for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
3478  if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
3479  ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
3480  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3481  ast_odbc_release_obj(obj);
3482  goto yuck;
3483  } else {
3484  res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
3485  munmap(fdm, CHUNKSIZE);
3486  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3487  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3488  unlink(full_fn);
3489  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3490  ast_odbc_release_obj(obj);
3491  goto yuck;
3492  }
3493  }
3494  }
3495  if (truncate(full_fn, fdlen) < 0) {
3496  ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
3497  }
3498  }
3499  } else {
3500  res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3501  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3502  ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
3503  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3504  ast_odbc_release_obj(obj);
3505  goto yuck;
3506  }
3507  if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
3508  fprintf(f, "%s=%s\n", coltitle, rowdata);
3509  }
3510  }
3511  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3512  ast_odbc_release_obj(obj);
3513  } else
3514  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3515 yuck:
3516  if (f)
3517  fclose(f);
3518  if (fd > -1)
3519  close(fd);
3520  return x - 1;
3521 }
3522 
3523 /*!
3524  * \brief Determines the highest message number in use for a given user and mailbox folder.
3525  * \param vmu
3526  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
3527  *
3528  * This method is used when mailboxes are stored in an ODBC back end.
3529  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
3530  *
3531  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
3532 
3533  */
3534 static int last_message_index(struct ast_vm_user *vmu, char *dir)
3535 {
3536  int x = 0;
3537  int res;
3538  SQLHSTMT stmt;
3539  char sql[PATH_MAX];
3540  char rowdata[20];
3541  char *argv[] = { dir };
3542  struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
3543 
3544  struct odbc_obj *obj;
3545  obj = ast_odbc_request_obj(odbc_database, 0);
3546  if (obj) {
3547  snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
3548 
3549  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3550  if (!stmt) {
3551  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3552  ast_odbc_release_obj(obj);
3553  goto yuck;
3554  }
3555  res = SQLFetch(stmt);
3556  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3557  if (res == SQL_NO_DATA) {
3558  ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
3559  } else {
3560  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3561  }
3562 
3563  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3564  ast_odbc_release_obj(obj);
3565  goto yuck;
3566  }
3567  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3568  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3569  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3570  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3571  ast_odbc_release_obj(obj);
3572  goto yuck;
3573  }
3574  if (sscanf(rowdata, "%30d", &x) != 1)
3575  ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
3576  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3577  ast_odbc_release_obj(obj);
3578  return x;
3579  } else
3580  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3581 yuck:
3582  return x - 1;
3583 }
3584 
3585 /*!
3586  * \brief Determines if the specified message exists.
3587  * \param dir the folder the mailbox folder to look for messages.
3588  * \param msgnum the message index to query for.
3589  *
3590  * This method is used when mailboxes are stored in an ODBC back end.
3591  *
3592  * \return greater than zero if the message exists, zero when the message does not exist or on error.
3593  */
3594 static int message_exists(char *dir, int msgnum)
3595 {
3596  int x = 0;
3597  int res;
3598  SQLHSTMT stmt;
3599  char sql[PATH_MAX];
3600  char rowdata[20];
3601  char msgnums[20];
3602  char *argv[] = { dir, msgnums };
3603  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3604 
3605  struct odbc_obj *obj;
3606  obj = ast_odbc_request_obj(odbc_database, 0);
3607  if (obj) {
3608  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3609  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
3610  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3611  if (!stmt) {
3612  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3613  ast_odbc_release_obj(obj);
3614  goto yuck;
3615  }
3616  res = SQLFetch(stmt);
3617  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3618  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3619  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3620  ast_odbc_release_obj(obj);
3621  goto yuck;
3622  }
3623  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3624  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3625  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3626  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3627  ast_odbc_release_obj(obj);
3628  goto yuck;
3629  }
3630  if (sscanf(rowdata, "%30d", &x) != 1)
3631  ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
3632  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3633  ast_odbc_release_obj(obj);
3634  } else
3635  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3636 yuck:
3637  return x;
3638 }
3639 
3640 /*!
3641  * \brief returns the number of messages found.
3642  * \param vmu
3643  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
3644  *
3645  * This method is used when mailboxes are stored in an ODBC back end.
3646  *
3647  * \return The count of messages being zero or more, less than zero on error.
3648  */
3649 static int count_messages(struct ast_vm_user *vmu, char *dir)
3650 {
3651  int x = 0;
3652  int res;
3653  SQLHSTMT stmt;
3654  char sql[PATH_MAX];
3655  char rowdata[20];
3656  char *argv[] = { dir };
3657  struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
3658 
3659  struct odbc_obj *obj;
3660  obj = ast_odbc_request_obj(odbc_database, 0);
3661  if (obj) {
3662  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
3663  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3664  if (!stmt) {
3665  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3666  ast_odbc_release_obj(obj);
3667  goto yuck;
3668  }
3669  res = SQLFetch(stmt);
3670  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3671  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3672  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3673  ast_odbc_release_obj(obj);
3674  goto yuck;
3675  }
3676  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3677  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3678  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3679  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3680  ast_odbc_release_obj(obj);
3681  goto yuck;
3682  }
3683  if (sscanf(rowdata, "%30d", &x) != 1)
3684  ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
3685  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3686  ast_odbc_release_obj(obj);
3687  return x;
3688  } else
3689  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3690 yuck:
3691  return x - 1;
3692 
3693 }
3694 
3695 /*!
3696  * \brief Deletes a message from the mailbox folder.
3697  * \param sdir The mailbox folder to work in.
3698  * \param smsg The message index to be deleted.
3699  *
3700  * This method is used when mailboxes are stored in an ODBC back end.
3701  * The specified message is directly deleted from the database 'voicemessages' table.
3702  *
3703  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
3704  */
3705 static void delete_file(const char *sdir, int smsg)
3706 {
3707  SQLHSTMT stmt;
3708  char sql[PATH_MAX];
3709  char msgnums[20];
3710  char *argv[] = { NULL, msgnums };
3711  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3712  struct odbc_obj *obj;
3713 
3714  argv[0] = ast_strdupa(sdir);
3715 
3716  obj = ast_odbc_request_obj(odbc_database, 0);
3717  if (obj) {
3718  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3719  snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
3720  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3721  if (!stmt)
3722  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3723  else
3724  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3725  ast_odbc_release_obj(obj);
3726  } else
3727  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3728  return;
3729 }
3730 
3731 /*!
3732  * \brief Copies a voicemail from one mailbox to another.
3733  * \param sdir the folder for which to look for the message to be copied.
3734  * \param smsg the index of the message to be copied.
3735  * \param ddir the destination folder to copy the message into.
3736  * \param dmsg the index to be used for the copied message.
3737  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
3738  * \param dmailboxcontext The context for the destination user.
3739  *
3740  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
3741  */
3742 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
3743 {
3744  SQLHSTMT stmt;
3745  char sql[512];
3746  char msgnums[20];
3747  char msgnumd[20];
3748  struct odbc_obj *obj;
3749  char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
3750  struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
3751 
3752  delete_file(ddir, dmsg);
3753  obj = ast_odbc_request_obj(odbc_database, 0);
3754  if (obj) {
3755  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3756  snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
3757  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);
3758  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3759  if (!stmt)
3760  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
3761  else
3762  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3763  ast_odbc_release_obj(obj);
3764  } else
3765  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3766  return;
3767 }
3768 
3769 struct insert_data {
3770  char *sql;
3771  const char *dir;
3772  const char *msgnums;
3773  void *data;
3774  SQLLEN datalen;
3775  SQLLEN indlen;
3776  const char *context;
3777  const char *macrocontext;
3778  const char *callerid;
3779  const char *origtime;
3780  const char *duration;
3781  const char *mailboxuser;
3782  const char *mailboxcontext;
3783  const char *category;
3784  const char *flag;
3785 };
3786 
3787 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
3788 {
3789  struct insert_data *data = vdata;
3790  int res;
3791  SQLHSTMT stmt;
3792 
3793  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
3794  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3795  ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
3796  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3797  return NULL;
3798  }
3799 
3800  SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
3801  SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
3802  SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
3803  SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
3804  SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
3805  SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
3806  SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
3807  SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
3808  SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
3809  SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
3810  SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
3811  if (!ast_strlen_zero(data->category)) {
3812  SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
3813  }
3814  res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
3815  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3816  ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
3817  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3818  return NULL;
3819  }
3820 
3821  return stmt;
3822 }
3823 
3824 /*!
3825  * \brief Stores a voicemail into the database.
3826  * \param dir the folder the mailbox folder to store the message.
3827  * \param mailboxuser the user owning the mailbox folder.
3828  * \param mailboxcontext
3829  * \param msgnum the message index for the message to be stored.
3830  *
3831  * This method is used when mailboxes are stored in an ODBC back end.
3832  * The message sound file and information file is looked up on the file system.
3833  * A SQL query is invoked to store the message into the (MySQL) database.
3834  *
3835  * \return the zero on success -1 on error.
3836  */
3837 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
3838 {
3839  int res = 0;
3840  int fd = -1;
3841  void *fdm = MAP_FAILED;
3842  off_t fdlen = -1;
3843  SQLHSTMT stmt;
3844  char sql[PATH_MAX];
3845  char msgnums[20];
3846  char fn[PATH_MAX];
3847  char full_fn[PATH_MAX];
3848  char fmt[80]="";
3849  char *c;
3850  struct ast_config *cfg = NULL;
3851  struct odbc_obj *obj;
3852  struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
3853  .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
3854  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
3855 
3856  delete_file(dir, msgnum);
3857  if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
3858  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3859  return -1;
3860  }
3861 
3862  do {
3863  ast_copy_string(fmt, vmfmts, sizeof(fmt));
3864  c = strchr(fmt, '|');
3865  if (c)
3866  *c = '\0';
3867  if (!strcasecmp(fmt, "wav49"))
3868  strcpy(fmt, "WAV");
3869  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3870  if (msgnum > -1)
3871  make_file(fn, sizeof(fn), dir, msgnum);
3872  else
3873  ast_copy_string(fn, dir, sizeof(fn));
3874  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3875  cfg = ast_config_load(full_fn, config_flags);
3876  snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
3877  fd = open(full_fn, O_RDWR);
3878  if (fd < 0) {
3879  ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
3880  res = -1;
3881  break;
3882  }
3883  if (valid_config(cfg)) {
3884  if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
3885  idata.context = "";
3886  }
3887  if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
3888  idata.macrocontext = "";
3889  }
3890  if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
3891  idata.callerid = "";
3892  }
3893  if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
3894  idata.origtime = "";
3895  }
3896  if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
3897  idata.duration = "";
3898  }
3899  if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
3900  idata.category = "";
3901  }
3902  if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
3903  idata.flag = "";
3904  }
3905  }
3906  fdlen = lseek(fd, 0, SEEK_END);
3907  if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
3908  ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
3909  res = -1;
3910  break;
3911  }
3912  fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
3913  if (fdm == MAP_FAILED) {
3914  ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
3915  res = -1;
3916  break;
3917  }
3918  idata.data = fdm;
3919  idata.datalen = idata.indlen = fdlen;
3920 
3921  if (!ast_strlen_zero(idata.category))
3922  snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
3923  else
3924  snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
3925 
3926  if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
3927  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3928  } else {
3929  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3930  res = -1;
3931  }
3932  } while (0);
3933  if (obj) {
3934  ast_odbc_release_obj(obj);
3935  }
3936  if (valid_config(cfg))
3937  ast_config_destroy(cfg);
3938  if (fdm != MAP_FAILED)
3939  munmap(fdm, fdlen);
3940  if (fd > -1)
3941  close(fd);
3942  return res;
3943 }
3944 
3945 /*!
3946  * \brief Renames a message in a mailbox folder.
3947  * \param sdir The folder of the message to be renamed.
3948  * \param smsg The index of the message to be renamed.
3949  * \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.
3950  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
3951  * \param ddir The destination folder for the message to be renamed into
3952  * \param dmsg The destination message for the message to be renamed.
3953  *
3954  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
3955  * 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.
3956  * 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.
3957  */
3958 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
3959 {
3960  SQLHSTMT stmt;
3961  char sql[PATH_MAX];
3962  char msgnums[20];
3963  char msgnumd[20];
3964  struct odbc_obj *obj;
3965  char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
3966  struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
3967 
3968  delete_file(ddir, dmsg);
3969  obj = ast_odbc_request_obj(odbc_database, 0);
3970  if (obj) {
3971  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3972  snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
3973  snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
3974  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3975  if (!stmt)
3976  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3977  else
3978  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3979  ast_odbc_release_obj(obj);
3980  } else
3981  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3982  return;
3983 }
3984 
3985 /*!
3986  * \brief Removes a voicemail message file.
3987  * \param dir the path to the message file.
3988  * \param msgnum the unique number for the message within the mailbox.
3989  *
3990  * Removes the message content file and the information file.
3991  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
3992  * Typical use is to clean up after a RETRIEVE operation.
3993  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
3994  * \return zero on success, -1 on error.
3995  */
3996 static int remove_file(char *dir, int msgnum)
3997 {
3998  char fn[PATH_MAX];
3999  char full_fn[PATH_MAX];
4000  char msgnums[80];
4001 
4002  if (msgnum > -1) {
4003  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4004  make_file(fn, sizeof(fn), dir, msgnum);
4005  } else
4006  ast_copy_string(fn, dir, sizeof(fn));
4007  ast_filedelete(fn, NULL);
4008  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
4009  unlink(full_fn);
4010  return 0;
4011 }
4012 #else
4013 #ifndef IMAP_STORAGE
4014 /*!
4015  * \brief Find all .txt files - even if they are not in sequence from 0000.
4016  * \param vmu
4017  * \param dir
4018  *
4019  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
4020  *
4021  * \return the count of messages, zero or more.
4022  */
4023 static int count_messages(struct ast_vm_user *vmu, char *dir)
4024 {
4025 
4026  int vmcount = 0;
4027  DIR *vmdir = NULL;
4028  struct dirent *vment = NULL;
4029 
4030  if (vm_lock_path(dir))
4031  return ERROR_LOCK_PATH;
4032 
4033  if ((vmdir = opendir(dir))) {
4034  while ((vment = readdir(vmdir))) {
4035  if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
4036  vmcount++;
4037  }
4038  }
4039  closedir(vmdir);
4040  }
4041  ast_unlock_path(dir);
4042 
4043  return vmcount;
4044 }
4045 
4046 /*!
4047  * \brief Renames a message in a mailbox folder.
4048  * \param sfn The path to the mailbox information and data file to be renamed.
4049  * \param dfn The path for where the message data and information files will be renamed to.
4050  *
4051  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
4052  */
4053 static void rename_file(char *sfn, char *dfn)
4054 {
4055  char stxt[PATH_MAX];
4056  char dtxt[PATH_MAX];
4057  ast_filerename(sfn, dfn, NULL);
4058  snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
4059  snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
4060  if (ast_check_realtime("voicemail_data")) {
4061  ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
4062  }
4063  rename(stxt, dtxt);
4064 }
4065 
4066 /*!
4067  * \brief Determines the highest message number in use for a given user and mailbox folder.
4068  * \param vmu
4069  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
4070  *
4071  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
4072  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
4073  *
4074  * \note Should always be called with a lock already set on dir.
4075  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
4076  */
4077 static int last_message_index(struct ast_vm_user *vmu, char *dir)
4078 {
4079  int x;
4080  unsigned char map[MAXMSGLIMIT] = "";
4081  DIR *msgdir;
4082  struct dirent *msgdirent;
4083  int msgdirint;
4084  char extension[4];
4085  int stopcount = 0;
4086 
4087  /* Reading the entire directory into a file map scales better than
4088  * doing a stat repeatedly on a predicted sequence. I suspect this
4089  * is partially due to stat(2) internally doing a readdir(2) itself to
4090  * find each file. */
4091  if (!(msgdir = opendir(dir))) {
4092  return -1;
4093  }
4094 
4095  while ((msgdirent = readdir(msgdir))) {
4096  if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
4097  map[msgdirint] = 1;
4098  stopcount++;
4099  ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
4100  }
4101  }
4102  closedir(msgdir);
4103 
4104  for (x = 0; x < vmu->maxmsg; x++) {
4105  if (map[x] == 1) {
4106  stopcount--;
4107  } else if (map[x] == 0 && !stopcount) {
4108  break;
4109  }
4110  }
4111 
4112  return x - 1;
4113 }
4114 
4115 #endif /* #ifndef IMAP_STORAGE */
4116 #endif /* #else of #ifdef ODBC_STORAGE */
4117 #ifndef IMAP_STORAGE
4118 /*!
4119  * \brief Utility function to copy a file.
4120  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
4121  * \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.
4122  *
4123  * 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.
4124  * The copy operation copies up to 4096 bytes at once.
4125  *
4126  * \return zero on success, -1 on error.
4127  */
4128 static int copy(char *infile, char *outfile)
4129 {
4130  int ifd;
4131  int ofd;
4132  int res;
4133  int len;
4134  char buf[4096];
4135 
4136 #ifdef HARDLINK_WHEN_POSSIBLE
4137  /* Hard link if possible; saves disk space & is faster */
4138  if (link(infile, outfile)) {
4139 #endif
4140  if ((ifd = open(infile, O_RDONLY)) < 0) {
4141  ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
4142  return -1;
4143  }
4144  if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
4145  ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
4146  close(ifd);
4147  return -1;
4148  }
4149  do {
4150  len = read(ifd, buf, sizeof(buf));
4151  if (len < 0) {
4152  ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
4153  close(ifd);
4154  close(ofd);
4155  unlink(outfile);
4156  } else if (len) {
4157  res = write(ofd, buf, len);
4158  if (errno == ENOMEM || errno == ENOSPC || res != len) {
4159  ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
4160  close(ifd);
4161  close(ofd);
4162  unlink(outfile);
4163  }
4164  }
4165  } while (len);
4166  close(ifd);
4167  close(ofd);
4168  return 0;
4169 #ifdef HARDLINK_WHEN_POSSIBLE
4170  } else {
4171  /* Hard link succeeded */
4172  return 0;
4173  }
4174 #endif
4175 }
4176 
4177 /*!
4178  * \brief Copies a voicemail information (envelope) file.
4179  * \param frompath
4180  * \param topath
4181  *
4182  * Every voicemail has the data (.wav) file, and the information file.
4183  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
4184  * This is used by the COPY macro when not using IMAP storage.
4185  */
4186 static void copy_plain_file(char *frompath, char *topath)
4187 {
4188  char frompath2[PATH_MAX], topath2[PATH_MAX];
4189  struct ast_variable *tmp,*var = NULL;
4190  const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
4191  ast_filecopy(frompath, topath, NULL);
4192  snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
4193  snprintf(topath2, sizeof(topath2), "%s.txt", topath);
4194  if (ast_check_realtime("voicemail_data")) {
4195  var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
4196  /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
4197  for (tmp = var; tmp; tmp = tmp->next) {
4198  if (!strcasecmp(tmp->name, "origmailbox")) {
4199  origmailbox = tmp->value;
4200  } else if (!strcasecmp(tmp->name, "context")) {
4201  context = tmp->value;
4202  } else if (!strcasecmp(tmp->name, "macrocontext")) {
4203  macrocontext = tmp->value;
4204  } else if (!strcasecmp(tmp->name, "exten")) {
4205  exten = tmp->value;
4206  } else if (!strcasecmp(tmp->name, "priority")) {
4207  priority = tmp->value;
4208  } else if (!strcasecmp(tmp->name, "callerchan")) {
4209  callerchan = tmp->value;
4210  } else if (!strcasecmp(tmp->name, "callerid")) {
4211  callerid = tmp->value;
4212  } else if (!strcasecmp(tmp->name, "origdate")) {
4213  origdate = tmp->value;
4214  } else if (!strcasecmp(tmp->name, "origtime")) {
4215  origtime = tmp->value;
4216  } else if (!strcasecmp(tmp->name, "category")) {
4217  category = tmp->value;
4218  } else if (!strcasecmp(tmp->name, "duration")) {
4219  duration = tmp->value;
4220  }
4221  }
4222  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);
4223  }
4224  copy(frompath2, topath2);
4225  ast_variables_destroy(var);
4226 }
4227 #endif
4228 
4229 /*!
4230  * \brief Removes the voicemail sound and information file.
4231  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
4232  *
4233  * This is used by the DELETE macro when voicemails are stored on the file system.
4234  *
4235  * \return zero on success, -1 on error.
4236  */
4237 static int vm_delete(char *file)
4238 {
4239  char *txt;
4240  int txtsize = 0;
4241 
4242  txtsize = (strlen(file) + 5)*sizeof(char);
4243  txt = ast_alloca(txtsize);
4244  /* Sprintf here would safe because we alloca'd exactly the right length,
4245  * but trying to eliminate all sprintf's anyhow
4246  */
4247  if (ast_check_realtime("voicemail_data")) {
4248  ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
4249  }
4250  snprintf(txt, txtsize, "%s.txt", file);
4251  unlink(txt);
4252  return ast_filedelete(file, NULL);
4253 }
4254 
4255 /*!
4256  * \brief utility used by inchar(), for base_encode()
4257  */
4258 static int inbuf(struct baseio *bio, FILE *fi)
4259 {
4260  int l;
4261 
4262  if (bio->ateof)
4263  return 0;
4264 
4265  if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
4266  if (ferror(fi))
4267  return -1;
4268 
4269  bio->ateof = 1;
4270  return 0;
4271  }
4272 
4273  bio->iolen = l;
4274  bio->iocp = 0;
4275 
4276  return 1;
4277 }
4278 
4279 /*!
4280  * \brief utility used by base_encode()
4281  */
4282 static int inchar(struct baseio *bio, FILE *fi)
4283 {
4284  if (bio->iocp>=bio->iolen) {
4285  if (!inbuf(bio, fi))
4286  return EOF;
4287  }
4288 
4289  return bio->iobuf[bio->iocp++];
4290 }
4291 
4292 /*!
4293  * \brief utility used by base_encode()
4294  */
4295 static int ochar(struct baseio *bio, int c, FILE *so)
4296 {
4297  if (bio->linelength >= BASELINELEN) {
4298  if (fputs(ENDL, so) == EOF) {
4299  return -1;
4300  }
4301 
4302  bio->linelength = 0;
4303  }
4304 
4305  if (putc(((unsigned char) c), so) == EOF) {
4306  return -1;
4307  }
4308 
4309  bio->linelength++;
4310 
4311  return 1;
4312 }
4313 
4314 /*!
4315  * \brief Performs a base 64 encode algorithm on the contents of a File
4316  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
4317  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
4318  *
4319  * 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 ?
4320  *
4321  * \return zero on success, -1 on error.
4322  */
4323 static int base_encode(char *filename, FILE *so)
4324 {
4325  static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
4326  'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
4327  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
4328  '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
4329  int i, hiteof = 0;
4330  FILE *fi;
4331  struct baseio bio;
4332 
4333  memset(&bio, 0, sizeof(bio));
4334  bio.iocp = BASEMAXINLINE;
4335 
4336  if (!(fi = fopen(filename, "rb"))) {
4337  ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
4338  return -1;
4339  }
4340 
4341  while (!hiteof){
4342  unsigned char igroup[3], ogroup[4];
4343  int c, n;
4344 
4345  memset(igroup, 0, sizeof(igroup));
4346 
4347  for (n = 0; n < 3; n++) {
4348  if ((c = inchar(&bio, fi)) == EOF) {
4349  hiteof = 1;
4350  break;
4351  }
4352 
4353  igroup[n] = (unsigned char) c;
4354  }
4355 
4356  if (n > 0) {
4357  ogroup[0]= dtable[igroup[0] >> 2];
4358  ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
4359  ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
4360  ogroup[3]= dtable[igroup[2] & 0x3F];
4361 
4362  if (n < 3) {
4363  ogroup[3] = '=';
4364 
4365  if (n < 2)
4366  ogroup[2] = '=';
4367  }
4368 
4369  for (i = 0; i < 4; i++)
4370  ochar(&bio, ogroup[i], so);
4371  }
4372  }
4373 
4374  fclose(fi);
4375 
4376  if (fputs(ENDL, so) == EOF) {
4377  return 0;
4378  }
4379 
4380  return 1;
4381 }
4382 
4383 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)
4384 {
4385  char callerid[256];
4386  char num[12];
4387  char fromdir[256], fromfile[256];
4388  struct ast_config *msg_cfg;
4389  const char *origcallerid, *origtime;
4390  char origcidname[80], origcidnum[80], origdate[80];
4391  int inttime;
4392  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
4393 
4394  /* Prepare variables for substitution in email body and subject */
4395  pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
4396  pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
4397  snprintf(num, sizeof(num), "%d", msgnum);
4398  pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
4399  pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
4400  pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
4401  pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
4402  ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
4403  pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
4404  pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
4405  pbx_builtin_setvar_helper(ast, "VM_DATE", date);
4406  pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
4407  pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
4408 
4409  /* Retrieve info from VM attribute file */
4410  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
4411  make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
4412  if (strlen(fromfile) < sizeof(fromfile) - 5) {
4413  strcat(fromfile, ".txt");
4414  }
4415  if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
4416  if (option_debug > 0) {
4417  ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
4418  }
4419  return;
4420  }
4421 
4422  if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
4423  pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
4424  ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
4425  pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
4426  pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
4427  }
4428 
4429  if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
4430  struct timeval tv = { inttime, };
4431  struct ast_tm tm;
4432  ast_localtime(&tv, &tm, NULL);
4433  ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
4434  pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
4435  }
4436  ast_config_destroy(msg_cfg);
4437 }
4438 
4439 /*!
4440  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
4441  * \param from The string to work with.
4442  * \param buf The buffer into which to write the modified quoted string.
4443  * \param maxlen Always zero, but see \see ast_str
4444  *
4445  * \return The destination string with quotes wrapped on it (the to field).
4446  */
4447 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
4448 {
4449  const char *ptr;
4450 
4451  /* We're only ever passing 0 to maxlen, so short output isn't possible */
4452  ast_str_set(buf, maxlen, "\"");
4453  for (ptr = from; *ptr; ptr++) {
4454  if (*ptr == '"' || *ptr == '\\') {
4455  ast_str_append(buf, maxlen, "\\%c", *ptr);
4456  } else {
4457  ast_str_append(buf, maxlen, "%c", *ptr);
4458  }
4459  }
4460  ast_str_append(buf, maxlen, "\"");
4461 
4462  return ast_str_buffer(*buf);
4463 }
4464 
4465 /*! \brief
4466  * fill in *tm for current time according to the proper timezone, if any.
4467  * \return tm so it can be used as a function argument.
4468  */
4469 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
4470 {
4471  const struct vm_zone *z = NULL;
4472  struct timeval t = ast_tvnow();
4473 
4474  /* Does this user have a timezone specified? */
4475  if (!ast_strlen_zero(vmu->zonetag)) {
4476  /* Find the zone in the list */
4477  AST_LIST_LOCK(&zones);
4478  AST_LIST_TRAVERSE(&zones, z, list) {
4479  if (!strcmp(z->name, vmu->zonetag))
4480  break;
4481  }
4483  }
4484  ast_localtime(&t, tm, z ? z->timezone : NULL);
4485  return tm;
4486 }
4487 
4488 /*!\brief Check if the string would need encoding within the MIME standard, to
4489  * avoid confusing certain mail software that expects messages to be 7-bit
4490  * clean.
4491  */
4492 static int check_mime(const char *str)
4493 {
4494  for (; *str; str++) {
4495  if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
4496  return 1;
4497  }
4498  }
4499  return 0;
4500 }
4501 
4502 /*!\brief Encode a string according to the MIME rules for encoding strings
4503  * that are not 7-bit clean or contain control characters.
4504  *
4505  * Additionally, if the encoded string would exceed the MIME limit of 76
4506  * characters per line, then the encoding will be broken up into multiple
4507  * sections, separated by a space character, in order to facilitate
4508  * breaking up the associated header across multiple lines.
4509  *
4510  * \param end An expandable buffer for holding the result
4511  * \param maxlen Always zero, but see \see ast_str
4512  * \param start A string to be encoded
4513  * \param preamble The length of the first line already used for this string,
4514  * to ensure that each line maintains a maximum length of 76 chars.
4515  * \param postamble the length of any additional characters appended to the
4516  * line, used to ensure proper field wrapping.
4517  * \retval The encoded string.
4518  */
4519 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
4520 {
4521  struct ast_str *tmp = ast_str_alloca(80);
4522  int first_section = 1;
4523 
4524  ast_str_reset(*end);
4525  ast_str_set(&tmp, -1, "=?%s?Q?", charset);
4526  for (; *start; start++) {
4527  int need_encoding = 0;
4528  if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
4529  need_encoding = 1;
4530  }
4531  if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
4532  (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
4533  (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
4534  (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
4535  /* Start new line */
4536  ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
4537  ast_str_set(&tmp, -1, "=?%s?Q?", charset);
4538  first_section = 0;
4539  }
4540  if (need_encoding && *start == ' ') {
4541  ast_str_append(&tmp, -1, "_");
4542  } else if (need_encoding) {
4543  ast_str_append(&tmp, -1, "=%hhX", *start);
4544  } else {
4545  ast_str_append(&tmp, -1, "%c", *start);
4546  }
4547  }
4548  ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
4549  return ast_str_buffer(*end);
4550 }
4551 
4552 /*!
4553  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
4554  * \param p The output file to generate the email contents into.
4555  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
4556  * \param vmu The voicemail user who is sending the voicemail.
4557  * \param msgnum The message index in the mailbox folder.
4558  * \param context
4559  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
4560  * \param fromfolder
4561  * \param cidnum The caller ID number.
4562  * \param cidname The caller ID name.
4563  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
4564  * \param attach2
4565  * \param format The message sound file format. i.e. .wav
4566  * \param duration The time of the message content, in seconds.
4567  * \param attach_user_voicemail if 1, the sound file is attached to the email.
4568  * \param chan
4569  * \param category
4570  * \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.
4571  * \param flag
4572  *
4573  * 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.
4574  */
4575 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)
4576 {
4577  char date[256];
4578  char host[MAXHOSTNAMELEN] = "";
4579  char who[256];
4580  char bound[256];
4581  char dur[256];
4582  struct ast_tm tm;
4583  char enc_cidnum[256] = "", enc_cidname[256] = "";
4584  struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
4585  char *greeting_attachment;
4586  char filename[256];
4587 
4588  if (!str1 || !str2) {
4589  ast_free(str1);
4590  ast_free(str2);
4591  return;
4592  }
4593 
4594  if (cidnum) {
4595  strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
4596  }
4597  if (cidname) {
4598  strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
4599  }
4600  gethostname(host, sizeof(host) - 1);
4601 
4602  if (strchr(srcemail, '@')) {
4603  ast_copy_string(who, srcemail, sizeof(who));
4604  } else {
4605  snprintf(who, sizeof(who), "%s@%s", srcemail, host);
4606  }
4607 
4608  greeting_attachment = strrchr(ast_strdupa(attach), '/');
4609  if (greeting_attachment) {
4610  *greeting_attachment++ = '\0';
4611  }
4612 
4613  snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
4614  ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
4615  fprintf(p, "Date: %s" ENDL, date);
4616 
4617  /* Set date format for voicemail mail */
4618  ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
4619 
4620  if (!ast_strlen_zero(fromstring)) {
4621  struct ast_channel *ast;
4622  if ((ast = ast_dummy_channel_alloc())) {
4623  char *ptr;
4624  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
4625  ast_str_substitute_variables(&str1, 0, ast, fromstring);
4626 
4627  if (check_mime(ast_str_buffer(str1))) {
4628  int first_line = 1;
4629  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
4630  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
4631  *ptr = '\0';
4632  fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
4633  first_line = 0;
4634  /* Substring is smaller, so this will never grow */
4635  ast_str_set(&str2, 0, "%s", ptr + 1);
4636  }
4637  fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
4638  } else {
4639  fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
4640  }
4641  ast = ast_channel_unref(ast);
4642  } else {
4643  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4644  }
4645  } else {
4646  fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
4647  }
4648 
4649  if (check_mime(vmu->fullname)) {
4650  int first_line = 1;
4651  char *ptr;
4652  ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
4653  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
4654  *ptr = '\0';
4655  fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
4656  first_line = 0;
4657  /* Substring is smaller, so this will never grow */
4658  ast_str_set(&str2, 0, "%s", ptr + 1);
4659  }
4660  fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
4661  } else {
4662  fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
4663  }
4664 
4665  if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
4666  char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
4667  struct ast_channel *ast;
4668  if ((ast = ast_dummy_channel_alloc())) {
4669  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
4670  ast_str_substitute_variables(&str1, 0, ast, e_subj);
4671  if (check_mime(ast_str_buffer(str1))) {
4672  int first_line = 1;
4673  char *ptr;
4674  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
4675  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
4676  *ptr = '\0';
4677  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
4678  first_line = 0;
4679  /* Substring is smaller, so this will never grow */
4680  ast_str_set(&str2, 0, "%s", ptr + 1);
4681  }
4682  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
4683  } else {
4684  fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
4685  }
4686  ast = ast_channel_unref(ast);
4687  } else {
4688  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4689  }
4690  } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
4691  if (ast_strlen_zero(flag)) {
4692  fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
4693  } else {
4694  fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
4695  }
4696  } else {
4697  if (ast_strlen_zero(flag)) {
4698  fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
4699  } else {
4700  fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
4701  }
4702  }
4703 
4704  fprintf(p, "Message-ID: <Asterisk-%d-%u-%s-%d@%s>" ENDL, msgnum + 1,
4705  (unsigned int) ast_random(), mailbox, (int) getpid(), host);
4706  if (imap) {
4707  /* additional information needed for IMAP searching */
4708  fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
4709  /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
4710  fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
4711  fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
4712 #ifdef IMAP_STORAGE
4713  fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
4714 #else
4715  fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
4716 #endif
4717  /* flag added for Urgent */
4718  fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
4719  fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
4720  fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
4721  fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
4722  fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
4723  fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
4724  if (!ast_strlen_zero(category)) {
4725  fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
4726  } else {
4727  fprintf(p, "X-Asterisk-VM-Category: " ENDL);
4728  }
4729  fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
4730  fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
4731  fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
4732  }
4733  if (!ast_strlen_zero(cidnum)) {
4734  fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
4735  }
4736  if (!ast_strlen_zero(cidname)) {
4737  fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
4738  }
4739  fprintf(p, "MIME-Version: 1.0" ENDL);
4740  if (attach_user_voicemail) {
4741  /* Something unique. */
4742  snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%u", msgnum + 1, mailbox,
4743  (int) getpid(), (unsigned int) ast_random());
4744 
4745  fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
4746  fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
4747  fprintf(p, "--%s" ENDL, bound);
4748  }
4749  fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
4750  if (emailbody || vmu->emailbody) {
4751  char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
4752  struct ast_channel *ast;
4753  if ((ast = ast_dummy_channel_alloc())) {
4754  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
4755  ast_str_substitute_variables(&str1, 0, ast, e_body);
4756 #ifdef IMAP_STORAGE
4757  {
4758  /* Convert body to native line terminators for IMAP backend */
4759  char *line = ast_str_buffer(str1), *next;
4760  do {
4761  /* Terminate line before outputting it to the file */
4762  if ((next = strchr(line, '\n'))) {
4763  *next++ = '\0';
4764  }
4765  fprintf(p, "%s" ENDL, line);
4766  line = next;
4767  } while (!ast_strlen_zero(line));
4768  }
4769 #else
4770  fprintf(p, "%s" ENDL, ast_str_buffer(str1));
4771 #endif
4772  ast = ast_channel_unref(ast);
4773  } else {
4774  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4775  }
4776  } else if (msgnum > -1) {
4777  if (strcmp(vmu->mailbox, mailbox)) {
4778  /* Forwarded type */
4779  struct ast_config *msg_cfg;
4780  const char *v;
4781  int inttime;
4782  char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
4783  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
4784  /* Retrieve info from VM attribute file */
4785  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
4786  make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
4787  if (strlen(fromfile) < sizeof(fromfile) - 5) {
4788  strcat(fromfile, ".txt");
4789  }
4790  if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
4791  if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
4792  ast_copy_string(origcallerid, v, sizeof(origcallerid));
4793  }
4794 
4795  /* You might be tempted to do origdate, except that a) it's in the wrong
4796  * format, and b) it's missing for IMAP recordings. */
4797  if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
4798  struct timeval tv = { inttime, };
4799  struct ast_tm tm;
4800  ast_localtime(&tv, &tm, NULL);
4801  ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
4802  }
4803  fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
4804  " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
4805  "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
4806  " chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
4807  msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
4808  date, origcallerid, origdate);
4809  ast_config_destroy(msg_cfg);
4810  } else {
4811  goto plain_message;
4812  }
4813  } else {
4814 plain_message:
4815  fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
4816  "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
4817  "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
4818  ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
4819  (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
4820  }
4821  } else {
4822  fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
4823  "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
4824  }
4825 
4826  if (imap || attach_user_voicemail) {
4827  if (!ast_strlen_zero(attach2)) {
4828  snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
4829  ast_debug(5, "creating second attachment filename %s\n", filename);
4830  add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
4831  snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
4832  ast_debug(5, "creating attachment filename %s\n", filename);
4833  add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
4834  } else {
4835  snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
4836  ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
4837  add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
4838  }
4839  }
4840  ast_free(str1);
4841  ast_free(str2);
4842 }
4843 
4844 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)
4845 {
4846  char tmpdir[256], newtmp[256];
4847  char fname[256];
4848  char tmpcmd[256];
4849  int tmpfd = -1;
4850  int soxstatus = 0;
4851 
4852  /* Eww. We want formats to tell us their own MIME type */
4853  char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
4854 
4855  if (vmu->volgain < -.001 || vmu->volgain > .001) {
4856  create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
4857  snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
4858  tmpfd = mkstemp(newtmp);
4859  chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
4860  ast_debug(3, "newtmp: %s\n", newtmp);
4861  if (tmpfd > -1) {
4862  snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
4863  if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
4864  attach = newtmp;
4865  ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
4866  } else {
4867  ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
4868  soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
4869  ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
4870  }
4871  }
4872  }
4873  fprintf(p, "--%s" ENDL, bound);
4874  if (msgnum > -1)
4875  fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
4876  else
4877  fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
4878  fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
4879  fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
4880  if (msgnum > -1)
4881  fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
4882  else
4883  fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
4884  snprintf(fname, sizeof(fname), "%s.%s", attach, format);
4885  base_encode(fname, p);
4886  if (last)
4887  fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
4888  if (tmpfd > -1) {
4889  if (soxstatus == 0) {
4890  unlink(fname);
4891  }
4892  close(tmpfd);
4893  unlink(newtmp);
4894  }
4895  return 0;
4896 }
4897 
4898 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)
4899 {
4900  FILE *p = NULL;
4901  char tmp[80] = "/tmp/astmail-XXXXXX";
4902  char tmp2[256];
4903  char *stringp;
4904 
4905  if (vmu && ast_strlen_zero(vmu->email)) {
4906  ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
4907  return(0);
4908  }
4909 
4910  /* Mail only the first format */
4911  format = ast_strdupa(format);
4912  stringp = format;
4913  strsep(&stringp, "|");
4914 
4915  if (!strcmp(format, "wav49"))
4916  format = "WAV";
4917  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));
4918  /* Make a temporary file instead of piping directly to sendmail, in case the mail
4919  command hangs */
4920  if ((p = vm_mkftemp(tmp)) == NULL) {
4921  ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
4922  return -1;
4923  } else {
4924  make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
4925  fclose(p);
4926  snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
4927  ast_safe_system(tmp2);
4928  ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
4929  }
4930  return 0;
4931 }
4932 
4933 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)
4934 {
4935  char enc_cidnum[256], enc_cidname[256];
4936  char date[256];
4937  char host[MAXHOSTNAMELEN] = "";
4938  char who[256];
4939  char dur[PATH_MAX];
4940  char tmp[80] = "/tmp/astmail-XXXXXX";
4941  char tmp2[PATH_MAX];
4942  struct ast_tm tm;
4943  FILE *p;
4944  struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
4945 
4946  if (!str1 || !str2) {
4947  ast_free(str1);
4948  ast_free(str2);
4949  return -1;
4950  }
4951 
4952  if (cidnum) {
4953  strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
4954  }
4955  if (cidname) {
4956  strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
4957  }
4958 
4959  if ((p = vm_mkftemp(tmp)) == NULL) {
4960  ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
4961  ast_free(str1);
4962  ast_free(str2);
4963  return -1;
4964  }
4965  gethostname(host, sizeof(host)-1);
4966  if (strchr(srcemail, '@')) {
4967  ast_copy_string(who, srcemail, sizeof(who));
4968  } else {
4969  snprintf(who, sizeof(who), "%s@%s", srcemail, host);
4970  }
4971  snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
4972  ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
4973  fprintf(p, "Date: %s\n", date);
4974 
4975  /* Reformat for custom pager format */
4976  ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
4977 
4978  if (!ast_strlen_zero(pagerfromstring)) {
4979  struct ast_channel *ast;
4980  if ((ast = ast_dummy_channel_alloc())) {
4981  char *ptr;
4982  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
4983  ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
4984 
4985  if (check_mime(ast_str_buffer(str1))) {
4986  int first_line = 1;
4987  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
4988  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
4989  *ptr = '\0';
4990  fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
4991  first_line = 0;
4992  /* Substring is smaller, so this will never grow */
4993  ast_str_set(&str2, 0, "%s", ptr + 1);
4994  }
4995  fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
4996  } else {
4997  fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
4998  }
4999  ast = ast_channel_unref(ast);
5000  } else {
5001  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5002  }
5003  } else {
5004  fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
5005  }
5006 
5007  if (check_mime(vmu->fullname)) {
5008  int first_line = 1;
5009  char *ptr;
5010  ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
5011  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5012  *ptr = '\0';
5013  fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
5014  first_line = 0;
5015  /* Substring is smaller, so this will never grow */
5016  ast_str_set(&str2, 0, "%s", ptr + 1);
5017  }
5018  fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
5019  } else {
5020  fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
5021  }
5022 
5023  if (!ast_strlen_zero(pagersubject)) {
5024  struct ast_channel *ast;
5025  if ((ast = ast_dummy_channel_alloc())) {
5026  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5027  ast_str_substitute_variables(&str1, 0, ast, pagersubject);
5028  if (check_mime(ast_str_buffer(str1))) {
5029  int first_line = 1;
5030  char *ptr;
5031  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
5032  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5033  *ptr = '\0';
5034  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5035  first_line = 0;
5036  /* Substring is smaller, so this will never grow */
5037  ast_str_set(&str2, 0, "%s", ptr + 1);
5038  }
5039  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5040  } else {
5041  fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
5042  }
5043  ast = ast_channel_unref(ast);
5044  } else {
5045  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5046  }
5047  } else {
5048  if (ast_strlen_zero(flag)) {
5049  fprintf(p, "Subject: New VM\n\n");
5050  } else {
5051  fprintf(p, "Subject: New %s VM\n\n", flag);
5052  }
5053  }
5054 
5055  if (pagerbody) {
5056  struct ast_channel *ast;
5057  if ((ast = ast_dummy_channel_alloc())) {
5058  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5059  ast_str_substitute_variables(&str1, 0, ast, pagerbody);
5060  fprintf(p, "%s" ENDL, ast_str_buffer(str1));
5061  ast = ast_channel_unref(ast);
5062  } else {
5063  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5064  }
5065  } else {
5066  fprintf(p, "New %s long %s msg in box %s\n"
5067  "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
5068  }
5069 
5070  fclose(p);
5071  snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
5072  ast_safe_system(tmp2);
5073  ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
5074  ast_free(str1);
5075  ast_free(str2);
5076  return 0;
5077 }
5078 
5079 /*!
5080  * \brief Gets the current date and time, as formatted string.
5081  * \param s The buffer to hold the output formatted date.
5082  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
5083  *
5084  * The date format string used is "%a %b %e %r UTC %Y".
5085  *
5086  * \return zero on success, -1 on error.
5087  */
5088 static int get_date(char *s, int len)
5089 {
5090  struct ast_tm tm;
5091  struct timeval t = ast_tvnow();
5092 
5093  ast_localtime(&t, &tm, "UTC");
5094 
5095  return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
5096 }
5097 
5098 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
5099 {
5100  int res;
5101  char fn[PATH_MAX];
5102  char dest[PATH_MAX];
5103 
5104  snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
5105 
5106  if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
5107  ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
5108  return -1;
5109  }
5110 
5111  RETRIEVE(fn, -1, ext, context);
5112  if (ast_fileexists(fn, NULL, NULL) > 0) {
5113  res = ast_stream_and_wait(chan, fn, ecodes);
5114  if (res) {
5115  DISPOSE(fn, -1);
5116  return res;
5117  }
5118  } else {
5119  /* Dispose just in case */
5120  DISPOSE(fn, -1);
5121  res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
5122  if (res)
5123  return res;
5124  res = ast_say_digit_str(chan, ext, ecodes, chan->language);
5125  if (res)
5126  return res;
5127  }
5128  res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
5129  return res;
5130 }
5131 
5132 static void free_zone(struct vm_zone *z)
5133 {
5134  ast_free(z);
5135 }
5136 
5137 #ifdef ODBC_STORAGE
5138 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
5139 {
5140  int x = -1;
5141  int res;
5142  SQLHSTMT stmt = NULL;
5143  char sql[PATH_MAX];
5144  char rowdata[20];
5145  char tmp[PATH_MAX] = "";
5146  struct odbc_obj *obj = NULL;
5147  char *context;
5148  struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
5149 
5150  if (newmsgs)
5151  *newmsgs = 0;
5152  if (oldmsgs)
5153  *oldmsgs = 0;
5154  if (urgentmsgs)
5155  *urgentmsgs = 0;
5156 
5157  /* If no mailbox, return immediately */
5158  if (ast_strlen_zero(mailbox))
5159  return 0;
5160 
5161  ast_copy_string(tmp, mailbox, sizeof(tmp));
5162 
5163  if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
5164  int u, n, o;
5165  char *next, *remaining = tmp;
5166  while ((next = strsep(&remaining, " ,"))) {
5167  if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
5168  return -1;
5169  }
5170  if (urgentmsgs) {
5171  *urgentmsgs += u;
5172  }
5173  if (newmsgs) {
5174  *newmsgs += n;
5175  }
5176  if (oldmsgs) {
5177  *oldmsgs += o;
5178  }
5179  }
5180  return 0;
5181  }
5182 
5183  context = strchr(tmp, '@');
5184  if (context) {
5185  *context = '\0';
5186  context++;
5187  } else
5188  context = "default";
5189 
5190  if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
5191  do {
5192  if (newmsgs) {
5193  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
5194  if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
5195  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5196  break;
5197  }
5198  res = SQLFetch(stmt);
5199  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5200  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5201  break;
5202  }
5203  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5204  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5205  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5206  break;
5207  }
5208  *newmsgs = atoi(rowdata);
5209  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5210  }
5211 
5212  if (oldmsgs) {
5213  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
5214  if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
5215  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5216  break;
5217  }
5218  res = SQLFetch(stmt);
5219  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5220  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5221  break;
5222  }
5223  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5224  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5225  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5226  break;
5227  }
5228  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
5229  *oldmsgs = atoi(rowdata);
5230  }
5231 
5232  if (urgentmsgs) {
5233  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
5234  if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
5235  ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5236  break;
5237  }
5238  res = SQLFetch(stmt);
5239  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5240  ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5241  break;
5242  }
5243  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5244  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5245  ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5246  break;
5247  }
5248  *urgentmsgs = atoi(rowdata);
5249  }
5250 
5251  x = 0;
5252  } while (0);
5253  } else {
5254  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
5255  }
5256 
5257  if (stmt) {
5258  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5259  }
5260  if (obj) {
5261  ast_odbc_release_obj(obj);
5262  }
5263  return x;
5264 }
5265 
5266 /*!
5267  * \brief Gets the number of messages that exist in a mailbox folder.
5268  * \param context
5269  * \param mailbox
5270  * \param folder
5271  *
5272  * This method is used when ODBC backend is used.
5273  * \return The number of messages in this mailbox folder (zero or more).
5274  */
5275 static int messagecount(const char *context, const char *mailbox, const char *folder)
5276 {
5277  struct odbc_obj *obj = NULL;
5278  int nummsgs = 0;
5279  int res;
5280  SQLHSTMT stmt = NULL;
5281  char sql[PATH_MAX];
5282  char rowdata[20];
5283  struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
5284  if (!folder)
5285  folder = "INBOX";
5286  /* If no mailbox, return immediately */
5287  if (ast_strlen_zero(mailbox))
5288  return 0;
5289 
5290  obj = ast_odbc_request_obj(odbc_database, 0);
5291  if (obj) {
5292  if (!strcmp(folder, "INBOX")) {
5293  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);
5294  } else {
5295  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
5296  }
5297  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
5298  if (!stmt) {
5299  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5300  goto yuck;
5301  }
5302  res = SQLFetch(stmt);
5303  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5304  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5305  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5306  goto yuck;
5307  }
5308  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5309  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5310  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5311  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5312  goto yuck;
5313  }
5314  nummsgs = atoi(rowdata);
5315  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5316  } else
5317  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
5318 
5319 yuck:
5320  if (obj)
5321  ast_odbc_release_obj(obj);
5322  return nummsgs;
5323 }
5324 
5325 /**
5326  * \brief Determines if the given folder has messages.
5327  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
5328  *
5329  * This function is used when the mailbox is stored in an ODBC back end.
5330  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
5331  * \return 1 if the folder has one or more messages. zero otherwise.
5332  */
5333 static int has_voicemail(const char *mailbox, const char *folder)
5334 {
5335  char tmp[256], *tmp2 = tmp, *box, *context;
5336  ast_copy_string(tmp, mailbox, sizeof(tmp));
5337  while ((context = box = strsep(&tmp2, ",&"))) {
5338  strsep(&context, "@");
5339  if (ast_strlen_zero(context))
5340  context = "default";
5341  if (messagecount(context, box, folder))
5342  return 1;
5343  }
5344  return 0;
5345 }
5346 #endif
5347 #ifndef IMAP_STORAGE
5348 /*!
5349  * \brief Copies a message from one mailbox to another.
5350  * \param chan
5351  * \param vmu
5352  * \param imbox
5353  * \param msgnum
5354  * \param duration
5355  * \param recip
5356  * \param fmt
5357  * \param dir
5358  * \param flag
5359  *
5360  * This is only used by file storage based mailboxes.
5361  *
5362  * \return zero on success, -1 on error.
5363  */
5364 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)
5365 {
5366  char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
5367  const char *frombox = mbox(vmu, imbox);
5368  const char *userfolder;
5369  int recipmsgnum;
5370  int res = 0;
5371 
5372  ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
5373 
5374  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
5375  userfolder = "Urgent";
5376  } else {
5377  userfolder = "INBOX";
5378  }
5379 
5380  create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
5381 
5382  if (!dir)
5383  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
5384  else
5385  ast_copy_string(fromdir, dir, sizeof(fromdir));
5386 
5387  make_file(frompath, sizeof(frompath), fromdir, msgnum);
5388  make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
5389 
5390  if (vm_lock_path(todir))
5391  return ERROR_LOCK_PATH;
5392 
5393  recipmsgnum = last_message_index(recip, todir) + 1;
5394  if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
5395  make_file(topath, sizeof(topath), todir, recipmsgnum);
5396 #ifndef ODBC_STORAGE
5397  if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
5398  COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
5399  } else {
5400 #endif
5401  /* If we are prepending a message for ODBC, then the message already
5402  * exists in the database, but we want to force copying from the
5403  * filesystem (since only the FS contains the prepend). */
5404  copy_plain_file(frompath, topath);
5405  STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
5406  vm_delete(topath);
5407 #ifndef ODBC_STORAGE
5408  }
5409 #endif
5410  } else {
5411  ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
5412  res = -1;
5413  }
5414  ast_unlock_path(todir);
5415  notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
5416  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
5417  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
5418  flag);
5419 
5420  return res;
5421 }
5422 #endif
5423 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
5424 
5425 static int messagecount(const char *context, const char *mailbox, const char *folder)
5426 {
5427  return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
5428 }
5429 
5430 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
5431 {
5432  DIR *dir;
5433  struct dirent *de;
5434  char fn[256];
5435  int ret = 0;
5436 
5437  /* If no mailbox, return immediately */
5438  if (ast_strlen_zero(mailbox))
5439  return 0;
5440 
5441  if (ast_strlen_zero(folder))
5442  folder = "INBOX";
5443  if (ast_strlen_zero(context))
5444  context = "default";
5445 
5446  snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
5447 
5448  if (!(dir = opendir(fn)))
5449  return 0;
5450 
5451  while ((de = readdir(dir))) {
5452  if (!strncasecmp(de->d_name, "msg", 3)) {
5453  if (shortcircuit) {
5454  ret = 1;
5455  break;
5456  } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
5457  ret++;
5458  }
5459  }
5460  }
5461 
5462  closedir(dir);
5463 
5464  return ret;
5465 }
5466 
5467 /**
5468  * \brief Determines if the given folder has messages.
5469  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
5470  * \param folder the folder to look in
5471  *
5472  * This function is used when the mailbox is stored in a filesystem back end.
5473  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
5474  * \return 1 if the folder has one or more messages. zero otherwise.
5475  */
5476 static int has_voicemail(const char *mailbox, const char *folder)
5477 {
5478  char tmp[256], *tmp2 = tmp, *box, *context;
5479  ast_copy_string(tmp, mailbox, sizeof(tmp));
5480  if (ast_strlen_zero(folder)) {
5481  folder = "INBOX";
5482  }
5483  while ((box = strsep(&tmp2, ",&"))) {
5484  if ((context = strchr(box, '@')))
5485  *context++ = '\0';
5486  else
5487  context = "default";
5488  if (__has_voicemail(context, box, folder, 1))
5489  return 1;
5490  /* If we are checking INBOX, we should check Urgent as well */
5491  if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
5492  return 1;
5493  }
5494  }
5495  return 0;
5496 }
5497 
5498 
5499 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
5500 {
5501  char tmp[256];
5502  char *context;
5503 
5504  /* If no mailbox, return immediately */
5505  if (ast_strlen_zero(mailbox))
5506  return 0;
5507 
5508  if (newmsgs)
5509  *newmsgs = 0;
5510  if (oldmsgs)
5511  *oldmsgs = 0;
5512  if (urgentmsgs)
5513  *urgentmsgs = 0;
5514 
5515  if (strchr(mailbox, ',')) {
5516  int tmpnew, tmpold, tmpurgent;
5517  char *mb, *cur;
5518 
5519  ast_copy_string(tmp, mailbox, sizeof(tmp));
5520  mb = tmp;
5521  while ((cur = strsep(&mb, ", "))) {
5522  if (!ast_strlen_zero(cur)) {
5523  if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
5524  return -1;
5525  else {
5526  if (newmsgs)
5527  *newmsgs += tmpnew;
5528  if (oldmsgs)
5529  *oldmsgs += tmpold;
5530  if (urgentmsgs)
5531  *urgentmsgs += tmpurgent;
5532  }
5533  }
5534  }
5535  return 0;
5536  }
5537 
5538  ast_copy_string(tmp, mailbox, sizeof(tmp));
5539 
5540  if ((context = strchr(tmp, '@')))
5541  *context++ = '\0';
5542  else
5543  context = "default";
5544 
5545  if (newmsgs)
5546  *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
5547  if (oldmsgs)
5548  *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
5549  if (urgentmsgs)
5550  *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
5551 
5552  return 0;
5553 }
5554 
5555 #endif
5556 
5557 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
5558 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
5559 {
5560  int urgentmsgs = 0;
5561  int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
5562  if (newmsgs) {
5563  *newmsgs += urgentmsgs;
5564  }
5565  return res;
5566 }
5567 
5568 static void run_externnotify(char *context, char *extension, const char *flag)
5569 {
5570  char arguments[255];
5571  char ext_context[256] = "";
5572  int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
5573  struct ast_smdi_mwi_message *mwi_msg;
5574 
5575  if (!ast_strlen_zero(context))
5576  snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
5577  else
5578  ast_copy_string(ext_context, extension, sizeof(ext_context));
5579 
5580  if (smdi_iface) {
5581  if (ast_app_has_voicemail(ext_context, NULL))
5582  ast_smdi_mwi_set(smdi_iface, extension);
5583  else
5584  ast_smdi_mwi_unset(smdi_iface, extension);
5585 
5586  if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
5587  ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
5588  if (!strncmp(mwi_msg->cause, "INV", 3))
5589  ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
5590  else if (!strncmp(mwi_msg->cause, "BLK", 3))
5591  ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
5592  ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
5594  } else {
5595  ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
5596  }
5597  }
5598 
5599  if (!ast_strlen_zero(externnotify)) {
5600  if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
5601  ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
5602  } else {
5603  snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
5604  externnotify, S_OR(context, "\"\""),
5605  extension, newvoicemails,
5606  oldvoicemails, urgentvoicemails);
5607  ast_debug(1, "Executing %s\n", arguments);
5608  ast_safe_system(arguments);
5609  }
5610  }
5611 }
5612 
5613 /*!
5614  * \brief Variables used for saving a voicemail.
5615  *
5616  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
5617  */
5618 struct leave_vm_options {
5619  unsigned int flags;
5620  signed char record_gain;
5621  char *exitcontext;
5622 };
5623 
5624 /*!
5625  * \brief Prompts the user and records a voicemail to a mailbox.
5626  * \param chan
5627  * \param ext
5628  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
5629  *
5630  *
5631  *
5632  * \return zero on success, -1 on error.
5633  */
5634 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
5635 {
5636 #ifdef IMAP_STORAGE
5637  int newmsgs, oldmsgs;
5638 #else
5639  char urgdir[PATH_MAX];
5640 #endif
5641  char txtfile[PATH_MAX];
5642  char tmptxtfile[PATH_MAX];
5643  struct vm_state *vms = NULL;
5644  char callerid[256];
5645  FILE *txt;
5646  char date[256];
5647  int txtdes;
5648  int res = 0;
5649  int msgnum;
5650  int duration = 0;
5651  int sound_duration = 0;
5652  int ausemacro = 0;
5653  int ousemacro = 0;
5654  int ouseexten = 0;
5655  char tmpdur[16];
5656  char priority[16];
5657  char origtime[16];
5658  char dir[PATH_MAX];
5659  char tmpdir[PATH_MAX];
5660  char fn[PATH_MAX];
5661  char prefile[PATH_MAX] = "";
5662  char tempfile[PATH_MAX] = "";
5663  char ext_context[256] = "";
5664  char fmt[80];
5665  char *context;
5666  char ecodes[17] = "#";
5667  struct ast_str *tmp = ast_str_create(16);
5668  char *tmpptr;
5669  struct ast_vm_user *vmu;
5670  struct ast_vm_user svm;
5671  const char *category = NULL;
5672  const char *code;
5673  const char *alldtmf = "0123456789ABCD*#";
5674  char flag[80];
5675 
5676  if (!tmp) {
5677  return -1;
5678  }
5679 
5680  ast_str_set(&tmp, 0, "%s", ext);
5681  ext = ast_str_buffer(tmp);
5682  if ((context = strchr(ext, '@'))) {
5683  *context++ = '\0';
5684  tmpptr = strchr(context, '&');
5685  } else {
5686  tmpptr = strchr(ext, '&');
5687  }
5688 
5689  if (tmpptr)
5690  *tmpptr++ = '\0';
5691 
5692  ast_channel_lock(chan);
5693  if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
5694  category = ast_strdupa(category);
5695  }
5696  ast_channel_unlock(chan);
5697 
5698  if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
5699  ast_copy_string(flag, "Urgent", sizeof(flag));
5700  } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
5701  ast_copy_string(flag, "PRIORITY", sizeof(flag));
5702  } else {
5703  flag[0] = '\0';
5704  }
5705 
5706  ast_debug(3, "Before find_user\n");
5707  if (!(vmu = find_user(&svm, context, ext))) {
5708  ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
5709  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5710  ast_free(tmp);
5711  return res;
5712  }
5713  /* Setup pre-file if appropriate */
5714  if (strcmp(vmu->context, "default"))
5715  snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
5716  else
5717  ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
5718 
5719  /* Set the path to the prefile. Will be one of
5720  VM_SPOOL_DIRcontext/ext/busy
5721  VM_SPOOL_DIRcontext/ext/unavail
5722  Depending on the flag set in options.
5723  */
5724  if (ast_test_flag(options, OPT_BUSY_GREETING)) {
5725  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
5726  } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
5727  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
5728  }
5729  /* Set the path to the tmpfile as
5730  VM_SPOOL_DIR/context/ext/temp
5731  and attempt to create the folder structure.
5732  */
5733  snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
5734  if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
5735  ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
5736  ast_free(tmp);
5737  return -1;
5738  }
5739  RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
5740  if (ast_fileexists(tempfile, NULL, NULL) > 0)
5741  ast_copy_string(prefile, tempfile, sizeof(prefile));
5742 
5743  DISPOSE(tempfile, -1);
5744  /* It's easier just to try to make it than to check for its existence */
5745 #ifndef IMAP_STORAGE
5746  create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
5747 #else
5748  snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
5749  if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
5750  ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
5751  }
5752 #endif
5753 
5754  /* Check current or macro-calling context for special extensions */
5755  if (ast_test_flag(vmu, VM_OPERATOR)) {
5756  if (!ast_strlen_zero(vmu->exit)) {
5757  if (ast_exists_extension(chan, vmu->exit, "o", 1,
5758  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5759  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
5760  ouseexten = 1;
5761  }
5762  } else if (ast_exists_extension(chan, chan->context, "o", 1,
5763  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5764  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
5765  ouseexten = 1;
5766  } else if (!ast_strlen_zero(chan->macrocontext)
5767  && ast_exists_extension(chan, chan->macrocontext, "o", 1,
5768  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5769  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
5770  ousemacro = 1;
5771  }
5772  }
5773 
5774  if (!ast_strlen_zero(vmu->exit)) {
5775  if (ast_exists_extension(chan, vmu->exit, "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  }
5779  } else if (ast_exists_extension(chan, chan->context, "a", 1,
5780  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5781  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
5782  } else if (!ast_strlen_zero(chan->macrocontext)
5783  && ast_exists_extension(chan, chan->macrocontext, "a", 1,
5784  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5785  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
5786  ausemacro = 1;
5787  }
5788 
5789  if (ast_test_flag(options, OPT_DTMFEXIT)) {
5790  for (code = alldtmf; *code; code++) {
5791  char e[2] = "";
5792  e[0] = *code;
5793  if (strchr(ecodes, e[0]) == NULL
5794  && ast_canmatch_extension(chan,
5795  (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : chan->context),
5796  e, 1, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5797  strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
5798  }
5799  }
5800  }
5801 
5802  /* Play the beginning intro if desired */
5803  if (!ast_strlen_zero(prefile)) {
5804 #ifdef ODBC_STORAGE
5805  int success =
5806 #endif
5807  RETRIEVE(prefile, -1, ext, context);
5808  if (ast_fileexists(prefile, NULL, NULL) > 0) {
5809  if (ast_streamfile(chan, prefile, chan->language) > -1)
5810  res = ast_waitstream(chan, ecodes);
5811 #ifdef ODBC_STORAGE
5812  if (success == -1) {
5813  /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
5814  ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
5815  store_file(prefile, vmu->mailbox, vmu->context, -1);
5816  }
5817 #endif
5818  } else {
5819  ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
5820  res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
5821  }
5822  DISPOSE(prefile, -1);
5823  if (res < 0) {
5824  ast_debug(1, "Hang up during prefile playback\n");
5825  free_user(vmu);
5826  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5827  ast_free(tmp);
5828  return -1;
5829  }
5830  }
5831  if (res == '#') {
5832  /* On a '#' we skip the instructions */
5833  ast_set_flag(options, OPT_SILENT);
5834  res = 0;
5835  }
5836  /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
5837  if (vmu->maxmsg == 0) {
5838  if (option_debug > 2)
5839  ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
5840  pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
5841  goto leave_vm_out;
5842  }
5843  if (!res && !ast_test_flag(options, OPT_SILENT)) {
5844  res = ast_stream_and_wait(chan, INTRO, ecodes);
5845  if (res == '#') {
5846  ast_set_flag(options, OPT_SILENT);
5847  res = 0;
5848  }
5849  }
5850  if (res > 0)
5851  ast_stopstream(chan);
5852  /* Check for a '*' here in case the caller wants to escape from voicemail to something
5853  other than the operator -- an automated attendant or mailbox login for example */
5854  if (res == '*') {
5855  chan->exten[0] = 'a';
5856  chan->exten[1] = '\0';
5857  if (!ast_strlen_zero(vmu->exit)) {
5858  ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
5859  } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
5860  ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
5861  }
5862  chan->priority = 0;
5863  free_user(vmu);
5864  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
5865  ast_free(tmp);
5866  return 0;
5867  }
5868 
5869  /* Check for a '0' here */
5870  if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
5871  transfer:
5872  if (ouseexten || ousemacro) {
5873  chan->exten[0] = 'o';
5874  chan->exten[1] = '\0';
5875  if (!ast_strlen_zero(vmu->exit)) {
5876  ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
5877  } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
5878  ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
5879  }
5880  ast_play_and_wait(chan, "transfer");
5881  chan->priority = 0;
5882  free_user(vmu);
5883  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
5884  }
5885  ast_free(tmp);
5886  return OPERATOR_EXIT;
5887  }
5888 
5889  /* Allow all other digits to exit Voicemail and return to the dialplan */
5890  if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
5891  if (!ast_strlen_zero(options->exitcontext)) {
5892  ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
5893  }
5894  free_user(vmu);
5895  ast_free(tmp);
5896  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
5897  return res;
5898  }
5899 
5900  if (res < 0) {
5901  free_user(vmu);
5902  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5903  ast_free(tmp);
5904  return -1;
5905  }
5906  /* The meat of recording the message... All the announcements and beeps have been played*/
5907  ast_copy_string(fmt, vmfmts, sizeof(fmt));
5908  if (!ast_strlen_zero(fmt)) {
5909  msgnum = 0;
5910 
5911 #ifdef IMAP_STORAGE
5912  /* Is ext a mailbox? */
5913  /* must open stream for this user to get info! */
5914  res = inboxcount(ext_context, &newmsgs, &oldmsgs);
5915  if (res < 0) {
5916  ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
5917  ast_free(tmp);
5918  return -1;
5919  }
5920  if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
5921  /* It is possible under certain circumstances that inboxcount did not
5922  * create a vm_state when it was needed. This is a catchall which will
5923  * rarely be used.
5924  */
5925  if (!(vms = create_vm_state_from_user(vmu))) {
5926  ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
5927  ast_free(tmp);
5928  return -1;
5929  }
5930  }
5931  vms->newmessages++;
5932 
5933  /* here is a big difference! We add one to it later */
5934  msgnum = newmsgs + oldmsgs;
5935  ast_debug(3, "Messagecount set to %d\n", msgnum);
5936  snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
5937  /* set variable for compatibility */
5938  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
5939 
5940  if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
5941  goto leave_vm_out;
5942  }
5943 #else
5944  if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
5945  res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
5946  if (!res)
5947  res = ast_waitstream(chan, "");
5948  ast_log(AST_LOG_WARNING, "No more messages possible\n");
5949  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5950  inprocess_count(vmu->mailbox, vmu->context, -1);
5951  goto leave_vm_out;
5952  }
5953 
5954 #endif
5955  snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
5956  txtdes = mkstemp(tmptxtfile);
5957  chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
5958  if (txtdes < 0) {
5959  res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
5960  if (!res)
5961  res = ast_waitstream(chan, "");
5962  ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
5963  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5964  inprocess_count(vmu->mailbox, vmu->context, -1);
5965  goto leave_vm_out;
5966  }
5967 
5968  /* Now play the beep once we have the message number for our next message. */
5969  if (res >= 0) {
5970  /* Unless we're *really* silent, try to send the beep */
5971  res = ast_stream_and_wait(chan, "beep", "");
5972  }
5973 
5974  /* Store information in real-time storage */
5975  if (ast_check_realtime("voicemail_data")) {
5976  snprintf(priority, sizeof(priority), "%d", chan->priority);
5977  snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
5978  get_date(date, sizeof(date));
5979  ast_callerid_merge(callerid, sizeof(callerid),
5980  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
5981  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
5982  "Unknown");
5983  ast_store_realtime("voicemail_data",
5984  "origmailbox", ext,
5985  "context", chan->context,
5986  "macrocontext", chan->macrocontext,
5987  "exten", chan->exten,
5988  "priority", priority,
5989  "callerchan", chan->name,
5990  "callerid", callerid,
5991  "origdate", date,
5992  "origtime", origtime,
5993  "category", S_OR(category, ""),
5994  "filename", tmptxtfile,
5995  SENTINEL);
5996  }
5997 
5998  /* Store information */
5999  txt = fdopen(txtdes, "w+");
6000  if (txt) {
6001  get_date(date, sizeof(date));
6002  ast_callerid_merge(callerid, sizeof(callerid),
6003  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
6004  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
6005  "Unknown");
6006  fprintf(txt,
6007  ";\n"
6008  "; Message Information file\n"
6009  ";\n"
6010  "[message]\n"
6011  "origmailbox=%s\n"
6012  "context=%s\n"
6013  "macrocontext=%s\n"
6014  "exten=%s\n"
6015  "rdnis=%s\n"
6016  "priority=%d\n"
6017  "callerchan=%s\n"
6018  "callerid=%s\n"
6019  "origdate=%s\n"
6020  "origtime=%ld\n"
6021  "category=%s\n",
6022  ext,
6023  chan->context,
6024  chan->macrocontext,
6025  chan->exten,
6027  chan->redirecting.from.number.str, "unknown"),
6028  chan->priority,
6029  chan->name,
6030  callerid,
6031  date, (long) time(NULL),
6032  category ? category : "");
6033  } else {
6034  ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
6035  inprocess_count(vmu->mailbox, vmu->context, -1);
6036  if (ast_check_realtime("voicemail_data")) {
6037  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6038  }
6039  res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
6040  goto leave_vm_out;
6041  }
6042  res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
6043 
6044  if (txt) {
6045  fprintf(txt, "flag=%s\n", flag);
6046  if (sound_duration < vmu->minsecs) {
6047  fclose(txt);
6048  ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
6049  ast_filedelete(tmptxtfile, NULL);
6050  unlink(tmptxtfile);
6051  if (ast_check_realtime("voicemail_data")) {
6052  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6053  }
6054  inprocess_count(vmu->mailbox, vmu->context, -1);
6055  } else {
6056  fprintf(txt, "duration=%d\n", duration);
6057  fclose(txt);
6058  if (vm_lock_path(dir)) {
6059  ast_log(AST_LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
6060  /* Delete files */
6061  ast_filedelete(tmptxtfile, NULL);
6062  unlink(tmptxtfile);
6063  inprocess_count(vmu->mailbox, vmu->context, -1);
6064  } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
6065  ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
6066  unlink(tmptxtfile);
6067  ast_unlock_path(dir);
6068  inprocess_count(vmu->mailbox, vmu->context, -1);
6069  if (ast_check_realtime("voicemail_data")) {
6070  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6071  }
6072  } else {
6073 #ifndef IMAP_STORAGE
6074  msgnum = last_message_index(vmu, dir) + 1;
6075 #endif
6076  make_file(fn, sizeof(fn), dir, msgnum);
6077 
6078  /* assign a variable with the name of the voicemail file */
6079 #ifndef IMAP_STORAGE
6080  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
6081 #else
6082  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
6083 #endif
6084 
6085  snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
6086  ast_filerename(tmptxtfile, fn, NULL);
6087  rename(tmptxtfile, txtfile);
6088  inprocess_count(vmu->mailbox, vmu->context, -1);
6089 
6090  /* Properly set permissions on voicemail text descriptor file.
6091  Unfortunately mkstemp() makes this file 0600 on most unix systems. */
6092  if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
6093  ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
6094 
6095  ast_unlock_path(dir);
6096  if (ast_check_realtime("voicemail_data")) {
6097  snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
6098  ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
6099  }
6100  /* We must store the file first, before copying the message, because
6101  * ODBC storage does the entire copy with SQL.
6102  */
6103  if (ast_fileexists(fn, NULL, NULL) > 0) {
6104  STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
6105  }
6106 
6107  /* Are there to be more recipients of this message? */
6108  while (tmpptr) {
6109  struct ast_vm_user recipu, *recip;
6110  char *exten, *cntx;
6111 
6112  exten = strsep(&tmpptr, "&");
6113  cntx = strchr(exten, '@');
6114  if (cntx) {
6115  *cntx = '\0';
6116  cntx++;
6117  }
6118  if ((recip = find_user(&recipu, cntx, exten))) {
6119  copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
6120  free_user(recip);
6121  }
6122  }
6123 #ifndef IMAP_STORAGE
6124  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
6125  /* Move the message from INBOX to Urgent folder if this is urgent! */
6126  char sfn[PATH_MAX];
6127  char dfn[PATH_MAX];
6128  int x;
6129  /* It's easier just to try to make it than to check for its existence */
6130  create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
6131  x = last_message_index(vmu, urgdir) + 1;
6132  make_file(sfn, sizeof(sfn), dir, msgnum);
6133  make_file(dfn, sizeof(dfn), urgdir, x);
6134  ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
6135  RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
6136  /* Notification must happen for this new message in Urgent folder, not INBOX */
6137  ast_copy_string(fn, dfn, sizeof(fn));
6138  msgnum = x;
6139  }
6140 #endif
6141  /* Notification needs to happen after the copy, though. */
6142  if (ast_fileexists(fn, NULL, NULL)) {
6143 #ifdef IMAP_STORAGE
6144  notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
6145  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
6146  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
6147  flag);
6148 #else
6149  notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
6150  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
6151  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
6152  flag);
6153 #endif
6154  }
6155 
6156  /* Disposal needs to happen after the optional move and copy */
6157  if (ast_fileexists(fn, NULL, NULL)) {
6158  DISPOSE(dir, msgnum);
6159  }
6160  }
6161  }
6162  } else {
6163  inprocess_count(vmu->mailbox, vmu->context, -1);
6164  }
6165  if (res == '0') {
6166  goto transfer;
6167  } else if (res > 0 && res != 't')
6168  res = 0;
6169 
6170  if (sound_duration < vmu->minsecs)
6171  /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
6172  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
6173  else
6174  pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
6175  } else
6176  ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
6177 leave_vm_out:
6178  free_user(vmu);
6179 
6180 #ifdef IMAP_STORAGE
6181  /* expunge message - use UID Expunge if supported on IMAP server*/
6182  ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
6183  if (expungeonhangup == 1) {
6184  ast_mutex_lock(&vms->lock);
6185 #ifdef HAVE_IMAP_TK2006
6186  if (LEVELUIDPLUS (vms->mailstream)) {
6187  mail_expunge_full(vms->mailstream, NIL, EX_UID);
6188  } else
6189 #endif
6190  mail_expunge(vms->mailstream);
6191  ast_mutex_unlock(&vms->lock);
6192  }
6193 #endif
6194 
6195  ast_free(tmp);
6196  return res;
6197 }
6198 
6199 #if !defined(IMAP_STORAGE)
6200 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
6201 {
6202  /* we know the actual number of messages, so stop process when number is hit */
6203 
6204  int x, dest;
6205  char sfn[PATH_MAX];
6206  char dfn[PATH_MAX];
6207 
6208  if (vm_lock_path(dir)) {
6209  return ERROR_LOCK_PATH;
6210  }
6211 
6212  for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
6213  make_file(sfn, sizeof(sfn), dir, x);
6214  if (EXISTS(dir, x, sfn, NULL)) {
6215 
6216  if (x != dest) {
6217  make_file(dfn, sizeof(dfn), dir, dest);
6218  RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
6219  }
6220 
6221  dest++;
6222  }
6223  }
6224  ast_unlock_path(dir);
6225 
6226  return dest;
6227 }
6228 #endif
6229 
6230 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
6231 {
6232  int d;
6233  d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
6234  return d;
6235 }
6236 
6237 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
6238 {
6239 #ifdef IMAP_STORAGE
6240  /* we must use mbox(x) folder names, and copy the message there */
6241  /* simple. huh? */
6242  char sequence[10];
6243  char mailbox[256];
6244  int res;
6245 
6246  /* get the real IMAP message number for this message */
6247  snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
6248 
6249  ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
6250  ast_mutex_lock(&vms->lock);
6251  /* if save to Old folder, put in INBOX as read */
6252  if (box == OLD_FOLDER) {
6253  mail_setflag(vms->mailstream, sequence, "\\Seen");
6254  mail_clearflag(vms->mailstream, sequence, "\\Unseen");
6255  } else if (box == NEW_FOLDER) {
6256  mail_setflag(vms->mailstream, sequence, "\\Unseen");
6257  mail_clearflag(vms->mailstream, sequence, "\\Seen");
6258  }
6259  if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
6260  ast_mutex_unlock(&vms->lock);
6261  return 0;
6262  }
6263  /* Create the folder if it don't exist */
6264  imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
6265  ast_debug(5, "Checking if folder exists: %s\n", mailbox);
6266  if (mail_create(vms->mailstream, mailbox) == NIL)
6267  ast_debug(5, "Folder exists.\n");
6268  else
6269  ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
6270  res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
6271  ast_mutex_unlock(&vms->lock);
6272  return res;
6273 #else
6274  char *dir = vms->curdir;
6275  char *username = vms->username;
6276  char *context = vmu->context;
6277  char sfn[PATH_MAX];
6278  char dfn[PATH_MAX];
6279  char ddir[PATH_MAX];
6280  const char *dbox = mbox(vmu, box);
6281  int x, i;
6282  create_dirpath(ddir, sizeof(ddir), context, username, dbox);
6283 
6284  if (vm_lock_path(ddir))
6285  return ERROR_LOCK_PATH;
6286 
6287  x = last_message_index(vmu, ddir) + 1;
6288 
6289  if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
6290  x--;
6291  for (i = 1; i <= x; i++) {
6292  /* Push files down a "slot". The oldest file (msg0000) will be deleted. */
6293  make_file(sfn, sizeof(sfn), ddir, i);
6294  make_file(dfn, sizeof(dfn), ddir, i - 1);
6295  if (EXISTS(ddir, i, sfn, NULL)) {
6296  RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
6297  } else
6298  break;
6299  }
6300  } else {
6301  if (x >= vmu->maxmsg) {
6302  ast_unlock_path(ddir);
6303  return -1;
6304  }
6305  }
6306  make_file(sfn, sizeof(sfn), dir, msg);
6307  make_file(dfn, sizeof(dfn), ddir, x);
6308  if (strcmp(sfn, dfn)) {
6309  COPY(dir, msg, ddir, x, username, context, sfn, dfn);
6310  }
6311  ast_unlock_path(ddir);
6312 #endif
6313  return 0;
6314 }
6315 
6316 static int adsi_logo(unsigned char *buf)
6317 {
6318  int bytes = 0;
6319  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
6320  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
6321  return bytes;
6322 }
6323 
6324 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
6325 {
6326  unsigned char buf[256];
6327  int bytes = 0;
6328  int x;
6329  char num[5];
6330 
6331  *useadsi = 0;
6332  bytes += ast_adsi_data_mode(buf + bytes);
6333  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6334 
6335  bytes = 0;
6336  bytes += adsi_logo(buf);
6337  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
6338 #ifdef DISPLAY
6339  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
6340 #endif
6341  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6342  bytes += ast_adsi_data_mode(buf + bytes);
6343  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6344 
6345  if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
6346  bytes = 0;
6347  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
6348  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
6349  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6350  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6351  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6352  return 0;
6353  }
6354 
6355 #ifdef DISPLAY
6356  /* Add a dot */
6357  bytes = 0;
6358  bytes += ast_adsi_logo(buf);
6359  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
6360  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
6361  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6362  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6363 #endif
6364  bytes = 0;
6365  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
6366  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
6367  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
6368  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
6369  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
6370  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
6371  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
6372 
6373 #ifdef DISPLAY
6374  /* Add another dot */
6375  bytes = 0;
6376  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
6377  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6378 
6379  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6380  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6381 #endif
6382 
6383  bytes = 0;
6384  /* These buttons we load but don't use yet */
6385  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
6386  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
6387  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
6388  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
6389  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
6390  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
6391  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
6392 
6393 #ifdef DISPLAY
6394  /* Add another dot */
6395  bytes = 0;
6396  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
6397  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6398  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6399 #endif
6400 
6401  bytes = 0;
6402  for (x = 0; x < 5; x++) {
6403  snprintf(num, sizeof(num), "%d", x);
6404  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
6405  }
6406  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
6407  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
6408 
6409 #ifdef DISPLAY
6410  /* Add another dot */
6411  bytes = 0;
6412  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
6413  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6414  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6415 #endif
6416 
6417  if (ast_adsi_end_download(chan)) {
6418  bytes = 0;
6419  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
6420  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
6421  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6422  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6423  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6424  return 0;
6425  }
6426  bytes = 0;
6427  bytes += ast_adsi_download_disconnect(buf + bytes);
6428  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6429  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
6430 
6431  ast_debug(1, "Done downloading scripts...\n");
6432 
6433 #ifdef DISPLAY
6434  /* Add last dot */
6435  bytes = 0;
6436  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
6437  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6438 #endif
6439  ast_debug(1, "Restarting session...\n");
6440 
6441  bytes = 0;
6442  /* Load the session now */
6443  if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
6444  *useadsi = 1;
6445  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
6446  } else
6447  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
6448 
6449  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6450  return 0;
6451 }
6452 
6453 static void adsi_begin(struct ast_channel *chan, int *useadsi)
6454 {
6455  int x;
6456  if (!ast_adsi_available(chan))
6457  return;
6458  x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
6459  if (x < 0)
6460  return;
6461  if (!x) {
6462  if (adsi_load_vmail(chan, useadsi)) {
6463  ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
6464  return;
6465  }
6466  } else
6467  *useadsi = 1;
6468 }
6469 
6470 static void adsi_login(struct ast_channel *chan)
6471 {
6472  unsigned char buf[256];
6473  int bytes = 0;
6474  unsigned char keys[8];
6475  int x;
6476  if (!ast_adsi_available(chan))
6477  return;
6478 
6479  for (x = 0; x < 8; x++)
6480  keys[x] = 0;
6481  /* Set one key for next */
6482  keys[3] = ADSI_KEY_APPS + 3;
6483 
6484  bytes += adsi_logo(buf + bytes);
6485  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
6486  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
6487  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6488  bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
6489  bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
6490  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
6491  bytes += ast_adsi_set_keys(buf + bytes, keys);
6492  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6493  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6494 }
6495 
6496 static void adsi_password(struct ast_channel *chan)
6497 {
6498  unsigned char buf[256];
6499  int bytes = 0;
6500  unsigned char keys[8];
6501  int x;
6502  if (!ast_adsi_available(chan))
6503  return;
6504 
6505  for (x = 0; x < 8; x++)
6506  keys[x] = 0;
6507  /* Set one key for next */
6508  keys[3] = ADSI_KEY_APPS + 3;
6509 
6510  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6511  bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
6512  bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
6513  bytes += ast_adsi_set_keys(buf + bytes, keys);
6514  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6515  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6516 }
6517 
6518 static void adsi_folders(struct ast_channel *chan, int start, char *label)
6519 {
6520  unsigned char buf[256];
6521  int bytes = 0;
6522  unsigned char keys[8];
6523  int x, y;
6524 
6525  if (!ast_adsi_available(chan))
6526  return;
6527 
6528  for (x = 0; x < 5; x++) {
6529  y = ADSI_KEY_APPS + 12 + start + x;
6530  if (y > ADSI_KEY_APPS + 12 + 4)
6531  y = 0;
6532  keys[x] = ADSI_KEY_SKT | y;
6533  }
6534  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
6535  keys[6] = 0;
6536  keys[7] = 0;
6537 
6538  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
6539  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
6540  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6541  bytes += ast_adsi_set_keys(buf + bytes, keys);
6542  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6543 
6544  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6545 }
6546 
6547 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
6548 {
6549  int bytes = 0;
6550  unsigned char buf[256];
6551  char buf1[256], buf2[256];
6552  char fn2[PATH_MAX];
6553 
6554  char cid[256] = "";
6555  char *val;
6556  char *name, *num;
6557  char datetime[21] = "";
6558  FILE *f;
6559 
6560  unsigned char keys[8];
6561 
6562  int x;
6563 
6564  if (!ast_adsi_available(chan))
6565  return;
6566 
6567  /* Retrieve important info */
6568  snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
6569  f = fopen(fn2, "r");
6570  if (f) {
6571  while (!feof(f)) {
6572  if (!fgets((char *) buf, sizeof(buf), f)) {
6573  continue;
6574  }
6575  if (!feof(f)) {
6576  char *stringp = NULL;
6577  stringp = (char *) buf;
6578  strsep(&stringp, "=");
6579  val = strsep(&stringp, "=");
6580  if (!ast_strlen_zero(val)) {
6581  if (!strcmp((char *) buf, "callerid"))
6582  ast_copy_string(cid, val, sizeof(cid));
6583  if (!strcmp((char *) buf, "origdate"))
6584  ast_copy_string(datetime, val, sizeof(datetime));
6585  }
6586  }
6587  }
6588  fclose(f);
6589  }
6590  /* New meaning for keys */
6591  for (x = 0; x < 5; x++)
6592  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
6593  keys[6] = 0x0;
6594  keys[7] = 0x0;
6595 
6596  if (!vms->curmsg) {
6597  /* No prev key, provide "Folder" instead */
6598  keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6599  }
6600  if (vms->curmsg >= vms->lastmsg) {
6601  /* If last message ... */
6602  if (vms->curmsg) {
6603  /* but not only message, provide "Folder" instead */
6604  keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6605  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6606 
6607  } else {
6608  /* Otherwise if only message, leave blank */
6609  keys[3] = 1;
6610  }
6611  }
6612 
6613  if (!ast_strlen_zero(cid)) {
6614  ast_callerid_parse(cid, &name, &num);
6615  if (!name)
6616  name = num;
6617  } else
6618  name = "Unknown Caller";
6619 
6620  /* If deleted, show "undeleted" */
6621 #ifdef IMAP_STORAGE
6622  ast_mutex_lock(&vms->lock);
6623 #endif
6624  if (vms->deleted[vms->curmsg]) {
6625  keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
6626  }
6627 #ifdef IMAP_STORAGE
6628  ast_mutex_unlock(&vms->lock);
6629 #endif
6630 
6631  /* Except "Exit" */
6632  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
6633  snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
6634  strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
6635  snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
6636 
6637  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
6638  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
6639  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
6640  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
6641  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6642  bytes += ast_adsi_set_keys(buf + bytes, keys);
6643  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6644 
6645  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6646 }
6647 
6648 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
6649 {
6650  int bytes = 0;
6651  unsigned char buf[256];
6652  unsigned char keys[8];
6653 
6654  int x;
6655 
6656  if (!ast_adsi_available(chan))
6657  return;
6658 
6659  /* New meaning for keys */
6660  for (x = 0; x < 5; x++)
6661  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
6662 
6663  keys[6] = 0x0;
6664  keys[7] = 0x0;
6665 
6666  if (!vms->curmsg) {
6667  /* No prev key, provide "Folder" instead */
6668  keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6669  }
6670  if (vms->curmsg >= vms->lastmsg) {
6671  /* If last message ... */
6672  if (vms->curmsg) {
6673  /* but not only message, provide "Folder" instead */
6674  keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6675  } else {
6676  /* Otherwise if only message, leave blank */
6677  keys[3] = 1;
6678  }
6679  }
6680 
6681  /* If deleted, show "undeleted" */
6682 #ifdef IMAP_STORAGE
6683  ast_mutex_lock(&vms->lock);
6684 #endif
6685  if (vms->deleted[vms->curmsg]) {
6686  keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
6687  }
6688 #ifdef IMAP_STORAGE
6689  ast_mutex_unlock(&vms->lock);
6690 #endif
6691 
6692  /* Except "Exit" */
6693  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
6694  bytes += ast_adsi_set_keys(buf + bytes, keys);
6695  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6696 
6697  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6698 }
6699 
6700 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
6701 {
6702  unsigned char buf[256] = "";
6703  char buf1[256] = "", buf2[256] = "";
6704  int bytes = 0;
6705  unsigned char keys[8];
6706  int x;
6707 
6708  char *newm = (vms->newmessages == 1) ? "message" : "messages";
6709  char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
6710  if (!ast_adsi_available(chan))
6711  return;
6712  if (vms->newmessages) {
6713  snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
6714  if (vms->oldmessages) {
6715  strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
6716  snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
6717  } else {
6718  snprintf(buf2, sizeof(buf2), "%s.", newm);
6719  }
6720  } else if (vms->oldmessages) {
6721  snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
6722  snprintf(buf2, sizeof(buf2), "%s.", oldm);
6723  } else {
6724  strcpy(buf1, "You have no messages.");
6725  buf2[0] = ' ';
6726  buf2[1] = '\0';
6727  }
6728  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
6729  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
6730  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6731 
6732  for (x = 0; x < 6; x++)
6733  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
6734  keys[6] = 0;
6735  keys[7] = 0;
6736 
6737  /* Don't let them listen if there are none */
6738  if (vms->lastmsg < 0)
6739  keys[0] = 1;
6740  bytes += ast_adsi_set_keys(buf + bytes, keys);
6741 
6742  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6743 
6744  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6745 }
6746 
6747 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
6748 {
6749  unsigned char buf[256] = "";
6750  char buf1[256] = "", buf2[256] = "";
6751  int bytes = 0;
6752  unsigned char keys[8];
6753  int x;
6754 
6755  char *mess = (vms->lastmsg == 0) ? "message" : "messages";
6756 
6757  if (!ast_adsi_available(chan))
6758  return;
6759 
6760  /* Original command keys */
6761  for (x = 0; x < 6; x++)
6762  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
6763 
6764  keys[6] = 0;
6765  keys[7] = 0;
6766 
6767  if ((vms->lastmsg + 1) < 1)
6768  keys[0] = 0;
6769 
6770  snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
6771  strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
6772 
6773  if (vms->lastmsg + 1)
6774  snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
6775  else
6776  strcpy(buf2, "no messages.");
6777  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
6778  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
6779  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
6780  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6781  bytes += ast_adsi_set_keys(buf + bytes, keys);
6782 
6783  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6784 
6785  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6786 
6787 }
6788 
6789 /*
6790 static void adsi_clear(struct ast_channel *chan)
6791 {
6792  char buf[256];
6793  int bytes=0;
6794  if (!ast_adsi_available(chan))
6795  return;
6796  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6797  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6798 
6799  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6800 }
6801 */
6802 
6803 static void adsi_goodbye(struct ast_channel *chan)
6804 {
6805  unsigned char buf[256];
6806  int bytes = 0;
6807 
6808  if (!ast_adsi_available(chan))
6809  return;
6810  bytes += adsi_logo(buf + bytes);
6811  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
6812  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
6813  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6814  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6815 
6816  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6817 }
6818 
6819 /*!\brief get_folder: Folder menu
6820  * Plays "press 1 for INBOX messages" etc.
6821  * Should possibly be internationalized
6822  */
6823 static int get_folder(struct ast_channel *chan, int start)
6824 {
6825  int x;
6826  int d;
6827  char fn[PATH_MAX];
6828  d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
6829  if (d)
6830  return d;
6831  for (x = start; x < 5; x++) { /* For all folders */
6832  if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
6833  return d;
6834  d = ast_play_and_wait(chan, "vm-for"); /* "for" */
6835  if (d)
6836  return d;
6837  snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
6838 
6839  /* The inbox folder can have its name changed under certain conditions
6840  * so this checks if the sound file exists for the inbox folder name and
6841  * if it doesn't, plays the default name instead. */
6842  if (x == 0) {
6843  if (ast_fileexists(fn, NULL, NULL)) {
6844  d = vm_play_folder_name(chan, fn);
6845  } else {
6846  ast_verb(1, "failed to find %s\n", fn);
6847  d = vm_play_folder_name(chan, "vm-INBOX");
6848  }
6849  } else {
6850  ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
6851  d = vm_play_folder_name(chan, fn);
6852  }
6853 
6854  if (d)
6855  return d;
6856  d = ast_waitfordigit(chan, 500);
6857  if (d)
6858  return d;
6859  }
6860 
6861  d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
6862  if (d)
6863  return d;
6864  d = ast_waitfordigit(chan, 4000);
6865  return d;
6866 }
6867 
6868 /*!
6869  * \brief plays a prompt and waits for a keypress.
6870  * \param chan
6871  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
6872  * \param start Does not appear to be used at this time.
6873  *
6874  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
6875  * After playing the message identified by the fn parameter value, it calls get_folder(), which plays the
6876  * prompting for the number inputs that correspond to the available folders.
6877  *
6878  * \return zero on success, or -1 on error.
6879  */
6880 static int get_folder2(struct ast_channel *chan, char *fn, int start)
6881 {
6882  int res = 0;
6883  int loops = 0;
6884 
6885  res = ast_play_and_wait(chan, fn); /* Folder name */
6886  while (((res < '0') || (res > '9')) &&
6887  (res != '#') && (res >= 0) &&
6888  loops < 4) {
6889  res = get_folder(chan, 0);
6890  loops++;
6891  }
6892  if (loops == 4) { /* give up */
6893  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
6894  return '#';
6895  }
6896  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
6897  return res;
6898 }
6899 
6900 /*!
6901  * \brief presents the option to prepend to an existing message when forwarding it.
6902  * \param chan
6903  * \param vmu
6904  * \param curdir
6905  * \param curmsg
6906  * \param vm_fmts
6907  * \param context
6908  * \param record_gain
6909  * \param duration
6910  * \param vms
6911  * \param flag
6912  *
6913  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
6914  *
6915  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
6916  * \return zero on success, -1 on error.
6917  */
6918 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
6919  char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
6920 {
6921  int cmd = 0;
6922  int retries = 0, prepend_duration = 0, already_recorded = 0;
6923  char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
6924  char textfile[PATH_MAX];
6925  struct ast_config *msg_cfg;
6926  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
6927 #ifndef IMAP_STORAGE
6928  signed char zero_gain = 0;
6929 #endif
6930  const char *duration_str;
6931 
6932  /* Must always populate duration correctly */
6933  make_file(msgfile, sizeof(msgfile), curdir, curmsg);
6934  strcpy(textfile, msgfile);
6935  strcpy(backup, msgfile);
6936  strcpy(backup_textfile, msgfile);
6937  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
6938  strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
6939  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
6940 
6941  if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
6942  *duration = atoi(duration_str);
6943  } else {
6944  *duration = 0;
6945  }
6946 
6947  while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
6948  if (cmd)
6949  retries = 0;
6950  switch (cmd) {
6951  case '1':
6952 
6953 #ifdef IMAP_STORAGE
6954  /* Record new intro file */
6955  make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
6956  strncat(vms->introfn, "intro", sizeof(vms->introfn));
6957  ast_play_and_wait(chan, INTRO);
6958  ast_play_and_wait(chan, "beep");
6959  cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
6960  if (cmd == -1) {
6961  break;
6962  }
6963  cmd = 't';
6964 #else
6965 
6966  /* prepend a message to the current message, update the metadata and return */
6967 
6968  make_file(msgfile, sizeof(msgfile), curdir, curmsg);
6969  strcpy(textfile, msgfile);
6970  strncat(textfile, ".txt", sizeof(textfile) - 1);
6971  *duration = 0;
6972 
6973  /* if we can't read the message metadata, stop now */
6974  if (!valid_config(msg_cfg)) {
6975  cmd = 0;
6976  break;
6977  }
6978 
6979  /* Back up the original file, so we can retry the prepend and restore it after forward. */
6980 #ifndef IMAP_STORAGE
6981  if (already_recorded) {
6982  ast_filecopy(backup, msgfile, NULL);
6983  copy(backup_textfile, textfile);
6984  }
6985  else {
6986  ast_filecopy(msgfile, backup, NULL);
6987  copy(textfile, backup_textfile);
6988  }
6989 #endif
6990  already_recorded = 1;
6991 
6992  if (record_gain)
6993  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
6994 
6995  cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
6996 
6997  if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
6998  ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
6999  ast_stream_and_wait(chan, vm_prepend_timeout, "");
7000  ast_filerename(backup, msgfile, NULL);
7001  }
7002 
7003  if (record_gain)
7004  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
7005 
7006 
7007  if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
7008  *duration = atoi(duration_str);
7009 
7010  if (prepend_duration) {
7011  struct ast_category *msg_cat;
7012  /* need enough space for a maximum-length message duration */
7013  char duration_buf[12];
7014 
7015  *duration += prepend_duration;
7016  msg_cat = ast_category_get(msg_cfg, "message");
7017  snprintf(duration_buf, 11, "%ld", *duration);
7018  if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
7019  ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
7020  }
7021  }
7022 
7023 #endif
7024  break;
7025  case '2':
7026  /* NULL out introfile so we know there is no intro! */
7027 #ifdef IMAP_STORAGE
7028  *vms->introfn = '\0';
7029 #endif
7030  cmd = 't';
7031  break;
7032  case '*':
7033  cmd = '*';
7034  break;
7035  default:
7036  /* If time_out and return to menu, reset already_recorded */
7037  already_recorded = 0;
7038 
7039  cmd = ast_play_and_wait(chan, "vm-forwardoptions");
7040  /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
7041  if (!cmd) {
7042  cmd = ast_play_and_wait(chan, "vm-starmain");
7043  /* "press star to return to the main menu" */
7044  }
7045  if (!cmd) {
7046  cmd = ast_waitfordigit(chan, 6000);
7047  }
7048  if (!cmd) {
7049  retries++;
7050  }
7051  if (retries > 3) {
7052  cmd = '*'; /* Let's cancel this beast */
7053  }
7054  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
7055  }
7056  }
7057 
7058  if (valid_config(msg_cfg))
7059  ast_config_destroy(msg_cfg);
7060  if (prepend_duration)
7061  *duration = prepend_duration;
7062 
7063  if (already_recorded && cmd == -1) {
7064  /* restore original message if prepention cancelled */
7065  ast_filerename(backup, msgfile, NULL);
7066  rename(backup_textfile, textfile);
7067  }
7068 
7069  if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
7070  cmd = 0;
7071  return cmd;
7072 }
7073 
7074 static void queue_mwi_event(const char *box, int urgent, int new, int old)
7075 {
7076  struct ast_event *event;
7077  char *mailbox, *context;
7078 
7079  /* Strip off @default */
7080  context = mailbox = ast_strdupa(box);
7081  strsep(&context, "@");
7082  if (ast_strlen_zero(context))
7083  context = "default";
7084 
7085  if (!(event = ast_event_new(AST_EVENT_MWI,
7090  AST_EVENT_IE_END))) {
7091  return;
7092  }
7093 
7095 }
7096 
7097 /*!
7098  * \brief Sends email notification that a user has a new voicemail waiting for them.
7099  * \param chan
7100  * \param vmu
7101  * \param vms
7102  * \param msgnum
7103  * \param duration
7104  * \param fmt
7105  * \param cidnum The Caller ID phone number value.
7106  * \param cidname The Caller ID name value.
7107  * \param flag
7108  *
7109  * \return zero on success, -1 on error.
7110  */
7111 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)
7112 {
7113  char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
7114  int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
7115  const char *category;
7116  char *myserveremail = serveremail;
7117 
7118  ast_channel_lock(chan);
7119  if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
7120  category = ast_strdupa(category);
7121  }
7122  ast_channel_unlock(chan);
7123 
7124 #ifndef IMAP_STORAGE
7125  make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
7126 #else
7127  snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
7128 #endif
7129  make_file(fn, sizeof(fn), todir, msgnum);
7130  snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
7131 
7132  if (!ast_strlen_zero(vmu->attachfmt)) {
7133  if (strstr(fmt, vmu->attachfmt))
7134  fmt = vmu->attachfmt;
7135  else
7136  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);
7137  }
7138 
7139  /* Attach only the first format */
7140  fmt = ast_strdupa(fmt);
7141  stringp = fmt;
7142  strsep(&stringp, "|");
7143 
7144  if (!ast_strlen_zero(vmu->serveremail))
7145  myserveremail = vmu->serveremail;
7146 
7147  if (!ast_strlen_zero(vmu->email)) {
7148  int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
7149 
7150  if (attach_user_voicemail)
7151  RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
7152 
7153  /* XXX possible imap issue, should category be NULL XXX */
7154  sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
7155 
7156  if (attach_user_voicemail)
7157  DISPOSE(todir, msgnum);
7158  }
7159 
7160  if (!ast_strlen_zero(vmu->pager)) {
7161  sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
7162  }
7163 
7164  if (ast_test_flag(vmu, VM_DELETE))
7165  DELETE(todir, msgnum, fn, vmu);
7166 
7167  /* Leave voicemail for someone */
7168  if (ast_app_has_voicemail(ext_context, NULL))
7169  ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
7170 
7171  queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
7172 
7173  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);
7174  run_externnotify(vmu->context, vmu->mailbox, flag);
7175 
7176 #ifdef IMAP_STORAGE
7177  vm_delete(fn); /* Delete the file, but not the IMAP message */
7178  if (ast_test_flag(vmu, VM_DELETE)) { /* Delete the IMAP message if delete = yes */
7179  vm_imap_delete(NULL, vms->curmsg, vmu);
7180  vms->newmessages--; /* Fix new message count */
7181  }
7182 #endif
7183 
7184  return 0;
7185 }
7186 
7187 /*!
7188  * \brief Sends a voicemail message to a mailbox recipient.
7189  * \param chan
7190  * \param context
7191  * \param vms
7192  * \param sender
7193  * \param fmt
7194  * \param is_new_message Used to indicate the mode for which this method was invoked.
7195  * Will be 0 when called to forward an existing message (option 8)
7196  * Will be 1 when called to leave a message (option 3->5)
7197  * \param record_gain
7198  * \param urgent
7199  *
7200  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
7201  *
7202  * When in the leave message mode (is_new_message == 1):
7203  * - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
7204  * - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
7205  *
7206  * When in the forward message mode (is_new_message == 0):
7207  * - retreives the current message to be forwarded
7208  * - copies the original message to a temporary file, so updates to the envelope can be done.
7209  * - determines the target mailbox and folders
7210  * - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
7211  *
7212  * \return zero on success, -1 on error.
7213  */
7214 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)
7215 {
7216 #ifdef IMAP_STORAGE
7217  int todircount = 0;
7218  struct vm_state *dstvms;
7219 #endif
7220  char username[70]="";
7221  char fn[PATH_MAX]; /* for playback of name greeting */
7222  char ecodes[16] = "#";
7223  int res = 0, cmd = 0;
7224  struct ast_vm_user *receiver = NULL, *vmtmp;
7226  char *stringp;
7227  const char *s;
7228  int saved_messages = 0;
7229  int valid_extensions = 0;
7230  char *dir;
7231  int curmsg;
7232  char urgent_str[7] = "";
7233  int prompt_played = 0;
7234 #ifndef IMAP_STORAGE
7235  char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
7236 #endif
7237  if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
7238  ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
7239  }
7240 
7241  if (vms == NULL) return -1;
7242  dir = vms->curdir;
7243  curmsg = vms->curmsg;
7244 
7245  ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
7246  while (!res && !valid_extensions) {
7247  int use_directory = 0;
7248  if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
7249  int done = 0;
7250  int retries = 0;
7251  cmd = 0;
7252  while ((cmd >= 0) && !done ){
7253  if (cmd)
7254  retries = 0;
7255  switch (cmd) {
7256  case '1':
7257  use_directory = 0;
7258  done = 1;
7259  break;
7260  case '2':
7261  use_directory = 1;
7262  done = 1;
7263  break;
7264  case '*':
7265  cmd = 't';
7266  done = 1;
7267  break;
7268  default:
7269  /* Press 1 to enter an extension press 2 to use the directory */
7270  cmd = ast_play_and_wait(chan, "vm-forward");
7271  if (!cmd) {
7272  cmd = ast_waitfordigit(chan, 3000);
7273  }
7274  if (!cmd) {
7275  retries++;
7276  }
7277  if (retries > 3) {
7278  cmd = 't';
7279  done = 1;
7280  }
7281  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
7282  }
7283  }
7284  if (cmd < 0 || cmd == 't')
7285  break;
7286  }
7287 
7288  if (use_directory) {
7289  /* use app_directory */
7290 
7291  char old_context[sizeof(chan->context)];
7292  char old_exten[sizeof(chan->exten)];
7293  int old_priority;
7294  struct ast_app* directory_app;
7295 
7296  directory_app = pbx_findapp("Directory");
7297  if (directory_app) {
7298  char vmcontext[256];
7299  /* make backup copies */
7300  memcpy(old_context, chan->context, sizeof(chan->context));
7301  memcpy(old_exten, chan->exten, sizeof(chan->exten));
7302  old_priority = chan->priority;
7303 
7304  /* call the the Directory, changes the channel */
7305  snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
7306  res = pbx_exec(chan, directory_app, vmcontext);
7307 
7308  ast_copy_string(username, chan->exten, sizeof(username));
7309 
7310  /* restore the old context, exten, and priority */
7311  memcpy(chan->context, old_context, sizeof(chan->context));
7312  memcpy(chan->exten, old_exten, sizeof(chan->exten));
7313  chan->priority = old_priority;
7314  } else {
7315  ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
7316  ast_clear_flag((&globalflags), VM_DIRECFORWARD);
7317  }
7318  } else {
7319  /* Ask for an extension */
7320  res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
7321  prompt_played++;
7322  if (res || prompt_played > 4)
7323  break;
7324  if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
7325  break;
7326  }
7327 
7328  /* start all over if no username */
7329  if (ast_strlen_zero(username))
7330  continue;
7331  stringp = username;
7332  s = strsep(&stringp, "*");
7333  /* start optimistic */
7334  valid_extensions = 1;
7335  while (s) {
7336  if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
7337  int oldmsgs;
7338  int newmsgs;
7339  int capacity;
7340  if (inboxcount(s, &newmsgs, &oldmsgs)) {
7341  ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
7342  /* Shouldn't happen, but allow trying another extension if it does */
7343  res = ast_play_and_wait(chan, "pbx-invalid");
7344  valid_extensions = 0;
7345  break;
7346  }
7347  capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
7348  if ((newmsgs + oldmsgs) >= capacity) {
7349  ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
7350  res = ast_play_and_wait(chan, "vm-mailboxfull");
7351  valid_extensions = 0;
7352  while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
7353  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
7354  free_user(vmtmp);
7355  }
7356  inprocess_count(receiver->mailbox, receiver->context, -1);
7357  break;
7358  }
7359  AST_LIST_INSERT_HEAD(&extensions, receiver, list);
7360  } else {
7361  /* XXX Optimization for the future. When we encounter a single bad extension,
7362  * bailing out on all of the extensions may not be the way to go. We should
7363  * probably just bail on that single extension, then allow the user to enter
7364  * several more. XXX
7365  */
7366  while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
7367  free_user(receiver);
7368  }
7369  ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
7370  /* "I am sorry, that's not a valid extension. Please try again." */
7371  res = ast_play_and_wait(chan, "pbx-invalid");
7372  valid_extensions = 0;
7373  break;
7374  }
7375 
7376  /* play name if available, else play extension number */
7377  snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
7378  RETRIEVE(fn, -1, s, receiver->context);
7379  if (ast_fileexists(fn, NULL, NULL) > 0) {
7380  res = ast_stream_and_wait(chan, fn, ecodes);
7381  if (res) {
7382  DISPOSE(fn, -1);
7383  return res;
7384  }
7385  } else {
7386  res = ast_say_digit_str(chan, s, ecodes, chan->language);
7387  }
7388  DISPOSE(fn, -1);
7389 
7390  s = strsep(&stringp, "*");
7391  }
7392  /* break from the loop of reading the extensions */
7393  if (valid_extensions)
7394  break;
7395  }
7396  /* check if we're clear to proceed */
7397  if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
7398  return res;
7399  if (is_new_message == 1) {
7400  struct leave_vm_options leave_options;
7401  char mailbox[AST_MAX_EXTENSION * 2 + 2];
7402  snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
7403 
7404  /* Send VoiceMail */
7405  memset(&leave_options, 0, sizeof(leave_options));
7406  leave_options.record_gain = record_gain;
7407  cmd = leave_voicemail(chan, mailbox, &leave_options);
7408  } else {
7409  /* Forward VoiceMail */
7410  long duration = 0;
7411  struct vm_state vmstmp;
7412  int copy_msg_result = 0;
7413  memcpy(&vmstmp, vms, sizeof(vmstmp));
7414 
7415  RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
7416 
7417  cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
7418  if (!cmd) {
7419  AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
7420 #ifdef IMAP_STORAGE
7421  int attach_user_voicemail;
7422  char *myserveremail = serveremail;
7423 
7424  /* get destination mailbox */
7425  dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
7426  if (!dstvms) {
7427  dstvms = create_vm_state_from_user(vmtmp);
7428  }
7429  if (dstvms) {
7430  init_mailstream(dstvms, 0);
7431  if (!dstvms->mailstream) {
7432  ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
7433  } else {
7434  copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
7435  run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str);
7436  }
7437  } else {
7438  ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
7439  }
7440  if (!ast_strlen_zero(vmtmp->serveremail))
7441  myserveremail = vmtmp->serveremail;
7442  attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
7443  /* NULL category for IMAP storage */
7444  sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
7445  dstvms->curbox,
7446  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
7447  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
7448  vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
7449  NULL, urgent_str);
7450 #else
7451  copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
7452 #endif
7453  saved_messages++;
7455  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
7456  free_user(vmtmp);
7457  if (res)
7458  break;
7459  }
7461  if (saved_messages > 0 && !copy_msg_result) {
7462  /* give confirmation that the message was saved */
7463  /* commented out since we can't forward batches yet
7464  if (saved_messages == 1)
7465  res = ast_play_and_wait(chan, "vm-message");
7466  else
7467  res = ast_play_and_wait(chan, "vm-messages");
7468  if (!res)
7469  res = ast_play_and_wait(chan, "vm-saved"); */
7470 #ifdef IMAP_STORAGE
7471  /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
7472  if (ast_strlen_zero(vmstmp.introfn))
7473 #endif
7474  res = ast_play_and_wait(chan, "vm-msgsaved");
7475  }
7476 #ifndef IMAP_STORAGE
7477  else {
7478  /* with IMAP, mailbox full warning played by imap_check_limits */
7479  res = ast_play_and_wait(chan, "vm-mailboxfull");
7480  }
7481  /* Restore original message without prepended message if backup exists */
7482  make_file(msgfile, sizeof(msgfile), dir, curmsg);
7483  strcpy(textfile, msgfile);
7484  strcpy(backup, msgfile);
7485  strcpy(backup_textfile, msgfile);
7486  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
7487  strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
7488  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
7489  if (ast_fileexists(backup, NULL, NULL) > 0) {
7490  ast_filerename(backup, msgfile, NULL);
7491  rename(backup_textfile, textfile);
7492  }
7493 #endif
7494  }
7495  DISPOSE(dir, curmsg);
7496 #ifndef IMAP_STORAGE
7497  if (cmd) { /* assuming hangup, cleanup backup file */
7498  make_file(msgfile, sizeof(msgfile), dir, curmsg);
7499  strcpy(textfile, msgfile);
7500  strcpy(backup_textfile, msgfile);
7501  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
7502  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
7503  rename(backup_textfile, textfile);
7504  }
7505 #endif
7506  }
7507 
7508  /* If anything failed above, we still have this list to free */
7509  while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
7510  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
7511  free_user(vmtmp);
7512  }
7513  return res ? res : cmd;
7514 }
7515 
7516 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
7517 {
7518  int res;
7519  if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
7520  ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file);
7521  return res;
7522 }
7523 
7524 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
7525 {
7526  ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
7527  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);
7528 }
7529 
7530 static int play_message_category(struct ast_channel *chan, const char *category)
7531 {
7532  int res = 0;
7533 
7534  if (!ast_strlen_zero(category))
7535  res = ast_play_and_wait(chan, category);
7536 
7537  if (res) {
7538  ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
7539  res = 0;
7540  }
7541 
7542  return res;
7543 }
7544 
7545 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
7546 {
7547  int res = 0;
7548  struct vm_zone *the_zone = NULL;
7549  time_t t;
7550 
7551  if (ast_get_time_t(origtime, &t, 0, NULL)) {
7552  ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
7553  return 0;
7554  }
7555 
7556  /* Does this user have a timezone specified? */
7557  if (!ast_strlen_zero(vmu->zonetag)) {
7558  /* Find the zone in the list */
7559  struct vm_zone *z;
7560  AST_LIST_LOCK(&zones);
7561  AST_LIST_TRAVERSE(&zones, z, list) {
7562  if (!strcmp(z->name, vmu->zonetag)) {
7563  the_zone = z;
7564  break;
7565  }
7566  }
7568  }
7569 
7570 /* No internal variable parsing for now, so we'll comment it out for the time being */
7571 #if 0
7572  /* Set the DIFF_* variables */
7573  ast_localtime(&t, &time_now, NULL);
7574  tv_now = ast_tvnow();
7575  ast_localtime(&tv_now, &time_then, NULL);
7576 
7577  /* Day difference */
7578  if (time_now.tm_year == time_then.tm_year)
7579  snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
7580  else
7581  snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
7582  pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
7583 
7584  /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
7585 #endif
7586  if (the_zone) {
7587  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
7588  } else if (!strncasecmp(chan->language, "de", 2)) { /* GERMAN 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, "gr", 2)) { /* GREEK syntax */
7591  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
7592  } else if (!strncasecmp(chan->language, "it", 2)) { /* ITALIAN syntax */
7593  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);
7594  } else if (!strncasecmp(chan->language, "nl", 2)) { /* DUTCH syntax */
7595  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
7596  } else if (!strncasecmp(chan->language, "no", 2)) { /* NORWEGIAN syntax */
7597  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
7598  } else if (!strncasecmp(chan->language, "pl", 2)) { /* POLISH syntax */
7599  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
7600  } else if (!strncasecmp(chan->language, "pt_BR", 5)) { /* Brazillian PORTUGUESE syntax */
7601  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);
7602  } else if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
7603  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
7604  } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
7605  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
7606  } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
7607  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);
7608  } else {
7609  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
7610  }
7611 #if 0
7612  pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
7613 #endif
7614  return res;
7615 }
7616 
7617 
7618 
7619 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
7620 {
7621  int res = 0;
7622  int i;
7623  char *callerid, *name;
7624  char prefile[PATH_MAX] = "";
7625 
7626 
7627  /* If voicemail cid is not enabled, or we didn't get cid or context from
7628  * the attribute file, leave now.
7629  *
7630  * TODO Still need to change this so that if this function is called by the
7631  * message envelope (and someone is explicitly requesting to hear the CID),
7632  * it does not check to see if CID is enabled in the config file.
7633  */
7634  if ((cid == NULL)||(context == NULL))
7635  return res;
7636 
7637  /* Strip off caller ID number from name */
7638  ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
7639  ast_callerid_parse(cid, &name, &callerid);
7640  if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
7641  /* Check for internal contexts and only */
7642  /* say extension when the call didn't come from an internal context in the list */
7643  for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
7644  ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
7645  if ((strcmp(cidinternalcontexts[i], context) == 0))
7646  break;
7647  }
7648  if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
7649  if (!res) {
7650  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
7651  if (!ast_strlen_zero(prefile)) {
7652  /* See if we can find a recorded name for this person instead of their extension number */
7653  if (ast_fileexists(prefile, NULL, NULL) > 0) {
7654  ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
7655  if (!callback)
7656  res = wait_file2(chan, vms, "vm-from");
7657  res = ast_stream_and_wait(chan, prefile, "");
7658  } else {
7659  ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
7660  /* Say "from extension" as one saying to sound smoother */
7661  if (!callback)
7662  res = wait_file2(chan, vms, "vm-from-extension");
7663  res = ast_say_digit_str(chan, callerid, "", chan->language);
7664  }
7665  }
7666  }
7667  } else if (!res) {
7668  ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
7669  /* Since this is all nicely figured out, why not say "from phone number" in this case? */
7670  if (!callback)
7671  res = wait_file2(chan, vms, "vm-from-phonenumber");
7672  res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
7673  }
7674  } else {
7675  /* Number unknown */
7676  ast_debug(1, "VM-CID: From an unknown number\n");
7677  /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
7678  res = wait_file2(chan, vms, "vm-unknown-caller");
7679  }
7680  return res;
7681 }
7682 
7683 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
7684 {
7685  int res = 0;
7686  int durationm;
7687  int durations;
7688  /* Verify that we have a duration for the message */
7689  if (duration == NULL)
7690  return res;
7691 
7692  /* Convert from seconds to minutes */
7693  durations = atoi(duration);
7694  durationm = (durations / 60);
7695 
7696  ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
7697 
7698  if ((!res) && (durationm >= minduration)) {
7699  res = wait_file2(chan, vms, "vm-duration");
7700 
7701  /* POLISH syntax */
7702  if (!strncasecmp(chan->language, "pl", 2)) {
7703  div_t num = div(durationm, 10);
7704 
7705  if (durationm == 1) {
7706  res = ast_play_and_wait(chan, "digits/1z");
7707  res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
7708  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
7709  if (num.rem == 2) {
7710  if (!num.quot) {
7711  res = ast_play_and_wait(chan, "digits/2-ie");
7712  } else {
7713  res = say_and_wait(chan, durationm - 2 , chan->language);
7714  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
7715  }
7716  } else {
7717  res = say_and_wait(chan, durationm, chan->language);
7718  }
7719  res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
7720  } else {
7721  res = say_and_wait(chan, durationm, chan->language);
7722  res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
7723  }
7724  /* DEFAULT syntax */
7725  } else {
7726  res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
7727  res = wait_file2(chan, vms, "vm-minutes");
7728  }
7729  }
7730  return res;
7731 }
7732 
7733 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
7734 {
7735  int res = 0;
7736  char filename[256], *cid;
7737  const char *origtime, *context, *category, *duration, *flag;
7738  struct ast_config *msg_cfg;
7739  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
7740 
7741  vms->starting = 0;
7742  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
7743  adsi_message(chan, vms);
7744  if (!vms->curmsg) {
7745  res = wait_file2(chan, vms, "vm-first"); /* "First" */
7746  } else if (vms->curmsg == vms->lastmsg) {
7747  res = wait_file2(chan, vms, "vm-last"); /* "last" */
7748  }
7749 
7750  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
7751  RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
7752  msg_cfg = ast_config_load(filename, config_flags);
7753  if (!valid_config(msg_cfg)) {
7754  ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
7755  return 0;
7756  }
7757  flag = ast_variable_retrieve(msg_cfg, "message", "flag");
7758 
7759  /* Play the word urgent if we are listening to urgent messages */
7760  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
7761  res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
7762  }
7763 
7764  if (!res) {
7765  /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
7766  /* POLISH syntax */
7767  if (!strncasecmp(chan->language, "pl", 2)) {
7768  if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
7769  int ten, one;
7770  char nextmsg[256];
7771  ten = (vms->curmsg + 1) / 10;
7772  one = (vms->curmsg + 1) % 10;
7773 
7774  if (vms->curmsg < 20) {
7775  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
7776  res = wait_file2(chan, vms, nextmsg);
7777  } else {
7778  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
7779  res = wait_file2(chan, vms, nextmsg);
7780  if (one > 0) {
7781  if (!res) {
7782  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
7783  res = wait_file2(chan, vms, nextmsg);
7784  }
7785  }
7786  }
7787  }
7788  if (!res)
7789  res = wait_file2(chan, vms, "vm-message");
7790  /* HEBREW syntax */
7791  } else if (!strncasecmp(chan->language, "he", 2)) {
7792  if (!vms->curmsg) {
7793  res = wait_file2(chan, vms, "vm-message");
7794  res = wait_file2(chan, vms, "vm-first");
7795  } else if (vms->curmsg == vms->lastmsg) {
7796  res = wait_file2(chan, vms, "vm-message");
7797  res = wait_file2(chan, vms, "vm-last");
7798  } else {
7799  res = wait_file2(chan, vms, "vm-message");
7800  res = wait_file2(chan, vms, "vm-number");
7801  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
7802  }
7803  /* VIETNAMESE syntax */
7804  } else if (!strncasecmp(chan->language, "vi", 2)) {
7805  if (!vms->curmsg) {
7806  res = wait_file2(chan, vms, "vm-message");
7807  res = wait_file2(chan, vms, "vm-first");
7808  } else if (vms->curmsg == vms->lastmsg) {
7809  res = wait_file2(chan, vms, "vm-message");
7810  res = wait_file2(chan, vms, "vm-last");
7811  } else {
7812  res = wait_file2(chan, vms, "vm-message");
7813  res = wait_file2(chan, vms, "vm-number");
7814  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
7815  }
7816  } else {
7817  if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
7818  res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
7819  } else { /* DEFAULT syntax */
7820  res = wait_file2(chan, vms, "vm-message");
7821  }
7822  if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
7823  if (!res) {
7824  ast_test_suite_event_notify("PLAYBACK", "Message: message number");
7825  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
7826  }
7827  }
7828  }
7829  }
7830 
7831  if (!valid_config(msg_cfg)) {
7832  ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
7833  return 0;
7834  }
7835 
7836  if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
7837  ast_log(AST_LOG_WARNING, "No origtime?!\n");
7838  DISPOSE(vms->curdir, vms->curmsg);
7839  ast_config_destroy(msg_cfg);
7840  return 0;
7841  }
7842 
7843  cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
7844  duration = ast_variable_retrieve(msg_cfg, "message", "duration");
7845  category = ast_variable_retrieve(msg_cfg, "message", "category");
7846 
7847  context = ast_variable_retrieve(msg_cfg, "message", "context");
7848  if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
7849  context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
7850  if (!res) {
7851  res = play_message_category(chan, category);
7852  }
7853  if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
7854  res = play_message_datetime(chan, vmu, origtime, filename);
7855  }
7856  if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
7857  res = play_message_callerid(chan, vms, cid, context, 0);
7858  }
7859  if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
7860  res = play_message_duration(chan, vms, duration, vmu->saydurationm);
7861  }
7862  /* Allow pressing '1' to skip envelope / callerid */
7863  if (res == '1') {
7864  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
7865  res = 0;
7866  }
7867  ast_config_destroy(msg_cfg);
7868 
7869  if (!res) {
7870  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
7871 #ifdef IMAP_STORAGE
7872  ast_mutex_lock(&vms->lock);
7873 #endif
7874  vms->heard[vms->curmsg] = 1;
7875 #ifdef IMAP_STORAGE
7876  ast_mutex_unlock(&vms->lock);
7877  /*IMAP storage stores any prepended message from a forward
7878  * as a separate file from the rest of the message
7879  */
7880  if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
7881  wait_file(chan, vms, vms->introfn);
7882  }
7883 #endif
7884  if ((res = wait_file(chan, vms, vms->fn)) < 0) {
7885  ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
7886  res = 0;
7887  }
7888  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
7889  }
7890  DISPOSE(vms->curdir, vms->curmsg);
7891  return res;
7892 }
7893 
7894 #ifdef IMAP_STORAGE
7895 static int imap_remove_file(char *dir, int msgnum)
7896 {
7897  char fn[PATH_MAX];
7898  char full_fn[PATH_MAX];
7899  char intro[PATH_MAX] = {0,};
7900 
7901  if (msgnum > -1) {
7902  make_file(fn, sizeof(fn), dir, msgnum);
7903  snprintf(intro, sizeof(intro), "%sintro", fn);
7904  } else
7905  ast_copy_string(fn, dir, sizeof(fn));
7906 
7907  if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
7908  ast_filedelete(fn, NULL);
7909  if (!ast_strlen_zero(intro)) {
7910  ast_filedelete(intro, NULL);
7911  }
7912  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
7913  unlink(full_fn);
7914  }
7915  return 0;
7916 }
7917 
7918 
7919 
7920 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
7921 {
7922  char *file, *filename;
7923  char *attachment;
7924  char arg[10];
7925  int i;
7926  BODY* body;
7927 
7928  file = strrchr(ast_strdupa(dir), '/');
7929  if (file) {
7930  *file++ = '\0';
7931  } else {
7932  ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
7933  return -1;
7934  }
7935 
7936  ast_mutex_lock(&vms->lock);
7937  for (i = 0; i < vms->mailstream->nmsgs; i++) {
7938  mail_fetchstructure(vms->mailstream, i + 1, &body);
7939  /* We have the body, now we extract the file name of the first attachment. */
7940  if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
7941  attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
7942  } else {
7943  ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
7944  ast_mutex_unlock(&vms->lock);
7945  return -1;
7946  }
7947  filename = strsep(&attachment, ".");
7948  if (!strcmp(filename, file)) {
7949  sprintf(arg, "%d", i + 1);
7950  mail_setflag(vms->mailstream, arg, "\\DELETED");
7951  }
7952  }
7953  mail_expunge(vms->mailstream);
7954  ast_mutex_unlock(&vms->lock);
7955  return 0;
7956 }
7957 
7958 #elif !defined(IMAP_STORAGE)
7959 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
7960 {
7961  int count_msg, last_msg;
7962 
7963  ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
7964 
7965  /* Rename the member vmbox HERE so that we don't try to return before
7966  * we know what's going on.
7967  */
7968  snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
7969 
7970  /* Faster to make the directory than to check if it exists. */
7971  create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
7972 
7973  /* traverses directory using readdir (or select query for ODBC) */
7974  count_msg = count_messages(vmu, vms->curdir);
7975  if (count_msg < 0) {
7976  return count_msg;
7977  } else {
7978  vms->lastmsg = count_msg - 1;
7979  }
7980 
7981  if (vm_allocate_dh(vms, vmu, count_msg)) {
7982  return -1;
7983  }
7984 
7985  /*
7986  The following test is needed in case sequencing gets messed up.
7987  There appears to be more than one way to mess up sequence, so
7988  we will not try to find all of the root causes--just fix it when
7989  detected.
7990  */
7991 
7992  if (vm_lock_path(vms->curdir)) {
7993  ast_log(AST_LOG_ERROR, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
7994  return ERROR_LOCK_PATH;
7995  }
7996 
7997  /* for local storage, checks directory for messages up to maxmsg limit */
7998  last_msg = last_message_index(vmu, vms->curdir);
7999  ast_unlock_path(vms->curdir);
8000 
8001  if (last_msg < -1) {
8002  return last_msg;
8003  } else if (vms->lastmsg != last_msg) {
8004  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);
8005  resequence_mailbox(vmu, vms->curdir, count_msg);
8006  }
8007 
8008  return 0;
8009 }
8010 #endif
8011 
8012 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
8013 {
8014  int x = 0;
8015  int last_msg_idx = 0;
8016 
8017 #ifndef IMAP_STORAGE
8018  int res = 0, nummsg;
8019  char fn2[PATH_MAX];
8020 #endif
8021 
8022  if (vms->lastmsg <= -1) {
8023  goto done;
8024  }
8025 
8026  vms->curmsg = -1;
8027 #ifndef IMAP_STORAGE
8028  /* Get the deleted messages fixed */
8029  if (vm_lock_path(vms->curdir)) {
8030  return ERROR_LOCK_PATH;
8031  }
8032 
8033  /* update count as message may have arrived while we've got mailbox open */
8034  last_msg_idx = last_message_index(vmu, vms->curdir);
8035  if (last_msg_idx != vms->lastmsg) {
8036  ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
8037  }
8038 
8039  /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
8040  for (x = 0; x < last_msg_idx + 1; x++) {
8041  if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
8042  /* Save this message. It's not in INBOX or hasn't been heard */
8043  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
8044  if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
8045  break;
8046  }
8047  vms->curmsg++;
8048  make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
8049  if (strcmp(vms->fn, fn2)) {
8050  RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
8051  }
8052  } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
8053  /* Move to old folder before deleting */
8054  res = save_to_folder(vmu, vms, x, 1);
8055  if (res == ERROR_LOCK_PATH) {
8056  /* If save failed do not delete the message */
8057  ast_log(AST_LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
8058  vms->deleted[x] = 0;
8059  vms->heard[x] = 0;
8060  --x;
8061  }
8062  } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
8063  /* Move to deleted folder */
8064  res = save_to_folder(vmu, vms, x, 10);
8065  if (res == ERROR_LOCK_PATH) {
8066  /* If save failed do not delete the message */
8067  vms->deleted[x] = 0;
8068  vms->heard[x] = 0;
8069  --x;
8070  }
8071  } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
8072  /* If realtime storage enabled - we should explicitly delete this message,
8073  cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
8074  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
8075  if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
8076  DELETE(vms->curdir, x, vms->fn, vmu);
8077  }
8078  }
8079  }
8080 
8081  /* Delete ALL remaining messages */
8082  nummsg = x - 1;
8083  for (x = vms->curmsg + 1; x <= nummsg; x++) {
8084  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
8085  if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
8086  DELETE(vms->curdir, x, vms->fn, vmu);
8087  }
8088  }
8089  ast_unlock_path(vms->curdir);
8090 #else /* defined(IMAP_STORAGE) */
8091  ast_mutex_lock(&vms->lock);
8092  if (vms->deleted) {
8093  /* Since we now expunge after each delete, deleting in reverse order
8094  * ensures that no reordering occurs between each step. */
8095  last_msg_idx = vms->dh_arraysize;
8096  for (x = last_msg_idx - 1; x >= 0; x--) {
8097  if (vms->deleted[x]) {
8098  ast_debug(3, "IMAP delete of %d\n", x);
8099  DELETE(vms->curdir, x, vms->fn, vmu);
8100  }
8101  }
8102  }
8103 #endif
8104 
8105 done:
8106  if (vms->deleted) {
8107  ast_free(vms->deleted);
8108  vms->deleted = NULL;
8109  }
8110  if (vms->heard) {
8111  ast_free(vms->heard);
8112  vms->heard = NULL;
8113  }
8114  vms->dh_arraysize = 0;
8115 #ifdef IMAP_STORAGE
8116  ast_mutex_unlock(&vms->lock);
8117 #endif
8118 
8119  return 0;
8120 }
8121 
8122 /* In Greek even though we CAN use a syntax like "friends messages"
8123  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
8124  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
8125  * syntax for the above three categories which is more elegant.
8126  */
8127 
8128 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
8129 {
8130  int cmd;
8131  char *buf;
8132 
8133  buf = ast_alloca(strlen(box) + 2);
8134  strcpy(buf, box);
8135  strcat(buf, "s");
8136 
8137  if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
8138  cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
8139  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
8140  } else {
8141  cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
8142  return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
8143  }
8144 }
8145 
8146 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
8147 {
8148  int cmd;
8149 
8150  if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
8151  if (!strcasecmp(box, "vm-INBOX"))
8152  cmd = ast_play_and_wait(chan, "vm-new-e");
8153  else
8154  cmd = ast_play_and_wait(chan, "vm-old-e");
8155  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
8156  } else {
8157  cmd = ast_play_and_wait(chan, "vm-messages");
8158  return cmd ? cmd : ast_play_and_wait(chan, box);
8159  }
8160 }
8161 
8162 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
8163 {
8164  int cmd;
8165 
8166  if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
8167  cmd = ast_play_and_wait(chan, "vm-messages");
8168  return cmd ? cmd : ast_play_and_wait(chan, box);
8169  } else {
8170  cmd = ast_play_and_wait(chan, box);
8171  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
8172  }
8173 }
8174 
8175 static int vm_play_folder_name(struct ast_channel *chan, char *box)
8176 {
8177  int cmd;
8178 
8179  if ( !strncasecmp(chan->language, "it", 2) ||
8180  !strncasecmp(chan->language, "es", 2) ||
8181  !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
8182  cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
8183  return cmd ? cmd : ast_play_and_wait(chan, box);
8184  } else if (!strncasecmp(chan->language, "gr", 2)) {
8185  return vm_play_folder_name_gr(chan, box);
8186  } else if (!strncasecmp(chan->language, "he", 2)) { /* Hebrew syntax */
8187  return ast_play_and_wait(chan, box);
8188  } else if (!strncasecmp(chan->language, "pl", 2)) {
8189  return vm_play_folder_name_pl(chan, box);
8190  } else if (!strncasecmp(chan->language, "ua", 2)) { /* Ukrainian syntax */
8191  return vm_play_folder_name_ua(chan, box);
8192  } else if (!strncasecmp(chan->language, "vi", 2)) {
8193  return ast_play_and_wait(chan, box);
8194  } else { /* Default English */
8195  cmd = ast_play_and_wait(chan, box);
8196  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
8197  }
8198 }
8199 
8200 /* GREEK SYNTAX
8201  In greek the plural for old/new is
8202  different so we need the following files
8203  We also need vm-denExeteMynhmata because
8204  this syntax is different.
8205 
8206  -> vm-Olds.wav : "Palia"
8207  -> vm-INBOXs.wav : "Nea"
8208  -> vm-denExeteMynhmata : "den exete mynhmata"
8209 */
8210 
8211 
8212 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
8213 {
8214  int res = 0;
8215 
8216  if (vms->newmessages) {
8217  res = ast_play_and_wait(chan, "vm-youhave");
8218  if (!res)
8219  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
8220  if (!res) {
8221  if ((vms->newmessages == 1)) {
8222  res = ast_play_and_wait(chan, "vm-INBOX");
8223  if (!res)
8224  res = ast_play_and_wait(chan, "vm-message");
8225  } else {
8226  res = ast_play_and_wait(chan, "vm-INBOXs");
8227  if (!res)
8228  res = ast_play_and_wait(chan, "vm-messages");
8229  }
8230  }
8231  } else if (vms->oldmessages){
8232  res = ast_play_and_wait(chan, "vm-youhave");
8233  if (!res)
8234  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
8235  if ((vms->oldmessages == 1)){
8236  res = ast_play_and_wait(chan, "vm-Old");
8237  if (!res)
8238  res = ast_play_and_wait(chan, "vm-message");
8239  } else {
8240  res = ast_play_and_wait(chan, "vm-Olds");
8241  if (!res)
8242  res = ast_play_and_wait(chan, "vm-messages");
8243  }
8244  } else if (!vms->oldmessages && !vms->newmessages)
8245  res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
8246  return res;
8247 }
8248 
8249 /* Version of vm_intro() designed to work for many languages.
8250  *
8251  * It is hoped that this function can prevent the proliferation of
8252  * language-specific vm_intro() functions and in time replace the language-
8253  * specific functions which already exist. An examination of the language-
8254  * specific functions revealed that they all corrected the same deficiencies
8255  * in vm_intro_en() (which was the default function). Namely:
8256  *
8257  * 1) The vm-Old and vm-INBOX sound files were overloaded. The English
8258  * wording of the voicemail greeting hides this problem. For example,
8259  * vm-INBOX contains only the word "new". This means that both of these
8260  * sequences produce valid utterances:
8261  * * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
8262  * * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
8263  * However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
8264  * in many languages) the first utterance becomes "you have 1 the new message".
8265  * 2) The function contains hardcoded rules for pluralizing the word "message".
8266  * These rules are correct for English, but not for many other languages.
8267  * 3) No attempt is made to pluralize the adjectives ("old" and "new") as
8268  * required in many languages.
8269  * 4) The gender of the word for "message" is not specified. This is a problem
8270  * because in many languages the gender of the number in phrases such
8271  * as "you have one new message" must match the gender of the word
8272  * meaning "message".
8273  *
8274  * Fixing these problems for each new language has meant duplication of effort.
8275  * This new function solves the problems in the following general ways:
8276  * 1) Add new sound files vm-new and vm-old. These can be linked to vm-INBOX
8277  * and vm-Old respectively for those languages where it makes sense.
8278  * 2) Call ast_say_counted_noun() to put the proper gender and number prefix
8279  * on vm-message.
8280  * 3) Call ast_say_counted_adjective() to put the proper gender and number
8281  * prefix on vm-new and vm-old (none for English).
8282  * 4) Pass the gender of the language's word for "message" as an agument to
8283  * this function which is can in turn pass on to the functions which
8284  * say numbers and put endings on nounds and adjectives.
8285  *
8286  * All languages require these messages:
8287  * vm-youhave "You have..."
8288  * vm-and "and"
8289  * vm-no "no" (in the sense of "none", as in "you have no messages")
8290  *
8291  * To use it for English, you will need these additional sound files:
8292  * vm-new "new"
8293  * vm-message "message", singular
8294  * vm-messages "messages", plural
8295  *
8296  * If you use it for Russian and other slavic languages, you will need these additional sound files:
8297  *
8298  * vm-newn "novoye" (singular, neuter)
8299  * vm-newx "novikh" (counting plural form, genative plural)
8300  * vm-message "sobsheniye" (singular form)
8301  * vm-messagex1 "sobsheniya" (first counting plural form, genative singular)
8302  * vm-messagex2 "sobsheniy" (second counting plural form, genative plural)
8303  * digits/1n "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
8304  * digits/2n "dva" (neuter singular)
8305  */
8306 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
8307 {
8308  int res;
8309  int lastnum = 0;
8310 
8311  res = ast_play_and_wait(chan, "vm-youhave");
8312 
8313  if (!res && vms->newmessages) {
8314  lastnum = vms->newmessages;
8315 
8316  if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
8317  res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
8318  }
8319 
8320  if (!res && vms->oldmessages) {
8321  res = ast_play_and_wait(chan, "vm-and");
8322  }
8323  }
8324 
8325  if (!res && vms->oldmessages) {
8326  lastnum = vms->oldmessages;
8327 
8328  if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
8329  res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
8330  }
8331  }
8332 
8333  if (!res) {
8334  if (lastnum == 0) {
8335  res = ast_play_and_wait(chan, "vm-no");
8336  }
8337  if (!res) {
8338  res = ast_say_counted_noun(chan, lastnum, "vm-message");
8339  }
8340  }
8341 
8342  return res;
8343 }
8344 
8345 /* Default Hebrew syntax */
8346 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
8347 {
8348  int res = 0;
8349 
8350  /* Introduce messages they have */
8351  if (!res) {
8352  if ((vms->newmessages) || (vms->oldmessages)) {
8353  res = ast_play_and_wait(chan, "vm-youhave");
8354  }
8355  /*
8356  * The word "shtei" refers to the number 2 in hebrew when performing a count
8357  * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
8358  * an element, this is one of them.
8359  */
8360  if (vms->newmessages) {
8361  if (!res) {
8362  if (vms->newmessages == 1) {
8363  res = ast_play_and_wait(chan, "vm-INBOX1");
8364  } else {
8365  if (vms->newmessages == 2) {
8366  res = ast_play_and_wait(chan, "vm-shtei");
8367  } else {
8368  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
8369  }
8370  res = ast_play_and_wait(chan, "vm-INBOX");
8371  }
8372  }
8373  if (vms->oldmessages && !res) {
8374  res = ast_play_and_wait(chan, "vm-and");
8375  if (vms->oldmessages == 1) {
8376  res = ast_play_and_wait(chan, "vm-Old1");
8377  } else {
8378  if (vms->oldmessages == 2) {
8379  res = ast_play_and_wait(chan, "vm-shtei");
8380  } else {
8381  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
8382  }
8383  res = ast_play_and_wait(chan, "vm-Old");
8384  }
8385  }
8386  }
8387  if (!res && vms->oldmessages && !vms->newmessages) {
8388  if (!res) {
8389  if (vms->oldmessages == 1) {
8390  res = ast_play_and_wait(chan, "vm-Old1");
8391  } else {
8392  if (vms->oldmessages == 2) {
8393  res = ast_play_and_wait(chan, "vm-shtei");
8394  } else {
8395  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
8396  }
8397  res = ast_play_and_wait(chan, "vm-Old");
8398  }
8399  }
8400  }
8401  if (!res) {
8402  if (!vms->oldmessages && !vms->newmessages) {
8403  if (!res) {
8404  res = ast_play_and_wait(chan, "vm-nomessages");
8405  }
8406  }
8407  }
8408  }
8409  return res;
8410 }
8411 
8412 /* Default English syntax */
8413 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
8414 {
8415  int res;
8416 
8417  /* Introduce messages they have */
8418  res = ast_play_and_wait(chan, "vm-youhave");
8419  if (!res) {
8420  if (vms->urgentmessages) {
8421  res = say_and_wait(chan, vms->urgentmessages, chan->language);
8422  if (!res)
8423  res = ast_play_and_wait(chan, "vm-Urgent");
8424  if ((vms->oldmessages || vms->newmessages) && !res) {
8425  res = ast_play_and_wait(chan, "vm-and");
8426  } else if (!res) {
8427  if ((vms->urgentmessages == 1))
8428  res = ast_play_and_wait(chan, "vm-message");
8429  else
8430  res = ast_play_and_wait(chan, "vm-messages");
8431  }
8432  }
8433  if (vms->newmessages) {
8434  res = say_and_wait(chan, vms->newmessages, chan->language);
8435  if (!res)
8436  res = ast_play_and_wait(chan, "vm-INBOX");
8437  if (vms->oldmessages && !res)
8438  res = ast_play_and_wait(chan, "vm-and");
8439  else if (!res) {
8440  if ((vms->newmessages == 1))
8441  res = ast_play_and_wait(chan, "vm-message");
8442  else
8443  res = ast_play_and_wait(chan, "vm-messages");
8444  }
8445 
8446  }
8447  if (!res && vms->oldmessages) {
8448  res = say_and_wait(chan, vms->oldmessages, chan->language);
8449  if (!res)
8450  res = ast_play_and_wait(chan, "vm-Old");
8451  if (!res) {
8452  if (vms->oldmessages == 1)
8453  res = ast_play_and_wait(chan, "vm-message");
8454  else
8455  res = ast_play_and_wait(chan, "vm-messages");
8456  }
8457  }
8458  if (!res) {
8459  if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
8460  res = ast_play_and_wait(chan, "vm-no");
8461  if (!res)
8462  res = ast_play_and_wait(chan, "vm-messages");
8463  }
8464  }
8465  }
8466  return res;
8467 }
8468 
8469 /* ITALIAN syntax */
8470 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
8471 {
8472  /* Introduce messages they have */
8473  int res;
8474  if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
8475  res = ast_play_and_wait(chan, "vm-no") ||
8476  ast_play_and_wait(chan, "vm-message");
8477  else
8478  res = ast_play_and_wait(chan, "vm-youhave");
8479  if (!res && vms->newmessages) {
8480  res = (vms->newmessages == 1) ?
8481  ast_play_and_wait(chan, "digits/un") ||
8482  ast_play_and_wait(chan, "vm-nuovo") ||
8483  ast_play_and_wait(chan, "vm-message") :
8484  /* 2 or more new messages */
8485  say_and_wait(chan, vms->newmessages, chan->language) ||
8486  ast_play_and_wait(chan, "vm-nuovi") ||
8487  ast_play_and_wait(chan, "vm-messages");
8488  if (!res && vms->oldmessages)
8489  res = ast_play_and_wait(chan, "vm-and");
8490  }
8491  if (!res && vms->oldmessages) {
8492  res = (vms->oldmessages == 1) ?
8493  ast_play_and_wait(chan, "digits/un") ||
8494  ast_play_and_wait(chan, "vm-vecchio") ||
8495  ast_play_and_wait(chan, "vm-message") :
8496  /* 2 or more old messages */
8497  say_and_wait(chan, vms->oldmessages, chan->language) ||
8498  ast_play_and_wait(chan, "vm-vecchi") ||
8499  ast_play_and_wait(chan, "vm-messages");
8500  }
8501  return res;
8502 }
8503 
8504 /* POLISH syntax */
8505 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
8506 {
8507  /* Introduce messages they have */
8508  int res;
8509  div_t num;
8510 
8511  if (!vms->oldmessages && !vms->newmessages) {
8512  res = ast_play_and_wait(chan, "vm-no");
8513  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8514  return res;
8515  } else {
8516  res = ast_play_and_wait(chan, "vm-youhave");
8517  }
8518 
8519  if (vms->newmessages) {
8520  num = div(vms->newmessages, 10);
8521  if (vms->newmessages == 1) {
8522  res = ast_play_and_wait(chan, "digits/1-a");
8523  res = res ? res : ast_play_and_wait(chan, "vm-new-a");
8524  res = res ? res : ast_play_and_wait(chan, "vm-message");
8525  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
8526  if (num.rem == 2) {
8527  if (!num.quot) {
8528  res = ast_play_and_wait(chan, "digits/2-ie");
8529  } else {
8530  res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
8531  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
8532  }
8533  } else {
8534  res = say_and_wait(chan, vms->newmessages, chan->language);
8535  }
8536  res = res ? res : ast_play_and_wait(chan, "vm-new-e");
8537  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8538  } else {
8539  res = say_and_wait(chan, vms->newmessages, chan->language);
8540  res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
8541  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8542  }
8543  if (!res && vms->oldmessages)
8544  res = ast_play_and_wait(chan, "vm-and");
8545  }
8546  if (!res && vms->oldmessages) {
8547  num = div(vms->oldmessages, 10);
8548  if (vms->oldmessages == 1) {
8549  res = ast_play_and_wait(chan, "digits/1-a");
8550  res = res ? res : ast_play_and_wait(chan, "vm-old-a");
8551  res = res ? res : ast_play_and_wait(chan, "vm-message");
8552  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
8553  if (num.rem == 2) {
8554  if (!num.quot) {
8555  res = ast_play_and_wait(chan, "digits/2-ie");
8556  } else {
8557  res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
8558  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
8559  }
8560  } else {
8561  res = say_and_wait(chan, vms->oldmessages, chan->language);
8562  }
8563  res = res ? res : ast_play_and_wait(chan, "vm-old-e");
8564  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8565  } else {
8566  res = say_and_wait(chan, vms->oldmessages, chan->language);
8567  res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
8568  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8569  }
8570  }
8571 
8572  return res;
8573 }
8574 
8575 /* SWEDISH syntax */
8576 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
8577 {
8578  /* Introduce messages they have */
8579  int res;
8580 
8581  res = ast_play_and_wait(chan, "vm-youhave");
8582  if (res)
8583  return res;
8584 
8585  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8586  res = ast_play_and_wait(chan, "vm-no");
8587  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8588  return res;
8589  }
8590 
8591  if (vms->newmessages) {
8592  if ((vms->newmessages == 1)) {
8593  res = ast_play_and_wait(chan, "digits/ett");
8594  res = res ? res : ast_play_and_wait(chan, "vm-nytt");
8595  res = res ? res : ast_play_and_wait(chan, "vm-message");
8596  } else {
8597  res = say_and_wait(chan, vms->newmessages, chan->language);
8598  res = res ? res : ast_play_and_wait(chan, "vm-nya");
8599  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8600  }
8601  if (!res && vms->oldmessages)
8602  res = ast_play_and_wait(chan, "vm-and");
8603  }
8604  if (!res && vms->oldmessages) {
8605  if (vms->oldmessages == 1) {
8606  res = ast_play_and_wait(chan, "digits/ett");
8607  res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
8608  res = res ? res : ast_play_and_wait(chan, "vm-message");
8609  } else {
8610  res = say_and_wait(chan, vms->oldmessages, chan->language);
8611  res = res ? res : ast_play_and_wait(chan, "vm-gamla");
8612  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8613  }
8614  }
8615 
8616  return res;
8617 }
8618 
8619 /* NORWEGIAN syntax */
8620 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
8621 {
8622  /* Introduce messages they have */
8623  int res;
8624 
8625  res = ast_play_and_wait(chan, "vm-youhave");
8626  if (res)
8627  return res;
8628 
8629  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8630  res = ast_play_and_wait(chan, "vm-no");
8631  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8632  return res;
8633  }
8634 
8635  if (vms->newmessages) {
8636  if ((vms->newmessages == 1)) {
8637  res = ast_play_and_wait(chan, "digits/1");
8638  res = res ? res : ast_play_and_wait(chan, "vm-ny");
8639  res = res ? res : ast_play_and_wait(chan, "vm-message");
8640  } else {
8641  res = say_and_wait(chan, vms->newmessages, chan->language);
8642  res = res ? res : ast_play_and_wait(chan, "vm-nye");
8643  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8644  }
8645  if (!res && vms->oldmessages)
8646  res = ast_play_and_wait(chan, "vm-and");
8647  }
8648  if (!res && vms->oldmessages) {
8649  if (vms->oldmessages == 1) {
8650  res = ast_play_and_wait(chan, "digits/1");
8651  res = res ? res : ast_play_and_wait(chan, "vm-gamel");
8652  res = res ? res : ast_play_and_wait(chan, "vm-message");
8653  } else {
8654  res = say_and_wait(chan, vms->oldmessages, chan->language);
8655  res = res ? res : ast_play_and_wait(chan, "vm-gamle");
8656  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8657  }
8658  }
8659 
8660  return res;
8661 }
8662 
8663 /* GERMAN syntax */
8664 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
8665 {
8666  /* Introduce messages they have */
8667  int res;
8668  res = ast_play_and_wait(chan, "vm-youhave");
8669  if (!res) {
8670  if (vms->newmessages) {
8671  if ((vms->newmessages == 1))
8672  res = ast_play_and_wait(chan, "digits/1F");
8673  else
8674  res = say_and_wait(chan, vms->newmessages, chan->language);
8675  if (!res)
8676  res = ast_play_and_wait(chan, "vm-INBOX");
8677  if (vms->oldmessages && !res)
8678  res = ast_play_and_wait(chan, "vm-and");
8679  else if (!res) {
8680  if ((vms->newmessages == 1))
8681  res = ast_play_and_wait(chan, "vm-message");
8682  else
8683  res = ast_play_and_wait(chan, "vm-messages");
8684  }
8685 
8686  }
8687  if (!res && vms->oldmessages) {
8688  if (vms->oldmessages == 1)
8689  res = ast_play_and_wait(chan, "digits/1F");
8690  else
8691  res = say_and_wait(chan, vms->oldmessages, chan->language);
8692  if (!res)
8693  res = ast_play_and_wait(chan, "vm-Old");
8694  if (!res) {
8695  if (vms->oldmessages == 1)
8696  res = ast_play_and_wait(chan, "vm-message");
8697  else
8698  res = ast_play_and_wait(chan, "vm-messages");
8699  }
8700  }
8701  if (!res) {
8702  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8703  res = ast_play_and_wait(chan, "vm-no");
8704  if (!res)
8705  res = ast_play_and_wait(chan, "vm-messages");
8706  }
8707  }
8708  }
8709  return res;
8710 }
8711 
8712 /* SPANISH syntax */
8713 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
8714 {
8715  /* Introduce messages they have */
8716  int res;
8717  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8718  res = ast_play_and_wait(chan, "vm-youhaveno");
8719  if (!res)
8720  res = ast_play_and_wait(chan, "vm-messages");
8721  } else {
8722  res = ast_play_and_wait(chan, "vm-youhave");
8723  }
8724  if (!res) {
8725  if (vms->newmessages) {
8726  if (!res) {
8727  if ((vms->newmessages == 1)) {
8728  res = ast_play_and_wait(chan, "digits/1");
8729  if (!res)
8730  res = ast_play_and_wait(chan, "vm-message");
8731  if (!res)
8732  res = ast_play_and_wait(chan, "vm-INBOXs");
8733  } else {
8734  res = say_and_wait(chan, vms->newmessages, chan->language);
8735  if (!res)
8736  res = ast_play_and_wait(chan, "vm-messages");
8737  if (!res)
8738  res = ast_play_and_wait(chan, "vm-INBOX");
8739  }
8740  }
8741  if (vms->oldmessages && !res)
8742  res = ast_play_and_wait(chan, "vm-and");
8743  }
8744  if (vms->oldmessages) {
8745  if (!res) {
8746  if (vms->oldmessages == 1) {
8747  res = ast_play_and_wait(chan, "digits/1");
8748  if (!res)
8749  res = ast_play_and_wait(chan, "vm-message");
8750  if (!res)
8751  res = ast_play_and_wait(chan, "vm-Olds");
8752  } else {
8753  res = say_and_wait(chan, vms->oldmessages, chan->language);
8754  if (!res)
8755  res = ast_play_and_wait(chan, "vm-messages");
8756  if (!res)
8757  res = ast_play_and_wait(chan, "vm-Old");
8758  }
8759  }
8760  }
8761  }
8762 return res;
8763 }
8764 
8765 /* BRAZILIAN PORTUGUESE syntax */
8766 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
8767  /* Introduce messages they have */
8768  int res;
8769  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8770  res = ast_play_and_wait(chan, "vm-nomessages");
8771  return res;
8772  } else {
8773  res = ast_play_and_wait(chan, "vm-youhave");
8774  }
8775  if (vms->newmessages) {
8776  if (!res)
8777  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
8778  if ((vms->newmessages == 1)) {
8779  if (!res)
8780  res = ast_play_and_wait(chan, "vm-message");
8781  if (!res)
8782  res = ast_play_and_wait(chan, "vm-INBOXs");
8783  } else {
8784  if (!res)
8785  res = ast_play_and_wait(chan, "vm-messages");
8786  if (!res)
8787  res = ast_play_and_wait(chan, "vm-INBOX");
8788  }
8789  if (vms->oldmessages && !res)
8790  res = ast_play_and_wait(chan, "vm-and");
8791  }
8792  if (vms->oldmessages) {
8793  if (!res)
8794  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
8795  if (vms->oldmessages == 1) {
8796  if (!res)
8797  res = ast_play_and_wait(chan, "vm-message");
8798  if (!res)
8799  res = ast_play_and_wait(chan, "vm-Olds");
8800  } else {
8801  if (!res)
8802  res = ast_play_and_wait(chan, "vm-messages");
8803  if (!res)
8804  res = ast_play_and_wait(chan, "vm-Old");
8805  }
8806  }
8807  return res;
8808 }
8809 
8810 /* FRENCH syntax */
8811 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
8812 {
8813  /* Introduce messages they have */
8814  int res;
8815  res = ast_play_and_wait(chan, "vm-youhave");
8816  if (!res) {
8817  if (vms->newmessages) {
8818  res = say_and_wait(chan, vms->newmessages, chan->language);
8819  if (!res)
8820  res = ast_play_and_wait(chan, "vm-INBOX");
8821  if (vms->oldmessages && !res)
8822  res = ast_play_and_wait(chan, "vm-and");
8823  else if (!res) {
8824  if ((vms->newmessages == 1))
8825  res = ast_play_and_wait(chan, "vm-message");
8826  else
8827  res = ast_play_and_wait(chan, "vm-messages");
8828  }
8829 
8830  }
8831  if (!res && vms->oldmessages) {
8832  res = say_and_wait(chan, vms->oldmessages, chan->language);
8833  if (!res)
8834  res = ast_play_and_wait(chan, "vm-Old");
8835  if (!res) {
8836  if (vms->oldmessages == 1)
8837  res = ast_play_and_wait(chan, "vm-message");
8838  else
8839  res = ast_play_and_wait(chan, "vm-messages");
8840  }
8841  }
8842  if (!res) {
8843  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8844  res = ast_play_and_wait(chan, "vm-no");
8845  if (!res)
8846  res = ast_play_and_wait(chan, "vm-messages");
8847  }
8848  }
8849  }
8850  return res;
8851 }
8852 
8853 /* DUTCH syntax */
8854 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
8855 {
8856  /* Introduce messages they have */
8857  int res;
8858  res = ast_play_and_wait(chan, "vm-youhave");
8859  if (!res) {
8860  if (vms->newmessages) {
8861  res = say_and_wait(chan, vms->newmessages, chan->language);
8862  if (!res) {
8863  if (vms->newmessages == 1)
8864  res = ast_play_and_wait(chan, "vm-INBOXs");
8865  else
8866  res = ast_play_and_wait(chan, "vm-INBOX");
8867  }
8868  if (vms->oldmessages && !res)
8869  res = ast_play_and_wait(chan, "vm-and");
8870  else if (!res) {
8871  if ((vms->newmessages == 1))
8872  res = ast_play_and_wait(chan, "vm-message");
8873  else
8874  res = ast_play_and_wait(chan, "vm-messages");
8875  }
8876 
8877  }
8878  if (!res && vms->oldmessages) {
8879  res = say_and_wait(chan, vms->oldmessages, chan->language);
8880  if (!res) {
8881  if (vms->oldmessages == 1)
8882  res = ast_play_and_wait(chan, "vm-Olds");
8883  else
8884  res = ast_play_and_wait(chan, "vm-Old");
8885  }
8886  if (!res) {
8887  if (vms->oldmessages == 1)
8888  res = ast_play_and_wait(chan, "vm-message");
8889  else
8890  res = ast_play_and_wait(chan, "vm-messages");
8891  }
8892  }
8893  if (!res) {
8894  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8895  res = ast_play_and_wait(chan, "vm-no");
8896  if (!res)
8897  res = ast_play_and_wait(chan, "vm-messages");
8898  }
8899  }
8900  }
8901  return res;
8902 }
8903 
8904 /* PORTUGUESE syntax */
8905 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
8906 {
8907  /* Introduce messages they have */
8908  int res;
8909  res = ast_play_and_wait(chan, "vm-youhave");
8910  if (!res) {
8911  if (vms->newmessages) {
8912  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
8913  if (!res) {
8914  if ((vms->newmessages == 1)) {
8915  res = ast_play_and_wait(chan, "vm-message");
8916  if (!res)
8917  res = ast_play_and_wait(chan, "vm-INBOXs");
8918  } else {
8919  res = ast_play_and_wait(chan, "vm-messages");
8920  if (!res)
8921  res = ast_play_and_wait(chan, "vm-INBOX");
8922  }
8923  }
8924  if (vms->oldmessages && !res)
8925  res = ast_play_and_wait(chan, "vm-and");
8926  }
8927  if (!res && vms->oldmessages) {
8928  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
8929  if (!res) {
8930  if (vms->oldmessages == 1) {
8931  res = ast_play_and_wait(chan, "vm-message");
8932  if (!res)
8933  res = ast_play_and_wait(chan, "vm-Olds");
8934  } else {
8935  res = ast_play_and_wait(chan, "vm-messages");
8936  if (!res)
8937  res = ast_play_and_wait(chan, "vm-Old");
8938  }
8939  }
8940  }
8941  if (!res) {
8942  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8943  res = ast_play_and_wait(chan, "vm-no");
8944  if (!res)
8945  res = ast_play_and_wait(chan, "vm-messages");
8946  }
8947  }
8948  }
8949  return res;
8950 }
8951 
8952 
8953 /* CZECH syntax */
8954 /* in czech there must be declension of word new and message
8955  * czech : english : czech : english
8956  * --------------------------------------------------------
8957  * vm-youhave : you have
8958  * vm-novou : one new : vm-zpravu : message
8959  * vm-nove : 2-4 new : vm-zpravy : messages
8960  * vm-novych : 5-infinite new : vm-zprav : messages
8961  * vm-starou : one old
8962  * vm-stare : 2-4 old
8963  * vm-starych : 5-infinite old
8964  * jednu : one - falling 4.
8965  * vm-no : no ( no messages )
8966  */
8967 
8968 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
8969 {
8970  int res;
8971  res = ast_play_and_wait(chan, "vm-youhave");
8972  if (!res) {
8973  if (vms->newmessages) {
8974  if (vms->newmessages == 1) {
8975  res = ast_play_and_wait(chan, "digits/jednu");
8976  } else {
8977  res = say_and_wait(chan, vms->newmessages, chan->language);
8978  }
8979  if (!res) {
8980  if ((vms->newmessages == 1))
8981  res = ast_play_and_wait(chan, "vm-novou");
8982  if ((vms->newmessages) > 1 && (vms->newmessages < 5))
8983  res = ast_play_and_wait(chan, "vm-nove");
8984  if (vms->newmessages > 4)
8985  res = ast_play_and_wait(chan, "vm-novych");
8986  }
8987  if (vms->oldmessages && !res)
8988  res = ast_play_and_wait(chan, "vm-and");
8989  else if (!res) {
8990  if ((vms->newmessages == 1))
8991  res = ast_play_and_wait(chan, "vm-zpravu");
8992  if ((vms->newmessages) > 1 && (vms->newmessages < 5))
8993  res = ast_play_and_wait(chan, "vm-zpravy");
8994  if (vms->newmessages > 4)
8995  res = ast_play_and_wait(chan, "vm-zprav");
8996  }
8997  }
8998  if (!res && vms->oldmessages) {
8999  res = say_and_wait(chan, vms->oldmessages, chan->language);
9000  if (!res) {
9001  if ((vms->oldmessages == 1))
9002  res = ast_play_and_wait(chan, "vm-starou");
9003  if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
9004  res = ast_play_and_wait(chan, "vm-stare");
9005  if (vms->oldmessages > 4)
9006  res = ast_play_and_wait(chan, "vm-starych");
9007  }
9008  if (!res) {
9009  if ((vms->oldmessages == 1))
9010  res = ast_play_and_wait(chan, "vm-zpravu");
9011  if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
9012  res = ast_play_and_wait(chan, "vm-zpravy");
9013  if (vms->oldmessages > 4)
9014  res = ast_play_and_wait(chan, "vm-zprav");
9015  }
9016  }
9017  if (!res) {
9018  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
9019  res = ast_play_and_wait(chan, "vm-no");
9020  if (!res)
9021  res = ast_play_and_wait(chan, "vm-zpravy");
9022  }
9023  }
9024  }
9025  return res;
9026 }
9027 
9028 /* CHINESE (Taiwan) syntax */
9029 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
9030 {
9031  int res;
9032  /* Introduce messages they have */
9033  res = ast_play_and_wait(chan, "vm-you");
9034 
9035  if (!res && vms->newmessages) {
9036  res = ast_play_and_wait(chan, "vm-have");
9037  if (!res)
9038  res = say_and_wait(chan, vms->newmessages, chan->language);
9039  if (!res)
9040  res = ast_play_and_wait(chan, "vm-tong");
9041  if (!res)
9042  res = ast_play_and_wait(chan, "vm-INBOX");
9043  if (vms->oldmessages && !res)
9044  res = ast_play_and_wait(chan, "vm-and");
9045  else if (!res)
9046  res = ast_play_and_wait(chan, "vm-messages");
9047  }
9048  if (!res && vms->oldmessages) {
9049  res = ast_play_and_wait(chan, "vm-have");
9050  if (!res)
9051  res = say_and_wait(chan, vms->oldmessages, chan->language);
9052  if (!res)
9053  res = ast_play_and_wait(chan, "vm-tong");
9054  if (!res)
9055  res = ast_play_and_wait(chan, "vm-Old");
9056  if (!res)
9057  res = ast_play_and_wait(chan, "vm-messages");
9058  }
9059  if (!res && !vms->oldmessages && !vms->newmessages) {
9060  res = ast_play_and_wait(chan, "vm-haveno");
9061  if (!res)
9062  res = ast_play_and_wait(chan, "vm-messages");
9063  }
9064  return res;
9065 }
9066 
9067 /* Vietnamese syntax */
9068 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
9069 {
9070  int res;
9071 
9072  /* Introduce messages they have */
9073  res = ast_play_and_wait(chan, "vm-youhave");
9074  if (!res) {
9075  if (vms->newmessages) {
9076  res = say_and_wait(chan, vms->newmessages, chan->language);
9077  if (!res)
9078  res = ast_play_and_wait(chan, "vm-INBOX");
9079  if (vms->oldmessages && !res)
9080  res = ast_play_and_wait(chan, "vm-and");
9081  }
9082  if (!res && vms->oldmessages) {
9083  res = say_and_wait(chan, vms->oldmessages, chan->language);
9084  if (!res)
9085  res = ast_play_and_wait(chan, "vm-Old");
9086  }
9087  if (!res) {
9088  if (!vms->oldmessages && !vms->newmessages) {
9089  res = ast_play_and_wait(chan, "vm-no");
9090  if (!res)
9091  res = ast_play_and_wait(chan, "vm-message");
9092  }
9093  }
9094  }
9095  return res;
9096 }
9097 
9098 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
9099 {
9100  char prefile[256];
9101 
9102  /* Notify the user that the temp greeting is set and give them the option to remove it */
9103  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
9104  if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
9105  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
9106  if (ast_fileexists(prefile, NULL, NULL) > 0) {
9107  ast_play_and_wait(chan, "vm-tempgreetactive");
9108  }
9109  DISPOSE(prefile, -1);
9110  }
9111 
9112  /* Play voicemail intro - syntax is different for different languages */
9113  if (0) {
9114  return 0;
9115  } else if (!strncasecmp(chan->language, "cs", 2)) { /* CZECH syntax */
9116  return vm_intro_cs(chan, vms);
9117  } else if (!strncasecmp(chan->language, "cz", 2)) { /* deprecated CZECH syntax */
9118  static int deprecation_warning = 0;
9119  if (deprecation_warning++ % 10 == 0) {
9120  ast_log(LOG_WARNING, "cz is not a standard language code. Please switch to using cs instead.\n");
9121  }
9122  return vm_intro_cs(chan, vms);
9123  } else if (!strncasecmp(chan->language, "de", 2)) { /* GERMAN syntax */
9124  return vm_intro_de(chan, vms);
9125  } else if (!strncasecmp(chan->language, "es", 2)) { /* SPANISH syntax */
9126  return vm_intro_es(chan, vms);
9127  } else if (!strncasecmp(chan->language, "fr", 2)) { /* FRENCH syntax */
9128  return vm_intro_fr(chan, vms);
9129  } else if (!strncasecmp(chan->language, "gr", 2)) { /* GREEK syntax */
9130  return vm_intro_gr(chan, vms);
9131  } else if (!strncasecmp(chan->language, "he", 2)) { /* HEBREW syntax */
9132  return vm_intro_he(chan, vms);
9133  } else if (!strncasecmp(chan->language, "it", 2)) { /* ITALIAN syntax */
9134  return vm_intro_it(chan, vms);
9135  } else if (!strncasecmp(chan->language, "nl", 2)) { /* DUTCH syntax */
9136  return vm_intro_nl(chan, vms);
9137  } else if (!strncasecmp(chan->language, "no", 2)) { /* NORWEGIAN syntax */
9138  return vm_intro_no(chan, vms);
9139  } else if (!strncasecmp(chan->language, "pl", 2)) { /* POLISH syntax */
9140  return vm_intro_pl(chan, vms);
9141  } else if (!strncasecmp(chan->language, "pt_BR", 5)) { /* BRAZILIAN PORTUGUESE syntax */
9142  return vm_intro_pt_BR(chan, vms);
9143  } else if (!strncasecmp(chan->language, "pt", 2)) { /* PORTUGUESE syntax */
9144  return vm_intro_pt(chan, vms);
9145  } else if (!strncasecmp(chan->language, "ru", 2)) { /* RUSSIAN syntax */
9146  return vm_intro_multilang(chan, vms, "n");
9147  } else if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
9148  return vm_intro_se(chan, vms);
9149  } else if (!strncasecmp(chan->language, "ua", 2)) { /* UKRAINIAN syntax */
9150  return vm_intro_multilang(chan, vms, "n");
9151  } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
9152  return vm_intro_vi(chan, vms);
9153  } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
9154  return vm_intro_zh(chan, vms);
9155  } else { /* Default to ENGLISH */
9156  return vm_intro_en(chan, vms);
9157  }
9158 }
9159 
9160 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
9161 {
9162  int res = 0;
9163  /* Play instructions and wait for new command */
9164  while (!res) {
9165  if (vms->starting) {
9166  if (vms->lastmsg > -1) {
9167  if (skipadvanced)
9168  res = ast_play_and_wait(chan, "vm-onefor-full");
9169  else
9170  res = ast_play_and_wait(chan, "vm-onefor");
9171  if (!res)
9172  res = vm_play_folder_name(chan, vms->vmbox);
9173  }
9174  if (!res) {
9175  if (skipadvanced)
9176  res = ast_play_and_wait(chan, "vm-opts-full");
9177  else
9178  res = ast_play_and_wait(chan, "vm-opts");
9179  }
9180  } else {
9181  /* Added for additional help */
9182  if (skipadvanced) {
9183  res = ast_play_and_wait(chan, "vm-onefor-full");
9184  if (!res)
9185  res = vm_play_folder_name(chan, vms->vmbox);
9186  res = ast_play_and_wait(chan, "vm-opts-full");
9187  }
9188  /* Logic:
9189  * If the current message is not the first OR
9190  * if we're listening to the first new message and there are
9191  * also urgent messages, then prompt for navigation to the
9192  * previous message
9193  */
9194  if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
9195  res = ast_play_and_wait(chan, "vm-prev");
9196  }
9197  if (!res && !skipadvanced)
9198  res = ast_play_and_wait(chan, "vm-advopts");
9199  if (!res)
9200  res = ast_play_and_wait(chan, "vm-repeat");
9201  /* Logic:
9202  * If we're not listening to the last message OR
9203  * we're listening to the last urgent message and there are
9204  * also new non-urgent messages, then prompt for navigation
9205  * to the next message
9206  */
9207  if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
9208  (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
9209  res = ast_play_and_wait(chan, "vm-next");
9210  }
9211  if (!res) {
9212  int curmsg_deleted;
9213 #ifdef IMAP_STORAGE
9214  ast_mutex_lock(&vms->lock);
9215 #endif
9216  curmsg_deleted = vms->deleted[vms->curmsg];
9217 #ifdef IMAP_STORAGE
9218  ast_mutex_unlock(&vms->lock);
9219 #endif
9220  if (!curmsg_deleted) {
9221  res = ast_play_and_wait(chan, "vm-delete");
9222  } else {
9223  res = ast_play_and_wait(chan, "vm-undelete");
9224  }
9225  if (!res) {
9226  res = ast_play_and_wait(chan, "vm-toforward");
9227  }
9228  if (!res) {
9229  res = ast_play_and_wait(chan, "vm-savemessage");
9230  }
9231  }
9232  }
9233  if (!res) {
9234  res = ast_play_and_wait(chan, "vm-helpexit");
9235  }
9236  if (!res)
9237  res = ast_waitfordigit(chan, 6000);
9238  if (!res) {
9239  vms->repeats++;
9240  if (vms->repeats > 2) {
9241  res = 't';
9242  }
9243  }
9244  }
9245  return res;
9246 }
9247 
9248 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
9249 {
9250  int res = 0;
9251  /* Play instructions and wait for new command */
9252  while (!res) {
9253  if (vms->lastmsg > -1) {
9254  res = ast_play_and_wait(chan, "vm-listen");
9255  if (!res)
9256  res = vm_play_folder_name(chan, vms->vmbox);
9257  if (!res)
9258  res = ast_play_and_wait(chan, "press");
9259  if (!res)
9260  res = ast_play_and_wait(chan, "digits/1");
9261  }
9262  if (!res)
9263  res = ast_play_and_wait(chan, "vm-opts");
9264  if (!res) {
9265  vms->starting = 0;
9266  return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
9267  }
9268  }
9269  return res;
9270 }
9271 
9272 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
9273 {
9274  if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
9275  return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
9276  } else { /* Default to ENGLISH */
9277  return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
9278  }
9279 }
9280 
9281 
9282 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
9283 {
9284  int cmd = 0;
9285  int duration = 0;
9286  int tries = 0;
9287  char newpassword[80] = "";
9288  char newpassword2[80] = "";
9289  char prefile[PATH_MAX] = "";
9290  unsigned char buf[256];
9291  int bytes = 0;
9292 
9293  ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
9294  if (ast_adsi_available(chan)) {
9295  bytes += adsi_logo(buf + bytes);
9296  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
9297  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
9298  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
9299  bytes += ast_adsi_voice_mode(buf + bytes, 0);
9300  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
9301  }
9302 
9303  /* If forcename is set, have the user record their name */
9304  if (ast_test_flag(vmu, VM_FORCENAME)) {
9305  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
9306  if (ast_fileexists(prefile, NULL, NULL) < 1) {
9307  cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9308  if (cmd < 0 || cmd == 't' || cmd == '#')
9309  return cmd;
9310  }
9311  }
9312 
9313  /* If forcegreetings is set, have the user record their greetings */
9314  if (ast_test_flag(vmu, VM_FORCEGREET)) {
9315  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
9316  if (ast_fileexists(prefile, NULL, NULL) < 1) {
9317  cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9318  if (cmd < 0 || cmd == 't' || cmd == '#')
9319  return cmd;
9320  }
9321 
9322  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
9323  if (ast_fileexists(prefile, NULL, NULL) < 1) {
9324  cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9325  if (cmd < 0 || cmd == 't' || cmd == '#')
9326  return cmd;
9327  }
9328  }
9329 
9330  /*
9331  * Change the password last since new users will be able to skip over any steps this one comes before
9332  * by hanging up and calling back to voicemail main since the password is used to verify new user status.
9333  */
9334  for (;;) {
9335  newpassword[1] = '\0';
9336  newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
9337  if (cmd == '#')
9338  newpassword[0] = '\0';
9339  if (cmd < 0 || cmd == 't' || cmd == '#')
9340  return cmd;
9341  cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
9342  if (cmd < 0 || cmd == 't' || cmd == '#')
9343  return cmd;
9344  cmd = check_password(vmu, newpassword); /* perform password validation */
9345  if (cmd != 0) {
9346  ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
9347  cmd = ast_play_and_wait(chan, vm_invalid_password);
9348  } else {
9349  newpassword2[1] = '\0';
9350  newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
9351  if (cmd == '#')
9352  newpassword2[0] = '\0';
9353  if (cmd < 0 || cmd == 't' || cmd == '#')
9354  return cmd;
9355  cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
9356  if (cmd < 0 || cmd == 't' || cmd == '#')
9357  return cmd;
9358  if (!strcmp(newpassword, newpassword2))
9359  break;
9360  ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
9361  cmd = ast_play_and_wait(chan, vm_mismatch);
9362  }
9363  if (++tries == 3)
9364  return -1;
9365  if (cmd != 0) {
9366  cmd = ast_play_and_wait(chan, vm_pls_try_again);
9367  }
9368  }
9369  if (pwdchange & PWDCHANGE_INTERNAL)
9370  vm_change_password(vmu, newpassword);
9371  if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
9372  vm_change_password_shell(vmu, newpassword);
9373 
9374  ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
9375  cmd = ast_play_and_wait(chan, vm_passchanged);
9376 
9377  return cmd;
9378 }
9379 
9380 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
9381 {
9382  int cmd = 0;
9383  int retries = 0;
9384  int duration = 0;
9385  char newpassword[80] = "";
9386  char newpassword2[80] = "";
9387  char prefile[PATH_MAX] = "";
9388  unsigned char buf[256];
9389  int bytes = 0;
9390 
9391  ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
9392  if (ast_adsi_available(chan)) {
9393  bytes += adsi_logo(buf + bytes);
9394  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
9395  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
9396  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
9397  bytes += ast_adsi_voice_mode(buf + bytes, 0);
9398  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
9399  }
9400  while ((cmd >= 0) && (cmd != 't')) {
9401  if (cmd)
9402  retries = 0;
9403  switch (cmd) {
9404  case '1': /* Record your unavailable message */
9405  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
9406  cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9407  break;
9408  case '2': /* Record your busy message */
9409  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
9410  cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9411  break;
9412  case '3': /* Record greeting */
9413  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
9414  cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9415  break;
9416  case '4': /* manage the temporary greeting */
9417  cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
9418  break;
9419  case '5': /* change password */
9420  if (vmu->password[0] == '-') {
9421  cmd = ast_play_and_wait(chan, "vm-no");
9422  break;
9423  }
9424  newpassword[1] = '\0';
9425  newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
9426  if (cmd == '#')
9427  newpassword[0] = '\0';
9428  else {
9429  if (cmd < 0)
9430  break;
9431  if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
9432  break;
9433  }
9434  }
9435  cmd = check_password(vmu, newpassword); /* perform password validation */
9436  if (cmd != 0) {
9437  ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
9438  cmd = ast_play_and_wait(chan, vm_invalid_password);
9439  if (!cmd) {
9440  cmd = ast_play_and_wait(chan, vm_pls_try_again);
9441  }
9442  break;
9443  }
9444  newpassword2[1] = '\0';
9445  newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
9446  if (cmd == '#')
9447  newpassword2[0] = '\0';
9448  else {
9449  if (cmd < 0)
9450  break;
9451 
9452  if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
9453  break;
9454  }
9455  }
9456  if (strcmp(newpassword, newpassword2)) {
9457  ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
9458  cmd = ast_play_and_wait(chan, vm_mismatch);
9459  if (!cmd) {
9460  cmd = ast_play_and_wait(chan, vm_pls_try_again);
9461  }
9462  break;
9463  }
9464 
9465  if (pwdchange & PWDCHANGE_INTERNAL) {
9466  vm_change_password(vmu, newpassword);
9467  }
9468  if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
9469  vm_change_password_shell(vmu, newpassword);
9470  }
9471 
9472  ast_debug(1, "User %s set password to %s of length %d\n",
9473  vms->username, newpassword, (int) strlen(newpassword));
9474  cmd = ast_play_and_wait(chan, vm_passchanged);
9475  break;
9476  case '*':
9477  cmd = 't';
9478  break;
9479  default:
9480  cmd = 0;
9481  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
9482  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
9483  if (ast_fileexists(prefile, NULL, NULL)) {
9484  cmd = ast_play_and_wait(chan, "vm-tmpexists");
9485  }
9486  DISPOSE(prefile, -1);
9487  if (!cmd) {
9488  cmd = ast_play_and_wait(chan, "vm-options");
9489  }
9490  if (!cmd) {
9491  cmd = ast_waitfordigit(chan, 6000);
9492  }
9493  if (!cmd) {
9494  retries++;
9495  }
9496  if (retries > 3) {
9497  cmd = 't';
9498  }
9499  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
9500  }
9501  }
9502  if (cmd == 't')
9503  cmd = 0;
9504  return cmd;
9505 }
9506 
9507 /*!
9508  * \brief The handler for 'record a temporary greeting'.
9509  * \param chan
9510  * \param vmu
9511  * \param vms
9512  * \param fmtc
9513  * \param record_gain
9514  *
9515  * This is option 4 from the mailbox options menu.
9516  * This function manages the following promptings:
9517  * 1: play / record / review the temporary greeting. : invokes play_record_review().
9518  * 2: remove (delete) the temporary greeting.
9519  * *: return to the main menu.
9520  *
9521  * \return zero on success, -1 on error.
9522  */
9523 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
9524 {
9525  int cmd = 0;
9526  int retries = 0;
9527  int duration = 0;
9528  char prefile[PATH_MAX] = "";
9529  unsigned char buf[256];
9530  int bytes = 0;
9531 
9532  if (ast_adsi_available(chan)) {
9533  bytes += adsi_logo(buf + bytes);
9534  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
9535  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
9536  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
9537  bytes += ast_adsi_voice_mode(buf + bytes, 0);
9538  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
9539  }
9540 
9541  ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
9542  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
9543  while ((cmd >= 0) && (cmd != 't')) {
9544  if (cmd)
9545  retries = 0;
9546  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
9547  if (ast_fileexists(prefile, NULL, NULL) <= 0) {
9548  cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9549  if (cmd == -1) {
9550  break;
9551  }
9552  cmd = 't';
9553  } else {
9554  switch (cmd) {
9555  case '1':
9556  cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9557  break;
9558  case '2':
9559  DELETE(prefile, -1, prefile, vmu);
9560  ast_play_and_wait(chan, "vm-tempremoved");
9561  cmd = 't';
9562  break;
9563  case '*':
9564  cmd = 't';
9565  break;
9566  default:
9567  cmd = ast_play_and_wait(chan,
9568  ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
9569  "vm-tempgreeting2" : "vm-tempgreeting");
9570  if (!cmd) {
9571  cmd = ast_waitfordigit(chan, 6000);
9572  }
9573  if (!cmd) {
9574  retries++;
9575  }
9576  if (retries > 3) {
9577  cmd = 't';
9578  }
9579  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
9580  }
9581  }
9582  DISPOSE(prefile, -1);
9583  }
9584  if (cmd == 't')
9585  cmd = 0;
9586  return cmd;
9587 }
9588 
9589 
9590 /* Hebrew Syntax */
9591 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9592 {
9593  int cmd = 0;
9594 
9595  if (vms->lastmsg > -1) {
9596  cmd = play_message(chan, vmu, vms);
9597  } else {
9598  if (!strcasecmp(vms->fn, "INBOX")) {
9599  cmd = ast_play_and_wait(chan, "vm-nonewmessages");
9600  } else {
9601  cmd = ast_play_and_wait(chan, "vm-nomessages");
9602  }
9603  }
9604  return cmd;
9605 }
9606 
9607 /*!
9608  * \brief Default English syntax for 'You have N messages' greeting.
9609  * \param chan
9610  * \param vms
9611  * \param vmu
9612  *
9613  * \return zero on success, -1 on error.
9614  */
9615 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9616 {
9617  int cmd = 0;
9618 
9619  if (vms->lastmsg > -1) {
9620  cmd = play_message(chan, vmu, vms);
9621  } else {
9622  cmd = ast_play_and_wait(chan, "vm-youhave");
9623  if (!cmd)
9624  cmd = ast_play_and_wait(chan, "vm-no");
9625  if (!cmd) {
9626  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
9627  cmd = ast_play_and_wait(chan, vms->fn);
9628  }
9629  if (!cmd)
9630  cmd = ast_play_and_wait(chan, "vm-messages");
9631  }
9632  return cmd;
9633 }
9634 
9635 
9636 /*!
9637  * \brief Common LATIN languages syntax for 'You have N messages' greeting.
9638  * \param chan
9639  * \param vms
9640  * \param vmu
9641  *
9642  * \return zero on success, -1 on error.
9643  */
9644 static int vm_browse_messages_latin(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9645 {
9646  int cmd;
9647 
9648  if (vms->lastmsg > -1) {
9649  cmd = play_message(chan, vmu, vms);
9650  } else {
9651  cmd = ast_play_and_wait(chan, "vm-youhaveno");
9652  if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
9653  if (!cmd) {
9654  snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
9655  cmd = ast_play_and_wait(chan, vms->fn);
9656  }
9657  if (!cmd)
9658  cmd = ast_play_and_wait(chan, "vm-messages");
9659  } else {
9660  if (!cmd)
9661  cmd = ast_play_and_wait(chan, "vm-messages");
9662  if (!cmd) {
9663  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
9664  cmd = ast_play_and_wait(chan, vms->fn);
9665  }
9666  }
9667  }
9668  return cmd;
9669 }
9670 
9671 /*!
9672  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
9673  * \param chan
9674  * \param vms
9675  * \param vmu
9676  *
9677  * \return zero on success, -1 on error.
9678  */
9679 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9680 {
9681  int cmd;
9682 
9683  if (vms->lastmsg > -1) {
9684  cmd = play_message(chan, vmu, vms);
9685  } else {
9686  cmd = ast_play_and_wait(chan, "vm-you");
9687  if (!cmd)
9688  cmd = ast_play_and_wait(chan, "vm-haveno");
9689  if (!cmd)
9690  cmd = ast_play_and_wait(chan, "vm-messages");
9691  if (!cmd) {
9692  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
9693  cmd = ast_play_and_wait(chan, vms->fn);
9694  }
9695  }
9696  return cmd;
9697 }
9698 
9699 /*!
9700  * \brief Vietnamese syntax for 'You have N messages' greeting.
9701  * \param chan
9702  * \param vms
9703  * \param vmu
9704  *
9705  * \return zero on success, -1 on error.
9706  */
9707 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9708 {
9709  int cmd = 0;
9710 
9711  if (vms->lastmsg > -1) {
9712  cmd = play_message(chan, vmu, vms);
9713  } else {
9714  cmd = ast_play_and_wait(chan, "vm-no");
9715  if (!cmd) {
9716  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
9717  cmd = ast_play_and_wait(chan, vms->fn);
9718  }
9719  }
9720  return cmd;
9721 }
9722 
9723 /*!
9724  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
9725  * \param chan The channel for the current user. We read the language property from this.
9726  * \param vms passed into the language-specific vm_browse_messages function.
9727  * \param vmu passed into the language-specific vm_browse_messages function.
9728  *
9729  * The method to be invoked is determined by the value of language code property in the user's channel.
9730  * The default (when unable to match) is to use english.
9731  *
9732  * \return zero on success, -1 on error.
9733  */
9734 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9735 {
9736  if (!strncasecmp(chan->language, "es", 2) ||
9737  !strncasecmp(chan->language, "it", 2) ||
9738  !strncasecmp(chan->language, "pt", 2) ||
9739  !strncasecmp(chan->language, "gr", 2)) { /* SPANISH, ITALIAN, PORTUGUESE or GREEK */
9740  return vm_browse_messages_latin(chan, vms, vmu);
9741  } else if (!strncasecmp(chan->language, "he", 2)) { /* HEBREW */
9742  return vm_browse_messages_he(chan, vms, vmu);
9743  } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE */
9744  return vm_browse_messages_vi(chan, vms, vmu);
9745  } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) */
9746  return vm_browse_messages_zh(chan, vms, vmu);
9747  } else { /* Default to English syntax */
9748  return vm_browse_messages_en(chan, vms, vmu);
9749  }
9750 }
9751 
9752 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
9753  struct ast_vm_user *res_vmu, const char *context, const char *prefix,
9754  int skipuser, int max_logins, int silent)
9755 {
9756  int useadsi = 0, valid = 0, logretries = 0;
9757  char password[AST_MAX_EXTENSION]="", *passptr;
9758  struct ast_vm_user vmus, *vmu = NULL;
9759 
9760  /* If ADSI is supported, setup login screen */
9761  adsi_begin(chan, &useadsi);
9762  if (!skipuser && useadsi)
9763  adsi_login(chan);
9764  if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
9765  ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
9766  return -1;
9767  }
9768 
9769  /* Authenticate them and get their mailbox/password */
9770 
9771  while (!valid && (logretries < max_logins)) {
9772  /* Prompt for, and read in the username */
9773  if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
9774  ast_log(AST_LOG_WARNING, "Couldn't read username\n");
9775  return -1;
9776  }
9777  if (ast_strlen_zero(mailbox)) {
9778  if (chan->caller.id.number.valid && chan->caller.id.number.str) {
9779  ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
9780  } else {
9781  ast_verb(3, "Username not entered\n");
9782  return -1;
9783  }
9784  } else if (mailbox[0] == '*') {
9785  /* user entered '*' */
9786  ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
9787  if (ast_exists_extension(chan, chan->context, "a", 1,
9788  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
9789  return -1;
9790  }
9791  ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
9792  mailbox[0] = '\0';
9793  }
9794 
9795  if (useadsi)
9796  adsi_password(chan);
9797 
9798  if (!ast_strlen_zero(prefix)) {
9799  char fullusername[80] = "";
9800  ast_copy_string(fullusername, prefix, sizeof(fullusername));
9801  strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
9802  ast_copy_string(mailbox, fullusername, mailbox_size);
9803  }
9804 
9805  ast_debug(1, "Before find user for mailbox %s\n", mailbox);
9806  vmu = find_user(&vmus, context, mailbox);
9807  if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
9808  /* saved password is blank, so don't bother asking */
9809  password[0] = '\0';
9810  } else {
9811  if (ast_streamfile(chan, vm_password, chan->language)) {
9812  ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
9813  return -1;
9814  }
9815  if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
9816  ast_log(AST_LOG_WARNING, "Unable to read password\n");
9817  return -1;
9818  } else if (password[0] == '*') {
9819  /* user entered '*' */
9820  ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
9821  if (ast_exists_extension(chan, chan->context, "a", 1,
9822  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
9823  mailbox[0] = '*';
9824  return -1;
9825  }
9826  ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
9827  mailbox[0] = '\0';
9828  /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
9829  vmu = NULL;
9830  }
9831  }
9832 
9833  if (vmu) {
9834  passptr = vmu->password;
9835  if (passptr[0] == '-') passptr++;
9836  }
9837  if (vmu && !strcmp(passptr, password))
9838  valid++;
9839  else {
9840  ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
9841  if (!ast_strlen_zero(prefix))
9842  mailbox[0] = '\0';
9843  }
9844  logretries++;
9845  if (!valid) {
9846  if (skipuser || logretries >= max_logins) {
9847  if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
9848  ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
9849  return -1;
9850  }
9851  } else {
9852  if (useadsi)
9853  adsi_login(chan);
9854  if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
9855  ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
9856  return -1;
9857  }
9858  }
9859  if (ast_waitstream(chan, "")) /* Channel is hung up */
9860  return -1;
9861  }
9862  }
9863  if (!valid && (logretries >= max_logins)) {
9864  ast_stopstream(chan);
9865  ast_play_and_wait(chan, "vm-goodbye");
9866  return -1;
9867  }
9868  if (vmu && !skipuser) {
9869  memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
9870  }
9871  return 0;
9872 }
9873 
9874 static int vm_execmain(struct ast_channel *chan, const char *data)
9875 {
9876  /* XXX This is, admittedly, some pretty horrendous code. For some
9877  reason it just seemed a lot easier to do with GOTO's. I feel
9878  like I'm back in my GWBASIC days. XXX */
9879  int res = -1;
9880  int cmd = 0;
9881  int valid = 0;
9882  char prefixstr[80] ="";
9883  char ext_context[256]="";
9884  int box;
9885  int useadsi = 0;
9886  int skipuser = 0;
9887  struct vm_state vms;
9888  struct ast_vm_user *vmu = NULL, vmus;
9889  char *context = NULL;
9890  int silentexit = 0;
9891  struct ast_flags flags = { 0 };
9892  signed char record_gain = 0;
9893  int play_auto = 0;
9894  int play_folder = 0;
9895  int in_urgent = 0;
9896 #ifdef IMAP_STORAGE
9897  int deleted = 0;
9898 #endif
9899 
9900  /* Add the vm_state to the active list and keep it active */
9901  memset(&vms, 0, sizeof(vms));
9902 
9903  vms.lastmsg = -1;
9904 
9905  memset(&vmus, 0, sizeof(vmus));
9906 
9907  ast_test_suite_event_notify("START", "Message: vm_execmain started");
9908  if (chan->_state != AST_STATE_UP) {
9909  ast_debug(1, "Before ast_answer\n");
9910  ast_answer(chan);
9911  }
9912 
9913  if (!ast_strlen_zero(data)) {
9914  char *opts[OPT_ARG_ARRAY_SIZE];
9915  char *parse;
9917  AST_APP_ARG(argv0);
9918  AST_APP_ARG(argv1);
9919  );
9920 
9921  parse = ast_strdupa(data);
9922 
9923  AST_STANDARD_APP_ARGS(args, parse);
9924 
9925  if (args.argc == 2) {
9926  if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
9927  return -1;
9928  if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
9929  int gain;
9930  if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
9931  if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
9932  ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
9933  return -1;
9934  } else {
9935  record_gain = (signed char) gain;
9936  }
9937  } else {
9938  ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
9939  }
9940  }
9941  if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
9942  play_auto = 1;
9943  if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
9944  /* See if it is a folder name first */
9945  if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
9946  if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
9947  play_folder = -1;
9948  }
9949  } else {
9950  play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
9951  }
9952  } else {
9953  ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
9954  }
9955  if (play_folder > 9 || play_folder < 0) {
9957  "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
9958  opts[OPT_ARG_PLAYFOLDER]);
9959  play_folder = 0;
9960  }
9961  }
9962  } else {
9963  /* old style options parsing */
9964  while (*(args.argv0)) {
9965  if (*(args.argv0) == 's')
9966  ast_set_flag(&flags, OPT_SILENT);
9967  else if (*(args.argv0) == 'p')
9969  else
9970  break;
9971  (args.argv0)++;
9972  }
9973 
9974  }
9975 
9976  valid = ast_test_flag(&flags, OPT_SILENT);
9977 
9978  if ((context = strchr(args.argv0, '@')))
9979  *context++ = '\0';
9980 
9981  if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
9982  ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
9983  else
9984  ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
9985 
9986  if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
9987  skipuser++;
9988  else
9989  valid = 0;
9990  }
9991 
9992  if (!valid)
9993  res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
9994 
9995  ast_debug(1, "After vm_authenticate\n");
9996 
9997  if (vms.username[0] == '*') {
9998  ast_debug(1, "user pressed * in context '%s'\n", chan->context);
9999 
10000  /* user entered '*' */
10001  if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
10002  ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
10003  res = 0; /* prevent hangup */
10004  goto out;
10005  }
10006  }
10007 
10008  if (!res) {
10009  valid = 1;
10010  if (!skipuser)
10011  vmu = &vmus;
10012  } else {
10013  res = 0;
10014  }
10015 
10016  /* If ADSI is supported, setup login screen */
10017  adsi_begin(chan, &useadsi);
10018 
10019  ast_test_suite_assert(valid);
10020  if (!valid) {
10021  goto out;
10022  }
10023  ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
10024 
10025 #ifdef IMAP_STORAGE
10026  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10027  pthread_setspecific(ts_vmstate.key, &vms);
10028 
10029  vms.interactive = 1;
10030  vms.updated = 1;
10031  if (vmu)
10032  ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10033  vmstate_insert(&vms);
10034  init_vm_state(&vms);
10035 #endif
10036 
10037  /* Set language from config to override channel language */
10038  if (!ast_strlen_zero(vmu->language))
10039  ast_string_field_set(chan, language, vmu->language);
10040 
10041  /* Retrieve urgent, old and new message counts */
10042  ast_debug(1, "Before open_mailbox\n");
10043  res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10044  if (res < 0)
10045  goto out;
10046  vms.oldmessages = vms.lastmsg + 1;
10047  ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10048  /* check INBOX */
10049  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10050  if (res < 0)
10051  goto out;
10052  vms.newmessages = vms.lastmsg + 1;
10053  ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10054  /* Start in Urgent */
10055  in_urgent = 1;
10056  res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10057  if (res < 0)
10058  goto out;
10059  vms.urgentmessages = vms.lastmsg + 1;
10060  ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10061 
10062  /* Select proper mailbox FIRST!! */
10063  if (play_auto) {
10064  ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10065  if (vms.urgentmessages) {
10066  in_urgent = 1;
10067  res = open_mailbox(&vms, vmu, 11);
10068  } else {
10069  in_urgent = 0;
10070  res = open_mailbox(&vms, vmu, play_folder);
10071  }
10072  if (res < 0)
10073  goto out;
10074 
10075  /* If there are no new messages, inform the user and hangup */
10076  if (vms.lastmsg == -1) {
10077  in_urgent = 0;
10078  cmd = vm_browse_messages(chan, &vms, vmu);
10079  res = 0;
10080  goto out;
10081  }
10082  } else {
10083  if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10084  /* If we only have old messages start here */
10085  res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10086  in_urgent = 0;
10087  play_folder = 1;
10088  if (res < 0)
10089  goto out;
10090  } else if (!vms.urgentmessages && vms.newmessages) {
10091  /* If we have new messages but none are urgent */
10092  in_urgent = 0;
10093  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10094  if (res < 0)
10095  goto out;
10096  }
10097  }
10098 
10099  if (useadsi)
10100  adsi_status(chan, &vms);
10101  res = 0;
10102 
10103  /* Check to see if this is a new user */
10104  if (!strcasecmp(vmu->mailbox, vmu->password) &&
10106  if (ast_play_and_wait(chan, "vm-newuser") == -1)
10107  ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10108  cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10109  if ((cmd == 't') || (cmd == '#')) {
10110  /* Timeout */
10111  ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10112  res = 0;
10113  goto out;
10114  } else if (cmd < 0) {
10115  /* Hangup */
10116  ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10117  res = -1;
10118  goto out;
10119  }
10120  }
10121 #ifdef IMAP_STORAGE
10122  ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10123  if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10124  ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10125  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10126  }
10127  ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10128  if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10129  ast_log(AST_LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10130  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10131  }
10132 #endif
10133 
10134  ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10135  if (play_auto) {
10136  cmd = '1';
10137  } else {
10138  cmd = vm_intro(chan, vmu, &vms);
10139  }
10140  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10141 
10142  vms.repeats = 0;
10143  vms.starting = 1;
10144  while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10145  /* Run main menu */
10146  switch (cmd) {
10147  case '1': /* First message */
10148  vms.curmsg = 0;
10149  /* Fall through */
10150  case '5': /* Play current message */
10151  ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10152  cmd = vm_browse_messages(chan, &vms, vmu);
10153  break;
10154  case '2': /* Change folders */
10155  ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10156  if (useadsi)
10157  adsi_folders(chan, 0, "Change to folder...");
10158 
10159  cmd = get_folder2(chan, "vm-changeto", 0);
10160  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10161  if (cmd == '#') {
10162  cmd = 0;
10163  } else if (cmd > 0) {
10164  cmd = cmd - '0';
10165  res = close_mailbox(&vms, vmu);
10166  if (res == ERROR_LOCK_PATH)
10167  goto out;
10168  /* If folder is not urgent, set in_urgent to zero! */
10169  if (cmd != 11) in_urgent = 0;
10170  res = open_mailbox(&vms, vmu, cmd);
10171  if (res < 0)
10172  goto out;
10173  play_folder = cmd;
10174  cmd = 0;
10175  }
10176  if (useadsi)
10177  adsi_status2(chan, &vms);
10178 
10179  if (!cmd) {
10180  cmd = vm_play_folder_name(chan, vms.vmbox);
10181  }
10182 
10183  vms.starting = 1;
10184  vms.curmsg = 0;
10185  break;
10186  case '3': /* Advanced options */
10187  ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10188  cmd = 0;
10189  vms.repeats = 0;
10190  while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10191  switch (cmd) {
10192  case '1': /* Reply */
10193  if (vms.lastmsg > -1 && !vms.starting) {
10194  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10195  if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10196  res = cmd;
10197  goto out;
10198  }
10199  } else {
10200  cmd = ast_play_and_wait(chan, "vm-sorry");
10201  }
10202  cmd = 't';
10203  break;
10204  case '2': /* Callback */
10205  if (!vms.starting)
10206  ast_verb(3, "Callback Requested\n");
10207  if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10208  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10209  if (cmd == 9) {
10210  silentexit = 1;
10211  goto out;
10212  } else if (cmd == ERROR_LOCK_PATH) {
10213  res = cmd;
10214  goto out;
10215  }
10216  } else {
10217  cmd = ast_play_and_wait(chan, "vm-sorry");
10218  }
10219  cmd = 't';
10220  break;
10221  case '3': /* Envelope */
10222  if (vms.lastmsg > -1 && !vms.starting) {
10223  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10224  if (cmd == ERROR_LOCK_PATH) {
10225  res = cmd;
10226  goto out;
10227  }
10228  } else {
10229  cmd = ast_play_and_wait(chan, "vm-sorry");
10230  }
10231  cmd = 't';
10232  break;
10233  case '4': /* Dialout */
10234  if (!ast_strlen_zero(vmu->dialout)) {
10235  cmd = dialout(chan, vmu, NULL, vmu->dialout);
10236  if (cmd == 9) {
10237  silentexit = 1;
10238  goto out;
10239  }
10240  } else {
10241  cmd = ast_play_and_wait(chan, "vm-sorry");
10242  }
10243  cmd = 't';
10244  break;
10245 
10246  case '5': /* Leave VoiceMail */
10247  if (ast_test_flag(vmu, VM_SVMAIL)) {
10248  cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10249  if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10250  res = cmd;
10251  goto out;
10252  }
10253  } else {
10254  cmd = ast_play_and_wait(chan, "vm-sorry");
10255  }
10256  cmd = 't';
10257  break;
10258 
10259  case '*': /* Return to main menu */
10260  cmd = 't';
10261  break;
10262 
10263  default:
10264  cmd = 0;
10265  if (!vms.starting) {
10266  cmd = ast_play_and_wait(chan, "vm-toreply");
10267  }
10268  if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10269  cmd = ast_play_and_wait(chan, "vm-tocallback");
10270  }
10271  if (!cmd && !vms.starting) {
10272  cmd = ast_play_and_wait(chan, "vm-tohearenv");
10273  }
10274  if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10275  cmd = ast_play_and_wait(chan, "vm-tomakecall");
10276  }
10277  if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10278  cmd = ast_play_and_wait(chan, "vm-leavemsg");
10279  }
10280  if (!cmd) {
10281  cmd = ast_play_and_wait(chan, "vm-starmain");
10282  }
10283  if (!cmd) {
10284  cmd = ast_waitfordigit(chan, 6000);
10285  }
10286  if (!cmd) {
10287  vms.repeats++;
10288  }
10289  if (vms.repeats > 3) {
10290  cmd = 't';
10291  }
10292  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10293  }
10294  }
10295  if (cmd == 't') {
10296  cmd = 0;
10297  vms.repeats = 0;
10298  }
10299  break;
10300  case '4': /* Go to the previous message */
10301  ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10302  if (vms.curmsg > 0) {
10303  vms.curmsg--;
10304  cmd = play_message(chan, vmu, &vms);
10305  } else {
10306  /* Check if we were listening to new
10307  messages. If so, go to Urgent messages
10308  instead of saying "no more messages"
10309  */
10310  if (in_urgent == 0 && vms.urgentmessages > 0) {
10311  /* Check for Urgent messages */
10312  in_urgent = 1;
10313  res = close_mailbox(&vms, vmu);
10314  if (res == ERROR_LOCK_PATH)
10315  goto out;
10316  res = open_mailbox(&vms, vmu, 11); /* Open Urgent folder */
10317  if (res < 0)
10318  goto out;
10319  ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10320  vms.curmsg = vms.lastmsg;
10321  if (vms.lastmsg < 0) {
10322  cmd = ast_play_and_wait(chan, "vm-nomore");
10323  }
10324  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10325  vms.curmsg = vms.lastmsg;
10326  cmd = play_message(chan, vmu, &vms);
10327  } else {
10328  cmd = ast_play_and_wait(chan, "vm-nomore");
10329  }
10330  }
10331  break;
10332  case '6': /* Go to the next message */
10333  ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10334  if (vms.curmsg < vms.lastmsg) {
10335  vms.curmsg++;
10336  cmd = play_message(chan, vmu, &vms);
10337  } else {
10338  if (in_urgent && vms.newmessages > 0) {
10339  /* Check if we were listening to urgent
10340  * messages. If so, go to regular new messages
10341  * instead of saying "no more messages"
10342  */
10343  in_urgent = 0;
10344  res = close_mailbox(&vms, vmu);
10345  if (res == ERROR_LOCK_PATH)
10346  goto out;
10347  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10348  if (res < 0)
10349  goto out;
10350  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10351  vms.curmsg = -1;
10352  if (vms.lastmsg < 0) {
10353  cmd = ast_play_and_wait(chan, "vm-nomore");
10354  }
10355  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10356  vms.curmsg = 0;
10357  cmd = play_message(chan, vmu, &vms);
10358  } else {
10359  cmd = ast_play_and_wait(chan, "vm-nomore");
10360  }
10361  }
10362  break;
10363  case '7': /* Delete the current message */
10364  if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10365  vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10366  if (useadsi)
10367  adsi_delete(chan, &vms);
10368  if (vms.deleted[vms.curmsg]) {
10369  if (play_folder == 0) {
10370  if (in_urgent) {
10371  vms.urgentmessages--;
10372  } else {
10373  vms.newmessages--;
10374  }
10375  }
10376  else if (play_folder == 1)
10377  vms.oldmessages--;
10378  cmd = ast_play_and_wait(chan, "vm-deleted");
10379  } else {
10380  if (play_folder == 0) {
10381  if (in_urgent) {
10382  vms.urgentmessages++;
10383  } else {
10384  vms.newmessages++;
10385  }
10386  }
10387  else if (play_folder == 1)
10388  vms.oldmessages++;
10389  cmd = ast_play_and_wait(chan, "vm-undeleted");
10390  }
10391  if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10392  if (vms.curmsg < vms.lastmsg) {
10393  vms.curmsg++;
10394  cmd = play_message(chan, vmu, &vms);
10395  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10396  vms.curmsg = 0;
10397  cmd = play_message(chan, vmu, &vms);
10398  } else {
10399  /* Check if we were listening to urgent
10400  messages. If so, go to regular new messages
10401  instead of saying "no more messages"
10402  */
10403  if (in_urgent == 1) {
10404  /* Check for new messages */
10405  in_urgent = 0;
10406  res = close_mailbox(&vms, vmu);
10407  if (res == ERROR_LOCK_PATH)
10408  goto out;
10409  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10410  if (res < 0)
10411  goto out;
10412  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10413  vms.curmsg = -1;
10414  if (vms.lastmsg < 0) {
10415  cmd = ast_play_and_wait(chan, "vm-nomore");
10416  }
10417  } else {
10418  cmd = ast_play_and_wait(chan, "vm-nomore");
10419  }
10420  }
10421  }
10422  } else /* Delete not valid if we haven't selected a message */
10423  cmd = 0;
10424 #ifdef IMAP_STORAGE
10425  deleted = 1;
10426 #endif
10427  break;
10428 
10429  case '8': /* Forward the current message */
10430  if (vms.lastmsg > -1) {
10431  cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10432  if (cmd == ERROR_LOCK_PATH) {
10433  res = cmd;
10434  goto out;
10435  }
10436  } else {
10437  /* Check if we were listening to urgent
10438  messages. If so, go to regular new messages
10439  instead of saying "no more messages"
10440  */
10441  if (in_urgent == 1 && vms.newmessages > 0) {
10442  /* Check for new messages */
10443  in_urgent = 0;
10444  res = close_mailbox(&vms, vmu);
10445  if (res == ERROR_LOCK_PATH)
10446  goto out;
10447  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10448  if (res < 0)
10449  goto out;
10450  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10451  vms.curmsg = -1;
10452  if (vms.lastmsg < 0) {
10453  cmd = ast_play_and_wait(chan, "vm-nomore");
10454  }
10455  } else {
10456  cmd = ast_play_and_wait(chan, "vm-nomore");
10457  }
10458  }
10459  break;
10460  case '9': /* Save message to folder */
10461  ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10462  if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10463  /* No message selected */
10464  cmd = 0;
10465  break;
10466  }
10467  if (useadsi)
10468  adsi_folders(chan, 1, "Save to folder...");
10469  cmd = get_folder2(chan, "vm-savefolder", 1);
10470  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10471  box = 0; /* Shut up compiler */
10472  if (cmd == '#') {
10473  cmd = 0;
10474  break;
10475  } else if (cmd > 0) {
10476  box = cmd = cmd - '0';
10477  cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10478  if (cmd == ERROR_LOCK_PATH) {
10479  res = cmd;
10480  goto out;
10481 #ifndef IMAP_STORAGE
10482  } else if (!cmd) {
10483  vms.deleted[vms.curmsg] = 1;
10484 #endif
10485  } else {
10486  vms.deleted[vms.curmsg] = 0;
10487  vms.heard[vms.curmsg] = 0;
10488  }
10489  }
10490  make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10491  if (useadsi)
10492  adsi_message(chan, &vms);
10493  snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10494  if (!cmd) {
10495  cmd = ast_play_and_wait(chan, "vm-message");
10496  if (!cmd)
10497  cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10498  if (!cmd)
10499  cmd = ast_play_and_wait(chan, "vm-savedto");
10500  if (!cmd)
10501  cmd = vm_play_folder_name(chan, vms.fn);
10502  } else {
10503  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10504  }
10505  if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10506  if (vms.curmsg < vms.lastmsg) {
10507  vms.curmsg++;
10508  cmd = play_message(chan, vmu, &vms);
10509  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10510  vms.curmsg = 0;
10511  cmd = play_message(chan, vmu, &vms);
10512  } else {
10513  /* Check if we were listening to urgent
10514  messages. If so, go to regular new messages
10515  instead of saying "no more messages"
10516  */
10517  if (in_urgent == 1 && vms.newmessages > 0) {
10518  /* Check for new messages */
10519  in_urgent = 0;
10520  res = close_mailbox(&vms, vmu);
10521  if (res == ERROR_LOCK_PATH)
10522  goto out;
10523  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10524  if (res < 0)
10525  goto out;
10526  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10527  vms.curmsg = -1;
10528  if (vms.lastmsg < 0) {
10529  cmd = ast_play_and_wait(chan, "vm-nomore");
10530  }
10531  } else {
10532  cmd = ast_play_and_wait(chan, "vm-nomore");
10533  }
10534  }
10535  }
10536  break;
10537  case '*': /* Help */
10538  if (!vms.starting) {
10539  cmd = ast_play_and_wait(chan, "vm-onefor");
10540  if (!strncasecmp(chan->language, "he", 2)) {
10541  cmd = ast_play_and_wait(chan, "vm-for");
10542  }
10543  if (!cmd)
10544  cmd = vm_play_folder_name(chan, vms.vmbox);
10545  if (!cmd)
10546  cmd = ast_play_and_wait(chan, "vm-opts");
10547  if (!cmd)
10548  cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10549  } else
10550  cmd = 0;
10551  break;
10552  case '0': /* Mailbox options */
10553  cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10554  if (useadsi)
10555  adsi_status(chan, &vms);
10556  break;
10557  default: /* Nothing */
10558  ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10559  cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10560  break;
10561  }
10562  }
10563  if ((cmd == 't') || (cmd == '#')) {
10564  /* Timeout */
10565  res = 0;
10566  } else {
10567  /* Hangup */
10568  res = -1;
10569  }
10570 
10571 out:
10572  if (res > -1) {
10573  ast_stopstream(chan);
10574  adsi_goodbye(chan);
10575  if (valid && res != OPERATOR_EXIT) {
10576  if (silentexit)
10577  res = ast_play_and_wait(chan, "vm-dialout");
10578  else
10579  res = ast_play_and_wait(chan, "vm-goodbye");
10580  }
10581  if ((valid && res > 0) || res == OPERATOR_EXIT) {
10582  res = 0;
10583  }
10584  if (useadsi)
10586  }
10587  if (vmu)
10588  close_mailbox(&vms, vmu);
10589  if (valid) {
10590  int new = 0, old = 0, urgent = 0;
10591  snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10592  ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10593  /* Urgent flag not passwd to externnotify here */
10594  run_externnotify(vmu->context, vmu->mailbox, NULL);
10595  ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10596  queue_mwi_event(ext_context, urgent, new, old);
10597  }
10598 #ifdef IMAP_STORAGE
10599  /* expunge message - use UID Expunge if supported on IMAP server*/
10600  ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10601  if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10602  ast_mutex_lock(&vms.lock);
10603 #ifdef HAVE_IMAP_TK2006
10604  if (LEVELUIDPLUS (vms.mailstream)) {
10605  mail_expunge_full(vms.mailstream, NIL, EX_UID);
10606  } else
10607 #endif
10608  mail_expunge(vms.mailstream);
10609  ast_mutex_unlock(&vms.lock);
10610  }
10611  /* before we delete the state, we should copy pertinent info
10612  * back to the persistent model */
10613  if (vmu) {
10614  vmstate_delete(&vms);
10615  }
10616 #endif
10617  if (vmu)
10618  free_user(vmu);
10619 
10620 #ifdef IMAP_STORAGE
10621  pthread_setspecific(ts_vmstate.key, NULL);
10622 #endif
10623  return res;
10624 }
10625 
10626 static int vm_exec(struct ast_channel *chan, const char *data)
10627 {
10628  int res = 0;
10629  char *tmp;
10630  struct leave_vm_options leave_options;
10631  struct ast_flags flags = { 0 };
10632  char *opts[OPT_ARG_ARRAY_SIZE];
10634  AST_APP_ARG(argv0);
10635  AST_APP_ARG(argv1);
10636  );
10637 
10638  memset(&leave_options, 0, sizeof(leave_options));
10639 
10640  if (chan->_state != AST_STATE_UP)
10641  ast_answer(chan);
10642 
10643  if (!ast_strlen_zero(data)) {
10644  tmp = ast_strdupa(data);
10646  if (args.argc == 2) {
10647  if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10648  return -1;
10650  if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10651  int gain;
10652 
10653  if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10654  ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10655  return -1;
10656  } else {
10657  leave_options.record_gain = (signed char) gain;
10658  }
10659  }
10660  if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10661  if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10662  leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10663  }
10664  }
10665  } else {
10666  char temp[256];
10667  res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10668  if (res < 0)
10669  return res;
10670  if (ast_strlen_zero(temp))
10671  return 0;
10672  args.argv0 = ast_strdupa(temp);
10673  }
10674 
10675  res = leave_voicemail(chan, args.argv0, &leave_options);
10676  if (res == 't') {
10677  ast_play_and_wait(chan, "vm-goodbye");
10678  res = 0;
10679  }
10680 
10681  if (res == OPERATOR_EXIT) {
10682  res = 0;
10683  }
10684 
10685  if (res == ERROR_LOCK_PATH) {
10686  ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10687  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10688  res = 0;
10689  }
10690 
10691  return res;
10692 }
10693 
10694 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10695 {
10696  struct ast_vm_user *vmu;
10697 
10698  if (!ast_strlen_zero(box) && box[0] == '*') {
10699  ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character. The '*' character,"
10700  "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10701  "\n\tpredefined extension 'a'. A mailbox or password beginning with '*' is not valid"
10702  "\n\tand will be ignored.\n", box, context);
10703  return NULL;
10704  }
10705 
10706  AST_LIST_TRAVERSE(&users, vmu, list) {
10707  if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10708  if (strcasecmp(vmu->context, context)) {
10709  ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10710  \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10711  \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10712  \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10713  }
10714  ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10715  return NULL;
10716  }
10717  if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10718  ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10719  return NULL;
10720  }
10721  }
10722 
10723  if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10724  return NULL;
10725 
10726  ast_copy_string(vmu->context, context, sizeof(vmu->context));
10727  ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10728 
10729  AST_LIST_INSERT_TAIL(&users, vmu, list);
10730 
10731  return vmu;
10732 }
10733 
10734 static int append_mailbox(const char *context, const char *box, const char *data)
10735 {
10736  /* Assumes lock is already held */
10737  char *tmp;
10738  char *stringp;
10739  char *s;
10740  struct ast_vm_user *vmu;
10741  char *mailbox_full;
10742  int new = 0, old = 0, urgent = 0;
10743  char secretfn[PATH_MAX] = "";
10744 
10745  tmp = ast_strdupa(data);
10746 
10747  if (!(vmu = find_or_create(context, box)))
10748  return -1;
10749 
10750  populate_defaults(vmu);
10751 
10752  stringp = tmp;
10753  if ((s = strsep(&stringp, ","))) {
10754  if (!ast_strlen_zero(s) && s[0] == '*') {
10755  ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
10756  "\n\tmust be reset in voicemail.conf.\n", box);
10757  }
10758  /* assign password regardless of validity to prevent NULL password from being assigned */
10759  ast_copy_string(vmu->password, s, sizeof(vmu->password));
10760  }
10761  if (stringp && (s = strsep(&stringp, ","))) {
10762  ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10763  }
10764  if (stringp && (s = strsep(&stringp, ","))) {
10765  ast_copy_string(vmu->email, s, sizeof(vmu->email));
10766  }
10767  if (stringp && (s = strsep(&stringp, ","))) {
10768  ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10769  }
10770  if (stringp && (s = strsep(&stringp, ","))) {
10771  apply_options(vmu, s);
10772  }
10773 
10774  switch (vmu->passwordlocation) {
10775  case OPT_PWLOC_SPOOLDIR:
10776  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10777  read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10778  }
10779 
10780  mailbox_full = ast_alloca(strlen(box) + strlen(context) + 1);
10781  strcpy(mailbox_full, box);
10782  strcat(mailbox_full, "@");
10783  strcat(mailbox_full, context);
10784 
10785  inboxcount2(mailbox_full, &urgent, &new, &old);
10786  queue_mwi_event(mailbox_full, urgent, new, old);
10787 
10788  return 0;
10789 }
10790 
10791 AST_TEST_DEFINE(test_voicemail_vmuser)
10792 {
10793  int res = 0;
10794  struct ast_vm_user *vmu;
10795  /* language parameter seems to only be used for display in manager action */
10796  static const char options_string[] = "attach=yes|attachfmt=wav49|"
10797  "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10798  "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10799  "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10800  "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10801  "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10802  "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10803  "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10804  "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10805 #ifdef IMAP_STORAGE
10806  static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10807  "imapfolder=INBOX|imapvmshareid=6000";
10808 #endif
10809 
10810  switch (cmd) {
10811  case TEST_INIT:
10812  info->name = "vmuser";
10813  info->category = "/apps/app_voicemail/";
10814  info->summary = "Vmuser unit test";
10815  info->description =
10816  "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10817  return AST_TEST_NOT_RUN;
10818  case TEST_EXECUTE:
10819  break;
10820  }
10821 
10822  if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10823  return AST_TEST_NOT_RUN;
10824  }
10825  populate_defaults(vmu);
10826  ast_set_flag(vmu, VM_ALLOCED);
10827 
10828  apply_options(vmu, options_string);
10829 
10830  if (!ast_test_flag(vmu, VM_ATTACH)) {
10831  ast_test_status_update(test, "Parse failure for attach option\n");
10832  res = 1;
10833  }
10834  if (strcasecmp(vmu->attachfmt, "wav49")) {
10835  ast_test_status_update(test, "Parse failure for attachftm option\n");
10836  res = 1;
10837  }
10838  if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10839  ast_test_status_update(test, "Parse failure for serveremail option\n");
10840  res = 1;
10841  }
10842  if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10843  ast_test_status_update(test, "Parse failure for emailsubject option\n");
10844  res = 1;
10845  }
10846  if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10847  ast_test_status_update(test, "Parse failure for emailbody option\n");
10848  res = 1;
10849  }
10850  if (strcasecmp(vmu->zonetag, "central")) {
10851  ast_test_status_update(test, "Parse failure for tz option\n");
10852  res = 1;
10853  }
10854  if (!ast_test_flag(vmu, VM_DELETE)) {
10855  ast_test_status_update(test, "Parse failure for delete option\n");
10856  res = 1;
10857  }
10858  if (!ast_test_flag(vmu, VM_SAYCID)) {
10859  ast_test_status_update(test, "Parse failure for saycid option\n");
10860  res = 1;
10861  }
10862  if (!ast_test_flag(vmu, VM_SVMAIL)) {
10863  ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10864  res = 1;
10865  }
10866  if (!ast_test_flag(vmu, VM_REVIEW)) {
10867  ast_test_status_update(test, "Parse failure for review option\n");
10868  res = 1;
10869  }
10870  if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10871  ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10872  res = 1;
10873  }
10874  if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10875  ast_test_status_update(test, "Parse failure for messagewrap option\n");
10876  res = 1;
10877  }
10878  if (!ast_test_flag(vmu, VM_OPERATOR)) {
10879  ast_test_status_update(test, "Parse failure for operator option\n");
10880  res = 1;
10881  }
10882  if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10883  ast_test_status_update(test, "Parse failure for envelope option\n");
10884  res = 1;
10885  }
10886  if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10887  ast_test_status_update(test, "Parse failure for moveheard option\n");
10888  res = 1;
10889  }
10890  if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10891  ast_test_status_update(test, "Parse failure for sayduration option\n");
10892  res = 1;
10893  }
10894  if (vmu->saydurationm != 5) {
10895  ast_test_status_update(test, "Parse failure for saydurationm option\n");
10896  res = 1;
10897  }
10898  if (!ast_test_flag(vmu, VM_FORCENAME)) {
10899  ast_test_status_update(test, "Parse failure for forcename option\n");
10900  res = 1;
10901  }
10902  if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10903  ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10904  res = 1;
10905  }
10906  if (strcasecmp(vmu->callback, "somecontext")) {
10907  ast_test_status_update(test, "Parse failure for callbacks option\n");
10908  res = 1;
10909  }
10910  if (strcasecmp(vmu->dialout, "somecontext2")) {
10911  ast_test_status_update(test, "Parse failure for dialout option\n");
10912  res = 1;
10913  }
10914  if (strcasecmp(vmu->exit, "somecontext3")) {
10915  ast_test_status_update(test, "Parse failure for exitcontext option\n");
10916  res = 1;
10917  }
10918  if (vmu->minsecs != 10) {
10919  ast_test_status_update(test, "Parse failure for minsecs option\n");
10920  res = 1;
10921  }
10922  if (vmu->maxsecs != 100) {
10923  ast_test_status_update(test, "Parse failure for maxsecs option\n");
10924  res = 1;
10925  }
10926  if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10927  ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10928  res = 1;
10929  }
10930  if (vmu->maxdeletedmsg != 50) {
10931  ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10932  res = 1;
10933  }
10934  if (vmu->volgain != 1.3) {
10935  ast_test_status_update(test, "Parse failure for volgain option\n");
10936  res = 1;
10937  }
10938  if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10939  ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10940  res = 1;
10941  }
10942 #ifdef IMAP_STORAGE
10943  apply_options(vmu, option_string2);
10944 
10945  if (strcasecmp(vmu->imapuser, "imapuser")) {
10946  ast_test_status_update(test, "Parse failure for imapuser option\n");
10947  res = 1;
10948  }
10949  if (strcasecmp(vmu->imappassword, "imappasswd")) {
10950  ast_test_status_update(test, "Parse failure for imappasswd option\n");
10951  res = 1;
10952  }
10953  if (strcasecmp(vmu->imapfolder, "INBOX")) {
10954  ast_test_status_update(test, "Parse failure for imapfolder option\n");
10955  res = 1;
10956  }
10957  if (strcasecmp(vmu->imapvmshareid, "6000")) {
10958  ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10959  res = 1;
10960  }
10961 #endif
10962 
10963  free_user(vmu);
10964  return res ? AST_TEST_FAIL : AST_TEST_PASS;
10965 }
10966 
10967 static int vm_box_exists(struct ast_channel *chan, const char *data)
10968 {
10969  struct ast_vm_user svm;
10970  char *context, *box;
10972  AST_APP_ARG(mbox);
10973  AST_APP_ARG(options);
10974  );
10975  static int dep_warning = 0;
10976 
10977  if (ast_strlen_zero(data)) {
10978  ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10979  return -1;
10980  }
10981 
10982  if (!dep_warning) {
10983  dep_warning = 1;
10984  ast_log(AST_LOG_WARNING, "MailboxExists is deprecated. Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10985  }
10986 
10987  box = ast_strdupa(data);
10988 
10990 
10991  if (args.options) {
10992  }
10993 
10994  if ((context = strchr(args.mbox, '@'))) {
10995  *context = '\0';
10996  context++;
10997  }
10998 
10999  if (find_user(&svm, context, args.mbox)) {
11000  pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
11001  } else
11002  pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
11003 
11004  return 0;
11005 }
11006 
11007 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
11008 {
11009  struct ast_vm_user svm;
11011  AST_APP_ARG(mbox);
11012  AST_APP_ARG(context);
11013  );
11014 
11015  AST_NONSTANDARD_APP_ARGS(arg, args, '@');
11016 
11017  if (ast_strlen_zero(arg.mbox)) {
11018  ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
11019  return -1;
11020  }
11021 
11022  ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
11023  return 0;
11024 }
11025 
11027  .name = "MAILBOX_EXISTS",
11028  .read = acf_mailbox_exists,
11029 };
11030 
11031 static int vmauthenticate(struct ast_channel *chan, const char *data)
11032 {
11033  char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11034  struct ast_vm_user vmus;
11035  char *options = NULL;
11036  int silent = 0, skipuser = 0;
11037  int res = -1;
11038 
11039  if (data) {
11040  s = ast_strdupa(data);
11041  user = strsep(&s, ",");
11042  options = strsep(&s, ",");
11043  if (user) {
11044  s = user;
11045  user = strsep(&s, "@");
11046  context = strsep(&s, "");
11047  if (!ast_strlen_zero(user))
11048  skipuser++;
11049  ast_copy_string(mailbox, user, sizeof(mailbox));
11050  }
11051  }
11052 
11053  if (options) {
11054  silent = (strchr(options, 's')) != NULL;
11055  }
11056 
11057  if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11058  pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11059  pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11060  ast_play_and_wait(chan, "auth-thankyou");
11061  res = 0;
11062  } else if (mailbox[0] == '*') {
11063  /* user entered '*' */
11064  if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11065  res = 0; /* prevent hangup */
11066  }
11067  }
11068 
11069  return res;
11070 }
11071 
11072 static char *show_users_realtime(int fd, const char *context)
11073 {
11074  struct ast_config *cfg;
11075  const char *cat = NULL;
11076 
11077  if (!(cfg = ast_load_realtime_multientry("voicemail",
11078  "context", context, SENTINEL))) {
11079  return CLI_FAILURE;
11080  }
11081 
11082  ast_cli(fd,
11083  "\n"
11084  "=============================================================\n"
11085  "=== Configured Voicemail Users ==============================\n"
11086  "=============================================================\n"
11087  "===\n");
11088 
11089  while ((cat = ast_category_browse(cfg, cat))) {
11090  struct ast_variable *var = NULL;
11091  ast_cli(fd,
11092  "=== Mailbox ...\n"
11093  "===\n");
11094  for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11095  ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11096  ast_cli(fd,
11097  "===\n"
11098  "=== ---------------------------------------------------------\n"
11099  "===\n");
11100  }
11101 
11102  ast_cli(fd,
11103  "=============================================================\n"
11104  "\n");
11105 
11106  ast_config_destroy(cfg);
11107 
11108  return CLI_SUCCESS;
11109 }
11110 
11111 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11112 {
11113  int which = 0;
11114  int wordlen;
11115  struct ast_vm_user *vmu;
11116  const char *context = "";
11117 
11118  /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11119  if (pos > 4)
11120  return NULL;
11121  if (pos == 3)
11122  return (state == 0) ? ast_strdup("for") : NULL;
11123  wordlen = strlen(word);
11124  AST_LIST_TRAVERSE(&users, vmu, list) {
11125  if (!strncasecmp(word, vmu->context, wordlen)) {
11126  if (context && strcmp(context, vmu->context) && ++which > state)
11127  return ast_strdup(vmu->context);
11128  /* ignore repeated contexts ? */
11129  context = vmu->context;
11130  }
11131  }
11132  return NULL;
11133 }
11134 
11135 /*! \brief Show a list of voicemail users in the CLI */
11136 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11137 {
11138  struct ast_vm_user *vmu;
11139 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11140  const char *context = NULL;
11141  int users_counter = 0;
11142 
11143  switch (cmd) {
11144  case CLI_INIT:
11145  e->command = "voicemail show users";
11146  e->usage =
11147  "Usage: voicemail show users [for <context>]\n"
11148  " Lists all mailboxes currently set up\n";
11149  return NULL;
11150  case CLI_GENERATE:
11151  return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11152  }
11153 
11154  if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11155  return CLI_SHOWUSAGE;
11156  if (a->argc == 5) {
11157  if (strcmp(a->argv[3],"for"))
11158  return CLI_SHOWUSAGE;
11159  context = a->argv[4];
11160  }
11161 
11162  if (ast_check_realtime("voicemail")) {
11163  if (!context) {
11164  ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11165  return CLI_SHOWUSAGE;
11166  }
11167  return show_users_realtime(a->fd, context);
11168  }
11169 
11170  AST_LIST_LOCK(&users);
11171  if (AST_LIST_EMPTY(&users)) {
11172  ast_cli(a->fd, "There are no voicemail users currently defined\n");
11174  return CLI_FAILURE;
11175  }
11176  if (!context) {
11177  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11178  } else {
11179  int count = 0;
11180  AST_LIST_TRAVERSE(&users, vmu, list) {
11181  if (!strcmp(context, vmu->context)) {
11182  count++;
11183  break;
11184  }
11185  }
11186  if (count) {
11187  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11188  } else {
11189  ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11191  return CLI_FAILURE;
11192  }
11193  }
11194  AST_LIST_TRAVERSE(&users, vmu, list) {
11195  int newmsgs = 0, oldmsgs = 0;
11196  char count[12], tmp[256] = "";
11197 
11198  if (!context || !strcmp(context, vmu->context)) {
11199  snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11200  inboxcount(tmp, &newmsgs, &oldmsgs);
11201  snprintf(count, sizeof(count), "%d", newmsgs);
11202  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11203  users_counter++;
11204  }
11205  }
11207  ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11208  return CLI_SUCCESS;
11209 }
11210 
11211 /*! \brief Show a list of voicemail zones in the CLI */
11212 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11213 {
11214  struct vm_zone *zone;
11215 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11216  char *res = CLI_SUCCESS;
11217 
11218  switch (cmd) {
11219  case CLI_INIT:
11220  e->command = "voicemail show zones";
11221  e->usage =
11222  "Usage: voicemail show zones\n"
11223  " Lists zone message formats\n";
11224  return NULL;
11225  case CLI_GENERATE:
11226  return NULL;
11227  }
11228 
11229  if (a->argc != 3)
11230  return CLI_SHOWUSAGE;
11231 
11232  AST_LIST_LOCK(&zones);
11233  if (!AST_LIST_EMPTY(&zones)) {
11234  ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11235  AST_LIST_TRAVERSE(&zones, zone, list) {
11236  ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11237  }
11238  } else {
11239  ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11240  res = CLI_FAILURE;
11241  }
11243 
11244  return res;
11245 }
11246 
11247 /*! \brief Reload voicemail configuration from the CLI */
11248 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11249 {
11250  switch (cmd) {
11251  case CLI_INIT:
11252  e->command = "voicemail reload";
11253  e->usage =
11254  "Usage: voicemail reload\n"
11255  " Reload voicemail configuration\n";
11256  return NULL;
11257  case CLI_GENERATE:
11258  return NULL;
11259  }
11260 
11261  if (a->argc != 2)
11262  return CLI_SHOWUSAGE;
11263 
11264  ast_cli(a->fd, "Reloading voicemail configuration...\n");
11265  load_config(1);
11266 
11267  return CLI_SUCCESS;
11268 }
11269 
11270 static struct ast_cli_entry cli_voicemail[] = {
11271  AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11272  AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11273  AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11274 };
11275 
11276 #ifdef IMAP_STORAGE
11277  #define DATA_EXPORT_VM_USERS(USER) \
11278  USER(ast_vm_user, context, AST_DATA_STRING) \
11279  USER(ast_vm_user, mailbox, AST_DATA_STRING) \
11280  USER(ast_vm_user, password, AST_DATA_PASSWORD) \
11281  USER(ast_vm_user, fullname, AST_DATA_STRING) \
11282  USER(ast_vm_user, email, AST_DATA_STRING) \
11283  USER(ast_vm_user, emailsubject, AST_DATA_STRING) \
11284  USER(ast_vm_user, emailbody, AST_DATA_STRING) \
11285  USER(ast_vm_user, pager, AST_DATA_STRING) \
11286  USER(ast_vm_user, serveremail, AST_DATA_STRING) \
11287  USER(ast_vm_user, language, AST_DATA_STRING) \
11288  USER(ast_vm_user, zonetag, AST_DATA_STRING) \
11289  USER(ast_vm_user, callback, AST_DATA_STRING) \
11290  USER(ast_vm_user, dialout, AST_DATA_STRING) \
11291  USER(ast_vm_user, uniqueid, AST_DATA_STRING) \
11292  USER(ast_vm_user, exit, AST_DATA_STRING) \
11293  USER(ast_vm_user, attachfmt, AST_DATA_STRING) \
11294  USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER) \
11295  USER(ast_vm_user, saydurationm, AST_DATA_INTEGER) \
11296  USER(ast_vm_user, maxmsg, AST_DATA_INTEGER) \
11297  USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER) \
11298  USER(ast_vm_user, maxsecs, AST_DATA_INTEGER) \
11299  USER(ast_vm_user, imapuser, AST_DATA_STRING) \
11300  USER(ast_vm_user, imappassword, AST_DATA_STRING) \
11301  USER(ast_vm_user, imapvmshareid, AST_DATA_STRING) \
11302  USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11303 #else
11304  #define DATA_EXPORT_VM_USERS(USER) \
11305  USER(ast_vm_user, context, AST_DATA_STRING) \
11306  USER(ast_vm_user, mailbox, AST_DATA_STRING) \
11307  USER(ast_vm_user, password, AST_DATA_PASSWORD) \
11308  USER(ast_vm_user, fullname, AST_DATA_STRING) \
11309  USER(ast_vm_user, email, AST_DATA_STRING) \
11310  USER(ast_vm_user, emailsubject, AST_DATA_STRING) \
11311  USER(ast_vm_user, emailbody, AST_DATA_STRING) \
11312  USER(ast_vm_user, pager, AST_DATA_STRING) \
11313  USER(ast_vm_user, serveremail, AST_DATA_STRING) \
11314  USER(ast_vm_user, language, AST_DATA_STRING) \
11315  USER(ast_vm_user, zonetag, AST_DATA_STRING) \
11316  USER(ast_vm_user, callback, AST_DATA_STRING) \
11317  USER(ast_vm_user, dialout, AST_DATA_STRING) \
11318  USER(ast_vm_user, uniqueid, AST_DATA_STRING) \
11319  USER(ast_vm_user, exit, AST_DATA_STRING) \
11320  USER(ast_vm_user, attachfmt, AST_DATA_STRING) \
11321  USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER) \
11322  USER(ast_vm_user, saydurationm, AST_DATA_INTEGER) \
11323  USER(ast_vm_user, maxmsg, AST_DATA_INTEGER) \
11324  USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER) \
11325  USER(ast_vm_user, maxsecs, AST_DATA_INTEGER) \
11326  USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11327 #endif
11328 
11330 
11331 #define DATA_EXPORT_VM_ZONES(ZONE) \
11332  ZONE(vm_zone, name, AST_DATA_STRING) \
11333  ZONE(vm_zone, timezone, AST_DATA_STRING) \
11334  ZONE(vm_zone, msg_format, AST_DATA_STRING)
11335 
11337 
11338 /*!
11339  * \internal
11340  * \brief Add voicemail user to the data_root.
11341  * \param[in] search The search tree.
11342  * \param[in] data_root The main result node.
11343  * \param[in] user The voicemail user.
11344  */
11345 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11346  struct ast_data *data_root, struct ast_vm_user *user)
11347 {
11348  struct ast_data *data_user, *data_zone;
11349  struct ast_data *data_state;
11350  struct vm_zone *zone = NULL;
11351  int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11352  char ext_context[256] = "";
11353 
11354  data_user = ast_data_add_node(data_root, "user");
11355  if (!data_user) {
11356  return -1;
11357  }
11358 
11359  ast_data_add_structure(ast_vm_user, data_user, user);
11360 
11361  AST_LIST_LOCK(&zones);
11362  AST_LIST_TRAVERSE(&zones, zone, list) {
11363  if (!strcmp(zone->name, user->zonetag)) {
11364  break;
11365  }
11366  }
11368 
11369  /* state */
11370  data_state = ast_data_add_node(data_user, "state");
11371  if (!data_state) {
11372  return -1;
11373  }
11374  snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11375  inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11376  ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11377  ast_data_add_int(data_state, "newmsg", newmsg);
11378  ast_data_add_int(data_state, "oldmsg", oldmsg);
11379 
11380  if (zone) {
11381  data_zone = ast_data_add_node(data_user, "zone");
11382  ast_data_add_structure(vm_zone, data_zone, zone);
11383  }
11384 
11385  if (!ast_data_search_match(search, data_user)) {
11386  ast_data_remove_node(data_root, data_user);
11387  }
11388 
11389  return 0;
11390 }
11391 
11392 static int vm_users_data_provider_get(const struct ast_data_search *search,
11393  struct ast_data *data_root)
11394 {
11395  struct ast_vm_user *user;
11396 
11397  AST_LIST_LOCK(&users);
11398  AST_LIST_TRAVERSE(&users, user, list) {
11399  vm_users_data_provider_get_helper(search, data_root, user);
11400  }
11402 
11403  return 0;
11404 }
11405 
11409 };
11410 
11411 static const struct ast_data_entry vm_data_providers[] = {
11412  AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11413 };
11414 
11416 {
11417  int new = 0, old = 0, urgent = 0;
11418 
11419  inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11420 
11421  if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11422  mwi_sub->old_urgent = urgent;
11423  mwi_sub->old_new = new;
11424  mwi_sub->old_old = old;
11425  queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11426  run_externnotify(NULL, mwi_sub->mailbox, NULL);
11427  }
11428 }
11429 
11430 static void poll_subscribed_mailboxes(void)
11431 {
11432  struct mwi_sub *mwi_sub;
11433 
11435  AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11436  if (!ast_strlen_zero(mwi_sub->mailbox)) {
11437  poll_subscribed_mailbox(mwi_sub);
11438  }
11439  }
11441 }
11442 
11443 static void *mb_poll_thread(void *data)
11444 {
11445  while (poll_thread_run) {
11446  struct timespec ts = { 0, };
11447  struct timeval wait;
11448 
11449  wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11450  ts.tv_sec = wait.tv_sec;
11451  ts.tv_nsec = wait.tv_usec * 1000;
11452 
11454  ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11456 
11457  if (!poll_thread_run)
11458  break;
11459 
11461  }
11462 
11463  return NULL;
11464 }
11465 
11466 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11467 {
11468  ast_free(mwi_sub);
11469 }
11470 
11471 static int handle_unsubscribe(void *datap)
11472 {
11473  struct mwi_sub *mwi_sub;
11474  uint32_t *uniqueid = datap;
11475 
11477  AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11478  if (mwi_sub->uniqueid == *uniqueid) {
11479  AST_LIST_REMOVE_CURRENT(entry);
11480  break;
11481  }
11482  }
11485 
11486  if (mwi_sub)
11487  mwi_sub_destroy(mwi_sub);
11488 
11489  ast_free(uniqueid);
11490  return 0;
11491 }
11492 
11493 static int handle_subscribe(void *datap)
11494 {
11495  unsigned int len;
11496  struct mwi_sub *mwi_sub;
11497  struct mwi_sub_task *p = datap;
11498 
11499  len = sizeof(*mwi_sub);
11500  if (!ast_strlen_zero(p->mailbox))
11501  len += strlen(p->mailbox);
11502 
11503  if (!ast_strlen_zero(p->context))
11504  len += strlen(p->context) + 1; /* Allow for seperator */
11505 
11506  if (!(mwi_sub = ast_calloc(1, len)))
11507  return -1;
11508 
11509  mwi_sub->uniqueid = p->uniqueid;
11510  if (!ast_strlen_zero(p->mailbox))
11511  strcpy(mwi_sub->mailbox, p->mailbox);
11512 
11513  if (!ast_strlen_zero(p->context)) {
11514  strcat(mwi_sub->mailbox, "@");
11515  strcat(mwi_sub->mailbox, p->context);
11516  }
11517 
11519  AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11521  ast_free((void *) p->mailbox);
11522  ast_free((void *) p->context);
11523  ast_free(p);
11524  poll_subscribed_mailbox(mwi_sub);
11525  return 0;
11526 }
11527 
11528 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11529 {
11530  uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11531 
11532  if (!uniqueid) {
11533  ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11534  return;
11535  }
11536 
11537  if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11538  ast_free(uniqueid);
11539  return;
11540  }
11541 
11543  ast_free(uniqueid);
11544  return;
11545  }
11546 
11548  *uniqueid = u;
11549  if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11550  ast_free(uniqueid);
11551  }
11552 }
11553 
11554 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11555 {
11556  struct mwi_sub_task *mwist;
11557 
11558  if (ast_event_get_type(event) != AST_EVENT_SUB)
11559  return;
11560 
11562  return;
11563 
11564  if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11565  ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11566  return;
11567  }
11571 
11572  if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11573  ast_free(mwist);
11574  }
11575 }
11576 
11577 static void start_poll_thread(void)
11578 {
11579  int errcode;
11580  mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11583 
11584  mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11587 
11588  if (mwi_sub_sub)
11589  ast_event_report_subs(mwi_sub_sub);
11590 
11591  poll_thread_run = 1;
11592 
11593  if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11594  ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11595  }
11596 }
11597 
11598 static void stop_poll_thread(void)
11599 {
11600  poll_thread_run = 0;
11601 
11602  if (mwi_sub_sub) {
11603  ast_event_unsubscribe(mwi_sub_sub);
11604  mwi_sub_sub = NULL;
11605  }
11606 
11607  if (mwi_unsub_sub) {
11608  ast_event_unsubscribe(mwi_unsub_sub);
11609  mwi_unsub_sub = NULL;
11610  }
11611 
11613  ast_cond_signal(&poll_cond);
11615 
11616  pthread_join(poll_thread, NULL);
11617 
11618  poll_thread = AST_PTHREADT_NULL;
11619 }
11620 
11621 /*! \brief Manager list voicemail users command */
11622 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11623 {
11624  struct ast_vm_user *vmu = NULL;
11625  const char *id = astman_get_header(m, "ActionID");
11626  char actionid[128] = "";
11627 
11628  if (!ast_strlen_zero(id))
11629  snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11630 
11631  AST_LIST_LOCK(&users);
11632 
11633  if (AST_LIST_EMPTY(&users)) {
11634  astman_send_ack(s, m, "There are no voicemail users currently defined.");
11636  return RESULT_SUCCESS;
11637  }
11638 
11639  astman_send_ack(s, m, "Voicemail user list will follow");
11640 
11641  AST_LIST_TRAVERSE(&users, vmu, list) {
11642  char dirname[256];
11643 
11644 #ifdef IMAP_STORAGE
11645  int new, old;
11646  inboxcount(vmu->mailbox, &new, &old);
11647 #endif
11648 
11649  make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11650  astman_append(s,
11651  "%s"
11652  "Event: VoicemailUserEntry\r\n"
11653  "VMContext: %s\r\n"
11654  "VoiceMailbox: %s\r\n"
11655  "Fullname: %s\r\n"
11656  "Email: %s\r\n"
11657  "Pager: %s\r\n"
11658  "ServerEmail: %s\r\n"
11659  "MailCommand: %s\r\n"
11660  "Language: %s\r\n"
11661  "TimeZone: %s\r\n"
11662  "Callback: %s\r\n"
11663  "Dialout: %s\r\n"
11664  "UniqueID: %s\r\n"
11665  "ExitContext: %s\r\n"
11666  "SayDurationMinimum: %d\r\n"
11667  "SayEnvelope: %s\r\n"
11668  "SayCID: %s\r\n"
11669  "AttachMessage: %s\r\n"
11670  "AttachmentFormat: %s\r\n"
11671  "DeleteMessage: %s\r\n"
11672  "VolumeGain: %.2f\r\n"
11673  "CanReview: %s\r\n"
11674  "CallOperator: %s\r\n"
11675  "MaxMessageCount: %d\r\n"
11676  "MaxMessageLength: %d\r\n"
11677  "NewMessageCount: %d\r\n"
11678 #ifdef IMAP_STORAGE
11679  "OldMessageCount: %d\r\n"
11680  "IMAPUser: %s\r\n"
11681 #endif
11682  "\r\n",
11683  actionid,
11684  vmu->context,
11685  vmu->mailbox,
11686  vmu->fullname,
11687  vmu->email,
11688  vmu->pager,
11689  ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
11690  mailcmd,
11691  vmu->language,
11692  vmu->zonetag,
11693  vmu->callback,
11694  vmu->dialout,
11695  vmu->uniqueid,
11696  vmu->exit,
11697  vmu->saydurationm,
11698  ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11699  ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11700  ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11701  vmu->attachfmt,
11702  ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11703  vmu->volgain,
11704  ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11705  ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11706  vmu->maxmsg,
11707  vmu->maxsecs,
11708 #ifdef IMAP_STORAGE
11709  new, old, vmu->imapuser
11710 #else
11711  count_messages(vmu, dirname)
11712 #endif
11713  );
11714  }
11715  astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11716 
11718 
11719  return RESULT_SUCCESS;
11720 }
11721 
11722 /*! \brief Free the users structure. */
11723 static void free_vm_users(void)
11724 {
11725  struct ast_vm_user *current;
11726  AST_LIST_LOCK(&users);
11727  while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11728  ast_set_flag(current, VM_ALLOCED);
11729  free_user(current);
11730  }
11732 }
11733 
11734 /*! \brief Free the zones structure. */
11735 static void free_vm_zones(void)
11736 {
11737  struct vm_zone *zcur;
11738  AST_LIST_LOCK(&zones);
11739  while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11740  free_zone(zcur);
11742 }
11743 
11744 static const char *substitute_escapes(const char *value)
11745 {
11746  char *current;
11747 
11748  /* Add 16 for fudge factor */
11749  struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11750 
11751  ast_str_reset(str);
11752 
11753  /* Substitute strings \r, \n, and \t into the appropriate characters */
11754  for (current = (char *) value; *current; current++) {
11755  if (*current == '\\') {
11756  current++;
11757  if (!*current) {
11758  ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11759  break;
11760  }
11761  switch (*current) {
11762  case '\\':
11763  ast_str_append(&str, 0, "\\");
11764  break;
11765  case 'r':
11766  ast_str_append(&str, 0, "\r");
11767  break;
11768  case 'n':
11769 #ifdef IMAP_STORAGE
11770  if (!str->used || str->str[str->used - 1] != '\r') {
11771  ast_str_append(&str, 0, "\r");
11772  }
11773 #endif
11774  ast_str_append(&str, 0, "\n");
11775  break;
11776  case 't':
11777  ast_str_append(&str, 0, "\t");
11778  break;
11779  default:
11780  ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11781  break;
11782  }
11783  } else {
11784  ast_str_append(&str, 0, "%c", *current);
11785  }
11786  }
11787 
11788  return ast_str_buffer(str);
11789 }
11790 
11791 static int load_config(int reload)
11792 {
11793  struct ast_config *cfg, *ucfg;
11794  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11795  int res;
11796 
11797  ast_unload_realtime("voicemail");
11798  ast_unload_realtime("voicemail_data");
11799 
11800  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11801  if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11802  return 0;
11803  } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11804  ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
11805  ucfg = NULL;
11806  }
11807  ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11808  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11809  ast_config_destroy(ucfg);
11810  ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
11811  return 0;
11812  }
11813  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11814  ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
11815  return 0;
11816  } else {
11817  ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11818  if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11819  ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
11820  ucfg = NULL;
11821  }
11822  }
11823 
11824  res = actual_load_config(reload, cfg, ucfg);
11825 
11826  ast_config_destroy(cfg);
11827  ast_config_destroy(ucfg);
11828 
11829  return res;
11830 }
11831 
11832 #ifdef TEST_FRAMEWORK
11833 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11834 {
11835  ast_unload_realtime("voicemail");
11836  ast_unload_realtime("voicemail_data");
11837  return actual_load_config(reload, cfg, ucfg);
11838 }
11839 #endif
11840 
11841 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11842 {
11843  struct ast_vm_user *current;
11844  char *cat;
11845  struct ast_variable *var;
11846  const char *val;
11847  char *q, *stringp, *tmp;
11848  int x;
11849  unsigned int tmpadsi[4];
11850  char secretfn[PATH_MAX] = "";
11851 
11852 #ifdef IMAP_STORAGE
11853  ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11854 #endif
11855  /* set audio control prompts */
11856  strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11857  strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11858  strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11859  strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11860  strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11861 
11862  /* Free all the users structure */
11863  free_vm_users();
11864 
11865  /* Free all the zones structure */
11866  free_vm_zones();
11867 
11868  AST_LIST_LOCK(&users);
11869 
11870  memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11871  memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11872 
11873  if (cfg) {
11874  /* General settings */
11875 
11876  if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11877  val = "default";
11878  ast_copy_string(userscontext, val, sizeof(userscontext));
11879  /* Attach voice message to mail message ? */
11880  if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
11881  val = "yes";
11882  ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH);
11883 
11884  if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11885  val = "no";
11886  ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11887 
11888  volgain = 0.0;
11889  if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11890  sscanf(val, "%30lf", &volgain);
11891 
11892 #ifdef ODBC_STORAGE
11893  strcpy(odbc_database, "asterisk");
11894  if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11895  ast_copy_string(odbc_database, val, sizeof(odbc_database));
11896  }
11897  strcpy(odbc_table, "voicemessages");
11898  if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11899  ast_copy_string(odbc_table, val, sizeof(odbc_table));
11900  }
11901 #endif
11902  /* Mail command */
11903  strcpy(mailcmd, SENDMAIL);
11904  if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11905  ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11906 
11907  maxsilence = 0;
11908  if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11909  maxsilence = atoi(val);
11910  if (maxsilence > 0)
11911  maxsilence *= 1000;
11912  }
11913 
11914  if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11915  maxmsg = MAXMSG;
11916  } else {
11917  maxmsg = atoi(val);
11918  if (maxmsg < 0) {
11919  ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11920  maxmsg = MAXMSG;
11921  } else if (maxmsg > MAXMSGLIMIT) {
11922  ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11923  maxmsg = MAXMSGLIMIT;
11924  }
11925  }
11926 
11927  if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11928  maxdeletedmsg = 0;
11929  } else {
11930  if (sscanf(val, "%30d", &x) == 1)
11931  maxdeletedmsg = x;
11932  else if (ast_true(val))
11933  maxdeletedmsg = MAXMSG;
11934  else
11935  maxdeletedmsg = 0;
11936 
11937  if (maxdeletedmsg < 0) {
11938  ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11939  maxdeletedmsg = MAXMSG;
11940  } else if (maxdeletedmsg > MAXMSGLIMIT) {
11941  ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11942  maxdeletedmsg = MAXMSGLIMIT;
11943  }
11944  }
11945 
11946  /* Load date format config for voicemail mail */
11947  if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11948  ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11949  }
11950 
11951  /* Load date format config for voicemail pager mail */
11952  if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11953  ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11954  }
11955 
11956  /* External password changing command */
11957  if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11958  ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11959  pwdchange = PWDCHANGE_EXTERNAL;
11960  } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11961  ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11962  pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11963  }
11964 
11965  /* External password validation command */
11966  if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11967  ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11968  ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11969  }
11970 
11971 #ifdef IMAP_STORAGE
11972  /* IMAP server address */
11973  if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11974  ast_copy_string(imapserver, val, sizeof(imapserver));
11975  } else {
11976  ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11977  }
11978  /* IMAP server port */
11979  if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11980  ast_copy_string(imapport, val, sizeof(imapport));
11981  } else {
11982  ast_copy_string(imapport, "143", sizeof(imapport));
11983  }
11984  /* IMAP server flags */
11985  if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11986  ast_copy_string(imapflags, val, sizeof(imapflags));
11987  }
11988  /* IMAP server master username */
11989  if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11990  ast_copy_string(authuser, val, sizeof(authuser));
11991  }
11992  /* IMAP server master password */
11993  if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11994  ast_copy_string(authpassword, val, sizeof(authpassword));
11995  }
11996  /* Expunge on exit */
11997  if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11998  if (ast_false(val))
11999  expungeonhangup = 0;
12000  else
12001  expungeonhangup = 1;
12002  } else {
12003  expungeonhangup = 1;
12004  }
12005  /* IMAP voicemail folder */
12006  if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
12007  ast_copy_string(imapfolder, val, sizeof(imapfolder));
12008  } else {
12009  ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
12010  }
12011  if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
12012  ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
12013  }
12014  if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
12015  imapgreetings = ast_true(val);
12016  } else {
12017  imapgreetings = 0;
12018  }
12019  if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
12020  ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12021  } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
12022  /* Also support greetingsfolder as documented in voicemail.conf.sample */
12023  ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
12024  } else {
12025  ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
12026  }
12027 
12028  /* There is some very unorthodox casting done here. This is due
12029  * to the way c-client handles the argument passed in. It expects a
12030  * void pointer and casts the pointer directly to a long without
12031  * first dereferencing it. */
12032  if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12033  mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12034  } else {
12035  mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12036  }
12037 
12038  if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12039  mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12040  } else {
12041  mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12042  }
12043 
12044  if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12045  mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12046  } else {
12047  mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12048  }
12049 
12050  if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12051  mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12052  } else {
12053  mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12054  }
12055 
12056  /* Increment configuration version */
12057  imapversion++;
12058 #endif
12059  /* External voicemail notify application */
12060  if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12061  ast_copy_string(externnotify, val, sizeof(externnotify));
12062  ast_debug(1, "found externnotify: %s\n", externnotify);
12063  } else {
12064  externnotify[0] = '\0';
12065  }
12066 
12067  /* SMDI voicemail notification */
12068  if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12069  ast_debug(1, "Enabled SMDI voicemail notification\n");
12070  if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12071  smdi_iface = ast_smdi_interface_find(val);
12072  } else {
12073  ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12074  smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12075  }
12076  if (!smdi_iface) {
12077  ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12078  }
12079  }
12080 
12081  /* Silence treshold */
12083  if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12084  silencethreshold = atoi(val);
12085 
12086  if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
12087  val = ASTERISK_USERNAME;
12088  ast_copy_string(serveremail, val, sizeof(serveremail));
12089 
12090  vmmaxsecs = 0;
12091  if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12092  if (sscanf(val, "%30d", &x) == 1) {
12093  vmmaxsecs = x;
12094  } else {
12095  ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12096  }
12097  } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12098  static int maxmessage_deprecate = 0;
12099  if (maxmessage_deprecate == 0) {
12100  maxmessage_deprecate = 1;
12101  ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12102  }
12103  if (sscanf(val, "%30d", &x) == 1) {
12104  vmmaxsecs = x;
12105  } else {
12106  ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12107  }
12108  }
12109 
12110  vmminsecs = 0;
12111  if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12112  if (sscanf(val, "%30d", &x) == 1) {
12113  vmminsecs = x;
12114  if (maxsilence / 1000 >= vmminsecs) {
12115  ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12116  }
12117  } else {
12118  ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12119  }
12120  } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12121  static int maxmessage_deprecate = 0;
12122  if (maxmessage_deprecate == 0) {
12123  maxmessage_deprecate = 1;
12124  ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12125  }
12126  if (sscanf(val, "%30d", &x) == 1) {
12127  vmminsecs = x;
12128  if (maxsilence / 1000 >= vmminsecs) {
12129  ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12130  }
12131  } else {
12132  ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12133  }
12134  }
12135 
12136  val = ast_variable_retrieve(cfg, "general", "format");
12137  if (!val) {
12138  val = "wav";
12139  } else {
12140  tmp = ast_strdupa(val);
12141  val = ast_format_str_reduce(tmp);
12142  if (!val) {
12143  ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12144  val = "wav";
12145  }
12146  }
12147  ast_copy_string(vmfmts, val, sizeof(vmfmts));
12148 
12149  skipms = 3000;
12150  if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12151  if (sscanf(val, "%30d", &x) == 1) {
12152  maxgreet = x;
12153  } else {
12154  ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12155  }
12156  }
12157 
12158  if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12159  if (sscanf(val, "%30d", &x) == 1) {
12160  skipms = x;
12161  } else {
12162  ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12163  }
12164  }
12165 
12166  maxlogins = 3;
12167  if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12168  if (sscanf(val, "%30d", &x) == 1) {
12169  maxlogins = x;
12170  } else {
12171  ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12172  }
12173  }
12174 
12175  minpassword = MINPASSWORD;
12176  if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12177  if (sscanf(val, "%30d", &x) == 1) {
12178  minpassword = x;
12179  } else {
12180  ast_log(AST_LOG_WARNING, "Invalid minimum password length. Default to %d\n", minpassword);
12181  }
12182  }
12183 
12184  /* Force new user to record name ? */
12185  if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
12186  val = "no";
12187  ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12188 
12189  /* Force new user to record greetings ? */
12190  if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
12191  val = "no";
12192  ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12193 
12194  if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12195  ast_debug(1, "VM_CID Internal context string: %s\n", val);
12196  stringp = ast_strdupa(val);
12197  for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12198  if (!ast_strlen_zero(stringp)) {
12199  q = strsep(&stringp, ",");
12200  while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12201  q++;
12202  ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12203  ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12204  } else {
12205  cidinternalcontexts[x][0] = '\0';
12206  }
12207  }
12208  }
12209  if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12210  ast_debug(1, "VM Review Option disabled globally\n");
12211  val = "no";
12212  }
12213  ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW);
12214 
12215  /* Temporary greeting reminder */
12216  if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12217  ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12218  val = "no";
12219  } else {
12220  ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12221  }
12222  ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12223  if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12224  ast_debug(1, "VM next message wrap disabled globally\n");
12225  val = "no";
12226  }
12227  ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);
12228 
12229  if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12230  ast_debug(1, "VM Operator break disabled globally\n");
12231  val = "no";
12232  }
12233  ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);
12234 
12235  if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12236  ast_debug(1, "VM CID Info before msg disabled globally\n");
12237  val = "no";
12238  }
12239  ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID);
12240 
12241  if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12242  ast_debug(1, "Send Voicemail msg disabled globally\n");
12243  val = "no";
12244  }
12245  ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12246 
12247  if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12248  ast_debug(1, "ENVELOPE before msg enabled globally\n");
12249  val = "yes";
12250  }
12251  ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);
12252 
12253  if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12254  ast_debug(1, "Move Heard enabled globally\n");
12255  val = "yes";
12256  }
12257  ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD);
12258 
12259  if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12260  ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12261  val = "no";
12262  }
12263  ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);
12264 
12265  if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12266  ast_debug(1, "Duration info before msg enabled globally\n");
12267  val = "yes";
12268  }
12269  ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);
12270 
12271  saydurationminfo = 2;
12272  if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12273  if (sscanf(val, "%30d", &x) == 1) {
12274  saydurationminfo = x;
12275  } else {
12276  ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12277  }
12278  }
12279 
12280  if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12281  ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12282  val = "no";
12283  }
12284  ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12285 
12286  if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12287  ast_copy_string(dialcontext, val, sizeof(dialcontext));
12288  ast_debug(1, "found dialout context: %s\n", dialcontext);
12289  } else {
12290  dialcontext[0] = '\0';
12291  }
12292 
12293  if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12294  ast_copy_string(callcontext, val, sizeof(callcontext));
12295  ast_debug(1, "found callback context: %s\n", callcontext);
12296  } else {
12297  callcontext[0] = '\0';
12298  }
12299 
12300  if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12301  ast_copy_string(exitcontext, val, sizeof(exitcontext));
12302  ast_debug(1, "found operator context: %s\n", exitcontext);
12303  } else {
12304  exitcontext[0] = '\0';
12305  }
12306 
12307  /* load password sounds configuration */
12308  if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12309  ast_copy_string(vm_password, val, sizeof(vm_password));
12310  if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12311  ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12312  if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12313  ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12314  if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12315  ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12316  if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12317  ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12318  if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12319  ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12320  if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12321  ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12322  }
12323  if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12324  ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12325  }
12326  /* load configurable audio prompts */
12327  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12328  ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12329  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12330  ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12331  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12332  ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12333  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12334  ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12335  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12336  ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12337 
12338  if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
12339  val = "no";
12340  ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD);
12341 
12342  if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12343  val = "voicemail.conf";
12344  }
12345  if (!(strcmp(val, "spooldir"))) {
12346  passwordlocation = OPT_PWLOC_SPOOLDIR;
12347  } else {
12348  passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12349  }
12350 
12351  poll_freq = DEFAULT_POLL_FREQ;
12352  if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12353  if (sscanf(val, "%30u", &poll_freq) != 1) {
12354  poll_freq = DEFAULT_POLL_FREQ;
12355  ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12356  }
12357  }
12358 
12359  poll_mailboxes = 0;
12360  if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12361  poll_mailboxes = ast_true(val);
12362 
12363  memset(fromstring, 0, sizeof(fromstring));
12364  memset(pagerfromstring, 0, sizeof(pagerfromstring));
12365  strcpy(charset, "ISO-8859-1");
12366  if (emailbody) {
12367  ast_free(emailbody);
12368  emailbody = NULL;
12369  }
12370  if (emailsubject) {
12371  ast_free(emailsubject);
12372  emailsubject = NULL;
12373  }
12374  if (pagerbody) {
12375  ast_free(pagerbody);
12376  pagerbody = NULL;
12377  }
12378  if (pagersubject) {
12379  ast_free(pagersubject);
12380  pagersubject = NULL;
12381  }
12382  if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12383  ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12384  if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12385  ast_copy_string(fromstring, val, sizeof(fromstring));
12386  if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12387  ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12388  if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12389  ast_copy_string(charset, val, sizeof(charset));
12390  if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12391  sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12392  for (x = 0; x < 4; x++) {
12393  memcpy(&adsifdn[x], &tmpadsi[x], 1);
12394  }
12395  }
12396  if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12397  sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12398  for (x = 0; x < 4; x++) {
12399  memcpy(&adsisec[x], &tmpadsi[x], 1);
12400  }
12401  }
12402  if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12403  if (atoi(val)) {
12404  adsiver = atoi(val);
12405  }
12406  }
12407  if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12408  ast_copy_string(zonetag, val, sizeof(zonetag));
12409  }
12410  if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12411  ast_copy_string(locale, val, sizeof(locale));
12412  }
12413  if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12414  emailsubject = ast_strdup(substitute_escapes(val));
12415  }
12416  if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12417  emailbody = ast_strdup(substitute_escapes(val));
12418  }
12419  if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12420  pagersubject = ast_strdup(substitute_escapes(val));
12421  }
12422  if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12423  pagerbody = ast_strdup(substitute_escapes(val));
12424  }
12425 
12426  /* load mailboxes from users.conf */
12427  if (ucfg) {
12428  for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12429  if (!strcasecmp(cat, "general")) {
12430  continue;
12431  }
12432  if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12433  continue;
12434  if ((current = find_or_create(userscontext, cat))) {
12435  populate_defaults(current);
12436  apply_options_full(current, ast_variable_browse(ucfg, cat));
12437  ast_copy_string(current->context, userscontext, sizeof(current->context));
12438  if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12440  }
12441 
12442  switch (current->passwordlocation) {
12443  case OPT_PWLOC_SPOOLDIR:
12444  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12445  read_password_from_file(secretfn, current->password, sizeof(current->password));
12446  }
12447  }
12448  }
12449  }
12450 
12451  /* load mailboxes from voicemail.conf */
12452  cat = ast_category_browse(cfg, NULL);
12453  while (cat) {
12454  if (strcasecmp(cat, "general")) {
12455  var = ast_variable_browse(cfg, cat);
12456  if (strcasecmp(cat, "zonemessages")) {
12457  /* Process mailboxes in this context */
12458  while (var) {
12459  append_mailbox(cat, var->name, var->value);
12460  var = var->next;
12461  }
12462  } else {
12463  /* Timezones in this context */
12464  while (var) {
12465  struct vm_zone *z;
12466  if ((z = ast_malloc(sizeof(*z)))) {
12467  char *msg_format, *tzone;
12468  msg_format = ast_strdupa(var->value);
12469  tzone = strsep(&msg_format, "|,");
12470  if (msg_format) {
12471  ast_copy_string(z->name, var->name, sizeof(z->name));
12472  ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12473  ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12474  AST_LIST_LOCK(&zones);
12475  AST_LIST_INSERT_HEAD(&zones, z, list);
12477  } else {
12478  ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12479  ast_free(z);
12480  }
12481  } else {
12483  return -1;
12484  }
12485  var = var->next;
12486  }
12487  }
12488  }
12489  cat = ast_category_browse(cfg, cat);
12490  }
12491 
12493 
12494  if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12496  if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12497  stop_poll_thread();;
12498 
12499  return 0;
12500  } else {
12502  ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12503  return 0;
12504  }
12505 }
12506 
12507 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12508 {
12509  int res = -1;
12510  char dir[PATH_MAX];
12511  snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12512  ast_debug(2, "About to try retrieving name file %s\n", dir);
12513  RETRIEVE(dir, -1, mailbox, context);
12514  if (ast_fileexists(dir, NULL, NULL)) {
12515  res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12516  }
12517  DISPOSE(dir, -1);
12518  return res;
12519 }
12520 
12521 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12522  struct ast_config *pwconf;
12523  struct ast_flags config_flags = { 0 };
12524 
12525  pwconf = ast_config_load(secretfn, config_flags);
12526  if (valid_config(pwconf)) {
12527  const char *val = ast_variable_retrieve(pwconf, "general", "password");
12528  if (val) {
12529  ast_copy_string(password, val, passwordlen);
12530  ast_config_destroy(pwconf);
12531  return;
12532  }
12533  ast_config_destroy(pwconf);
12534  }
12535  ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12536 }
12537 
12538 static int write_password_to_file(const char *secretfn, const char *password) {
12539  struct ast_config *conf;
12540  struct ast_category *cat;
12541  struct ast_variable *var;
12542  int res = -1;
12543 
12544  if (!(conf = ast_config_new())) {
12545  ast_log(LOG_ERROR, "Error creating new config structure\n");
12546  return res;
12547  }
12548  if (!(cat = ast_category_new("general", "", 1))) {
12549  ast_log(LOG_ERROR, "Error creating new category structure\n");
12550  ast_config_destroy(conf);
12551  return res;
12552  }
12553  if (!(var = ast_variable_new("password", password, ""))) {
12554  ast_log(LOG_ERROR, "Error creating new variable structure\n");
12555  ast_config_destroy(conf);
12556  ast_category_destroy(cat);
12557  return res;
12558  }
12559  ast_category_append(conf, cat);
12560  ast_variable_append(cat, var);
12561  if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12562  res = 0;
12563  } else {
12564  ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12565  }
12566 
12567  ast_config_destroy(conf);
12568  return res;
12569 }
12570 
12571 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12572 {
12573  char *context;
12574  char *args_copy;
12575  int res;
12576 
12577  if (ast_strlen_zero(data)) {
12578  ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12579  return -1;
12580  }
12581 
12582  args_copy = ast_strdupa(data);
12583  if ((context = strchr(args_copy, '@'))) {
12584  *context++ = '\0';
12585  } else {
12586  context = "default";
12587  }
12588 
12589  if ((res = sayname(chan, args_copy, context) < 0)) {
12590  ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12591  res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12592  if (!res) {
12593  res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12594  }
12595  }
12596 
12597  return res;
12598 }
12599 
12600 #ifdef TEST_FRAMEWORK
12601 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12602 {
12603  return 0;
12604 }
12605 
12606 static struct ast_frame *fake_read(struct ast_channel *ast)
12607 {
12608  return &ast_null_frame;
12609 }
12610 
12611 AST_TEST_DEFINE(test_voicemail_vmsayname)
12612 {
12613  char dir[PATH_MAX];
12614  char dir2[PATH_MAX];
12615  static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12616  static const char TEST_EXTENSION[] = "1234";
12617 
12618  struct ast_channel *test_channel1 = NULL;
12619  int res = -1;
12620 
12621  static const struct ast_channel_tech fake_tech = {
12622  .write = fake_write,
12623  .read = fake_read,
12624  };
12625 
12626  switch (cmd) {
12627  case TEST_INIT:
12628  info->name = "vmsayname_exec";
12629  info->category = "/apps/app_voicemail/";
12630  info->summary = "Vmsayname unit test";
12631  info->description =
12632  "This tests passing various parameters to vmsayname";
12633  return AST_TEST_NOT_RUN;
12634  case TEST_EXECUTE:
12635  break;
12636  }
12637 
12638  if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12639  NULL, NULL, 0, 0, "TestChannel1"))) {
12640  goto exit_vmsayname_test;
12641  }
12642 
12643  /* normally this is done in the channel driver */
12644  test_channel1->nativeformats = AST_FORMAT_GSM;
12645  test_channel1->writeformat = AST_FORMAT_GSM;
12646  test_channel1->rawwriteformat = AST_FORMAT_GSM;
12647  test_channel1->readformat = AST_FORMAT_GSM;
12648  test_channel1->rawreadformat = AST_FORMAT_GSM;
12649  test_channel1->tech = &fake_tech;
12650 
12651  ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12652  snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12653  if (!(res = vmsayname_exec(test_channel1, dir))) {
12654  snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12655  if (ast_fileexists(dir, NULL, NULL)) {
12656  ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12657  res = -1;
12658  goto exit_vmsayname_test;
12659  } else {
12660  /* no greeting already exists as expected, let's create one to fully test sayname */
12661  if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12662  ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12663  goto exit_vmsayname_test;
12664  }
12665  snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12666  snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12667  /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12668  if ((res = symlink(dir, dir2))) {
12669  ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12670  goto exit_vmsayname_test;
12671  }
12672  ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12673  snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12674  res = vmsayname_exec(test_channel1, dir);
12675 
12676  /* TODO: there may be a better way to do this */
12677  unlink(dir2);
12678  snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12679  rmdir(dir2);
12680  snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12681  rmdir(dir2);
12682  }
12683  }
12684 
12685 exit_vmsayname_test:
12686 
12687  if (test_channel1) {
12688  ast_hangup(test_channel1);
12689  }
12690 
12691  return res ? AST_TEST_FAIL : AST_TEST_PASS;
12692 }
12693 
12694 AST_TEST_DEFINE(test_voicemail_msgcount)
12695 {
12696  int i, j, res = AST_TEST_PASS, syserr;
12697  struct ast_vm_user *vmu;
12698  struct ast_vm_user svm;
12699  struct vm_state vms;
12700 #ifdef IMAP_STORAGE
12701  struct ast_channel *chan = NULL;
12702 #endif
12703  struct {
12704  char dir[256];
12705  char file[256];
12706  char txtfile[256];
12707  } tmp[3];
12708  char syscmd[256];
12709  const char origweasels[] = "tt-weasels";
12710  const char testcontext[] = "test";
12711  const char testmailbox[] = "00000000";
12712  const char testspec[] = "00000000@test";
12713  FILE *txt;
12714  int new, old, urgent;
12715  const char *folders[3] = { "Old", "Urgent", "INBOX" };
12716  const int folder2mbox[3] = { 1, 11, 0 };
12717  const int expected_results[3][12] = {
12718  /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12719  { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
12720  { 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
12721  { 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 2 },
12722  };
12723 
12724  switch (cmd) {
12725  case TEST_INIT:
12726  info->name = "test_voicemail_msgcount";
12727  info->category = "/apps/app_voicemail/";
12728  info->summary = "Test Voicemail status checks";
12729  info->description =
12730  "Verify that message counts are correct when retrieved through the public API";
12731  return AST_TEST_NOT_RUN;
12732  case TEST_EXECUTE:
12733  break;
12734  }
12735 
12736  /* Make sure the original path was completely empty */
12737  snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12738  if ((syserr = ast_safe_system(syscmd))) {
12739  ast_test_status_update(test, "Unable to clear test directory: %s\n",
12740  syserr > 0 ? strerror(syserr) : "unable to fork()");
12741  return AST_TEST_FAIL;
12742  }
12743 
12744 #ifdef IMAP_STORAGE
12745  if (!(chan = ast_dummy_channel_alloc())) {
12746  ast_test_status_update(test, "Unable to create dummy channel\n");
12747  return AST_TEST_FAIL;
12748  }
12749 #endif
12750 
12751  if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
12752  !(vmu = find_or_create(testcontext, testmailbox))) {
12753  ast_test_status_update(test, "Cannot create vmu structure\n");
12755 #ifdef IMAP_STORAGE
12756  chan = ast_channel_unref(chan);
12757 #endif
12758  return AST_TEST_FAIL;
12759  }
12760 
12761  populate_defaults(vmu);
12762  memset(&vms, 0, sizeof(vms));
12763 
12764  /* Create temporary voicemail */
12765  for (i = 0; i < 3; i++) {
12766  create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12767  make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12768  snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12769 
12770  if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12771  snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12772  VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12773  if ((syserr = ast_safe_system(syscmd))) {
12774  ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12775  syserr > 0 ? strerror(syserr) : "unable to fork()");
12777 #ifdef IMAP_STORAGE
12778  chan = ast_channel_unref(chan);
12779 #endif
12780  return AST_TEST_FAIL;
12781  }
12782  }
12783 
12784  if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12785  fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12786  fclose(txt);
12787  } else {
12788  ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12789  res = AST_TEST_FAIL;
12790  break;
12791  }
12792  open_mailbox(&vms, vmu, folder2mbox[i]);
12793  STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12794 
12795  /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12796  for (j = 0; j < 3; j++) {
12797  /* folder[2] is INBOX, __has_voicemail will default back to INBOX */
12798  if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12799  ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12800  testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12801  res = AST_TEST_FAIL;
12802  }
12803  }
12804 
12805  new = old = urgent = 0;
12806  if (ast_app_inboxcount(testspec, &new, &old)) {
12807  ast_test_status_update(test, "inboxcount returned failure\n");
12808  res = AST_TEST_FAIL;
12809  } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12810  ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12811  testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12812  res = AST_TEST_FAIL;
12813  }
12814 
12815  new = old = urgent = 0;
12816  if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12817  ast_test_status_update(test, "inboxcount2 returned failure\n");
12818  res = AST_TEST_FAIL;
12819  } else if (old != expected_results[i][6 + 0] ||
12820  urgent != expected_results[i][6 + 1] ||
12821  new != expected_results[i][6 + 2] ) {
12822  ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12823  testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12824  res = AST_TEST_FAIL;
12825  }
12826 
12827  new = old = urgent = 0;
12828  for (j = 0; j < 3; j++) {
12829  if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12830  ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12831  testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12832  res = AST_TEST_FAIL;
12833  }
12834  }
12835  }
12836 
12837  for (i = 0; i < 3; i++) {
12838  /* This is necessary if the voicemails are stored on an ODBC/IMAP
12839  * server, in which case, the rm below will not affect the
12840  * voicemails. */
12841  DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12842  DISPOSE(tmp[i].dir, 0);
12843  }
12844 
12845  if (vms.deleted) {
12846  ast_free(vms.deleted);
12847  }
12848  if (vms.heard) {
12849  ast_free(vms.heard);
12850  }
12851 
12852 #ifdef IMAP_STORAGE
12853  chan = ast_channel_unref(chan);
12854 #endif
12855 
12856  /* And remove test directory */
12857  snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12858  if ((syserr = ast_safe_system(syscmd))) {
12859  ast_test_status_update(test, "Unable to clear test directory: %s\n",
12860  syserr > 0 ? strerror(syserr) : "unable to fork()");
12861  }
12862 
12863  return res;
12864 }
12865 
12866 AST_TEST_DEFINE(test_voicemail_notify_endl)
12867 {
12868  int res = AST_TEST_PASS;
12869  char testcontext[] = "test";
12870  char testmailbox[] = "00000000";
12871  char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12872  char attach[256], attach2[256];
12873  char buf[256] = ""; /* No line should actually be longer than 80 */
12874  struct ast_channel *chan = NULL;
12875  struct ast_vm_user *vmu, vmus = {
12876  .flags = 0,
12877  };
12878  FILE *file;
12879  struct {
12880  char *name;
12881  enum { INT, FLAGVAL, STATIC, STRPTR } type;
12882  void *location;
12883  union {
12884  int intval;
12885  char *strval;
12886  } u;
12887  } test_items[] = {
12888  { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12889  { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12890  { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12891  { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12892  { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12893  { "attach2", STRPTR, attach2, .u.strval = "" },
12894  { "attach", STRPTR, attach, .u.strval = "" },
12895  };
12896  int which;
12897 
12898  switch (cmd) {
12899  case TEST_INIT:
12900  info->name = "test_voicemail_notify_endl";
12901  info->category = "/apps/app_voicemail/";
12902  info->summary = "Test Voicemail notification end-of-line";
12903  info->description =
12904  "Verify that notification emails use a consistent end-of-line character";
12905  return AST_TEST_NOT_RUN;
12906  case TEST_EXECUTE:
12907  break;
12908  }
12909 
12910  snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12911  snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12912 
12913  if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12914  !(vmu = find_or_create(testcontext, testmailbox))) {
12915  ast_test_status_update(test, "Cannot create vmu structure\n");
12916  return AST_TEST_NOT_RUN;
12917  }
12918 
12919  if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12920  ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12921  return AST_TEST_NOT_RUN;
12922  }
12923 
12924  populate_defaults(vmu);
12925  ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12926 #ifdef IMAP_STORAGE
12927  /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12928 #endif
12929 
12930  file = tmpfile();
12931  for (which = 0; which < ARRAY_LEN(test_items); which++) {
12932  /* Kill previous test, if any */
12933  rewind(file);
12934  if (ftruncate(fileno(file), 0)) {
12935  ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12936  res = AST_TEST_FAIL;
12937  break;
12938  }
12939 
12940  /* Make each change, in order, to the test mailbox */
12941  if (test_items[which].type == INT) {
12942  *((int *) test_items[which].location) = test_items[which].u.intval;
12943  } else if (test_items[which].type == FLAGVAL) {
12944  if (ast_test_flag(vmu, test_items[which].u.intval)) {
12945  ast_clear_flag(vmu, test_items[which].u.intval);
12946  } else {
12947  ast_set_flag(vmu, test_items[which].u.intval);
12948  }
12949  } else if (test_items[which].type == STATIC) {
12950  strcpy(test_items[which].location, test_items[which].u.strval);
12951  } else if (test_items[which].type == STRPTR) {
12952  test_items[which].location = test_items[which].u.strval;
12953  }
12954 
12955  make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12956  rewind(file);
12957  while (fgets(buf, sizeof(buf), file)) {
12958  if (
12959 #ifdef IMAP_STORAGE
12960  buf[strlen(buf) - 2] != '\r'
12961 #else
12962  buf[strlen(buf) - 2] == '\r'
12963 #endif
12964  || buf[strlen(buf) - 1] != '\n') {
12965  res = AST_TEST_FAIL;
12966  }
12967  }
12968  }
12969  fclose(file);
12970  return res;
12971 }
12972 
12973 AST_TEST_DEFINE(test_voicemail_load_config)
12974 {
12975  int res = AST_TEST_PASS;
12976  struct ast_vm_user *vmu;
12977  struct ast_config *cfg;
12978  char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
12979  int fd;
12980  FILE *file;
12981  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
12982 
12983  switch (cmd) {
12984  case TEST_INIT:
12985  info->name = "test_voicemail_load_config";
12986  info->category = "/apps/app_voicemail/";
12987  info->summary = "Test loading Voicemail config";
12988  info->description =
12989  "Verify that configuration is loaded consistently. "
12990  "This is to test regressions of ASTERISK-18838 where it was noticed that "
12991  "some options were loaded after the mailboxes were instantiated, causing "
12992  "those options not to be set correctly.";
12993  return AST_TEST_NOT_RUN;
12994  case TEST_EXECUTE:
12995  break;
12996  }
12997 
12998  /* build a config file by hand... */
12999  if ((fd = mkstemp(config_filename)) < 0) {
13000  return AST_TEST_FAIL;
13001  }
13002  if (!(file = fdopen(fd, "w"))) {
13003  close(fd);
13004  unlink(config_filename);
13005  return AST_TEST_FAIL;
13006  }
13007  fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
13008  fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
13009  fputs("00000002 => 9999,Mrs. Test\n", file);
13010  fclose(file);
13011 
13012  if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
13013  res = AST_TEST_FAIL;
13014  goto cleanup;
13015  }
13016 
13017  load_config_from_memory(1, cfg, NULL);
13018  ast_config_destroy(cfg);
13019 
13020 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
13021  ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
13022  u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
13023 
13024  AST_LIST_LOCK(&users);
13025  AST_LIST_TRAVERSE(&users, vmu, list) {
13026  if (!strcmp(vmu->mailbox, "00000001")) {
13027  if (0); /* trick to get CHECK to work */
13028  CHECK(vmu, callback, "othercontext")
13029  CHECK(vmu, locale, "nl_NL.UTF-8")
13030  CHECK(vmu, zonetag, "central")
13031  } else if (!strcmp(vmu->mailbox, "00000002")) {
13032  if (0); /* trick to get CHECK to work */
13033  CHECK(vmu, callback, "somecontext")
13034  CHECK(vmu, locale, "de_DE.UTF-8")
13035  CHECK(vmu, zonetag, "european")
13036  }
13037  }
13039 
13040 #undef CHECK
13041 
13042  /* restore config */
13043  load_config(1); /* this might say "Failed to load configuration file." */
13044 
13045 cleanup:
13046  unlink(config_filename);
13047  return res;
13048 }
13049 
13050 #endif /* defined(TEST_FRAMEWORK) */
13051 
13052 static int reload(void)
13053 {
13054  return load_config(1);
13055 }
13056 
13057 static int unload_module(void)
13058 {
13059  int res;
13060 
13061  res = ast_unregister_application(app);
13062  res |= ast_unregister_application(app2);
13063  res |= ast_unregister_application(app3);
13064  res |= ast_unregister_application(app4);
13065  res |= ast_unregister_application(sayname_app);
13066  res |= ast_custom_function_unregister(&mailbox_exists_acf);
13067  res |= ast_manager_unregister("VoicemailUsersList");
13068  res |= ast_data_unregister(NULL);
13069 #ifdef TEST_FRAMEWORK
13070  res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13071  res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13072  res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13073  res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13074  res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13075 #endif
13076  ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13078  ao2_ref(inprocess_container, -1);
13079 
13080  if (poll_thread != AST_PTHREADT_NULL)
13081  stop_poll_thread();
13082 
13083  mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13084  ast_unload_realtime("voicemail");
13085  ast_unload_realtime("voicemail_data");
13086 
13087  free_vm_users();
13088  free_vm_zones();
13089  return res;
13090 }
13091 
13092 static int load_module(void)
13093 {
13094  int res;
13095  my_umask = umask(0);
13096  umask(my_umask);
13097 
13098  if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13099  return AST_MODULE_LOAD_DECLINE;
13100  }
13101 
13102  /* compute the location of the voicemail spool directory */
13103  snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13104 
13105  if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13106  ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor. MWI will not work\n");
13107  }
13108 
13109  if ((res = load_config(0)))
13110  return res;
13111 
13116  res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13117  res |= ast_custom_function_register(&mailbox_exists_acf);
13119 #ifdef TEST_FRAMEWORK
13120  res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13121  res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13122  res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13123  res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13124  res |= AST_TEST_REGISTER(test_voicemail_load_config);
13125 #endif
13126 
13127  if (res)
13128  return res;
13129 
13130  ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13131  ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13132 
13134  ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13135  ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13136 
13137  return res;
13138 }
13139 
13140 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
13141 {
13142  int cmd = 0;
13143  char destination[80] = "";
13144  int retries = 0;
13145 
13146  if (!num) {
13147  ast_verb(3, "Destination number will be entered manually\n");
13148  while (retries < 3 && cmd != 't') {
13149  destination[1] = '\0';
13150  destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13151  if (!cmd)
13152  destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13153  if (!cmd)
13154  destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13155  if (!cmd) {
13156  cmd = ast_waitfordigit(chan, 6000);
13157  if (cmd)
13158  destination[0] = cmd;
13159  }
13160  if (!cmd) {
13161  retries++;
13162  } else {
13163 
13164  if (cmd < 0)
13165  return 0;
13166  if (cmd == '*') {
13167  ast_verb(3, "User hit '*' to cancel outgoing call\n");
13168  return 0;
13169  }
13170  if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0)
13171  retries++;
13172  else
13173  cmd = 't';
13174  }
13175  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13176  }
13177  if (retries >= 3) {
13178  return 0;
13179  }
13180 
13181  } else {
13182  if (option_verbose > 2)
13183  ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13184  ast_copy_string(destination, num, sizeof(destination));
13185  }
13186 
13187  if (!ast_strlen_zero(destination)) {
13188  if (destination[strlen(destination) -1 ] == '*')
13189  return 0;
13190  if (option_verbose > 2)
13191  ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13192  ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13193  ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13194  chan->priority = 0;
13195  return 9;
13196  }
13197  return 0;
13198 }
13199 
13200 /*!
13201  * \brief The advanced options within a message.
13202  * \param chan
13203  * \param vmu
13204  * \param vms
13205  * \param msg
13206  * \param option
13207  * \param record_gain
13208  *
13209  * Provides handling for the play message envelope, call the person back, or reply to message.
13210  *
13211  * \return zero on success, -1 on error.
13212  */
13213 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)
13214 {
13215  int res = 0;
13216  char filename[PATH_MAX];
13217  struct ast_config *msg_cfg = NULL;
13218  const char *origtime, *context;
13219  char *name, *num;
13220  int retries = 0;
13221  char *cid;
13222  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13223 
13224  vms->starting = 0;
13225 
13226  make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13227 
13228  /* Retrieve info from VM attribute file */
13229  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13230  RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13231  msg_cfg = ast_config_load(filename, config_flags);
13232  DISPOSE(vms->curdir, vms->curmsg);
13233  if (!valid_config(msg_cfg)) {
13234  ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13235  return 0;
13236  }
13237 
13238  if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13239  ast_config_destroy(msg_cfg);
13240  return 0;
13241  }
13242 
13243  cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13244 
13245  context = ast_variable_retrieve(msg_cfg, "message", "context");
13246  if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13247  context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13248  switch (option) {
13249  case 3: /* Play message envelope */
13250  if (!res)
13251  res = play_message_datetime(chan, vmu, origtime, filename);
13252  if (!res)
13253  res = play_message_callerid(chan, vms, cid, context, 0);
13254 
13255  res = 't';
13256  break;
13257 
13258  case 2: /* Call back */
13259 
13260  if (ast_strlen_zero(cid))
13261  break;
13262 
13263  ast_callerid_parse(cid, &name, &num);
13264  while ((res > -1) && (res != 't')) {
13265  switch (res) {
13266  case '1':
13267  if (num) {
13268  /* Dial the CID number */
13269  res = dialout(chan, vmu, num, vmu->callback);
13270  if (res) {
13271  ast_config_destroy(msg_cfg);
13272  return 9;
13273  }
13274  } else {
13275  res = '2';
13276  }
13277  break;
13278 
13279  case '2':
13280  /* Want to enter a different number, can only do this if there's a dialout context for this user */
13281  if (!ast_strlen_zero(vmu->dialout)) {
13282  res = dialout(chan, vmu, NULL, vmu->dialout);
13283  if (res) {
13284  ast_config_destroy(msg_cfg);
13285  return 9;
13286  }
13287  } else {
13288  ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13289  res = ast_play_and_wait(chan, "vm-sorry");
13290  }
13291  ast_config_destroy(msg_cfg);
13292  return res;
13293  case '*':
13294  res = 't';
13295  break;
13296  case '3':
13297  case '4':
13298  case '5':
13299  case '6':
13300  case '7':
13301  case '8':
13302  case '9':
13303  case '0':
13304 
13305  res = ast_play_and_wait(chan, "vm-sorry");
13306  retries++;
13307  break;
13308  default:
13309  if (num) {
13310  ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13311  res = ast_play_and_wait(chan, "vm-num-i-have");
13312  if (!res)
13313  res = play_message_callerid(chan, vms, num, vmu->context, 1);
13314  if (!res)
13315  res = ast_play_and_wait(chan, "vm-tocallnum");
13316  /* Only prompt for a caller-specified number if there is a dialout context specified */
13317  if (!ast_strlen_zero(vmu->dialout)) {
13318  if (!res)
13319  res = ast_play_and_wait(chan, "vm-calldiffnum");
13320  }
13321  } else {
13322  res = ast_play_and_wait(chan, "vm-nonumber");
13323  if (!ast_strlen_zero(vmu->dialout)) {
13324  if (!res)
13325  res = ast_play_and_wait(chan, "vm-toenternumber");
13326  }
13327  }
13328  if (!res) {
13329  res = ast_play_and_wait(chan, "vm-star-cancel");
13330  }
13331  if (!res) {
13332  res = ast_waitfordigit(chan, 6000);
13333  }
13334  if (!res) {
13335  retries++;
13336  if (retries > 3) {
13337  res = 't';
13338  }
13339  }
13340  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13341  break;
13342 
13343  }
13344  if (res == 't')
13345  res = 0;
13346  else if (res == '*')
13347  res = -1;
13348  }
13349  break;
13350 
13351  case 1: /* Reply */
13352  /* Send reply directly to sender */
13353  if (ast_strlen_zero(cid))
13354  break;
13355 
13356  ast_callerid_parse(cid, &name, &num);
13357  if (!num) {
13358  ast_verb(3, "No CID number available, no reply sent\n");
13359  if (!res)
13360  res = ast_play_and_wait(chan, "vm-nonumber");
13361  ast_config_destroy(msg_cfg);
13362  return res;
13363  } else {
13364  struct ast_vm_user vmu2;
13365  if (find_user(&vmu2, vmu->context, num)) {
13366  struct leave_vm_options leave_options;
13367  char mailbox[AST_MAX_EXTENSION * 2 + 2];
13368  snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13369 
13370  ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13371 
13372  memset(&leave_options, 0, sizeof(leave_options));
13373  leave_options.record_gain = record_gain;
13374  res = leave_voicemail(chan, mailbox, &leave_options);
13375  if (!res)
13376  res = 't';
13377  ast_config_destroy(msg_cfg);
13378  return res;
13379  } else {
13380  /* Sender has no mailbox, can't reply */
13381  ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13382  ast_play_and_wait(chan, "vm-nobox");
13383  res = 't';
13384  ast_config_destroy(msg_cfg);
13385  return res;
13386  }
13387  }
13388  res = 0;
13389 
13390  break;
13391  }
13392 
13393  ast_config_destroy(msg_cfg);
13394 
13395 #ifndef IMAP_STORAGE
13396  if (!res) {
13397  make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13398  vms->heard[msg] = 1;
13399  res = wait_file(chan, vms, vms->fn);
13400  }
13401 #endif
13402  return res;
13403 }
13404 
13405 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13406  int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13407  signed char record_gain, struct vm_state *vms, char *flag)
13408 {
13409  /* Record message & let caller review or re-record it, or set options if applicable */
13410  int res = 0;
13411  int cmd = 0;
13412  int max_attempts = 3;
13413  int attempts = 0;
13414  int recorded = 0;
13415  int msg_exists = 0;
13416  signed char zero_gain = 0;
13417  char tempfile[PATH_MAX];
13418  char *acceptdtmf = "#";
13419  char *canceldtmf = "";
13420  int canceleddtmf = 0;
13421 
13422  /* Note that urgent and private are for flagging messages as such in the future */
13423 
13424  /* barf if no pointer passed to store duration in */
13425  if (duration == NULL) {
13426  ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13427  return -1;
13428  }
13429 
13430  if (!outsidecaller)
13431  snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13432  else
13433  ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13434 
13435  cmd = '3'; /* Want to start by recording */
13436 
13437  while ((cmd >= 0) && (cmd != 't')) {
13438  switch (cmd) {
13439  case '1':
13440  if (!msg_exists) {
13441  /* In this case, 1 is to record a message */
13442  cmd = '3';
13443  break;
13444  } else {
13445  /* Otherwise 1 is to save the existing message */
13446  ast_verb(3, "Saving message as is\n");
13447  if (!outsidecaller)
13448  ast_filerename(tempfile, recordfile, NULL);
13449  ast_stream_and_wait(chan, "vm-msgsaved", "");
13450  if (!outsidecaller) {
13451  /* Saves to IMAP server only if imapgreeting=yes */
13452  STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13453  DISPOSE(recordfile, -1);
13454  }
13455  cmd = 't';
13456  return res;
13457  }
13458  case '2':
13459  /* Review */
13460  ast_verb(3, "Reviewing the message\n");
13461  cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13462  break;
13463  case '3':
13464  msg_exists = 0;
13465  /* Record */
13466  if (recorded == 1)
13467  ast_verb(3, "Re-recording the message\n");
13468  else
13469  ast_verb(3, "Recording the message\n");
13470 
13471  if (recorded && outsidecaller) {
13472  cmd = ast_play_and_wait(chan, INTRO);
13473  cmd = ast_play_and_wait(chan, "beep");
13474  }
13475  recorded = 1;
13476  /* 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 */
13477  if (record_gain)
13478  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13479  if (ast_test_flag(vmu, VM_OPERATOR))
13480  canceldtmf = "0";
13481  cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13482  if (strchr(canceldtmf, cmd)) {
13483  /* need this flag here to distinguish between pressing '0' during message recording or after */
13484  canceleddtmf = 1;
13485  }
13486  if (record_gain)
13487  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13488  if (cmd == -1) {
13489  /* User has hung up, no options to give */
13490  if (!outsidecaller) {
13491  /* user was recording a greeting and they hung up, so let's delete the recording. */
13492  ast_filedelete(tempfile, NULL);
13493  }
13494  return cmd;
13495  }
13496  if (cmd == '0') {
13497  break;
13498  } else if (cmd == '*') {
13499  break;
13500 #if 0
13501  } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13502  /* Message is too short */
13503  ast_verb(3, "Message too short\n");
13504  cmd = ast_play_and_wait(chan, "vm-tooshort");
13505  cmd = ast_filedelete(tempfile, NULL);
13506  break;
13507  } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13508  /* Message is all silence */
13509  ast_verb(3, "Nothing recorded\n");
13510  cmd = ast_filedelete(tempfile, NULL);
13511  cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13512  if (!cmd)
13513  cmd = ast_play_and_wait(chan, "vm-speakup");
13514  break;
13515 #endif
13516  } else {
13517  /* If all is well, a message exists */
13518  msg_exists = 1;
13519  cmd = 0;
13520  }
13521  break;
13522  case '4':
13523  if (outsidecaller) { /* only mark vm messages */
13524  /* Mark Urgent */
13525  if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13526  ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13527  res = ast_play_and_wait(chan, "vm-marked-urgent");
13528  strcpy(flag, "Urgent");
13529  } else if (flag) {
13530  ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13531  res = ast_play_and_wait(chan, "vm-marked-nonurgent");
13532  strcpy(flag, "");
13533  } else {
13534  ast_play_and_wait(chan, "vm-sorry");
13535  }
13536  cmd = 0;
13537  } else {
13538  cmd = ast_play_and_wait(chan, "vm-sorry");
13539  }
13540  break;
13541  case '5':
13542  case '6':
13543  case '7':
13544  case '8':
13545  case '9':
13546  case '*':
13547  case '#':
13548  cmd = ast_play_and_wait(chan, "vm-sorry");
13549  break;
13550 #if 0
13551 /* XXX Commented out for the moment because of the dangers of deleting
13552  a message while recording (can put the message numbers out of sync) */
13553  case '*':
13554  /* Cancel recording, delete message, offer to take another message*/
13555  cmd = ast_play_and_wait(chan, "vm-deleted");
13556  cmd = ast_filedelete(tempfile, NULL);
13557  if (outsidecaller) {
13558  res = vm_exec(chan, NULL);
13559  return res;
13560  }
13561  else
13562  return 1;
13563 #endif
13564  case '0':
13565  if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13566  cmd = ast_play_and_wait(chan, "vm-sorry");
13567  break;
13568  }
13569  if (msg_exists || recorded) {
13570  cmd = ast_play_and_wait(chan, "vm-saveoper");
13571  if (!cmd)
13572  cmd = ast_waitfordigit(chan, 3000);
13573  if (cmd == '1') {
13574  ast_filerename(tempfile, recordfile, NULL);
13575  ast_play_and_wait(chan, "vm-msgsaved");
13576  cmd = '0';
13577  } else if (cmd == '4') {
13578  if (flag) {
13579  ast_play_and_wait(chan, "vm-marked-urgent");
13580  strcpy(flag, "Urgent");
13581  }
13582  ast_play_and_wait(chan, "vm-msgsaved");
13583  cmd = '0';
13584  } else {
13585  ast_play_and_wait(chan, "vm-deleted");
13586  DELETE(tempfile, -1, tempfile, vmu);
13587  cmd = '0';
13588  }
13589  }
13590  return cmd;
13591  default:
13592  /* If the caller is an ouside caller, and the review option is enabled,
13593  allow them to review the message, but let the owner of the box review
13594  their OGM's */
13595  if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13596  return cmd;
13597  if (msg_exists) {
13598  cmd = ast_play_and_wait(chan, "vm-review");
13599  if (!cmd && outsidecaller) {
13600  if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13601  cmd = ast_play_and_wait(chan, "vm-review-urgent");
13602  } else if (flag) {
13603  cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13604  }
13605  }
13606  } else {
13607  cmd = ast_play_and_wait(chan, "vm-torerecord");
13608  if (!cmd)
13609  cmd = ast_waitfordigit(chan, 600);
13610  }
13611 
13612  if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13613  cmd = ast_play_and_wait(chan, "vm-reachoper");
13614  if (!cmd)
13615  cmd = ast_waitfordigit(chan, 600);
13616  }
13617 #if 0
13618  if (!cmd)
13619  cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13620 #endif
13621  if (!cmd)
13622  cmd = ast_waitfordigit(chan, 6000);
13623  if (!cmd) {
13624  attempts++;
13625  }
13626  if (attempts > max_attempts) {
13627  cmd = 't';
13628  }
13629  }
13630  }
13631  if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13632  /* Hang up or timeout, so delete the recording. */
13633  ast_filedelete(tempfile, NULL);
13634  }
13635 
13636  if (cmd != 't' && outsidecaller)
13637  ast_play_and_wait(chan, "vm-goodbye");
13638 
13639  return cmd;
13640 }
13641 
13642 /* This is a workaround so that menuselect displays a proper description
13643  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13644  */
13645 
13647  .load = load_module,
13648  .unload = unload_module,
13649  .reload = reload,
13650  .nonoptreq = "res_adsi,res_smdi",
13651  );
static char user[512]
static struct ast_vm_user * find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
Finds a voicemail user from the realtime engine.
static char emaildateformat[32]
static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
double volgain
int ast_filecopy(const char *oldname, const char *newname, const char *fmt)
Copies a file.
Definition: file.c:941
static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
static struct ast_cli_entry cli_voicemail[]
SQLHDBC con
Definition: res_odbc.h:48
#define BASELINELEN
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:81
static double volgain
int ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2804
#define ast_channel_lock(chan)
Definition: channel.h:2466
static char exten[AST_MAX_EXTENSION]
Definition: chan_alsa.c:109
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 vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:191
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.
An event.
Definition: event.c:85
char * str
Subscriber phone number (Malloced)
Definition: channel.h:241
#define VM_OPERATOR
static struct ast_event_sub * mwi_unsub_sub
#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
static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
static void free_user(struct ast_vm_user *vmu)
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
static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
char uniqueid[80]
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 void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
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
#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 int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
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
static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
struct ast_party_caller caller
Channel Caller ID information.
Definition: channel.h:804
static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
#define MAXMSGLIMIT
char * strsep(char **str, const char *delims)
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...
static void adsi_goodbye(struct ast_channel *chan)
struct ast_app * pbx_findapp(const char *app)
Look up an application.
Definition: pbx.c:1537
static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
static int handle_unsubscribe(void *datap)
static void queue_mwi_event(const char *box, int urgent, int new, int old)
char msg_format[512]
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 HVSU_OUTPUT_FORMAT
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
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition: pbx.c:1497
static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
Resets a user password to a specified password.
static void run_externnotify(char *context, char *extension, const char *flag)
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)
#define VM_ATTACH
#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 void rename_file(char *sfn, char *dfn)
Renames a message in a mailbox folder.
#define RETRIEVE(a, b, c, d)
#define VM_FORCEGREET
#define EXISTS(a, b, c, d)
static int append_mailbox(const char *context, const char *box, const char *data)
char name[80]
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: utils.h:653
#define AST_DIGIT_ANY
Definition: file.h:47
static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
static int make_file(char *dest, const int len, const char *dir, const int num)
Creates a file system path expression for a folder within the voicemail data folder and the appropria...
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: cli.c:2177
static const char *const mailbox_folders[]
static char vmfmts[80]
static int say_and_wait(struct ast_channel *chan, int num, const char *language)
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2502
static char * complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
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 void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
The handler for the change password option.
static unsigned char adsisec[4]
#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
#define SMDI_MWI_WAIT_TIMEOUT
static void copy_plain_file(char *frompath, char *topath)
Copies a voicemail information (envelope) file.
static int inbuf(struct baseio *bio, FILE *fi)
utility used by inchar(), for base_encode()
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
Definition: config.c:2630
Time-related functions and macros.
unsigned int flags
static int valid_config(const struct ast_config *cfg)
Check if configuration file is valid.
struct ast_party_name name
Subscriber name.
Definition: channel.h:290
static int handle_subscribe(void *datap)
static int saydurationminfo
struct ast_party_id from
Who is redirecting the call (Sent to the party the call is redirected toward)
Definition: channel.h:449
static unsigned char poll_thread_run
static int load_config(int reload)
#define DEFAULT_LISTEN_CONTROL_STOP_KEY
char fn[PATH_MAX]
static int * map
Definition: misdn_config.c:434
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
static int maxsilence
char context[AST_MAX_CONTEXT]
Definition: channel.h:868
static char * emailsubject
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 actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
int ast_event_queue_and_cache(struct ast_event *event)
Queue and cache an event.
Definition: event.c:1465
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
descriptor for a cli entry.
Definition: cli.h:165
static int skipms
const int argc
Definition: cli.h:154
#define LOG_WARNING
Definition: logger.h:144
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 char listen_control_restart_key[12]
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category)
Goes through variables.
Definition: config.c:597
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
static void free_vm_users(void)
Free the users structure.
static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
char timezone[80]
const char * context
static const char * substitute_escapes(const char *value)
static struct ast_custom_function mailbox_exists_acf
struct ast_taskprocessor * ast_taskprocessor_get(const char *name, enum ast_tps_options create)
Get a reference to a taskprocessor with the specified name and create the taskprocessor if necessary...
int 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
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
static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
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
This entries are for multiple registers.
Definition: data.h:253
static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
static struct ast_vm_user * find_or_create(const char *context, const char *box)
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 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.
char curdir[PATH_MAX]
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 int messagecount(const char *context, const char *mailbox, const char *folder)
static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
#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 maxdeletedmsg
static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
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
An MWI subscription.
#define EVENT_FLAG_CALL
Definition: manager.h:72
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
Definition: cli.h:146
#define DATA_EXPORT_VM_USERS(USER)
static FILE * vm_mkftemp(char *template)
static struct ast_data_handler vm_users_data_provider
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
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 int minpassword
#define VM_TEMPGREETWARN
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 int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
Prompts the user and records a voicemail to a mailbox.
static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
static int adsiver
static struct ast_threadstorage buf2
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. ...
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
static int passwordlocation
#define ADSI_JUST_LEFT
Definition: adsi.h:112
Number of Used by: AST_EVENT_MWI Payload type: UINT.
Definition: event_defs.h:77
static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
Manager list voicemail users command.
static char * show_users_realtime(int fd, const char *context)
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
#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
unsigned char iobuf[BASEMAXINLINE]
#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_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
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
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 char * pagersubject
Definitions to aid in the use of thread local storage.
static void poll_subscribed_mailboxes(void)
static char * handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Reload voicemail configuration from the CLI.
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:931
int value
Definition: syslog.c:39
void ast_cli(int fd, const char *fmt,...)
Definition: cli.c:105
ADSI Support (built upon Caller*ID)
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 input(yyscan_t yyscanner)
Definition: ast_expr2f.c:1575
#define LOG_DEBUG
Definition: logger.h:122
static int inprocess_cmp_fn(void *obj, void *arg, int flags)
#define VM_SVMAIL
#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
#define VM_SEARCH
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_taskprocessor * mwi_subscription_tps
#define VM_FORCENAME
static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
static int transfer
Definition: chan_mgcp.c:187
#define ast_verb(level,...)
Definition: logger.h:243
static int copy(char *infile, char *outfile)
Utility function to copy a file.
Definition: ael.tab.c:203
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
char * emailsubject
static char VM_SPOOL_DIR[PATH_MAX]
void ast_unreplace_sigchld(void)
Restore the SIGCHLD handler.
Definition: asterisk.c:1062
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 char pagerdateformat[32]
static struct ast_data_entry vm_data_providers[]
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
static void stop_poll_thread(void)
#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
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 ADSI_COMM_PAGE
Definition: adsi.h:107
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
static ast_cond_t poll_cond
#define MAX_LANGUAGE
Definition: channel.h:138
String fields in structures.
static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
Utility functions.
static char userscontext[AST_MAX_EXTENSION]
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 int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
static void * mb_poll_thread(void *data)
char cause[SMDI_MWI_FAIL_CAUSE_LEN+1]
Definition: smdi.h:58
#define ast_manager_event(chan, category, event, contents,...)
Definition: manager.h:221
const char * mailbox
static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
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 adsi_logo(unsigned char *buf)
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_status2(struct ast_channel *chan, struct vm_state *vms)
#define INTRO
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
struct ast_party_id id
Caller party ID.
Definition: channel.h:370
#define VM_SAYDURATION
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
static char listen_control_forward_key[12]
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
Definition: config.c:2679
char * context
#define AST_LOG_NOTICE
Definition: logger.h:138
static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
static int pwdchange
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
Definition: config.h:68
#define SENDMAIL
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
static int my_umask
int ast_adsi_set_line(unsigned char *buf, int page, int line)
Sets the current line and page.
Definition: adsi.c:285
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
static char charset[32]
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
static int get_folder2(struct ast_channel *chan, char *fn, int start)
plays a prompt and waits for a keypress.
#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
#define AST_LOG_ERROR
Definition: logger.h:160
int ast_say_counted_noun(struct ast_channel *chan, int num, const char *noun)
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
static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
char * ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown)
Definition: callerid.c:1074
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.
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 vm_mismatch[80]
static int get_folder_by_name(const char *name)
static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
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 int count_messages(struct ast_vm_user *vmu, char *dir)
Find all .txt files - even if they are not in sequence from 0000.
#define VMSTATE_MAX_MSG_ARRAY
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
const int fd
Definition: cli.h:153
#define ast_config_load(filename, flags)
Load a config file.
Definition: config.h:170
#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
static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms)
#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
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
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 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)
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
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
static int vmsayname_exec(struct ast_channel *chan, const char *data)
#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
#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_play_folder_name_ua(struct ast_channel *chan, char *box)
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 vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
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.
static char ext_pass_check_cmd[128]
static int vmauthenticate(struct ast_channel *chan, const char *data)
#define VM_REVIEW
uint32_t uniqueid
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 void adsi_login(struct ast_channel *chan)
static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
Loads the options specific to a voicemail user.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
Determine number of new/old messages in a mailbox.
Definition: app.c:435
static int inprocess_hash_fn(const void *obj, const int flags)
ODBC resource manager.
char zonetag[80]
static int check_password(struct ast_vm_user *vmu, char *password)
Check that password meets minimum required length.
static char * pagerbody
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
char mailbox[0]
Structure to describe a channel &quot;technology&quot;, ie a channel driver See for examples: ...
Definition: channel.h:507
static char vm_newpassword[80]
static char dialcontext[AST_MAX_CONTEXT]
static pthread_t poll_thread
Core PBX routines and definitions.
static int is_valid_dtmf(const char *key)
Determines if a DTMF key entered is valid.
static char callcontext[AST_MAX_CONTEXT]
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:542
#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
static char * app3
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
#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
#define ERROR_LOCK_PATH
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:184
static char vm_prepend_timeout[80]
const char *const * argv
Definition: cli.h:155
static int maxlogins
static void apply_options(struct ast_vm_user *vmu, const char *options)
Destructively Parse options and apply.
const char * ast_config_AST_DATA_DIR
Definition: asterisk.c:262
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
static char * emailbody
#define VM_DIRECFORWARD
#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 DEFAULT_LISTEN_CONTROL_PAUSE_KEY
static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
static char * app2
#define ASTERISK_USERNAME
static unsigned char adsifdn[4]
char pager[80]
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_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
static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
static void adsi_begin(struct ast_channel *chan, int *useadsi)
#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
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
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_exec(struct ast_channel *chan, const char *data)
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 VOICEMAIL_FILE_MODE
#define CLI_SHOWUSAGE
Definition: cli.h:44
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
char * emailbody
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
static unsigned int poll_freq
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: config.c:483
static int vmminsecs
static struct ast_event_sub * mwi_sub_sub
static char pagerfromstring[100]
Event subscription.
Definition: event.c:124
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
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)
enum ast_channel_state _state
Definition: channel.h:839
static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
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 unload_module(void)
const ast_string_field name
Definition: channel.h:787
#define VM_PBXSKIP
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 vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
int linelength
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:430
char exit[80]
#define LOG_NOTICE
Definition: logger.h:133
#define VM_ALLOCED
#define VM_ENVELOPE
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:11261
static char * sayname_app
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 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
#define DATA_EXPORT_VM_ZONES(ZONE)
static struct ast_threadstorage buf1
#define VM_FWDURGAUTO
static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
#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
#define CLI_FAILURE
Definition: cli.h:45
int errno
static int vm_execmain(struct ast_channel *chan, const char *data)
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 MAXMSG
#define ast_free(a)
Definition: astmm.h:97
char * command
Definition: cli.h:180
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:418
char macrocontext[AST_MAX_CONTEXT]
Definition: channel.h:870
static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
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.
static int get_date(char *s, int len)
Gets the current date and time, as formatted string.
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
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 ...
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
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 COPY(a, b, c, d, e, f, g, h)
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 void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
Sets a a specific property value.
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
#define VOICEMAIL_DIR_MODE
int * deleted
static struct ast_format f[]
Definition: format_g726.c:181
SMDI support for Asterisk.
#define MINPASSWORD
const char * word
Definition: cli.h:157
static struct ast_flags globalflags
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
#define CHUNKSIZE
static struct ast_app_option vm_app_options[128]
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 * addesc
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
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...
An API for managing task processing threads that can be shared across modules.
static char * app
static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
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
static char vm_reenterpassword[80]
static char mailcmd[160]
char fwd_st[SMDI_MAX_STATION_NUM_LEN+1]
Definition: smdi.h:56
#define PWDCHANGE_EXTERNAL
static void adsi_password(struct ast_channel *chan)
static int inprocess_count(const char *context, const char *mailbox, int delta)
if(yyss+yystacksize-1<=yyssp)
Definition: ast_expr2.c:1874
static const char type[]
Definition: chan_nbs.c:57
structure to hold users read from users.conf
static void * cleanup(void *unused)
Definition: pbx_realtime.c:125
Structure used to handle boolean flags.
Definition: utils.h:200
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.
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
Definition: config.c:2606
static char listen_control_stop_key[12]
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.
#define ast_clear_flag(p, flag)
Definition: utils.h:77
#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY
#define VM_SKIPAFTERCMD
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
static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
static int maxmsg
static unsigned int poll_mailboxes
static int vm_box_exists(struct ast_channel *chan, const char *data)
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.
Options for leaving voicemail with the voicemail() application.
Definition: app_minivm.c:635
const char * usage
Definition: cli.h:171
static char serveremail[80]
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 void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
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
static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
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
static int vm_users_data_provider_get(const struct ast_data_search *search, struct ast_data *data_root)
#define CLI_SUCCESS
Definition: cli.h:43
static char locale[20]
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
static int has_voicemail(const char *mailbox, const char *folder)
Determines if the given folder has messages.
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
#define VM_SAYCID
#define STORE(a, b, c, d, e, f, g, h, i, j)
static char vm_password[80]
static char zonetag[80]
static int ochar(struct baseio *bio, int c, FILE *so)
utility used by base_encode()
static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg)
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
Standard Command Line Interface.
static int reload(void)
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
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 char listen_control_reverse_key[12]
#define ao2_container_alloc(arg1, arg2, arg3)
Definition: astobj2.h:734
#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 ast_mutex_t poll_lock
ast_app: A registered application
Definition: pbx.c:971
char dialout[80]
static char externnotify[160]
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
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
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 void adsi_folders(struct ast_channel *chan, int start, char *label)
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:3086
static char vm_invalid_password[80]
static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64]
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.
static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
basically mkdir -p $dest/$context/$ext/$folder
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
#define VM_MOVEHEARD
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 void adsi_status(struct ast_channel *chan, struct vm_state *vms)
static int vm_intro_de(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;.
Data structure associated with a single frame of data.
Definition: frame.h:142
#define AST_DATA_STRUCTURE(__struct, __name)
Definition: data.h:300
#define HVSZ_OUTPUT_FORMAT
static int silencethreshold
char curbox[80]
static void read_password_from_file(const char *secretfn, char *password, int passwordlen)
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
static void free_vm_zones(void)
Free the zones structure.
static char * vm_check_password_shell(char *command, char *buf, size_t len)
char callback[80]
Unique ID Used by: AST_EVENT_SUB, AST_EVENT_UNSUB Payload type: UINT.
Definition: event_defs.h:89
#define DEFAULT_LISTEN_CONTROL_RESTART_KEY
static int vmmaxsecs
#define DEFAULT_POLL_FREQ
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_INTERNAL
#define RENAME(a, b, c, d, e, f, g, h)
static int vm_delete(char *file)
Removes the voicemail sound and information file.
enum queue_result id
Definition: app_queue.c:1090
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_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
static struct ast_smdi_interface * smdi_iface
static int base_encode(char *filename, FILE *so)
Performs a base 64 encode algorithm on the contents of a File.
struct ast_variable * next
Definition: config.h:82
static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
static int play_message_category(struct ast_channel *chan, const char *category)
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
static void populate_defaults(struct ast_vm_user *vmu)
Sets default voicemail system options to a voicemail user.
#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
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 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.
static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
char serveremail[80]
int ast_adsi_unload_session(struct ast_channel *chan)
Definition: adsi.c:87
static int maxgreet
static int load_module(void)
static int inchar(struct baseio *bio, FILE *fi)
utility used by base_encode()
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
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.
struct ast_channel * ast_dummy_channel_alloc(void)
Create a fake channel structure.
Definition: channel.c:1391
#define DELETE(a, b, c, d)
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
static void start_poll_thread(void)
#define OPERATOR_EXIT
Say numbers and dates (maybe words one day too)
#define tdesc
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
#define RESULT_SUCCESS
Definition: cli.h:39
static int check_mime(const char *str)
Check if the string would need encoding within the MIME standard, to avoid confusing certain mail sof...
#define BASEMAXINLINE
#define VALID_DTMF
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.
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 char * app4
const char * ast_config_AST_VAR_DIR
Definition: asterisk.c:261
static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
#define VM_MESSAGEWRAP
static int write_password_to_file(const char *secretfn, const char *password)
static void delete_file(struct phoneprov_file *file)
static snd_pcm_format_t format
Definition: chan_alsa.c:93
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 int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
struct ast_channel_tech * tech
Definition: channel.h:743
char email[80]
#define ADSI_JUST_CENT
Definition: adsi.h:114
static char vm_pls_try_again[80]
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 vm_users_data_provider_get_helper(const struct ast_data_search *search, struct ast_data *data_root, struct ast_vm_user *user)
struct ast_event_sub * ast_event_unsubscribe(struct ast_event_sub *event_sub)
Un-subscribe from events.
Definition: event.c:987
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)
int newmessages
#define AST_FORMAT_GSM
Definition: frame.h:244
static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:247
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 char vm_passchanged[80]
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:172
#define ADSI_KEY_APPS
Definition: adsi.h:109
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
#define ENDL
struct ast_variable * ast_variable_new(const char *name, const char *value, const char *filename)
Definition: config.c:278
static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
Structure for mutex and tracking information.
Definition: lock.h:121
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.
An SMDI message waiting indicator message.
Definition: smdi.h:55
static char mailbox[AST_MAX_EXTENSION]
Definition: chan_mgcp.c:197
static char listen_control_pause_key[12]
static void free_zone(struct vm_zone *z)
The structure of the node handler.
Definition: data.h:245
#define VOICEMAIL_CONFIG
static const char * mbox(struct ast_vm_user *vmu, int id)
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
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
static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: config.c:2650
static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
#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
struct ao2_container * inprocess_container
#define ast_mutex_unlock(a)
Definition: lock.h:156
static char exitcontext[AST_MAX_CONTEXT]
#define DISPOSE(a, b)
static char fromstring[100]
#define VM_DELETE
static char prefix[MAX_PREFIX]
Definition: http.c:107
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
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.
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 char ext_pass_cmd[128]
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
#define MAX_NUM_CID_CONTEXTS
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 ...