Wed Jan 8 2020 09:49:41

Asterisk developer's documentation


app_voicemail_odbcstorage.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 #include "asterisk.h"
47 
48 #ifdef IMAP_STORAGE
49 #include <ctype.h>
50 #include <signal.h>
51 #include <pwd.h>
52 #ifdef USE_SYSTEM_IMAP
53 #include <imap/c-client.h>
54 #include <imap/imap4r1.h>
55 #include <imap/linkage.h>
56 #elif defined (USE_SYSTEM_CCLIENT)
57 #include <c-client/c-client.h>
58 #include <c-client/imap4r1.h>
59 #include <c-client/linkage.h>
60 #else
61 #include "c-client.h"
62 #include "imap4r1.h"
63 #include "linkage.h"
64 #endif
65 #endif
66 
67 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 426691 $")
68 
69 #include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
70 #include <sys/time.h>
71 #include <sys/stat.h>
72 #include <sys/mman.h>
73 #include <time.h>
74 #include <dirent.h>
75 #if defined(__FreeBSD__) || defined(__OpenBSD__)
76 #include <sys/wait.h>
77 #endif
78 
79 #include "asterisk/logger.h"
80 #include "asterisk/lock.h"
81 #include "asterisk/file.h"
82 #include "asterisk/channel.h"
83 #include "asterisk/pbx.h"
84 #include "asterisk/config.h"
85 #include "asterisk/say.h"
86 #include "asterisk/module.h"
87 #include "asterisk/adsi.h"
88 #include "asterisk/app.h"
89 #include "asterisk/manager.h"
90 #include "asterisk/dsp.h"
91 #include "asterisk/localtime.h"
92 #include "asterisk/cli.h"
93 #include "asterisk/utils.h"
94 #include "asterisk/stringfields.h"
95 #include "asterisk/smdi.h"
96 #include "asterisk/astobj2.h"
97 #include "asterisk/event.h"
98 #include "asterisk/taskprocessor.h"
99 #include "asterisk/test.h"
100 
101 #ifdef ODBC_STORAGE
102 #include "asterisk/res_odbc.h"
103 #endif
104 
105 #ifdef IMAP_STORAGE
106 #include "asterisk/threadstorage.h"
107 #endif
108 
109 /*** DOCUMENTATION
110  <application name="VoiceMail" language="en_US">
111  <synopsis>
112  Leave a Voicemail message.
113  </synopsis>
114  <syntax>
115  <parameter name="mailboxs" argsep="&amp;" required="true">
116  <argument name="mailbox1" argsep="@" required="true">
117  <argument name="mailbox" required="true" />
118  <argument name="context" />
119  </argument>
120  <argument name="mailbox2" argsep="@" multiple="true">
121  <argument name="mailbox" required="true" />
122  <argument name="context" />
123  </argument>
124  </parameter>
125  <parameter name="options">
126  <optionlist>
127  <option name="b">
128  <para>Play the <literal>busy</literal> greeting to the calling party.</para>
129  </option>
130  <option name="d">
131  <argument name="c" />
132  <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
133  if played during the greeting. Context defaults to the current context.</para>
134  </option>
135  <option name="g">
136  <argument name="#" required="true" />
137  <para>Use the specified amount of gain when recording the voicemail
138  message. The units are whole-number decibels (dB). Only works on supported
139  technologies, which is DAHDI only.</para>
140  </option>
141  <option name="s">
142  <para>Skip the playback of instructions for leaving a message to the
143  calling party.</para>
144  </option>
145  <option name="u">
146  <para>Play the <literal>unavailable</literal> greeting.</para>
147  </option>
148  <option name="U">
149  <para>Mark message as <literal>URGENT</literal>.</para>
150  </option>
151  <option name="P">
152  <para>Mark message as <literal>PRIORITY</literal>.</para>
153  </option>
154  </optionlist>
155  </parameter>
156  </syntax>
157  <description>
158  <para>This application allows the calling party to leave a message for the specified
159  list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
160  the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
161  exist.</para>
162  <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
163  <enumlist>
164  <enum name="0">
165  <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
166  </enum>
167  <enum name="*">
168  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
169  </enum>
170  </enumlist>
171  <para>This application will set the following channel variable upon completion:</para>
172  <variablelist>
173  <variable name="VMSTATUS">
174  <para>This indicates the status of the execution of the VoiceMail application.</para>
175  <value name="SUCCESS" />
176  <value name="USEREXIT" />
177  <value name="FAILED" />
178  </variable>
179  </variablelist>
180  </description>
181  <see-also>
182  <ref type="application">VoiceMailMain</ref>
183  </see-also>
184  </application>
185  <application name="VoiceMailMain" language="en_US">
186  <synopsis>
187  Check Voicemail messages.
188  </synopsis>
189  <syntax>
190  <parameter name="mailbox" required="true" argsep="@">
191  <argument name="mailbox" />
192  <argument name="context" />
193  </parameter>
194  <parameter name="options">
195  <optionlist>
196  <option name="p">
197  <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
198  the mailbox that is entered by the caller.</para>
199  </option>
200  <option name="g">
201  <argument name="#" required="true" />
202  <para>Use the specified amount of gain when recording a voicemail message.
203  The units are whole-number decibels (dB).</para>
204  </option>
205  <option name="s">
206  <para>Skip checking the passcode for the mailbox.</para>
207  </option>
208  <option name="a">
209  <argument name="folder" required="true" />
210  <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
211  Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
212  <enumlist>
213  <enum name="0"><para>INBOX</para></enum>
214  <enum name="1"><para>Old</para></enum>
215  <enum name="2"><para>Work</para></enum>
216  <enum name="3"><para>Family</para></enum>
217  <enum name="4"><para>Friends</para></enum>
218  <enum name="5"><para>Cust1</para></enum>
219  <enum name="6"><para>Cust2</para></enum>
220  <enum name="7"><para>Cust3</para></enum>
221  <enum name="8"><para>Cust4</para></enum>
222  <enum name="9"><para>Cust5</para></enum>
223  </enumlist>
224  </option>
225  </optionlist>
226  </parameter>
227  </syntax>
228  <description>
229  <para>This application allows the calling party to check voicemail messages. A specific
230  <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
231  may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
232  be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
233  <literal>default</literal> context will be used.</para>
234  <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
235  or Password, and the extension exists:</para>
236  <enumlist>
237  <enum name="*">
238  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
239  </enum>
240  </enumlist>
241  </description>
242  <see-also>
243  <ref type="application">VoiceMail</ref>
244  </see-also>
245  </application>
246  <application name="MailboxExists" language="en_US">
247  <synopsis>
248  Check to see if Voicemail mailbox exists.
249  </synopsis>
250  <syntax>
251  <parameter name="mailbox" required="true" argsep="@">
252  <argument name="mailbox" required="true" />
253  <argument name="context" />
254  </parameter>
255  <parameter name="options">
256  <para>None options.</para>
257  </parameter>
258  </syntax>
259  <description>
260  <para>Check to see if the specified <replaceable>mailbox</replaceable> exists. If no voicemail
261  <replaceable>context</replaceable> is specified, the <literal>default</literal> context
262  will be used.</para>
263  <para>This application will set the following channel variable upon completion:</para>
264  <variablelist>
265  <variable name="VMBOXEXISTSSTATUS">
266  <para>This will contain the status of the execution of the MailboxExists application.
267  Possible values include:</para>
268  <value name="SUCCESS" />
269  <value name="FAILED" />
270  </variable>
271  </variablelist>
272  </description>
273  </application>
274  <application name="VMAuthenticate" language="en_US">
275  <synopsis>
276  Authenticate with Voicemail passwords.
277  </synopsis>
278  <syntax>
279  <parameter name="mailbox" required="true" argsep="@">
280  <argument name="mailbox" />
281  <argument name="context" />
282  </parameter>
283  <parameter name="options">
284  <optionlist>
285  <option name="s">
286  <para>Skip playing the initial prompts.</para>
287  </option>
288  </optionlist>
289  </parameter>
290  </syntax>
291  <description>
292  <para>This application behaves the same way as the Authenticate application, but the passwords
293  are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
294  specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
295  is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
296  mailbox.</para>
297  <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
298  or Password, and the extension exists:</para>
299  <enumlist>
300  <enum name="*">
301  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
302  </enum>
303  </enumlist>
304  </description>
305  </application>
306  <application name="VMSayName" language="en_US">
307  <synopsis>
308  Play the name of a voicemail user
309  </synopsis>
310  <syntax>
311  <parameter name="mailbox" required="true" argsep="@">
312  <argument name="mailbox" />
313  <argument name="context" />
314  </parameter>
315  </syntax>
316  <description>
317  <para>This application will say the recorded name of the voicemail user specified as the
318  argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
319  </description>
320  </application>
321  <function name="MAILBOX_EXISTS" language="en_US">
322  <synopsis>
323  Tell if a mailbox is configured.
324  </synopsis>
325  <syntax argsep="@">
326  <parameter name="mailbox" required="true" />
327  <parameter name="context" />
328  </syntax>
329  <description>
330  <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.
331  If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
332  context.</para>
333  </description>
334  </function>
335  <manager name="VoicemailUsersList" language="en_US">
336  <synopsis>
337  List All Voicemail User Information.
338  </synopsis>
339  <syntax>
340  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
341  </syntax>
342  <description>
343  </description>
344  </manager>
345  ***/
346 
347 #ifdef IMAP_STORAGE
348 static char imapserver[48];
349 static char imapport[8];
350 static char imapflags[128];
351 static char imapfolder[64];
352 static char imapparentfolder[64] = "\0";
353 static char greetingfolder[64];
354 static char authuser[32];
355 static char authpassword[42];
356 static int imapversion = 1;
357 
358 static int expungeonhangup = 1;
359 static int imapgreetings = 0;
360 static char delimiter = '\0';
361 
362 struct vm_state;
363 struct ast_vm_user;
364 
365 AST_THREADSTORAGE(ts_vmstate);
366 
367 /* Forward declarations for IMAP */
368 static int init_mailstream(struct vm_state *vms, int box);
369 static void write_file(char *filename, char *buffer, unsigned long len);
370 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
371 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
372 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
373 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
374 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
375 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
376 static void vmstate_insert(struct vm_state *vms);
377 static void vmstate_delete(struct vm_state *vms);
378 static void set_update(MAILSTREAM * stream);
379 static void init_vm_state(struct vm_state *vms);
380 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
381 static void get_mailbox_delimiter(MAILSTREAM *stream);
382 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
383 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
384 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);
385 static void update_messages_by_imapuser(const char *user, unsigned long number);
386 static int vm_delete(char *file);
387 
388 static int imap_remove_file (char *dir, int msgnum);
389 static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
390 static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
391 static void check_quota(struct vm_state *vms, char *mailbox);
392 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
393 struct vmstate {
394  struct vm_state *vms;
395  AST_LIST_ENTRY(vmstate) list;
396 };
397 
398 static AST_LIST_HEAD_STATIC(vmstates, vmstate);
399 
400 #endif
401 
402 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
403 
404 #define COMMAND_TIMEOUT 5000
405 /* Don't modify these here; set your umask at runtime instead */
406 #define VOICEMAIL_DIR_MODE 0777
407 #define VOICEMAIL_FILE_MODE 0666
408 #define CHUNKSIZE 65536
409 
410 #define VOICEMAIL_CONFIG "voicemail.conf"
411 #define ASTERISK_USERNAME "asterisk"
412 
413 /* Define fast-forward, pause, restart, and reverse keys
414  * while listening to a voicemail message - these are
415  * strings, not characters */
416 #define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
417 #define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
418 #define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
419 #define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
420 #define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
421 #define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
422 
423 /* Default mail command to mail voicemail. Change it with the
424  * mailcmd= command in voicemail.conf */
425 #define SENDMAIL "/usr/sbin/sendmail -t"
426 
427 #define INTRO "vm-intro"
428 
429 #define MAXMSG 100
430 #define MAXMSGLIMIT 9999
431 
432 #define MINPASSWORD 0 /*!< Default minimum mailbox password length */
433 
434 #define BASELINELEN 72
435 #define BASEMAXINLINE 256
436 #ifdef IMAP_STORAGE
437 #define ENDL "\r\n"
438 #else
439 #define ENDL "\n"
440 #endif
441 
442 #define MAX_DATETIME_FORMAT 512
443 #define MAX_NUM_CID_CONTEXTS 10
444 
445 #define VM_REVIEW (1 << 0) /*!< After recording, permit the caller to review the recording before saving */
446 #define VM_OPERATOR (1 << 1) /*!< Allow 0 to be pressed to go to 'o' extension */
447 #define VM_SAYCID (1 << 2) /*!< Repeat the CallerID info during envelope playback */
448 #define VM_SVMAIL (1 << 3) /*!< Allow the user to compose a new VM from within VoicemailMain */
449 #define VM_ENVELOPE (1 << 4) /*!< Play the envelope information (who-from, time received, etc.) */
450 #define VM_SAYDURATION (1 << 5) /*!< Play the length of the message during envelope playback */
451 #define VM_SKIPAFTERCMD (1 << 6) /*!< After deletion, assume caller wants to go to the next message */
452 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
453 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
454 #define VM_PBXSKIP (1 << 9) /*!< Skip the [PBX] preamble in the Subject line of emails */
455 #define VM_DIRECFORWARD (1 << 10) /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
456 #define VM_ATTACH (1 << 11) /*!< Attach message to voicemail notifications? */
457 #define VM_DELETE (1 << 12) /*!< Delete message after sending notification */
458 #define VM_ALLOCED (1 << 13) /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
459 #define VM_SEARCH (1 << 14) /*!< Search all contexts for a matching mailbox */
460 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
461 #define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
462 #define VM_MESSAGEWRAP (1 << 17) /*!< Wrap around from the last message to the first, and vice-versa */
463 #define VM_FWDURGAUTO (1 << 18) /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
464 #define ERROR_LOCK_PATH -100
465 #define OPERATOR_EXIT 300
466 
467 
468 enum vm_box {
475 };
476 
478  OPT_SILENT = (1 << 0),
479  OPT_BUSY_GREETING = (1 << 1),
481  OPT_RECORDGAIN = (1 << 3),
483  OPT_AUTOPLAY = (1 << 6),
484  OPT_DTMFEXIT = (1 << 7),
485  OPT_MESSAGE_Urgent = (1 << 8),
487 };
488 
493  /* This *must* be the last value in this enum! */
495 };
496 
501 };
502 
513 });
514 
515 static int load_config(int reload);
516 #ifdef TEST_FRAMEWORK
517 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
518 #endif
519 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
520 
521 /*! \page vmlang Voicemail Language Syntaxes Supported
522 
523  \par Syntaxes supported, not really language codes.
524  \arg \b en - English
525  \arg \b de - German
526  \arg \b es - Spanish
527  \arg \b fr - French
528  \arg \b it - Italian
529  \arg \b nl - Dutch
530  \arg \b pt - Portuguese
531  \arg \b pt_BR - Portuguese (Brazil)
532  \arg \b gr - Greek
533  \arg \b no - Norwegian
534  \arg \b se - Swedish
535  \arg \b tw - Chinese (Taiwan)
536  \arg \b ua - Ukrainian
537 
538 German requires the following additional soundfile:
539 \arg \b 1F einE (feminine)
540 
541 Spanish requires the following additional soundfile:
542 \arg \b 1M un (masculine)
543 
544 Dutch, Greek, Portuguese & Spanish require the following additional soundfiles:
545 \arg \b vm-INBOXs singular of 'new'
546 \arg \b vm-Olds singular of 'old/heard/read'
547 
548 NB these are plural:
549 \arg \b vm-INBOX nieuwe (nl)
550 \arg \b vm-Old oude (nl)
551 
552 Polish uses:
553 \arg \b vm-new-a 'new', feminine singular accusative
554 \arg \b vm-new-e 'new', feminine plural accusative
555 \arg \b vm-new-ych 'new', feminine plural genitive
556 \arg \b vm-old-a 'old', feminine singular accusative
557 \arg \b vm-old-e 'old', feminine plural accusative
558 \arg \b vm-old-ych 'old', feminine plural genitive
559 \arg \b digits/1-a 'one', not always same as 'digits/1'
560 \arg \b digits/2-ie 'two', not always same as 'digits/2'
561 
562 Swedish uses:
563 \arg \b vm-nytt singular of 'new'
564 \arg \b vm-nya plural of 'new'
565 \arg \b vm-gammalt singular of 'old'
566 \arg \b vm-gamla plural of 'old'
567 \arg \b digits/ett 'one', not always same as 'digits/1'
568 
569 Norwegian uses:
570 \arg \b vm-ny singular of 'new'
571 \arg \b vm-nye plural of 'new'
572 \arg \b vm-gammel singular of 'old'
573 \arg \b vm-gamle plural of 'old'
574 
575 Dutch also uses:
576 \arg \b nl-om 'at'?
577 
578 Greek, Portuguese & Spanish also uses:
579 \arg \b vm-youhaveno
580 
581 Italian requires the following additional soundfile:
582 
583 For vm_intro_it:
584 \arg \b vm-nuovo new
585 \arg \b vm-nuovi new plural
586 \arg \b vm-vecchio old
587 \arg \b vm-vecchi old plural
588 
589 Chinese (Taiwan) requires the following additional soundfile:
590 \arg \b vm-tong A class-word for call (tong1)
591 \arg \b vm-ri A class-word for day (ri4)
592 \arg \b vm-you You (ni3)
593 \arg \b vm-haveno Have no (mei2 you3)
594 \arg \b vm-have Have (you3)
595 \arg \b vm-listen To listen (yao4 ting1)
596 
597 
598 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
599 spelled among others when you have to change folder. For the above reasons, vm-INBOX
600 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
601 
602 */
603 
604 struct baseio {
605  int iocp;
606  int iolen;
607  int linelength;
608  int ateof;
609  unsigned char iobuf[BASEMAXINLINE];
610 };
611 
612 /*! Structure for linked list of users
613  * Use ast_vm_user_destroy() to free one of these structures. */
614 struct ast_vm_user {
615  char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
616  char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
617  char password[80]; /*!< Secret pin code, numbers only */
618  char fullname[80]; /*!< Full name, for directory app */
619  char email[80]; /*!< E-mail address */
620  char *emailsubject; /*!< E-mail subject */
621  char *emailbody; /*!< E-mail body */
622  char pager[80]; /*!< E-mail address to pager (no attachment) */
623  char serveremail[80]; /*!< From: Mail address */
624  char language[MAX_LANGUAGE]; /*!< Config: Language setting */
625  char zonetag[80]; /*!< Time zone */
626  char locale[20]; /*!< The locale (for presentation of date/time) */
627  char callback[80];
628  char dialout[80];
629  char uniqueid[80]; /*!< Unique integer identifier */
630  char exit[80];
631  char attachfmt[20]; /*!< Attachment format */
632  unsigned int flags; /*!< VM_ flags */
633  int saydurationm;
634  int minsecs; /*!< Minimum number of seconds per message for this mailbox */
635  int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
636  int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
637  int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
638  int passwordlocation; /*!< Storage location of the password */
639 #ifdef IMAP_STORAGE
640  char imapuser[80]; /*!< IMAP server login */
641  char imappassword[80]; /*!< IMAP server password if authpassword not defined */
642  char imapfolder[64]; /*!< IMAP voicemail folder */
643  char imapvmshareid[80]; /*!< Shared mailbox ID to use rather than the dialed one */
644  int imapversion; /*!< If configuration changes, use the new values */
645 #endif
646  double volgain; /*!< Volume gain for voicemails sent via email */
648 };
649 
650 /*! Voicemail time zones */
651 struct vm_zone {
652  AST_LIST_ENTRY(vm_zone) list;
653  char name[80];
654  char timezone[80];
655  char msg_format[512];
656 };
657 
658 #define VMSTATE_MAX_MSG_ARRAY 256
659 
660 /*! Voicemail mailbox state */
661 struct vm_state {
662  char curbox[80];
663  char username[80];
664  char context[80];
665  char curdir[PATH_MAX];
666  char vmbox[PATH_MAX];
667  char fn[PATH_MAX];
668  char intro[PATH_MAX];
669  int *deleted;
670  int *heard;
671  int dh_arraysize; /* used for deleted / heard allocation */
672  int curmsg;
673  int lastmsg;
674  int newmessages;
675  int oldmessages;
676  int urgentmessages;
677  int starting;
678  int repeats;
679 #ifdef IMAP_STORAGE
681  int updated; /*!< decremented on each mail check until 1 -allows delay */
682  long *msgArray;
683  unsigned msg_array_max;
684  MAILSTREAM *mailstream;
685  int vmArrayIndex;
686  char imapuser[80]; /*!< IMAP server login */
687  char imapfolder[64]; /*!< IMAP voicemail folder */
688  int imapversion;
689  int interactive;
690  char introfn[PATH_MAX]; /*!< Name of prepended file */
691  unsigned int quota_limit;
692  unsigned int quota_usage;
693  struct vm_state *persist_vms;
694 #endif
695 };
696 
697 #ifdef ODBC_STORAGE
698 static char odbc_database[80];
699 static char odbc_table[80];
700 #define RETRIEVE(a,b,c,d) retrieve_file(a,b)
701 #define DISPOSE(a,b) remove_file(a,b)
702 #define STORE(a,b,c,d,e,f,g,h,i,j) store_file(a,b,c,d)
703 #define EXISTS(a,b,c,d) (message_exists(a,b))
704 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
705 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
706 #define DELETE(a,b,c,d) (delete_file(a,b))
707 #else
708 #ifdef IMAP_STORAGE
709 #define DISPOSE(a,b) (imap_remove_file(a,b))
710 #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))
711 #define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
712 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
713 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
714 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
715 #define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
716 #else
717 #define RETRIEVE(a,b,c,d)
718 #define DISPOSE(a,b)
719 #define STORE(a,b,c,d,e,f,g,h,i,j)
720 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
721 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
722 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
723 #define DELETE(a,b,c,d) (vm_delete(c))
724 #endif
725 #endif
726 
727 static char VM_SPOOL_DIR[PATH_MAX];
728 
729 static char ext_pass_cmd[128];
730 static char ext_pass_check_cmd[128];
731 
732 static int my_umask;
733 
734 #define PWDCHANGE_INTERNAL (1 << 1)
735 #define PWDCHANGE_EXTERNAL (1 << 2)
737 
738 #ifdef ODBC_STORAGE
739 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
740 #else
741 # ifdef IMAP_STORAGE
742 # define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
743 # else
744 # define tdesc "Comedian Mail (Voicemail System)"
745 # endif
746 #endif
747 
748 static char userscontext[AST_MAX_EXTENSION] = "default";
749 
750 static char *addesc = "Comedian Mail";
751 
752 /* Leave a message */
753 static char *app = "VoiceMail";
754 
755 /* Check mail, control, etc */
756 static char *app2 = "VoiceMailMain";
757 
758 static char *app3 = "MailboxExists";
759 static char *app4 = "VMAuthenticate";
760 
761 static char *sayname_app = "VMSayName";
762 
764 static AST_LIST_HEAD_STATIC(zones, vm_zone);
765 static char zonetag[80];
766 static char locale[20];
767 static int maxsilence;
768 static int maxmsg;
769 static int maxdeletedmsg;
770 static int silencethreshold = 128;
771 static char serveremail[80];
772 static char mailcmd[160]; /* Configurable mail cmd */
773 static char externnotify[160];
774 static struct ast_smdi_interface *smdi_iface = NULL;
775 static char vmfmts[80];
776 static double volgain;
777 static int vmminsecs;
778 static int vmmaxsecs;
779 static int maxgreet;
780 static int skipms;
781 static int maxlogins;
782 static int minpassword;
783 static int passwordlocation;
784 
785 /*! Poll mailboxes for changes since there is something external to
786  * app_voicemail that may change them. */
787 static unsigned int poll_mailboxes;
788 
789 /*! Polling frequency */
790 static unsigned int poll_freq;
791 /*! By default, poll every 30 seconds */
792 #define DEFAULT_POLL_FREQ 30
793 
795 static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
796 static pthread_t poll_thread = AST_PTHREADT_NULL;
797 static unsigned char poll_thread_run;
798 
799 /*! Subscription to ... MWI event subscriptions */
800 static struct ast_event_sub *mwi_sub_sub;
801 /*! Subscription to ... MWI event un-subscriptions */
803 
804 /*!
805  * \brief An MWI subscription
806  *
807  * This is so we can keep track of which mailboxes are subscribed to.
808  * This way, we know which mailboxes to poll when the pollmailboxes
809  * option is being used.
810  */
811 struct mwi_sub {
812  AST_RWLIST_ENTRY(mwi_sub) entry;
813  int old_urgent;
814  int old_new;
815  int old_old;
816  uint32_t uniqueid;
817  char mailbox[1];
818 };
819 
820 struct mwi_sub_task {
821  const char *mailbox;
822  const char *context;
823  uint32_t uniqueid;
824 };
825 
827 
829 
830 /* custom audio control prompts for voicemail playback */
833 static char listen_control_pause_key[12];
835 static char listen_control_stop_key[12];
836 
837 /* custom password sounds */
838 static char vm_password[80] = "vm-password";
839 static char vm_newpassword[80] = "vm-newpassword";
840 static char vm_passchanged[80] = "vm-passchanged";
841 static char vm_reenterpassword[80] = "vm-reenterpassword";
842 static char vm_mismatch[80] = "vm-mismatch";
843 static char vm_invalid_password[80] = "vm-invalid-password";
844 static char vm_pls_try_again[80] = "vm-pls-try-again";
845 
846 /*
847  * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
848  * 1. create a sound along the lines of "Please try again. When done, press the pound key" which could be spliced
849  * from existing sound clips. This would require some programming changes in the area of vm_forward options and also
850  * app.c's __ast_play_and_record function
851  * 2. create a sound prompt saying "Please try again. When done recording, press any key to stop and send the prepended
852  * message." At the time of this comment, I think this would require new voice work to be commissioned.
853  * 3. Something way different like providing instructions before a time out or a post-recording menu. This would require
854  * more effort than either of the other two.
855  */
856 static char vm_prepend_timeout[80] = "vm-then-pound";
857 
858 static struct ast_flags globalflags = {0};
859 
860 static int saydurationminfo;
861 
862 static char dialcontext[AST_MAX_CONTEXT] = "";
863 static char callcontext[AST_MAX_CONTEXT] = "";
864 static char exitcontext[AST_MAX_CONTEXT] = "";
865 
867 
868 
869 static char *emailbody = NULL;
870 static char *emailsubject = NULL;
871 static char *pagerbody = NULL;
872 static char *pagersubject = NULL;
873 static char fromstring[100];
874 static char pagerfromstring[100];
875 static char charset[32] = "ISO-8859-1";
876 
877 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
878 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
879 static int adsiver = 1;
880 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
881 static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
882 
883 /* Forward declarations - generic */
884 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
885 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);
886 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
887 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
888  char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
889  signed char record_gain, struct vm_state *vms, char *flag);
890 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
891 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
892 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);
893 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);
894 static void apply_options(struct ast_vm_user *vmu, const char *options);
895 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);
896 static int is_valid_dtmf(const char *key);
897 static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
898 static int write_password_to_file(const char *secretfn, const char *password);
899 static const char *substitute_escapes(const char *value);
900 static void free_user(struct ast_vm_user *vmu);
901 
903 
904 struct inprocess {
905  int count;
906  char *context;
907  char mailbox[0];
908 };
909 
910 static int inprocess_hash_fn(const void *obj, const int flags)
911 {
912  const struct inprocess *i = obj;
913  return atoi(i->mailbox);
914 }
915 
916 static int inprocess_cmp_fn(void *obj, void *arg, int flags)
917 {
918  struct inprocess *i = obj, *j = arg;
919  if (strcmp(i->mailbox, j->mailbox)) {
920  return 0;
921  }
922  return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
923 }
924 
925 static int inprocess_count(const char *context, const char *mailbox, int delta)
926 {
927  struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + strlen(context) + strlen(mailbox) + 2);
928  arg->context = arg->mailbox + strlen(mailbox) + 1;
929  strcpy(arg->mailbox, mailbox); /* SAFE */
930  strcpy(arg->context, context); /* SAFE */
931  ao2_lock(inprocess_container);
932  if ((i = ao2_find(inprocess_container, arg, 0))) {
933  int ret = ast_atomic_fetchadd_int(&i->count, delta);
934  ao2_unlock(inprocess_container);
935  ao2_ref(i, -1);
936  return ret;
937  }
938  if (delta < 0) {
939  ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
940  }
941  if (!(i = ao2_alloc(sizeof(*i) + strlen(context) + strlen(mailbox) + 2, NULL))) {
942  ao2_unlock(inprocess_container);
943  return 0;
944  }
945  i->context = i->mailbox + strlen(mailbox) + 1;
946  strcpy(i->mailbox, mailbox); /* SAFE */
947  strcpy(i->context, context); /* SAFE */
948  i->count = delta;
949  ao2_link(inprocess_container, i);
950  ao2_unlock(inprocess_container);
951  ao2_ref(i, -1);
952  return 0;
953 }
954 
955 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
956 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
957 #endif
958 
959 /*!
960  * \brief Strips control and non 7-bit clean characters from input string.
961  *
962  * \note To map control and none 7-bit characters to a 7-bit clean characters
963  * please use ast_str_encode_mine().
964  */
965 static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
966 {
967  char *bufptr = buf;
968  for (; *input; input++) {
969  if (*input < 32) {
970  continue;
971  }
972  *bufptr++ = *input;
973  if (bufptr == buf + buflen - 1) {
974  break;
975  }
976  }
977  *bufptr = '\0';
978  return buf;
979 }
980 
981 
982 /*!
983  * \brief Sets default voicemail system options to a voicemail user.
984  *
985  * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
986  * - all the globalflags
987  * - the saydurationminfo
988  * - the callcontext
989  * - the dialcontext
990  * - the exitcontext
991  * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
992  * - volume gain
993  * - emailsubject, emailbody set to NULL
994  */
995 static void populate_defaults(struct ast_vm_user *vmu)
996 {
997  ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
999  if (saydurationminfo) {
1001  }
1002  ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
1003  ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
1004  ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
1005  ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
1006  ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
1007  if (vmminsecs) {
1008  vmu->minsecs = vmminsecs;
1009  }
1010  if (vmmaxsecs) {
1011  vmu->maxsecs = vmmaxsecs;
1012  }
1013  if (maxmsg) {
1014  vmu->maxmsg = maxmsg;
1015  }
1016  if (maxdeletedmsg) {
1018  }
1019  vmu->volgain = volgain;
1020  ast_free(vmu->emailsubject);
1021  vmu->emailsubject = NULL;
1022  ast_free(vmu->emailbody);
1023  vmu->emailbody = NULL;
1024 #ifdef IMAP_STORAGE
1025  ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
1026 #endif
1027 }
1028 
1029 /*!
1030  * \brief Sets a a specific property value.
1031  * \param vmu The voicemail user object to work with.
1032  * \param var The name of the property to be set.
1033  * \param value The value to be set to the property.
1034  *
1035  * The property name must be one of the understood properties. See the source for details.
1036  */
1037 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
1038 {
1039  int x;
1040  if (!strcasecmp(var, "attach")) {
1041  ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
1042  } else if (!strcasecmp(var, "attachfmt")) {
1043  ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
1044  } else if (!strcasecmp(var, "serveremail")) {
1045  ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
1046  } else if (!strcasecmp(var, "emailbody")) {
1047  vmu->emailbody = ast_strdup(substitute_escapes(value));
1048  } else if (!strcasecmp(var, "emailsubject")) {
1050  } else if (!strcasecmp(var, "language")) {
1051  ast_copy_string(vmu->language, value, sizeof(vmu->language));
1052  } else if (!strcasecmp(var, "tz")) {
1053  ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
1054  } else if (!strcasecmp(var, "locale")) {
1055  ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
1056 #ifdef IMAP_STORAGE
1057  } else if (!strcasecmp(var, "imapuser")) {
1058  ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
1059  vmu->imapversion = imapversion;
1060  } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
1061  ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
1062  vmu->imapversion = imapversion;
1063  } else if (!strcasecmp(var, "imapfolder")) {
1064  ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
1065  } else if (!strcasecmp(var, "imapvmshareid")) {
1066  ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
1067  vmu->imapversion = imapversion;
1068 #endif
1069  } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
1070  ast_set2_flag(vmu, ast_true(value), VM_DELETE);
1071  } else if (!strcasecmp(var, "saycid")){
1072  ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
1073  } else if (!strcasecmp(var, "sendvoicemail")){
1074  ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
1075  } else if (!strcasecmp(var, "review")){
1076  ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
1077  } else if (!strcasecmp(var, "tempgreetwarn")){
1078  ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
1079  } else if (!strcasecmp(var, "messagewrap")){
1080  ast_set2_flag(vmu, ast_true(value), VM_MESSAGEWRAP);
1081  } else if (!strcasecmp(var, "operator")) {
1082  ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
1083  } else if (!strcasecmp(var, "envelope")){
1084  ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
1085  } else if (!strcasecmp(var, "moveheard")){
1086  ast_set2_flag(vmu, ast_true(value), VM_MOVEHEARD);
1087  } else if (!strcasecmp(var, "sayduration")){
1088  ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
1089  } else if (!strcasecmp(var, "saydurationm")){
1090  if (sscanf(value, "%30d", &x) == 1) {
1091  vmu->saydurationm = x;
1092  } else {
1093  ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
1094  }
1095  } else if (!strcasecmp(var, "forcename")){
1096  ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
1097  } else if (!strcasecmp(var, "forcegreetings")){
1098  ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
1099  } else if (!strcasecmp(var, "callback")) {
1100  ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
1101  } else if (!strcasecmp(var, "dialout")) {
1102  ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
1103  } else if (!strcasecmp(var, "exitcontext")) {
1104  ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
1105  } else if (!strcasecmp(var, "minsecs")) {
1106  if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
1107  vmu->minsecs = x;
1108  } else {
1109  ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
1110  vmu->minsecs = vmminsecs;
1111  }
1112  } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
1113  vmu->maxsecs = atoi(value);
1114  if (vmu->maxsecs <= 0) {
1115  ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
1116  vmu->maxsecs = vmmaxsecs;
1117  } else {
1118  vmu->maxsecs = atoi(value);
1119  }
1120  if (!strcasecmp(var, "maxmessage"))
1121  ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
1122  } else if (!strcasecmp(var, "maxmsg")) {
1123  vmu->maxmsg = atoi(value);
1124  /* Accept maxmsg=0 (Greetings only voicemail) */
1125  if (vmu->maxmsg < 0) {
1126  ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
1127  vmu->maxmsg = MAXMSG;
1128  } else if (vmu->maxmsg > MAXMSGLIMIT) {
1129  ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
1130  vmu->maxmsg = MAXMSGLIMIT;
1131  }
1132  } else if (!strcasecmp(var, "nextaftercmd")) {
1133  ast_set2_flag(vmu, ast_true(value), VM_SKIPAFTERCMD);
1134  } else if (!strcasecmp(var, "backupdeleted")) {
1135  if (sscanf(value, "%30d", &x) == 1)
1136  vmu->maxdeletedmsg = x;
1137  else if (ast_true(value))
1138  vmu->maxdeletedmsg = MAXMSG;
1139  else
1140  vmu->maxdeletedmsg = 0;
1141 
1142  if (vmu->maxdeletedmsg < 0) {
1143  ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
1144  vmu->maxdeletedmsg = MAXMSG;
1145  } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
1146  ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
1147  vmu->maxdeletedmsg = MAXMSGLIMIT;
1148  }
1149  } else if (!strcasecmp(var, "volgain")) {
1150  sscanf(value, "%30lf", &vmu->volgain);
1151  } else if (!strcasecmp(var, "passwordlocation")) {
1152  if (!strcasecmp(value, "spooldir")) {
1154  } else {
1156  }
1157  } else if (!strcasecmp(var, "options")) {
1158  apply_options(vmu, value);
1159  }
1160 }
1161 
1162 static char *vm_check_password_shell(char *command, char *buf, size_t len)
1163 {
1164  int fds[2], pid = 0;
1165 
1166  memset(buf, 0, len);
1167 
1168  if (pipe(fds)) {
1169  snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
1170  } else {
1171  /* good to go*/
1172  pid = ast_safe_fork(0);
1173 
1174  if (pid < 0) {
1175  /* ok maybe not */
1176  close(fds[0]);
1177  close(fds[1]);
1178  snprintf(buf, len, "FAILURE: Fork failed");
1179  } else if (pid) {
1180  /* parent */
1181  close(fds[1]);
1182  if (read(fds[0], buf, len) < 0) {
1183  ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
1184  }
1185  close(fds[0]);
1186  } else {
1187  /* child */
1189  AST_APP_ARG(v)[20];
1190  );
1191  char *mycmd = ast_strdupa(command);
1192 
1193  close(fds[0]);
1194  dup2(fds[1], STDOUT_FILENO);
1195  close(fds[1]);
1196  ast_close_fds_above_n(STDOUT_FILENO);
1197 
1198  AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
1199 
1200  execv(arg.v[0], arg.v);
1201  printf("FAILURE: %s", strerror(errno));
1202  _exit(0);
1203  }
1204  }
1205  return buf;
1206 }
1207 
1208 /*!
1209  * \brief Check that password meets minimum required length
1210  * \param vmu The voicemail user to change the password for.
1211  * \param password The password string to check
1212  *
1213  * \return zero on ok, 1 on not ok.
1214  */
1215 static int check_password(struct ast_vm_user *vmu, char *password)
1216 {
1217  /* check minimum length */
1218  if (strlen(password) < minpassword)
1219  return 1;
1220  /* check that password does not contain '*' character */
1221  if (!ast_strlen_zero(password) && password[0] == '*')
1222  return 1;
1223  if (!ast_strlen_zero(ext_pass_check_cmd)) {
1224  char cmd[255], buf[255];
1225 
1226  ast_log(AST_LOG_DEBUG, "Verify password policies for %s\n", password);
1227 
1228  snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
1229  if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
1230  ast_debug(5, "Result: %s\n", buf);
1231  if (!strncasecmp(buf, "VALID", 5)) {
1232  ast_debug(3, "Passed password check: '%s'\n", buf);
1233  return 0;
1234  } else if (!strncasecmp(buf, "FAILURE", 7)) {
1235  ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
1236  return 0;
1237  } else {
1238  ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
1239  return 1;
1240  }
1241  }
1242  }
1243  return 0;
1244 }
1245 
1246 /*!
1247  * \brief Performs a change of the voicemail passowrd in the realtime engine.
1248  * \param vmu The voicemail user to change the password for.
1249  * \param password The new value to be set to the password for this user.
1250  *
1251  * This only works if there is a realtime engine configured.
1252  * This is called from the (top level) vm_change_password.
1253  *
1254  * \return zero on success, -1 on error.
1255  */
1256 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
1257 {
1258  int res = -1;
1259  if (!strcmp(vmu->password, password)) {
1260  /* No change (but an update would return 0 rows updated, so we opt out here) */
1261  return 0;
1262  }
1263 
1264  if (strlen(password) > 10) {
1265  ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
1266  }
1267  if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
1268  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
1269  ast_copy_string(vmu->password, password, sizeof(vmu->password));
1270  res = 0;
1271  }
1272  return res;
1273 }
1274 
1275 /*!
1276  * \brief Destructively Parse options and apply.
1277  */
1278 static void apply_options(struct ast_vm_user *vmu, const char *options)
1279 {
1280  char *stringp;
1281  char *s;
1282  char *var, *value;
1283  stringp = ast_strdupa(options);
1284  while ((s = strsep(&stringp, "|"))) {
1285  value = s;
1286  if ((var = strsep(&value, "=")) && value) {
1287  apply_option(vmu, var, value);
1288  }
1289  }
1290 }
1291 
1292 /*!
1293  * \brief Loads the options specific to a voicemail user.
1294  *
1295  * This is called when a vm_user structure is being set up, such as from load_options.
1296  */
1297 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
1298 {
1299  for (; var; var = var->next) {
1300  if (!strcasecmp(var->name, "vmsecret")) {
1301  ast_copy_string(retval->password, var->value, sizeof(retval->password));
1302  } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
1303  if (ast_strlen_zero(retval->password)) {
1304  if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
1305  ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
1306  "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
1307  } else {
1308  ast_copy_string(retval->password, var->value, sizeof(retval->password));
1309  }
1310  }
1311  } else if (!strcasecmp(var->name, "uniqueid")) {
1312  ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
1313  } else if (!strcasecmp(var->name, "pager")) {
1314  ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
1315  } else if (!strcasecmp(var->name, "email")) {
1316  ast_copy_string(retval->email, var->value, sizeof(retval->email));
1317  } else if (!strcasecmp(var->name, "fullname")) {
1318  ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
1319  } else if (!strcasecmp(var->name, "context")) {
1320  ast_copy_string(retval->context, var->value, sizeof(retval->context));
1321  } else if (!strcasecmp(var->name, "emailsubject")) {
1322  ast_free(retval->emailsubject);
1324  } else if (!strcasecmp(var->name, "emailbody")) {
1325  ast_free(retval->emailbody);
1326  retval->emailbody = ast_strdup(substitute_escapes(var->value));
1327 #ifdef IMAP_STORAGE
1328  } else if (!strcasecmp(var->name, "imapuser")) {
1329  ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
1330  retval->imapversion = imapversion;
1331  } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
1332  ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
1333  retval->imapversion = imapversion;
1334  } else if (!strcasecmp(var->name, "imapfolder")) {
1335  ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
1336  } else if (!strcasecmp(var->name, "imapvmshareid")) {
1337  ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
1338  retval->imapversion = imapversion;
1339 #endif
1340  } else
1341  apply_option(retval, var->name, var->value);
1342  }
1343 }
1344 
1345 /*!
1346  * \brief Determines if a DTMF key entered is valid.
1347  * \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.
1348  *
1349  * Tests the character entered against the set of valid DTMF characters.
1350  * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1351  */
1352 static int is_valid_dtmf(const char *key)
1353 {
1354  int i;
1355  char *local_key = ast_strdupa(key);
1356 
1357  for (i = 0; i < strlen(key); ++i) {
1358  if (!strchr(VALID_DTMF, *local_key)) {
1359  ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1360  return 0;
1361  }
1362  local_key++;
1363  }
1364  return 1;
1365 }
1366 
1367 /*!
1368  * \brief Finds a voicemail user from the realtime engine.
1369  * \param ivm
1370  * \param context
1371  * \param mailbox
1372  *
1373  * 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.
1374  *
1375  * \return The ast_vm_user structure for the user that was found.
1376  */
1377 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1378 {
1379  struct ast_variable *var;
1380  struct ast_vm_user *retval;
1381 
1382  if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1383  if (ivm) {
1384  memset(retval, 0, sizeof(*retval));
1385  }
1386  populate_defaults(retval);
1387  if (!ivm) {
1388  ast_set_flag(retval, VM_ALLOCED);
1389  }
1390  if (mailbox) {
1391  ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1392  }
1393  if (!context && ast_test_flag((&globalflags), VM_SEARCH)) {
1394  var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1395  } else {
1396  var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1397  }
1398  if (var) {
1399  apply_options_full(retval, var);
1400  ast_variables_destroy(var);
1401  } else {
1402  if (!ivm)
1403  free_user(retval);
1404  retval = NULL;
1405  }
1406  }
1407  return retval;
1408 }
1409 
1410 /*!
1411  * \brief Finds a voicemail user from the users file or the realtime engine.
1412  * \param ivm
1413  * \param context
1414  * \param mailbox
1415  *
1416  * \return The ast_vm_user structure for the user that was found.
1417  */
1418 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1419 {
1420  /* This function could be made to generate one from a database, too */
1421  struct ast_vm_user *vmu = NULL, *cur;
1422  AST_LIST_LOCK(&users);
1423 
1424  if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
1425  context = "default";
1426 
1427  AST_LIST_TRAVERSE(&users, cur, list) {
1428 #ifdef IMAP_STORAGE
1429  if (cur->imapversion != imapversion) {
1430  continue;
1431  }
1432 #endif
1433  if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1434  break;
1435  if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1436  break;
1437  }
1438  if (cur) {
1439  /* Make a copy, so that on a reload, we have no race */
1440  if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
1441  *vmu = *cur;
1442  if (!ivm) {
1443  vmu->emailbody = ast_strdup(cur->emailbody);
1444  vmu->emailsubject = ast_strdup(cur->emailsubject);
1445  }
1446  ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1447  AST_LIST_NEXT(vmu, list) = NULL;
1448  }
1449  } else
1450  vmu = find_user_realtime(ivm, context, mailbox);
1452  return vmu;
1453 }
1454 
1455 /*!
1456  * \brief Resets a user password to a specified password.
1457  * \param context
1458  * \param mailbox
1459  * \param newpass
1460  *
1461  * This does the actual change password work, called by the vm_change_password() function.
1462  *
1463  * \return zero on success, -1 on error.
1464  */
1465 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
1466 {
1467  /* This function could be made to generate one from a database, too */
1468  struct ast_vm_user *cur;
1469  int res = -1;
1470  AST_LIST_LOCK(&users);
1471  AST_LIST_TRAVERSE(&users, cur, list) {
1472  if ((!context || !strcasecmp(context, cur->context)) &&
1473  (!strcasecmp(mailbox, cur->mailbox)))
1474  break;
1475  }
1476  if (cur) {
1477  ast_copy_string(cur->password, newpass, sizeof(cur->password));
1478  res = 0;
1479  }
1481  return res;
1482 }
1483 
1484 /*!
1485  * \brief Check if configuration file is valid
1486  */
1487 static inline int valid_config(const struct ast_config *cfg)
1488 {
1489  return cfg && cfg != CONFIG_STATUS_FILEINVALID;
1490 }
1491 
1492 /*!
1493  * \brief The handler for the change password option.
1494  * \param vmu The voicemail user to work with.
1495  * \param newpassword The new password (that has been gathered from the appropriate prompting).
1496  * 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.
1497  * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
1498  */
1499 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
1500 {
1501  struct ast_config *cfg = NULL;
1502  struct ast_variable *var = NULL;
1503  struct ast_category *cat = NULL;
1504  char *category = NULL, *value = NULL, *new = NULL;
1505  const char *tmp = NULL;
1506  struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
1507  char secretfn[PATH_MAX] = "";
1508  int found = 0;
1509 
1510  if (!change_password_realtime(vmu, newpassword))
1511  return;
1512 
1513  /* check if we should store the secret in the spool directory next to the messages */
1514  switch (vmu->passwordlocation) {
1515  case OPT_PWLOC_SPOOLDIR:
1516  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
1517  if (write_password_to_file(secretfn, newpassword) == 0) {
1518  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
1519  ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
1520  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1521  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1522  break;
1523  } else {
1524  ast_verb(4, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
1525  }
1526  /* Fall-through */
1528  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
1529  while ((category = ast_category_browse(cfg, category))) {
1530  if (!strcasecmp(category, vmu->context)) {
1531  if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
1532  ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
1533  break;
1534  }
1535  value = strstr(tmp, ",");
1536  if (!value) {
1537  new = ast_alloca(strlen(newpassword)+1);
1538  sprintf(new, "%s", newpassword);
1539  } else {
1540  new = ast_alloca((strlen(value) + strlen(newpassword) + 1));
1541  sprintf(new, "%s%s", newpassword, value);
1542  }
1543  if (!(cat = ast_category_get(cfg, category))) {
1544  ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
1545  break;
1546  }
1547  ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
1548  found = 1;
1549  }
1550  }
1551  /* save the results */
1552  if (found) {
1553  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
1554  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1555  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1556  ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
1557  ast_config_destroy(cfg);
1558  break;
1559  }
1560 
1561  ast_config_destroy(cfg);
1562  }
1563  /* Fall-through */
1564  case OPT_PWLOC_USERSCONF:
1565  /* check users.conf and update the password stored for the mailbox */
1566  /* if no vmsecret entry exists create one. */
1567  if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
1568  ast_debug(4, "we are looking for %s\n", vmu->mailbox);
1569  for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
1570  ast_debug(4, "users.conf: %s\n", category);
1571  if (!strcasecmp(category, vmu->mailbox)) {
1572  if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
1573  ast_debug(3, "looks like we need to make vmsecret!\n");
1574  var = ast_variable_new("vmsecret", newpassword, "");
1575  } else {
1576  var = NULL;
1577  }
1578  new = ast_alloca(strlen(newpassword) + 1);
1579  sprintf(new, "%s", newpassword);
1580  if (!(cat = ast_category_get(cfg, category))) {
1581  ast_debug(4, "failed to get category!\n");
1582  ast_free(var);
1583  break;
1584  }
1585  if (!var) {
1586  ast_variable_update(cat, "vmsecret", new, NULL, 0);
1587  } else {
1588  ast_variable_append(cat, var);
1589  }
1590  found = 1;
1591  break;
1592  }
1593  }
1594  /* save the results and clean things up */
1595  if (found) {
1596  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
1597  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1598  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1599  ast_config_text_file_save("users.conf", cfg, "AppVoicemail");
1600  }
1601 
1602  ast_config_destroy(cfg);
1603  }
1604  }
1605 }
1606 
1607 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
1608 {
1609  char buf[255];
1610  snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
1611  ast_debug(1, "External password: %s\n",buf);
1612  if (!ast_safe_system(buf)) {
1613  ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
1614  ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
1615  /* Reset the password in memory, too */
1616  reset_user_pw(vmu->context, vmu->mailbox, newpassword);
1617  }
1618 }
1619 
1620 /*!
1621  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1622  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1623  * \param len The length of the path string that was written out.
1624  * \param context
1625  * \param ext
1626  * \param folder
1627  *
1628  * The path is constructed as
1629  * VM_SPOOL_DIRcontext/ext/folder
1630  *
1631  * \return zero on success, -1 on error.
1632  */
1633 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
1634 {
1635  return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
1636 }
1637 
1638 /*!
1639  * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
1640  * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
1641  * \param len The length of the path string that was written out.
1642  * \param dir
1643  * \param num
1644  *
1645  * The path is constructed as
1646  * VM_SPOOL_DIRcontext/ext/folder
1647  *
1648  * \return zero on success, -1 on error.
1649  */
1650 static int make_file(char *dest, const int len, const char *dir, const int num)
1651 {
1652  return snprintf(dest, len, "%s/msg%04d", dir, num);
1653 }
1654 
1655 /* same as mkstemp, but return a FILE * */
1656 static FILE *vm_mkftemp(char *template)
1657 {
1658  FILE *p = NULL;
1659  int pfd = mkstemp(template);
1660  chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
1661  if (pfd > -1) {
1662  p = fdopen(pfd, "w+");
1663  if (!p) {
1664  close(pfd);
1665  pfd = -1;
1666  }
1667  }
1668  return p;
1669 }
1670 
1671 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
1672  * \param dest String. base directory.
1673  * \param len Length of dest.
1674  * \param context String. Ignored if is null or empty string.
1675  * \param ext String. Ignored if is null or empty string.
1676  * \param folder String. Ignored if is null or empty string.
1677  * \return -1 on failure, 0 on success.
1678  */
1679 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
1680 {
1681  mode_t mode = VOICEMAIL_DIR_MODE;
1682  int res;
1683 
1684  make_dir(dest, len, context, ext, folder);
1685  if ((res = ast_mkdir(dest, mode))) {
1686  ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1687  return -1;
1688  }
1689  return 0;
1690 }
1691 
1692 static const char * const mailbox_folders[] = {
1693 #ifdef IMAP_STORAGE
1694  imapfolder,
1695 #else
1696  "INBOX",
1697 #endif
1698  "Old",
1699  "Work",
1700  "Family",
1701  "Friends",
1702  "Cust1",
1703  "Cust2",
1704  "Cust3",
1705  "Cust4",
1706  "Cust5",
1707  "Deleted",
1708  "Urgent",
1709 };
1710 
1711 static const char *mbox(struct ast_vm_user *vmu, int id)
1712 {
1713 #ifdef IMAP_STORAGE
1714  if (vmu && id == 0) {
1715  return vmu->imapfolder;
1716  }
1717 #endif
1718  return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
1719 }
1720 
1721 static int get_folder_by_name(const char *name)
1722 {
1723  size_t i;
1724 
1725  for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
1726  if (strcasecmp(name, mailbox_folders[i]) == 0) {
1727  return i;
1728  }
1729  }
1730 
1731  return -1;
1732 }
1733 
1734 static void free_user(struct ast_vm_user *vmu)
1735 {
1736  if (ast_test_flag(vmu, VM_ALLOCED)) {
1737 
1738  ast_free(vmu->emailbody);
1739  vmu->emailbody = NULL;
1740 
1741  ast_free(vmu->emailsubject);
1742  vmu->emailsubject = NULL;
1743 
1744  ast_free(vmu);
1745  }
1746 }
1747 
1748 static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
1749 
1750  int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
1751 
1752  /* remove old allocation */
1753  if (vms->deleted) {
1754  ast_free(vms->deleted);
1755  vms->deleted = NULL;
1756  }
1757  if (vms->heard) {
1758  ast_free(vms->heard);
1759  vms->heard = NULL;
1760  }
1761  vms->dh_arraysize = 0;
1762 
1763  if (arraysize > 0) {
1764  if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
1765  return -1;
1766  }
1767  if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
1768  ast_free(vms->deleted);
1769  vms->deleted = NULL;
1770  return -1;
1771  }
1772  vms->dh_arraysize = arraysize;
1773  }
1774 
1775  return 0;
1776 }
1777 
1778 /* All IMAP-specific functions should go in this block. This
1779  * keeps them from being spread out all over the code */
1780 #ifdef IMAP_STORAGE
1781 static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
1782 {
1783  char arg[10];
1784  struct vm_state *vms;
1785  unsigned long messageNum;
1786 
1787  /* If greetings aren't stored in IMAP, just delete the file */
1788  if (msgnum < 0 && !imapgreetings) {
1789  ast_filedelete(file, NULL);
1790  return;
1791  }
1792 
1793  if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1794  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);
1795  return;
1796  }
1797 
1798  if (msgnum < 0) {
1799  imap_delete_old_greeting(file, vms);
1800  return;
1801  }
1802 
1803  /* find real message number based on msgnum */
1804  /* this may be an index into vms->msgArray based on the msgnum. */
1805  messageNum = vms->msgArray[msgnum];
1806  if (messageNum == 0) {
1807  ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
1808  return;
1809  }
1810  if (option_debug > 2)
1811  ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
1812  /* delete message */
1813  snprintf (arg, sizeof(arg), "%lu", messageNum);
1814  ast_mutex_lock(&vms->lock);
1815  mail_setflag (vms->mailstream, arg, "\\DELETED");
1816  mail_expunge(vms->mailstream);
1817  ast_mutex_unlock(&vms->lock);
1818 }
1819 
1820 static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
1821 {
1822  struct vm_state *vms_p;
1823  char *file, *filename;
1824  char *attachment;
1825  int i;
1826  BODY *body;
1827 
1828  /* This function is only used for retrieval of IMAP greetings
1829  * regular messages are not retrieved this way, nor are greetings
1830  * if they are stored locally*/
1831  if (msgnum > -1 || !imapgreetings) {
1832  return 0;
1833  } else {
1834  file = strrchr(ast_strdupa(dir), '/');
1835  if (file)
1836  *file++ = '\0';
1837  else {
1838  ast_debug (1, "Failed to procure file name from directory passed.\n");
1839  return -1;
1840  }
1841  }
1842 
1843  /* check if someone is accessing this box right now... */
1844  if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) &&
1845  !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1846  /* Unlike when retrieving a message, it is reasonable not to be able to find a
1847  * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
1848  * that's all we need to do.
1849  */
1850  if (!(vms_p = create_vm_state_from_user(vmu))) {
1851  ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
1852  return -1;
1853  }
1854  }
1855 
1856  /* Greetings will never have a prepended message */
1857  *vms_p->introfn = '\0';
1858 
1859  ast_mutex_lock(&vms_p->lock);
1860  init_mailstream(vms_p, GREETINGS_FOLDER);
1861  if (!vms_p->mailstream) {
1862  ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL\n");
1863  ast_mutex_unlock(&vms_p->lock);
1864  return -1;
1865  }
1866 
1867  /*XXX Yuck, this could probably be done a lot better */
1868  for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
1869  mail_fetchstructure(vms_p->mailstream, i + 1, &body);
1870  /* We have the body, now we extract the file name of the first attachment. */
1871  if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1872  attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
1873  } else {
1874  ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
1875  ast_mutex_unlock(&vms_p->lock);
1876  return -1;
1877  }
1878  filename = strsep(&attachment, ".");
1879  if (!strcmp(filename, file)) {
1880  ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
1881  vms_p->msgArray[vms_p->curmsg] = i + 1;
1882  save_body(body, vms_p, "2", attachment, 0);
1883  ast_mutex_unlock(&vms_p->lock);
1884  return 0;
1885  }
1886  }
1887  ast_mutex_unlock(&vms_p->lock);
1888 
1889  return -1;
1890 }
1891 
1892 static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
1893 {
1894  BODY *body;
1895  char *header_content;
1896  char *attachedfilefmt;
1897  char buf[80];
1898  struct vm_state *vms;
1899  char text_file[PATH_MAX];
1900  FILE *text_file_ptr;
1901  int res = 0;
1902  struct ast_vm_user *vmu;
1903 
1904  if (!(vmu = find_user(NULL, context, mailbox))) {
1905  ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
1906  return -1;
1907  }
1908 
1909  if (msgnum < 0) {
1910  if (imapgreetings) {
1911  res = imap_retrieve_greeting(dir, msgnum, vmu);
1912  goto exit;
1913  } else {
1914  res = 0;
1915  goto exit;
1916  }
1917  }
1918 
1919  /* Before anything can happen, we need a vm_state so that we can
1920  * actually access the imap server through the vms->mailstream
1921  */
1922  if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1923  /* This should not happen. If it does, then I guess we'd
1924  * need to create the vm_state, extract which mailbox to
1925  * open, and then set up the msgArray so that the correct
1926  * IMAP message could be accessed. If I have seen correctly
1927  * though, the vms should be obtainable from the vmstates list
1928  * and should have its msgArray properly set up.
1929  */
1930  ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
1931  res = -1;
1932  goto exit;
1933  }
1934 
1935  make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
1936  snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
1937 
1938  /* Don't try to retrieve a message from IMAP if it already is on the file system */
1939  if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
1940  res = 0;
1941  goto exit;
1942  }
1943 
1944  if (option_debug > 2)
1945  ast_log(LOG_DEBUG, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
1946  if (vms->msgArray[msgnum] == 0) {
1947  ast_log(LOG_WARNING, "Trying to access unknown message\n");
1948  res = -1;
1949  goto exit;
1950  }
1951 
1952  /* This will only work for new messages... */
1953  ast_mutex_lock(&vms->lock);
1954  header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
1955  ast_mutex_unlock(&vms->lock);
1956  /* empty string means no valid header */
1957  if (ast_strlen_zero(header_content)) {
1958  ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
1959  res = -1;
1960  goto exit;
1961  }
1962 
1963  ast_mutex_lock(&vms->lock);
1964  mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
1965  ast_mutex_unlock(&vms->lock);
1966 
1967  /* We have the body, now we extract the file name of the first attachment. */
1968  if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1969  attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
1970  } else {
1971  ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
1972  res = -1;
1973  goto exit;
1974  }
1975 
1976  /* Find the format of the attached file */
1977 
1978  strsep(&attachedfilefmt, ".");
1979  if (!attachedfilefmt) {
1980  ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
1981  res = -1;
1982  goto exit;
1983  }
1984 
1985  save_body(body, vms, "2", attachedfilefmt, 0);
1986  if (save_body(body, vms, "3", attachedfilefmt, 1)) {
1987  *vms->introfn = '\0';
1988  }
1989 
1990  /* Get info from headers!! */
1991  snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
1992 
1993  if (!(text_file_ptr = fopen(text_file, "w"))) {
1994  ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
1995  }
1996 
1997  fprintf(text_file_ptr, "%s\n", "[message]");
1998 
1999  get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf));
2000  fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
2001  get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf));
2002  fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
2003  get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf));
2004  fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
2005  get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf));
2006  fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
2007  get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf));
2008  fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
2009  get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf));
2010  fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
2011  get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf));
2012  fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
2013  fclose(text_file_ptr);
2014 
2015 exit:
2016  free_user(vmu);
2017  return res;
2018 }
2019 
2020 static int folder_int(const char *folder)
2021 {
2022  /*assume a NULL folder means INBOX*/
2023  if (!folder) {
2024  return 0;
2025  }
2026  if (!strcasecmp(folder, imapfolder)) {
2027  return 0;
2028  } else if (!strcasecmp(folder, "Old")) {
2029  return 1;
2030  } else if (!strcasecmp(folder, "Work")) {
2031  return 2;
2032  } else if (!strcasecmp(folder, "Family")) {
2033  return 3;
2034  } else if (!strcasecmp(folder, "Friends")) {
2035  return 4;
2036  } else if (!strcasecmp(folder, "Cust1")) {
2037  return 5;
2038  } else if (!strcasecmp(folder, "Cust2")) {
2039  return 6;
2040  } else if (!strcasecmp(folder, "Cust3")) {
2041  return 7;
2042  } else if (!strcasecmp(folder, "Cust4")) {
2043  return 8;
2044  } else if (!strcasecmp(folder, "Cust5")) {
2045  return 9;
2046  } else if (!strcasecmp(folder, "Urgent")) {
2047  return 11;
2048  } else { /*assume they meant INBOX if folder is not found otherwise*/
2049  return 0;
2050  }
2051 }
2052 
2053 static int __messagecount(const char *context, const char *mailbox, const char *folder)
2054 {
2055  SEARCHPGM *pgm;
2056  SEARCHHEADER *hdr;
2057 
2058  struct ast_vm_user *vmu, vmus;
2059  struct vm_state *vms_p;
2060  int ret = 0;
2061  int fold = folder_int(folder);
2062  int urgent = 0;
2063 
2064  /* If URGENT, then look at INBOX */
2065  if (fold == 11) {
2066  fold = NEW_FOLDER;
2067  urgent = 1;
2068  }
2069 
2070  if (ast_strlen_zero(mailbox))
2071  return 0;
2072 
2073  /* We have to get the user before we can open the stream! */
2074  vmu = find_user(&vmus, context, mailbox);
2075  if (!vmu) {
2076  ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailbox, context);
2077  return -1;
2078  } else {
2079  /* No IMAP account available */
2080  if (vmu->imapuser[0] == '\0') {
2081  ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2082  return -1;
2083  }
2084  }
2085 
2086  /* No IMAP account available */
2087  if (vmu->imapuser[0] == '\0') {
2088  ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2089  free_user(vmu);
2090  return -1;
2091  }
2092  ast_assert(msgnum < vms->msg_array_max);
2093 
2094  /* check if someone is accessing this box right now... */
2095  vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
2096  if (!vms_p) {
2097  vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
2098  }
2099  if (vms_p) {
2100  ast_debug(3, "Returning before search - user is logged in\n");
2101  if (fold == 0) { /* INBOX */
2102  return urgent ? vms_p->urgentmessages : vms_p->newmessages;
2103  }
2104  if (fold == 1) { /* Old messages */
2105  return vms_p->oldmessages;
2106  }
2107  }
2108 
2109  /* add one if not there... */
2110  vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
2111  if (!vms_p) {
2112  vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
2113  }
2114 
2115  if (!vms_p) {
2116  vms_p = create_vm_state_from_user(vmu);
2117  }
2118  ret = init_mailstream(vms_p, fold);
2119  if (!vms_p->mailstream) {
2120  ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
2121  return -1;
2122  }
2123  if (ret == 0) {
2124  ast_mutex_lock(&vms_p->lock);
2125  pgm = mail_newsearchpgm ();
2126  hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
2127  hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
2128  pgm->header = hdr;
2129  if (fold != OLD_FOLDER) {
2130  pgm->unseen = 1;
2131  pgm->seen = 0;
2132  }
2133  /* In the special case where fold is 1 (old messages) we have to do things a bit
2134  * differently. Old messages are stored in the INBOX but are marked as "seen"
2135  */
2136  else {
2137  pgm->unseen = 0;
2138  pgm->seen = 1;
2139  }
2140  /* look for urgent messages */
2141  if (fold == NEW_FOLDER) {
2142  if (urgent) {
2143  pgm->flagged = 1;
2144  pgm->unflagged = 0;
2145  } else {
2146  pgm->flagged = 0;
2147  pgm->unflagged = 1;
2148  }
2149  }
2150  pgm->undeleted = 1;
2151  pgm->deleted = 0;
2152 
2153  vms_p->vmArrayIndex = 0;
2154  mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2155  if (fold == 0 && urgent == 0)
2156  vms_p->newmessages = vms_p->vmArrayIndex;
2157  if (fold == 1)
2158  vms_p->oldmessages = vms_p->vmArrayIndex;
2159  if (fold == 0 && urgent == 1)
2160  vms_p->urgentmessages = vms_p->vmArrayIndex;
2161  /*Freeing the searchpgm also frees the searchhdr*/
2162  mail_free_searchpgm(&pgm);
2163  ast_mutex_unlock(&vms_p->lock);
2164  vms_p->updated = 0;
2165  return vms_p->vmArrayIndex;
2166  } else {
2167  ast_mutex_lock(&vms_p->lock);
2168  mail_ping(vms_p->mailstream);
2169  ast_mutex_unlock(&vms_p->lock);
2170  }
2171  return 0;
2172 }
2173 
2174 static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
2175 {
2176  /* Check if mailbox is full */
2177  check_quota(vms, vmu->imapfolder);
2178  if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
2179  ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
2180  ast_play_and_wait(chan, "vm-mailboxfull");
2181  return -1;
2182  }
2183 
2184  /* Check if we have exceeded maxmsg */
2185  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));
2186  if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
2187  ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
2188  ast_play_and_wait(chan, "vm-mailboxfull");
2189  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2190  return -1;
2191  }
2192 
2193  return 0;
2194 }
2195 
2196 /*!
2197  * \brief Gets the number of messages that exist in a mailbox folder.
2198  * \param context
2199  * \param mailbox
2200  * \param folder
2201  *
2202  * This method is used when IMAP backend is used.
2203  * \return The number of messages in this mailbox folder (zero or more).
2204  */
2205 static int messagecount(const char *context, const char *mailbox, const char *folder)
2206 {
2207  if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
2208  return __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
2209  } else {
2210  return __messagecount(context, mailbox, folder);
2211  }
2212 }
2213 
2214 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)
2215 {
2216  char *myserveremail = serveremail;
2217  char fn[PATH_MAX];
2218  char introfn[PATH_MAX];
2219  char mailbox[256];
2220  char *stringp;
2221  FILE *p = NULL;
2222  char tmp[80] = "/tmp/astmail-XXXXXX";
2223  long len;
2224  void *buf;
2225  int tempcopy = 0;
2226  STRING str;
2227  int ret; /* for better error checking */
2228  char *imap_flags = NIL;
2229  int msgcount = (messagecount(vmu->context, vmu->mailbox, "INBOX") + messagecount(vmu->context, vmu->mailbox, "Old"));
2230  int box = NEW_FOLDER;
2231 
2232  /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
2233  if (msgnum < 0) {
2234  if(!imapgreetings) {
2235  return 0;
2236  } else {
2237  box = GREETINGS_FOLDER;
2238  }
2239  }
2240 
2241  if (imap_check_limits(chan, vms, vmu, msgcount)) {
2242  return -1;
2243  }
2244 
2245  /* Set urgent flag for IMAP message */
2246  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
2247  ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
2248  imap_flags = "\\FLAGGED";
2249  }
2250 
2251  /* Attach only the first format */
2252  fmt = ast_strdupa(fmt);
2253  stringp = fmt;
2254  strsep(&stringp, "|");
2255 
2256  if (!ast_strlen_zero(vmu->serveremail))
2257  myserveremail = vmu->serveremail;
2258 
2259  if (msgnum > -1)
2260  make_file(fn, sizeof(fn), dir, msgnum);
2261  else
2262  ast_copy_string (fn, dir, sizeof(fn));
2263 
2264  snprintf(introfn, sizeof(introfn), "%sintro", fn);
2265  if (ast_fileexists(introfn, NULL, NULL) <= 0) {
2266  *introfn = '\0';
2267  }
2268 
2269  if (ast_strlen_zero(vmu->email)) {
2270  /* We need the vmu->email to be set when we call make_email_file, but
2271  * if we keep it set, a duplicate e-mail will be created. So at the end
2272  * of this function, we will revert back to an empty string if tempcopy
2273  * is 1.
2274  */
2275  ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
2276  tempcopy = 1;
2277  }
2278 
2279  if (!strcmp(fmt, "wav49"))
2280  fmt = "WAV";
2281  ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2282 
2283  /* Make a temporary file instead of piping directly to sendmail, in case the mail
2284  command hangs. */
2285  if (!(p = vm_mkftemp(tmp))) {
2286  ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2287  if (tempcopy)
2288  *(vmu->email) = '\0';
2289  return -1;
2290  }
2291 
2292  if (msgnum < 0 && imapgreetings) {
2293  if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
2294  ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
2295  return -1;
2296  }
2297  imap_delete_old_greeting(fn, vms);
2298  }
2299 
2300  make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
2301  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
2302  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
2303  fn, introfn, fmt, duration, 1, chan, NULL, 1, flag);
2304  /* read mail file to memory */
2305  len = ftell(p);
2306  rewind(p);
2307  if (!(buf = ast_malloc(len + 1))) {
2308  ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
2309  fclose(p);
2310  if (tempcopy)
2311  *(vmu->email) = '\0';
2312  return -1;
2313  }
2314  if (fread(buf, len, 1, p) < len) {
2315  if (ferror(p)) {
2316  ast_log(LOG_ERROR, "Short read while reading in mail file.\n");
2317  return -1;
2318  }
2319  }
2320  ((char *) buf)[len] = '\0';
2321  INIT(&str, mail_string, buf, len);
2322  ret = init_mailstream(vms, box);
2323  if (ret == 0) {
2324  imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
2325  ast_mutex_lock(&vms->lock);
2326  if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
2327  ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
2328  ast_mutex_unlock(&vms->lock);
2329  fclose(p);
2330  unlink(tmp);
2331  ast_free(buf);
2332  } else {
2333  ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
2334  fclose(p);
2335  unlink(tmp);
2336  ast_free(buf);
2337  return -1;
2338  }
2339  ast_debug(3, "%s stored\n", fn);
2340 
2341  if (tempcopy)
2342  *(vmu->email) = '\0';
2343  inprocess_count(vmu->mailbox, vmu->context, -1);
2344  return 0;
2345 
2346 }
2347 
2348 /*!
2349  * \brief Gets the number of messages that exist in the inbox folder.
2350  * \param mailbox_context
2351  * \param newmsgs The variable that is updated with the count of new messages within this inbox.
2352  * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
2353  * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
2354  *
2355  * This method is used when IMAP backend is used.
2356  * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
2357  *
2358  * \return zero on success, -1 on error.
2359  */
2360 
2361 static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
2362 {
2363  char tmp[PATH_MAX] = "";
2364  char *mailboxnc;
2365  char *context;
2366  char *mb;
2367  char *cur;
2368  if (newmsgs)
2369  *newmsgs = 0;
2370  if (oldmsgs)
2371  *oldmsgs = 0;
2372  if (urgentmsgs)
2373  *urgentmsgs = 0;
2374 
2375  ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
2376  /* If no mailbox, return immediately */
2377  if (ast_strlen_zero(mailbox_context))
2378  return 0;
2379 
2380  ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2381  context = strchr(tmp, '@');
2382  if (strchr(mailbox_context, ',')) {
2383  int tmpnew, tmpold, tmpurgent;
2384  ast_copy_string(tmp, mailbox_context, sizeof(tmp));
2385  mb = tmp;
2386  while ((cur = strsep(&mb, ", "))) {
2387  if (!ast_strlen_zero(cur)) {
2388  if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
2389  return -1;
2390  else {
2391  if (newmsgs)
2392  *newmsgs += tmpnew;
2393  if (oldmsgs)
2394  *oldmsgs += tmpold;
2395  if (urgentmsgs)
2396  *urgentmsgs += tmpurgent;
2397  }
2398  }
2399  }
2400  return 0;
2401  }
2402  if (context) {
2403  *context = '\0';
2404  mailboxnc = tmp;
2405  context++;
2406  } else {
2407  context = "default";
2408  mailboxnc = (char *) mailbox_context;
2409  }
2410 
2411  if (newmsgs) {
2412  struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
2413  if (!vmu) {
2414  ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
2415  return -1;
2416  }
2417  if ((*newmsgs = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
2418  free_user(vmu);
2419  return -1;
2420  }
2421  free_user(vmu);
2422  }
2423  if (oldmsgs) {
2424  if ((*oldmsgs = __messagecount(context, mailboxnc, "Old")) < 0) {
2425  return -1;
2426  }
2427  }
2428  if (urgentmsgs) {
2429  if ((*urgentmsgs = __messagecount(context, mailboxnc, "Urgent")) < 0) {
2430  return -1;
2431  }
2432  }
2433  return 0;
2434 }
2435 
2436 /**
2437  * \brief Determines if the given folder has messages.
2438  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
2439  * \param folder the folder to look in
2440  *
2441  * This function is used when the mailbox is stored in an IMAP back end.
2442  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
2443  * \return 1 if the folder has one or more messages. zero otherwise.
2444  */
2445 
2446 static int has_voicemail(const char *mailbox, const char *folder)
2447 {
2448  char tmp[256], *tmp2, *box, *context;
2449  ast_copy_string(tmp, mailbox, sizeof(tmp));
2450  tmp2 = tmp;
2451  if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
2452  while ((box = strsep(&tmp2, ",&"))) {
2453  if (!ast_strlen_zero(box)) {
2454  if (has_voicemail(box, folder)) {
2455  return 1;
2456  }
2457  }
2458  }
2459  }
2460  if ((context = strchr(tmp, '@'))) {
2461  *context++ = '\0';
2462  } else {
2463  context = "default";
2464  }
2465  return __messagecount(context, tmp, folder) ? 1 : 0;
2466 }
2467 
2468 /*!
2469  * \brief Copies a message from one mailbox to another.
2470  * \param chan
2471  * \param vmu
2472  * \param imbox
2473  * \param msgnum
2474  * \param duration
2475  * \param recip
2476  * \param fmt
2477  * \param dir
2478  *
2479  * This works with IMAP storage based mailboxes.
2480  *
2481  * \return zero on success, -1 on error.
2482  */
2483 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)
2484 {
2485  struct vm_state *sendvms = NULL, *destvms = NULL;
2486  char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2487  if (msgnum >= recip->maxmsg) {
2488  ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
2489  return -1;
2490  }
2491  if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
2492  ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
2493  return -1;
2494  }
2495  if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
2496  ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
2497  return -1;
2498  }
2499  snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
2500  ast_mutex_lock(&sendvms->lock);
2501  if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
2502  ast_mutex_unlock(&sendvms->lock);
2503  return 0;
2504  }
2505  ast_mutex_unlock(&sendvms->lock);
2506  ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
2507  return -1;
2508 }
2509 
2510 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
2511 {
2512  char tmp[256], *t = tmp;
2513  size_t left = sizeof(tmp);
2514 
2515  if (box == OLD_FOLDER) {
2516  ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
2517  } else {
2518  ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
2519  }
2520 
2521  if (box == NEW_FOLDER) {
2522  ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
2523  } else {
2524  snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
2525  }
2526 
2527  /* Build up server information */
2528  ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
2529 
2530  /* Add authentication user if present */
2531  if (!ast_strlen_zero(authuser))
2532  ast_build_string(&t, &left, "/authuser=%s", authuser);
2533 
2534  /* Add flags if present */
2535  if (!ast_strlen_zero(imapflags))
2536  ast_build_string(&t, &left, "/%s", imapflags);
2537 
2538  /* End with username */
2539 #if 1
2540  ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
2541 #else
2542  ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
2543 #endif
2544  if (box == NEW_FOLDER || box == OLD_FOLDER)
2545  snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
2546  else if (box == GREETINGS_FOLDER)
2547  snprintf(spec, len, "%s%s", tmp, greetingfolder);
2548  else { /* Other folders such as Friends, Family, etc... */
2549  if (!ast_strlen_zero(imapparentfolder)) {
2550  /* imapparentfolder would typically be set to INBOX */
2551  snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
2552  } else {
2553  snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
2554  }
2555  }
2556 }
2557 
2558 static int init_mailstream(struct vm_state *vms, int box)
2559 {
2560  MAILSTREAM *stream = NIL;
2561  long debug;
2562  char tmp[256];
2563 
2564  if (!vms) {
2565  ast_log(LOG_ERROR, "vm_state is NULL!\n");
2566  return -1;
2567  }
2568  if (option_debug > 2)
2569  ast_log(LOG_DEBUG, "vm_state user is:%s\n", vms->imapuser);
2570  if (vms->mailstream == NIL || !vms->mailstream) {
2571  if (option_debug)
2572  ast_log(LOG_DEBUG, "mailstream not set.\n");
2573  } else {
2574  stream = vms->mailstream;
2575  }
2576  /* debug = T; user wants protocol telemetry? */
2577  debug = NIL; /* NO protocol telemetry? */
2578 
2579  if (delimiter == '\0') { /* did not probe the server yet */
2580  char *cp;
2581 #ifdef USE_SYSTEM_IMAP
2582 #include <imap/linkage.c>
2583 #elif defined(USE_SYSTEM_CCLIENT)
2584 #include <c-client/linkage.c>
2585 #else
2586 #include "linkage.c"
2587 #endif
2588  /* Connect to INBOX first to get folders delimiter */
2589  imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
2590  ast_mutex_lock(&vms->lock);
2591  stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2592  ast_mutex_unlock(&vms->lock);
2593  if (stream == NIL) {
2594  ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
2595  return -1;
2596  }
2597  get_mailbox_delimiter(stream);
2598  /* update delimiter in imapfolder */
2599  for (cp = vms->imapfolder; *cp; cp++)
2600  if (*cp == '/')
2601  *cp = delimiter;
2602  }
2603  /* Now connect to the target folder */
2604  imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
2605  if (option_debug > 2)
2606  ast_log(LOG_DEBUG, "Before mail_open, server: %s, box:%d\n", tmp, box);
2607  ast_mutex_lock(&vms->lock);
2608  vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
2609  ast_mutex_unlock(&vms->lock);
2610  if (vms->mailstream == NIL) {
2611  return -1;
2612  } else {
2613  return 0;
2614  }
2615 }
2616 
2617 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
2618 {
2619  SEARCHPGM *pgm;
2620  SEARCHHEADER *hdr;
2621  int ret, urgent = 0;
2622 
2623  /* If Urgent, then look at INBOX */
2624  if (box == 11) {
2625  box = NEW_FOLDER;
2626  urgent = 1;
2627  }
2628 
2629  ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
2630  ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
2631  vms->imapversion = vmu->imapversion;
2632  ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
2633 
2634  if ((ret = init_mailstream(vms, box)) || !vms->mailstream) {
2635  ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
2636  return -1;
2637  }
2638 
2639  create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
2640 
2641  /* Check Quota */
2642  if (box == 0) {
2643  ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
2644  check_quota(vms, (char *) mbox(vmu, box));
2645  }
2646 
2647  ast_mutex_lock(&vms->lock);
2648  pgm = mail_newsearchpgm();
2649 
2650  /* Check IMAP folder for Asterisk messages only... */
2651  hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
2652  hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
2653  pgm->header = hdr;
2654  pgm->deleted = 0;
2655  pgm->undeleted = 1;
2656 
2657  /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
2658  if (box == NEW_FOLDER && urgent == 1) {
2659  pgm->unseen = 1;
2660  pgm->seen = 0;
2661  pgm->flagged = 1;
2662  pgm->unflagged = 0;
2663  } else if (box == NEW_FOLDER && urgent == 0) {
2664  pgm->unseen = 1;
2665  pgm->seen = 0;
2666  pgm->flagged = 0;
2667  pgm->unflagged = 1;
2668  } else if (box == OLD_FOLDER) {
2669  pgm->seen = 1;
2670  pgm->unseen = 0;
2671  }
2672 
2673  ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
2674 
2675  vms->vmArrayIndex = 0;
2676  mail_search_full (vms->mailstream, NULL, pgm, NIL);
2677  vms->lastmsg = vms->vmArrayIndex - 1;
2678  mail_free_searchpgm(&pgm);
2679  /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
2680  * ensure to allocate enough space to account for all of them. Warn if old messages
2681  * have not been checked first as that is required.
2682  */
2683  if (box == 0 && !vms->dh_arraysize) {
2684  ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
2685  }
2686  if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
2687  ast_mutex_unlock(&vms->lock);
2688  return -1;
2689  }
2690 
2691  ast_mutex_unlock(&vms->lock);
2692  return 0;
2693 }
2694 
2695 static void write_file(char *filename, char *buffer, unsigned long len)
2696 {
2697  FILE *output;
2698 
2699  output = fopen (filename, "w");
2700  if (fwrite(buffer, len, 1, output) != 1) {
2701  if (ferror(output)) {
2702  ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
2703  }
2704  }
2705  fclose (output);
2706 }
2707 
2708 static void update_messages_by_imapuser(const char *user, unsigned long number)
2709 {
2710  struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
2711 
2712  if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
2713  return;
2714  }
2715 
2716  ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
2717 
2718  /* Ensure we have room for the next message. */
2719  if (vms->vmArrayIndex >= vms->msg_array_max) {
2720  long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
2721  if (!new_mem) {
2722  return;
2723  }
2724  vms->msgArray = new_mem;
2725  vms->msg_array_max *= 2;
2726  }
2727 
2728  vms->msgArray[vms->vmArrayIndex++] = number;
2729 }
2730 
2731 void mm_searched(MAILSTREAM *stream, unsigned long number)
2732 {
2733  char *mailbox = stream->mailbox, buf[1024] = "", *user;
2734 
2735  if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
2736  return;
2737 
2738  update_messages_by_imapuser(user, number);
2739 }
2740 
2741 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
2742 {
2743  struct ast_variable *var;
2744  struct ast_vm_user *vmu;
2745 
2746  vmu = ast_calloc(1, sizeof *vmu);
2747  if (!vmu)
2748  return NULL;
2749 
2750  populate_defaults(vmu);
2751  ast_set_flag(vmu, VM_ALLOCED);
2752 
2753  var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
2754  if (var) {
2755  apply_options_full(vmu, var);
2756  ast_variables_destroy(var);
2757  return vmu;
2758  } else {
2759  ast_free(vmu);
2760  return NULL;
2761  }
2762 }
2763 
2764 /* Interfaces to C-client */
2765 
2766 void mm_exists(MAILSTREAM * stream, unsigned long number)
2767 {
2768  /* mail_ping will callback here if new mail! */
2769  ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
2770  if (number == 0) return;
2771  set_update(stream);
2772 }
2773 
2774 
2775 void mm_expunged(MAILSTREAM * stream, unsigned long number)
2776 {
2777  /* mail_ping will callback here if expunged mail! */
2778  ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
2779  if (number == 0) return;
2780  set_update(stream);
2781 }
2782 
2783 
2784 void mm_flags(MAILSTREAM * stream, unsigned long number)
2785 {
2786  /* mail_ping will callback here if read mail! */
2787  ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
2788  if (number == 0) return;
2789  set_update(stream);
2790 }
2791 
2792 
2793 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
2794 {
2795  ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
2796  mm_log (string, errflg);
2797 }
2798 
2799 
2800 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2801 {
2802  if (delimiter == '\0') {
2803  delimiter = delim;
2804  }
2805 
2806  ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
2807  if (attributes & LATT_NOINFERIORS)
2808  ast_debug(5, "no inferiors\n");
2809  if (attributes & LATT_NOSELECT)
2810  ast_debug(5, "no select\n");
2811  if (attributes & LATT_MARKED)
2812  ast_debug(5, "marked\n");
2813  if (attributes & LATT_UNMARKED)
2814  ast_debug(5, "unmarked\n");
2815 }
2816 
2817 
2818 void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
2819 {
2820  ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
2821  if (attributes & LATT_NOINFERIORS)
2822  ast_debug(5, "no inferiors\n");
2823  if (attributes & LATT_NOSELECT)
2824  ast_debug(5, "no select\n");
2825  if (attributes & LATT_MARKED)
2826  ast_debug(5, "marked\n");
2827  if (attributes & LATT_UNMARKED)
2828  ast_debug(5, "unmarked\n");
2829 }
2830 
2831 
2832 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
2833 {
2834  ast_log(AST_LOG_NOTICE, " Mailbox %s", mailbox);
2835  if (status->flags & SA_MESSAGES)
2836  ast_log(AST_LOG_NOTICE, ", %lu messages", status->messages);
2837  if (status->flags & SA_RECENT)
2838  ast_log(AST_LOG_NOTICE, ", %lu recent", status->recent);
2839  if (status->flags & SA_UNSEEN)
2840  ast_log(AST_LOG_NOTICE, ", %lu unseen", status->unseen);
2841  if (status->flags & SA_UIDVALIDITY)
2842  ast_log(AST_LOG_NOTICE, ", %lu UID validity", status->uidvalidity);
2843  if (status->flags & SA_UIDNEXT)
2844  ast_log(AST_LOG_NOTICE, ", %lu next UID", status->uidnext);
2845  ast_log(AST_LOG_NOTICE, "\n");
2846 }
2847 
2848 
2849 void mm_log(char *string, long errflg)
2850 {
2851  switch ((short) errflg) {
2852  case NIL:
2853  ast_debug(1, "IMAP Info: %s\n", string);
2854  break;
2855  case PARSE:
2856  case WARN:
2857  ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
2858  break;
2859  case ERROR:
2860  ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
2861  break;
2862  }
2863 }
2864 
2865 
2866 void mm_dlog(char *string)
2867 {
2868  ast_log(AST_LOG_NOTICE, "%s\n", string);
2869 }
2870 
2871 
2872 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
2873 {
2874  struct ast_vm_user *vmu;
2875 
2876  ast_debug(4, "Entering callback mm_login\n");
2877 
2878  ast_copy_string(user, mb->user, MAILTMPLEN);
2879 
2880  /* We should only do this when necessary */
2881  if (!ast_strlen_zero(authpassword)) {
2882  ast_copy_string(pwd, authpassword, MAILTMPLEN);
2883  } else {
2884  AST_LIST_TRAVERSE(&users, vmu, list) {
2885  if (!strcasecmp(mb->user, vmu->imapuser)) {
2886  ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2887  break;
2888  }
2889  }
2890  if (!vmu) {
2891  if ((vmu = find_user_realtime_imapuser(mb->user))) {
2892  ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
2893  free_user(vmu);
2894  }
2895  }
2896  }
2897 }
2898 
2899 
2900 void mm_critical(MAILSTREAM * stream)
2901 {
2902 }
2903 
2904 
2905 void mm_nocritical(MAILSTREAM * stream)
2906 {
2907 }
2908 
2909 
2910 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
2911 {
2912  kill (getpid (), SIGSTOP);
2913  return NIL;
2914 }
2915 
2916 
2917 void mm_fatal(char *string)
2918 {
2919  ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
2920 }
2921 
2922 /* C-client callback to handle quota */
2923 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
2924 {
2925  struct vm_state *vms;
2926  char *mailbox = stream->mailbox, *user;
2927  char buf[1024] = "";
2928  unsigned long usage = 0, limit = 0;
2929 
2930  while (pquota) {
2931  usage = pquota->usage;
2932  limit = pquota->limit;
2933  pquota = pquota->next;
2934  }
2935 
2936  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)))) {
2937  ast_log(AST_LOG_ERROR, "No state found.\n");
2938  return;
2939  }
2940 
2941  ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
2942 
2943  vms->quota_usage = usage;
2944  vms->quota_limit = limit;
2945 }
2946 
2947 static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
2948 {
2949  char *start, *eol_pnt;
2950  int taglen;
2951 
2952  if (ast_strlen_zero(header) || ast_strlen_zero(tag))
2953  return NULL;
2954 
2955  taglen = strlen(tag) + 1;
2956  if (taglen < 1)
2957  return NULL;
2958 
2959  if (!(start = strstr(header, tag)))
2960  return NULL;
2961 
2962  /* Since we can be called multiple times we should clear our buffer */
2963  memset(buf, 0, len);
2964 
2965  ast_copy_string(buf, start+taglen, len);
2966  if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
2967  *eol_pnt = '\0';
2968  return buf;
2969 }
2970 
2971 static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
2972 {
2973  char *start, *quote, *eol_pnt;
2974 
2975  if (ast_strlen_zero(mailbox))
2976  return NULL;
2977 
2978  if (!(start = strstr(mailbox, "/user=")))
2979  return NULL;
2980 
2981  ast_copy_string(buf, start+6, len);
2982 
2983  if (!(quote = strchr(buf, '\"'))) {
2984  if (!(eol_pnt = strchr(buf, '/')))
2985  eol_pnt = strchr(buf,'}');
2986  *eol_pnt = '\0';
2987  return buf;
2988  } else {
2989  eol_pnt = strchr(buf+1,'\"');
2990  *eol_pnt = '\0';
2991  return buf+1;
2992  }
2993 }
2994 
2995 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
2996 {
2997  struct vm_state *vms_p;
2998 
2999  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3000  if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
3001  return vms_p;
3002  }
3003  if (option_debug > 4)
3004  ast_log(AST_LOG_DEBUG, "Adding new vmstate for %s\n", vmu->imapuser);
3005  /* XXX: Is this correctly freed always? */
3006  if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
3007  return NULL;
3008  ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
3009  ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
3010  ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
3011  ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
3012  vms_p->mailstream = NIL; /* save for access from interactive entry point */
3013  vms_p->imapversion = vmu->imapversion;
3014  if (option_debug > 4)
3015  ast_log(AST_LOG_DEBUG, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
3016  vms_p->updated = 1;
3017  /* set mailbox to INBOX! */
3018  ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
3019  init_vm_state(vms_p);
3020  vmstate_insert(vms_p);
3021  return vms_p;
3022 }
3023 
3024 static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
3025 {
3026  struct vmstate *vlist = NULL;
3027 
3028  if (interactive) {
3029  struct vm_state *vms;
3030  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3031  vms = pthread_getspecific(ts_vmstate.key);
3032  return vms;
3033  }
3034 
3035  AST_LIST_LOCK(&vmstates);
3036  AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3037  if (!vlist->vms) {
3038  ast_debug(3, "error: vms is NULL for %s\n", user);
3039  continue;
3040  }
3041  if (vlist->vms->imapversion != imapversion) {
3042  continue;
3043  }
3044  if (!vlist->vms->imapuser) {
3045  ast_debug(3, "error: imapuser is NULL for %s\n", user);
3046  continue;
3047  }
3048 
3049  if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
3050  AST_LIST_UNLOCK(&vmstates);
3051  return vlist->vms;
3052  }
3053  }
3054  AST_LIST_UNLOCK(&vmstates);
3055 
3056  ast_debug(3, "%s not found in vmstates\n", user);
3057 
3058  return NULL;
3059 }
3060 
3061 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
3062 {
3063 
3064  struct vmstate *vlist = NULL;
3065  const char *local_context = S_OR(context, "default");
3066 
3067  if (interactive) {
3068  struct vm_state *vms;
3069  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3070  vms = pthread_getspecific(ts_vmstate.key);
3071  return vms;
3072  }
3073 
3074  AST_LIST_LOCK(&vmstates);
3075  AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3076  if (!vlist->vms) {
3077  ast_debug(3, "error: vms is NULL for %s\n", mailbox);
3078  continue;
3079  }
3080  if (vlist->vms->imapversion != imapversion) {
3081  continue;
3082  }
3083  if (!vlist->vms->username || !vlist->vms->context) {
3084  ast_debug(3, "error: username is NULL for %s\n", mailbox);
3085  continue;
3086  }
3087 
3088  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);
3089 
3090  if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
3091  ast_debug(3, "Found it!\n");
3092  AST_LIST_UNLOCK(&vmstates);
3093  return vlist->vms;
3094  }
3095  }
3096  AST_LIST_UNLOCK(&vmstates);
3097 
3098  ast_debug(3, "%s not found in vmstates\n", mailbox);
3099 
3100  return NULL;
3101 }
3102 
3103 static void vmstate_insert(struct vm_state *vms)
3104 {
3105  struct vmstate *v;
3106  struct vm_state *altvms;
3107 
3108  /* If interactive, it probably already exists, and we should
3109  use the one we already have since it is more up to date.
3110  We can compare the username to find the duplicate */
3111  if (vms->interactive == 1) {
3112  altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
3113  if (altvms) {
3114  ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3115  vms->newmessages = altvms->newmessages;
3116  vms->oldmessages = altvms->oldmessages;
3117  vms->vmArrayIndex = altvms->vmArrayIndex;
3118  /* XXX: no msgArray copying? */
3119  vms->lastmsg = altvms->lastmsg;
3120  vms->curmsg = altvms->curmsg;
3121  /* get a pointer to the persistent store */
3122  vms->persist_vms = altvms;
3123  /* Reuse the mailstream? */
3124 #ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
3125  vms->mailstream = altvms->mailstream;
3126 #else
3127  vms->mailstream = NIL;
3128 #endif
3129  }
3130  return;
3131  }
3132 
3133  if (!(v = ast_calloc(1, sizeof(*v))))
3134  return;
3135 
3136  v->vms = vms;
3137 
3138  ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3139 
3140  AST_LIST_LOCK(&vmstates);
3141  AST_LIST_INSERT_TAIL(&vmstates, v, list);
3142  AST_LIST_UNLOCK(&vmstates);
3143 }
3144 
3145 static void vmstate_delete(struct vm_state *vms)
3146 {
3147  struct vmstate *vc = NULL;
3148  struct vm_state *altvms = NULL;
3149 
3150  /* If interactive, we should copy pertinent info
3151  back to the persistent state (to make update immediate) */
3152  if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
3153  ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3154  altvms->newmessages = vms->newmessages;
3155  altvms->oldmessages = vms->oldmessages;
3156  altvms->updated = 1;
3157  vms->mailstream = mail_close(vms->mailstream);
3158 
3159  /* Interactive states are not stored within the persistent list */
3160  return;
3161  }
3162 
3163  ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3164 
3165  AST_LIST_LOCK(&vmstates);
3166  AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
3167  if (vc->vms == vms) {
3169  break;
3170  }
3171  }
3173  AST_LIST_UNLOCK(&vmstates);
3174 
3175  if (vc) {
3176  ast_mutex_destroy(&vc->vms->lock);
3177  ast_free(vc->vms->msgArray);
3178  vc->vms->msgArray = NULL;
3179  vc->vms->msg_array_max = 0;
3180  /* XXX: is no one supposed to free vms itself? */
3181  ast_free(vc);
3182  } else {
3183  ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3184  }
3185 }
3186 
3187 static void set_update(MAILSTREAM * stream)
3188 {
3189  struct vm_state *vms;
3190  char *mailbox = stream->mailbox, *user;
3191  char buf[1024] = "";
3192 
3193  if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
3194  if (user && option_debug > 2)
3195  ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
3196  return;
3197  }
3198 
3199  ast_debug(3, "User %s mailbox set for update.\n", user);
3200 
3201  vms->updated = 1; /* Set updated flag since mailbox changed */
3202 }
3203 
3204 static void init_vm_state(struct vm_state *vms)
3205 {
3206  vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
3207  vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
3208  if (!vms->msgArray) {
3209  /* Out of mem? This can't be good. */
3210  vms->msg_array_max = 0;
3211  }
3212  vms->vmArrayIndex = 0;
3213  ast_mutex_init(&vms->lock);
3214 }
3215 
3216 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro)
3217 {
3218  char *body_content;
3219  char *body_decoded;
3220  char *fn = is_intro ? vms->introfn : vms->fn;
3221  unsigned long len;
3222  unsigned long newlen;
3223  char filename[256];
3224 
3225  if (!body || body == NIL)
3226  return -1;
3227 
3228  ast_mutex_lock(&vms->lock);
3229  body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
3230  ast_mutex_unlock(&vms->lock);
3231  if (body_content != NIL) {
3232  snprintf(filename, sizeof(filename), "%s.%s", fn, format);
3233  /* ast_debug(1,body_content); */
3234  body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
3235  /* If the body of the file is empty, return an error */
3236  if (!newlen) {
3237  return -1;
3238  }
3239  write_file(filename, (char *) body_decoded, newlen);
3240  } else {
3241  ast_debug(5, "Body of message is NULL.\n");
3242  return -1;
3243  }
3244  return 0;
3245 }
3246 
3247 /*!
3248  * \brief Get delimiter via mm_list callback
3249  * \param stream
3250  *
3251  * Determines the delimiter character that is used by the underlying IMAP based mail store.
3252  */
3253 /* MUTEX should already be held */
3254 static void get_mailbox_delimiter(MAILSTREAM *stream) {
3255  char tmp[50];
3256  snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
3257  mail_list(stream, tmp, "*");
3258 }
3259 
3260 /*!
3261  * \brief Check Quota for user
3262  * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
3263  * \param mailbox the mailbox to check the quota for.
3264  *
3265  * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
3266  */
3267 static void check_quota(struct vm_state *vms, char *mailbox) {
3268  ast_mutex_lock(&vms->lock);
3269  mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
3270  ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
3271  if (vms && vms->mailstream != NULL) {
3272  imap_getquotaroot(vms->mailstream, mailbox);
3273  } else {
3274  ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
3275  }
3276  ast_mutex_unlock(&vms->lock);
3277 }
3278 
3279 #endif /* IMAP_STORAGE */
3280 
3281 /*! \brief Lock file path
3282  * only return failure if ast_lock_path returns 'timeout',
3283  * not if the path does not exist or any other reason
3284  */
3285 static int vm_lock_path(const char *path)
3286 {
3287  switch (ast_lock_path(path)) {
3288  case AST_LOCK_TIMEOUT:
3289  return -1;
3290  default:
3291  return 0;
3292  }
3293 }
3294 
3295 
3296 #ifdef ODBC_STORAGE
3297 struct generic_prepare_struct {
3298  char *sql;
3299  int argc;
3300  char **argv;
3301 };
3302 
3303 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
3304 {
3305  struct generic_prepare_struct *gps = data;
3306  int res, i;
3307  SQLHSTMT stmt;
3308 
3309  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
3310  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3311  ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
3312  return NULL;
3313  }
3314  res = SQLPrepare(stmt, (unsigned char *) gps->sql, SQL_NTS);
3315  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3316  ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
3317  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3318  return NULL;
3319  }
3320  for (i = 0; i < gps->argc; i++)
3321  SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
3322 
3323  return stmt;
3324 }
3325 
3326 /*!
3327  * \brief Retrieves a file from an ODBC data store.
3328  * \param dir the path to the file to be retreived.
3329  * \param msgnum the message number, such as within a mailbox folder.
3330  *
3331  * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
3332  * 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.
3333  *
3334  * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
3335  * The output is the message information file with the name msgnum and the extension .txt
3336  * and the message file with the extension of its format, in the directory with base file name of the msgnum.
3337  *
3338  * \return 0 on success, -1 on error.
3339  */
3340 static int retrieve_file(char *dir, int msgnum)
3341 {
3342  int x = 0;
3343  int res;
3344  int fd = -1;
3345  size_t fdlen = 0;
3346  void *fdm = MAP_FAILED;
3347  SQLSMALLINT colcount = 0;
3348  SQLHSTMT stmt;
3349  char sql[PATH_MAX];
3350  char fmt[80]="";
3351  char *c;
3352  char coltitle[256];
3353  SQLSMALLINT collen;
3354  SQLSMALLINT datatype;
3355  SQLSMALLINT decimaldigits;
3356  SQLSMALLINT nullable;
3357  SQLULEN colsize;
3358  SQLLEN colsize2;
3359  FILE *f = NULL;
3360  char rowdata[80];
3361  char fn[PATH_MAX];
3362  char full_fn[PATH_MAX];
3363  char msgnums[80];
3364  char *argv[] = { dir, msgnums };
3365  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3366 
3367  struct odbc_obj *obj;
3368  obj = ast_odbc_request_obj(odbc_database, 0);
3369  if (obj) {
3370  ast_copy_string(fmt, vmfmts, sizeof(fmt));
3371  c = strchr(fmt, '|');
3372  if (c)
3373  *c = '\0';
3374  if (!strcasecmp(fmt, "wav49"))
3375  strcpy(fmt, "WAV");
3376  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3377  if (msgnum > -1)
3378  make_file(fn, sizeof(fn), dir, msgnum);
3379  else
3380  ast_copy_string(fn, dir, sizeof(fn));
3381 
3382  /* Create the information file */
3383  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3384 
3385  if (!(f = fopen(full_fn, "w+"))) {
3386  ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
3387  goto yuck;
3388  }
3389 
3390  snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
3391  snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?", odbc_table);
3392  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3393  if (!stmt) {
3394  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3395  ast_odbc_release_obj(obj);
3396  goto yuck;
3397  }
3398  res = SQLFetch(stmt);
3399  if (res == SQL_NO_DATA) {
3400  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3401  ast_odbc_release_obj(obj);
3402  goto yuck;
3403  } else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3404  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3405  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3406  ast_odbc_release_obj(obj);
3407  goto yuck;
3408  }
3409  fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
3410  if (fd < 0) {
3411  ast_log(AST_LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
3412  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3413  ast_odbc_release_obj(obj);
3414  goto yuck;
3415  }
3416  res = SQLNumResultCols(stmt, &colcount);
3417  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3418  ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
3419  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3420  ast_odbc_release_obj(obj);
3421  goto yuck;
3422  }
3423  if (f)
3424  fprintf(f, "[message]\n");
3425  for (x = 0; x < colcount; x++) {
3426  rowdata[0] = '\0';
3427  colsize = 0;
3428  collen = sizeof(coltitle);
3429  res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen,
3430  &datatype, &colsize, &decimaldigits, &nullable);
3431  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3432  ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
3433  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3434  ast_odbc_release_obj(obj);
3435  goto yuck;
3436  }
3437  if (!strcasecmp(coltitle, "recording")) {
3438  off_t offset;
3439  res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
3440  fdlen = colsize2;
3441  if (fd > -1) {
3442  char tmp[1]="";
3443  lseek(fd, fdlen - 1, SEEK_SET);
3444  if (write(fd, tmp, 1) != 1) {
3445  close(fd);
3446  fd = -1;
3447  continue;
3448  }
3449  /* Read out in small chunks */
3450  for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
3451  if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
3452  ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
3453  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3454  ast_odbc_release_obj(obj);
3455  goto yuck;
3456  } else {
3457  res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
3458  munmap(fdm, CHUNKSIZE);
3459  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3460  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3461  unlink(full_fn);
3462  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3463  ast_odbc_release_obj(obj);
3464  goto yuck;
3465  }
3466  }
3467  }
3468  if (truncate(full_fn, fdlen) < 0) {
3469  ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
3470  }
3471  }
3472  } else {
3473  res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3474  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3475  ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
3476  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3477  ast_odbc_release_obj(obj);
3478  goto yuck;
3479  }
3480  if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
3481  fprintf(f, "%s=%s\n", coltitle, rowdata);
3482  }
3483  }
3484  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3485  ast_odbc_release_obj(obj);
3486  } else
3487  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3488 yuck:
3489  if (f)
3490  fclose(f);
3491  if (fd > -1)
3492  close(fd);
3493  return x - 1;
3494 }
3495 
3496 /*!
3497  * \brief Determines the highest message number in use for a given user and mailbox folder.
3498  * \param vmu
3499  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
3500  *
3501  * This method is used when mailboxes are stored in an ODBC back end.
3502  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
3503  *
3504  * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
3505 
3506  */
3507 static int last_message_index(struct ast_vm_user *vmu, char *dir)
3508 {
3509  int x = 0;
3510  int res;
3511  SQLHSTMT stmt;
3512  char sql[PATH_MAX];
3513  char rowdata[20];
3514  char *argv[] = { dir };
3515  struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
3516 
3517  struct odbc_obj *obj;
3518  obj = ast_odbc_request_obj(odbc_database, 0);
3519  if (obj) {
3520  snprintf(sql, sizeof(sql), "SELECT msgnum FROM %s WHERE dir=? order by msgnum desc", odbc_table);
3521 
3522  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3523  if (!stmt) {
3524  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3525  ast_odbc_release_obj(obj);
3526  goto yuck;
3527  }
3528  res = SQLFetch(stmt);
3529  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3530  if (res == SQL_NO_DATA) {
3531  ast_log(AST_LOG_DEBUG, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
3532  } else {
3533  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3534  }
3535 
3536  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3537  ast_odbc_release_obj(obj);
3538  goto yuck;
3539  }
3540  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3541  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3542  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3543  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3544  ast_odbc_release_obj(obj);
3545  goto yuck;
3546  }
3547  if (sscanf(rowdata, "%30d", &x) != 1)
3548  ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
3549  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3550  ast_odbc_release_obj(obj);
3551  return x;
3552  } else
3553  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3554 yuck:
3555  return x - 1;
3556 }
3557 
3558 /*!
3559  * \brief Determines if the specified message exists.
3560  * \param dir the folder the mailbox folder to look for messages.
3561  * \param msgnum the message index to query for.
3562  *
3563  * This method is used when mailboxes are stored in an ODBC back end.
3564  *
3565  * \return greater than zero if the message exists, zero when the message does not exist or on error.
3566  */
3567 static int message_exists(char *dir, int msgnum)
3568 {
3569  int x = 0;
3570  int res;
3571  SQLHSTMT stmt;
3572  char sql[PATH_MAX];
3573  char rowdata[20];
3574  char msgnums[20];
3575  char *argv[] = { dir, msgnums };
3576  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3577 
3578  struct odbc_obj *obj;
3579  obj = ast_odbc_request_obj(odbc_database, 0);
3580  if (obj) {
3581  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3582  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?", odbc_table);
3583  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3584  if (!stmt) {
3585  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3586  ast_odbc_release_obj(obj);
3587  goto yuck;
3588  }
3589  res = SQLFetch(stmt);
3590  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3591  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3592  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3593  ast_odbc_release_obj(obj);
3594  goto yuck;
3595  }
3596  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3597  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3598  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3599  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3600  ast_odbc_release_obj(obj);
3601  goto yuck;
3602  }
3603  if (sscanf(rowdata, "%30d", &x) != 1)
3604  ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
3605  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3606  ast_odbc_release_obj(obj);
3607  } else
3608  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3609 yuck:
3610  return x;
3611 }
3612 
3613 /*!
3614  * \brief returns the number of messages found.
3615  * \param vmu
3616  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
3617  *
3618  * This method is used when mailboxes are stored in an ODBC back end.
3619  *
3620  * \return The count of messages being zero or more, less than zero on error.
3621  */
3622 static int count_messages(struct ast_vm_user *vmu, char *dir)
3623 {
3624  int x = 0;
3625  int res;
3626  SQLHSTMT stmt;
3627  char sql[PATH_MAX];
3628  char rowdata[20];
3629  char *argv[] = { dir };
3630  struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
3631 
3632  struct odbc_obj *obj;
3633  obj = ast_odbc_request_obj(odbc_database, 0);
3634  if (obj) {
3635  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?", odbc_table);
3636  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3637  if (!stmt) {
3638  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3639  ast_odbc_release_obj(obj);
3640  goto yuck;
3641  }
3642  res = SQLFetch(stmt);
3643  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3644  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3645  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3646  ast_odbc_release_obj(obj);
3647  goto yuck;
3648  }
3649  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3650  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3651  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3652  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3653  ast_odbc_release_obj(obj);
3654  goto yuck;
3655  }
3656  if (sscanf(rowdata, "%30d", &x) != 1)
3657  ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
3658  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3659  ast_odbc_release_obj(obj);
3660  return x;
3661  } else
3662  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3663 yuck:
3664  return x - 1;
3665 
3666 }
3667 
3668 /*!
3669  * \brief Deletes a message from the mailbox folder.
3670  * \param sdir The mailbox folder to work in.
3671  * \param smsg The message index to be deleted.
3672  *
3673  * This method is used when mailboxes are stored in an ODBC back end.
3674  * The specified message is directly deleted from the database 'voicemessages' table.
3675  *
3676  * \return the value greater than zero on success to indicate the number of messages, less than zero on error.
3677  */
3678 static void delete_file(const char *sdir, int smsg)
3679 {
3680  SQLHSTMT stmt;
3681  char sql[PATH_MAX];
3682  char msgnums[20];
3683  char *argv[] = { NULL, msgnums };
3684  struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
3685  struct odbc_obj *obj;
3686 
3687  argv[0] = ast_strdupa(sdir);
3688 
3689  obj = ast_odbc_request_obj(odbc_database, 0);
3690  if (obj) {
3691  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3692  snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?", odbc_table);
3693  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3694  if (!stmt)
3695  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3696  else
3697  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3698  ast_odbc_release_obj(obj);
3699  } else
3700  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3701  return;
3702 }
3703 
3704 /*!
3705  * \brief Copies a voicemail from one mailbox to another.
3706  * \param sdir the folder for which to look for the message to be copied.
3707  * \param smsg the index of the message to be copied.
3708  * \param ddir the destination folder to copy the message into.
3709  * \param dmsg the index to be used for the copied message.
3710  * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
3711  * \param dmailboxcontext The context for the destination user.
3712  *
3713  * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
3714  */
3715 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
3716 {
3717  SQLHSTMT stmt;
3718  char sql[512];
3719  char msgnums[20];
3720  char msgnumd[20];
3721  struct odbc_obj *obj;
3722  char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
3723  struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
3724 
3725  delete_file(ddir, dmsg);
3726  obj = ast_odbc_request_obj(odbc_database, 0);
3727  if (obj) {
3728  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3729  snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
3730  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);
3731  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3732  if (!stmt)
3733  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
3734  else
3735  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3736  ast_odbc_release_obj(obj);
3737  } else
3738  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3739  return;
3740 }
3741 
3742 struct insert_data {
3743  char *sql;
3744  const char *dir;
3745  const char *msgnums;
3746  void *data;
3747  SQLLEN datalen;
3748  SQLLEN indlen;
3749  const char *context;
3750  const char *macrocontext;
3751  const char *callerid;
3752  const char *origtime;
3753  const char *duration;
3754  const char *mailboxuser;
3755  const char *mailboxcontext;
3756  const char *category;
3757  const char *flag;
3758 };
3759 
3760 static SQLHSTMT insert_data_cb(struct odbc_obj *obj, void *vdata)
3761 {
3762  struct insert_data *data = vdata;
3763  int res;
3764  SQLHSTMT stmt;
3765 
3766  res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
3767  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3768  ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
3769  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3770  return NULL;
3771  }
3772 
3773  SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
3774  SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
3775  SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
3776  SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
3777  SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->macrocontext), 0, (void *) data->macrocontext, 0, NULL);
3778  SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
3779  SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
3780  SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
3781  SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
3782  SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
3783  SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
3784  if (!ast_strlen_zero(data->category)) {
3785  SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
3786  }
3787  res = SQLExecDirect(stmt, (unsigned char *) data->sql, SQL_NTS);
3788  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3789  ast_log(AST_LOG_WARNING, "SQL Direct Execute failed!\n");
3790  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3791  return NULL;
3792  }
3793 
3794  return stmt;
3795 }
3796 
3797 /*!
3798  * \brief Stores a voicemail into the database.
3799  * \param dir the folder the mailbox folder to store the message.
3800  * \param mailboxuser the user owning the mailbox folder.
3801  * \param mailboxcontext
3802  * \param msgnum the message index for the message to be stored.
3803  *
3804  * This method is used when mailboxes are stored in an ODBC back end.
3805  * The message sound file and information file is looked up on the file system.
3806  * A SQL query is invoked to store the message into the (MySQL) database.
3807  *
3808  * \return the zero on success -1 on error.
3809  */
3810 static int store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
3811 {
3812  int res = 0;
3813  int fd = -1;
3814  void *fdm = MAP_FAILED;
3815  off_t fdlen = -1;
3816  SQLHSTMT stmt;
3817  char sql[PATH_MAX];
3818  char msgnums[20];
3819  char fn[PATH_MAX];
3820  char full_fn[PATH_MAX];
3821  char fmt[80]="";
3822  char *c;
3823  struct ast_config *cfg = NULL;
3824  struct odbc_obj *obj;
3825  struct insert_data idata = { .sql = sql, .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
3826  .context = "", .macrocontext = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "" };
3827  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
3828 
3829  delete_file(dir, msgnum);
3830  if (!(obj = ast_odbc_request_obj(odbc_database, 0))) {
3831  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3832  return -1;
3833  }
3834 
3835  do {
3836  ast_copy_string(fmt, vmfmts, sizeof(fmt));
3837  c = strchr(fmt, '|');
3838  if (c)
3839  *c = '\0';
3840  if (!strcasecmp(fmt, "wav49"))
3841  strcpy(fmt, "WAV");
3842  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3843  if (msgnum > -1)
3844  make_file(fn, sizeof(fn), dir, msgnum);
3845  else
3846  ast_copy_string(fn, dir, sizeof(fn));
3847  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3848  cfg = ast_config_load(full_fn, config_flags);
3849  snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
3850  fd = open(full_fn, O_RDWR);
3851  if (fd < 0) {
3852  ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
3853  res = -1;
3854  break;
3855  }
3856  if (valid_config(cfg)) {
3857  if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
3858  idata.context = "";
3859  }
3860  if (!(idata.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext"))) {
3861  idata.macrocontext = "";
3862  }
3863  if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
3864  idata.callerid = "";
3865  }
3866  if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
3867  idata.origtime = "";
3868  }
3869  if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
3870  idata.duration = "";
3871  }
3872  if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
3873  idata.category = "";
3874  }
3875  if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
3876  idata.flag = "";
3877  }
3878  }
3879  fdlen = lseek(fd, 0, SEEK_END);
3880  if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
3881  ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
3882  res = -1;
3883  break;
3884  }
3885  fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
3886  if (fdm == MAP_FAILED) {
3887  ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
3888  res = -1;
3889  break;
3890  }
3891  idata.data = fdm;
3892  idata.datalen = idata.indlen = fdlen;
3893 
3894  if (!ast_strlen_zero(idata.category))
3895  snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag,category) VALUES (?,?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
3896  else
3897  snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,flag) VALUES (?,?,?,?,?,?,?,?,?,?,?)", odbc_table);
3898 
3899  if ((stmt = ast_odbc_direct_execute(obj, insert_data_cb, &idata))) {
3900  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3901  } else {
3902  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3903  res = -1;
3904  }
3905  } while (0);
3906  if (obj) {
3907  ast_odbc_release_obj(obj);
3908  }
3909  if (valid_config(cfg))
3910  ast_config_destroy(cfg);
3911  if (fdm != MAP_FAILED)
3912  munmap(fdm, fdlen);
3913  if (fd > -1)
3914  close(fd);
3915  return res;
3916 }
3917 
3918 /*!
3919  * \brief Renames a message in a mailbox folder.
3920  * \param sdir The folder of the message to be renamed.
3921  * \param smsg The index of the message to be renamed.
3922  * \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.
3923  * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
3924  * \param ddir The destination folder for the message to be renamed into
3925  * \param dmsg The destination message for the message to be renamed.
3926  *
3927  * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
3928  * 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.
3929  * 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.
3930  */
3931 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
3932 {
3933  SQLHSTMT stmt;
3934  char sql[PATH_MAX];
3935  char msgnums[20];
3936  char msgnumd[20];
3937  struct odbc_obj *obj;
3938  char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
3939  struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
3940 
3941  delete_file(ddir, dmsg);
3942  obj = ast_odbc_request_obj(odbc_database, 0);
3943  if (obj) {
3944  snprintf(msgnums, sizeof(msgnums), "%d", smsg);
3945  snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
3946  snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?", odbc_table);
3947  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3948  if (!stmt)
3949  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3950  else
3951  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
3952  ast_odbc_release_obj(obj);
3953  } else
3954  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3955  return;
3956 }
3957 
3958 /*!
3959  * \brief Removes a voicemail message file.
3960  * \param dir the path to the message file.
3961  * \param msgnum the unique number for the message within the mailbox.
3962  *
3963  * Removes the message content file and the information file.
3964  * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
3965  * Typical use is to clean up after a RETRIEVE operation.
3966  * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
3967  * \return zero on success, -1 on error.
3968  */
3969 static int remove_file(char *dir, int msgnum)
3970 {
3971  char fn[PATH_MAX];
3972  char full_fn[PATH_MAX];
3973  char msgnums[80];
3974 
3975  if (msgnum > -1) {
3976  snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
3977  make_file(fn, sizeof(fn), dir, msgnum);
3978  } else
3979  ast_copy_string(fn, dir, sizeof(fn));
3980  ast_filedelete(fn, NULL);
3981  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
3982  unlink(full_fn);
3983  return 0;
3984 }
3985 #else
3986 #ifndef IMAP_STORAGE
3987 /*!
3988  * \brief Find all .txt files - even if they are not in sequence from 0000.
3989  * \param vmu
3990  * \param dir
3991  *
3992  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
3993  *
3994  * \return the count of messages, zero or more.
3995  */
3996 static int count_messages(struct ast_vm_user *vmu, char *dir)
3997 {
3998 
3999  int vmcount = 0;
4000  DIR *vmdir = NULL;
4001  struct dirent *vment = NULL;
4002 
4003  if (vm_lock_path(dir))
4004  return ERROR_LOCK_PATH;
4005 
4006  if ((vmdir = opendir(dir))) {
4007  while ((vment = readdir(vmdir))) {
4008  if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
4009  vmcount++;
4010  }
4011  }
4012  closedir(vmdir);
4013  }
4014  ast_unlock_path(dir);
4015 
4016  return vmcount;
4017 }
4018 
4019 /*!
4020  * \brief Renames a message in a mailbox folder.
4021  * \param sfn The path to the mailbox information and data file to be renamed.
4022  * \param dfn The path for where the message data and information files will be renamed to.
4023  *
4024  * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
4025  */
4026 static void rename_file(char *sfn, char *dfn)
4027 {
4028  char stxt[PATH_MAX];
4029  char dtxt[PATH_MAX];
4030  ast_filerename(sfn, dfn, NULL);
4031  snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
4032  snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
4033  if (ast_check_realtime("voicemail_data")) {
4034  ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
4035  }
4036  rename(stxt, dtxt);
4037 }
4038 
4039 /*!
4040  * \brief Determines the highest message number in use for a given user and mailbox folder.
4041  * \param vmu
4042  * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
4043  *
4044  * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
4045  * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
4046  *
4047  * \note Should always be called with a lock already set on dir.
4048  * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
4049  */
4050 static int last_message_index(struct ast_vm_user *vmu, char *dir)
4051 {
4052  int x;
4053  unsigned char map[MAXMSGLIMIT] = "";
4054  DIR *msgdir;
4055  struct dirent *msgdirent;
4056  int msgdirint;
4057  char extension[4];
4058  int stopcount = 0;
4059 
4060  /* Reading the entire directory into a file map scales better than
4061  * doing a stat repeatedly on a predicted sequence. I suspect this
4062  * is partially due to stat(2) internally doing a readdir(2) itself to
4063  * find each file. */
4064  if (!(msgdir = opendir(dir))) {
4065  return -1;
4066  }
4067 
4068  while ((msgdirent = readdir(msgdir))) {
4069  if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
4070  map[msgdirint] = 1;
4071  stopcount++;
4072  ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
4073  }
4074  }
4075  closedir(msgdir);
4076 
4077  for (x = 0; x < vmu->maxmsg; x++) {
4078  if (map[x] == 1) {
4079  stopcount--;
4080  } else if (map[x] == 0 && !stopcount) {
4081  break;
4082  }
4083  }
4084 
4085  return x - 1;
4086 }
4087 
4088 #endif /* #ifndef IMAP_STORAGE */
4089 #endif /* #else of #ifdef ODBC_STORAGE */
4090 #ifndef IMAP_STORAGE
4091 /*!
4092  * \brief Utility function to copy a file.
4093  * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
4094  * \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.
4095  *
4096  * 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.
4097  * The copy operation copies up to 4096 bytes at once.
4098  *
4099  * \return zero on success, -1 on error.
4100  */
4101 static int copy(char *infile, char *outfile)
4102 {
4103  int ifd;
4104  int ofd;
4105  int res;
4106  int len;
4107  char buf[4096];
4108 
4109 #ifdef HARDLINK_WHEN_POSSIBLE
4110  /* Hard link if possible; saves disk space & is faster */
4111  if (link(infile, outfile)) {
4112 #endif
4113  if ((ifd = open(infile, O_RDONLY)) < 0) {
4114  ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
4115  return -1;
4116  }
4117  if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
4118  ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
4119  close(ifd);
4120  return -1;
4121  }
4122  do {
4123  len = read(ifd, buf, sizeof(buf));
4124  if (len < 0) {
4125  ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
4126  close(ifd);
4127  close(ofd);
4128  unlink(outfile);
4129  } else if (len) {
4130  res = write(ofd, buf, len);
4131  if (errno == ENOMEM || errno == ENOSPC || res != len) {
4132  ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
4133  close(ifd);
4134  close(ofd);
4135  unlink(outfile);
4136  }
4137  }
4138  } while (len);
4139  close(ifd);
4140  close(ofd);
4141  return 0;
4142 #ifdef HARDLINK_WHEN_POSSIBLE
4143  } else {
4144  /* Hard link succeeded */
4145  return 0;
4146  }
4147 #endif
4148 }
4149 
4150 /*!
4151  * \brief Copies a voicemail information (envelope) file.
4152  * \param frompath
4153  * \param topath
4154  *
4155  * Every voicemail has the data (.wav) file, and the information file.
4156  * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
4157  * This is used by the COPY macro when not using IMAP storage.
4158  */
4159 static void copy_plain_file(char *frompath, char *topath)
4160 {
4161  char frompath2[PATH_MAX], topath2[PATH_MAX];
4162  struct ast_variable *tmp,*var = NULL;
4163  const char *origmailbox = NULL, *context = NULL, *macrocontext = NULL, *exten = NULL, *priority = NULL, *callerchan = NULL, *callerid = NULL, *origdate = NULL, *origtime = NULL, *category = NULL, *duration = NULL;
4164  ast_filecopy(frompath, topath, NULL);
4165  snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
4166  snprintf(topath2, sizeof(topath2), "%s.txt", topath);
4167  if (ast_check_realtime("voicemail_data")) {
4168  var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
4169  /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
4170  for (tmp = var; tmp; tmp = tmp->next) {
4171  if (!strcasecmp(tmp->name, "origmailbox")) {
4172  origmailbox = tmp->value;
4173  } else if (!strcasecmp(tmp->name, "context")) {
4174  context = tmp->value;
4175  } else if (!strcasecmp(tmp->name, "macrocontext")) {
4176  macrocontext = tmp->value;
4177  } else if (!strcasecmp(tmp->name, "exten")) {
4178  exten = tmp->value;
4179  } else if (!strcasecmp(tmp->name, "priority")) {
4180  priority = tmp->value;
4181  } else if (!strcasecmp(tmp->name, "callerchan")) {
4182  callerchan = tmp->value;
4183  } else if (!strcasecmp(tmp->name, "callerid")) {
4184  callerid = tmp->value;
4185  } else if (!strcasecmp(tmp->name, "origdate")) {
4186  origdate = tmp->value;
4187  } else if (!strcasecmp(tmp->name, "origtime")) {
4188  origtime = tmp->value;
4189  } else if (!strcasecmp(tmp->name, "category")) {
4190  category = tmp->value;
4191  } else if (!strcasecmp(tmp->name, "duration")) {
4192  duration = tmp->value;
4193  }
4194  }
4195  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);
4196  }
4197  copy(frompath2, topath2);
4198  ast_variables_destroy(var);
4199 }
4200 #endif
4201 
4202 /*!
4203  * \brief Removes the voicemail sound and information file.
4204  * \param file The path to the sound file. This will be the the folder and message index, without the extension.
4205  *
4206  * This is used by the DELETE macro when voicemails are stored on the file system.
4207  *
4208  * \return zero on success, -1 on error.
4209  */
4210 static int vm_delete(char *file)
4211 {
4212  char *txt;
4213  int txtsize = 0;
4214 
4215  txtsize = (strlen(file) + 5)*sizeof(char);
4216  txt = ast_alloca(txtsize);
4217  /* Sprintf here would safe because we alloca'd exactly the right length,
4218  * but trying to eliminate all sprintf's anyhow
4219  */
4220  if (ast_check_realtime("voicemail_data")) {
4221  ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
4222  }
4223  snprintf(txt, txtsize, "%s.txt", file);
4224  unlink(txt);
4225  return ast_filedelete(file, NULL);
4226 }
4227 
4228 /*!
4229  * \brief utility used by inchar(), for base_encode()
4230  */
4231 static int inbuf(struct baseio *bio, FILE *fi)
4232 {
4233  int l;
4234 
4235  if (bio->ateof)
4236  return 0;
4237 
4238  if ((l = fread(bio->iobuf, 1, BASEMAXINLINE, fi)) <= 0) {
4239  if (ferror(fi))
4240  return -1;
4241 
4242  bio->ateof = 1;
4243  return 0;
4244  }
4245 
4246  bio->iolen = l;
4247  bio->iocp = 0;
4248 
4249  return 1;
4250 }
4251 
4252 /*!
4253  * \brief utility used by base_encode()
4254  */
4255 static int inchar(struct baseio *bio, FILE *fi)
4256 {
4257  if (bio->iocp>=bio->iolen) {
4258  if (!inbuf(bio, fi))
4259  return EOF;
4260  }
4261 
4262  return bio->iobuf[bio->iocp++];
4263 }
4264 
4265 /*!
4266  * \brief utility used by base_encode()
4267  */
4268 static int ochar(struct baseio *bio, int c, FILE *so)
4269 {
4270  if (bio->linelength >= BASELINELEN) {
4271  if (fputs(ENDL, so) == EOF) {
4272  return -1;
4273  }
4274 
4275  bio->linelength = 0;
4276  }
4277 
4278  if (putc(((unsigned char) c), so) == EOF) {
4279  return -1;
4280  }
4281 
4282  bio->linelength++;
4283 
4284  return 1;
4285 }
4286 
4287 /*!
4288  * \brief Performs a base 64 encode algorithm on the contents of a File
4289  * \param filename The path to the file to be encoded. Must be readable, file is opened in read mode.
4290  * \param so A FILE handle to the output file to receive the base 64 encoded contents of the input file, identified by filename.
4291  *
4292  * 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 ?
4293  *
4294  * \return zero on success, -1 on error.
4295  */
4296 static int base_encode(char *filename, FILE *so)
4297 {
4298  static const unsigned char dtable[] = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K',
4299  'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',
4300  'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0',
4301  '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};
4302  int i, hiteof = 0;
4303  FILE *fi;
4304  struct baseio bio;
4305 
4306  memset(&bio, 0, sizeof(bio));
4307  bio.iocp = BASEMAXINLINE;
4308 
4309  if (!(fi = fopen(filename, "rb"))) {
4310  ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
4311  return -1;
4312  }
4313 
4314  while (!hiteof){
4315  unsigned char igroup[3], ogroup[4];
4316  int c, n;
4317 
4318  memset(igroup, 0, sizeof(igroup));
4319 
4320  for (n = 0; n < 3; n++) {
4321  if ((c = inchar(&bio, fi)) == EOF) {
4322  hiteof = 1;
4323  break;
4324  }
4325 
4326  igroup[n] = (unsigned char) c;
4327  }
4328 
4329  if (n > 0) {
4330  ogroup[0]= dtable[igroup[0] >> 2];
4331  ogroup[1]= dtable[((igroup[0] & 3) << 4) | (igroup[1] >> 4)];
4332  ogroup[2]= dtable[((igroup[1] & 0xF) << 2) | (igroup[2] >> 6)];
4333  ogroup[3]= dtable[igroup[2] & 0x3F];
4334 
4335  if (n < 3) {
4336  ogroup[3] = '=';
4337 
4338  if (n < 2)
4339  ogroup[2] = '=';
4340  }
4341 
4342  for (i = 0; i < 4; i++)
4343  ochar(&bio, ogroup[i], so);
4344  }
4345  }
4346 
4347  fclose(fi);
4348 
4349  if (fputs(ENDL, so) == EOF) {
4350  return 0;
4351  }
4352 
4353  return 1;
4354 }
4355 
4356 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)
4357 {
4358  char callerid[256];
4359  char num[12];
4360  char fromdir[256], fromfile[256];
4361  struct ast_config *msg_cfg;
4362  const char *origcallerid, *origtime;
4363  char origcidname[80], origcidnum[80], origdate[80];
4364  int inttime;
4365  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
4366 
4367  /* Prepare variables for substitution in email body and subject */
4368  pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
4369  pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
4370  snprintf(num, sizeof(num), "%d", msgnum);
4371  pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
4372  pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
4373  pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
4374  pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
4375  ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
4376  pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
4377  pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
4378  pbx_builtin_setvar_helper(ast, "VM_DATE", date);
4379  pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
4380  pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
4381 
4382  /* Retrieve info from VM attribute file */
4383  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
4384  make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
4385  if (strlen(fromfile) < sizeof(fromfile) - 5) {
4386  strcat(fromfile, ".txt");
4387  }
4388  if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
4389  if (option_debug > 0) {
4390  ast_log(LOG_DEBUG, "Config load for message text file '%s' failed\n", fromfile);
4391  }
4392  return;
4393  }
4394 
4395  if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
4396  pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
4397  ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
4398  pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
4399  pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
4400  }
4401 
4402  if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
4403  struct timeval tv = { inttime, };
4404  struct ast_tm tm;
4405  ast_localtime(&tv, &tm, NULL);
4406  ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
4407  pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
4408  }
4409  ast_config_destroy(msg_cfg);
4410 }
4411 
4412 /*!
4413  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
4414  * \param from The string to work with.
4415  * \param buf The buffer into which to write the modified quoted string.
4416  * \param maxlen Always zero, but see \see ast_str
4417  *
4418  * \return The destination string with quotes wrapped on it (the to field).
4419  */
4420 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
4421 {
4422  const char *ptr;
4423 
4424  /* We're only ever passing 0 to maxlen, so short output isn't possible */
4425  ast_str_set(buf, maxlen, "\"");
4426  for (ptr = from; *ptr; ptr++) {
4427  if (*ptr == '"' || *ptr == '\\') {
4428  ast_str_append(buf, maxlen, "\\%c", *ptr);
4429  } else {
4430  ast_str_append(buf, maxlen, "%c", *ptr);
4431  }
4432  }
4433  ast_str_append(buf, maxlen, "\"");
4434 
4435  return ast_str_buffer(*buf);
4436 }
4437 
4438 /*! \brief
4439  * fill in *tm for current time according to the proper timezone, if any.
4440  * \return tm so it can be used as a function argument.
4441  */
4442 static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
4443 {
4444  const struct vm_zone *z = NULL;
4445  struct timeval t = ast_tvnow();
4446 
4447  /* Does this user have a timezone specified? */
4448  if (!ast_strlen_zero(vmu->zonetag)) {
4449  /* Find the zone in the list */
4450  AST_LIST_LOCK(&zones);
4451  AST_LIST_TRAVERSE(&zones, z, list) {
4452  if (!strcmp(z->name, vmu->zonetag))
4453  break;
4454  }
4456  }
4457  ast_localtime(&t, tm, z ? z->timezone : NULL);
4458  return tm;
4459 }
4460 
4461 /*!\brief Check if the string would need encoding within the MIME standard, to
4462  * avoid confusing certain mail software that expects messages to be 7-bit
4463  * clean.
4464  */
4465 static int check_mime(const char *str)
4466 {
4467  for (; *str; str++) {
4468  if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
4469  return 1;
4470  }
4471  }
4472  return 0;
4473 }
4474 
4475 /*!\brief Encode a string according to the MIME rules for encoding strings
4476  * that are not 7-bit clean or contain control characters.
4477  *
4478  * Additionally, if the encoded string would exceed the MIME limit of 76
4479  * characters per line, then the encoding will be broken up into multiple
4480  * sections, separated by a space character, in order to facilitate
4481  * breaking up the associated header across multiple lines.
4482  *
4483  * \param end An expandable buffer for holding the result
4484  * \param maxlen Always zero, but see \see ast_str
4485  * \param start A string to be encoded
4486  * \param preamble The length of the first line already used for this string,
4487  * to ensure that each line maintains a maximum length of 76 chars.
4488  * \param postamble the length of any additional characters appended to the
4489  * line, used to ensure proper field wrapping.
4490  * \retval The encoded string.
4491  */
4492 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
4493 {
4494  struct ast_str *tmp = ast_str_alloca(80);
4495  int first_section = 1;
4496 
4497  ast_str_reset(*end);
4498  ast_str_set(&tmp, -1, "=?%s?Q?", charset);
4499  for (; *start; start++) {
4500  int need_encoding = 0;
4501  if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
4502  need_encoding = 1;
4503  }
4504  if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
4505  (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
4506  (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
4507  (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
4508  /* Start new line */
4509  ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
4510  ast_str_set(&tmp, -1, "=?%s?Q?", charset);
4511  first_section = 0;
4512  }
4513  if (need_encoding && *start == ' ') {
4514  ast_str_append(&tmp, -1, "_");
4515  } else if (need_encoding) {
4516  ast_str_append(&tmp, -1, "=%hhX", *start);
4517  } else {
4518  ast_str_append(&tmp, -1, "%c", *start);
4519  }
4520  }
4521  ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
4522  return ast_str_buffer(*end);
4523 }
4524 
4525 /*!
4526  * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
4527  * \param p The output file to generate the email contents into.
4528  * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
4529  * \param vmu The voicemail user who is sending the voicemail.
4530  * \param msgnum The message index in the mailbox folder.
4531  * \param context
4532  * \param mailbox The voicemail box to read the voicemail to be notified in this email.
4533  * \param fromfolder
4534  * \param cidnum The caller ID number.
4535  * \param cidname The caller ID name.
4536  * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
4537  * \param attach2
4538  * \param format The message sound file format. i.e. .wav
4539  * \param duration The time of the message content, in seconds.
4540  * \param attach_user_voicemail if 1, the sound file is attached to the email.
4541  * \param chan
4542  * \param category
4543  * \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.
4544  * \param flag
4545  *
4546  * 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.
4547  */
4548 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)
4549 {
4550  char date[256];
4551  char host[MAXHOSTNAMELEN] = "";
4552  char who[256];
4553  char bound[256];
4554  char dur[256];
4555  struct ast_tm tm;
4556  char enc_cidnum[256] = "", enc_cidname[256] = "";
4557  struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
4558  char *greeting_attachment;
4559  char filename[256];
4560 
4561  if (!str1 || !str2) {
4562  ast_free(str1);
4563  ast_free(str2);
4564  return;
4565  }
4566 
4567  if (cidnum) {
4568  strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
4569  }
4570  if (cidname) {
4571  strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
4572  }
4573  gethostname(host, sizeof(host) - 1);
4574 
4575  if (strchr(srcemail, '@')) {
4576  ast_copy_string(who, srcemail, sizeof(who));
4577  } else {
4578  snprintf(who, sizeof(who), "%s@%s", srcemail, host);
4579  }
4580 
4581  greeting_attachment = strrchr(ast_strdupa(attach), '/');
4582  if (greeting_attachment) {
4583  *greeting_attachment++ = '\0';
4584  }
4585 
4586  snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
4587  ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
4588  fprintf(p, "Date: %s" ENDL, date);
4589 
4590  /* Set date format for voicemail mail */
4591  ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
4592 
4593  if (!ast_strlen_zero(fromstring)) {
4594  struct ast_channel *ast;
4595  if ((ast = ast_dummy_channel_alloc())) {
4596  char *ptr;
4597  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
4598  ast_str_substitute_variables(&str1, 0, ast, fromstring);
4599 
4600  if (check_mime(ast_str_buffer(str1))) {
4601  int first_line = 1;
4602  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
4603  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
4604  *ptr = '\0';
4605  fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
4606  first_line = 0;
4607  /* Substring is smaller, so this will never grow */
4608  ast_str_set(&str2, 0, "%s", ptr + 1);
4609  }
4610  fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
4611  } else {
4612  fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
4613  }
4614  ast = ast_channel_unref(ast);
4615  } else {
4616  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4617  }
4618  } else {
4619  fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
4620  }
4621 
4622  if (check_mime(vmu->fullname)) {
4623  int first_line = 1;
4624  char *ptr;
4625  ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(vmu->email) + 3);
4626  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
4627  *ptr = '\0';
4628  fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
4629  first_line = 0;
4630  /* Substring is smaller, so this will never grow */
4631  ast_str_set(&str2, 0, "%s", ptr + 1);
4632  }
4633  fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), vmu->email);
4634  } else {
4635  fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), vmu->email);
4636  }
4637 
4638  if (!ast_strlen_zero(emailsubject) || !ast_strlen_zero(vmu->emailsubject)) {
4639  char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
4640  struct ast_channel *ast;
4641  if ((ast = ast_dummy_channel_alloc())) {
4642  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
4643  ast_str_substitute_variables(&str1, 0, ast, e_subj);
4644  if (check_mime(ast_str_buffer(str1))) {
4645  int first_line = 1;
4646  char *ptr;
4647  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
4648  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
4649  *ptr = '\0';
4650  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
4651  first_line = 0;
4652  /* Substring is smaller, so this will never grow */
4653  ast_str_set(&str2, 0, "%s", ptr + 1);
4654  }
4655  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
4656  } else {
4657  fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
4658  }
4659  ast = ast_channel_unref(ast);
4660  } else {
4661  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4662  }
4663  } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
4664  if (ast_strlen_zero(flag)) {
4665  fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
4666  } else {
4667  fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
4668  }
4669  } else {
4670  if (ast_strlen_zero(flag)) {
4671  fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
4672  } else {
4673  fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
4674  }
4675  }
4676 
4677  fprintf(p, "Message-ID: <Asterisk-%d-%u-%s-%d@%s>" ENDL, msgnum + 1,
4678  (unsigned int) ast_random(), mailbox, (int) getpid(), host);
4679  if (imap) {
4680  /* additional information needed for IMAP searching */
4681  fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
4682  /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
4683  fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
4684  fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
4685 #ifdef IMAP_STORAGE
4686  fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
4687 #else
4688  fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
4689 #endif
4690  /* flag added for Urgent */
4691  fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, flag);
4692  fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
4693  fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
4694  fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
4695  fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
4696  fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
4697  if (!ast_strlen_zero(category)) {
4698  fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
4699  } else {
4700  fprintf(p, "X-Asterisk-VM-Category: " ENDL);
4701  }
4702  fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
4703  fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
4704  fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
4705  }
4706  if (!ast_strlen_zero(cidnum)) {
4707  fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
4708  }
4709  if (!ast_strlen_zero(cidname)) {
4710  fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
4711  }
4712  fprintf(p, "MIME-Version: 1.0" ENDL);
4713  if (attach_user_voicemail) {
4714  /* Something unique. */
4715  snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%u", msgnum + 1, mailbox,
4716  (int) getpid(), (unsigned int) ast_random());
4717 
4718  fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
4719  fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
4720  fprintf(p, "--%s" ENDL, bound);
4721  }
4722  fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
4723  if (emailbody || vmu->emailbody) {
4724  char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
4725  struct ast_channel *ast;
4726  if ((ast = ast_dummy_channel_alloc())) {
4727  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
4728  ast_str_substitute_variables(&str1, 0, ast, e_body);
4729 #ifdef IMAP_STORAGE
4730  {
4731  /* Convert body to native line terminators for IMAP backend */
4732  char *line = ast_str_buffer(str1), *next;
4733  do {
4734  /* Terminate line before outputting it to the file */
4735  if ((next = strchr(line, '\n'))) {
4736  *next++ = '\0';
4737  }
4738  fprintf(p, "%s" ENDL, line);
4739  line = next;
4740  } while (!ast_strlen_zero(line));
4741  }
4742 #else
4743  fprintf(p, "%s" ENDL, ast_str_buffer(str1));
4744 #endif
4745  ast = ast_channel_unref(ast);
4746  } else {
4747  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4748  }
4749  } else if (msgnum > -1) {
4750  if (strcmp(vmu->mailbox, mailbox)) {
4751  /* Forwarded type */
4752  struct ast_config *msg_cfg;
4753  const char *v;
4754  int inttime;
4755  char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
4756  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
4757  /* Retrieve info from VM attribute file */
4758  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
4759  make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
4760  if (strlen(fromfile) < sizeof(fromfile) - 5) {
4761  strcat(fromfile, ".txt");
4762  }
4763  if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
4764  if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
4765  ast_copy_string(origcallerid, v, sizeof(origcallerid));
4766  }
4767 
4768  /* You might be tempted to do origdate, except that a) it's in the wrong
4769  * format, and b) it's missing for IMAP recordings. */
4770  if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
4771  struct timeval tv = { inttime, };
4772  struct ast_tm tm;
4773  ast_localtime(&tv, &tm, NULL);
4774  ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
4775  }
4776  fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
4777  " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
4778  "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
4779  " chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
4780  msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
4781  date, origcallerid, origdate);
4782  ast_config_destroy(msg_cfg);
4783  } else {
4784  goto plain_message;
4785  }
4786  } else {
4787 plain_message:
4788  fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
4789  "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
4790  "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
4791  ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
4792  (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
4793  }
4794  } else {
4795  fprintf(p, "This message is to let you know that your greeting was changed on %s." ENDL
4796  "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL, date);
4797  }
4798 
4799  if (imap || attach_user_voicemail) {
4800  if (!ast_strlen_zero(attach2)) {
4801  snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
4802  ast_debug(5, "creating second attachment filename %s\n", filename);
4803  add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
4804  snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
4805  ast_debug(5, "creating attachment filename %s\n", filename);
4806  add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
4807  } else {
4808  snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
4809  ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
4810  add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
4811  }
4812  }
4813  ast_free(str1);
4814  ast_free(str2);
4815 }
4816 
4817 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)
4818 {
4819  char tmpdir[256], newtmp[256];
4820  char fname[256];
4821  char tmpcmd[256];
4822  int tmpfd = -1;
4823  int soxstatus = 0;
4824 
4825  /* Eww. We want formats to tell us their own MIME type */
4826  char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
4827 
4828  if (vmu->volgain < -.001 || vmu->volgain > .001) {
4829  create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
4830  snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
4831  tmpfd = mkstemp(newtmp);
4832  chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
4833  ast_debug(3, "newtmp: %s\n", newtmp);
4834  if (tmpfd > -1) {
4835  snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
4836  if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
4837  attach = newtmp;
4838  ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
4839  } else {
4840  ast_log(LOG_WARNING, "Sox failed to re-encode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
4841  soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
4842  ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
4843  }
4844  }
4845  }
4846  fprintf(p, "--%s" ENDL, bound);
4847  if (msgnum > -1)
4848  fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, ctype, format, filename);
4849  else
4850  fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, ctype, format, greeting_attachment, format);
4851  fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
4852  fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
4853  if (msgnum > -1)
4854  fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
4855  else
4856  fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
4857  snprintf(fname, sizeof(fname), "%s.%s", attach, format);
4858  base_encode(fname, p);
4859  if (last)
4860  fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
4861  if (tmpfd > -1) {
4862  if (soxstatus == 0) {
4863  unlink(fname);
4864  }
4865  close(tmpfd);
4866  unlink(newtmp);
4867  }
4868  return 0;
4869 }
4870 
4871 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)
4872 {
4873  FILE *p = NULL;
4874  char tmp[80] = "/tmp/astmail-XXXXXX";
4875  char tmp2[256];
4876  char *stringp;
4877 
4878  if (vmu && ast_strlen_zero(vmu->email)) {
4879  ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
4880  return(0);
4881  }
4882 
4883  /* Mail only the first format */
4884  format = ast_strdupa(format);
4885  stringp = format;
4886  strsep(&stringp, "|");
4887 
4888  if (!strcmp(format, "wav49"))
4889  format = "WAV";
4890  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));
4891  /* Make a temporary file instead of piping directly to sendmail, in case the mail
4892  command hangs */
4893  if ((p = vm_mkftemp(tmp)) == NULL) {
4894  ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
4895  return -1;
4896  } else {
4897  make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag);
4898  fclose(p);
4899  snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
4900  ast_safe_system(tmp2);
4901  ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
4902  }
4903  return 0;
4904 }
4905 
4906 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)
4907 {
4908  char enc_cidnum[256], enc_cidname[256];
4909  char date[256];
4910  char host[MAXHOSTNAMELEN] = "";
4911  char who[256];
4912  char dur[PATH_MAX];
4913  char tmp[80] = "/tmp/astmail-XXXXXX";
4914  char tmp2[PATH_MAX];
4915  struct ast_tm tm;
4916  FILE *p;
4917  struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
4918 
4919  if (!str1 || !str2) {
4920  ast_free(str1);
4921  ast_free(str2);
4922  return -1;
4923  }
4924 
4925  if (cidnum) {
4926  strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
4927  }
4928  if (cidname) {
4929  strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
4930  }
4931 
4932  if ((p = vm_mkftemp(tmp)) == NULL) {
4933  ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
4934  ast_free(str1);
4935  ast_free(str2);
4936  return -1;
4937  }
4938  gethostname(host, sizeof(host)-1);
4939  if (strchr(srcemail, '@')) {
4940  ast_copy_string(who, srcemail, sizeof(who));
4941  } else {
4942  snprintf(who, sizeof(who), "%s@%s", srcemail, host);
4943  }
4944  snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
4945  ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
4946  fprintf(p, "Date: %s\n", date);
4947 
4948  /* Reformat for custom pager format */
4949  ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
4950 
4951  if (!ast_strlen_zero(pagerfromstring)) {
4952  struct ast_channel *ast;
4953  if ((ast = ast_dummy_channel_alloc())) {
4954  char *ptr;
4955  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
4956  ast_str_substitute_variables(&str1, 0, ast, pagerfromstring);
4957 
4958  if (check_mime(ast_str_buffer(str1))) {
4959  int first_line = 1;
4960  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
4961  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
4962  *ptr = '\0';
4963  fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
4964  first_line = 0;
4965  /* Substring is smaller, so this will never grow */
4966  ast_str_set(&str2, 0, "%s", ptr + 1);
4967  }
4968  fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
4969  } else {
4970  fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
4971  }
4972  ast = ast_channel_unref(ast);
4973  } else {
4974  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
4975  }
4976  } else {
4977  fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
4978  }
4979 
4980  if (check_mime(vmu->fullname)) {
4981  int first_line = 1;
4982  char *ptr;
4983  ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
4984  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
4985  *ptr = '\0';
4986  fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
4987  first_line = 0;
4988  /* Substring is smaller, so this will never grow */
4989  ast_str_set(&str2, 0, "%s", ptr + 1);
4990  }
4991  fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
4992  } else {
4993  fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
4994  }
4995 
4996  if (!ast_strlen_zero(pagersubject)) {
4997  struct ast_channel *ast;
4998  if ((ast = ast_dummy_channel_alloc())) {
4999  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5000  ast_str_substitute_variables(&str1, 0, ast, pagersubject);
5001  if (check_mime(ast_str_buffer(str1))) {
5002  int first_line = 1;
5003  char *ptr;
5004  ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
5005  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5006  *ptr = '\0';
5007  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5008  first_line = 0;
5009  /* Substring is smaller, so this will never grow */
5010  ast_str_set(&str2, 0, "%s", ptr + 1);
5011  }
5012  fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5013  } else {
5014  fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
5015  }
5016  ast = ast_channel_unref(ast);
5017  } else {
5018  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5019  }
5020  } else {
5021  if (ast_strlen_zero(flag)) {
5022  fprintf(p, "Subject: New VM\n\n");
5023  } else {
5024  fprintf(p, "Subject: New %s VM\n\n", flag);
5025  }
5026  }
5027 
5028  if (pagerbody) {
5029  struct ast_channel *ast;
5030  if ((ast = ast_dummy_channel_alloc())) {
5031  prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5032  ast_str_substitute_variables(&str1, 0, ast, pagerbody);
5033  fprintf(p, "%s" ENDL, ast_str_buffer(str1));
5034  ast = ast_channel_unref(ast);
5035  } else {
5036  ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5037  }
5038  } else {
5039  fprintf(p, "New %s long %s msg in box %s\n"
5040  "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
5041  }
5042 
5043  fclose(p);
5044  snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
5045  ast_safe_system(tmp2);
5046  ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
5047  ast_free(str1);
5048  ast_free(str2);
5049  return 0;
5050 }
5051 
5052 /*!
5053  * \brief Gets the current date and time, as formatted string.
5054  * \param s The buffer to hold the output formatted date.
5055  * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
5056  *
5057  * The date format string used is "%a %b %e %r UTC %Y".
5058  *
5059  * \return zero on success, -1 on error.
5060  */
5061 static int get_date(char *s, int len)
5062 {
5063  struct ast_tm tm;
5064  struct timeval t = ast_tvnow();
5065 
5066  ast_localtime(&t, &tm, "UTC");
5067 
5068  return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
5069 }
5070 
5071 static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
5072 {
5073  int res;
5074  char fn[PATH_MAX];
5075  char dest[PATH_MAX];
5076 
5077  snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
5078 
5079  if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
5080  ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
5081  return -1;
5082  }
5083 
5084  RETRIEVE(fn, -1, ext, context);
5085  if (ast_fileexists(fn, NULL, NULL) > 0) {
5086  res = ast_stream_and_wait(chan, fn, ecodes);
5087  if (res) {
5088  DISPOSE(fn, -1);
5089  return res;
5090  }
5091  } else {
5092  /* Dispose just in case */
5093  DISPOSE(fn, -1);
5094  res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
5095  if (res)
5096  return res;
5097  res = ast_say_digit_str(chan, ext, ecodes, chan->language);
5098  if (res)
5099  return res;
5100  }
5101  res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
5102  return res;
5103 }
5104 
5105 static void free_zone(struct vm_zone *z)
5106 {
5107  ast_free(z);
5108 }
5109 
5110 #ifdef ODBC_STORAGE
5111 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
5112 {
5113  int x = -1;
5114  int res;
5115  SQLHSTMT stmt = NULL;
5116  char sql[PATH_MAX];
5117  char rowdata[20];
5118  char tmp[PATH_MAX] = "";
5119  struct odbc_obj *obj = NULL;
5120  char *context;
5121  struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
5122 
5123  if (newmsgs)
5124  *newmsgs = 0;
5125  if (oldmsgs)
5126  *oldmsgs = 0;
5127  if (urgentmsgs)
5128  *urgentmsgs = 0;
5129 
5130  /* If no mailbox, return immediately */
5131  if (ast_strlen_zero(mailbox))
5132  return 0;
5133 
5134  ast_copy_string(tmp, mailbox, sizeof(tmp));
5135 
5136  if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
5137  int u, n, o;
5138  char *next, *remaining = tmp;
5139  while ((next = strsep(&remaining, " ,"))) {
5140  if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
5141  return -1;
5142  }
5143  if (urgentmsgs) {
5144  *urgentmsgs += u;
5145  }
5146  if (newmsgs) {
5147  *newmsgs += n;
5148  }
5149  if (oldmsgs) {
5150  *oldmsgs += o;
5151  }
5152  }
5153  return 0;
5154  }
5155 
5156  context = strchr(tmp, '@');
5157  if (context) {
5158  *context = '\0';
5159  context++;
5160  } else
5161  context = "default";
5162 
5163  if ((obj = ast_odbc_request_obj(odbc_database, 0))) {
5164  do {
5165  if (newmsgs) {
5166  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
5167  if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
5168  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5169  break;
5170  }
5171  res = SQLFetch(stmt);
5172  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5173  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5174  break;
5175  }
5176  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5177  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5178  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5179  break;
5180  }
5181  *newmsgs = atoi(rowdata);
5182  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5183  }
5184 
5185  if (oldmsgs) {
5186  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
5187  if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
5188  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5189  break;
5190  }
5191  res = SQLFetch(stmt);
5192  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5193  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5194  break;
5195  }
5196  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5197  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5198  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5199  break;
5200  }
5201  SQLFreeHandle(SQL_HANDLE_STMT, stmt);
5202  *oldmsgs = atoi(rowdata);
5203  }
5204 
5205  if (urgentmsgs) {
5206  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Urgent");
5207  if (!(stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps))) {
5208  ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5209  break;
5210  }
5211  res = SQLFetch(stmt);
5212  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5213  ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5214  break;
5215  }
5216  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5217  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5218  ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5219  break;
5220  }
5221  *urgentmsgs = atoi(rowdata);
5222  }
5223 
5224  x = 0;
5225  } while (0);
5226  } else {
5227  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
5228  }
5229 
5230  if (stmt) {
5231  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5232  }
5233  if (obj) {
5234  ast_odbc_release_obj(obj);
5235  }
5236  return x;
5237 }
5238 
5239 /*!
5240  * \brief Gets the number of messages that exist in a mailbox folder.
5241  * \param context
5242  * \param mailbox
5243  * \param folder
5244  *
5245  * This method is used when ODBC backend is used.
5246  * \return The number of messages in this mailbox folder (zero or more).
5247  */
5248 static int messagecount(const char *context, const char *mailbox, const char *folder)
5249 {
5250  struct odbc_obj *obj = NULL;
5251  int nummsgs = 0;
5252  int res;
5253  SQLHSTMT stmt = NULL;
5254  char sql[PATH_MAX];
5255  char rowdata[20];
5256  struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
5257  if (!folder)
5258  folder = "INBOX";
5259  /* If no mailbox, return immediately */
5260  if (ast_strlen_zero(mailbox))
5261  return 0;
5262 
5263  obj = ast_odbc_request_obj(odbc_database, 0);
5264  if (obj) {
5265  if (!strcmp(folder, "INBOX")) {
5266  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);
5267  } else {
5268  snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
5269  }
5270  stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
5271  if (!stmt) {
5272  ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
5273  goto yuck;
5274  }
5275  res = SQLFetch(stmt);
5276  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5277  ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
5278  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5279  goto yuck;
5280  }
5281  res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
5282  if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
5283  ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
5284  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5285  goto yuck;
5286  }
5287  nummsgs = atoi(rowdata);
5288  SQLFreeHandle (SQL_HANDLE_STMT, stmt);
5289  } else
5290  ast_log(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
5291 
5292 yuck:
5293  if (obj)
5294  ast_odbc_release_obj(obj);
5295  return nummsgs;
5296 }
5297 
5298 /**
5299  * \brief Determines if the given folder has messages.
5300  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
5301  *
5302  * This function is used when the mailbox is stored in an ODBC back end.
5303  * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
5304  * \return 1 if the folder has one or more messages. zero otherwise.
5305  */
5306 static int has_voicemail(const char *mailbox, const char *folder)
5307 {
5308  char tmp[256], *tmp2 = tmp, *box, *context;
5309  ast_copy_string(tmp, mailbox, sizeof(tmp));
5310  while ((context = box = strsep(&tmp2, ",&"))) {
5311  strsep(&context, "@");
5312  if (ast_strlen_zero(context))
5313  context = "default";
5314  if (messagecount(context, box, folder))
5315  return 1;
5316  }
5317  return 0;
5318 }
5319 #endif
5320 #ifndef IMAP_STORAGE
5321 /*!
5322  * \brief Copies a message from one mailbox to another.
5323  * \param chan
5324  * \param vmu
5325  * \param imbox
5326  * \param msgnum
5327  * \param duration
5328  * \param recip
5329  * \param fmt
5330  * \param dir
5331  * \param flag
5332  *
5333  * This is only used by file storage based mailboxes.
5334  *
5335  * \return zero on success, -1 on error.
5336  */
5337 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)
5338 {
5339  char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
5340  const char *frombox = mbox(vmu, imbox);
5341  const char *userfolder;
5342  int recipmsgnum;
5343  int res = 0;
5344 
5345  ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
5346 
5347  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
5348  userfolder = "Urgent";
5349  } else {
5350  userfolder = "INBOX";
5351  }
5352 
5353  create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
5354 
5355  if (!dir)
5356  make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
5357  else
5358  ast_copy_string(fromdir, dir, sizeof(fromdir));
5359 
5360  make_file(frompath, sizeof(frompath), fromdir, msgnum);
5361  make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
5362 
5363  if (vm_lock_path(todir))
5364  return ERROR_LOCK_PATH;
5365 
5366  recipmsgnum = last_message_index(recip, todir) + 1;
5367  if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
5368  make_file(topath, sizeof(topath), todir, recipmsgnum);
5369 #ifndef ODBC_STORAGE
5370  if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
5371  COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
5372  } else {
5373 #endif
5374  /* If we are prepending a message for ODBC, then the message already
5375  * exists in the database, but we want to force copying from the
5376  * filesystem (since only the FS contains the prepend). */
5377  copy_plain_file(frompath, topath);
5378  STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL);
5379  vm_delete(topath);
5380 #ifndef ODBC_STORAGE
5381  }
5382 #endif
5383  } else {
5384  ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
5385  res = -1;
5386  }
5387  ast_unlock_path(todir);
5388  notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
5389  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
5390  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
5391  flag);
5392 
5393  return res;
5394 }
5395 #endif
5396 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
5397 
5398 static int messagecount(const char *context, const char *mailbox, const char *folder)
5399 {
5400  return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
5401 }
5402 
5403 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
5404 {
5405  DIR *dir;
5406  struct dirent *de;
5407  char fn[256];
5408  int ret = 0;
5409 
5410  /* If no mailbox, return immediately */
5411  if (ast_strlen_zero(mailbox))
5412  return 0;
5413 
5414  if (ast_strlen_zero(folder))
5415  folder = "INBOX";
5416  if (ast_strlen_zero(context))
5417  context = "default";
5418 
5419  snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
5420 
5421  if (!(dir = opendir(fn)))
5422  return 0;
5423 
5424  while ((de = readdir(dir))) {
5425  if (!strncasecmp(de->d_name, "msg", 3)) {
5426  if (shortcircuit) {
5427  ret = 1;
5428  break;
5429  } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
5430  ret++;
5431  }
5432  }
5433  }
5434 
5435  closedir(dir);
5436 
5437  return ret;
5438 }
5439 
5440 /**
5441  * \brief Determines if the given folder has messages.
5442  * \param mailbox The @ delimited string for user@context. If no context is found, uses 'default' for the context.
5443  * \param folder the folder to look in
5444  *
5445  * This function is used when the mailbox is stored in a filesystem back end.
5446  * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
5447  * \return 1 if the folder has one or more messages. zero otherwise.
5448  */
5449 static int has_voicemail(const char *mailbox, const char *folder)
5450 {
5451  char tmp[256], *tmp2 = tmp, *box, *context;
5452  ast_copy_string(tmp, mailbox, sizeof(tmp));
5453  if (ast_strlen_zero(folder)) {
5454  folder = "INBOX";
5455  }
5456  while ((box = strsep(&tmp2, ",&"))) {
5457  if ((context = strchr(box, '@')))
5458  *context++ = '\0';
5459  else
5460  context = "default";
5461  if (__has_voicemail(context, box, folder, 1))
5462  return 1;
5463  /* If we are checking INBOX, we should check Urgent as well */
5464  if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
5465  return 1;
5466  }
5467  }
5468  return 0;
5469 }
5470 
5471 
5472 static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
5473 {
5474  char tmp[256];
5475  char *context;
5476 
5477  /* If no mailbox, return immediately */
5478  if (ast_strlen_zero(mailbox))
5479  return 0;
5480 
5481  if (newmsgs)
5482  *newmsgs = 0;
5483  if (oldmsgs)
5484  *oldmsgs = 0;
5485  if (urgentmsgs)
5486  *urgentmsgs = 0;
5487 
5488  if (strchr(mailbox, ',')) {
5489  int tmpnew, tmpold, tmpurgent;
5490  char *mb, *cur;
5491 
5492  ast_copy_string(tmp, mailbox, sizeof(tmp));
5493  mb = tmp;
5494  while ((cur = strsep(&mb, ", "))) {
5495  if (!ast_strlen_zero(cur)) {
5496  if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
5497  return -1;
5498  else {
5499  if (newmsgs)
5500  *newmsgs += tmpnew;
5501  if (oldmsgs)
5502  *oldmsgs += tmpold;
5503  if (urgentmsgs)
5504  *urgentmsgs += tmpurgent;
5505  }
5506  }
5507  }
5508  return 0;
5509  }
5510 
5511  ast_copy_string(tmp, mailbox, sizeof(tmp));
5512 
5513  if ((context = strchr(tmp, '@')))
5514  *context++ = '\0';
5515  else
5516  context = "default";
5517 
5518  if (newmsgs)
5519  *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
5520  if (oldmsgs)
5521  *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
5522  if (urgentmsgs)
5523  *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
5524 
5525  return 0;
5526 }
5527 
5528 #endif
5529 
5530 /* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
5531 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
5532 {
5533  int urgentmsgs = 0;
5534  int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
5535  if (newmsgs) {
5536  *newmsgs += urgentmsgs;
5537  }
5538  return res;
5539 }
5540 
5541 static void run_externnotify(char *context, char *extension, const char *flag)
5542 {
5543  char arguments[255];
5544  char ext_context[256] = "";
5545  int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
5546  struct ast_smdi_mwi_message *mwi_msg;
5547 
5548  if (!ast_strlen_zero(context))
5549  snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
5550  else
5551  ast_copy_string(ext_context, extension, sizeof(ext_context));
5552 
5553  if (smdi_iface) {
5554  if (ast_app_has_voicemail(ext_context, NULL))
5555  ast_smdi_mwi_set(smdi_iface, extension);
5556  else
5557  ast_smdi_mwi_unset(smdi_iface, extension);
5558 
5559  if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
5560  ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
5561  if (!strncmp(mwi_msg->cause, "INV", 3))
5562  ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
5563  else if (!strncmp(mwi_msg->cause, "BLK", 3))
5564  ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
5565  ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
5567  } else {
5568  ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
5569  }
5570  }
5571 
5572  if (!ast_strlen_zero(externnotify)) {
5573  if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
5574  ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
5575  } else {
5576  snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
5577  externnotify, S_OR(context, "\"\""),
5578  extension, newvoicemails,
5579  oldvoicemails, urgentvoicemails);
5580  ast_debug(1, "Executing %s\n", arguments);
5581  ast_safe_system(arguments);
5582  }
5583  }
5584 }
5585 
5586 /*!
5587  * \brief Variables used for saving a voicemail.
5588  *
5589  * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
5590  */
5591 struct leave_vm_options {
5592  unsigned int flags;
5593  signed char record_gain;
5594  char *exitcontext;
5595 };
5596 
5597 /*!
5598  * \brief Prompts the user and records a voicemail to a mailbox.
5599  * \param chan
5600  * \param ext
5601  * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
5602  *
5603  *
5604  *
5605  * \return zero on success, -1 on error.
5606  */
5607 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
5608 {
5609 #ifdef IMAP_STORAGE
5610  int newmsgs, oldmsgs;
5611 #else
5612  char urgdir[PATH_MAX];
5613 #endif
5614  char txtfile[PATH_MAX];
5615  char tmptxtfile[PATH_MAX];
5616  struct vm_state *vms = NULL;
5617  char callerid[256];
5618  FILE *txt;
5619  char date[256];
5620  int txtdes;
5621  int res = 0;
5622  int msgnum;
5623  int duration = 0;
5624  int sound_duration = 0;
5625  int ausemacro = 0;
5626  int ousemacro = 0;
5627  int ouseexten = 0;
5628  char tmpdur[16];
5629  char priority[16];
5630  char origtime[16];
5631  char dir[PATH_MAX];
5632  char tmpdir[PATH_MAX];
5633  char fn[PATH_MAX];
5634  char prefile[PATH_MAX] = "";
5635  char tempfile[PATH_MAX] = "";
5636  char ext_context[256] = "";
5637  char fmt[80];
5638  char *context;
5639  char ecodes[17] = "#";
5640  struct ast_str *tmp = ast_str_create(16);
5641  char *tmpptr;
5642  struct ast_vm_user *vmu;
5643  struct ast_vm_user svm;
5644  const char *category = NULL;
5645  const char *code;
5646  const char *alldtmf = "0123456789ABCD*#";
5647  char flag[80];
5648 
5649  if (!tmp) {
5650  return -1;
5651  }
5652 
5653  ast_str_set(&tmp, 0, "%s", ext);
5654  ext = ast_str_buffer(tmp);
5655  if ((context = strchr(ext, '@'))) {
5656  *context++ = '\0';
5657  tmpptr = strchr(context, '&');
5658  } else {
5659  tmpptr = strchr(ext, '&');
5660  }
5661 
5662  if (tmpptr)
5663  *tmpptr++ = '\0';
5664 
5665  ast_channel_lock(chan);
5666  if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
5667  category = ast_strdupa(category);
5668  }
5669  ast_channel_unlock(chan);
5670 
5671  if (ast_test_flag(options, OPT_MESSAGE_Urgent)) {
5672  ast_copy_string(flag, "Urgent", sizeof(flag));
5673  } else if (ast_test_flag(options, OPT_MESSAGE_PRIORITY)) {
5674  ast_copy_string(flag, "PRIORITY", sizeof(flag));
5675  } else {
5676  flag[0] = '\0';
5677  }
5678 
5679  ast_debug(3, "Before find_user\n");
5680  if (!(vmu = find_user(&svm, context, ext))) {
5681  ast_log(AST_LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
5682  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5683  ast_free(tmp);
5684  return res;
5685  }
5686  /* Setup pre-file if appropriate */
5687  if (strcmp(vmu->context, "default"))
5688  snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
5689  else
5690  ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
5691 
5692  /* Set the path to the prefile. Will be one of
5693  VM_SPOOL_DIRcontext/ext/busy
5694  VM_SPOOL_DIRcontext/ext/unavail
5695  Depending on the flag set in options.
5696  */
5697  if (ast_test_flag(options, OPT_BUSY_GREETING)) {
5698  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
5699  } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
5700  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
5701  }
5702  /* Set the path to the tmpfile as
5703  VM_SPOOL_DIR/context/ext/temp
5704  and attempt to create the folder structure.
5705  */
5706  snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
5707  if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
5708  ast_log(AST_LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
5709  ast_free(tmp);
5710  return -1;
5711  }
5712  RETRIEVE(tempfile, -1, vmu->mailbox, vmu->context);
5713  if (ast_fileexists(tempfile, NULL, NULL) > 0)
5714  ast_copy_string(prefile, tempfile, sizeof(prefile));
5715 
5716  DISPOSE(tempfile, -1);
5717  /* It's easier just to try to make it than to check for its existence */
5718 #ifndef IMAP_STORAGE
5719  create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
5720 #else
5721  snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
5722  if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
5723  ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
5724  }
5725 #endif
5726 
5727  /* Check current or macro-calling context for special extensions */
5728  if (ast_test_flag(vmu, VM_OPERATOR)) {
5729  if (!ast_strlen_zero(vmu->exit)) {
5730  if (ast_exists_extension(chan, vmu->exit, "o", 1,
5731  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5732  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
5733  ouseexten = 1;
5734  }
5735  } else if (ast_exists_extension(chan, chan->context, "o", 1,
5736  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5737  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
5738  ouseexten = 1;
5739  } else if (!ast_strlen_zero(chan->macrocontext)
5740  && ast_exists_extension(chan, chan->macrocontext, "o", 1,
5741  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5742  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
5743  ousemacro = 1;
5744  }
5745  }
5746 
5747  if (!ast_strlen_zero(vmu->exit)) {
5748  if (ast_exists_extension(chan, vmu->exit, "a", 1,
5749  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5750  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
5751  }
5752  } else if (ast_exists_extension(chan, chan->context, "a", 1,
5753  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5754  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
5755  } else if (!ast_strlen_zero(chan->macrocontext)
5756  && ast_exists_extension(chan, chan->macrocontext, "a", 1,
5757  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5758  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
5759  ausemacro = 1;
5760  }
5761 
5762  if (ast_test_flag(options, OPT_DTMFEXIT)) {
5763  for (code = alldtmf; *code; code++) {
5764  char e[2] = "";
5765  e[0] = *code;
5766  if (strchr(ecodes, e[0]) == NULL
5767  && ast_canmatch_extension(chan,
5768  (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : chan->context),
5769  e, 1, S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
5770  strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
5771  }
5772  }
5773  }
5774 
5775  /* Play the beginning intro if desired */
5776  if (!ast_strlen_zero(prefile)) {
5777 #ifdef ODBC_STORAGE
5778  int success =
5779 #endif
5780  RETRIEVE(prefile, -1, ext, context);
5781  if (ast_fileexists(prefile, NULL, NULL) > 0) {
5782  if (ast_streamfile(chan, prefile, chan->language) > -1)
5783  res = ast_waitstream(chan, ecodes);
5784 #ifdef ODBC_STORAGE
5785  if (success == -1) {
5786  /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
5787  ast_debug(1, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
5788  store_file(prefile, vmu->mailbox, vmu->context, -1);
5789  }
5790 #endif
5791  } else {
5792  ast_debug(1, "%s doesn't exist, doing what we can\n", prefile);
5793  res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
5794  }
5795  DISPOSE(prefile, -1);
5796  if (res < 0) {
5797  ast_debug(1, "Hang up during prefile playback\n");
5798  free_user(vmu);
5799  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5800  ast_free(tmp);
5801  return -1;
5802  }
5803  }
5804  if (res == '#') {
5805  /* On a '#' we skip the instructions */
5806  ast_set_flag(options, OPT_SILENT);
5807  res = 0;
5808  }
5809  /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
5810  if (vmu->maxmsg == 0) {
5811  if (option_debug > 2)
5812  ast_log(LOG_DEBUG, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
5813  pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
5814  goto leave_vm_out;
5815  }
5816  if (!res && !ast_test_flag(options, OPT_SILENT)) {
5817  res = ast_stream_and_wait(chan, INTRO, ecodes);
5818  if (res == '#') {
5819  ast_set_flag(options, OPT_SILENT);
5820  res = 0;
5821  }
5822  }
5823  if (res > 0)
5824  ast_stopstream(chan);
5825  /* Check for a '*' here in case the caller wants to escape from voicemail to something
5826  other than the operator -- an automated attendant or mailbox login for example */
5827  if (res == '*') {
5828  chan->exten[0] = 'a';
5829  chan->exten[1] = '\0';
5830  if (!ast_strlen_zero(vmu->exit)) {
5831  ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
5832  } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
5833  ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
5834  }
5835  chan->priority = 0;
5836  free_user(vmu);
5837  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
5838  ast_free(tmp);
5839  return 0;
5840  }
5841 
5842  /* Check for a '0' here */
5843  if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
5844  transfer:
5845  if (ouseexten || ousemacro) {
5846  chan->exten[0] = 'o';
5847  chan->exten[1] = '\0';
5848  if (!ast_strlen_zero(vmu->exit)) {
5849  ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
5850  } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
5851  ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
5852  }
5853  ast_play_and_wait(chan, "transfer");
5854  chan->priority = 0;
5855  free_user(vmu);
5856  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
5857  }
5858  ast_free(tmp);
5859  return OPERATOR_EXIT;
5860  }
5861 
5862  /* Allow all other digits to exit Voicemail and return to the dialplan */
5863  if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
5864  if (!ast_strlen_zero(options->exitcontext)) {
5865  ast_copy_string(chan->context, options->exitcontext, sizeof(chan->context));
5866  }
5867  free_user(vmu);
5868  ast_free(tmp);
5869  pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
5870  return res;
5871  }
5872 
5873  if (res < 0) {
5874  free_user(vmu);
5875  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5876  ast_free(tmp);
5877  return -1;
5878  }
5879  /* The meat of recording the message... All the announcements and beeps have been played*/
5880  ast_copy_string(fmt, vmfmts, sizeof(fmt));
5881  if (!ast_strlen_zero(fmt)) {
5882  msgnum = 0;
5883 
5884 #ifdef IMAP_STORAGE
5885  /* Is ext a mailbox? */
5886  /* must open stream for this user to get info! */
5887  res = inboxcount(ext_context, &newmsgs, &oldmsgs);
5888  if (res < 0) {
5889  ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
5890  ast_free(tmp);
5891  return -1;
5892  }
5893  if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
5894  /* It is possible under certain circumstances that inboxcount did not
5895  * create a vm_state when it was needed. This is a catchall which will
5896  * rarely be used.
5897  */
5898  if (!(vms = create_vm_state_from_user(vmu))) {
5899  ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
5900  ast_free(tmp);
5901  return -1;
5902  }
5903  }
5904  vms->newmessages++;
5905 
5906  /* here is a big difference! We add one to it later */
5907  msgnum = newmsgs + oldmsgs;
5908  ast_debug(3, "Messagecount set to %d\n", msgnum);
5909  snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
5910  /* set variable for compatibility */
5911  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
5912 
5913  if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
5914  goto leave_vm_out;
5915  }
5916 #else
5917  if (count_messages(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
5918  res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
5919  if (!res)
5920  res = ast_waitstream(chan, "");
5921  ast_log(AST_LOG_WARNING, "No more messages possible\n");
5922  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5923  inprocess_count(vmu->mailbox, vmu->context, -1);
5924  goto leave_vm_out;
5925  }
5926 
5927 #endif
5928  snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
5929  txtdes = mkstemp(tmptxtfile);
5930  chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
5931  if (txtdes < 0) {
5932  res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
5933  if (!res)
5934  res = ast_waitstream(chan, "");
5935  ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
5936  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
5937  inprocess_count(vmu->mailbox, vmu->context, -1);
5938  goto leave_vm_out;
5939  }
5940 
5941  /* Now play the beep once we have the message number for our next message. */
5942  if (res >= 0) {
5943  /* Unless we're *really* silent, try to send the beep */
5944  res = ast_stream_and_wait(chan, "beep", "");
5945  }
5946 
5947  /* Store information in real-time storage */
5948  if (ast_check_realtime("voicemail_data")) {
5949  snprintf(priority, sizeof(priority), "%d", chan->priority);
5950  snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
5951  get_date(date, sizeof(date));
5952  ast_callerid_merge(callerid, sizeof(callerid),
5953  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
5954  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
5955  "Unknown");
5956  ast_store_realtime("voicemail_data",
5957  "origmailbox", ext,
5958  "context", chan->context,
5959  "macrocontext", chan->macrocontext,
5960  "exten", chan->exten,
5961  "priority", priority,
5962  "callerchan", chan->name,
5963  "callerid", callerid,
5964  "origdate", date,
5965  "origtime", origtime,
5966  "category", S_OR(category, ""),
5967  "filename", tmptxtfile,
5968  SENTINEL);
5969  }
5970 
5971  /* Store information */
5972  txt = fdopen(txtdes, "w+");
5973  if (txt) {
5974  get_date(date, sizeof(date));
5975  ast_callerid_merge(callerid, sizeof(callerid),
5976  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
5977  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
5978  "Unknown");
5979  fprintf(txt,
5980  ";\n"
5981  "; Message Information file\n"
5982  ";\n"
5983  "[message]\n"
5984  "origmailbox=%s\n"
5985  "context=%s\n"
5986  "macrocontext=%s\n"
5987  "exten=%s\n"
5988  "rdnis=%s\n"
5989  "priority=%d\n"
5990  "callerchan=%s\n"
5991  "callerid=%s\n"
5992  "origdate=%s\n"
5993  "origtime=%ld\n"
5994  "category=%s\n",
5995  ext,
5996  chan->context,
5997  chan->macrocontext,
5998  chan->exten,
6000  chan->redirecting.from.number.str, "unknown"),
6001  chan->priority,
6002  chan->name,
6003  callerid,
6004  date, (long) time(NULL),
6005  category ? category : "");
6006  } else {
6007  ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
6008  inprocess_count(vmu->mailbox, vmu->context, -1);
6009  if (ast_check_realtime("voicemail_data")) {
6010  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6011  }
6012  res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
6013  goto leave_vm_out;
6014  }
6015  res = play_record_review(chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag);
6016 
6017  if (txt) {
6018  fprintf(txt, "flag=%s\n", flag);
6019  if (sound_duration < vmu->minsecs) {
6020  fclose(txt);
6021  ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
6022  ast_filedelete(tmptxtfile, NULL);
6023  unlink(tmptxtfile);
6024  if (ast_check_realtime("voicemail_data")) {
6025  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6026  }
6027  inprocess_count(vmu->mailbox, vmu->context, -1);
6028  } else {
6029  fprintf(txt, "duration=%d\n", duration);
6030  fclose(txt);
6031  if (vm_lock_path(dir)) {
6032  ast_log(AST_LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
6033  /* Delete files */
6034  ast_filedelete(tmptxtfile, NULL);
6035  unlink(tmptxtfile);
6036  inprocess_count(vmu->mailbox, vmu->context, -1);
6037  } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
6038  ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
6039  unlink(tmptxtfile);
6040  ast_unlock_path(dir);
6041  inprocess_count(vmu->mailbox, vmu->context, -1);
6042  if (ast_check_realtime("voicemail_data")) {
6043  ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6044  }
6045  } else {
6046 #ifndef IMAP_STORAGE
6047  msgnum = last_message_index(vmu, dir) + 1;
6048 #endif
6049  make_file(fn, sizeof(fn), dir, msgnum);
6050 
6051  /* assign a variable with the name of the voicemail file */
6052 #ifndef IMAP_STORAGE
6053  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
6054 #else
6055  pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
6056 #endif
6057 
6058  snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
6059  ast_filerename(tmptxtfile, fn, NULL);
6060  rename(tmptxtfile, txtfile);
6061  inprocess_count(vmu->mailbox, vmu->context, -1);
6062 
6063  /* Properly set permissions on voicemail text descriptor file.
6064  Unfortunately mkstemp() makes this file 0600 on most unix systems. */
6065  if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
6066  ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
6067 
6068  ast_unlock_path(dir);
6069  if (ast_check_realtime("voicemail_data")) {
6070  snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
6071  ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
6072  }
6073  /* We must store the file first, before copying the message, because
6074  * ODBC storage does the entire copy with SQL.
6075  */
6076  if (ast_fileexists(fn, NULL, NULL) > 0) {
6077  STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag);
6078  }
6079 
6080  /* Are there to be more recipients of this message? */
6081  while (tmpptr) {
6082  struct ast_vm_user recipu, *recip;
6083  char *exten, *cntx;
6084 
6085  exten = strsep(&tmpptr, "&");
6086  cntx = strchr(exten, '@');
6087  if (cntx) {
6088  *cntx = '\0';
6089  cntx++;
6090  }
6091  if ((recip = find_user(&recipu, cntx, exten))) {
6092  copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag);
6093  free_user(recip);
6094  }
6095  }
6096 #ifndef IMAP_STORAGE
6097  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If this is an Urgent message */
6098  /* Move the message from INBOX to Urgent folder if this is urgent! */
6099  char sfn[PATH_MAX];
6100  char dfn[PATH_MAX];
6101  int x;
6102  /* It's easier just to try to make it than to check for its existence */
6103  create_dirpath(urgdir, sizeof(urgdir), vmu->context, ext, "Urgent");
6104  x = last_message_index(vmu, urgdir) + 1;
6105  make_file(sfn, sizeof(sfn), dir, msgnum);
6106  make_file(dfn, sizeof(dfn), urgdir, x);
6107  ast_debug(5, "Created an Urgent message, moving file from %s to %s.\n", sfn, dfn);
6108  RENAME(dir, msgnum, vmu->mailbox, vmu->context, urgdir, x, sfn, dfn);
6109  /* Notification must happen for this new message in Urgent folder, not INBOX */
6110  ast_copy_string(fn, dfn, sizeof(fn));
6111  msgnum = x;
6112  }
6113 #endif
6114  /* Notification needs to happen after the copy, though. */
6115  if (ast_fileexists(fn, NULL, NULL)) {
6116 #ifdef IMAP_STORAGE
6117  notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
6118  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
6119  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
6120  flag);
6121 #else
6122  notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
6123  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
6124  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
6125  flag);
6126 #endif
6127  }
6128 
6129  /* Disposal needs to happen after the optional move and copy */
6130  if (ast_fileexists(fn, NULL, NULL)) {
6131  DISPOSE(dir, msgnum);
6132  }
6133  }
6134  }
6135  } else {
6136  inprocess_count(vmu->mailbox, vmu->context, -1);
6137  }
6138  if (res == '0') {
6139  goto transfer;
6140  } else if (res > 0 && res != 't')
6141  res = 0;
6142 
6143  if (sound_duration < vmu->minsecs)
6144  /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
6145  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
6146  else
6147  pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
6148  } else
6149  ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
6150 leave_vm_out:
6151  free_user(vmu);
6152 
6153 #ifdef IMAP_STORAGE
6154  /* expunge message - use UID Expunge if supported on IMAP server*/
6155  ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
6156  if (expungeonhangup == 1) {
6157  ast_mutex_lock(&vms->lock);
6158 #ifdef HAVE_IMAP_TK2006
6159  if (LEVELUIDPLUS (vms->mailstream)) {
6160  mail_expunge_full(vms->mailstream, NIL, EX_UID);
6161  } else
6162 #endif
6163  mail_expunge(vms->mailstream);
6164  ast_mutex_unlock(&vms->lock);
6165  }
6166 #endif
6167 
6168  ast_free(tmp);
6169  return res;
6170 }
6171 
6172 #if !defined(IMAP_STORAGE)
6173 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
6174 {
6175  /* we know the actual number of messages, so stop process when number is hit */
6176 
6177  int x, dest;
6178  char sfn[PATH_MAX];
6179  char dfn[PATH_MAX];
6180 
6181  if (vm_lock_path(dir)) {
6182  return ERROR_LOCK_PATH;
6183  }
6184 
6185  for (x = 0, dest = 0; dest != stopcount && x < vmu->maxmsg + 10; x++) {
6186  make_file(sfn, sizeof(sfn), dir, x);
6187  if (EXISTS(dir, x, sfn, NULL)) {
6188 
6189  if (x != dest) {
6190  make_file(dfn, sizeof(dfn), dir, dest);
6191  RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
6192  }
6193 
6194  dest++;
6195  }
6196  }
6197  ast_unlock_path(dir);
6198 
6199  return dest;
6200 }
6201 #endif
6202 
6203 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
6204 {
6205  int d;
6206  d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
6207  return d;
6208 }
6209 
6210 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
6211 {
6212 #ifdef IMAP_STORAGE
6213  /* we must use mbox(x) folder names, and copy the message there */
6214  /* simple. huh? */
6215  char sequence[10];
6216  char mailbox[256];
6217  int res;
6218 
6219  /* get the real IMAP message number for this message */
6220  snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
6221 
6222  ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
6223  ast_mutex_lock(&vms->lock);
6224  /* if save to Old folder, put in INBOX as read */
6225  if (box == OLD_FOLDER) {
6226  mail_setflag(vms->mailstream, sequence, "\\Seen");
6227  mail_clearflag(vms->mailstream, sequence, "\\Unseen");
6228  } else if (box == NEW_FOLDER) {
6229  mail_setflag(vms->mailstream, sequence, "\\Unseen");
6230  mail_clearflag(vms->mailstream, sequence, "\\Seen");
6231  }
6232  if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
6233  ast_mutex_unlock(&vms->lock);
6234  return 0;
6235  }
6236  /* Create the folder if it don't exist */
6237  imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
6238  ast_debug(5, "Checking if folder exists: %s\n", mailbox);
6239  if (mail_create(vms->mailstream, mailbox) == NIL)
6240  ast_debug(5, "Folder exists.\n");
6241  else
6242  ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
6243  res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
6244  ast_mutex_unlock(&vms->lock);
6245  return res;
6246 #else
6247  char *dir = vms->curdir;
6248  char *username = vms->username;
6249  char *context = vmu->context;
6250  char sfn[PATH_MAX];
6251  char dfn[PATH_MAX];
6252  char ddir[PATH_MAX];
6253  const char *dbox = mbox(vmu, box);
6254  int x, i;
6255  create_dirpath(ddir, sizeof(ddir), context, username, dbox);
6256 
6257  if (vm_lock_path(ddir))
6258  return ERROR_LOCK_PATH;
6259 
6260  x = last_message_index(vmu, ddir) + 1;
6261 
6262  if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
6263  x--;
6264  for (i = 1; i <= x; i++) {
6265  /* Push files down a "slot". The oldest file (msg0000) will be deleted. */
6266  make_file(sfn, sizeof(sfn), ddir, i);
6267  make_file(dfn, sizeof(dfn), ddir, i - 1);
6268  if (EXISTS(ddir, i, sfn, NULL)) {
6269  RENAME(ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
6270  } else
6271  break;
6272  }
6273  } else {
6274  if (x >= vmu->maxmsg) {
6275  ast_unlock_path(ddir);
6276  return -1;
6277  }
6278  }
6279  make_file(sfn, sizeof(sfn), dir, msg);
6280  make_file(dfn, sizeof(dfn), ddir, x);
6281  if (strcmp(sfn, dfn)) {
6282  COPY(dir, msg, ddir, x, username, context, sfn, dfn);
6283  }
6284  ast_unlock_path(ddir);
6285 #endif
6286  return 0;
6287 }
6288 
6289 static int adsi_logo(unsigned char *buf)
6290 {
6291  int bytes = 0;
6292  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
6293  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
6294  return bytes;
6295 }
6296 
6297 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
6298 {
6299  unsigned char buf[256];
6300  int bytes = 0;
6301  int x;
6302  char num[5];
6303 
6304  *useadsi = 0;
6305  bytes += ast_adsi_data_mode(buf + bytes);
6306  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6307 
6308  bytes = 0;
6309  bytes += adsi_logo(buf);
6310  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
6311 #ifdef DISPLAY
6312  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
6313 #endif
6314  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6315  bytes += ast_adsi_data_mode(buf + bytes);
6316  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6317 
6318  if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
6319  bytes = 0;
6320  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
6321  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
6322  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6323  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6324  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6325  return 0;
6326  }
6327 
6328 #ifdef DISPLAY
6329  /* Add a dot */
6330  bytes = 0;
6331  bytes += ast_adsi_logo(buf);
6332  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
6333  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
6334  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6335  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6336 #endif
6337  bytes = 0;
6338  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
6339  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
6340  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
6341  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
6342  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
6343  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
6344  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
6345 
6346 #ifdef DISPLAY
6347  /* Add another dot */
6348  bytes = 0;
6349  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
6350  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6351 
6352  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6353  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6354 #endif
6355 
6356  bytes = 0;
6357  /* These buttons we load but don't use yet */
6358  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
6359  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
6360  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
6361  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
6362  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
6363  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
6364  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
6365 
6366 #ifdef DISPLAY
6367  /* Add another dot */
6368  bytes = 0;
6369  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
6370  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6371  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6372 #endif
6373 
6374  bytes = 0;
6375  for (x = 0; x < 5; x++) {
6376  snprintf(num, sizeof(num), "%d", x);
6377  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
6378  }
6379  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
6380  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
6381 
6382 #ifdef DISPLAY
6383  /* Add another dot */
6384  bytes = 0;
6385  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
6386  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6387  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6388 #endif
6389 
6390  if (ast_adsi_end_download(chan)) {
6391  bytes = 0;
6392  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
6393  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
6394  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6395  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6396  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6397  return 0;
6398  }
6399  bytes = 0;
6400  bytes += ast_adsi_download_disconnect(buf + bytes);
6401  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6402  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
6403 
6404  ast_debug(1, "Done downloading scripts...\n");
6405 
6406 #ifdef DISPLAY
6407  /* Add last dot */
6408  bytes = 0;
6409  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
6410  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6411 #endif
6412  ast_debug(1, "Restarting session...\n");
6413 
6414  bytes = 0;
6415  /* Load the session now */
6416  if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
6417  *useadsi = 1;
6418  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
6419  } else
6420  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
6421 
6422  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6423  return 0;
6424 }
6425 
6426 static void adsi_begin(struct ast_channel *chan, int *useadsi)
6427 {
6428  int x;
6429  if (!ast_adsi_available(chan))
6430  return;
6431  x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
6432  if (x < 0)
6433  return;
6434  if (!x) {
6435  if (adsi_load_vmail(chan, useadsi)) {
6436  ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
6437  return;
6438  }
6439  } else
6440  *useadsi = 1;
6441 }
6442 
6443 static void adsi_login(struct ast_channel *chan)
6444 {
6445  unsigned char buf[256];
6446  int bytes = 0;
6447  unsigned char keys[8];
6448  int x;
6449  if (!ast_adsi_available(chan))
6450  return;
6451 
6452  for (x = 0; x < 8; x++)
6453  keys[x] = 0;
6454  /* Set one key for next */
6455  keys[3] = ADSI_KEY_APPS + 3;
6456 
6457  bytes += adsi_logo(buf + bytes);
6458  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
6459  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
6460  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6461  bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
6462  bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
6463  bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
6464  bytes += ast_adsi_set_keys(buf + bytes, keys);
6465  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6466  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6467 }
6468 
6469 static void adsi_password(struct ast_channel *chan)
6470 {
6471  unsigned char buf[256];
6472  int bytes = 0;
6473  unsigned char keys[8];
6474  int x;
6475  if (!ast_adsi_available(chan))
6476  return;
6477 
6478  for (x = 0; x < 8; x++)
6479  keys[x] = 0;
6480  /* Set one key for next */
6481  keys[3] = ADSI_KEY_APPS + 3;
6482 
6483  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6484  bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
6485  bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
6486  bytes += ast_adsi_set_keys(buf + bytes, keys);
6487  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6488  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6489 }
6490 
6491 static void adsi_folders(struct ast_channel *chan, int start, char *label)
6492 {
6493  unsigned char buf[256];
6494  int bytes = 0;
6495  unsigned char keys[8];
6496  int x, y;
6497 
6498  if (!ast_adsi_available(chan))
6499  return;
6500 
6501  for (x = 0; x < 5; x++) {
6502  y = ADSI_KEY_APPS + 12 + start + x;
6503  if (y > ADSI_KEY_APPS + 12 + 4)
6504  y = 0;
6505  keys[x] = ADSI_KEY_SKT | y;
6506  }
6507  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
6508  keys[6] = 0;
6509  keys[7] = 0;
6510 
6511  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
6512  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
6513  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6514  bytes += ast_adsi_set_keys(buf + bytes, keys);
6515  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6516 
6517  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6518 }
6519 
6520 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
6521 {
6522  int bytes = 0;
6523  unsigned char buf[256];
6524  char buf1[256], buf2[256];
6525  char fn2[PATH_MAX];
6526 
6527  char cid[256] = "";
6528  char *val;
6529  char *name, *num;
6530  char datetime[21] = "";
6531  FILE *f;
6532 
6533  unsigned char keys[8];
6534 
6535  int x;
6536 
6537  if (!ast_adsi_available(chan))
6538  return;
6539 
6540  /* Retrieve important info */
6541  snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
6542  f = fopen(fn2, "r");
6543  if (f) {
6544  while (!feof(f)) {
6545  if (!fgets((char *) buf, sizeof(buf), f)) {
6546  continue;
6547  }
6548  if (!feof(f)) {
6549  char *stringp = NULL;
6550  stringp = (char *) buf;
6551  strsep(&stringp, "=");
6552  val = strsep(&stringp, "=");
6553  if (!ast_strlen_zero(val)) {
6554  if (!strcmp((char *) buf, "callerid"))
6555  ast_copy_string(cid, val, sizeof(cid));
6556  if (!strcmp((char *) buf, "origdate"))
6557  ast_copy_string(datetime, val, sizeof(datetime));
6558  }
6559  }
6560  }
6561  fclose(f);
6562  }
6563  /* New meaning for keys */
6564  for (x = 0; x < 5; x++)
6565  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
6566  keys[6] = 0x0;
6567  keys[7] = 0x0;
6568 
6569  if (!vms->curmsg) {
6570  /* No prev key, provide "Folder" instead */
6571  keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6572  }
6573  if (vms->curmsg >= vms->lastmsg) {
6574  /* If last message ... */
6575  if (vms->curmsg) {
6576  /* but not only message, provide "Folder" instead */
6577  keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6578  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6579 
6580  } else {
6581  /* Otherwise if only message, leave blank */
6582  keys[3] = 1;
6583  }
6584  }
6585 
6586  if (!ast_strlen_zero(cid)) {
6587  ast_callerid_parse(cid, &name, &num);
6588  if (!name)
6589  name = num;
6590  } else
6591  name = "Unknown Caller";
6592 
6593  /* If deleted, show "undeleted" */
6594 #ifdef IMAP_STORAGE
6595  ast_mutex_lock(&vms->lock);
6596 #endif
6597  if (vms->deleted[vms->curmsg]) {
6598  keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
6599  }
6600 #ifdef IMAP_STORAGE
6601  ast_mutex_unlock(&vms->lock);
6602 #endif
6603 
6604  /* Except "Exit" */
6605  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
6606  snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
6607  strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
6608  snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
6609 
6610  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
6611  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
6612  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
6613  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
6614  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6615  bytes += ast_adsi_set_keys(buf + bytes, keys);
6616  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6617 
6618  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6619 }
6620 
6621 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
6622 {
6623  int bytes = 0;
6624  unsigned char buf[256];
6625  unsigned char keys[8];
6626 
6627  int x;
6628 
6629  if (!ast_adsi_available(chan))
6630  return;
6631 
6632  /* New meaning for keys */
6633  for (x = 0; x < 5; x++)
6634  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
6635 
6636  keys[6] = 0x0;
6637  keys[7] = 0x0;
6638 
6639  if (!vms->curmsg) {
6640  /* No prev key, provide "Folder" instead */
6641  keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6642  }
6643  if (vms->curmsg >= vms->lastmsg) {
6644  /* If last message ... */
6645  if (vms->curmsg) {
6646  /* but not only message, provide "Folder" instead */
6647  keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
6648  } else {
6649  /* Otherwise if only message, leave blank */
6650  keys[3] = 1;
6651  }
6652  }
6653 
6654  /* If deleted, show "undeleted" */
6655 #ifdef IMAP_STORAGE
6656  ast_mutex_lock(&vms->lock);
6657 #endif
6658  if (vms->deleted[vms->curmsg]) {
6659  keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
6660  }
6661 #ifdef IMAP_STORAGE
6662  ast_mutex_unlock(&vms->lock);
6663 #endif
6664 
6665  /* Except "Exit" */
6666  keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
6667  bytes += ast_adsi_set_keys(buf + bytes, keys);
6668  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6669 
6670  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6671 }
6672 
6673 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
6674 {
6675  unsigned char buf[256] = "";
6676  char buf1[256] = "", buf2[256] = "";
6677  int bytes = 0;
6678  unsigned char keys[8];
6679  int x;
6680 
6681  char *newm = (vms->newmessages == 1) ? "message" : "messages";
6682  char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
6683  if (!ast_adsi_available(chan))
6684  return;
6685  if (vms->newmessages) {
6686  snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
6687  if (vms->oldmessages) {
6688  strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
6689  snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
6690  } else {
6691  snprintf(buf2, sizeof(buf2), "%s.", newm);
6692  }
6693  } else if (vms->oldmessages) {
6694  snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
6695  snprintf(buf2, sizeof(buf2), "%s.", oldm);
6696  } else {
6697  strcpy(buf1, "You have no messages.");
6698  buf2[0] = ' ';
6699  buf2[1] = '\0';
6700  }
6701  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
6702  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
6703  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6704 
6705  for (x = 0; x < 6; x++)
6706  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
6707  keys[6] = 0;
6708  keys[7] = 0;
6709 
6710  /* Don't let them listen if there are none */
6711  if (vms->lastmsg < 0)
6712  keys[0] = 1;
6713  bytes += ast_adsi_set_keys(buf + bytes, keys);
6714 
6715  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6716 
6717  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6718 }
6719 
6720 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
6721 {
6722  unsigned char buf[256] = "";
6723  char buf1[256] = "", buf2[256] = "";
6724  int bytes = 0;
6725  unsigned char keys[8];
6726  int x;
6727 
6728  char *mess = (vms->lastmsg == 0) ? "message" : "messages";
6729 
6730  if (!ast_adsi_available(chan))
6731  return;
6732 
6733  /* Original command keys */
6734  for (x = 0; x < 6; x++)
6735  keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
6736 
6737  keys[6] = 0;
6738  keys[7] = 0;
6739 
6740  if ((vms->lastmsg + 1) < 1)
6741  keys[0] = 0;
6742 
6743  snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
6744  strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
6745 
6746  if (vms->lastmsg + 1)
6747  snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
6748  else
6749  strcpy(buf2, "no messages.");
6750  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
6751  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
6752  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
6753  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6754  bytes += ast_adsi_set_keys(buf + bytes, keys);
6755 
6756  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6757 
6758  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6759 
6760 }
6761 
6762 /*
6763 static void adsi_clear(struct ast_channel *chan)
6764 {
6765  char buf[256];
6766  int bytes=0;
6767  if (!ast_adsi_available(chan))
6768  return;
6769  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6770  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6771 
6772  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6773 }
6774 */
6775 
6776 static void adsi_goodbye(struct ast_channel *chan)
6777 {
6778  unsigned char buf[256];
6779  int bytes = 0;
6780 
6781  if (!ast_adsi_available(chan))
6782  return;
6783  bytes += adsi_logo(buf + bytes);
6784  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
6785  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
6786  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6787  bytes += ast_adsi_voice_mode(buf + bytes, 0);
6788 
6789  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6790 }
6791 
6792 /*!\brief get_folder: Folder menu
6793  * Plays "press 1 for INBOX messages" etc.
6794  * Should possibly be internationalized
6795  */
6796 static int get_folder(struct ast_channel *chan, int start)
6797 {
6798  int x;
6799  int d;
6800  char fn[PATH_MAX];
6801  d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
6802  if (d)
6803  return d;
6804  for (x = start; x < 5; x++) { /* For all folders */
6805  if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, NULL)))
6806  return d;
6807  d = ast_play_and_wait(chan, "vm-for"); /* "for" */
6808  if (d)
6809  return d;
6810  snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
6811 
6812  /* The inbox folder can have its name changed under certain conditions
6813  * so this checks if the sound file exists for the inbox folder name and
6814  * if it doesn't, plays the default name instead. */
6815  if (x == 0) {
6816  if (ast_fileexists(fn, NULL, NULL)) {
6817  d = vm_play_folder_name(chan, fn);
6818  } else {
6819  ast_verb(1, "failed to find %s\n", fn);
6820  d = vm_play_folder_name(chan, "vm-INBOX");
6821  }
6822  } else {
6823  ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
6824  d = vm_play_folder_name(chan, fn);
6825  }
6826 
6827  if (d)
6828  return d;
6829  d = ast_waitfordigit(chan, 500);
6830  if (d)
6831  return d;
6832  }
6833 
6834  d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
6835  if (d)
6836  return d;
6837  d = ast_waitfordigit(chan, 4000);
6838  return d;
6839 }
6840 
6841 /*!
6842  * \brief plays a prompt and waits for a keypress.
6843  * \param chan
6844  * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
6845  * \param start Does not appear to be used at this time.
6846  *
6847  * This is used by the main menu option to move a message to a folder or to save a message into a folder.
6848  * After playing the message identified by the fn parameter value, it calls get_folder(), which plays the
6849  * prompting for the number inputs that correspond to the available folders.
6850  *
6851  * \return zero on success, or -1 on error.
6852  */
6853 static int get_folder2(struct ast_channel *chan, char *fn, int start)
6854 {
6855  int res = 0;
6856  int loops = 0;
6857 
6858  res = ast_play_and_wait(chan, fn); /* Folder name */
6859  while (((res < '0') || (res > '9')) &&
6860  (res != '#') && (res >= 0) &&
6861  loops < 4) {
6862  res = get_folder(chan, 0);
6863  loops++;
6864  }
6865  if (loops == 4) { /* give up */
6866  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
6867  return '#';
6868  }
6869  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
6870  return res;
6871 }
6872 
6873 /*!
6874  * \brief presents the option to prepend to an existing message when forwarding it.
6875  * \param chan
6876  * \param vmu
6877  * \param curdir
6878  * \param curmsg
6879  * \param vm_fmts
6880  * \param context
6881  * \param record_gain
6882  * \param duration
6883  * \param vms
6884  * \param flag
6885  *
6886  * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
6887  *
6888  * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
6889  * \return zero on success, -1 on error.
6890  */
6891 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts,
6892  char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
6893 {
6894  int cmd = 0;
6895  int retries = 0, prepend_duration = 0, already_recorded = 0;
6896  char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
6897  char textfile[PATH_MAX];
6898  struct ast_config *msg_cfg;
6899  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
6900 #ifndef IMAP_STORAGE
6901  signed char zero_gain = 0;
6902 #endif
6903  const char *duration_str;
6904 
6905  /* Must always populate duration correctly */
6906  make_file(msgfile, sizeof(msgfile), curdir, curmsg);
6907  strcpy(textfile, msgfile);
6908  strcpy(backup, msgfile);
6909  strcpy(backup_textfile, msgfile);
6910  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
6911  strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
6912  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
6913 
6914  if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
6915  *duration = atoi(duration_str);
6916  } else {
6917  *duration = 0;
6918  }
6919 
6920  while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
6921  if (cmd)
6922  retries = 0;
6923  switch (cmd) {
6924  case '1':
6925 
6926 #ifdef IMAP_STORAGE
6927  /* Record new intro file */
6928  make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
6929  strncat(vms->introfn, "intro", sizeof(vms->introfn));
6930  ast_play_and_wait(chan, INTRO);
6931  ast_play_and_wait(chan, "beep");
6932  cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag);
6933  if (cmd == -1) {
6934  break;
6935  }
6936  cmd = 't';
6937 #else
6938 
6939  /* prepend a message to the current message, update the metadata and return */
6940 
6941  make_file(msgfile, sizeof(msgfile), curdir, curmsg);
6942  strcpy(textfile, msgfile);
6943  strncat(textfile, ".txt", sizeof(textfile) - 1);
6944  *duration = 0;
6945 
6946  /* if we can't read the message metadata, stop now */
6947  if (!valid_config(msg_cfg)) {
6948  cmd = 0;
6949  break;
6950  }
6951 
6952  /* Back up the original file, so we can retry the prepend and restore it after forward. */
6953 #ifndef IMAP_STORAGE
6954  if (already_recorded) {
6955  ast_filecopy(backup, msgfile, NULL);
6956  copy(backup_textfile, textfile);
6957  }
6958  else {
6959  ast_filecopy(msgfile, backup, NULL);
6960  copy(textfile, backup_textfile);
6961  }
6962 #endif
6963  already_recorded = 1;
6964 
6965  if (record_gain)
6966  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
6967 
6968  cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
6969 
6970  if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
6971  ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
6972  ast_stream_and_wait(chan, vm_prepend_timeout, "");
6973  ast_filerename(backup, msgfile, NULL);
6974  }
6975 
6976  if (record_gain)
6977  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
6978 
6979 
6980  if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
6981  *duration = atoi(duration_str);
6982 
6983  if (prepend_duration) {
6984  struct ast_category *msg_cat;
6985  /* need enough space for a maximum-length message duration */
6986  char duration_buf[12];
6987 
6988  *duration += prepend_duration;
6989  msg_cat = ast_category_get(msg_cfg, "message");
6990  snprintf(duration_buf, 11, "%ld", *duration);
6991  if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
6992  ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
6993  }
6994  }
6995 
6996 #endif
6997  break;
6998  case '2':
6999  /* NULL out introfile so we know there is no intro! */
7000 #ifdef IMAP_STORAGE
7001  *vms->introfn = '\0';
7002 #endif
7003  cmd = 't';
7004  break;
7005  case '*':
7006  cmd = '*';
7007  break;
7008  default:
7009  /* If time_out and return to menu, reset already_recorded */
7010  already_recorded = 0;
7011 
7012  cmd = ast_play_and_wait(chan, "vm-forwardoptions");
7013  /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
7014  if (!cmd) {
7015  cmd = ast_play_and_wait(chan, "vm-starmain");
7016  /* "press star to return to the main menu" */
7017  }
7018  if (!cmd) {
7019  cmd = ast_waitfordigit(chan, 6000);
7020  }
7021  if (!cmd) {
7022  retries++;
7023  }
7024  if (retries > 3) {
7025  cmd = '*'; /* Let's cancel this beast */
7026  }
7027  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
7028  }
7029  }
7030 
7031  if (valid_config(msg_cfg))
7032  ast_config_destroy(msg_cfg);
7033  if (prepend_duration)
7034  *duration = prepend_duration;
7035 
7036  if (already_recorded && cmd == -1) {
7037  /* restore original message if prepention cancelled */
7038  ast_filerename(backup, msgfile, NULL);
7039  rename(backup_textfile, textfile);
7040  }
7041 
7042  if (cmd == 't' || cmd == 'S') /* XXX entering this block with a value of 'S' is probably no longer possible. */
7043  cmd = 0;
7044  return cmd;
7045 }
7046 
7047 static void queue_mwi_event(const char *box, int urgent, int new, int old)
7048 {
7049  struct ast_event *event;
7050  char *mailbox, *context;
7051 
7052  /* Strip off @default */
7053  context = mailbox = ast_strdupa(box);
7054  strsep(&context, "@");
7055  if (ast_strlen_zero(context))
7056  context = "default";
7057 
7058  if (!(event = ast_event_new(AST_EVENT_MWI,
7063  AST_EVENT_IE_END))) {
7064  return;
7065  }
7066 
7068 }
7069 
7070 /*!
7071  * \brief Sends email notification that a user has a new voicemail waiting for them.
7072  * \param chan
7073  * \param vmu
7074  * \param vms
7075  * \param msgnum
7076  * \param duration
7077  * \param fmt
7078  * \param cidnum The Caller ID phone number value.
7079  * \param cidname The Caller ID name value.
7080  * \param flag
7081  *
7082  * \return zero on success, -1 on error.
7083  */
7084 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)
7085 {
7086  char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
7087  int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
7088  const char *category;
7089  char *myserveremail = serveremail;
7090 
7091  ast_channel_lock(chan);
7092  if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
7093  category = ast_strdupa(category);
7094  }
7095  ast_channel_unlock(chan);
7096 
7097 #ifndef IMAP_STORAGE
7098  make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
7099 #else
7100  snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
7101 #endif
7102  make_file(fn, sizeof(fn), todir, msgnum);
7103  snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
7104 
7105  if (!ast_strlen_zero(vmu->attachfmt)) {
7106  if (strstr(fmt, vmu->attachfmt))
7107  fmt = vmu->attachfmt;
7108  else
7109  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);
7110  }
7111 
7112  /* Attach only the first format */
7113  fmt = ast_strdupa(fmt);
7114  stringp = fmt;
7115  strsep(&stringp, "|");
7116 
7117  if (!ast_strlen_zero(vmu->serveremail))
7118  myserveremail = vmu->serveremail;
7119 
7120  if (!ast_strlen_zero(vmu->email)) {
7121  int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
7122 
7123  if (attach_user_voicemail)
7124  RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
7125 
7126  /* XXX possible imap issue, should category be NULL XXX */
7127  sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag);
7128 
7129  if (attach_user_voicemail)
7130  DISPOSE(todir, msgnum);
7131  }
7132 
7133  if (!ast_strlen_zero(vmu->pager)) {
7134  sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
7135  }
7136 
7137  if (ast_test_flag(vmu, VM_DELETE))
7138  DELETE(todir, msgnum, fn, vmu);
7139 
7140  /* Leave voicemail for someone */
7141  if (ast_app_has_voicemail(ext_context, NULL))
7142  ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
7143 
7144  queue_mwi_event(ext_context, urgentmsgs, newmsgs, oldmsgs);
7145 
7146  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);
7147  run_externnotify(vmu->context, vmu->mailbox, flag);
7148 
7149 #ifdef IMAP_STORAGE
7150  vm_delete(fn); /* Delete the file, but not the IMAP message */
7151  if (ast_test_flag(vmu, VM_DELETE)) { /* Delete the IMAP message if delete = yes */
7152  vm_imap_delete(NULL, vms->curmsg, vmu);
7153  vms->newmessages--; /* Fix new message count */
7154  }
7155 #endif
7156 
7157  return 0;
7158 }
7159 
7160 /*!
7161  * \brief Sends a voicemail message to a mailbox recipient.
7162  * \param chan
7163  * \param context
7164  * \param vms
7165  * \param sender
7166  * \param fmt
7167  * \param is_new_message Used to indicate the mode for which this method was invoked.
7168  * Will be 0 when called to forward an existing message (option 8)
7169  * Will be 1 when called to leave a message (option 3->5)
7170  * \param record_gain
7171  * \param urgent
7172  *
7173  * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
7174  *
7175  * When in the leave message mode (is_new_message == 1):
7176  * - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
7177  * - attempt to determine the context and and mailbox, and then invoke leave_message() function to record and store the message.
7178  *
7179  * When in the forward message mode (is_new_message == 0):
7180  * - retreives the current message to be forwarded
7181  * - copies the original message to a temporary file, so updates to the envelope can be done.
7182  * - determines the target mailbox and folders
7183  * - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
7184  *
7185  * \return zero on success, -1 on error.
7186  */
7187 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)
7188 {
7189 #ifdef IMAP_STORAGE
7190  int todircount = 0;
7191  struct vm_state *dstvms;
7192 #endif
7193  char username[70]="";
7194  char fn[PATH_MAX]; /* for playback of name greeting */
7195  char ecodes[16] = "#";
7196  int res = 0, cmd = 0;
7197  struct ast_vm_user *receiver = NULL, *vmtmp;
7199  char *stringp;
7200  const char *s;
7201  int saved_messages = 0;
7202  int valid_extensions = 0;
7203  char *dir;
7204  int curmsg;
7205  char urgent_str[7] = "";
7206  int prompt_played = 0;
7207 #ifndef IMAP_STORAGE
7208  char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
7209 #endif
7210  if (ast_test_flag((&globalflags), VM_FWDURGAUTO)) {
7211  ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
7212  }
7213 
7214  if (vms == NULL) return -1;
7215  dir = vms->curdir;
7216  curmsg = vms->curmsg;
7217 
7218  ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
7219  while (!res && !valid_extensions) {
7220  int use_directory = 0;
7221  if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
7222  int done = 0;
7223  int retries = 0;
7224  cmd = 0;
7225  while ((cmd >= 0) && !done ){
7226  if (cmd)
7227  retries = 0;
7228  switch (cmd) {
7229  case '1':
7230  use_directory = 0;
7231  done = 1;
7232  break;
7233  case '2':
7234  use_directory = 1;
7235  done = 1;
7236  break;
7237  case '*':
7238  cmd = 't';
7239  done = 1;
7240  break;
7241  default:
7242  /* Press 1 to enter an extension press 2 to use the directory */
7243  cmd = ast_play_and_wait(chan, "vm-forward");
7244  if (!cmd) {
7245  cmd = ast_waitfordigit(chan, 3000);
7246  }
7247  if (!cmd) {
7248  retries++;
7249  }
7250  if (retries > 3) {
7251  cmd = 't';
7252  done = 1;
7253  }
7254  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
7255  }
7256  }
7257  if (cmd < 0 || cmd == 't')
7258  break;
7259  }
7260 
7261  if (use_directory) {
7262  /* use app_directory */
7263 
7264  char old_context[sizeof(chan->context)];
7265  char old_exten[sizeof(chan->exten)];
7266  int old_priority;
7267  struct ast_app* directory_app;
7268 
7269  directory_app = pbx_findapp("Directory");
7270  if (directory_app) {
7271  char vmcontext[256];
7272  /* make backup copies */
7273  memcpy(old_context, chan->context, sizeof(chan->context));
7274  memcpy(old_exten, chan->exten, sizeof(chan->exten));
7275  old_priority = chan->priority;
7276 
7277  /* call the the Directory, changes the channel */
7278  snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
7279  res = pbx_exec(chan, directory_app, vmcontext);
7280 
7281  ast_copy_string(username, chan->exten, sizeof(username));
7282 
7283  /* restore the old context, exten, and priority */
7284  memcpy(chan->context, old_context, sizeof(chan->context));
7285  memcpy(chan->exten, old_exten, sizeof(chan->exten));
7286  chan->priority = old_priority;
7287  } else {
7288  ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
7289  ast_clear_flag((&globalflags), VM_DIRECFORWARD);
7290  }
7291  } else {
7292  /* Ask for an extension */
7293  res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
7294  prompt_played++;
7295  if (res || prompt_played > 4)
7296  break;
7297  if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
7298  break;
7299  }
7300 
7301  /* start all over if no username */
7302  if (ast_strlen_zero(username))
7303  continue;
7304  stringp = username;
7305  s = strsep(&stringp, "*");
7306  /* start optimistic */
7307  valid_extensions = 1;
7308  while (s) {
7309  if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
7310  int oldmsgs;
7311  int newmsgs;
7312  int capacity;
7313  if (inboxcount(s, &newmsgs, &oldmsgs)) {
7314  ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", s);
7315  /* Shouldn't happen, but allow trying another extension if it does */
7316  res = ast_play_and_wait(chan, "pbx-invalid");
7317  valid_extensions = 0;
7318  break;
7319  }
7320  capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
7321  if ((newmsgs + oldmsgs) >= capacity) {
7322  ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", s, capacity);
7323  res = ast_play_and_wait(chan, "vm-mailboxfull");
7324  valid_extensions = 0;
7325  while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
7326  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
7327  free_user(vmtmp);
7328  }
7329  inprocess_count(receiver->mailbox, receiver->context, -1);
7330  break;
7331  }
7332  AST_LIST_INSERT_HEAD(&extensions, receiver, list);
7333  } else {
7334  /* XXX Optimization for the future. When we encounter a single bad extension,
7335  * bailing out on all of the extensions may not be the way to go. We should
7336  * probably just bail on that single extension, then allow the user to enter
7337  * several more. XXX
7338  */
7339  while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
7340  free_user(receiver);
7341  }
7342  ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", s);
7343  /* "I am sorry, that's not a valid extension. Please try again." */
7344  res = ast_play_and_wait(chan, "pbx-invalid");
7345  valid_extensions = 0;
7346  break;
7347  }
7348 
7349  /* play name if available, else play extension number */
7350  snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
7351  RETRIEVE(fn, -1, s, receiver->context);
7352  if (ast_fileexists(fn, NULL, NULL) > 0) {
7353  res = ast_stream_and_wait(chan, fn, ecodes);
7354  if (res) {
7355  DISPOSE(fn, -1);
7356  return res;
7357  }
7358  } else {
7359  res = ast_say_digit_str(chan, s, ecodes, chan->language);
7360  }
7361  DISPOSE(fn, -1);
7362 
7363  s = strsep(&stringp, "*");
7364  }
7365  /* break from the loop of reading the extensions */
7366  if (valid_extensions)
7367  break;
7368  }
7369  /* check if we're clear to proceed */
7370  if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
7371  return res;
7372  if (is_new_message == 1) {
7373  struct leave_vm_options leave_options;
7374  char mailbox[AST_MAX_EXTENSION * 2 + 2];
7375  snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
7376 
7377  /* Send VoiceMail */
7378  memset(&leave_options, 0, sizeof(leave_options));
7379  leave_options.record_gain = record_gain;
7380  cmd = leave_voicemail(chan, mailbox, &leave_options);
7381  } else {
7382  /* Forward VoiceMail */
7383  long duration = 0;
7384  struct vm_state vmstmp;
7385  int copy_msg_result = 0;
7386  memcpy(&vmstmp, vms, sizeof(vmstmp));
7387 
7388  RETRIEVE(dir, curmsg, sender->mailbox, sender->context);
7389 
7390  cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
7391  if (!cmd) {
7392  AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
7393 #ifdef IMAP_STORAGE
7394  int attach_user_voicemail;
7395  char *myserveremail = serveremail;
7396 
7397  /* get destination mailbox */
7398  dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
7399  if (!dstvms) {
7400  dstvms = create_vm_state_from_user(vmtmp);
7401  }
7402  if (dstvms) {
7403  init_mailstream(dstvms, 0);
7404  if (!dstvms->mailstream) {
7405  ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
7406  } else {
7407  copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str);
7408  run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str);
7409  }
7410  } else {
7411  ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
7412  }
7413  if (!ast_strlen_zero(vmtmp->serveremail))
7414  myserveremail = vmtmp->serveremail;
7415  attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
7416  /* NULL category for IMAP storage */
7417  sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
7418  dstvms->curbox,
7419  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL),
7420  S_COR(chan->caller.id.name.valid, chan->caller.id.name.str, NULL),
7421  vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
7422  NULL, urgent_str);
7423 #else
7424  copy_msg_result = copy_message(chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str);
7425 #endif
7426  saved_messages++;
7428  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
7429  free_user(vmtmp);
7430  if (res)
7431  break;
7432  }
7434  if (saved_messages > 0 && !copy_msg_result) {
7435  /* give confirmation that the message was saved */
7436  /* commented out since we can't forward batches yet
7437  if (saved_messages == 1)
7438  res = ast_play_and_wait(chan, "vm-message");
7439  else
7440  res = ast_play_and_wait(chan, "vm-messages");
7441  if (!res)
7442  res = ast_play_and_wait(chan, "vm-saved"); */
7443 #ifdef IMAP_STORAGE
7444  /* If forwarded with intro, DON'T PLAY THIS MESSAGE AGAIN! */
7445  if (ast_strlen_zero(vmstmp.introfn))
7446 #endif
7447  res = ast_play_and_wait(chan, "vm-msgsaved");
7448  }
7449 #ifndef IMAP_STORAGE
7450  else {
7451  /* with IMAP, mailbox full warning played by imap_check_limits */
7452  res = ast_play_and_wait(chan, "vm-mailboxfull");
7453  }
7454  /* Restore original message without prepended message if backup exists */
7455  make_file(msgfile, sizeof(msgfile), dir, curmsg);
7456  strcpy(textfile, msgfile);
7457  strcpy(backup, msgfile);
7458  strcpy(backup_textfile, msgfile);
7459  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
7460  strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
7461  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
7462  if (ast_fileexists(backup, NULL, NULL) > 0) {
7463  ast_filerename(backup, msgfile, NULL);
7464  rename(backup_textfile, textfile);
7465  }
7466 #endif
7467  }
7468  DISPOSE(dir, curmsg);
7469 #ifndef IMAP_STORAGE
7470  if (cmd) { /* assuming hangup, cleanup backup file */
7471  make_file(msgfile, sizeof(msgfile), dir, curmsg);
7472  strcpy(textfile, msgfile);
7473  strcpy(backup_textfile, msgfile);
7474  strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
7475  strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
7476  rename(backup_textfile, textfile);
7477  }
7478 #endif
7479  }
7480 
7481  /* If anything failed above, we still have this list to free */
7482  while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
7483  inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
7484  free_user(vmtmp);
7485  }
7486  return res ? res : cmd;
7487 }
7488 
7489 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
7490 {
7491  int res;
7492  if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
7493  ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file);
7494  return res;
7495 }
7496 
7497 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
7498 {
7499  ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
7500  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);
7501 }
7502 
7503 static int play_message_category(struct ast_channel *chan, const char *category)
7504 {
7505  int res = 0;
7506 
7507  if (!ast_strlen_zero(category))
7508  res = ast_play_and_wait(chan, category);
7509 
7510  if (res) {
7511  ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
7512  res = 0;
7513  }
7514 
7515  return res;
7516 }
7517 
7518 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
7519 {
7520  int res = 0;
7521  struct vm_zone *the_zone = NULL;
7522  time_t t;
7523 
7524  if (ast_get_time_t(origtime, &t, 0, NULL)) {
7525  ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
7526  return 0;
7527  }
7528 
7529  /* Does this user have a timezone specified? */
7530  if (!ast_strlen_zero(vmu->zonetag)) {
7531  /* Find the zone in the list */
7532  struct vm_zone *z;
7533  AST_LIST_LOCK(&zones);
7534  AST_LIST_TRAVERSE(&zones, z, list) {
7535  if (!strcmp(z->name, vmu->zonetag)) {
7536  the_zone = z;
7537  break;
7538  }
7539  }
7541  }
7542 
7543 /* No internal variable parsing for now, so we'll comment it out for the time being */
7544 #if 0
7545  /* Set the DIFF_* variables */
7546  ast_localtime(&t, &time_now, NULL);
7547  tv_now = ast_tvnow();
7548  ast_localtime(&tv_now, &time_then, NULL);
7549 
7550  /* Day difference */
7551  if (time_now.tm_year == time_then.tm_year)
7552  snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
7553  else
7554  snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
7555  pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
7556 
7557  /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
7558 #endif
7559  if (the_zone) {
7560  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
7561  } else if (!strncasecmp(chan->language, "de", 2)) { /* GERMAN syntax */
7562  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
7563  } else if (!strncasecmp(chan->language, "gr", 2)) { /* GREEK syntax */
7564  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
7565  } else if (!strncasecmp(chan->language, "it", 2)) { /* ITALIAN syntax */
7566  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);
7567  } else if (!strncasecmp(chan->language, "nl", 2)) { /* DUTCH syntax */
7568  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
7569  } else if (!strncasecmp(chan->language, "no", 2)) { /* NORWEGIAN syntax */
7570  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
7571  } else if (!strncasecmp(chan->language, "pl", 2)) { /* POLISH syntax */
7572  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
7573  } else if (!strncasecmp(chan->language, "pt_BR", 5)) { /* Brazillian PORTUGUESE syntax */
7574  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);
7575  } else if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
7576  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
7577  } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
7578  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "qR 'vm-received'", NULL);
7579  } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
7580  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);
7581  } else {
7582  res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
7583  }
7584 #if 0
7585  pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
7586 #endif
7587  return res;
7588 }
7589 
7590 
7591 
7592 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
7593 {
7594  int res = 0;
7595  int i;
7596  char *callerid, *name;
7597  char prefile[PATH_MAX] = "";
7598 
7599 
7600  /* If voicemail cid is not enabled, or we didn't get cid or context from
7601  * the attribute file, leave now.
7602  *
7603  * TODO Still need to change this so that if this function is called by the
7604  * message envelope (and someone is explicitly requesting to hear the CID),
7605  * it does not check to see if CID is enabled in the config file.
7606  */
7607  if ((cid == NULL)||(context == NULL))
7608  return res;
7609 
7610  /* Strip off caller ID number from name */
7611  ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
7612  ast_callerid_parse(cid, &name, &callerid);
7613  if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
7614  /* Check for internal contexts and only */
7615  /* say extension when the call didn't come from an internal context in the list */
7616  for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
7617  ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
7618  if ((strcmp(cidinternalcontexts[i], context) == 0))
7619  break;
7620  }
7621  if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
7622  if (!res) {
7623  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
7624  if (!ast_strlen_zero(prefile)) {
7625  /* See if we can find a recorded name for this person instead of their extension number */
7626  if (ast_fileexists(prefile, NULL, NULL) > 0) {
7627  ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
7628  if (!callback)
7629  res = wait_file2(chan, vms, "vm-from");
7630  res = ast_stream_and_wait(chan, prefile, "");
7631  } else {
7632  ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
7633  /* Say "from extension" as one saying to sound smoother */
7634  if (!callback)
7635  res = wait_file2(chan, vms, "vm-from-extension");
7636  res = ast_say_digit_str(chan, callerid, "", chan->language);
7637  }
7638  }
7639  }
7640  } else if (!res) {
7641  ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
7642  /* Since this is all nicely figured out, why not say "from phone number" in this case? */
7643  if (!callback)
7644  res = wait_file2(chan, vms, "vm-from-phonenumber");
7645  res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
7646  }
7647  } else {
7648  /* Number unknown */
7649  ast_debug(1, "VM-CID: From an unknown number\n");
7650  /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
7651  res = wait_file2(chan, vms, "vm-unknown-caller");
7652  }
7653  return res;
7654 }
7655 
7656 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
7657 {
7658  int res = 0;
7659  int durationm;
7660  int durations;
7661  /* Verify that we have a duration for the message */
7662  if (duration == NULL)
7663  return res;
7664 
7665  /* Convert from seconds to minutes */
7666  durations = atoi(duration);
7667  durationm = (durations / 60);
7668 
7669  ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
7670 
7671  if ((!res) && (durationm >= minduration)) {
7672  res = wait_file2(chan, vms, "vm-duration");
7673 
7674  /* POLISH syntax */
7675  if (!strncasecmp(chan->language, "pl", 2)) {
7676  div_t num = div(durationm, 10);
7677 
7678  if (durationm == 1) {
7679  res = ast_play_and_wait(chan, "digits/1z");
7680  res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
7681  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
7682  if (num.rem == 2) {
7683  if (!num.quot) {
7684  res = ast_play_and_wait(chan, "digits/2-ie");
7685  } else {
7686  res = say_and_wait(chan, durationm - 2 , chan->language);
7687  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
7688  }
7689  } else {
7690  res = say_and_wait(chan, durationm, chan->language);
7691  }
7692  res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
7693  } else {
7694  res = say_and_wait(chan, durationm, chan->language);
7695  res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
7696  }
7697  /* DEFAULT syntax */
7698  } else {
7699  res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
7700  res = wait_file2(chan, vms, "vm-minutes");
7701  }
7702  }
7703  return res;
7704 }
7705 
7706 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
7707 {
7708  int res = 0;
7709  char filename[256], *cid;
7710  const char *origtime, *context, *category, *duration, *flag;
7711  struct ast_config *msg_cfg;
7712  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
7713 
7714  vms->starting = 0;
7715  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
7716  adsi_message(chan, vms);
7717  if (!vms->curmsg) {
7718  res = wait_file2(chan, vms, "vm-first"); /* "First" */
7719  } else if (vms->curmsg == vms->lastmsg) {
7720  res = wait_file2(chan, vms, "vm-last"); /* "last" */
7721  }
7722 
7723  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
7724  RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
7725  msg_cfg = ast_config_load(filename, config_flags);
7726  if (!valid_config(msg_cfg)) {
7727  ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
7728  return 0;
7729  }
7730  flag = ast_variable_retrieve(msg_cfg, "message", "flag");
7731 
7732  /* Play the word urgent if we are listening to urgent messages */
7733  if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
7734  res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
7735  }
7736 
7737  if (!res) {
7738  /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
7739  /* POLISH syntax */
7740  if (!strncasecmp(chan->language, "pl", 2)) {
7741  if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
7742  int ten, one;
7743  char nextmsg[256];
7744  ten = (vms->curmsg + 1) / 10;
7745  one = (vms->curmsg + 1) % 10;
7746 
7747  if (vms->curmsg < 20) {
7748  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
7749  res = wait_file2(chan, vms, nextmsg);
7750  } else {
7751  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
7752  res = wait_file2(chan, vms, nextmsg);
7753  if (one > 0) {
7754  if (!res) {
7755  snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
7756  res = wait_file2(chan, vms, nextmsg);
7757  }
7758  }
7759  }
7760  }
7761  if (!res)
7762  res = wait_file2(chan, vms, "vm-message");
7763  /* HEBREW syntax */
7764  } else if (!strncasecmp(chan->language, "he", 2)) {
7765  if (!vms->curmsg) {
7766  res = wait_file2(chan, vms, "vm-message");
7767  res = wait_file2(chan, vms, "vm-first");
7768  } else if (vms->curmsg == vms->lastmsg) {
7769  res = wait_file2(chan, vms, "vm-message");
7770  res = wait_file2(chan, vms, "vm-last");
7771  } else {
7772  res = wait_file2(chan, vms, "vm-message");
7773  res = wait_file2(chan, vms, "vm-number");
7774  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
7775  }
7776  /* VIETNAMESE syntax */
7777  } else if (!strncasecmp(chan->language, "vi", 2)) {
7778  if (!vms->curmsg) {
7779  res = wait_file2(chan, vms, "vm-message");
7780  res = wait_file2(chan, vms, "vm-first");
7781  } else if (vms->curmsg == vms->lastmsg) {
7782  res = wait_file2(chan, vms, "vm-message");
7783  res = wait_file2(chan, vms, "vm-last");
7784  } else {
7785  res = wait_file2(chan, vms, "vm-message");
7786  res = wait_file2(chan, vms, "vm-number");
7787  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
7788  }
7789  } else {
7790  if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
7791  res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
7792  } else { /* DEFAULT syntax */
7793  res = wait_file2(chan, vms, "vm-message");
7794  }
7795  if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
7796  if (!res) {
7797  ast_test_suite_event_notify("PLAYBACK", "Message: message number");
7798  res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
7799  }
7800  }
7801  }
7802  }
7803 
7804  if (!valid_config(msg_cfg)) {
7805  ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
7806  return 0;
7807  }
7808 
7809  if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
7810  ast_log(AST_LOG_WARNING, "No origtime?!\n");
7811  DISPOSE(vms->curdir, vms->curmsg);
7812  ast_config_destroy(msg_cfg);
7813  return 0;
7814  }
7815 
7816  cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
7817  duration = ast_variable_retrieve(msg_cfg, "message", "duration");
7818  category = ast_variable_retrieve(msg_cfg, "message", "category");
7819 
7820  context = ast_variable_retrieve(msg_cfg, "message", "context");
7821  if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
7822  context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
7823  if (!res) {
7824  res = play_message_category(chan, category);
7825  }
7826  if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
7827  res = play_message_datetime(chan, vmu, origtime, filename);
7828  }
7829  if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
7830  res = play_message_callerid(chan, vms, cid, context, 0);
7831  }
7832  if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
7833  res = play_message_duration(chan, vms, duration, vmu->saydurationm);
7834  }
7835  /* Allow pressing '1' to skip envelope / callerid */
7836  if (res == '1') {
7837  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
7838  res = 0;
7839  }
7840  ast_config_destroy(msg_cfg);
7841 
7842  if (!res) {
7843  make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
7844 #ifdef IMAP_STORAGE
7845  ast_mutex_lock(&vms->lock);
7846 #endif
7847  vms->heard[vms->curmsg] = 1;
7848 #ifdef IMAP_STORAGE
7849  ast_mutex_unlock(&vms->lock);
7850  /*IMAP storage stores any prepended message from a forward
7851  * as a separate file from the rest of the message
7852  */
7853  if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
7854  wait_file(chan, vms, vms->introfn);
7855  }
7856 #endif
7857  if ((res = wait_file(chan, vms, vms->fn)) < 0) {
7858  ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
7859  res = 0;
7860  }
7861  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
7862  }
7863  DISPOSE(vms->curdir, vms->curmsg);
7864  return res;
7865 }
7866 
7867 #ifdef IMAP_STORAGE
7868 static int imap_remove_file(char *dir, int msgnum)
7869 {
7870  char fn[PATH_MAX];
7871  char full_fn[PATH_MAX];
7872  char intro[PATH_MAX] = {0,};
7873 
7874  if (msgnum > -1) {
7875  make_file(fn, sizeof(fn), dir, msgnum);
7876  snprintf(intro, sizeof(intro), "%sintro", fn);
7877  } else
7878  ast_copy_string(fn, dir, sizeof(fn));
7879 
7880  if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
7881  ast_filedelete(fn, NULL);
7882  if (!ast_strlen_zero(intro)) {
7883  ast_filedelete(intro, NULL);
7884  }
7885  snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
7886  unlink(full_fn);
7887  }
7888  return 0;
7889 }
7890 
7891 
7892 
7893 static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
7894 {
7895  char *file, *filename;
7896  char *attachment;
7897  char arg[10];
7898  int i;
7899  BODY* body;
7900 
7901  file = strrchr(ast_strdupa(dir), '/');
7902  if (file) {
7903  *file++ = '\0';
7904  } else {
7905  ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
7906  return -1;
7907  }
7908 
7909  ast_mutex_lock(&vms->lock);
7910  for (i = 0; i < vms->mailstream->nmsgs; i++) {
7911  mail_fetchstructure(vms->mailstream, i + 1, &body);
7912  /* We have the body, now we extract the file name of the first attachment. */
7913  if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
7914  attachment = ast_strdupa(body->nested.part->next->body.parameter->value);
7915  } else {
7916  ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
7917  ast_mutex_unlock(&vms->lock);
7918  return -1;
7919  }
7920  filename = strsep(&attachment, ".");
7921  if (!strcmp(filename, file)) {
7922  sprintf(arg, "%d", i + 1);
7923  mail_setflag(vms->mailstream, arg, "\\DELETED");
7924  }
7925  }
7926  mail_expunge(vms->mailstream);
7927  ast_mutex_unlock(&vms->lock);
7928  return 0;
7929 }
7930 
7931 #elif !defined(IMAP_STORAGE)
7932 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
7933 {
7934  int count_msg, last_msg;
7935 
7936  ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
7937 
7938  /* Rename the member vmbox HERE so that we don't try to return before
7939  * we know what's going on.
7940  */
7941  snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
7942 
7943  /* Faster to make the directory than to check if it exists. */
7944  create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
7945 
7946  /* traverses directory using readdir (or select query for ODBC) */
7947  count_msg = count_messages(vmu, vms->curdir);
7948  if (count_msg < 0) {
7949  return count_msg;
7950  } else {
7951  vms->lastmsg = count_msg - 1;
7952  }
7953 
7954  if (vm_allocate_dh(vms, vmu, count_msg)) {
7955  return -1;
7956  }
7957 
7958  /*
7959  The following test is needed in case sequencing gets messed up.
7960  There appears to be more than one way to mess up sequence, so
7961  we will not try to find all of the root causes--just fix it when
7962  detected.
7963  */
7964 
7965  if (vm_lock_path(vms->curdir)) {
7966  ast_log(AST_LOG_ERROR, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
7967  return ERROR_LOCK_PATH;
7968  }
7969 
7970  /* for local storage, checks directory for messages up to maxmsg limit */
7971  last_msg = last_message_index(vmu, vms->curdir);
7972  ast_unlock_path(vms->curdir);
7973 
7974  if (last_msg < -1) {
7975  return last_msg;
7976  } else if (vms->lastmsg != last_msg) {
7977  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);
7978  resequence_mailbox(vmu, vms->curdir, count_msg);
7979  }
7980 
7981  return 0;
7982 }
7983 #endif
7984 
7985 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
7986 {
7987  int x = 0;
7988  int last_msg_idx = 0;
7989 
7990 #ifndef IMAP_STORAGE
7991  int res = 0, nummsg;
7992  char fn2[PATH_MAX];
7993 #endif
7994 
7995  if (vms->lastmsg <= -1) {
7996  goto done;
7997  }
7998 
7999  vms->curmsg = -1;
8000 #ifndef IMAP_STORAGE
8001  /* Get the deleted messages fixed */
8002  if (vm_lock_path(vms->curdir)) {
8003  return ERROR_LOCK_PATH;
8004  }
8005 
8006  /* update count as message may have arrived while we've got mailbox open */
8007  last_msg_idx = last_message_index(vmu, vms->curdir);
8008  if (last_msg_idx != vms->lastmsg) {
8009  ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
8010  }
8011 
8012  /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
8013  for (x = 0; x < last_msg_idx + 1; x++) {
8014  if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
8015  /* Save this message. It's not in INBOX or hasn't been heard */
8016  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
8017  if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
8018  break;
8019  }
8020  vms->curmsg++;
8021  make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
8022  if (strcmp(vms->fn, fn2)) {
8023  RENAME(vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
8024  }
8025  } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
8026  /* Move to old folder before deleting */
8027  res = save_to_folder(vmu, vms, x, 1);
8028  if (res == ERROR_LOCK_PATH) {
8029  /* If save failed do not delete the message */
8030  ast_log(AST_LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
8031  vms->deleted[x] = 0;
8032  vms->heard[x] = 0;
8033  --x;
8034  }
8035  } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
8036  /* Move to deleted folder */
8037  res = save_to_folder(vmu, vms, x, 10);
8038  if (res == ERROR_LOCK_PATH) {
8039  /* If save failed do not delete the message */
8040  vms->deleted[x] = 0;
8041  vms->heard[x] = 0;
8042  --x;
8043  }
8044  } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
8045  /* If realtime storage enabled - we should explicitly delete this message,
8046  cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
8047  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
8048  if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
8049  DELETE(vms->curdir, x, vms->fn, vmu);
8050  }
8051  }
8052  }
8053 
8054  /* Delete ALL remaining messages */
8055  nummsg = x - 1;
8056  for (x = vms->curmsg + 1; x <= nummsg; x++) {
8057  make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
8058  if (EXISTS(vms->curdir, x, vms->fn, NULL)) {
8059  DELETE(vms->curdir, x, vms->fn, vmu);
8060  }
8061  }
8062  ast_unlock_path(vms->curdir);
8063 #else /* defined(IMAP_STORAGE) */
8064  ast_mutex_lock(&vms->lock);
8065  if (vms->deleted) {
8066  /* Since we now expunge after each delete, deleting in reverse order
8067  * ensures that no reordering occurs between each step. */
8068  last_msg_idx = vms->dh_arraysize;
8069  for (x = last_msg_idx - 1; x >= 0; x--) {
8070  if (vms->deleted[x]) {
8071  ast_debug(3, "IMAP delete of %d\n", x);
8072  DELETE(vms->curdir, x, vms->fn, vmu);
8073  }
8074  }
8075  }
8076 #endif
8077 
8078 done:
8079  if (vms->deleted) {
8080  ast_free(vms->deleted);
8081  vms->deleted = NULL;
8082  }
8083  if (vms->heard) {
8084  ast_free(vms->heard);
8085  vms->heard = NULL;
8086  }
8087  vms->dh_arraysize = 0;
8088 #ifdef IMAP_STORAGE
8089  ast_mutex_unlock(&vms->lock);
8090 #endif
8091 
8092  return 0;
8093 }
8094 
8095 /* In Greek even though we CAN use a syntax like "friends messages"
8096  * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
8097  * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
8098  * syntax for the above three categories which is more elegant.
8099  */
8100 
8101 static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
8102 {
8103  int cmd;
8104  char *buf;
8105 
8106  buf = ast_alloca(strlen(box) + 2);
8107  strcpy(buf, box);
8108  strcat(buf, "s");
8109 
8110  if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
8111  cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
8112  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
8113  } else {
8114  cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
8115  return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
8116  }
8117 }
8118 
8119 static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
8120 {
8121  int cmd;
8122 
8123  if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
8124  if (!strcasecmp(box, "vm-INBOX"))
8125  cmd = ast_play_and_wait(chan, "vm-new-e");
8126  else
8127  cmd = ast_play_and_wait(chan, "vm-old-e");
8128  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
8129  } else {
8130  cmd = ast_play_and_wait(chan, "vm-messages");
8131  return cmd ? cmd : ast_play_and_wait(chan, box);
8132  }
8133 }
8134 
8135 static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
8136 {
8137  int cmd;
8138 
8139  if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
8140  cmd = ast_play_and_wait(chan, "vm-messages");
8141  return cmd ? cmd : ast_play_and_wait(chan, box);
8142  } else {
8143  cmd = ast_play_and_wait(chan, box);
8144  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
8145  }
8146 }
8147 
8148 static int vm_play_folder_name(struct ast_channel *chan, char *box)
8149 {
8150  int cmd;
8151 
8152  if ( !strncasecmp(chan->language, "it", 2) ||
8153  !strncasecmp(chan->language, "es", 2) ||
8154  !strncasecmp(chan->language, "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
8155  cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
8156  return cmd ? cmd : ast_play_and_wait(chan, box);
8157  } else if (!strncasecmp(chan->language, "gr", 2)) {
8158  return vm_play_folder_name_gr(chan, box);
8159  } else if (!strncasecmp(chan->language, "he", 2)) { /* Hebrew syntax */
8160  return ast_play_and_wait(chan, box);
8161  } else if (!strncasecmp(chan->language, "pl", 2)) {
8162  return vm_play_folder_name_pl(chan, box);
8163  } else if (!strncasecmp(chan->language, "ua", 2)) { /* Ukrainian syntax */
8164  return vm_play_folder_name_ua(chan, box);
8165  } else if (!strncasecmp(chan->language, "vi", 2)) {
8166  return ast_play_and_wait(chan, box);
8167  } else { /* Default English */
8168  cmd = ast_play_and_wait(chan, box);
8169  return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
8170  }
8171 }
8172 
8173 /* GREEK SYNTAX
8174  In greek the plural for old/new is
8175  different so we need the following files
8176  We also need vm-denExeteMynhmata because
8177  this syntax is different.
8178 
8179  -> vm-Olds.wav : "Palia"
8180  -> vm-INBOXs.wav : "Nea"
8181  -> vm-denExeteMynhmata : "den exete mynhmata"
8182 */
8183 
8184 
8185 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
8186 {
8187  int res = 0;
8188 
8189  if (vms->newmessages) {
8190  res = ast_play_and_wait(chan, "vm-youhave");
8191  if (!res)
8192  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
8193  if (!res) {
8194  if ((vms->newmessages == 1)) {
8195  res = ast_play_and_wait(chan, "vm-INBOX");
8196  if (!res)
8197  res = ast_play_and_wait(chan, "vm-message");
8198  } else {
8199  res = ast_play_and_wait(chan, "vm-INBOXs");
8200  if (!res)
8201  res = ast_play_and_wait(chan, "vm-messages");
8202  }
8203  }
8204  } else if (vms->oldmessages){
8205  res = ast_play_and_wait(chan, "vm-youhave");
8206  if (!res)
8207  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
8208  if ((vms->oldmessages == 1)){
8209  res = ast_play_and_wait(chan, "vm-Old");
8210  if (!res)
8211  res = ast_play_and_wait(chan, "vm-message");
8212  } else {
8213  res = ast_play_and_wait(chan, "vm-Olds");
8214  if (!res)
8215  res = ast_play_and_wait(chan, "vm-messages");
8216  }
8217  } else if (!vms->oldmessages && !vms->newmessages)
8218  res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
8219  return res;
8220 }
8221 
8222 /* Version of vm_intro() designed to work for many languages.
8223  *
8224  * It is hoped that this function can prevent the proliferation of
8225  * language-specific vm_intro() functions and in time replace the language-
8226  * specific functions which already exist. An examination of the language-
8227  * specific functions revealed that they all corrected the same deficiencies
8228  * in vm_intro_en() (which was the default function). Namely:
8229  *
8230  * 1) The vm-Old and vm-INBOX sound files were overloaded. The English
8231  * wording of the voicemail greeting hides this problem. For example,
8232  * vm-INBOX contains only the word "new". This means that both of these
8233  * sequences produce valid utterances:
8234  * * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
8235  * * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
8236  * However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
8237  * in many languages) the first utterance becomes "you have 1 the new message".
8238  * 2) The function contains hardcoded rules for pluralizing the word "message".
8239  * These rules are correct for English, but not for many other languages.
8240  * 3) No attempt is made to pluralize the adjectives ("old" and "new") as
8241  * required in many languages.
8242  * 4) The gender of the word for "message" is not specified. This is a problem
8243  * because in many languages the gender of the number in phrases such
8244  * as "you have one new message" must match the gender of the word
8245  * meaning "message".
8246  *
8247  * Fixing these problems for each new language has meant duplication of effort.
8248  * This new function solves the problems in the following general ways:
8249  * 1) Add new sound files vm-new and vm-old. These can be linked to vm-INBOX
8250  * and vm-Old respectively for those languages where it makes sense.
8251  * 2) Call ast_say_counted_noun() to put the proper gender and number prefix
8252  * on vm-message.
8253  * 3) Call ast_say_counted_adjective() to put the proper gender and number
8254  * prefix on vm-new and vm-old (none for English).
8255  * 4) Pass the gender of the language's word for "message" as an agument to
8256  * this function which is can in turn pass on to the functions which
8257  * say numbers and put endings on nounds and adjectives.
8258  *
8259  * All languages require these messages:
8260  * vm-youhave "You have..."
8261  * vm-and "and"
8262  * vm-no "no" (in the sense of "none", as in "you have no messages")
8263  *
8264  * To use it for English, you will need these additional sound files:
8265  * vm-new "new"
8266  * vm-message "message", singular
8267  * vm-messages "messages", plural
8268  *
8269  * If you use it for Russian and other slavic languages, you will need these additional sound files:
8270  *
8271  * vm-newn "novoye" (singular, neuter)
8272  * vm-newx "novikh" (counting plural form, genative plural)
8273  * vm-message "sobsheniye" (singular form)
8274  * vm-messagex1 "sobsheniya" (first counting plural form, genative singular)
8275  * vm-messagex2 "sobsheniy" (second counting plural form, genative plural)
8276  * digits/1n "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
8277  * digits/2n "dva" (neuter singular)
8278  */
8279 static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
8280 {
8281  int res;
8282  int lastnum = 0;
8283 
8284  res = ast_play_and_wait(chan, "vm-youhave");
8285 
8286  if (!res && vms->newmessages) {
8287  lastnum = vms->newmessages;
8288 
8289  if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
8290  res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
8291  }
8292 
8293  if (!res && vms->oldmessages) {
8294  res = ast_play_and_wait(chan, "vm-and");
8295  }
8296  }
8297 
8298  if (!res && vms->oldmessages) {
8299  lastnum = vms->oldmessages;
8300 
8301  if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, chan->language, message_gender))) {
8302  res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
8303  }
8304  }
8305 
8306  if (!res) {
8307  if (lastnum == 0) {
8308  res = ast_play_and_wait(chan, "vm-no");
8309  }
8310  if (!res) {
8311  res = ast_say_counted_noun(chan, lastnum, "vm-message");
8312  }
8313  }
8314 
8315  return res;
8316 }
8317 
8318 /* Default Hebrew syntax */
8319 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
8320 {
8321  int res = 0;
8322 
8323  /* Introduce messages they have */
8324  if (!res) {
8325  if ((vms->newmessages) || (vms->oldmessages)) {
8326  res = ast_play_and_wait(chan, "vm-youhave");
8327  }
8328  /*
8329  * The word "shtei" refers to the number 2 in hebrew when performing a count
8330  * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
8331  * an element, this is one of them.
8332  */
8333  if (vms->newmessages) {
8334  if (!res) {
8335  if (vms->newmessages == 1) {
8336  res = ast_play_and_wait(chan, "vm-INBOX1");
8337  } else {
8338  if (vms->newmessages == 2) {
8339  res = ast_play_and_wait(chan, "vm-shtei");
8340  } else {
8341  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
8342  }
8343  res = ast_play_and_wait(chan, "vm-INBOX");
8344  }
8345  }
8346  if (vms->oldmessages && !res) {
8347  res = ast_play_and_wait(chan, "vm-and");
8348  if (vms->oldmessages == 1) {
8349  res = ast_play_and_wait(chan, "vm-Old1");
8350  } else {
8351  if (vms->oldmessages == 2) {
8352  res = ast_play_and_wait(chan, "vm-shtei");
8353  } else {
8354  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
8355  }
8356  res = ast_play_and_wait(chan, "vm-Old");
8357  }
8358  }
8359  }
8360  if (!res && vms->oldmessages && !vms->newmessages) {
8361  if (!res) {
8362  if (vms->oldmessages == 1) {
8363  res = ast_play_and_wait(chan, "vm-Old1");
8364  } else {
8365  if (vms->oldmessages == 2) {
8366  res = ast_play_and_wait(chan, "vm-shtei");
8367  } else {
8368  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
8369  }
8370  res = ast_play_and_wait(chan, "vm-Old");
8371  }
8372  }
8373  }
8374  if (!res) {
8375  if (!vms->oldmessages && !vms->newmessages) {
8376  if (!res) {
8377  res = ast_play_and_wait(chan, "vm-nomessages");
8378  }
8379  }
8380  }
8381  }
8382  return res;
8383 }
8384 
8385 /* Default English syntax */
8386 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
8387 {
8388  int res;
8389 
8390  /* Introduce messages they have */
8391  res = ast_play_and_wait(chan, "vm-youhave");
8392  if (!res) {
8393  if (vms->urgentmessages) {
8394  res = say_and_wait(chan, vms->urgentmessages, chan->language);
8395  if (!res)
8396  res = ast_play_and_wait(chan, "vm-Urgent");
8397  if ((vms->oldmessages || vms->newmessages) && !res) {
8398  res = ast_play_and_wait(chan, "vm-and");
8399  } else if (!res) {
8400  if ((vms->urgentmessages == 1))
8401  res = ast_play_and_wait(chan, "vm-message");
8402  else
8403  res = ast_play_and_wait(chan, "vm-messages");
8404  }
8405  }
8406  if (vms->newmessages) {
8407  res = say_and_wait(chan, vms->newmessages, chan->language);
8408  if (!res)
8409  res = ast_play_and_wait(chan, "vm-INBOX");
8410  if (vms->oldmessages && !res)
8411  res = ast_play_and_wait(chan, "vm-and");
8412  else if (!res) {
8413  if ((vms->newmessages == 1))
8414  res = ast_play_and_wait(chan, "vm-message");
8415  else
8416  res = ast_play_and_wait(chan, "vm-messages");
8417  }
8418 
8419  }
8420  if (!res && vms->oldmessages) {
8421  res = say_and_wait(chan, vms->oldmessages, chan->language);
8422  if (!res)
8423  res = ast_play_and_wait(chan, "vm-Old");
8424  if (!res) {
8425  if (vms->oldmessages == 1)
8426  res = ast_play_and_wait(chan, "vm-message");
8427  else
8428  res = ast_play_and_wait(chan, "vm-messages");
8429  }
8430  }
8431  if (!res) {
8432  if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
8433  res = ast_play_and_wait(chan, "vm-no");
8434  if (!res)
8435  res = ast_play_and_wait(chan, "vm-messages");
8436  }
8437  }
8438  }
8439  return res;
8440 }
8441 
8442 /* ITALIAN syntax */
8443 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
8444 {
8445  /* Introduce messages they have */
8446  int res;
8447  if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
8448  res = ast_play_and_wait(chan, "vm-no") ||
8449  ast_play_and_wait(chan, "vm-message");
8450  else
8451  res = ast_play_and_wait(chan, "vm-youhave");
8452  if (!res && vms->newmessages) {
8453  res = (vms->newmessages == 1) ?
8454  ast_play_and_wait(chan, "digits/un") ||
8455  ast_play_and_wait(chan, "vm-nuovo") ||
8456  ast_play_and_wait(chan, "vm-message") :
8457  /* 2 or more new messages */
8458  say_and_wait(chan, vms->newmessages, chan->language) ||
8459  ast_play_and_wait(chan, "vm-nuovi") ||
8460  ast_play_and_wait(chan, "vm-messages");
8461  if (!res && vms->oldmessages)
8462  res = ast_play_and_wait(chan, "vm-and");
8463  }
8464  if (!res && vms->oldmessages) {
8465  res = (vms->oldmessages == 1) ?
8466  ast_play_and_wait(chan, "digits/un") ||
8467  ast_play_and_wait(chan, "vm-vecchio") ||
8468  ast_play_and_wait(chan, "vm-message") :
8469  /* 2 or more old messages */
8470  say_and_wait(chan, vms->oldmessages, chan->language) ||
8471  ast_play_and_wait(chan, "vm-vecchi") ||
8472  ast_play_and_wait(chan, "vm-messages");
8473  }
8474  return res;
8475 }
8476 
8477 /* POLISH syntax */
8478 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
8479 {
8480  /* Introduce messages they have */
8481  int res;
8482  div_t num;
8483 
8484  if (!vms->oldmessages && !vms->newmessages) {
8485  res = ast_play_and_wait(chan, "vm-no");
8486  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8487  return res;
8488  } else {
8489  res = ast_play_and_wait(chan, "vm-youhave");
8490  }
8491 
8492  if (vms->newmessages) {
8493  num = div(vms->newmessages, 10);
8494  if (vms->newmessages == 1) {
8495  res = ast_play_and_wait(chan, "digits/1-a");
8496  res = res ? res : ast_play_and_wait(chan, "vm-new-a");
8497  res = res ? res : ast_play_and_wait(chan, "vm-message");
8498  } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
8499  if (num.rem == 2) {
8500  if (!num.quot) {
8501  res = ast_play_and_wait(chan, "digits/2-ie");
8502  } else {
8503  res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
8504  res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
8505  }
8506  } else {
8507  res = say_and_wait(chan, vms->newmessages, chan->language);
8508  }
8509  res = res ? res : ast_play_and_wait(chan, "vm-new-e");
8510  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8511  } else {
8512  res = say_and_wait(chan, vms->newmessages, chan->language);
8513  res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
8514  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8515  }
8516  if (!res && vms->oldmessages)
8517  res = ast_play_and_wait(chan, "vm-and");
8518  }
8519  if (!res && vms->oldmessages) {
8520  num = div(vms->oldmessages, 10);
8521  if (vms->oldmessages == 1) {
8522  res = ast_play_and_wait(chan, "digits/1-a");
8523  res = res ? res : ast_play_and_wait(chan, "vm-old-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->oldmessages - 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->oldmessages, chan->language);
8535  }
8536  res = res ? res : ast_play_and_wait(chan, "vm-old-e");
8537  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8538  } else {
8539  res = say_and_wait(chan, vms->oldmessages, chan->language);
8540  res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
8541  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8542  }
8543  }
8544 
8545  return res;
8546 }
8547 
8548 /* SWEDISH syntax */
8549 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
8550 {
8551  /* Introduce messages they have */
8552  int res;
8553 
8554  res = ast_play_and_wait(chan, "vm-youhave");
8555  if (res)
8556  return res;
8557 
8558  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8559  res = ast_play_and_wait(chan, "vm-no");
8560  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8561  return res;
8562  }
8563 
8564  if (vms->newmessages) {
8565  if ((vms->newmessages == 1)) {
8566  res = ast_play_and_wait(chan, "digits/ett");
8567  res = res ? res : ast_play_and_wait(chan, "vm-nytt");
8568  res = res ? res : ast_play_and_wait(chan, "vm-message");
8569  } else {
8570  res = say_and_wait(chan, vms->newmessages, chan->language);
8571  res = res ? res : ast_play_and_wait(chan, "vm-nya");
8572  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8573  }
8574  if (!res && vms->oldmessages)
8575  res = ast_play_and_wait(chan, "vm-and");
8576  }
8577  if (!res && vms->oldmessages) {
8578  if (vms->oldmessages == 1) {
8579  res = ast_play_and_wait(chan, "digits/ett");
8580  res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
8581  res = res ? res : ast_play_and_wait(chan, "vm-message");
8582  } else {
8583  res = say_and_wait(chan, vms->oldmessages, chan->language);
8584  res = res ? res : ast_play_and_wait(chan, "vm-gamla");
8585  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8586  }
8587  }
8588 
8589  return res;
8590 }
8591 
8592 /* NORWEGIAN syntax */
8593 static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
8594 {
8595  /* Introduce messages they have */
8596  int res;
8597 
8598  res = ast_play_and_wait(chan, "vm-youhave");
8599  if (res)
8600  return res;
8601 
8602  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8603  res = ast_play_and_wait(chan, "vm-no");
8604  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8605  return res;
8606  }
8607 
8608  if (vms->newmessages) {
8609  if ((vms->newmessages == 1)) {
8610  res = ast_play_and_wait(chan, "digits/1");
8611  res = res ? res : ast_play_and_wait(chan, "vm-ny");
8612  res = res ? res : ast_play_and_wait(chan, "vm-message");
8613  } else {
8614  res = say_and_wait(chan, vms->newmessages, chan->language);
8615  res = res ? res : ast_play_and_wait(chan, "vm-nye");
8616  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8617  }
8618  if (!res && vms->oldmessages)
8619  res = ast_play_and_wait(chan, "vm-and");
8620  }
8621  if (!res && vms->oldmessages) {
8622  if (vms->oldmessages == 1) {
8623  res = ast_play_and_wait(chan, "digits/1");
8624  res = res ? res : ast_play_and_wait(chan, "vm-gamel");
8625  res = res ? res : ast_play_and_wait(chan, "vm-message");
8626  } else {
8627  res = say_and_wait(chan, vms->oldmessages, chan->language);
8628  res = res ? res : ast_play_and_wait(chan, "vm-gamle");
8629  res = res ? res : ast_play_and_wait(chan, "vm-messages");
8630  }
8631  }
8632 
8633  return res;
8634 }
8635 
8636 /* GERMAN syntax */
8637 static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
8638 {
8639  /* Introduce messages they have */
8640  int res;
8641  res = ast_play_and_wait(chan, "vm-youhave");
8642  if (!res) {
8643  if (vms->newmessages) {
8644  if ((vms->newmessages == 1))
8645  res = ast_play_and_wait(chan, "digits/1F");
8646  else
8647  res = say_and_wait(chan, vms->newmessages, chan->language);
8648  if (!res)
8649  res = ast_play_and_wait(chan, "vm-INBOX");
8650  if (vms->oldmessages && !res)
8651  res = ast_play_and_wait(chan, "vm-and");
8652  else if (!res) {
8653  if ((vms->newmessages == 1))
8654  res = ast_play_and_wait(chan, "vm-message");
8655  else
8656  res = ast_play_and_wait(chan, "vm-messages");
8657  }
8658 
8659  }
8660  if (!res && vms->oldmessages) {
8661  if (vms->oldmessages == 1)
8662  res = ast_play_and_wait(chan, "digits/1F");
8663  else
8664  res = say_and_wait(chan, vms->oldmessages, chan->language);
8665  if (!res)
8666  res = ast_play_and_wait(chan, "vm-Old");
8667  if (!res) {
8668  if (vms->oldmessages == 1)
8669  res = ast_play_and_wait(chan, "vm-message");
8670  else
8671  res = ast_play_and_wait(chan, "vm-messages");
8672  }
8673  }
8674  if (!res) {
8675  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8676  res = ast_play_and_wait(chan, "vm-no");
8677  if (!res)
8678  res = ast_play_and_wait(chan, "vm-messages");
8679  }
8680  }
8681  }
8682  return res;
8683 }
8684 
8685 /* SPANISH syntax */
8686 static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
8687 {
8688  /* Introduce messages they have */
8689  int res;
8690  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8691  res = ast_play_and_wait(chan, "vm-youhaveno");
8692  if (!res)
8693  res = ast_play_and_wait(chan, "vm-messages");
8694  } else {
8695  res = ast_play_and_wait(chan, "vm-youhave");
8696  }
8697  if (!res) {
8698  if (vms->newmessages) {
8699  if (!res) {
8700  if ((vms->newmessages == 1)) {
8701  res = ast_play_and_wait(chan, "digits/1");
8702  if (!res)
8703  res = ast_play_and_wait(chan, "vm-message");
8704  if (!res)
8705  res = ast_play_and_wait(chan, "vm-INBOXs");
8706  } else {
8707  res = say_and_wait(chan, vms->newmessages, chan->language);
8708  if (!res)
8709  res = ast_play_and_wait(chan, "vm-messages");
8710  if (!res)
8711  res = ast_play_and_wait(chan, "vm-INBOX");
8712  }
8713  }
8714  if (vms->oldmessages && !res)
8715  res = ast_play_and_wait(chan, "vm-and");
8716  }
8717  if (vms->oldmessages) {
8718  if (!res) {
8719  if (vms->oldmessages == 1) {
8720  res = ast_play_and_wait(chan, "digits/1");
8721  if (!res)
8722  res = ast_play_and_wait(chan, "vm-message");
8723  if (!res)
8724  res = ast_play_and_wait(chan, "vm-Olds");
8725  } else {
8726  res = say_and_wait(chan, vms->oldmessages, chan->language);
8727  if (!res)
8728  res = ast_play_and_wait(chan, "vm-messages");
8729  if (!res)
8730  res = ast_play_and_wait(chan, "vm-Old");
8731  }
8732  }
8733  }
8734  }
8735 return res;
8736 }
8737 
8738 /* BRAZILIAN PORTUGUESE syntax */
8739 static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
8740  /* Introduce messages they have */
8741  int res;
8742  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8743  res = ast_play_and_wait(chan, "vm-nomessages");
8744  return res;
8745  } else {
8746  res = ast_play_and_wait(chan, "vm-youhave");
8747  }
8748  if (vms->newmessages) {
8749  if (!res)
8750  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
8751  if ((vms->newmessages == 1)) {
8752  if (!res)
8753  res = ast_play_and_wait(chan, "vm-message");
8754  if (!res)
8755  res = ast_play_and_wait(chan, "vm-INBOXs");
8756  } else {
8757  if (!res)
8758  res = ast_play_and_wait(chan, "vm-messages");
8759  if (!res)
8760  res = ast_play_and_wait(chan, "vm-INBOX");
8761  }
8762  if (vms->oldmessages && !res)
8763  res = ast_play_and_wait(chan, "vm-and");
8764  }
8765  if (vms->oldmessages) {
8766  if (!res)
8767  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
8768  if (vms->oldmessages == 1) {
8769  if (!res)
8770  res = ast_play_and_wait(chan, "vm-message");
8771  if (!res)
8772  res = ast_play_and_wait(chan, "vm-Olds");
8773  } else {
8774  if (!res)
8775  res = ast_play_and_wait(chan, "vm-messages");
8776  if (!res)
8777  res = ast_play_and_wait(chan, "vm-Old");
8778  }
8779  }
8780  return res;
8781 }
8782 
8783 /* FRENCH syntax */
8784 static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
8785 {
8786  /* Introduce messages they have */
8787  int res;
8788  res = ast_play_and_wait(chan, "vm-youhave");
8789  if (!res) {
8790  if (vms->newmessages) {
8791  res = say_and_wait(chan, vms->newmessages, chan->language);
8792  if (!res)
8793  res = ast_play_and_wait(chan, "vm-INBOX");
8794  if (vms->oldmessages && !res)
8795  res = ast_play_and_wait(chan, "vm-and");
8796  else if (!res) {
8797  if ((vms->newmessages == 1))
8798  res = ast_play_and_wait(chan, "vm-message");
8799  else
8800  res = ast_play_and_wait(chan, "vm-messages");
8801  }
8802 
8803  }
8804  if (!res && vms->oldmessages) {
8805  res = say_and_wait(chan, vms->oldmessages, chan->language);
8806  if (!res)
8807  res = ast_play_and_wait(chan, "vm-Old");
8808  if (!res) {
8809  if (vms->oldmessages == 1)
8810  res = ast_play_and_wait(chan, "vm-message");
8811  else
8812  res = ast_play_and_wait(chan, "vm-messages");
8813  }
8814  }
8815  if (!res) {
8816  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8817  res = ast_play_and_wait(chan, "vm-no");
8818  if (!res)
8819  res = ast_play_and_wait(chan, "vm-messages");
8820  }
8821  }
8822  }
8823  return res;
8824 }
8825 
8826 /* DUTCH syntax */
8827 static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
8828 {
8829  /* Introduce messages they have */
8830  int res;
8831  res = ast_play_and_wait(chan, "vm-youhave");
8832  if (!res) {
8833  if (vms->newmessages) {
8834  res = say_and_wait(chan, vms->newmessages, chan->language);
8835  if (!res) {
8836  if (vms->newmessages == 1)
8837  res = ast_play_and_wait(chan, "vm-INBOXs");
8838  else
8839  res = ast_play_and_wait(chan, "vm-INBOX");
8840  }
8841  if (vms->oldmessages && !res)
8842  res = ast_play_and_wait(chan, "vm-and");
8843  else if (!res) {
8844  if ((vms->newmessages == 1))
8845  res = ast_play_and_wait(chan, "vm-message");
8846  else
8847  res = ast_play_and_wait(chan, "vm-messages");
8848  }
8849 
8850  }
8851  if (!res && vms->oldmessages) {
8852  res = say_and_wait(chan, vms->oldmessages, chan->language);
8853  if (!res) {
8854  if (vms->oldmessages == 1)
8855  res = ast_play_and_wait(chan, "vm-Olds");
8856  else
8857  res = ast_play_and_wait(chan, "vm-Old");
8858  }
8859  if (!res) {
8860  if (vms->oldmessages == 1)
8861  res = ast_play_and_wait(chan, "vm-message");
8862  else
8863  res = ast_play_and_wait(chan, "vm-messages");
8864  }
8865  }
8866  if (!res) {
8867  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8868  res = ast_play_and_wait(chan, "vm-no");
8869  if (!res)
8870  res = ast_play_and_wait(chan, "vm-messages");
8871  }
8872  }
8873  }
8874  return res;
8875 }
8876 
8877 /* PORTUGUESE syntax */
8878 static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
8879 {
8880  /* Introduce messages they have */
8881  int res;
8882  res = ast_play_and_wait(chan, "vm-youhave");
8883  if (!res) {
8884  if (vms->newmessages) {
8885  res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
8886  if (!res) {
8887  if ((vms->newmessages == 1)) {
8888  res = ast_play_and_wait(chan, "vm-message");
8889  if (!res)
8890  res = ast_play_and_wait(chan, "vm-INBOXs");
8891  } else {
8892  res = ast_play_and_wait(chan, "vm-messages");
8893  if (!res)
8894  res = ast_play_and_wait(chan, "vm-INBOX");
8895  }
8896  }
8897  if (vms->oldmessages && !res)
8898  res = ast_play_and_wait(chan, "vm-and");
8899  }
8900  if (!res && vms->oldmessages) {
8901  res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
8902  if (!res) {
8903  if (vms->oldmessages == 1) {
8904  res = ast_play_and_wait(chan, "vm-message");
8905  if (!res)
8906  res = ast_play_and_wait(chan, "vm-Olds");
8907  } else {
8908  res = ast_play_and_wait(chan, "vm-messages");
8909  if (!res)
8910  res = ast_play_and_wait(chan, "vm-Old");
8911  }
8912  }
8913  }
8914  if (!res) {
8915  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8916  res = ast_play_and_wait(chan, "vm-no");
8917  if (!res)
8918  res = ast_play_and_wait(chan, "vm-messages");
8919  }
8920  }
8921  }
8922  return res;
8923 }
8924 
8925 
8926 /* CZECH syntax */
8927 /* in czech there must be declension of word new and message
8928  * czech : english : czech : english
8929  * --------------------------------------------------------
8930  * vm-youhave : you have
8931  * vm-novou : one new : vm-zpravu : message
8932  * vm-nove : 2-4 new : vm-zpravy : messages
8933  * vm-novych : 5-infinite new : vm-zprav : messages
8934  * vm-starou : one old
8935  * vm-stare : 2-4 old
8936  * vm-starych : 5-infinite old
8937  * jednu : one - falling 4.
8938  * vm-no : no ( no messages )
8939  */
8940 
8941 static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
8942 {
8943  int res;
8944  res = ast_play_and_wait(chan, "vm-youhave");
8945  if (!res) {
8946  if (vms->newmessages) {
8947  if (vms->newmessages == 1) {
8948  res = ast_play_and_wait(chan, "digits/jednu");
8949  } else {
8950  res = say_and_wait(chan, vms->newmessages, chan->language);
8951  }
8952  if (!res) {
8953  if ((vms->newmessages == 1))
8954  res = ast_play_and_wait(chan, "vm-novou");
8955  if ((vms->newmessages) > 1 && (vms->newmessages < 5))
8956  res = ast_play_and_wait(chan, "vm-nove");
8957  if (vms->newmessages > 4)
8958  res = ast_play_and_wait(chan, "vm-novych");
8959  }
8960  if (vms->oldmessages && !res)
8961  res = ast_play_and_wait(chan, "vm-and");
8962  else if (!res) {
8963  if ((vms->newmessages == 1))
8964  res = ast_play_and_wait(chan, "vm-zpravu");
8965  if ((vms->newmessages) > 1 && (vms->newmessages < 5))
8966  res = ast_play_and_wait(chan, "vm-zpravy");
8967  if (vms->newmessages > 4)
8968  res = ast_play_and_wait(chan, "vm-zprav");
8969  }
8970  }
8971  if (!res && vms->oldmessages) {
8972  res = say_and_wait(chan, vms->oldmessages, chan->language);
8973  if (!res) {
8974  if ((vms->oldmessages == 1))
8975  res = ast_play_and_wait(chan, "vm-starou");
8976  if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
8977  res = ast_play_and_wait(chan, "vm-stare");
8978  if (vms->oldmessages > 4)
8979  res = ast_play_and_wait(chan, "vm-starych");
8980  }
8981  if (!res) {
8982  if ((vms->oldmessages == 1))
8983  res = ast_play_and_wait(chan, "vm-zpravu");
8984  if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
8985  res = ast_play_and_wait(chan, "vm-zpravy");
8986  if (vms->oldmessages > 4)
8987  res = ast_play_and_wait(chan, "vm-zprav");
8988  }
8989  }
8990  if (!res) {
8991  if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
8992  res = ast_play_and_wait(chan, "vm-no");
8993  if (!res)
8994  res = ast_play_and_wait(chan, "vm-zpravy");
8995  }
8996  }
8997  }
8998  return res;
8999 }
9000 
9001 /* CHINESE (Taiwan) syntax */
9002 static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
9003 {
9004  int res;
9005  /* Introduce messages they have */
9006  res = ast_play_and_wait(chan, "vm-you");
9007 
9008  if (!res && vms->newmessages) {
9009  res = ast_play_and_wait(chan, "vm-have");
9010  if (!res)
9011  res = say_and_wait(chan, vms->newmessages, chan->language);
9012  if (!res)
9013  res = ast_play_and_wait(chan, "vm-tong");
9014  if (!res)
9015  res = ast_play_and_wait(chan, "vm-INBOX");
9016  if (vms->oldmessages && !res)
9017  res = ast_play_and_wait(chan, "vm-and");
9018  else if (!res)
9019  res = ast_play_and_wait(chan, "vm-messages");
9020  }
9021  if (!res && vms->oldmessages) {
9022  res = ast_play_and_wait(chan, "vm-have");
9023  if (!res)
9024  res = say_and_wait(chan, vms->oldmessages, chan->language);
9025  if (!res)
9026  res = ast_play_and_wait(chan, "vm-tong");
9027  if (!res)
9028  res = ast_play_and_wait(chan, "vm-Old");
9029  if (!res)
9030  res = ast_play_and_wait(chan, "vm-messages");
9031  }
9032  if (!res && !vms->oldmessages && !vms->newmessages) {
9033  res = ast_play_and_wait(chan, "vm-haveno");
9034  if (!res)
9035  res = ast_play_and_wait(chan, "vm-messages");
9036  }
9037  return res;
9038 }
9039 
9040 /* Vietnamese syntax */
9041 static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
9042 {
9043  int res;
9044 
9045  /* Introduce messages they have */
9046  res = ast_play_and_wait(chan, "vm-youhave");
9047  if (!res) {
9048  if (vms->newmessages) {
9049  res = say_and_wait(chan, vms->newmessages, chan->language);
9050  if (!res)
9051  res = ast_play_and_wait(chan, "vm-INBOX");
9052  if (vms->oldmessages && !res)
9053  res = ast_play_and_wait(chan, "vm-and");
9054  }
9055  if (!res && vms->oldmessages) {
9056  res = say_and_wait(chan, vms->oldmessages, chan->language);
9057  if (!res)
9058  res = ast_play_and_wait(chan, "vm-Old");
9059  }
9060  if (!res) {
9061  if (!vms->oldmessages && !vms->newmessages) {
9062  res = ast_play_and_wait(chan, "vm-no");
9063  if (!res)
9064  res = ast_play_and_wait(chan, "vm-message");
9065  }
9066  }
9067  }
9068  return res;
9069 }
9070 
9071 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
9072 {
9073  char prefile[256];
9074 
9075  /* Notify the user that the temp greeting is set and give them the option to remove it */
9076  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
9077  if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
9078  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
9079  if (ast_fileexists(prefile, NULL, NULL) > 0) {
9080  ast_play_and_wait(chan, "vm-tempgreetactive");
9081  }
9082  DISPOSE(prefile, -1);
9083  }
9084 
9085  /* Play voicemail intro - syntax is different for different languages */
9086  if (0) {
9087  return 0;
9088  } else if (!strncasecmp(chan->language, "cs", 2)) { /* CZECH syntax */
9089  return vm_intro_cs(chan, vms);
9090  } else if (!strncasecmp(chan->language, "cz", 2)) { /* deprecated CZECH syntax */
9091  static int deprecation_warning = 0;
9092  if (deprecation_warning++ % 10 == 0) {
9093  ast_log(LOG_WARNING, "cz is not a standard language code. Please switch to using cs instead.\n");
9094  }
9095  return vm_intro_cs(chan, vms);
9096  } else if (!strncasecmp(chan->language, "de", 2)) { /* GERMAN syntax */
9097  return vm_intro_de(chan, vms);
9098  } else if (!strncasecmp(chan->language, "es", 2)) { /* SPANISH syntax */
9099  return vm_intro_es(chan, vms);
9100  } else if (!strncasecmp(chan->language, "fr", 2)) { /* FRENCH syntax */
9101  return vm_intro_fr(chan, vms);
9102  } else if (!strncasecmp(chan->language, "gr", 2)) { /* GREEK syntax */
9103  return vm_intro_gr(chan, vms);
9104  } else if (!strncasecmp(chan->language, "he", 2)) { /* HEBREW syntax */
9105  return vm_intro_he(chan, vms);
9106  } else if (!strncasecmp(chan->language, "it", 2)) { /* ITALIAN syntax */
9107  return vm_intro_it(chan, vms);
9108  } else if (!strncasecmp(chan->language, "nl", 2)) { /* DUTCH syntax */
9109  return vm_intro_nl(chan, vms);
9110  } else if (!strncasecmp(chan->language, "no", 2)) { /* NORWEGIAN syntax */
9111  return vm_intro_no(chan, vms);
9112  } else if (!strncasecmp(chan->language, "pl", 2)) { /* POLISH syntax */
9113  return vm_intro_pl(chan, vms);
9114  } else if (!strncasecmp(chan->language, "pt_BR", 5)) { /* BRAZILIAN PORTUGUESE syntax */
9115  return vm_intro_pt_BR(chan, vms);
9116  } else if (!strncasecmp(chan->language, "pt", 2)) { /* PORTUGUESE syntax */
9117  return vm_intro_pt(chan, vms);
9118  } else if (!strncasecmp(chan->language, "ru", 2)) { /* RUSSIAN syntax */
9119  return vm_intro_multilang(chan, vms, "n");
9120  } else if (!strncasecmp(chan->language, "se", 2)) { /* SWEDISH syntax */
9121  return vm_intro_se(chan, vms);
9122  } else if (!strncasecmp(chan->language, "ua", 2)) { /* UKRAINIAN syntax */
9123  return vm_intro_multilang(chan, vms, "n");
9124  } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE syntax */
9125  return vm_intro_vi(chan, vms);
9126  } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
9127  return vm_intro_zh(chan, vms);
9128  } else { /* Default to ENGLISH */
9129  return vm_intro_en(chan, vms);
9130  }
9131 }
9132 
9133 static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
9134 {
9135  int res = 0;
9136  /* Play instructions and wait for new command */
9137  while (!res) {
9138  if (vms->starting) {
9139  if (vms->lastmsg > -1) {
9140  if (skipadvanced)
9141  res = ast_play_and_wait(chan, "vm-onefor-full");
9142  else
9143  res = ast_play_and_wait(chan, "vm-onefor");
9144  if (!res)
9145  res = vm_play_folder_name(chan, vms->vmbox);
9146  }
9147  if (!res) {
9148  if (skipadvanced)
9149  res = ast_play_and_wait(chan, "vm-opts-full");
9150  else
9151  res = ast_play_and_wait(chan, "vm-opts");
9152  }
9153  } else {
9154  /* Added for additional help */
9155  if (skipadvanced) {
9156  res = ast_play_and_wait(chan, "vm-onefor-full");
9157  if (!res)
9158  res = vm_play_folder_name(chan, vms->vmbox);
9159  res = ast_play_and_wait(chan, "vm-opts-full");
9160  }
9161  /* Logic:
9162  * If the current message is not the first OR
9163  * if we're listening to the first new message and there are
9164  * also urgent messages, then prompt for navigation to the
9165  * previous message
9166  */
9167  if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
9168  res = ast_play_and_wait(chan, "vm-prev");
9169  }
9170  if (!res && !skipadvanced)
9171  res = ast_play_and_wait(chan, "vm-advopts");
9172  if (!res)
9173  res = ast_play_and_wait(chan, "vm-repeat");
9174  /* Logic:
9175  * If we're not listening to the last message OR
9176  * we're listening to the last urgent message and there are
9177  * also new non-urgent messages, then prompt for navigation
9178  * to the next message
9179  */
9180  if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
9181  (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
9182  res = ast_play_and_wait(chan, "vm-next");
9183  }
9184  if (!res) {
9185  int curmsg_deleted;
9186 #ifdef IMAP_STORAGE
9187  ast_mutex_lock(&vms->lock);
9188 #endif
9189  curmsg_deleted = vms->deleted[vms->curmsg];
9190 #ifdef IMAP_STORAGE
9191  ast_mutex_unlock(&vms->lock);
9192 #endif
9193  if (!curmsg_deleted) {
9194  res = ast_play_and_wait(chan, "vm-delete");
9195  } else {
9196  res = ast_play_and_wait(chan, "vm-undelete");
9197  }
9198  if (!res) {
9199  res = ast_play_and_wait(chan, "vm-toforward");
9200  }
9201  if (!res) {
9202  res = ast_play_and_wait(chan, "vm-savemessage");
9203  }
9204  }
9205  }
9206  if (!res) {
9207  res = ast_play_and_wait(chan, "vm-helpexit");
9208  }
9209  if (!res)
9210  res = ast_waitfordigit(chan, 6000);
9211  if (!res) {
9212  vms->repeats++;
9213  if (vms->repeats > 2) {
9214  res = 't';
9215  }
9216  }
9217  }
9218  return res;
9219 }
9220 
9221 static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
9222 {
9223  int res = 0;
9224  /* Play instructions and wait for new command */
9225  while (!res) {
9226  if (vms->lastmsg > -1) {
9227  res = ast_play_and_wait(chan, "vm-listen");
9228  if (!res)
9229  res = vm_play_folder_name(chan, vms->vmbox);
9230  if (!res)
9231  res = ast_play_and_wait(chan, "press");
9232  if (!res)
9233  res = ast_play_and_wait(chan, "digits/1");
9234  }
9235  if (!res)
9236  res = ast_play_and_wait(chan, "vm-opts");
9237  if (!res) {
9238  vms->starting = 0;
9239  return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
9240  }
9241  }
9242  return res;
9243 }
9244 
9245 static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
9246 {
9247  if (vms->starting && !strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) syntax */
9248  return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent);
9249  } else { /* Default to ENGLISH */
9250  return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent);
9251  }
9252 }
9253 
9254 
9255 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
9256 {
9257  int cmd = 0;
9258  int duration = 0;
9259  int tries = 0;
9260  char newpassword[80] = "";
9261  char newpassword2[80] = "";
9262  char prefile[PATH_MAX] = "";
9263  unsigned char buf[256];
9264  int bytes = 0;
9265 
9266  ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
9267  if (ast_adsi_available(chan)) {
9268  bytes += adsi_logo(buf + bytes);
9269  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
9270  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
9271  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
9272  bytes += ast_adsi_voice_mode(buf + bytes, 0);
9273  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
9274  }
9275 
9276  /* If forcename is set, have the user record their name */
9277  if (ast_test_flag(vmu, VM_FORCENAME)) {
9278  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
9279  if (ast_fileexists(prefile, NULL, NULL) < 1) {
9280  cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9281  if (cmd < 0 || cmd == 't' || cmd == '#')
9282  return cmd;
9283  }
9284  }
9285 
9286  /* If forcegreetings is set, have the user record their greetings */
9287  if (ast_test_flag(vmu, VM_FORCEGREET)) {
9288  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
9289  if (ast_fileexists(prefile, NULL, NULL) < 1) {
9290  cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9291  if (cmd < 0 || cmd == 't' || cmd == '#')
9292  return cmd;
9293  }
9294 
9295  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
9296  if (ast_fileexists(prefile, NULL, NULL) < 1) {
9297  cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9298  if (cmd < 0 || cmd == 't' || cmd == '#')
9299  return cmd;
9300  }
9301  }
9302 
9303  /*
9304  * Change the password last since new users will be able to skip over any steps this one comes before
9305  * by hanging up and calling back to voicemail main since the password is used to verify new user status.
9306  */
9307  for (;;) {
9308  newpassword[1] = '\0';
9309  newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
9310  if (cmd == '#')
9311  newpassword[0] = '\0';
9312  if (cmd < 0 || cmd == 't' || cmd == '#')
9313  return cmd;
9314  cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
9315  if (cmd < 0 || cmd == 't' || cmd == '#')
9316  return cmd;
9317  cmd = check_password(vmu, newpassword); /* perform password validation */
9318  if (cmd != 0) {
9319  ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
9320  cmd = ast_play_and_wait(chan, vm_invalid_password);
9321  } else {
9322  newpassword2[1] = '\0';
9323  newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
9324  if (cmd == '#')
9325  newpassword2[0] = '\0';
9326  if (cmd < 0 || cmd == 't' || cmd == '#')
9327  return cmd;
9328  cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
9329  if (cmd < 0 || cmd == 't' || cmd == '#')
9330  return cmd;
9331  if (!strcmp(newpassword, newpassword2))
9332  break;
9333  ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
9334  cmd = ast_play_and_wait(chan, vm_mismatch);
9335  }
9336  if (++tries == 3)
9337  return -1;
9338  if (cmd != 0) {
9339  cmd = ast_play_and_wait(chan, vm_pls_try_again);
9340  }
9341  }
9342  if (pwdchange & PWDCHANGE_INTERNAL)
9343  vm_change_password(vmu, newpassword);
9344  if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd))
9345  vm_change_password_shell(vmu, newpassword);
9346 
9347  ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
9348  cmd = ast_play_and_wait(chan, vm_passchanged);
9349 
9350  return cmd;
9351 }
9352 
9353 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
9354 {
9355  int cmd = 0;
9356  int retries = 0;
9357  int duration = 0;
9358  char newpassword[80] = "";
9359  char newpassword2[80] = "";
9360  char prefile[PATH_MAX] = "";
9361  unsigned char buf[256];
9362  int bytes = 0;
9363 
9364  ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
9365  if (ast_adsi_available(chan)) {
9366  bytes += adsi_logo(buf + bytes);
9367  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
9368  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
9369  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
9370  bytes += ast_adsi_voice_mode(buf + bytes, 0);
9371  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
9372  }
9373  while ((cmd >= 0) && (cmd != 't')) {
9374  if (cmd)
9375  retries = 0;
9376  switch (cmd) {
9377  case '1': /* Record your unavailable message */
9378  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
9379  cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9380  break;
9381  case '2': /* Record your busy message */
9382  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
9383  cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9384  break;
9385  case '3': /* Record greeting */
9386  snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
9387  cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9388  break;
9389  case '4': /* manage the temporary greeting */
9390  cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
9391  break;
9392  case '5': /* change password */
9393  if (vmu->password[0] == '-') {
9394  cmd = ast_play_and_wait(chan, "vm-no");
9395  break;
9396  }
9397  newpassword[1] = '\0';
9398  newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
9399  if (cmd == '#')
9400  newpassword[0] = '\0';
9401  else {
9402  if (cmd < 0)
9403  break;
9404  if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
9405  break;
9406  }
9407  }
9408  cmd = check_password(vmu, newpassword); /* perform password validation */
9409  if (cmd != 0) {
9410  ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
9411  cmd = ast_play_and_wait(chan, vm_invalid_password);
9412  if (!cmd) {
9413  cmd = ast_play_and_wait(chan, vm_pls_try_again);
9414  }
9415  break;
9416  }
9417  newpassword2[1] = '\0';
9418  newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
9419  if (cmd == '#')
9420  newpassword2[0] = '\0';
9421  else {
9422  if (cmd < 0)
9423  break;
9424 
9425  if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
9426  break;
9427  }
9428  }
9429  if (strcmp(newpassword, newpassword2)) {
9430  ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
9431  cmd = ast_play_and_wait(chan, vm_mismatch);
9432  if (!cmd) {
9433  cmd = ast_play_and_wait(chan, vm_pls_try_again);
9434  }
9435  break;
9436  }
9437 
9438  if (pwdchange & PWDCHANGE_INTERNAL) {
9439  vm_change_password(vmu, newpassword);
9440  }
9441  if ((pwdchange & PWDCHANGE_EXTERNAL) && !ast_strlen_zero(ext_pass_cmd)) {
9442  vm_change_password_shell(vmu, newpassword);
9443  }
9444 
9445  ast_debug(1, "User %s set password to %s of length %d\n",
9446  vms->username, newpassword, (int) strlen(newpassword));
9447  cmd = ast_play_and_wait(chan, vm_passchanged);
9448  break;
9449  case '*':
9450  cmd = 't';
9451  break;
9452  default:
9453  cmd = 0;
9454  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
9455  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
9456  if (ast_fileexists(prefile, NULL, NULL)) {
9457  cmd = ast_play_and_wait(chan, "vm-tmpexists");
9458  }
9459  DISPOSE(prefile, -1);
9460  if (!cmd) {
9461  cmd = ast_play_and_wait(chan, "vm-options");
9462  }
9463  if (!cmd) {
9464  cmd = ast_waitfordigit(chan, 6000);
9465  }
9466  if (!cmd) {
9467  retries++;
9468  }
9469  if (retries > 3) {
9470  cmd = 't';
9471  }
9472  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
9473  }
9474  }
9475  if (cmd == 't')
9476  cmd = 0;
9477  return cmd;
9478 }
9479 
9480 /*!
9481  * \brief The handler for 'record a temporary greeting'.
9482  * \param chan
9483  * \param vmu
9484  * \param vms
9485  * \param fmtc
9486  * \param record_gain
9487  *
9488  * This is option 4 from the mailbox options menu.
9489  * This function manages the following promptings:
9490  * 1: play / record / review the temporary greeting. : invokes play_record_review().
9491  * 2: remove (delete) the temporary greeting.
9492  * *: return to the main menu.
9493  *
9494  * \return zero on success, -1 on error.
9495  */
9496 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
9497 {
9498  int cmd = 0;
9499  int retries = 0;
9500  int duration = 0;
9501  char prefile[PATH_MAX] = "";
9502  unsigned char buf[256];
9503  int bytes = 0;
9504 
9505  if (ast_adsi_available(chan)) {
9506  bytes += adsi_logo(buf + bytes);
9507  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
9508  bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
9509  bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
9510  bytes += ast_adsi_voice_mode(buf + bytes, 0);
9511  ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
9512  }
9513 
9514  ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
9515  snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
9516  while ((cmd >= 0) && (cmd != 't')) {
9517  if (cmd)
9518  retries = 0;
9519  RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
9520  if (ast_fileexists(prefile, NULL, NULL) <= 0) {
9521  cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9522  if (cmd == -1) {
9523  break;
9524  }
9525  cmd = 't';
9526  } else {
9527  switch (cmd) {
9528  case '1':
9529  cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL);
9530  break;
9531  case '2':
9532  DELETE(prefile, -1, prefile, vmu);
9533  ast_play_and_wait(chan, "vm-tempremoved");
9534  cmd = 't';
9535  break;
9536  case '*':
9537  cmd = 't';
9538  break;
9539  default:
9540  cmd = ast_play_and_wait(chan,
9541  ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
9542  "vm-tempgreeting2" : "vm-tempgreeting");
9543  if (!cmd) {
9544  cmd = ast_waitfordigit(chan, 6000);
9545  }
9546  if (!cmd) {
9547  retries++;
9548  }
9549  if (retries > 3) {
9550  cmd = 't';
9551  }
9552  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
9553  }
9554  }
9555  DISPOSE(prefile, -1);
9556  }
9557  if (cmd == 't')
9558  cmd = 0;
9559  return cmd;
9560 }
9561 
9562 
9563 /* Hebrew Syntax */
9564 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9565 {
9566  int cmd = 0;
9567 
9568  if (vms->lastmsg > -1) {
9569  cmd = play_message(chan, vmu, vms);
9570  } else {
9571  if (!strcasecmp(vms->fn, "INBOX")) {
9572  cmd = ast_play_and_wait(chan, "vm-nonewmessages");
9573  } else {
9574  cmd = ast_play_and_wait(chan, "vm-nomessages");
9575  }
9576  }
9577  return cmd;
9578 }
9579 
9580 /*!
9581  * \brief Default English syntax for 'You have N messages' greeting.
9582  * \param chan
9583  * \param vms
9584  * \param vmu
9585  *
9586  * \return zero on success, -1 on error.
9587  */
9588 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9589 {
9590  int cmd = 0;
9591 
9592  if (vms->lastmsg > -1) {
9593  cmd = play_message(chan, vmu, vms);
9594  } else {
9595  cmd = ast_play_and_wait(chan, "vm-youhave");
9596  if (!cmd)
9597  cmd = ast_play_and_wait(chan, "vm-no");
9598  if (!cmd) {
9599  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
9600  cmd = ast_play_and_wait(chan, vms->fn);
9601  }
9602  if (!cmd)
9603  cmd = ast_play_and_wait(chan, "vm-messages");
9604  }
9605  return cmd;
9606 }
9607 
9608 
9609 /*!
9610  * \brief Common LATIN languages syntax for 'You have N messages' greeting.
9611  * \param chan
9612  * \param vms
9613  * \param vmu
9614  *
9615  * \return zero on success, -1 on error.
9616  */
9617 static int vm_browse_messages_latin(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9618 {
9619  int cmd;
9620 
9621  if (vms->lastmsg > -1) {
9622  cmd = play_message(chan, vmu, vms);
9623  } else {
9624  cmd = ast_play_and_wait(chan, "vm-youhaveno");
9625  if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
9626  if (!cmd) {
9627  snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
9628  cmd = ast_play_and_wait(chan, vms->fn);
9629  }
9630  if (!cmd)
9631  cmd = ast_play_and_wait(chan, "vm-messages");
9632  } else {
9633  if (!cmd)
9634  cmd = ast_play_and_wait(chan, "vm-messages");
9635  if (!cmd) {
9636  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
9637  cmd = ast_play_and_wait(chan, vms->fn);
9638  }
9639  }
9640  }
9641  return cmd;
9642 }
9643 
9644 /*!
9645  * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
9646  * \param chan
9647  * \param vms
9648  * \param vmu
9649  *
9650  * \return zero on success, -1 on error.
9651  */
9652 static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9653 {
9654  int cmd;
9655 
9656  if (vms->lastmsg > -1) {
9657  cmd = play_message(chan, vmu, vms);
9658  } else {
9659  cmd = ast_play_and_wait(chan, "vm-you");
9660  if (!cmd)
9661  cmd = ast_play_and_wait(chan, "vm-haveno");
9662  if (!cmd)
9663  cmd = ast_play_and_wait(chan, "vm-messages");
9664  if (!cmd) {
9665  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
9666  cmd = ast_play_and_wait(chan, vms->fn);
9667  }
9668  }
9669  return cmd;
9670 }
9671 
9672 /*!
9673  * \brief Vietnamese syntax for 'You have N messages' greeting.
9674  * \param chan
9675  * \param vms
9676  * \param vmu
9677  *
9678  * \return zero on success, -1 on error.
9679  */
9680 static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9681 {
9682  int cmd = 0;
9683 
9684  if (vms->lastmsg > -1) {
9685  cmd = play_message(chan, vmu, vms);
9686  } else {
9687  cmd = ast_play_and_wait(chan, "vm-no");
9688  if (!cmd) {
9689  snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
9690  cmd = ast_play_and_wait(chan, vms->fn);
9691  }
9692  }
9693  return cmd;
9694 }
9695 
9696 /*!
9697  * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
9698  * \param chan The channel for the current user. We read the language property from this.
9699  * \param vms passed into the language-specific vm_browse_messages function.
9700  * \param vmu passed into the language-specific vm_browse_messages function.
9701  *
9702  * The method to be invoked is determined by the value of language code property in the user's channel.
9703  * The default (when unable to match) is to use english.
9704  *
9705  * \return zero on success, -1 on error.
9706  */
9707 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
9708 {
9709  if (!strncasecmp(chan->language, "es", 2) ||
9710  !strncasecmp(chan->language, "it", 2) ||
9711  !strncasecmp(chan->language, "pt", 2) ||
9712  !strncasecmp(chan->language, "gr", 2)) { /* SPANISH, ITALIAN, PORTUGUESE or GREEK */
9713  return vm_browse_messages_latin(chan, vms, vmu);
9714  } else if (!strncasecmp(chan->language, "he", 2)) { /* HEBREW */
9715  return vm_browse_messages_he(chan, vms, vmu);
9716  } else if (!strncasecmp(chan->language, "vi", 2)) { /* VIETNAMESE */
9717  return vm_browse_messages_vi(chan, vms, vmu);
9718  } else if (!strncasecmp(chan->language, "zh", 2)) { /* CHINESE (Taiwan) */
9719  return vm_browse_messages_zh(chan, vms, vmu);
9720  } else { /* Default to English syntax */
9721  return vm_browse_messages_en(chan, vms, vmu);
9722  }
9723 }
9724 
9725 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
9726  struct ast_vm_user *res_vmu, const char *context, const char *prefix,
9727  int skipuser, int max_logins, int silent)
9728 {
9729  int useadsi = 0, valid = 0, logretries = 0;
9730  char password[AST_MAX_EXTENSION]="", *passptr;
9731  struct ast_vm_user vmus, *vmu = NULL;
9732 
9733  /* If ADSI is supported, setup login screen */
9734  adsi_begin(chan, &useadsi);
9735  if (!skipuser && useadsi)
9736  adsi_login(chan);
9737  if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
9738  ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
9739  return -1;
9740  }
9741 
9742  /* Authenticate them and get their mailbox/password */
9743 
9744  while (!valid && (logretries < max_logins)) {
9745  /* Prompt for, and read in the username */
9746  if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
9747  ast_log(AST_LOG_WARNING, "Couldn't read username\n");
9748  return -1;
9749  }
9750  if (ast_strlen_zero(mailbox)) {
9751  if (chan->caller.id.number.valid && chan->caller.id.number.str) {
9752  ast_copy_string(mailbox, chan->caller.id.number.str, mailbox_size);
9753  } else {
9754  ast_verb(3, "Username not entered\n");
9755  return -1;
9756  }
9757  } else if (mailbox[0] == '*') {
9758  /* user entered '*' */
9759  ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
9760  if (ast_exists_extension(chan, chan->context, "a", 1,
9761  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
9762  return -1;
9763  }
9764  ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
9765  mailbox[0] = '\0';
9766  }
9767 
9768  if (useadsi)
9769  adsi_password(chan);
9770 
9771  if (!ast_strlen_zero(prefix)) {
9772  char fullusername[80] = "";
9773  ast_copy_string(fullusername, prefix, sizeof(fullusername));
9774  strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
9775  ast_copy_string(mailbox, fullusername, mailbox_size);
9776  }
9777 
9778  ast_debug(1, "Before find user for mailbox %s\n", mailbox);
9779  vmu = find_user(&vmus, context, mailbox);
9780  if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
9781  /* saved password is blank, so don't bother asking */
9782  password[0] = '\0';
9783  } else {
9784  if (ast_streamfile(chan, vm_password, chan->language)) {
9785  ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
9786  return -1;
9787  }
9788  if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
9789  ast_log(AST_LOG_WARNING, "Unable to read password\n");
9790  return -1;
9791  } else if (password[0] == '*') {
9792  /* user entered '*' */
9793  ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
9794  if (ast_exists_extension(chan, chan->context, "a", 1,
9795  S_COR(chan->caller.id.number.valid, chan->caller.id.number.str, NULL))) {
9796  mailbox[0] = '*';
9797  return -1;
9798  }
9799  ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
9800  mailbox[0] = '\0';
9801  /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
9802  vmu = NULL;
9803  }
9804  }
9805 
9806  if (vmu) {
9807  passptr = vmu->password;
9808  if (passptr[0] == '-') passptr++;
9809  }
9810  if (vmu && !strcmp(passptr, password))
9811  valid++;
9812  else {
9813  ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
9814  if (!ast_strlen_zero(prefix))
9815  mailbox[0] = '\0';
9816  }
9817  logretries++;
9818  if (!valid) {
9819  if (skipuser || logretries >= max_logins) {
9820  if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
9821  ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
9822  return -1;
9823  }
9824  } else {
9825  if (useadsi)
9826  adsi_login(chan);
9827  if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
9828  ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
9829  return -1;
9830  }
9831  }
9832  if (ast_waitstream(chan, "")) /* Channel is hung up */
9833  return -1;
9834  }
9835  }
9836  if (!valid && (logretries >= max_logins)) {
9837  ast_stopstream(chan);
9838  ast_play_and_wait(chan, "vm-goodbye");
9839  return -1;
9840  }
9841  if (vmu && !skipuser) {
9842  memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
9843  }
9844  return 0;
9845 }
9846 
9847 static int vm_execmain(struct ast_channel *chan, const char *data)
9848 {
9849  /* XXX This is, admittedly, some pretty horrendous code. For some
9850  reason it just seemed a lot easier to do with GOTO's. I feel
9851  like I'm back in my GWBASIC days. XXX */
9852  int res = -1;
9853  int cmd = 0;
9854  int valid = 0;
9855  char prefixstr[80] ="";
9856  char ext_context[256]="";
9857  int box;
9858  int useadsi = 0;
9859  int skipuser = 0;
9860  struct vm_state vms;
9861  struct ast_vm_user *vmu = NULL, vmus;
9862  char *context = NULL;
9863  int silentexit = 0;
9864  struct ast_flags flags = { 0 };
9865  signed char record_gain = 0;
9866  int play_auto = 0;
9867  int play_folder = 0;
9868  int in_urgent = 0;
9869 #ifdef IMAP_STORAGE
9870  int deleted = 0;
9871 #endif
9872 
9873  /* Add the vm_state to the active list and keep it active */
9874  memset(&vms, 0, sizeof(vms));
9875 
9876  vms.lastmsg = -1;
9877 
9878  memset(&vmus, 0, sizeof(vmus));
9879 
9880  ast_test_suite_event_notify("START", "Message: vm_execmain started");
9881  if (chan->_state != AST_STATE_UP) {
9882  ast_debug(1, "Before ast_answer\n");
9883  ast_answer(chan);
9884  }
9885 
9886  if (!ast_strlen_zero(data)) {
9887  char *opts[OPT_ARG_ARRAY_SIZE];
9888  char *parse;
9890  AST_APP_ARG(argv0);
9891  AST_APP_ARG(argv1);
9892  );
9893 
9894  parse = ast_strdupa(data);
9895 
9896  AST_STANDARD_APP_ARGS(args, parse);
9897 
9898  if (args.argc == 2) {
9899  if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
9900  return -1;
9901  if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
9902  int gain;
9903  if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
9904  if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
9905  ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
9906  return -1;
9907  } else {
9908  record_gain = (signed char) gain;
9909  }
9910  } else {
9911  ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
9912  }
9913  }
9914  if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
9915  play_auto = 1;
9916  if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
9917  /* See if it is a folder name first */
9918  if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
9919  if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
9920  play_folder = -1;
9921  }
9922  } else {
9923  play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
9924  }
9925  } else {
9926  ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
9927  }
9928  if (play_folder > 9 || play_folder < 0) {
9930  "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
9931  opts[OPT_ARG_PLAYFOLDER]);
9932  play_folder = 0;
9933  }
9934  }
9935  } else {
9936  /* old style options parsing */
9937  while (*(args.argv0)) {
9938  if (*(args.argv0) == 's')
9939  ast_set_flag(&flags, OPT_SILENT);
9940  else if (*(args.argv0) == 'p')
9942  else
9943  break;
9944  (args.argv0)++;
9945  }
9946 
9947  }
9948 
9949  valid = ast_test_flag(&flags, OPT_SILENT);
9950 
9951  if ((context = strchr(args.argv0, '@')))
9952  *context++ = '\0';
9953 
9954  if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
9955  ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
9956  else
9957  ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
9958 
9959  if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
9960  skipuser++;
9961  else
9962  valid = 0;
9963  }
9964 
9965  if (!valid)
9966  res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
9967 
9968  ast_debug(1, "After vm_authenticate\n");
9969 
9970  if (vms.username[0] == '*') {
9971  ast_debug(1, "user pressed * in context '%s'\n", chan->context);
9972 
9973  /* user entered '*' */
9974  if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
9975  ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
9976  res = 0; /* prevent hangup */
9977  goto out;
9978  }
9979  }
9980 
9981  if (!res) {
9982  valid = 1;
9983  if (!skipuser)
9984  vmu = &vmus;
9985  } else {
9986  res = 0;
9987  }
9988 
9989  /* If ADSI is supported, setup login screen */
9990  adsi_begin(chan, &useadsi);
9991 
9992  ast_test_suite_assert(valid);
9993  if (!valid) {
9994  goto out;
9995  }
9996  ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
9997 
9998 #ifdef IMAP_STORAGE
9999  pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
10000  pthread_setspecific(ts_vmstate.key, &vms);
10001 
10002  vms.interactive = 1;
10003  vms.updated = 1;
10004  if (vmu)
10005  ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
10006  vmstate_insert(&vms);
10007  init_vm_state(&vms);
10008 #endif
10009 
10010  /* Set language from config to override channel language */
10011  if (!ast_strlen_zero(vmu->language))
10012  ast_string_field_set(chan, language, vmu->language);
10013 
10014  /* Retrieve urgent, old and new message counts */
10015  ast_debug(1, "Before open_mailbox\n");
10016  res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10017  if (res < 0)
10018  goto out;
10019  vms.oldmessages = vms.lastmsg + 1;
10020  ast_debug(1, "Number of old messages: %d\n", vms.oldmessages);
10021  /* check INBOX */
10022  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10023  if (res < 0)
10024  goto out;
10025  vms.newmessages = vms.lastmsg + 1;
10026  ast_debug(1, "Number of new messages: %d\n", vms.newmessages);
10027  /* Start in Urgent */
10028  in_urgent = 1;
10029  res = open_mailbox(&vms, vmu, 11); /*11 is the Urgent folder */
10030  if (res < 0)
10031  goto out;
10032  vms.urgentmessages = vms.lastmsg + 1;
10033  ast_debug(1, "Number of urgent messages: %d\n", vms.urgentmessages);
10034 
10035  /* Select proper mailbox FIRST!! */
10036  if (play_auto) {
10037  ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
10038  if (vms.urgentmessages) {
10039  in_urgent = 1;
10040  res = open_mailbox(&vms, vmu, 11);
10041  } else {
10042  in_urgent = 0;
10043  res = open_mailbox(&vms, vmu, play_folder);
10044  }
10045  if (res < 0)
10046  goto out;
10047 
10048  /* If there are no new messages, inform the user and hangup */
10049  if (vms.lastmsg == -1) {
10050  in_urgent = 0;
10051  cmd = vm_browse_messages(chan, &vms, vmu);
10052  res = 0;
10053  goto out;
10054  }
10055  } else {
10056  if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
10057  /* If we only have old messages start here */
10058  res = open_mailbox(&vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
10059  in_urgent = 0;
10060  play_folder = 1;
10061  if (res < 0)
10062  goto out;
10063  } else if (!vms.urgentmessages && vms.newmessages) {
10064  /* If we have new messages but none are urgent */
10065  in_urgent = 0;
10066  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10067  if (res < 0)
10068  goto out;
10069  }
10070  }
10071 
10072  if (useadsi)
10073  adsi_status(chan, &vms);
10074  res = 0;
10075 
10076  /* Check to see if this is a new user */
10077  if (!strcasecmp(vmu->mailbox, vmu->password) &&
10079  if (ast_play_and_wait(chan, "vm-newuser") == -1)
10080  ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
10081  cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
10082  if ((cmd == 't') || (cmd == '#')) {
10083  /* Timeout */
10084  ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
10085  res = 0;
10086  goto out;
10087  } else if (cmd < 0) {
10088  /* Hangup */
10089  ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
10090  res = -1;
10091  goto out;
10092  }
10093  }
10094 #ifdef IMAP_STORAGE
10095  ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
10096  if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
10097  ast_debug(1, "*** QUOTA EXCEEDED!!\n");
10098  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10099  }
10100  ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10101  if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
10102  ast_log(AST_LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
10103  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10104  }
10105 #endif
10106 
10107  ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
10108  if (play_auto) {
10109  cmd = '1';
10110  } else {
10111  cmd = vm_intro(chan, vmu, &vms);
10112  }
10113  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10114 
10115  vms.repeats = 0;
10116  vms.starting = 1;
10117  while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10118  /* Run main menu */
10119  switch (cmd) {
10120  case '1': /* First message */
10121  vms.curmsg = 0;
10122  /* Fall through */
10123  case '5': /* Play current message */
10124  ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10125  cmd = vm_browse_messages(chan, &vms, vmu);
10126  break;
10127  case '2': /* Change folders */
10128  ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
10129  if (useadsi)
10130  adsi_folders(chan, 0, "Change to folder...");
10131 
10132  cmd = get_folder2(chan, "vm-changeto", 0);
10133  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10134  if (cmd == '#') {
10135  cmd = 0;
10136  } else if (cmd > 0) {
10137  cmd = cmd - '0';
10138  res = close_mailbox(&vms, vmu);
10139  if (res == ERROR_LOCK_PATH)
10140  goto out;
10141  /* If folder is not urgent, set in_urgent to zero! */
10142  if (cmd != 11) in_urgent = 0;
10143  res = open_mailbox(&vms, vmu, cmd);
10144  if (res < 0)
10145  goto out;
10146  play_folder = cmd;
10147  cmd = 0;
10148  }
10149  if (useadsi)
10150  adsi_status2(chan, &vms);
10151 
10152  if (!cmd) {
10153  cmd = vm_play_folder_name(chan, vms.vmbox);
10154  }
10155 
10156  vms.starting = 1;
10157  vms.curmsg = 0;
10158  break;
10159  case '3': /* Advanced options */
10160  ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
10161  cmd = 0;
10162  vms.repeats = 0;
10163  while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
10164  switch (cmd) {
10165  case '1': /* Reply */
10166  if (vms.lastmsg > -1 && !vms.starting) {
10167  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
10168  if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10169  res = cmd;
10170  goto out;
10171  }
10172  } else {
10173  cmd = ast_play_and_wait(chan, "vm-sorry");
10174  }
10175  cmd = 't';
10176  break;
10177  case '2': /* Callback */
10178  if (!vms.starting)
10179  ast_verb(3, "Callback Requested\n");
10180  if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
10181  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
10182  if (cmd == 9) {
10183  silentexit = 1;
10184  goto out;
10185  } else if (cmd == ERROR_LOCK_PATH) {
10186  res = cmd;
10187  goto out;
10188  }
10189  } else {
10190  cmd = ast_play_and_wait(chan, "vm-sorry");
10191  }
10192  cmd = 't';
10193  break;
10194  case '3': /* Envelope */
10195  if (vms.lastmsg > -1 && !vms.starting) {
10196  cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
10197  if (cmd == ERROR_LOCK_PATH) {
10198  res = cmd;
10199  goto out;
10200  }
10201  } else {
10202  cmd = ast_play_and_wait(chan, "vm-sorry");
10203  }
10204  cmd = 't';
10205  break;
10206  case '4': /* Dialout */
10207  if (!ast_strlen_zero(vmu->dialout)) {
10208  cmd = dialout(chan, vmu, NULL, vmu->dialout);
10209  if (cmd == 9) {
10210  silentexit = 1;
10211  goto out;
10212  }
10213  } else {
10214  cmd = ast_play_and_wait(chan, "vm-sorry");
10215  }
10216  cmd = 't';
10217  break;
10218 
10219  case '5': /* Leave VoiceMail */
10220  if (ast_test_flag(vmu, VM_SVMAIL)) {
10221  cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
10222  if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
10223  res = cmd;
10224  goto out;
10225  }
10226  } else {
10227  cmd = ast_play_and_wait(chan, "vm-sorry");
10228  }
10229  cmd = 't';
10230  break;
10231 
10232  case '*': /* Return to main menu */
10233  cmd = 't';
10234  break;
10235 
10236  default:
10237  cmd = 0;
10238  if (!vms.starting) {
10239  cmd = ast_play_and_wait(chan, "vm-toreply");
10240  }
10241  if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
10242  cmd = ast_play_and_wait(chan, "vm-tocallback");
10243  }
10244  if (!cmd && !vms.starting) {
10245  cmd = ast_play_and_wait(chan, "vm-tohearenv");
10246  }
10247  if (!ast_strlen_zero(vmu->dialout) && !cmd) {
10248  cmd = ast_play_and_wait(chan, "vm-tomakecall");
10249  }
10250  if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
10251  cmd = ast_play_and_wait(chan, "vm-leavemsg");
10252  }
10253  if (!cmd) {
10254  cmd = ast_play_and_wait(chan, "vm-starmain");
10255  }
10256  if (!cmd) {
10257  cmd = ast_waitfordigit(chan, 6000);
10258  }
10259  if (!cmd) {
10260  vms.repeats++;
10261  }
10262  if (vms.repeats > 3) {
10263  cmd = 't';
10264  }
10265  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10266  }
10267  }
10268  if (cmd == 't') {
10269  cmd = 0;
10270  vms.repeats = 0;
10271  }
10272  break;
10273  case '4': /* Go to the previous message */
10274  ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
10275  if (vms.curmsg > 0) {
10276  vms.curmsg--;
10277  cmd = play_message(chan, vmu, &vms);
10278  } else {
10279  /* Check if we were listening to new
10280  messages. If so, go to Urgent messages
10281  instead of saying "no more messages"
10282  */
10283  if (in_urgent == 0 && vms.urgentmessages > 0) {
10284  /* Check for Urgent messages */
10285  in_urgent = 1;
10286  res = close_mailbox(&vms, vmu);
10287  if (res == ERROR_LOCK_PATH)
10288  goto out;
10289  res = open_mailbox(&vms, vmu, 11); /* Open Urgent folder */
10290  if (res < 0)
10291  goto out;
10292  ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
10293  vms.curmsg = vms.lastmsg;
10294  if (vms.lastmsg < 0) {
10295  cmd = ast_play_and_wait(chan, "vm-nomore");
10296  }
10297  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10298  vms.curmsg = vms.lastmsg;
10299  cmd = play_message(chan, vmu, &vms);
10300  } else {
10301  cmd = ast_play_and_wait(chan, "vm-nomore");
10302  }
10303  }
10304  break;
10305  case '6': /* Go to the next message */
10306  ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
10307  if (vms.curmsg < vms.lastmsg) {
10308  vms.curmsg++;
10309  cmd = play_message(chan, vmu, &vms);
10310  } else {
10311  if (in_urgent && vms.newmessages > 0) {
10312  /* Check if we were listening to urgent
10313  * messages. If so, go to regular new messages
10314  * instead of saying "no more messages"
10315  */
10316  in_urgent = 0;
10317  res = close_mailbox(&vms, vmu);
10318  if (res == ERROR_LOCK_PATH)
10319  goto out;
10320  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10321  if (res < 0)
10322  goto out;
10323  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10324  vms.curmsg = -1;
10325  if (vms.lastmsg < 0) {
10326  cmd = ast_play_and_wait(chan, "vm-nomore");
10327  }
10328  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10329  vms.curmsg = 0;
10330  cmd = play_message(chan, vmu, &vms);
10331  } else {
10332  cmd = ast_play_and_wait(chan, "vm-nomore");
10333  }
10334  }
10335  break;
10336  case '7': /* Delete the current message */
10337  if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
10338  vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
10339  if (useadsi)
10340  adsi_delete(chan, &vms);
10341  if (vms.deleted[vms.curmsg]) {
10342  if (play_folder == 0) {
10343  if (in_urgent) {
10344  vms.urgentmessages--;
10345  } else {
10346  vms.newmessages--;
10347  }
10348  }
10349  else if (play_folder == 1)
10350  vms.oldmessages--;
10351  cmd = ast_play_and_wait(chan, "vm-deleted");
10352  } else {
10353  if (play_folder == 0) {
10354  if (in_urgent) {
10355  vms.urgentmessages++;
10356  } else {
10357  vms.newmessages++;
10358  }
10359  }
10360  else if (play_folder == 1)
10361  vms.oldmessages++;
10362  cmd = ast_play_and_wait(chan, "vm-undeleted");
10363  }
10364  if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10365  if (vms.curmsg < vms.lastmsg) {
10366  vms.curmsg++;
10367  cmd = play_message(chan, vmu, &vms);
10368  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10369  vms.curmsg = 0;
10370  cmd = play_message(chan, vmu, &vms);
10371  } else {
10372  /* Check if we were listening to urgent
10373  messages. If so, go to regular new messages
10374  instead of saying "no more messages"
10375  */
10376  if (in_urgent == 1) {
10377  /* Check for new messages */
10378  in_urgent = 0;
10379  res = close_mailbox(&vms, vmu);
10380  if (res == ERROR_LOCK_PATH)
10381  goto out;
10382  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10383  if (res < 0)
10384  goto out;
10385  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10386  vms.curmsg = -1;
10387  if (vms.lastmsg < 0) {
10388  cmd = ast_play_and_wait(chan, "vm-nomore");
10389  }
10390  } else {
10391  cmd = ast_play_and_wait(chan, "vm-nomore");
10392  }
10393  }
10394  }
10395  } else /* Delete not valid if we haven't selected a message */
10396  cmd = 0;
10397 #ifdef IMAP_STORAGE
10398  deleted = 1;
10399 #endif
10400  break;
10401 
10402  case '8': /* Forward the current message */
10403  if (vms.lastmsg > -1) {
10404  cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
10405  if (cmd == ERROR_LOCK_PATH) {
10406  res = cmd;
10407  goto out;
10408  }
10409  } else {
10410  /* Check if we were listening to urgent
10411  messages. If so, go to regular new messages
10412  instead of saying "no more messages"
10413  */
10414  if (in_urgent == 1 && vms.newmessages > 0) {
10415  /* Check for new messages */
10416  in_urgent = 0;
10417  res = close_mailbox(&vms, vmu);
10418  if (res == ERROR_LOCK_PATH)
10419  goto out;
10420  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10421  if (res < 0)
10422  goto out;
10423  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10424  vms.curmsg = -1;
10425  if (vms.lastmsg < 0) {
10426  cmd = ast_play_and_wait(chan, "vm-nomore");
10427  }
10428  } else {
10429  cmd = ast_play_and_wait(chan, "vm-nomore");
10430  }
10431  }
10432  break;
10433  case '9': /* Save message to folder */
10434  ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
10435  if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
10436  /* No message selected */
10437  cmd = 0;
10438  break;
10439  }
10440  if (useadsi)
10441  adsi_folders(chan, 1, "Save to folder...");
10442  cmd = get_folder2(chan, "vm-savefolder", 1);
10443  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
10444  box = 0; /* Shut up compiler */
10445  if (cmd == '#') {
10446  cmd = 0;
10447  break;
10448  } else if (cmd > 0) {
10449  box = cmd = cmd - '0';
10450  cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
10451  if (cmd == ERROR_LOCK_PATH) {
10452  res = cmd;
10453  goto out;
10454 #ifndef IMAP_STORAGE
10455  } else if (!cmd) {
10456  vms.deleted[vms.curmsg] = 1;
10457 #endif
10458  } else {
10459  vms.deleted[vms.curmsg] = 0;
10460  vms.heard[vms.curmsg] = 0;
10461  }
10462  }
10463  make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
10464  if (useadsi)
10465  adsi_message(chan, &vms);
10466  snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
10467  if (!cmd) {
10468  cmd = ast_play_and_wait(chan, "vm-message");
10469  if (!cmd)
10470  cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
10471  if (!cmd)
10472  cmd = ast_play_and_wait(chan, "vm-savedto");
10473  if (!cmd)
10474  cmd = vm_play_folder_name(chan, vms.fn);
10475  } else {
10476  cmd = ast_play_and_wait(chan, "vm-mailboxfull");
10477  }
10478  if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
10479  if (vms.curmsg < vms.lastmsg) {
10480  vms.curmsg++;
10481  cmd = play_message(chan, vmu, &vms);
10482  } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
10483  vms.curmsg = 0;
10484  cmd = play_message(chan, vmu, &vms);
10485  } else {
10486  /* Check if we were listening to urgent
10487  messages. If so, go to regular new messages
10488  instead of saying "no more messages"
10489  */
10490  if (in_urgent == 1 && vms.newmessages > 0) {
10491  /* Check for new messages */
10492  in_urgent = 0;
10493  res = close_mailbox(&vms, vmu);
10494  if (res == ERROR_LOCK_PATH)
10495  goto out;
10496  res = open_mailbox(&vms, vmu, NEW_FOLDER);
10497  if (res < 0)
10498  goto out;
10499  ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
10500  vms.curmsg = -1;
10501  if (vms.lastmsg < 0) {
10502  cmd = ast_play_and_wait(chan, "vm-nomore");
10503  }
10504  } else {
10505  cmd = ast_play_and_wait(chan, "vm-nomore");
10506  }
10507  }
10508  }
10509  break;
10510  case '*': /* Help */
10511  if (!vms.starting) {
10512  cmd = ast_play_and_wait(chan, "vm-onefor");
10513  if (!strncasecmp(chan->language, "he", 2)) {
10514  cmd = ast_play_and_wait(chan, "vm-for");
10515  }
10516  if (!cmd)
10517  cmd = vm_play_folder_name(chan, vms.vmbox);
10518  if (!cmd)
10519  cmd = ast_play_and_wait(chan, "vm-opts");
10520  if (!cmd)
10521  cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent);
10522  } else
10523  cmd = 0;
10524  break;
10525  case '0': /* Mailbox options */
10526  cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
10527  if (useadsi)
10528  adsi_status(chan, &vms);
10529  break;
10530  default: /* Nothing */
10531  ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
10532  cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent);
10533  break;
10534  }
10535  }
10536  if ((cmd == 't') || (cmd == '#')) {
10537  /* Timeout */
10538  res = 0;
10539  } else {
10540  /* Hangup */
10541  res = -1;
10542  }
10543 
10544 out:
10545  if (res > -1) {
10546  ast_stopstream(chan);
10547  adsi_goodbye(chan);
10548  if (valid && res != OPERATOR_EXIT) {
10549  if (silentexit)
10550  res = ast_play_and_wait(chan, "vm-dialout");
10551  else
10552  res = ast_play_and_wait(chan, "vm-goodbye");
10553  }
10554  if ((valid && res > 0) || res == OPERATOR_EXIT) {
10555  res = 0;
10556  }
10557  if (useadsi)
10559  }
10560  if (vmu)
10561  close_mailbox(&vms, vmu);
10562  if (valid) {
10563  int new = 0, old = 0, urgent = 0;
10564  snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
10565  ast_manager_event(chan, EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
10566  /* Urgent flag not passwd to externnotify here */
10567  run_externnotify(vmu->context, vmu->mailbox, NULL);
10568  ast_app_inboxcount2(ext_context, &urgent, &new, &old);
10569  queue_mwi_event(ext_context, urgent, new, old);
10570  }
10571 #ifdef IMAP_STORAGE
10572  /* expunge message - use UID Expunge if supported on IMAP server*/
10573  ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
10574  if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
10575  ast_mutex_lock(&vms.lock);
10576 #ifdef HAVE_IMAP_TK2006
10577  if (LEVELUIDPLUS (vms.mailstream)) {
10578  mail_expunge_full(vms.mailstream, NIL, EX_UID);
10579  } else
10580 #endif
10581  mail_expunge(vms.mailstream);
10582  ast_mutex_unlock(&vms.lock);
10583  }
10584  /* before we delete the state, we should copy pertinent info
10585  * back to the persistent model */
10586  if (vmu) {
10587  vmstate_delete(&vms);
10588  }
10589 #endif
10590  if (vmu)
10591  free_user(vmu);
10592 
10593 #ifdef IMAP_STORAGE
10594  pthread_setspecific(ts_vmstate.key, NULL);
10595 #endif
10596  return res;
10597 }
10598 
10599 static int vm_exec(struct ast_channel *chan, const char *data)
10600 {
10601  int res = 0;
10602  char *tmp;
10603  struct leave_vm_options leave_options;
10604  struct ast_flags flags = { 0 };
10605  char *opts[OPT_ARG_ARRAY_SIZE];
10607  AST_APP_ARG(argv0);
10608  AST_APP_ARG(argv1);
10609  );
10610 
10611  memset(&leave_options, 0, sizeof(leave_options));
10612 
10613  if (chan->_state != AST_STATE_UP)
10614  ast_answer(chan);
10615 
10616  if (!ast_strlen_zero(data)) {
10617  tmp = ast_strdupa(data);
10619  if (args.argc == 2) {
10620  if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1))
10621  return -1;
10623  if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
10624  int gain;
10625 
10626  if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
10627  ast_log(AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
10628  return -1;
10629  } else {
10630  leave_options.record_gain = (signed char) gain;
10631  }
10632  }
10633  if (ast_test_flag(&flags, OPT_DTMFEXIT)) {
10634  if (!ast_strlen_zero(opts[OPT_ARG_DTMFEXIT]))
10635  leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
10636  }
10637  }
10638  } else {
10639  char temp[256];
10640  res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
10641  if (res < 0)
10642  return res;
10643  if (ast_strlen_zero(temp))
10644  return 0;
10645  args.argv0 = ast_strdupa(temp);
10646  }
10647 
10648  res = leave_voicemail(chan, args.argv0, &leave_options);
10649  if (res == 't') {
10650  ast_play_and_wait(chan, "vm-goodbye");
10651  res = 0;
10652  }
10653 
10654  if (res == OPERATOR_EXIT) {
10655  res = 0;
10656  }
10657 
10658  if (res == ERROR_LOCK_PATH) {
10659  ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
10660  pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
10661  res = 0;
10662  }
10663 
10664  return res;
10665 }
10666 
10667 static struct ast_vm_user *find_or_create(const char *context, const char *box)
10668 {
10669  struct ast_vm_user *vmu;
10670 
10671  if (!ast_strlen_zero(box) && box[0] == '*') {
10672  ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character. The '*' character,"
10673  "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
10674  "\n\tpredefined extension 'a'. A mailbox or password beginning with '*' is not valid"
10675  "\n\tand will be ignored.\n", box, context);
10676  return NULL;
10677  }
10678 
10679  AST_LIST_TRAVERSE(&users, vmu, list) {
10680  if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
10681  if (strcasecmp(vmu->context, context)) {
10682  ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
10683  \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
10684  \n\tconfiguration creates an ambiguity that you likely do not want. Please\
10685  \n\tamend your voicemail.conf file to avoid this situation.\n", box);
10686  }
10687  ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
10688  return NULL;
10689  }
10690  if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
10691  ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
10692  return NULL;
10693  }
10694  }
10695 
10696  if (!(vmu = ast_calloc(1, sizeof(*vmu))))
10697  return NULL;
10698 
10699  ast_copy_string(vmu->context, context, sizeof(vmu->context));
10700  ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
10701 
10702  AST_LIST_INSERT_TAIL(&users, vmu, list);
10703 
10704  return vmu;
10705 }
10706 
10707 static int append_mailbox(const char *context, const char *box, const char *data)
10708 {
10709  /* Assumes lock is already held */
10710  char *tmp;
10711  char *stringp;
10712  char *s;
10713  struct ast_vm_user *vmu;
10714  char *mailbox_full;
10715  int new = 0, old = 0, urgent = 0;
10716  char secretfn[PATH_MAX] = "";
10717 
10718  tmp = ast_strdupa(data);
10719 
10720  if (!(vmu = find_or_create(context, box)))
10721  return -1;
10722 
10723  populate_defaults(vmu);
10724 
10725  stringp = tmp;
10726  if ((s = strsep(&stringp, ","))) {
10727  if (!ast_strlen_zero(s) && s[0] == '*') {
10728  ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
10729  "\n\tmust be reset in voicemail.conf.\n", box);
10730  }
10731  /* assign password regardless of validity to prevent NULL password from being assigned */
10732  ast_copy_string(vmu->password, s, sizeof(vmu->password));
10733  }
10734  if (stringp && (s = strsep(&stringp, ","))) {
10735  ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
10736  }
10737  if (stringp && (s = strsep(&stringp, ","))) {
10738  ast_copy_string(vmu->email, s, sizeof(vmu->email));
10739  }
10740  if (stringp && (s = strsep(&stringp, ","))) {
10741  ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
10742  }
10743  if (stringp && (s = strsep(&stringp, ","))) {
10744  apply_options(vmu, s);
10745  }
10746 
10747  switch (vmu->passwordlocation) {
10748  case OPT_PWLOC_SPOOLDIR:
10749  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
10750  read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
10751  }
10752 
10753  mailbox_full = ast_alloca(strlen(box) + strlen(context) + 1);
10754  strcpy(mailbox_full, box);
10755  strcat(mailbox_full, "@");
10756  strcat(mailbox_full, context);
10757 
10758  inboxcount2(mailbox_full, &urgent, &new, &old);
10759  queue_mwi_event(mailbox_full, urgent, new, old);
10760 
10761  return 0;
10762 }
10763 
10764 AST_TEST_DEFINE(test_voicemail_vmuser)
10765 {
10766  int res = 0;
10767  struct ast_vm_user *vmu;
10768  /* language parameter seems to only be used for display in manager action */
10769  static const char options_string[] = "attach=yes|attachfmt=wav49|"
10770  "serveremail=someguy@digium.com|tz=central|delete=yes|saycid=yes|"
10771  "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|"
10772  "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
10773  "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
10774  "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
10775  "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
10776  "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
10777  "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
10778 #ifdef IMAP_STORAGE
10779  static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
10780  "imapfolder=INBOX|imapvmshareid=6000";
10781 #endif
10782 
10783  switch (cmd) {
10784  case TEST_INIT:
10785  info->name = "vmuser";
10786  info->category = "/apps/app_voicemail/";
10787  info->summary = "Vmuser unit test";
10788  info->description =
10789  "This tests passing all supported parameters to apply_options, the voicemail user config parser";
10790  return AST_TEST_NOT_RUN;
10791  case TEST_EXECUTE:
10792  break;
10793  }
10794 
10795  if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
10796  return AST_TEST_NOT_RUN;
10797  }
10798  populate_defaults(vmu);
10799  ast_set_flag(vmu, VM_ALLOCED);
10800 
10801  apply_options(vmu, options_string);
10802 
10803  if (!ast_test_flag(vmu, VM_ATTACH)) {
10804  ast_test_status_update(test, "Parse failure for attach option\n");
10805  res = 1;
10806  }
10807  if (strcasecmp(vmu->attachfmt, "wav49")) {
10808  ast_test_status_update(test, "Parse failure for attachftm option\n");
10809  res = 1;
10810  }
10811  if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
10812  ast_test_status_update(test, "Parse failure for serveremail option\n");
10813  res = 1;
10814  }
10815  if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
10816  ast_test_status_update(test, "Parse failure for emailsubject option\n");
10817  res = 1;
10818  }
10819  if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
10820  ast_test_status_update(test, "Parse failure for emailbody option\n");
10821  res = 1;
10822  }
10823  if (strcasecmp(vmu->zonetag, "central")) {
10824  ast_test_status_update(test, "Parse failure for tz option\n");
10825  res = 1;
10826  }
10827  if (!ast_test_flag(vmu, VM_DELETE)) {
10828  ast_test_status_update(test, "Parse failure for delete option\n");
10829  res = 1;
10830  }
10831  if (!ast_test_flag(vmu, VM_SAYCID)) {
10832  ast_test_status_update(test, "Parse failure for saycid option\n");
10833  res = 1;
10834  }
10835  if (!ast_test_flag(vmu, VM_SVMAIL)) {
10836  ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
10837  res = 1;
10838  }
10839  if (!ast_test_flag(vmu, VM_REVIEW)) {
10840  ast_test_status_update(test, "Parse failure for review option\n");
10841  res = 1;
10842  }
10843  if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10844  ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
10845  res = 1;
10846  }
10847  if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
10848  ast_test_status_update(test, "Parse failure for messagewrap option\n");
10849  res = 1;
10850  }
10851  if (!ast_test_flag(vmu, VM_OPERATOR)) {
10852  ast_test_status_update(test, "Parse failure for operator option\n");
10853  res = 1;
10854  }
10855  if (!ast_test_flag(vmu, VM_ENVELOPE)) {
10856  ast_test_status_update(test, "Parse failure for envelope option\n");
10857  res = 1;
10858  }
10859  if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
10860  ast_test_status_update(test, "Parse failure for moveheard option\n");
10861  res = 1;
10862  }
10863  if (!ast_test_flag(vmu, VM_SAYDURATION)) {
10864  ast_test_status_update(test, "Parse failure for sayduration option\n");
10865  res = 1;
10866  }
10867  if (vmu->saydurationm != 5) {
10868  ast_test_status_update(test, "Parse failure for saydurationm option\n");
10869  res = 1;
10870  }
10871  if (!ast_test_flag(vmu, VM_FORCENAME)) {
10872  ast_test_status_update(test, "Parse failure for forcename option\n");
10873  res = 1;
10874  }
10875  if (!ast_test_flag(vmu, VM_FORCEGREET)) {
10876  ast_test_status_update(test, "Parse failure for forcegreetings option\n");
10877  res = 1;
10878  }
10879  if (strcasecmp(vmu->callback, "somecontext")) {
10880  ast_test_status_update(test, "Parse failure for callbacks option\n");
10881  res = 1;
10882  }
10883  if (strcasecmp(vmu->dialout, "somecontext2")) {
10884  ast_test_status_update(test, "Parse failure for dialout option\n");
10885  res = 1;
10886  }
10887  if (strcasecmp(vmu->exit, "somecontext3")) {
10888  ast_test_status_update(test, "Parse failure for exitcontext option\n");
10889  res = 1;
10890  }
10891  if (vmu->minsecs != 10) {
10892  ast_test_status_update(test, "Parse failure for minsecs option\n");
10893  res = 1;
10894  }
10895  if (vmu->maxsecs != 100) {
10896  ast_test_status_update(test, "Parse failure for maxsecs option\n");
10897  res = 1;
10898  }
10899  if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
10900  ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
10901  res = 1;
10902  }
10903  if (vmu->maxdeletedmsg != 50) {
10904  ast_test_status_update(test, "Parse failure for backupdeleted option\n");
10905  res = 1;
10906  }
10907  if (vmu->volgain != 1.3) {
10908  ast_test_status_update(test, "Parse failure for volgain option\n");
10909  res = 1;
10910  }
10911  if (vmu->passwordlocation != OPT_PWLOC_SPOOLDIR) {
10912  ast_test_status_update(test, "Parse failure for passwordlocation option\n");
10913  res = 1;
10914  }
10915 #ifdef IMAP_STORAGE
10916  apply_options(vmu, option_string2);
10917 
10918  if (strcasecmp(vmu->imapuser, "imapuser")) {
10919  ast_test_status_update(test, "Parse failure for imapuser option\n");
10920  res = 1;
10921  }
10922  if (strcasecmp(vmu->imappassword, "imappasswd")) {
10923  ast_test_status_update(test, "Parse failure for imappasswd option\n");
10924  res = 1;
10925  }
10926  if (strcasecmp(vmu->imapfolder, "INBOX")) {
10927  ast_test_status_update(test, "Parse failure for imapfolder option\n");
10928  res = 1;
10929  }
10930  if (strcasecmp(vmu->imapvmshareid, "6000")) {
10931  ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
10932  res = 1;
10933  }
10934 #endif
10935 
10936  free_user(vmu);
10937  return res ? AST_TEST_FAIL : AST_TEST_PASS;
10938 }
10939 
10940 static int vm_box_exists(struct ast_channel *chan, const char *data)
10941 {
10942  struct ast_vm_user svm;
10943  char *context, *box;
10945  AST_APP_ARG(mbox);
10946  AST_APP_ARG(options);
10947  );
10948  static int dep_warning = 0;
10949 
10950  if (ast_strlen_zero(data)) {
10951  ast_log(AST_LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
10952  return -1;
10953  }
10954 
10955  if (!dep_warning) {
10956  dep_warning = 1;
10957  ast_log(AST_LOG_WARNING, "MailboxExists is deprecated. Please use ${MAILBOX_EXISTS(%s)} instead.\n", (char *) data);
10958  }
10959 
10960  box = ast_strdupa(data);
10961 
10963 
10964  if (args.options) {
10965  }
10966 
10967  if ((context = strchr(args.mbox, '@'))) {
10968  *context = '\0';
10969  context++;
10970  }
10971 
10972  if (find_user(&svm, context, args.mbox)) {
10973  pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
10974  } else
10975  pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
10976 
10977  return 0;
10978 }
10979 
10980 static int acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
10981 {
10982  struct ast_vm_user svm;
10984  AST_APP_ARG(mbox);
10985  AST_APP_ARG(context);
10986  );
10987 
10988  AST_NONSTANDARD_APP_ARGS(arg, args, '@');
10989 
10990  if (ast_strlen_zero(arg.mbox)) {
10991  ast_log(LOG_ERROR, "MAILBOX_EXISTS requires an argument (<mailbox>[@<context>])\n");
10992  return -1;
10993  }
10994 
10995  ast_copy_string(buf, find_user(&svm, ast_strlen_zero(arg.context) ? "default" : arg.context, arg.mbox) ? "1" : "0", len);
10996  return 0;
10997 }
10998 
11000  .name = "MAILBOX_EXISTS",
11001  .read = acf_mailbox_exists,
11002 };
11003 
11004 static int vmauthenticate(struct ast_channel *chan, const char *data)
11005 {
11006  char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
11007  struct ast_vm_user vmus;
11008  char *options = NULL;
11009  int silent = 0, skipuser = 0;
11010  int res = -1;
11011 
11012  if (data) {
11013  s = ast_strdupa(data);
11014  user = strsep(&s, ",");
11015  options = strsep(&s, ",");
11016  if (user) {
11017  s = user;
11018  user = strsep(&s, "@");
11019  context = strsep(&s, "");
11020  if (!ast_strlen_zero(user))
11021  skipuser++;
11022  ast_copy_string(mailbox, user, sizeof(mailbox));
11023  }
11024  }
11025 
11026  if (options) {
11027  silent = (strchr(options, 's')) != NULL;
11028  }
11029 
11030  if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
11031  pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
11032  pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
11033  ast_play_and_wait(chan, "auth-thankyou");
11034  res = 0;
11035  } else if (mailbox[0] == '*') {
11036  /* user entered '*' */
11037  if (!ast_goto_if_exists(chan, chan->context, "a", 1)) {
11038  res = 0; /* prevent hangup */
11039  }
11040  }
11041 
11042  return res;
11043 }
11044 
11045 static char *show_users_realtime(int fd, const char *context)
11046 {
11047  struct ast_config *cfg;
11048  const char *cat = NULL;
11049 
11050  if (!(cfg = ast_load_realtime_multientry("voicemail",
11051  "context", context, SENTINEL))) {
11052  return CLI_FAILURE;
11053  }
11054 
11055  ast_cli(fd,
11056  "\n"
11057  "=============================================================\n"
11058  "=== Configured Voicemail Users ==============================\n"
11059  "=============================================================\n"
11060  "===\n");
11061 
11062  while ((cat = ast_category_browse(cfg, cat))) {
11063  struct ast_variable *var = NULL;
11064  ast_cli(fd,
11065  "=== Mailbox ...\n"
11066  "===\n");
11067  for (var = ast_variable_browse(cfg, cat); var; var = var->next)
11068  ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
11069  ast_cli(fd,
11070  "===\n"
11071  "=== ---------------------------------------------------------\n"
11072  "===\n");
11073  }
11074 
11075  ast_cli(fd,
11076  "=============================================================\n"
11077  "\n");
11078 
11079  ast_config_destroy(cfg);
11080 
11081  return CLI_SUCCESS;
11082 }
11083 
11084 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
11085 {
11086  int which = 0;
11087  int wordlen;
11088  struct ast_vm_user *vmu;
11089  const char *context = "";
11090 
11091  /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
11092  if (pos > 4)
11093  return NULL;
11094  if (pos == 3)
11095  return (state == 0) ? ast_strdup("for") : NULL;
11096  wordlen = strlen(word);
11097  AST_LIST_TRAVERSE(&users, vmu, list) {
11098  if (!strncasecmp(word, vmu->context, wordlen)) {
11099  if (context && strcmp(context, vmu->context) && ++which > state)
11100  return ast_strdup(vmu->context);
11101  /* ignore repeated contexts ? */
11102  context = vmu->context;
11103  }
11104  }
11105  return NULL;
11106 }
11107 
11108 /*! \brief Show a list of voicemail users in the CLI */
11109 static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11110 {
11111  struct ast_vm_user *vmu;
11112 #define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
11113  const char *context = NULL;
11114  int users_counter = 0;
11115 
11116  switch (cmd) {
11117  case CLI_INIT:
11118  e->command = "voicemail show users";
11119  e->usage =
11120  "Usage: voicemail show users [for <context>]\n"
11121  " Lists all mailboxes currently set up\n";
11122  return NULL;
11123  case CLI_GENERATE:
11124  return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
11125  }
11126 
11127  if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
11128  return CLI_SHOWUSAGE;
11129  if (a->argc == 5) {
11130  if (strcmp(a->argv[3],"for"))
11131  return CLI_SHOWUSAGE;
11132  context = a->argv[4];
11133  }
11134 
11135  if (ast_check_realtime("voicemail")) {
11136  if (!context) {
11137  ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
11138  return CLI_SHOWUSAGE;
11139  }
11140  return show_users_realtime(a->fd, context);
11141  }
11142 
11143  AST_LIST_LOCK(&users);
11144  if (AST_LIST_EMPTY(&users)) {
11145  ast_cli(a->fd, "There are no voicemail users currently defined\n");
11147  return CLI_FAILURE;
11148  }
11149  if (!context) {
11150  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11151  } else {
11152  int count = 0;
11153  AST_LIST_TRAVERSE(&users, vmu, list) {
11154  if (!strcmp(context, vmu->context)) {
11155  count++;
11156  break;
11157  }
11158  }
11159  if (count) {
11160  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
11161  } else {
11162  ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
11164  return CLI_FAILURE;
11165  }
11166  }
11167  AST_LIST_TRAVERSE(&users, vmu, list) {
11168  int newmsgs = 0, oldmsgs = 0;
11169  char count[12], tmp[256] = "";
11170 
11171  if (!context || !strcmp(context, vmu->context)) {
11172  snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
11173  inboxcount(tmp, &newmsgs, &oldmsgs);
11174  snprintf(count, sizeof(count), "%d", newmsgs);
11175  ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
11176  users_counter++;
11177  }
11178  }
11180  ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
11181  return CLI_SUCCESS;
11182 }
11183 
11184 /*! \brief Show a list of voicemail zones in the CLI */
11185 static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11186 {
11187  struct vm_zone *zone;
11188 #define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
11189  char *res = CLI_SUCCESS;
11190 
11191  switch (cmd) {
11192  case CLI_INIT:
11193  e->command = "voicemail show zones";
11194  e->usage =
11195  "Usage: voicemail show zones\n"
11196  " Lists zone message formats\n";
11197  return NULL;
11198  case CLI_GENERATE:
11199  return NULL;
11200  }
11201 
11202  if (a->argc != 3)
11203  return CLI_SHOWUSAGE;
11204 
11205  AST_LIST_LOCK(&zones);
11206  if (!AST_LIST_EMPTY(&zones)) {
11207  ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
11208  AST_LIST_TRAVERSE(&zones, zone, list) {
11209  ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
11210  }
11211  } else {
11212  ast_cli(a->fd, "There are no voicemail zones currently defined\n");
11213  res = CLI_FAILURE;
11214  }
11216 
11217  return res;
11218 }
11219 
11220 /*! \brief Reload voicemail configuration from the CLI */
11221 static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
11222 {
11223  switch (cmd) {
11224  case CLI_INIT:
11225  e->command = "voicemail reload";
11226  e->usage =
11227  "Usage: voicemail reload\n"
11228  " Reload voicemail configuration\n";
11229  return NULL;
11230  case CLI_GENERATE:
11231  return NULL;
11232  }
11233 
11234  if (a->argc != 2)
11235  return CLI_SHOWUSAGE;
11236 
11237  ast_cli(a->fd, "Reloading voicemail configuration...\n");
11238  load_config(1);
11239 
11240  return CLI_SUCCESS;
11241 }
11242 
11243 static struct ast_cli_entry cli_voicemail[] = {
11244  AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
11245  AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
11246  AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
11247 };
11248 
11249 #ifdef IMAP_STORAGE
11250  #define DATA_EXPORT_VM_USERS(USER) \
11251  USER(ast_vm_user, context, AST_DATA_STRING) \
11252  USER(ast_vm_user, mailbox, AST_DATA_STRING) \
11253  USER(ast_vm_user, password, AST_DATA_PASSWORD) \
11254  USER(ast_vm_user, fullname, AST_DATA_STRING) \
11255  USER(ast_vm_user, email, AST_DATA_STRING) \
11256  USER(ast_vm_user, emailsubject, AST_DATA_STRING) \
11257  USER(ast_vm_user, emailbody, AST_DATA_STRING) \
11258  USER(ast_vm_user, pager, AST_DATA_STRING) \
11259  USER(ast_vm_user, serveremail, AST_DATA_STRING) \
11260  USER(ast_vm_user, language, AST_DATA_STRING) \
11261  USER(ast_vm_user, zonetag, AST_DATA_STRING) \
11262  USER(ast_vm_user, callback, AST_DATA_STRING) \
11263  USER(ast_vm_user, dialout, AST_DATA_STRING) \
11264  USER(ast_vm_user, uniqueid, AST_DATA_STRING) \
11265  USER(ast_vm_user, exit, AST_DATA_STRING) \
11266  USER(ast_vm_user, attachfmt, AST_DATA_STRING) \
11267  USER(ast_vm_user, flags, AST_DATA_UNSIGNED_INTEGER) \
11268  USER(ast_vm_user, saydurationm, AST_DATA_INTEGER) \
11269  USER(ast_vm_user, maxmsg, AST_DATA_INTEGER) \
11270  USER(ast_vm_user, maxdeletedmsg, AST_DATA_INTEGER) \
11271  USER(ast_vm_user, maxsecs, AST_DATA_INTEGER) \
11272  USER(ast_vm_user, imapuser, AST_DATA_STRING) \
11273  USER(ast_vm_user, imappassword, AST_DATA_STRING) \
11274  USER(ast_vm_user, imapvmshareid, AST_DATA_STRING) \
11275  USER(ast_vm_user, volgain, AST_DATA_DOUBLE)
11276 #else
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, volgain, AST_DATA_DOUBLE)
11300 #endif
11301 
11303 
11304 #define DATA_EXPORT_VM_ZONES(ZONE) \
11305  ZONE(vm_zone, name, AST_DATA_STRING) \
11306  ZONE(vm_zone, timezone, AST_DATA_STRING) \
11307  ZONE(vm_zone, msg_format, AST_DATA_STRING)
11308 
11310 
11311 /*!
11312  * \internal
11313  * \brief Add voicemail user to the data_root.
11314  * \param[in] search The search tree.
11315  * \param[in] data_root The main result node.
11316  * \param[in] user The voicemail user.
11317  */
11318 static int vm_users_data_provider_get_helper(const struct ast_data_search *search,
11319  struct ast_data *data_root, struct ast_vm_user *user)
11320 {
11321  struct ast_data *data_user, *data_zone;
11322  struct ast_data *data_state;
11323  struct vm_zone *zone = NULL;
11324  int urgentmsg = 0, newmsg = 0, oldmsg = 0;
11325  char ext_context[256] = "";
11326 
11327  data_user = ast_data_add_node(data_root, "user");
11328  if (!data_user) {
11329  return -1;
11330  }
11331 
11332  ast_data_add_structure(ast_vm_user, data_user, user);
11333 
11334  AST_LIST_LOCK(&zones);
11335  AST_LIST_TRAVERSE(&zones, zone, list) {
11336  if (!strcmp(zone->name, user->zonetag)) {
11337  break;
11338  }
11339  }
11341 
11342  /* state */
11343  data_state = ast_data_add_node(data_user, "state");
11344  if (!data_state) {
11345  return -1;
11346  }
11347  snprintf(ext_context, sizeof(ext_context), "%s@%s", user->mailbox, user->context);
11348  inboxcount2(ext_context, &urgentmsg, &newmsg, &oldmsg);
11349  ast_data_add_int(data_state, "urgentmsg", urgentmsg);
11350  ast_data_add_int(data_state, "newmsg", newmsg);
11351  ast_data_add_int(data_state, "oldmsg", oldmsg);
11352 
11353  if (zone) {
11354  data_zone = ast_data_add_node(data_user, "zone");
11355  ast_data_add_structure(vm_zone, data_zone, zone);
11356  }
11357 
11358  if (!ast_data_search_match(search, data_user)) {
11359  ast_data_remove_node(data_root, data_user);
11360  }
11361 
11362  return 0;
11363 }
11364 
11365 static int vm_users_data_provider_get(const struct ast_data_search *search,
11366  struct ast_data *data_root)
11367 {
11368  struct ast_vm_user *user;
11369 
11370  AST_LIST_LOCK(&users);
11371  AST_LIST_TRAVERSE(&users, user, list) {
11372  vm_users_data_provider_get_helper(search, data_root, user);
11373  }
11375 
11376  return 0;
11377 }
11378 
11382 };
11383 
11384 static const struct ast_data_entry vm_data_providers[] = {
11385  AST_DATA_ENTRY("asterisk/application/voicemail/list", &vm_users_data_provider)
11386 };
11387 
11389 {
11390  int new = 0, old = 0, urgent = 0;
11391 
11392  inboxcount2(mwi_sub->mailbox, &urgent, &new, &old);
11393 
11394  if (urgent != mwi_sub->old_urgent || new != mwi_sub->old_new || old != mwi_sub->old_old) {
11395  mwi_sub->old_urgent = urgent;
11396  mwi_sub->old_new = new;
11397  mwi_sub->old_old = old;
11398  queue_mwi_event(mwi_sub->mailbox, urgent, new, old);
11399  run_externnotify(NULL, mwi_sub->mailbox, NULL);
11400  }
11401 }
11402 
11403 static void poll_subscribed_mailboxes(void)
11404 {
11405  struct mwi_sub *mwi_sub;
11406 
11408  AST_RWLIST_TRAVERSE(&mwi_subs, mwi_sub, entry) {
11409  if (!ast_strlen_zero(mwi_sub->mailbox)) {
11410  poll_subscribed_mailbox(mwi_sub);
11411  }
11412  }
11414 }
11415 
11416 static void *mb_poll_thread(void *data)
11417 {
11418  while (poll_thread_run) {
11419  struct timespec ts = { 0, };
11420  struct timeval wait;
11421 
11422  wait = ast_tvadd(ast_tvnow(), ast_samp2tv(poll_freq, 1));
11423  ts.tv_sec = wait.tv_sec;
11424  ts.tv_nsec = wait.tv_usec * 1000;
11425 
11427  ast_cond_timedwait(&poll_cond, &poll_lock, &ts);
11429 
11430  if (!poll_thread_run)
11431  break;
11432 
11434  }
11435 
11436  return NULL;
11437 }
11438 
11439 static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
11440 {
11441  ast_free(mwi_sub);
11442 }
11443 
11444 static int handle_unsubscribe(void *datap)
11445 {
11446  struct mwi_sub *mwi_sub;
11447  uint32_t *uniqueid = datap;
11448 
11450  AST_RWLIST_TRAVERSE_SAFE_BEGIN(&mwi_subs, mwi_sub, entry) {
11451  if (mwi_sub->uniqueid == *uniqueid) {
11452  AST_LIST_REMOVE_CURRENT(entry);
11453  break;
11454  }
11455  }
11458 
11459  if (mwi_sub)
11460  mwi_sub_destroy(mwi_sub);
11461 
11462  ast_free(uniqueid);
11463  return 0;
11464 }
11465 
11466 static int handle_subscribe(void *datap)
11467 {
11468  unsigned int len;
11469  struct mwi_sub *mwi_sub;
11470  struct mwi_sub_task *p = datap;
11471 
11472  len = sizeof(*mwi_sub);
11473  if (!ast_strlen_zero(p->mailbox))
11474  len += strlen(p->mailbox);
11475 
11476  if (!ast_strlen_zero(p->context))
11477  len += strlen(p->context) + 1; /* Allow for seperator */
11478 
11479  if (!(mwi_sub = ast_calloc(1, len)))
11480  return -1;
11481 
11482  mwi_sub->uniqueid = p->uniqueid;
11483  if (!ast_strlen_zero(p->mailbox))
11484  strcpy(mwi_sub->mailbox, p->mailbox);
11485 
11486  if (!ast_strlen_zero(p->context)) {
11487  strcat(mwi_sub->mailbox, "@");
11488  strcat(mwi_sub->mailbox, p->context);
11489  }
11490 
11492  AST_RWLIST_INSERT_TAIL(&mwi_subs, mwi_sub, entry);
11494  ast_free((void *) p->mailbox);
11495  ast_free((void *) p->context);
11496  ast_free(p);
11497  poll_subscribed_mailbox(mwi_sub);
11498  return 0;
11499 }
11500 
11501 static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
11502 {
11503  uint32_t u, *uniqueid = ast_calloc(1, sizeof(*uniqueid));
11504 
11505  if (!uniqueid) {
11506  ast_log(LOG_ERROR, "Unable to allocate memory for uniqueid\n");
11507  return;
11508  }
11509 
11510  if (ast_event_get_type(event) != AST_EVENT_UNSUB) {
11511  ast_free(uniqueid);
11512  return;
11513  }
11514 
11516  ast_free(uniqueid);
11517  return;
11518  }
11519 
11521  *uniqueid = u;
11522  if (ast_taskprocessor_push(mwi_subscription_tps, handle_unsubscribe, uniqueid) < 0) {
11523  ast_free(uniqueid);
11524  }
11525 }
11526 
11527 static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
11528 {
11529  struct mwi_sub_task *mwist;
11530 
11531  if (ast_event_get_type(event) != AST_EVENT_SUB)
11532  return;
11533 
11535  return;
11536 
11537  if ((mwist = ast_calloc(1, (sizeof(*mwist)))) == NULL) {
11538  ast_log(LOG_ERROR, "could not allocate a mwi_sub_task\n");
11539  return;
11540  }
11544 
11545  if (ast_taskprocessor_push(mwi_subscription_tps, handle_subscribe, mwist) < 0) {
11546  ast_free(mwist);
11547  }
11548 }
11549 
11550 static void start_poll_thread(void)
11551 {
11552  int errcode;
11553  mwi_sub_sub = ast_event_subscribe(AST_EVENT_SUB, mwi_sub_event_cb, "Voicemail MWI subscription", NULL,
11556 
11557  mwi_unsub_sub = ast_event_subscribe(AST_EVENT_UNSUB, mwi_unsub_event_cb, "Voicemail MWI subscription", NULL,
11560 
11561  if (mwi_sub_sub)
11562  ast_event_report_subs(mwi_sub_sub);
11563 
11564  poll_thread_run = 1;
11565 
11566  if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
11567  ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
11568  }
11569 }
11570 
11571 static void stop_poll_thread(void)
11572 {
11573  poll_thread_run = 0;
11574 
11575  if (mwi_sub_sub) {
11576  ast_event_unsubscribe(mwi_sub_sub);
11577  mwi_sub_sub = NULL;
11578  }
11579 
11580  if (mwi_unsub_sub) {
11581  ast_event_unsubscribe(mwi_unsub_sub);
11582  mwi_unsub_sub = NULL;
11583  }
11584 
11586  ast_cond_signal(&poll_cond);
11588 
11589  pthread_join(poll_thread, NULL);
11590 
11591  poll_thread = AST_PTHREADT_NULL;
11592 }
11593 
11594 /*! \brief Manager list voicemail users command */
11595 static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
11596 {
11597  struct ast_vm_user *vmu = NULL;
11598  const char *id = astman_get_header(m, "ActionID");
11599  char actionid[128] = "";
11600 
11601  if (!ast_strlen_zero(id))
11602  snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
11603 
11604  AST_LIST_LOCK(&users);
11605 
11606  if (AST_LIST_EMPTY(&users)) {
11607  astman_send_ack(s, m, "There are no voicemail users currently defined.");
11609  return RESULT_SUCCESS;
11610  }
11611 
11612  astman_send_ack(s, m, "Voicemail user list will follow");
11613 
11614  AST_LIST_TRAVERSE(&users, vmu, list) {
11615  char dirname[256];
11616 
11617 #ifdef IMAP_STORAGE
11618  int new, old;
11619  inboxcount(vmu->mailbox, &new, &old);
11620 #endif
11621 
11622  make_dir(dirname, sizeof(dirname), vmu->context, vmu->mailbox, "INBOX");
11623  astman_append(s,
11624  "%s"
11625  "Event: VoicemailUserEntry\r\n"
11626  "VMContext: %s\r\n"
11627  "VoiceMailbox: %s\r\n"
11628  "Fullname: %s\r\n"
11629  "Email: %s\r\n"
11630  "Pager: %s\r\n"
11631  "ServerEmail: %s\r\n"
11632  "MailCommand: %s\r\n"
11633  "Language: %s\r\n"
11634  "TimeZone: %s\r\n"
11635  "Callback: %s\r\n"
11636  "Dialout: %s\r\n"
11637  "UniqueID: %s\r\n"
11638  "ExitContext: %s\r\n"
11639  "SayDurationMinimum: %d\r\n"
11640  "SayEnvelope: %s\r\n"
11641  "SayCID: %s\r\n"
11642  "AttachMessage: %s\r\n"
11643  "AttachmentFormat: %s\r\n"
11644  "DeleteMessage: %s\r\n"
11645  "VolumeGain: %.2f\r\n"
11646  "CanReview: %s\r\n"
11647  "CallOperator: %s\r\n"
11648  "MaxMessageCount: %d\r\n"
11649  "MaxMessageLength: %d\r\n"
11650  "NewMessageCount: %d\r\n"
11651 #ifdef IMAP_STORAGE
11652  "OldMessageCount: %d\r\n"
11653  "IMAPUser: %s\r\n"
11654 #endif
11655  "\r\n",
11656  actionid,
11657  vmu->context,
11658  vmu->mailbox,
11659  vmu->fullname,
11660  vmu->email,
11661  vmu->pager,
11662  ast_strlen_zero(vmu->serveremail) ? serveremail : vmu->serveremail,
11663  mailcmd,
11664  vmu->language,
11665  vmu->zonetag,
11666  vmu->callback,
11667  vmu->dialout,
11668  vmu->uniqueid,
11669  vmu->exit,
11670  vmu->saydurationm,
11671  ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
11672  ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
11673  ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
11674  vmu->attachfmt,
11675  ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
11676  vmu->volgain,
11677  ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
11678  ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
11679  vmu->maxmsg,
11680  vmu->maxsecs,
11681 #ifdef IMAP_STORAGE
11682  new, old, vmu->imapuser
11683 #else
11684  count_messages(vmu, dirname)
11685 #endif
11686  );
11687  }
11688  astman_append(s, "Event: VoicemailUserEntryComplete\r\n%s\r\n", actionid);
11689 
11691 
11692  return RESULT_SUCCESS;
11693 }
11694 
11695 /*! \brief Free the users structure. */
11696 static void free_vm_users(void)
11697 {
11698  struct ast_vm_user *current;
11699  AST_LIST_LOCK(&users);
11700  while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
11701  ast_set_flag(current, VM_ALLOCED);
11702  free_user(current);
11703  }
11705 }
11706 
11707 /*! \brief Free the zones structure. */
11708 static void free_vm_zones(void)
11709 {
11710  struct vm_zone *zcur;
11711  AST_LIST_LOCK(&zones);
11712  while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
11713  free_zone(zcur);
11715 }
11716 
11717 static const char *substitute_escapes(const char *value)
11718 {
11719  char *current;
11720 
11721  /* Add 16 for fudge factor */
11722  struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
11723 
11724  ast_str_reset(str);
11725 
11726  /* Substitute strings \r, \n, and \t into the appropriate characters */
11727  for (current = (char *) value; *current; current++) {
11728  if (*current == '\\') {
11729  current++;
11730  if (!*current) {
11731  ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
11732  break;
11733  }
11734  switch (*current) {
11735  case '\\':
11736  ast_str_append(&str, 0, "\\");
11737  break;
11738  case 'r':
11739  ast_str_append(&str, 0, "\r");
11740  break;
11741  case 'n':
11742 #ifdef IMAP_STORAGE
11743  if (!str->used || str->str[str->used - 1] != '\r') {
11744  ast_str_append(&str, 0, "\r");
11745  }
11746 #endif
11747  ast_str_append(&str, 0, "\n");
11748  break;
11749  case 't':
11750  ast_str_append(&str, 0, "\t");
11751  break;
11752  default:
11753  ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
11754  break;
11755  }
11756  } else {
11757  ast_str_append(&str, 0, "%c", *current);
11758  }
11759  }
11760 
11761  return ast_str_buffer(str);
11762 }
11763 
11764 static int load_config(int reload)
11765 {
11766  struct ast_config *cfg, *ucfg;
11767  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
11768  int res;
11769 
11770  ast_unload_realtime("voicemail");
11771  ast_unload_realtime("voicemail_data");
11772 
11773  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11774  if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
11775  return 0;
11776  } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
11777  ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
11778  ucfg = NULL;
11779  }
11780  ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11781  if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
11782  ast_config_destroy(ucfg);
11783  ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
11784  return 0;
11785  }
11786  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
11787  ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
11788  return 0;
11789  } else {
11790  ast_clear_flag(&config_flags, CONFIG_FLAG_FILEUNCHANGED);
11791  if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
11792  ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
11793  ucfg = NULL;
11794  }
11795  }
11796 
11797  res = actual_load_config(reload, cfg, ucfg);
11798 
11799  ast_config_destroy(cfg);
11800  ast_config_destroy(ucfg);
11801 
11802  return res;
11803 }
11804 
11805 #ifdef TEST_FRAMEWORK
11806 static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11807 {
11808  ast_unload_realtime("voicemail");
11809  ast_unload_realtime("voicemail_data");
11810  return actual_load_config(reload, cfg, ucfg);
11811 }
11812 #endif
11813 
11814 static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
11815 {
11816  struct ast_vm_user *current;
11817  char *cat;
11818  struct ast_variable *var;
11819  const char *val;
11820  char *q, *stringp, *tmp;
11821  int x;
11822  unsigned int tmpadsi[4];
11823  char secretfn[PATH_MAX] = "";
11824 
11825 #ifdef IMAP_STORAGE
11826  ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
11827 #endif
11828  /* set audio control prompts */
11829  strcpy(listen_control_forward_key, DEFAULT_LISTEN_CONTROL_FORWARD_KEY);
11830  strcpy(listen_control_reverse_key, DEFAULT_LISTEN_CONTROL_REVERSE_KEY);
11831  strcpy(listen_control_pause_key, DEFAULT_LISTEN_CONTROL_PAUSE_KEY);
11832  strcpy(listen_control_restart_key, DEFAULT_LISTEN_CONTROL_RESTART_KEY);
11833  strcpy(listen_control_stop_key, DEFAULT_LISTEN_CONTROL_STOP_KEY);
11834 
11835  /* Free all the users structure */
11836  free_vm_users();
11837 
11838  /* Free all the zones structure */
11839  free_vm_zones();
11840 
11841  AST_LIST_LOCK(&users);
11842 
11843  memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
11844  memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
11845 
11846  if (cfg) {
11847  /* General settings */
11848 
11849  if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
11850  val = "default";
11851  ast_copy_string(userscontext, val, sizeof(userscontext));
11852  /* Attach voice message to mail message ? */
11853  if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
11854  val = "yes";
11855  ast_set2_flag((&globalflags), ast_true(val), VM_ATTACH);
11856 
11857  if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
11858  val = "no";
11859  ast_set2_flag((&globalflags), ast_true(val), VM_SEARCH);
11860 
11861  volgain = 0.0;
11862  if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
11863  sscanf(val, "%30lf", &volgain);
11864 
11865 #ifdef ODBC_STORAGE
11866  strcpy(odbc_database, "asterisk");
11867  if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
11868  ast_copy_string(odbc_database, val, sizeof(odbc_database));
11869  }
11870  strcpy(odbc_table, "voicemessages");
11871  if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
11872  ast_copy_string(odbc_table, val, sizeof(odbc_table));
11873  }
11874 #endif
11875  /* Mail command */
11876  strcpy(mailcmd, SENDMAIL);
11877  if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
11878  ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
11879 
11880  maxsilence = 0;
11881  if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
11882  maxsilence = atoi(val);
11883  if (maxsilence > 0)
11884  maxsilence *= 1000;
11885  }
11886 
11887  if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
11888  maxmsg = MAXMSG;
11889  } else {
11890  maxmsg = atoi(val);
11891  if (maxmsg < 0) {
11892  ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
11893  maxmsg = MAXMSG;
11894  } else if (maxmsg > MAXMSGLIMIT) {
11895  ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11896  maxmsg = MAXMSGLIMIT;
11897  }
11898  }
11899 
11900  if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
11901  maxdeletedmsg = 0;
11902  } else {
11903  if (sscanf(val, "%30d", &x) == 1)
11904  maxdeletedmsg = x;
11905  else if (ast_true(val))
11906  maxdeletedmsg = MAXMSG;
11907  else
11908  maxdeletedmsg = 0;
11909 
11910  if (maxdeletedmsg < 0) {
11911  ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
11912  maxdeletedmsg = MAXMSG;
11913  } else if (maxdeletedmsg > MAXMSGLIMIT) {
11914  ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
11915  maxdeletedmsg = MAXMSGLIMIT;
11916  }
11917  }
11918 
11919  /* Load date format config for voicemail mail */
11920  if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
11921  ast_copy_string(emaildateformat, val, sizeof(emaildateformat));
11922  }
11923 
11924  /* Load date format config for voicemail pager mail */
11925  if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
11926  ast_copy_string(pagerdateformat, val, sizeof(pagerdateformat));
11927  }
11928 
11929  /* External password changing command */
11930  if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
11931  ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11932  pwdchange = PWDCHANGE_EXTERNAL;
11933  } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
11934  ast_copy_string(ext_pass_cmd, val, sizeof(ext_pass_cmd));
11935  pwdchange = PWDCHANGE_EXTERNAL | PWDCHANGE_INTERNAL;
11936  }
11937 
11938  /* External password validation command */
11939  if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
11940  ast_copy_string(ext_pass_check_cmd, val, sizeof(ext_pass_check_cmd));
11941  ast_log(AST_LOG_DEBUG, "found externpasscheck: %s\n", ext_pass_check_cmd);
11942  }
11943 
11944 #ifdef IMAP_STORAGE
11945  /* IMAP server address */
11946  if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
11947  ast_copy_string(imapserver, val, sizeof(imapserver));
11948  } else {
11949  ast_copy_string(imapserver, "localhost", sizeof(imapserver));
11950  }
11951  /* IMAP server port */
11952  if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
11953  ast_copy_string(imapport, val, sizeof(imapport));
11954  } else {
11955  ast_copy_string(imapport, "143", sizeof(imapport));
11956  }
11957  /* IMAP server flags */
11958  if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
11959  ast_copy_string(imapflags, val, sizeof(imapflags));
11960  }
11961  /* IMAP server master username */
11962  if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
11963  ast_copy_string(authuser, val, sizeof(authuser));
11964  }
11965  /* IMAP server master password */
11966  if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
11967  ast_copy_string(authpassword, val, sizeof(authpassword));
11968  }
11969  /* Expunge on exit */
11970  if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
11971  if (ast_false(val))
11972  expungeonhangup = 0;
11973  else
11974  expungeonhangup = 1;
11975  } else {
11976  expungeonhangup = 1;
11977  }
11978  /* IMAP voicemail folder */
11979  if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
11980  ast_copy_string(imapfolder, val, sizeof(imapfolder));
11981  } else {
11982  ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
11983  }
11984  if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
11985  ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
11986  }
11987  if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
11988  imapgreetings = ast_true(val);
11989  } else {
11990  imapgreetings = 0;
11991  }
11992  if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
11993  ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11994  } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
11995  /* Also support greetingsfolder as documented in voicemail.conf.sample */
11996  ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
11997  } else {
11998  ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
11999  }
12000 
12001  /* There is some very unorthodox casting done here. This is due
12002  * to the way c-client handles the argument passed in. It expects a
12003  * void pointer and casts the pointer directly to a long without
12004  * first dereferencing it. */
12005  if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
12006  mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
12007  } else {
12008  mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
12009  }
12010 
12011  if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
12012  mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
12013  } else {
12014  mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
12015  }
12016 
12017  if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
12018  mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
12019  } else {
12020  mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
12021  }
12022 
12023  if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
12024  mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
12025  } else {
12026  mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
12027  }
12028 
12029  /* Increment configuration version */
12030  imapversion++;
12031 #endif
12032  /* External voicemail notify application */
12033  if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
12034  ast_copy_string(externnotify, val, sizeof(externnotify));
12035  ast_debug(1, "found externnotify: %s\n", externnotify);
12036  } else {
12037  externnotify[0] = '\0';
12038  }
12039 
12040  /* SMDI voicemail notification */
12041  if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
12042  ast_debug(1, "Enabled SMDI voicemail notification\n");
12043  if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
12044  smdi_iface = ast_smdi_interface_find(val);
12045  } else {
12046  ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
12047  smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
12048  }
12049  if (!smdi_iface) {
12050  ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
12051  }
12052  }
12053 
12054  /* Silence treshold */
12056  if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
12057  silencethreshold = atoi(val);
12058 
12059  if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
12060  val = ASTERISK_USERNAME;
12061  ast_copy_string(serveremail, val, sizeof(serveremail));
12062 
12063  vmmaxsecs = 0;
12064  if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
12065  if (sscanf(val, "%30d", &x) == 1) {
12066  vmmaxsecs = x;
12067  } else {
12068  ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12069  }
12070  } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
12071  static int maxmessage_deprecate = 0;
12072  if (maxmessage_deprecate == 0) {
12073  maxmessage_deprecate = 1;
12074  ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
12075  }
12076  if (sscanf(val, "%30d", &x) == 1) {
12077  vmmaxsecs = x;
12078  } else {
12079  ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
12080  }
12081  }
12082 
12083  vmminsecs = 0;
12084  if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
12085  if (sscanf(val, "%30d", &x) == 1) {
12086  vmminsecs = x;
12087  if (maxsilence / 1000 >= vmminsecs) {
12088  ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
12089  }
12090  } else {
12091  ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12092  }
12093  } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
12094  static int maxmessage_deprecate = 0;
12095  if (maxmessage_deprecate == 0) {
12096  maxmessage_deprecate = 1;
12097  ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
12098  }
12099  if (sscanf(val, "%30d", &x) == 1) {
12100  vmminsecs = x;
12101  if (maxsilence / 1000 >= vmminsecs) {
12102  ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
12103  }
12104  } else {
12105  ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
12106  }
12107  }
12108 
12109  val = ast_variable_retrieve(cfg, "general", "format");
12110  if (!val) {
12111  val = "wav";
12112  } else {
12113  tmp = ast_strdupa(val);
12114  val = ast_format_str_reduce(tmp);
12115  if (!val) {
12116  ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
12117  val = "wav";
12118  }
12119  }
12120  ast_copy_string(vmfmts, val, sizeof(vmfmts));
12121 
12122  skipms = 3000;
12123  if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
12124  if (sscanf(val, "%30d", &x) == 1) {
12125  maxgreet = x;
12126  } else {
12127  ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
12128  }
12129  }
12130 
12131  if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
12132  if (sscanf(val, "%30d", &x) == 1) {
12133  skipms = x;
12134  } else {
12135  ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
12136  }
12137  }
12138 
12139  maxlogins = 3;
12140  if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
12141  if (sscanf(val, "%30d", &x) == 1) {
12142  maxlogins = x;
12143  } else {
12144  ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
12145  }
12146  }
12147 
12148  minpassword = MINPASSWORD;
12149  if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
12150  if (sscanf(val, "%30d", &x) == 1) {
12151  minpassword = x;
12152  } else {
12153  ast_log(AST_LOG_WARNING, "Invalid minimum password length. Default to %d\n", minpassword);
12154  }
12155  }
12156 
12157  /* Force new user to record name ? */
12158  if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
12159  val = "no";
12160  ast_set2_flag((&globalflags), ast_true(val), VM_FORCENAME);
12161 
12162  /* Force new user to record greetings ? */
12163  if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
12164  val = "no";
12165  ast_set2_flag((&globalflags), ast_true(val), VM_FORCEGREET);
12166 
12167  if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
12168  ast_debug(1, "VM_CID Internal context string: %s\n", val);
12169  stringp = ast_strdupa(val);
12170  for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
12171  if (!ast_strlen_zero(stringp)) {
12172  q = strsep(&stringp, ",");
12173  while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
12174  q++;
12175  ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
12176  ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
12177  } else {
12178  cidinternalcontexts[x][0] = '\0';
12179  }
12180  }
12181  }
12182  if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
12183  ast_debug(1, "VM Review Option disabled globally\n");
12184  val = "no";
12185  }
12186  ast_set2_flag((&globalflags), ast_true(val), VM_REVIEW);
12187 
12188  /* Temporary greeting reminder */
12189  if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
12190  ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
12191  val = "no";
12192  } else {
12193  ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
12194  }
12195  ast_set2_flag((&globalflags), ast_true(val), VM_TEMPGREETWARN);
12196  if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
12197  ast_debug(1, "VM next message wrap disabled globally\n");
12198  val = "no";
12199  }
12200  ast_set2_flag((&globalflags), ast_true(val), VM_MESSAGEWRAP);
12201 
12202  if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
12203  ast_debug(1, "VM Operator break disabled globally\n");
12204  val = "no";
12205  }
12206  ast_set2_flag((&globalflags), ast_true(val), VM_OPERATOR);
12207 
12208  if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
12209  ast_debug(1, "VM CID Info before msg disabled globally\n");
12210  val = "no";
12211  }
12212  ast_set2_flag((&globalflags), ast_true(val), VM_SAYCID);
12213 
12214  if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
12215  ast_debug(1, "Send Voicemail msg disabled globally\n");
12216  val = "no";
12217  }
12218  ast_set2_flag((&globalflags), ast_true(val), VM_SVMAIL);
12219 
12220  if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
12221  ast_debug(1, "ENVELOPE before msg enabled globally\n");
12222  val = "yes";
12223  }
12224  ast_set2_flag((&globalflags), ast_true(val), VM_ENVELOPE);
12225 
12226  if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
12227  ast_debug(1, "Move Heard enabled globally\n");
12228  val = "yes";
12229  }
12230  ast_set2_flag((&globalflags), ast_true(val), VM_MOVEHEARD);
12231 
12232  if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
12233  ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
12234  val = "no";
12235  }
12236  ast_set2_flag((&globalflags), ast_true(val), VM_FWDURGAUTO);
12237 
12238  if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
12239  ast_debug(1, "Duration info before msg enabled globally\n");
12240  val = "yes";
12241  }
12242  ast_set2_flag((&globalflags), ast_true(val), VM_SAYDURATION);
12243 
12244  saydurationminfo = 2;
12245  if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
12246  if (sscanf(val, "%30d", &x) == 1) {
12247  saydurationminfo = x;
12248  } else {
12249  ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
12250  }
12251  }
12252 
12253  if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
12254  ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
12255  val = "no";
12256  }
12257  ast_set2_flag((&globalflags), ast_true(val), VM_SKIPAFTERCMD);
12258 
12259  if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
12260  ast_copy_string(dialcontext, val, sizeof(dialcontext));
12261  ast_debug(1, "found dialout context: %s\n", dialcontext);
12262  } else {
12263  dialcontext[0] = '\0';
12264  }
12265 
12266  if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
12267  ast_copy_string(callcontext, val, sizeof(callcontext));
12268  ast_debug(1, "found callback context: %s\n", callcontext);
12269  } else {
12270  callcontext[0] = '\0';
12271  }
12272 
12273  if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
12274  ast_copy_string(exitcontext, val, sizeof(exitcontext));
12275  ast_debug(1, "found operator context: %s\n", exitcontext);
12276  } else {
12277  exitcontext[0] = '\0';
12278  }
12279 
12280  /* load password sounds configuration */
12281  if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
12282  ast_copy_string(vm_password, val, sizeof(vm_password));
12283  if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
12284  ast_copy_string(vm_newpassword, val, sizeof(vm_newpassword));
12285  if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
12286  ast_copy_string(vm_invalid_password, val, sizeof(vm_invalid_password));
12287  if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
12288  ast_copy_string(vm_passchanged, val, sizeof(vm_passchanged));
12289  if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
12290  ast_copy_string(vm_reenterpassword, val, sizeof(vm_reenterpassword));
12291  if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
12292  ast_copy_string(vm_mismatch, val, sizeof(vm_mismatch));
12293  if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
12294  ast_copy_string(vm_pls_try_again, val, sizeof(vm_pls_try_again));
12295  }
12296  if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
12297  ast_copy_string(vm_prepend_timeout, val, sizeof(vm_prepend_timeout));
12298  }
12299  /* load configurable audio prompts */
12300  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
12301  ast_copy_string(listen_control_forward_key, val, sizeof(listen_control_forward_key));
12302  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
12303  ast_copy_string(listen_control_reverse_key, val, sizeof(listen_control_reverse_key));
12304  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
12305  ast_copy_string(listen_control_pause_key, val, sizeof(listen_control_pause_key));
12306  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
12307  ast_copy_string(listen_control_restart_key, val, sizeof(listen_control_restart_key));
12308  if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
12309  ast_copy_string(listen_control_stop_key, val, sizeof(listen_control_stop_key));
12310 
12311  if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
12312  val = "no";
12313  ast_set2_flag((&globalflags), ast_true(val), VM_DIRECFORWARD);
12314 
12315  if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
12316  val = "voicemail.conf";
12317  }
12318  if (!(strcmp(val, "spooldir"))) {
12319  passwordlocation = OPT_PWLOC_SPOOLDIR;
12320  } else {
12321  passwordlocation = OPT_PWLOC_VOICEMAILCONF;
12322  }
12323 
12324  poll_freq = DEFAULT_POLL_FREQ;
12325  if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
12326  if (sscanf(val, "%30u", &poll_freq) != 1) {
12327  poll_freq = DEFAULT_POLL_FREQ;
12328  ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
12329  }
12330  }
12331 
12332  poll_mailboxes = 0;
12333  if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
12334  poll_mailboxes = ast_true(val);
12335 
12336  memset(fromstring, 0, sizeof(fromstring));
12337  memset(pagerfromstring, 0, sizeof(pagerfromstring));
12338  strcpy(charset, "ISO-8859-1");
12339  if (emailbody) {
12340  ast_free(emailbody);
12341  emailbody = NULL;
12342  }
12343  if (emailsubject) {
12344  ast_free(emailsubject);
12345  emailsubject = NULL;
12346  }
12347  if (pagerbody) {
12348  ast_free(pagerbody);
12349  pagerbody = NULL;
12350  }
12351  if (pagersubject) {
12352  ast_free(pagersubject);
12353  pagersubject = NULL;
12354  }
12355  if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
12356  ast_set2_flag((&globalflags), ast_true(val), VM_PBXSKIP);
12357  if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
12358  ast_copy_string(fromstring, val, sizeof(fromstring));
12359  if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
12360  ast_copy_string(pagerfromstring, val, sizeof(pagerfromstring));
12361  if ((val = ast_variable_retrieve(cfg, "general", "charset")))
12362  ast_copy_string(charset, val, sizeof(charset));
12363  if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
12364  sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12365  for (x = 0; x < 4; x++) {
12366  memcpy(&adsifdn[x], &tmpadsi[x], 1);
12367  }
12368  }
12369  if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
12370  sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
12371  for (x = 0; x < 4; x++) {
12372  memcpy(&adsisec[x], &tmpadsi[x], 1);
12373  }
12374  }
12375  if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
12376  if (atoi(val)) {
12377  adsiver = atoi(val);
12378  }
12379  }
12380  if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
12381  ast_copy_string(zonetag, val, sizeof(zonetag));
12382  }
12383  if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
12384  ast_copy_string(locale, val, sizeof(locale));
12385  }
12386  if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
12387  emailsubject = ast_strdup(substitute_escapes(val));
12388  }
12389  if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
12390  emailbody = ast_strdup(substitute_escapes(val));
12391  }
12392  if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
12393  pagersubject = ast_strdup(substitute_escapes(val));
12394  }
12395  if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
12396  pagerbody = ast_strdup(substitute_escapes(val));
12397  }
12398 
12399  /* load mailboxes from users.conf */
12400  if (ucfg) {
12401  for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
12402  if (!strcasecmp(cat, "general")) {
12403  continue;
12404  }
12405  if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
12406  continue;
12407  if ((current = find_or_create(userscontext, cat))) {
12408  populate_defaults(current);
12409  apply_options_full(current, ast_variable_browse(ucfg, cat));
12410  ast_copy_string(current->context, userscontext, sizeof(current->context));
12411  if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
12413  }
12414 
12415  switch (current->passwordlocation) {
12416  case OPT_PWLOC_SPOOLDIR:
12417  snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
12418  read_password_from_file(secretfn, current->password, sizeof(current->password));
12419  }
12420  }
12421  }
12422  }
12423 
12424  /* load mailboxes from voicemail.conf */
12425  cat = ast_category_browse(cfg, NULL);
12426  while (cat) {
12427  if (strcasecmp(cat, "general")) {
12428  var = ast_variable_browse(cfg, cat);
12429  if (strcasecmp(cat, "zonemessages")) {
12430  /* Process mailboxes in this context */
12431  while (var) {
12432  append_mailbox(cat, var->name, var->value);
12433  var = var->next;
12434  }
12435  } else {
12436  /* Timezones in this context */
12437  while (var) {
12438  struct vm_zone *z;
12439  if ((z = ast_malloc(sizeof(*z)))) {
12440  char *msg_format, *tzone;
12441  msg_format = ast_strdupa(var->value);
12442  tzone = strsep(&msg_format, "|,");
12443  if (msg_format) {
12444  ast_copy_string(z->name, var->name, sizeof(z->name));
12445  ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
12446  ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
12447  AST_LIST_LOCK(&zones);
12448  AST_LIST_INSERT_HEAD(&zones, z, list);
12450  } else {
12451  ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
12452  ast_free(z);
12453  }
12454  } else {
12456  return -1;
12457  }
12458  var = var->next;
12459  }
12460  }
12461  }
12462  cat = ast_category_browse(cfg, cat);
12463  }
12464 
12466 
12467  if (poll_mailboxes && poll_thread == AST_PTHREADT_NULL)
12469  if (!poll_mailboxes && poll_thread != AST_PTHREADT_NULL)
12470  stop_poll_thread();;
12471 
12472  return 0;
12473  } else {
12475  ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
12476  return 0;
12477  }
12478 }
12479 
12480 static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
12481 {
12482  int res = -1;
12483  char dir[PATH_MAX];
12484  snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
12485  ast_debug(2, "About to try retrieving name file %s\n", dir);
12486  RETRIEVE(dir, -1, mailbox, context);
12487  if (ast_fileexists(dir, NULL, NULL)) {
12488  res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
12489  }
12490  DISPOSE(dir, -1);
12491  return res;
12492 }
12493 
12494 static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
12495  struct ast_config *pwconf;
12496  struct ast_flags config_flags = { 0 };
12497 
12498  pwconf = ast_config_load(secretfn, config_flags);
12499  if (valid_config(pwconf)) {
12500  const char *val = ast_variable_retrieve(pwconf, "general", "password");
12501  if (val) {
12502  ast_copy_string(password, val, passwordlen);
12503  ast_config_destroy(pwconf);
12504  return;
12505  }
12506  ast_config_destroy(pwconf);
12507  }
12508  ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
12509 }
12510 
12511 static int write_password_to_file(const char *secretfn, const char *password) {
12512  struct ast_config *conf;
12513  struct ast_category *cat;
12514  struct ast_variable *var;
12515  int res = -1;
12516 
12517  if (!(conf = ast_config_new())) {
12518  ast_log(LOG_ERROR, "Error creating new config structure\n");
12519  return res;
12520  }
12521  if (!(cat = ast_category_new("general", "", 1))) {
12522  ast_log(LOG_ERROR, "Error creating new category structure\n");
12523  ast_config_destroy(conf);
12524  return res;
12525  }
12526  if (!(var = ast_variable_new("password", password, ""))) {
12527  ast_log(LOG_ERROR, "Error creating new variable structure\n");
12528  ast_config_destroy(conf);
12529  ast_category_destroy(cat);
12530  return res;
12531  }
12532  ast_category_append(conf, cat);
12533  ast_variable_append(cat, var);
12534  if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
12535  res = 0;
12536  } else {
12537  ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
12538  }
12539 
12540  ast_config_destroy(conf);
12541  return res;
12542 }
12543 
12544 static int vmsayname_exec(struct ast_channel *chan, const char *data)
12545 {
12546  char *context;
12547  char *args_copy;
12548  int res;
12549 
12550  if (ast_strlen_zero(data)) {
12551  ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
12552  return -1;
12553  }
12554 
12555  args_copy = ast_strdupa(data);
12556  if ((context = strchr(args_copy, '@'))) {
12557  *context++ = '\0';
12558  } else {
12559  context = "default";
12560  }
12561 
12562  if ((res = sayname(chan, args_copy, context) < 0)) {
12563  ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", args_copy, context);
12564  res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
12565  if (!res) {
12566  res = ast_say_character_str(chan, args_copy, AST_DIGIT_ANY, chan->language);
12567  }
12568  }
12569 
12570  return res;
12571 }
12572 
12573 #ifdef TEST_FRAMEWORK
12574 static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
12575 {
12576  return 0;
12577 }
12578 
12579 static struct ast_frame *fake_read(struct ast_channel *ast)
12580 {
12581  return &ast_null_frame;
12582 }
12583 
12584 AST_TEST_DEFINE(test_voicemail_vmsayname)
12585 {
12586  char dir[PATH_MAX];
12587  char dir2[PATH_MAX];
12588  static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
12589  static const char TEST_EXTENSION[] = "1234";
12590 
12591  struct ast_channel *test_channel1 = NULL;
12592  int res = -1;
12593 
12594  static const struct ast_channel_tech fake_tech = {
12595  .write = fake_write,
12596  .read = fake_read,
12597  };
12598 
12599  switch (cmd) {
12600  case TEST_INIT:
12601  info->name = "vmsayname_exec";
12602  info->category = "/apps/app_voicemail/";
12603  info->summary = "Vmsayname unit test";
12604  info->description =
12605  "This tests passing various parameters to vmsayname";
12606  return AST_TEST_NOT_RUN;
12607  case TEST_EXECUTE:
12608  break;
12609  }
12610 
12611  if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL,
12612  NULL, NULL, 0, 0, "TestChannel1"))) {
12613  goto exit_vmsayname_test;
12614  }
12615 
12616  /* normally this is done in the channel driver */
12617  test_channel1->nativeformats = AST_FORMAT_GSM;
12618  test_channel1->writeformat = AST_FORMAT_GSM;
12619  test_channel1->rawwriteformat = AST_FORMAT_GSM;
12620  test_channel1->readformat = AST_FORMAT_GSM;
12621  test_channel1->rawreadformat = AST_FORMAT_GSM;
12622  test_channel1->tech = &fake_tech;
12623 
12624  ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
12625  snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12626  if (!(res = vmsayname_exec(test_channel1, dir))) {
12627  snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12628  if (ast_fileexists(dir, NULL, NULL)) {
12629  ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
12630  res = -1;
12631  goto exit_vmsayname_test;
12632  } else {
12633  /* no greeting already exists as expected, let's create one to fully test sayname */
12634  if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
12635  ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
12636  goto exit_vmsayname_test;
12637  }
12638  snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_VAR_DIR);
12639  snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12640  /* we're not going to hear the sound anyway, just use a valid gsm audio file */
12641  if ((res = symlink(dir, dir2))) {
12642  ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
12643  goto exit_vmsayname_test;
12644  }
12645  ast_test_status_update(test, "Test playing created mailbox greeting...\n");
12646  snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
12647  res = vmsayname_exec(test_channel1, dir);
12648 
12649  /* TODO: there may be a better way to do this */
12650  unlink(dir2);
12651  snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
12652  rmdir(dir2);
12653  snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
12654  rmdir(dir2);
12655  }
12656  }
12657 
12658 exit_vmsayname_test:
12659 
12660  if (test_channel1) {
12661  ast_hangup(test_channel1);
12662  }
12663 
12664  return res ? AST_TEST_FAIL : AST_TEST_PASS;
12665 }
12666 
12667 AST_TEST_DEFINE(test_voicemail_msgcount)
12668 {
12669  int i, j, res = AST_TEST_PASS, syserr;
12670  struct ast_vm_user *vmu;
12671  struct ast_vm_user svm;
12672  struct vm_state vms;
12673 #ifdef IMAP_STORAGE
12674  struct ast_channel *chan = NULL;
12675 #endif
12676  struct {
12677  char dir[256];
12678  char file[256];
12679  char txtfile[256];
12680  } tmp[3];
12681  char syscmd[256];
12682  const char origweasels[] = "tt-weasels";
12683  const char testcontext[] = "test";
12684  const char testmailbox[] = "00000000";
12685  const char testspec[] = "00000000@test";
12686  FILE *txt;
12687  int new, old, urgent;
12688  const char *folders[3] = { "Old", "Urgent", "INBOX" };
12689  const int folder2mbox[3] = { 1, 11, 0 };
12690  const int expected_results[3][12] = {
12691  /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12692  { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
12693  { 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
12694  { 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 2 },
12695  };
12696 
12697  switch (cmd) {
12698  case TEST_INIT:
12699  info->name = "test_voicemail_msgcount";
12700  info->category = "/apps/app_voicemail/";
12701  info->summary = "Test Voicemail status checks";
12702  info->description =
12703  "Verify that message counts are correct when retrieved through the public API";
12704  return AST_TEST_NOT_RUN;
12705  case TEST_EXECUTE:
12706  break;
12707  }
12708 
12709  /* Make sure the original path was completely empty */
12710  snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12711  if ((syserr = ast_safe_system(syscmd))) {
12712  ast_test_status_update(test, "Unable to clear test directory: %s\n",
12713  syserr > 0 ? strerror(syserr) : "unable to fork()");
12714  return AST_TEST_FAIL;
12715  }
12716 
12717 #ifdef IMAP_STORAGE
12718  if (!(chan = ast_dummy_channel_alloc())) {
12719  ast_test_status_update(test, "Unable to create dummy channel\n");
12720  return AST_TEST_FAIL;
12721  }
12722 #endif
12723 
12724  if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
12725  !(vmu = find_or_create(testcontext, testmailbox))) {
12726  ast_test_status_update(test, "Cannot create vmu structure\n");
12728 #ifdef IMAP_STORAGE
12729  chan = ast_channel_unref(chan);
12730 #endif
12731  return AST_TEST_FAIL;
12732  }
12733 
12734  populate_defaults(vmu);
12735  memset(&vms, 0, sizeof(vms));
12736 
12737  /* Create temporary voicemail */
12738  for (i = 0; i < 3; i++) {
12739  create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
12740  make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
12741  snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
12742 
12743  if (ast_fileexists(origweasels, "gsm", "en") > 0) {
12744  snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
12745  VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
12746  if ((syserr = ast_safe_system(syscmd))) {
12747  ast_test_status_update(test, "Unable to create test voicemail: %s\n",
12748  syserr > 0 ? strerror(syserr) : "unable to fork()");
12750 #ifdef IMAP_STORAGE
12751  chan = ast_channel_unref(chan);
12752 #endif
12753  return AST_TEST_FAIL;
12754  }
12755  }
12756 
12757  if ((txt = fopen(tmp[i].txtfile, "w+"))) {
12758  fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
12759  fclose(txt);
12760  } else {
12761  ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
12762  res = AST_TEST_FAIL;
12763  break;
12764  }
12765  open_mailbox(&vms, vmu, folder2mbox[i]);
12766  STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent");
12767 
12768  /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
12769  for (j = 0; j < 3; j++) {
12770  /* folder[2] is INBOX, __has_voicemail will default back to INBOX */
12771  if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
12772  ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
12773  testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
12774  res = AST_TEST_FAIL;
12775  }
12776  }
12777 
12778  new = old = urgent = 0;
12779  if (ast_app_inboxcount(testspec, &new, &old)) {
12780  ast_test_status_update(test, "inboxcount returned failure\n");
12781  res = AST_TEST_FAIL;
12782  } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
12783  ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
12784  testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
12785  res = AST_TEST_FAIL;
12786  }
12787 
12788  new = old = urgent = 0;
12789  if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
12790  ast_test_status_update(test, "inboxcount2 returned failure\n");
12791  res = AST_TEST_FAIL;
12792  } else if (old != expected_results[i][6 + 0] ||
12793  urgent != expected_results[i][6 + 1] ||
12794  new != expected_results[i][6 + 2] ) {
12795  ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
12796  testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
12797  res = AST_TEST_FAIL;
12798  }
12799 
12800  new = old = urgent = 0;
12801  for (j = 0; j < 3; j++) {
12802  if (ast_app_messagecount(testcontext, testmailbox, folders[j]) != expected_results[i][9 + j]) {
12803  ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
12804  testspec, folders[j], ast_app_messagecount(testcontext, testmailbox, folders[j]), expected_results[i][9 + j]);
12805  res = AST_TEST_FAIL;
12806  }
12807  }
12808  }
12809 
12810  for (i = 0; i < 3; i++) {
12811  /* This is necessary if the voicemails are stored on an ODBC/IMAP
12812  * server, in which case, the rm below will not affect the
12813  * voicemails. */
12814  DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
12815  DISPOSE(tmp[i].dir, 0);
12816  }
12817 
12818  if (vms.deleted) {
12819  ast_free(vms.deleted);
12820  }
12821  if (vms.heard) {
12822  ast_free(vms.heard);
12823  }
12824 
12825 #ifdef IMAP_STORAGE
12826  chan = ast_channel_unref(chan);
12827 #endif
12828 
12829  /* And remove test directory */
12830  snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
12831  if ((syserr = ast_safe_system(syscmd))) {
12832  ast_test_status_update(test, "Unable to clear test directory: %s\n",
12833  syserr > 0 ? strerror(syserr) : "unable to fork()");
12834  }
12835 
12836  return res;
12837 }
12838 
12839 AST_TEST_DEFINE(test_voicemail_notify_endl)
12840 {
12841  int res = AST_TEST_PASS;
12842  char testcontext[] = "test";
12843  char testmailbox[] = "00000000";
12844  char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
12845  char attach[256], attach2[256];
12846  char buf[256] = ""; /* No line should actually be longer than 80 */
12847  struct ast_channel *chan = NULL;
12848  struct ast_vm_user *vmu, vmus = {
12849  .flags = 0,
12850  };
12851  FILE *file;
12852  struct {
12853  char *name;
12854  enum { INT, FLAGVAL, STATIC, STRPTR } type;
12855  void *location;
12856  union {
12857  int intval;
12858  char *strval;
12859  } u;
12860  } test_items[] = {
12861  { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
12862  { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
12863  { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
12864  { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
12865  { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
12866  { "attach2", STRPTR, attach2, .u.strval = "" },
12867  { "attach", STRPTR, attach, .u.strval = "" },
12868  };
12869  int which;
12870 
12871  switch (cmd) {
12872  case TEST_INIT:
12873  info->name = "test_voicemail_notify_endl";
12874  info->category = "/apps/app_voicemail/";
12875  info->summary = "Test Voicemail notification end-of-line";
12876  info->description =
12877  "Verify that notification emails use a consistent end-of-line character";
12878  return AST_TEST_NOT_RUN;
12879  case TEST_EXECUTE:
12880  break;
12881  }
12882 
12883  snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_VAR_DIR);
12884  snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_VAR_DIR);
12885 
12886  if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
12887  !(vmu = find_or_create(testcontext, testmailbox))) {
12888  ast_test_status_update(test, "Cannot create vmu structure\n");
12889  return AST_TEST_NOT_RUN;
12890  }
12891 
12892  if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
12893  ast_test_status_update(test, "Cannot find vmu structure?!!\n");
12894  return AST_TEST_NOT_RUN;
12895  }
12896 
12897  populate_defaults(vmu);
12898  ast_copy_string(vmu->email, "test2@example.net", sizeof(vmu->email));
12899 #ifdef IMAP_STORAGE
12900  /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
12901 #endif
12902 
12903  file = tmpfile();
12904  for (which = 0; which < ARRAY_LEN(test_items); which++) {
12905  /* Kill previous test, if any */
12906  rewind(file);
12907  if (ftruncate(fileno(file), 0)) {
12908  ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
12909  res = AST_TEST_FAIL;
12910  break;
12911  }
12912 
12913  /* Make each change, in order, to the test mailbox */
12914  if (test_items[which].type == INT) {
12915  *((int *) test_items[which].location) = test_items[which].u.intval;
12916  } else if (test_items[which].type == FLAGVAL) {
12917  if (ast_test_flag(vmu, test_items[which].u.intval)) {
12918  ast_clear_flag(vmu, test_items[which].u.intval);
12919  } else {
12920  ast_set_flag(vmu, test_items[which].u.intval);
12921  }
12922  } else if (test_items[which].type == STATIC) {
12923  strcpy(test_items[which].location, test_items[which].u.strval);
12924  } else if (test_items[which].type == STRPTR) {
12925  test_items[which].location = test_items[which].u.strval;
12926  }
12927 
12928  make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL);
12929  rewind(file);
12930  while (fgets(buf, sizeof(buf), file)) {
12931  if (
12932 #ifdef IMAP_STORAGE
12933  buf[strlen(buf) - 2] != '\r'
12934 #else
12935  buf[strlen(buf) - 2] == '\r'
12936 #endif
12937  || buf[strlen(buf) - 1] != '\n') {
12938  res = AST_TEST_FAIL;
12939  }
12940  }
12941  }
12942  fclose(file);
12943  return res;
12944 }
12945 
12946 AST_TEST_DEFINE(test_voicemail_load_config)
12947 {
12948  int res = AST_TEST_PASS;
12949  struct ast_vm_user *vmu;
12950  struct ast_config *cfg;
12951  char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
12952  int fd;
12953  FILE *file;
12954  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
12955 
12956  switch (cmd) {
12957  case TEST_INIT:
12958  info->name = "test_voicemail_load_config";
12959  info->category = "/apps/app_voicemail/";
12960  info->summary = "Test loading Voicemail config";
12961  info->description =
12962  "Verify that configuration is loaded consistently. "
12963  "This is to test regressions of ASTERISK-18838 where it was noticed that "
12964  "some options were loaded after the mailboxes were instantiated, causing "
12965  "those options not to be set correctly.";
12966  return AST_TEST_NOT_RUN;
12967  case TEST_EXECUTE:
12968  break;
12969  }
12970 
12971  /* build a config file by hand... */
12972  if ((fd = mkstemp(config_filename)) < 0) {
12973  return AST_TEST_FAIL;
12974  }
12975  if (!(file = fdopen(fd, "w"))) {
12976  close(fd);
12977  unlink(config_filename);
12978  return AST_TEST_FAIL;
12979  }
12980  fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
12981  fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
12982  fputs("00000002 => 9999,Mrs. Test\n", file);
12983  fclose(file);
12984 
12985  if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
12986  res = AST_TEST_FAIL;
12987  goto cleanup;
12988  }
12989 
12990  load_config_from_memory(1, cfg, NULL);
12991  ast_config_destroy(cfg);
12992 
12993 #define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
12994  ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
12995  u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
12996 
12997  AST_LIST_LOCK(&users);
12998  AST_LIST_TRAVERSE(&users, vmu, list) {
12999  if (!strcmp(vmu->mailbox, "00000001")) {
13000  if (0); /* trick to get CHECK to work */
13001  CHECK(vmu, callback, "othercontext")
13002  CHECK(vmu, locale, "nl_NL.UTF-8")
13003  CHECK(vmu, zonetag, "central")
13004  } else if (!strcmp(vmu->mailbox, "00000002")) {
13005  if (0); /* trick to get CHECK to work */
13006  CHECK(vmu, callback, "somecontext")
13007  CHECK(vmu, locale, "de_DE.UTF-8")
13008  CHECK(vmu, zonetag, "european")
13009  }
13010  }
13012 
13013 #undef CHECK
13014 
13015  /* restore config */
13016  load_config(1); /* this might say "Failed to load configuration file." */
13017 
13018 cleanup:
13019  unlink(config_filename);
13020  return res;
13021 }
13022 
13023 #endif /* defined(TEST_FRAMEWORK) */
13024 
13025 static int reload(void)
13026 {
13027  return load_config(1);
13028 }
13029 
13030 static int unload_module(void)
13031 {
13032  int res;
13033 
13034  res = ast_unregister_application(app);
13035  res |= ast_unregister_application(app2);
13036  res |= ast_unregister_application(app3);
13037  res |= ast_unregister_application(app4);
13038  res |= ast_unregister_application(sayname_app);
13039  res |= ast_custom_function_unregister(&mailbox_exists_acf);
13040  res |= ast_manager_unregister("VoicemailUsersList");
13041  res |= ast_data_unregister(NULL);
13042 #ifdef TEST_FRAMEWORK
13043  res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
13044  res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
13045  res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
13046  res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
13047  res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
13048 #endif
13049  ast_cli_unregister_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13051  ao2_ref(inprocess_container, -1);
13052 
13053  if (poll_thread != AST_PTHREADT_NULL)
13054  stop_poll_thread();
13055 
13056  mwi_subscription_tps = ast_taskprocessor_unreference(mwi_subscription_tps);
13057  ast_unload_realtime("voicemail");
13058  ast_unload_realtime("voicemail_data");
13059 
13060  free_vm_users();
13061  free_vm_zones();
13062  return res;
13063 }
13064 
13065 static int load_module(void)
13066 {
13067  int res;
13068  my_umask = umask(0);
13069  umask(my_umask);
13070 
13071  if (!(inprocess_container = ao2_container_alloc(573, inprocess_hash_fn, inprocess_cmp_fn))) {
13072  return AST_MODULE_LOAD_DECLINE;
13073  }
13074 
13075  /* compute the location of the voicemail spool directory */
13076  snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
13077 
13078  if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
13079  ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor. MWI will not work\n");
13080  }
13081 
13082  if ((res = load_config(0)))
13083  return res;
13084 
13089  res |= ast_register_application_xml(sayname_app, vmsayname_exec);
13090  res |= ast_custom_function_register(&mailbox_exists_acf);
13092 #ifdef TEST_FRAMEWORK
13093  res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
13094  res |= AST_TEST_REGISTER(test_voicemail_msgcount);
13095  res |= AST_TEST_REGISTER(test_voicemail_vmuser);
13096  res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
13097  res |= AST_TEST_REGISTER(test_voicemail_load_config);
13098 #endif
13099 
13100  if (res)
13101  return res;
13102 
13103  ast_cli_register_multiple(cli_voicemail, ARRAY_LEN(cli_voicemail));
13104  ast_data_register_multiple(vm_data_providers, ARRAY_LEN(vm_data_providers));
13105 
13107  ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
13108  ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
13109 
13110  return res;
13111 }
13112 
13113 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
13114 {
13115  int cmd = 0;
13116  char destination[80] = "";
13117  int retries = 0;
13118 
13119  if (!num) {
13120  ast_verb(3, "Destination number will be entered manually\n");
13121  while (retries < 3 && cmd != 't') {
13122  destination[1] = '\0';
13123  destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
13124  if (!cmd)
13125  destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
13126  if (!cmd)
13127  destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
13128  if (!cmd) {
13129  cmd = ast_waitfordigit(chan, 6000);
13130  if (cmd)
13131  destination[0] = cmd;
13132  }
13133  if (!cmd) {
13134  retries++;
13135  } else {
13136 
13137  if (cmd < 0)
13138  return 0;
13139  if (cmd == '*') {
13140  ast_verb(3, "User hit '*' to cancel outgoing call\n");
13141  return 0;
13142  }
13143  if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0)
13144  retries++;
13145  else
13146  cmd = 't';
13147  }
13148  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", cmd, cmd);
13149  }
13150  if (retries >= 3) {
13151  return 0;
13152  }
13153 
13154  } else {
13155  if (option_verbose > 2)
13156  ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
13157  ast_copy_string(destination, num, sizeof(destination));
13158  }
13159 
13160  if (!ast_strlen_zero(destination)) {
13161  if (destination[strlen(destination) -1 ] == '*')
13162  return 0;
13163  if (option_verbose > 2)
13164  ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
13165  ast_copy_string(chan->exten, destination, sizeof(chan->exten));
13166  ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
13167  chan->priority = 0;
13168  return 9;
13169  }
13170  return 0;
13171 }
13172 
13173 /*!
13174  * \brief The advanced options within a message.
13175  * \param chan
13176  * \param vmu
13177  * \param vms
13178  * \param msg
13179  * \param option
13180  * \param record_gain
13181  *
13182  * Provides handling for the play message envelope, call the person back, or reply to message.
13183  *
13184  * \return zero on success, -1 on error.
13185  */
13186 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)
13187 {
13188  int res = 0;
13189  char filename[PATH_MAX];
13190  struct ast_config *msg_cfg = NULL;
13191  const char *origtime, *context;
13192  char *name, *num;
13193  int retries = 0;
13194  char *cid;
13195  struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
13196 
13197  vms->starting = 0;
13198 
13199  make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13200 
13201  /* Retrieve info from VM attribute file */
13202  snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
13203  RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
13204  msg_cfg = ast_config_load(filename, config_flags);
13205  DISPOSE(vms->curdir, vms->curmsg);
13206  if (!valid_config(msg_cfg)) {
13207  ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
13208  return 0;
13209  }
13210 
13211  if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
13212  ast_config_destroy(msg_cfg);
13213  return 0;
13214  }
13215 
13216  cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
13217 
13218  context = ast_variable_retrieve(msg_cfg, "message", "context");
13219  if (!strncasecmp("macro", context, 5)) /* Macro names in contexts are useless for our needs */
13220  context = ast_variable_retrieve(msg_cfg, "message", "macrocontext");
13221  switch (option) {
13222  case 3: /* Play message envelope */
13223  if (!res)
13224  res = play_message_datetime(chan, vmu, origtime, filename);
13225  if (!res)
13226  res = play_message_callerid(chan, vms, cid, context, 0);
13227 
13228  res = 't';
13229  break;
13230 
13231  case 2: /* Call back */
13232 
13233  if (ast_strlen_zero(cid))
13234  break;
13235 
13236  ast_callerid_parse(cid, &name, &num);
13237  while ((res > -1) && (res != 't')) {
13238  switch (res) {
13239  case '1':
13240  if (num) {
13241  /* Dial the CID number */
13242  res = dialout(chan, vmu, num, vmu->callback);
13243  if (res) {
13244  ast_config_destroy(msg_cfg);
13245  return 9;
13246  }
13247  } else {
13248  res = '2';
13249  }
13250  break;
13251 
13252  case '2':
13253  /* Want to enter a different number, can only do this if there's a dialout context for this user */
13254  if (!ast_strlen_zero(vmu->dialout)) {
13255  res = dialout(chan, vmu, NULL, vmu->dialout);
13256  if (res) {
13257  ast_config_destroy(msg_cfg);
13258  return 9;
13259  }
13260  } else {
13261  ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
13262  res = ast_play_and_wait(chan, "vm-sorry");
13263  }
13264  ast_config_destroy(msg_cfg);
13265  return res;
13266  case '*':
13267  res = 't';
13268  break;
13269  case '3':
13270  case '4':
13271  case '5':
13272  case '6':
13273  case '7':
13274  case '8':
13275  case '9':
13276  case '0':
13277 
13278  res = ast_play_and_wait(chan, "vm-sorry");
13279  retries++;
13280  break;
13281  default:
13282  if (num) {
13283  ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
13284  res = ast_play_and_wait(chan, "vm-num-i-have");
13285  if (!res)
13286  res = play_message_callerid(chan, vms, num, vmu->context, 1);
13287  if (!res)
13288  res = ast_play_and_wait(chan, "vm-tocallnum");
13289  /* Only prompt for a caller-specified number if there is a dialout context specified */
13290  if (!ast_strlen_zero(vmu->dialout)) {
13291  if (!res)
13292  res = ast_play_and_wait(chan, "vm-calldiffnum");
13293  }
13294  } else {
13295  res = ast_play_and_wait(chan, "vm-nonumber");
13296  if (!ast_strlen_zero(vmu->dialout)) {
13297  if (!res)
13298  res = ast_play_and_wait(chan, "vm-toenternumber");
13299  }
13300  }
13301  if (!res) {
13302  res = ast_play_and_wait(chan, "vm-star-cancel");
13303  }
13304  if (!res) {
13305  res = ast_waitfordigit(chan, 6000);
13306  }
13307  if (!res) {
13308  retries++;
13309  if (retries > 3) {
13310  res = 't';
13311  }
13312  }
13313  ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
13314  break;
13315 
13316  }
13317  if (res == 't')
13318  res = 0;
13319  else if (res == '*')
13320  res = -1;
13321  }
13322  break;
13323 
13324  case 1: /* Reply */
13325  /* Send reply directly to sender */
13326  if (ast_strlen_zero(cid))
13327  break;
13328 
13329  ast_callerid_parse(cid, &name, &num);
13330  if (!num) {
13331  ast_verb(3, "No CID number available, no reply sent\n");
13332  if (!res)
13333  res = ast_play_and_wait(chan, "vm-nonumber");
13334  ast_config_destroy(msg_cfg);
13335  return res;
13336  } else {
13337  struct ast_vm_user vmu2;
13338  if (find_user(&vmu2, vmu->context, num)) {
13339  struct leave_vm_options leave_options;
13340  char mailbox[AST_MAX_EXTENSION * 2 + 2];
13341  snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
13342 
13343  ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
13344 
13345  memset(&leave_options, 0, sizeof(leave_options));
13346  leave_options.record_gain = record_gain;
13347  res = leave_voicemail(chan, mailbox, &leave_options);
13348  if (!res)
13349  res = 't';
13350  ast_config_destroy(msg_cfg);
13351  return res;
13352  } else {
13353  /* Sender has no mailbox, can't reply */
13354  ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
13355  ast_play_and_wait(chan, "vm-nobox");
13356  res = 't';
13357  ast_config_destroy(msg_cfg);
13358  return res;
13359  }
13360  }
13361  res = 0;
13362 
13363  break;
13364  }
13365 
13366  ast_config_destroy(msg_cfg);
13367 
13368 #ifndef IMAP_STORAGE
13369  if (!res) {
13370  make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
13371  vms->heard[msg] = 1;
13372  res = wait_file(chan, vms, vms->fn);
13373  }
13374 #endif
13375  return res;
13376 }
13377 
13378 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
13379  int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
13380  signed char record_gain, struct vm_state *vms, char *flag)
13381 {
13382  /* Record message & let caller review or re-record it, or set options if applicable */
13383  int res = 0;
13384  int cmd = 0;
13385  int max_attempts = 3;
13386  int attempts = 0;
13387  int recorded = 0;
13388  int msg_exists = 0;
13389  signed char zero_gain = 0;
13390  char tempfile[PATH_MAX];
13391  char *acceptdtmf = "#";
13392  char *canceldtmf = "";
13393  int canceleddtmf = 0;
13394 
13395  /* Note that urgent and private are for flagging messages as such in the future */
13396 
13397  /* barf if no pointer passed to store duration in */
13398  if (duration == NULL) {
13399  ast_log(AST_LOG_WARNING, "Error play_record_review called without duration pointer\n");
13400  return -1;
13401  }
13402 
13403  if (!outsidecaller)
13404  snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
13405  else
13406  ast_copy_string(tempfile, recordfile, sizeof(tempfile));
13407 
13408  cmd = '3'; /* Want to start by recording */
13409 
13410  while ((cmd >= 0) && (cmd != 't')) {
13411  switch (cmd) {
13412  case '1':
13413  if (!msg_exists) {
13414  /* In this case, 1 is to record a message */
13415  cmd = '3';
13416  break;
13417  } else {
13418  /* Otherwise 1 is to save the existing message */
13419  ast_verb(3, "Saving message as is\n");
13420  if (!outsidecaller)
13421  ast_filerename(tempfile, recordfile, NULL);
13422  ast_stream_and_wait(chan, "vm-msgsaved", "");
13423  if (!outsidecaller) {
13424  /* Saves to IMAP server only if imapgreeting=yes */
13425  STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag);
13426  DISPOSE(recordfile, -1);
13427  }
13428  cmd = 't';
13429  return res;
13430  }
13431  case '2':
13432  /* Review */
13433  ast_verb(3, "Reviewing the message\n");
13434  cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
13435  break;
13436  case '3':
13437  msg_exists = 0;
13438  /* Record */
13439  if (recorded == 1)
13440  ast_verb(3, "Re-recording the message\n");
13441  else
13442  ast_verb(3, "Recording the message\n");
13443 
13444  if (recorded && outsidecaller) {
13445  cmd = ast_play_and_wait(chan, INTRO);
13446  cmd = ast_play_and_wait(chan, "beep");
13447  }
13448  recorded = 1;
13449  /* 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 */
13450  if (record_gain)
13451  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
13452  if (ast_test_flag(vmu, VM_OPERATOR))
13453  canceldtmf = "0";
13454  cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
13455  if (strchr(canceldtmf, cmd)) {
13456  /* need this flag here to distinguish between pressing '0' during message recording or after */
13457  canceleddtmf = 1;
13458  }
13459  if (record_gain)
13460  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
13461  if (cmd == -1) {
13462  /* User has hung up, no options to give */
13463  if (!outsidecaller) {
13464  /* user was recording a greeting and they hung up, so let's delete the recording. */
13465  ast_filedelete(tempfile, NULL);
13466  }
13467  return cmd;
13468  }
13469  if (cmd == '0') {
13470  break;
13471  } else if (cmd == '*') {
13472  break;
13473 #if 0
13474  } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
13475  /* Message is too short */
13476  ast_verb(3, "Message too short\n");
13477  cmd = ast_play_and_wait(chan, "vm-tooshort");
13478  cmd = ast_filedelete(tempfile, NULL);
13479  break;
13480  } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
13481  /* Message is all silence */
13482  ast_verb(3, "Nothing recorded\n");
13483  cmd = ast_filedelete(tempfile, NULL);
13484  cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
13485  if (!cmd)
13486  cmd = ast_play_and_wait(chan, "vm-speakup");
13487  break;
13488 #endif
13489  } else {
13490  /* If all is well, a message exists */
13491  msg_exists = 1;
13492  cmd = 0;
13493  }
13494  break;
13495  case '4':
13496  if (outsidecaller) { /* only mark vm messages */
13497  /* Mark Urgent */
13498  if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13499  ast_verbose(VERBOSE_PREFIX_3 "marking message as Urgent\n");
13500  res = ast_play_and_wait(chan, "vm-marked-urgent");
13501  strcpy(flag, "Urgent");
13502  } else if (flag) {
13503  ast_verbose(VERBOSE_PREFIX_3 "UNmarking message as Urgent\n");
13504  res = ast_play_and_wait(chan, "vm-marked-nonurgent");
13505  strcpy(flag, "");
13506  } else {
13507  ast_play_and_wait(chan, "vm-sorry");
13508  }
13509  cmd = 0;
13510  } else {
13511  cmd = ast_play_and_wait(chan, "vm-sorry");
13512  }
13513  break;
13514  case '5':
13515  case '6':
13516  case '7':
13517  case '8':
13518  case '9':
13519  case '*':
13520  case '#':
13521  cmd = ast_play_and_wait(chan, "vm-sorry");
13522  break;
13523 #if 0
13524 /* XXX Commented out for the moment because of the dangers of deleting
13525  a message while recording (can put the message numbers out of sync) */
13526  case '*':
13527  /* Cancel recording, delete message, offer to take another message*/
13528  cmd = ast_play_and_wait(chan, "vm-deleted");
13529  cmd = ast_filedelete(tempfile, NULL);
13530  if (outsidecaller) {
13531  res = vm_exec(chan, NULL);
13532  return res;
13533  }
13534  else
13535  return 1;
13536 #endif
13537  case '0':
13538  if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
13539  cmd = ast_play_and_wait(chan, "vm-sorry");
13540  break;
13541  }
13542  if (msg_exists || recorded) {
13543  cmd = ast_play_and_wait(chan, "vm-saveoper");
13544  if (!cmd)
13545  cmd = ast_waitfordigit(chan, 3000);
13546  if (cmd == '1') {
13547  ast_filerename(tempfile, recordfile, NULL);
13548  ast_play_and_wait(chan, "vm-msgsaved");
13549  cmd = '0';
13550  } else if (cmd == '4') {
13551  if (flag) {
13552  ast_play_and_wait(chan, "vm-marked-urgent");
13553  strcpy(flag, "Urgent");
13554  }
13555  ast_play_and_wait(chan, "vm-msgsaved");
13556  cmd = '0';
13557  } else {
13558  ast_play_and_wait(chan, "vm-deleted");
13559  DELETE(tempfile, -1, tempfile, vmu);
13560  cmd = '0';
13561  }
13562  }
13563  return cmd;
13564  default:
13565  /* If the caller is an ouside caller, and the review option is enabled,
13566  allow them to review the message, but let the owner of the box review
13567  their OGM's */
13568  if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
13569  return cmd;
13570  if (msg_exists) {
13571  cmd = ast_play_and_wait(chan, "vm-review");
13572  if (!cmd && outsidecaller) {
13573  if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
13574  cmd = ast_play_and_wait(chan, "vm-review-urgent");
13575  } else if (flag) {
13576  cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
13577  }
13578  }
13579  } else {
13580  cmd = ast_play_and_wait(chan, "vm-torerecord");
13581  if (!cmd)
13582  cmd = ast_waitfordigit(chan, 600);
13583  }
13584 
13585  if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
13586  cmd = ast_play_and_wait(chan, "vm-reachoper");
13587  if (!cmd)
13588  cmd = ast_waitfordigit(chan, 600);
13589  }
13590 #if 0
13591  if (!cmd)
13592  cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
13593 #endif
13594  if (!cmd)
13595  cmd = ast_waitfordigit(chan, 6000);
13596  if (!cmd) {
13597  attempts++;
13598  }
13599  if (attempts > max_attempts) {
13600  cmd = 't';
13601  }
13602  }
13603  }
13604  if (!outsidecaller && (cmd == -1 || cmd == 't')) {
13605  /* Hang up or timeout, so delete the recording. */
13606  ast_filedelete(tempfile, NULL);
13607  }
13608 
13609  if (cmd != 't' && outsidecaller)
13610  ast_play_and_wait(chan, "vm-goodbye");
13611 
13612  return cmd;
13613 }
13614 
13615 /* This is a workaround so that menuselect displays a proper description
13616  * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
13617  */
13618 
13620  .load = load_module,
13621  .unload = unload_module,
13622  .reload = reload,
13623  .nonoptreq = "res_adsi,res_smdi",
13624  );
static char user[512]
double volgain
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 ...
int ast_filecopy(const char *oldname, const char *newname, const char *fmt)
Copies a file.
Definition: file.c:941
static void adsi_password(struct ast_channel *chan)
static void poll_subscribed_mailboxes(void)
SQLHDBC con
Definition: res_odbc.h:48
#define VM_OPERATOR
static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:81
static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
static void mwi_unsub_event_cb(const struct ast_event *event, void *userdata)
int ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2804
#define SMDI_MWI_WAIT_TIMEOUT
static char listen_control_restart_key[12]
static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
#define ast_channel_lock(chan)
Definition: channel.h:2466
static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
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 double volgain
static struct ast_flags globalflags
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:191
An event.
Definition: event.c:85
char * str
Subscriber phone number (Malloced)
Definition: channel.h:241
static int check_mime(const char *str)
Check if the string would need encoding within the MIME standard, to avoid confusing certain mail sof...
#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
static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
int oldmessages
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:946
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
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.
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
static int vmmaxsecs
Asterisk main include file. File version handling, generic pbx functions.
#define ao2_link(arg1, arg2)
Definition: astobj2.h:785
static char listen_control_forward_key[12]
static struct ast_vm_user * find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
Finds a voicemail user from the realtime engine.
static int vm_exec(struct ast_channel *chan, const char *data)
#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY
const char * ast_variable_retrieve(const struct ast_config *config, const char *category, const char *variable)
Gets a variable.
Definition: config.c:625
#define INTRO
int ast_adsi_begin_download(struct ast_channel *chan, char *service, unsigned char *fdn, unsigned char *sec, int version)
Definition: adsi.c:32
static void free_zone(struct vm_zone *z)
int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int silencethreshold, int maxsilence_ms, const char *path, const char *acceptdtmf, const char *canceldtmf)
Record a file based on input from a channel This function will play &quot;auth-thankyou&quot; upon successful r...
Definition: app.c:1178
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
Definition: app.h:712
static char vm_passchanged[80]
#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 vmauthenticate(struct ast_channel *chan, const char *data)
static int inchar(struct baseio *bio, FILE *fi)
utility used by base_encode()
int ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
Plays a stream and gets DTMF data from a channel.
Definition: app.c:178
struct ast_frame ast_null_frame
Definition: frame.c:131
struct ast_party_caller caller
Channel Caller ID information.
Definition: channel.h:804
static void * mb_poll_thread(void *data)
#define PWDCHANGE_EXTERNAL
char * strsep(char **str, const char *delims)
#define VM_PBXSKIP
static char userscontext[AST_MAX_EXTENSION]
struct ast_app * pbx_findapp(const char *app)
Look up an application.
Definition: pbx.c:1537
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. ...
char msg_format[512]
static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms)
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
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 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 char zonetag[80]
enum ast_event_type ast_event_get_type(const struct ast_event *event)
Get the type for an event.
Definition: event.c:1070
static unsigned int poll_mailboxes
#define ADSI_MSG_DISPLAY
Definition: adsi.h:32
static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
#define MINPASSWORD
#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
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
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: cli.c:2177
static unsigned char adsifdn[4]
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2502
static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
#define DELETE(a, b, c, d)
static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
int ast_control_streamfile(struct ast_channel *chan, const char *file, const char *fwd, const char *rev, const char *stop, const char *pause, const char *restart, int skipms, long *offsetms)
Stream a file with fast forward, pause, reverse, restart.
Definition: app.c:683
static int check_password(struct ast_vm_user *vmu, char *password)
Check that password meets minimum required length.
static int vm_delete(char *file)
Removes the voicemail sound and information file.
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define ast_test_flag(p, flag)
Definition: utils.h:63
int option_debug
Definition: asterisk.c:182
static struct ast_event_sub * mwi_sub_sub
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
Definition: config.c:2630
static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
static char * handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Reload voicemail configuration from the CLI.
Time-related functions and macros.
unsigned int flags
struct ast_party_name name
Subscriber name.
Definition: channel.h:290
struct ast_party_id from
Who is redirecting the call (Sent to the party the call is redirected toward)
Definition: channel.h:449
#define VALID_DTMF
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.
char fn[PATH_MAX]
#define VM_FORCEGREET
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
char context[AST_MAX_CONTEXT]
Definition: channel.h:868
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
static char pagerdateformat[32]
#define ast_set_flag(p, flag)
Definition: utils.h:70
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
const int argc
Definition: cli.h:154
#define LOG_WARNING
Definition: logger.h:144
#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY
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 vm_password[80]
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 int inbuf(struct baseio *bio, FILE *fi)
utility used by inchar(), for base_encode()
char timezone[80]
const char * context
#define BASEMAXINLINE
#define ASTERISK_USERNAME
struct ast_taskprocessor * ast_taskprocessor_get(const char *name, enum ast_tps_options create)
Get a reference to a taskprocessor with the specified name and create the taskprocessor if necessary...
static char externnotify[160]
int lineno
Definition: config.h:87
void ast_verbose(const char *fmt,...)
Definition: logger.c:1568
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: app.c:2101
static int 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)
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
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
static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
basically mkdir -p $dest/$context/$ext/$folder
This entries are for multiple registers.
Definition: data.h:253
static int unload_module(void)
Structure for variables, used for configurations and for channel variables.
Definition: config.h:75
static struct ast_vm_user * find_or_create(const char *context, const char *box)
vm_option_args
#define AST_LOG_WARNING
Definition: logger.h:149
#define DATA_EXPORT_VM_USERS(USER)
#define var
Definition: ast_expr2f.c:606
static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
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 void adsi_delete(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.
#define VOICEMAIL_DIR_MODE
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
static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
Sets a a specific property value.
#define VM_DIRECFORWARD
static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
Configuration File Parser.
int ast_adsi_download_disconnect(unsigned char *buf)
Disconnects (and hopefully saves) a downloaded script.
Definition: adsi.c:208
static void queue_mwi_event(const char *box, int urgent, int new, int old)
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
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.
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: config.c:2548
static int vm_execmain(struct ast_channel *chan, const char *data)
#define DEFAULT_LISTEN_CONTROL_STOP_KEY
static unsigned int poll_freq
static int get_folder_by_name(const char *name)
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 has_voicemail(const char *mailbox, const char *folder)
Determines if the given folder has messages.
static struct ast_threadstorage buf2
static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:142
static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
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 char charset[32]
#define VOICEMAIL_CONFIG
#define ADSI_JUST_LEFT
Definition: adsi.h:112
static char listen_control_pause_key[12]
Number of Used by: AST_EVENT_MWI Payload type: UINT.
Definition: event_defs.h:77
#define HVSZ_OUTPUT_FORMAT
#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
static int vm_users_data_provider_get(const struct ast_data_search *search, struct ast_data *data_root)
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
static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:374
static void start_poll_thread(void)
#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 adsi_logo(unsigned char *buf)
static char locale[20]
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 * handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail users in the CLI.
static int skipms
#define HVSU_OUTPUT_FORMAT
static int handle_unsubscribe(void *datap)
static int play_message_category(struct ast_channel *chan, const char *category)
Definitions to aid in the use of thread local storage.
static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
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 input(yyscan_t yyscanner)
Definition: ast_expr2f.c:1575
static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
#define LOG_DEBUG
Definition: logger.h:122
#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]
static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
const char * ext
Definition: http.c:112
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx.c:7705
format_t rawreadformat
Definition: channel.h:855
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:600
#define SENDMAIL
static char vm_reenterpassword[80]
#define ast_cond_signal(cond)
Definition: lock.h:169
int urgentmessages
static int maxlogins
static int transfer
Definition: chan_mgcp.c:187
#define ast_verb(level,...)
Definition: logger.h:243
Definition: ael.tab.c:203
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: config.c:1037
char * emailsubject
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.
void ast_unreplace_sigchld(void)
Restore the SIGCHLD handler.
Definition: asterisk.c:1062
static void free_vm_zones(void)
Free the zones structure.
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return * the previous value of *p. This can be used to handle reference co...
Definition: lock.h:603
#define ast_test_suite_assert(exp)
Definition: test.h:185
const char * line
Definition: cli.h:156
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Definition: pbx.c:3814
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]
static ast_mutex_t poll_lock
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
static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
int dh_arraysize
static int get_folder2(struct ast_channel *chan, char *fn, int start)
plays a prompt and waits for a keypress.
#define MAX_LANGUAGE
Definition: channel.h:138
#define VM_ENVELOPE
static int reload(void)
String fields in structures.
Utility functions.
#define VM_SAYDURATION
const char * astman_get_header(const struct message *m, char *var)
Get header from mananger transaction.
Definition: manager.c:1860
static void adsi_login(struct ast_channel *chan)
#define VM_FWDURGAUTO
pthread_cond_t ast_cond_t
Definition: lock.h:144
char cause[SMDI_MWI_FAIL_CAUSE_LEN+1]
Definition: smdi.h:58
#define ast_manager_event(chan, category, event, contents,...)
Definition: manager.h:221
static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
const char * mailbox
struct ast_category * ast_category_new(const char *name, const char *in_file, int lineno)
Create a category structure.
Definition: config.c:673
static unsigned char adsisec[4]
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 char * app
#define PWDCHANGE_INTERNAL
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)
Number structure.
Definition: app_followme.c:109
static struct ast_smdi_interface * smdi_iface
static void rename_file(char *sfn, char *dfn)
Renames a message in a mailbox folder.
#define ADSI_DIR_FROM_LEFT
Definition: adsi.h:120
Custom localtime functions for multiple timezones.
static int vm_box_exists(struct ast_channel *chan, const char *data)
#define RETRIEVE(a, b, c, d)
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
static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name)
Retrieve a category if it exists.
Definition: config.c:709
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
Definition: config.c:2679
char * context
#define AST_LOG_NOTICE
Definition: logger.h:138
#define MAX_NUM_CID_CONTEXTS
#define AST_RWLIST_RDLOCK(head)
Read locks a list.
Definition: linkedlists.h:77
static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
#define VM_ATTACH
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
static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:236
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
enum AST_LOCK_RESULT ast_lock_path(const char *path)
Lock a filesystem path.
Definition: app.c:1652
#define SENTINEL
Definition: compiler.h:75
Context IE Used by AST_EVENT_MWI Payload type: str.
Definition: event_defs.h:121
#define AST_DATA_HANDLER_VERSION
The Data API structures version.
Definition: data.h:204
void ast_category_destroy(struct ast_category *cat)
Definition: config.c:762
#define VM_SKIPAFTERCMD
const char * value
Definition: config.h:79
int ast_adsi_input_format(unsigned char *buf, int num, int dir, int wrap, char *format1, char *format2)
Set input format.
Definition: adsi.c:329
static FILE * vm_mkftemp(char *template)
#define AST_LOG_ERROR
Definition: logger.h:160
static char * emailbody
int ast_say_counted_noun(struct ast_channel *chan, int num, const char *noun)
#define COPY(a, b, c, d, e, f, g, h)
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 sayname(struct ast_channel *chan, const char *mailbox, const char *context)
char * ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown)
Definition: callerid.c:1074
static char * pagersubject
static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
The handler for the change password option.
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
#define VM_ALLOCED
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 saydurationminfo
static int load_config(int reload)
static char exitcontext[AST_MAX_CONTEXT]
static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
const int fd
Definition: cli.h:153
#define STORE(a, b, c, d, e, f, g, h, i, j)
#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 inprocess_count(const char *context, const char *mailbox, int delta)
#define AST_PTHREADT_NULL
Definition: lock.h:65
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)
#define BASELINELEN
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
const int n
Definition: cli.h:159
static int valid_config(const struct ast_config *cfg)
Check if configuration file is valid.
struct sla_ringing_trunk * last
Definition: app_meetme.c:965
Data structure associated with a custom dialplan function.
Definition: pbx.h:95
static char ext_pass_cmd[128]
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 void apply_options(struct ast_vm_user *vmu, const char *options)
Destructively Parse options and apply.
int passwordlocation
#define ast_data_unregister(path)
Definition: data.h:394
static void run_externnotify(char *context, char *extension, const char *flag)
static char * strip_control_and_high(const char *input, char *buf, size_t buflen)
Strips control and non 7-bit clean characters from input string.
static ast_cond_t poll_cond
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:554
static struct ast_data_handler vm_users_data_provider
char * ast_category_browse(struct ast_config *config, const char *prev)
Goes through categories.
Definition: config.c:810
#define ao2_ref(o, delta)
Definition: astobj2.h:472
#define DEFAULT_LISTEN_CONTROL_PAUSE_KEY
#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
static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
#define DATA_EXPORT_VM_ZONES(ZONE)
char context[AST_MAX_CONTEXT]
#define tdesc
long int ast_random(void)
Definition: utils.c:1640
static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
#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 struct ast_app_option vm_app_options[128]
static int my_umask
uint32_t uniqueid
static int base_encode(char *filename, FILE *so)
Performs a base 64 encode algorithm on the contents of a File.
static char language[MAX_LANGUAGE]
Definition: chan_alsa.c:108
int ast_adsi_display(unsigned char *buf, int page, int line, int just, int wrap, char *col1, char *col2)
Loads a line of info into the display.
Definition: adsi.c:274
const char * name
Definition: config.h:77
int ast_strftime_locale(char *buf, size_t len, const char *format, const struct ast_tm *tm, const char *locale)
Definition: localtime.c:2280
char * ast_format_str_reduce(char *fmts)
Definition: file.c:1382
static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag)
Sends email notification that a user has a new voicemail waiting for them.
#define VM_SAYCID
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.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:818
#define VM_TEMPGREETWARN
int ast_app_inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
Determine number of new/old messages in a mailbox.
Definition: app.c:435
ODBC resource manager.
char zonetag[80]
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
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.
#define ast_data_register_multiple(data_entries, entries)
Definition: data.h:377
static int vmminsecs
char mailbox[0]
static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
static struct ast_data_entry vm_data_providers[]
Structure to describe a channel &quot;technology&quot;, ie a channel driver See for examples: ...
Definition: channel.h:507
Core PBX routines and definitions.
static char * show_users_realtime(int fd, const char *context)
#define AST_RWLIST_TRAVERSE_SAFE_BEGIN
Definition: linkedlists.h:542
static void copy_plain_file(char *frompath, char *topath)
Copies a voicemail information (envelope) file.
#define ast_data_add_structure(structure_name, root, structure)
Definition: data.h:620
int * heard
int ast_app_inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
Determine number of urgent/new/old messages in a mailbox.
Definition: app.c:455
Event type Used by: AST_EVENT_SUB, AST_EVENT_UNSUB Payload type: UINT.
Definition: event_defs.h:95
The list of nodes with their search requirement.
Definition: data.c:122
#define ASTOBJ_UNREF(object, destructor)
Decrement the reference count on an object.
Definition: astobj.h:218
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:290
static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:184
const char *const * argv
Definition: cli.h:155
static void stop_poll_thread(void)
static void adsi_goodbye(struct ast_channel *chan)
static char * pagerbody
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 int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
#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
static void populate_defaults(struct ast_vm_user *vmu)
Sets default voicemail system options to a voicemail user.
static int inprocess_cmp_fn(void *obj, void *arg, int flags)
char pager[80]
#define AST_LIST_HEAD_NOLOCK_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:345
#define LOG_ERROR
Definition: logger.h:155
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:716
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 acf_mailbox_exists(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
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
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.
#define CLI_SHOWUSAGE
Definition: cli.h:44
static int append_mailbox(const char *context, const char *box, const char *data)
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 char fromstring[100]
static char emaildateformat[32]
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: config.c:483
static int write_password_to_file(const char *secretfn, const char *password)
Event subscription.
Definition: event.c:124
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
static int pwdchange
static const char * substitute_escapes(const char *value)
enum ast_channel_state _state
Definition: channel.h:839
static char VM_SPOOL_DIR[PATH_MAX]
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
const ast_string_field name
Definition: channel.h:787
static int vm_users_data_provider_get_helper(const struct ast_data_search *search, struct ast_data *data_root, struct ast_vm_user *user)
static int get_date(char *s, int len)
Gets the current date and time, as formatted string.
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 unsigned char poll_thread_run
static int handle_subscribe(void *datap)
int linelength
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:430
static int copy(char *infile, char *outfile)
Utility function to copy a file.
char exit[80]
#define LOG_NOTICE
Definition: logger.h:133
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:11261
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...
#define VM_SVMAIL
static char vm_pls_try_again[80]
static struct ast_event_sub * mwi_unsub_sub
static int say_and_wait(struct ast_channel *chan, int num, const char *language)
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
int ast_say_counted_adjective(struct ast_channel *chan, int num, const char *adjective, const char *gender)
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
static struct ast_threadstorage buf1
#define DEFAULT_POLL_FREQ
static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
#define VM_MESSAGEWRAP
static char * addesc
#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
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)
#define ast_channel_unlock(chan)
Definition: channel.h:2467
#define CLI_FAILURE
Definition: cli.h:45
int errno
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1858
static char ext_pass_check_cmd[128]
static const char name[]
#define AST_MAX_CONTEXT
Definition: channel.h:136
#define ast_free(a)
Definition: astmm.h:97
char * command
Definition: cli.h:180
static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:418
char macrocontext[AST_MAX_CONTEXT]
Definition: channel.h:870
static char * app4
static const char * mbox(struct ast_vm_user *vmu, int id)
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 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.
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1370
#define AST_FLAGS_ALL
Definition: utils.h:196
int ast_say_character_str(struct ast_channel *chan, const char *num, const char *ints, const char *lang)
Definition: channel.c:8421
#define ast_odbc_request_obj(a, b)
Definition: res_odbc.h:123
static char * emailsubject
static char * vm_check_password_shell(char *command, char *buf, size_t len)
static char dialcontext[AST_MAX_CONTEXT]
static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
void ast_close_fds_above_n(int n)
Common routine for child processes, to close all fds prior to exec(2)
Definition: app.c:2237
int * deleted
static struct ast_format f[]
Definition: format_g726.c:181
SMDI support for Asterisk.
static pthread_t poll_thread
const char * word
Definition: cli.h:157
static void free_user(struct ast_vm_user *vmu)
int ast_adsi_load_soft_key(unsigned char *buf, int key, const char *llabel, const char *slabel, char *ret, int data)
Creates &quot;load soft key&quot; parameters.
Definition: adsi.c:296
static int inprocess_hash_fn(const void *obj, const int flags)
static char vm_mismatch[80]
#define VM_REVIEW
static int count_messages(struct ast_vm_user *vmu, char *dir)
Find all .txt files - even if they are not in sequence from 0000.
static char * handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail zones in the CLI.
#define RENAME(a, b, c, d, e, f, g, h)
const char * ast_config_AST_SPOOL_DIR
Definition: asterisk.c:259
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2351
static char acceptdtmf
Definition: chan_agent.c:227
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
#define ADSI_MSG_DOWNLOAD
Definition: adsi.h:33
An API for managing task processing threads that can be shared across modules.
int ast_variable_update(struct ast_category *category, const char *variable, const char *value, const char *match, unsigned int object)
Update variable value within a config.
Definition: config.c:942
char fwd_st[SMDI_MAX_STATION_NUM_LEN+1]
Definition: smdi.h:56
static 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...
if(yyss+yystacksize-1<=yyssp)
Definition: ast_expr2.c:1874
#define EXISTS(a, b, c, d)
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
#define VMSTATE_MAX_MSG_ARRAY
Structure used to handle boolean flags.
Definition: utils.h:200
#define DEFAULT_LISTEN_CONTROL_RESTART_KEY
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
Definition: config.c:2606
#define ast_clear_flag(p, flag)
Definition: utils.h:77
Support for logging to various files, console and syslog Configuration in file logger.conf.
void ast_install_vm_functions(int(*has_voicemail_func)(const char *mailbox, const char *folder), int(*inboxcount_func)(const char *mailbox, int *newmsgs, int *oldmsgs), int(*inboxcount2_func)(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs), int(*messagecount_func)(const char *context, const char *mailbox, const char *folder), int(*sayname_func)(struct ast_channel *chan, const char *mailbox, const char *context))
Set voicemail function callbacks.
Definition: app.c:399
#define AST_RWLIST_ENTRY
Definition: linkedlists.h:414
static int load_module(void)
Options for leaving voicemail with the voicemail() application.
Definition: app_minivm.c:635
const char * usage
Definition: cli.h:171
static int silencethreshold
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
Definition: pbx.c:10546
uint32_t ast_event_get_ie_uint(const struct ast_event *event, enum ast_event_ie_type ie_type)
Get the value of an information element that has an integer payload.
Definition: event.c:1075
int(*const write)(struct ast_channel *chan, struct ast_frame *frame)
Write a frame, in standard format (see frame.h)
Definition: channel.h:554
static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
Performs a change of the voicemail passowrd in the realtime engine.
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
Definition: channel.c:3552
static void adsi_begin(struct ast_channel *chan, int *useadsi)
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
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
static void adsi_folders(struct ast_channel *chan, int start, char *label)
signed char record_gain
Definition: app_minivm.c:637
vm_passwordlocation
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:436
#define EVENT_FLAG_REPORTING
Definition: manager.h:80
#define CLI_SUCCESS
Definition: cli.h:43
static int messagecount(const char *context, const char *mailbox, const char *folder)
static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
#define DISPOSE(a, b)
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
static int maxsilence
static int vmsayname_exec(struct ast_channel *chan, const char *data)
#define MAXMSGLIMIT
static const char *const mailbox_folders[]
static char serveremail[80]
SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
Prepares, executes, and returns the resulting statement handle.
Definition: res_odbc.c:622
int ast_adsi_available(struct ast_channel *chan)
Returns non-zero if Channel does or might support ADSI.
Definition: adsi.c:263
#define VOICEMAIL_FILE_MODE
A ast_taskprocessor structure is a singleton by name.
Definition: taskprocessor.c:67
static struct ast_cli_entry cli_voicemail[]
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.
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 ERROR_LOCK_PATH
#define ast_calloc(a, b)
Definition: astmm.h:82
static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
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
#define VM_DELETE
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:223
static char vmfmts[80]
#define CHUNKSIZE
#define ao2_container_alloc(arg1, arg2, arg3)
Definition: astobj2.h:734
#define VM_SEARCH
#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.
static int maxmsg
#define AST_LOG_DEBUG
Definition: logger.h:127
ast_app: A registered application
Definition: pbx.c:971
char dialout[80]
static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg)
static char * app3
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
static void mwi_sub_destroy(struct mwi_sub *mwi_sub)
static char vm_prepend_timeout[80]
static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
#define ast_realloc(a, b)
Definition: astmm.h:103
static int maxdeletedmsg
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
#define OPERATOR_EXIT
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:919
static char vm_newpassword[80]
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:3086
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
void ast_smdi_mwi_message_destroy(struct ast_smdi_mwi_message *msg)
ast_smdi_mwi_message destructor.
Definition: res_smdi.c:829
static char pagerfromstring[100]
vm_box
Data structure associated with a single frame of data.
Definition: frame.h:142
#define AST_DATA_STRUCTURE(__struct, __name)
Definition: data.h:300
static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
static struct ast_custom_function mailbox_exists_acf
static void mwi_sub_event_cb(const struct ast_event *event, void *userdata)
char curbox[80]
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
char callback[80]
Unique ID Used by: AST_EVENT_SUB, AST_EVENT_UNSUB Payload type: UINT.
Definition: event_defs.h:89
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.
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
enum queue_result id
Definition: app_queue.c:1090
static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
#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 int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
static char * app2
#define MAXMSG
struct ast_variable * next
Definition: config.h:82
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 char * complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64]
#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
static void read_password_from_file(const char *secretfn, char *password, int passwordlen)
#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
static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
#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
static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
#define ast_mutex_destroy(a)
Definition: lock.h:154
static char listen_control_stop_key[12]
static int adsiver
char serveremail[80]
int ast_adsi_unload_session(struct ast_channel *chan)
Definition: adsi.c:87
static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
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 void free_vm_users(void)
Free the users structure.
static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
The handler for &#39;record a temporary greeting&#39;.
static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
struct ast_channel * ast_dummy_channel_alloc(void)
Create a fake channel structure.
Definition: channel.c:1391
else
Definition: ast_expr2.c:1975
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:1112
static int passwordlocation
static char vm_invalid_password[80]
Say numbers and dates (maybe words one day too)
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_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
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
static char * sayname_app
Asterisk module definitions.
const char * ast_config_AST_VAR_DIR
Definition: asterisk.c:261
static void poll_subscribed_mailbox(struct mwi_sub *mwi_sub)
static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
Manager list voicemail users command.
static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
#define VM_MOVEHEARD
static void delete_file(struct phoneprov_file *file)
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 snd_pcm_format_t format
Definition: chan_alsa.c:93
static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
static char callcontext[AST_MAX_CONTEXT]
struct ast_channel_tech * tech
Definition: channel.h:743
char email[80]
#define ADSI_JUST_CENT
Definition: adsi.h:114
struct ast_data * ast_data_add_int(struct ast_data *root, const char *childname, int value)
Add an integer node type.
Definition: data.c:2322
struct ast_event_sub * ast_event_unsubscribe(struct ast_event_sub *event_sub)
Un-subscribe from events.
Definition: event.c:987
static struct ast_taskprocessor * mwi_subscription_tps
int newmessages
#define AST_FORMAT_GSM
Definition: frame.h:244
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:247
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:172
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...
#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 VM_FORCENAME
static char listen_control_reverse_key[12]
#define AST_RWLIST_TRAVERSE_SAFE_END
Definition: linkedlists.h:602
const ast_string_field language
Definition: channel.h:787
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.
uint32_t version
Structure version.
Definition: data.h:247
char exten[AST_MAX_EXTENSION]
Definition: channel.h:869
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...
#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
static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
Resets a user password to a specified password.
struct ast_variable * ast_variable_new(const char *name, const char *value, const char *filename)
Definition: config.c:278
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)
Structure for mutex and tracking information.
Definition: lock.h:121
An SMDI message waiting indicator message.
Definition: smdi.h:55
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.
static char mailbox[AST_MAX_EXTENSION]
Definition: chan_mgcp.c:197
static int ochar(struct baseio *bio, int c, FILE *so)
utility used by base_encode()
static int is_valid_dtmf(const char *key)
Determines if a DTMF key entered is valid.
static int maxgreet
The structure of the node handler.
Definition: data.h:245
static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
Loads the options specific to a voicemail user.
char password[80]
static char mailcmd[160]
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 ENDL
#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 save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: config.c:2650
#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 prefix[MAX_PREFIX]
Definition: http.c:107
static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2151
int ast_callerid_parse(char *instr, char **name, char **location)
Destructively parse inbuf into name and location (or number)
Definition: callerid.c:1009
void ast_uninstall_vm_functions(void)
Definition: app.c:412
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:344
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
static int minpassword
static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent)
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.