Asterisk - The Open Source Telephony Project  GIT-master-44aef04
app_minivm.c
Go to the documentation of this file.
1 /*
2  * Asterisk -- An open source telephony toolkit.
3  *
4  * Copyright (C) 1999 - 2005, Digium, Inc.
5  * and Edvina AB, Sollentuna, Sweden
6  *
7  * Mark Spencer <markster@digium.com> (Comedian Mail)
8  * and Olle E. Johansson, Edvina.net <oej@edvina.net> (Mini-Voicemail changes)
9  *
10  * See http://www.asterisk.org for more information about
11  * the Asterisk project. Please do not directly contact
12  * any of the maintainers of this project for assistance;
13  * the project provides a web site, mailing lists and IRC
14  * channels for your use.
15  *
16  * This program is free software, distributed under the terms of
17  * the GNU General Public License Version 2. See the LICENSE file
18  * at the top of the source tree.
19  */
20 
21 /*! \file
22  *
23  * \brief MiniVoiceMail - A Minimal Voicemail System for Asterisk
24  *
25  * A voicemail system in small building blocks, working together
26  * based on the Comedian Mail voicemail system (app_voicemail.c).
27  *
28  * \par See also
29  * \arg \ref Config_minivm_examples
30  * \arg \ref App_minivm
31  *
32  * \ingroup applications
33  *
34  * \page App_minivm Asterisk Mini-voicemail - A minimal voicemail system
35  *
36  * This is a minimal voicemail system, building blocks for something
37  * else. It is built for multi-language systems.
38  * The current version is focused on accounts where voicemail is
39  * forwarded to users in e-mail. It's work in progress, with loosed ends hanging
40  * around from the old voicemail system and it's configuration.
41  *
42  * Hopefully, we can expand this to be a full replacement of voicemail() and voicemailmain()
43  * in the future.
44  *
45  * Dialplan applications
46  * - minivmRecord - record voicemail and send as e-mail ( \ref minivm_record_exec() )
47  * - minivmGreet - Play user's greeting or default greeting ( \ref minivm_greet_exec() )
48  * - minivmNotify - Notify user of message ( \ref minivm_notify_exec() )
49  * - minivmDelete - Delete voicemail message ( \ref minivm_delete_exec() )
50  * - minivmAccMess - Record personal messages (busy | unavailable | temporary)
51  *
52  * Dialplan functions
53  * - MINIVMACCOUNT() - A dialplan function
54  * - MINIVMCOUNTER() - Manage voicemail-related counters for accounts or domains
55  *
56  * CLI Commands
57  * - minivm list accounts
58  * - minivm list zones
59  * - minivm list templates
60  * - minivm show stats
61  * - minivm show settings
62  *
63  * Some notes
64  * - General configuration in minivm.conf
65  * - Users in realtime or configuration file
66  * - Or configured on the command line with just the e-mail address
67  *
68  * Voicemail accounts are identified by userid and domain
69  *
70  * Language codes are like setlocale - langcode_countrycode
71  * \note Don't use language codes like the rest of Asterisk, two letter countrycode. Use
72  * language_country like setlocale().
73  *
74  * Examples:
75  * - Swedish, Sweden sv_se
76  * - Swedish, Finland sv_fi
77  * - English, USA en_us
78  * - English, GB en_gb
79  *
80  * \par See also
81  * \arg \ref Config_minivm
82  * \arg \ref Config_minivm_examples
83  * \arg \ref Minivm_directories
84  * \arg \ref app_minivm.c
85  * \arg Comedian mail: app_voicemail.c
86  * \arg \ref descrip_minivm_accmess
87  * \arg \ref descrip_minivm_greet
88  * \arg \ref descrip_minivm_record
89  * \arg \ref descrip_minivm_delete
90  * \arg \ref descrip_minivm_notify
91  *
92  * \arg \ref App_minivm_todo
93  */
94 /*! \page Minivm_directories Asterisk Mini-Voicemail Directory structure
95  *
96  * The directory structure for storing voicemail
97  * - AST_SPOOL_DIR - usually /var/spool/asterisk (configurable in asterisk.conf)
98  * - MVM_SPOOL_DIR - should be configurable, usually AST_SPOOL_DIR/voicemail
99  * - Domain MVM_SPOOL_DIR/domain
100  * - Username MVM_SPOOL_DIR/domain/username
101  * - /greet : Recording of account owner's name
102  * - /busy : Busy message
103  * - /unavailable : Unavailable message
104  * - /temp : Temporary message
105  *
106  * For account anita@localdomain.xx the account directory would as a default be
107  * \b /var/spool/asterisk/voicemail/localdomain.xx/anita
108  *
109  * To avoid transcoding, these sound files should be converted into several formats
110  * They are recorded in the format closest to the incoming streams
111  *
112  *
113  * Back: \ref App_minivm
114  */
115 
116 /*! \page Config_minivm_examples Example dialplan for Mini-Voicemail
117  * \section Example dialplan scripts for Mini-Voicemail
118  * \verbinclude extensions_minivm.conf.sample
119  *
120  * Back: \ref App_minivm
121  */
122 
123 /*! \page App_minivm_todo Asterisk Mini-Voicemail - todo
124  * - configure accounts from AMI?
125  * - test, test, test, test
126  * - fix "vm-theextensionis.gsm" voiceprompt from Allison in various formats
127  * "The extension you are calling"
128  * - For trunk, consider using channel storage for information passing between small applications
129  * - Set default directory for voicemail
130  * - New app for creating directory for account if it does not exist
131  * - Re-insert code for IMAP storage at some point
132  * - Jabber integration for notifications
133  * - Figure out how to handle video in voicemail
134  * - Integration with the HTTP server
135  * - New app for moving messages between mailboxes, and optionally mark it as "new"
136  *
137  * For Asterisk 1.4/trunk
138  * - Use string fields for minivm_account
139  *
140  * Back: \ref App_minivm
141  */
142 
143 /*** MODULEINFO
144  <support_level>extended</support_level>
145  ***/
146 
147 #include "asterisk.h"
148 
149 #include <ctype.h>
150 #include <sys/time.h>
151 #include <sys/stat.h>
152 #include <sys/mman.h>
153 #include <time.h>
154 #include <dirent.h>
155 #include <locale.h>
156 
157 
158 #include "asterisk/paths.h" /* use various paths */
159 #include "asterisk/lock.h"
160 #include "asterisk/file.h"
161 #include "asterisk/channel.h"
162 #include "asterisk/pbx.h"
163 #include "asterisk/config.h"
164 #include "asterisk/say.h"
165 #include "asterisk/module.h"
166 #include "asterisk/app.h"
167 #include "asterisk/mwi.h"
168 #include "asterisk/dsp.h"
169 #include "asterisk/localtime.h"
170 #include "asterisk/cli.h"
171 #include "asterisk/utils.h"
172 #include "asterisk/linkedlists.h"
173 #include "asterisk/callerid.h"
174 #include "asterisk/stasis.h"
176 #include "asterisk/json.h"
177 
178 /*** DOCUMENTATION
179 <application name="MinivmRecord" language="en_US">
180  <synopsis>
181  Receive Mini-Voicemail and forward via e-mail.
182  </synopsis>
183  <syntax>
184  <parameter name="mailbox" required="true" argsep="@">
185  <argument name="username" required="true">
186  <para>Voicemail username</para>
187  </argument>
188  <argument name="domain" required="true">
189  <para>Voicemail domain</para>
190  </argument>
191  </parameter>
192  <parameter name="options" required="false">
193  <optionlist>
194  <option name="0">
195  <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
196  </option>
197  <option name="*">
198  <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
199  </option>
200  <option name="g">
201  <argument name="gain">
202  <para>Amount of gain to use</para>
203  </argument>
204  <para>Use the specified amount of gain when recording the voicemail message.
205  The units are whole-number decibels (dB).</para>
206  </option>
207  </optionlist>
208  </parameter>
209  </syntax>
210  <description>
211  <para>This application is part of the Mini-Voicemail system, configured in <filename>minivm.conf</filename></para>
212  <para>MiniVM records audio file in configured format and forwards message to e-mail and pager.</para>
213  <para>If there's no user account for that address, a temporary account will be used with default options.</para>
214  <para>The recorded file name and path will be stored in <variable>MVM_FILENAME</variable> and the duration
215  of the message will be stored in <variable>MVM_DURATION</variable></para>
216  <note><para>If the caller hangs up after the recording, the only way to send the message and clean up is to
217  execute in the <literal>h</literal> extension. The application will exit if any of the following DTMF digits
218  are received and the requested extension exist in the current context.</para></note>
219  <variablelist>
220  <variable name="MVM_RECORD_STATUS">
221  <para>This is the status of the record operation</para>
222  <value name="SUCCESS" />
223  <value name="USEREXIT" />
224  <value name="FAILED" />
225  </variable>
226  </variablelist>
227  </description>
228 </application>
229 <application name="MinivmGreet" language="en_US">
230  <synopsis>
231  Play Mini-Voicemail prompts.
232  </synopsis>
233  <syntax>
234  <parameter name="mailbox" required="true" argsep="@">
235  <argument name="username" required="true">
236  <para>Voicemail username</para>
237  </argument>
238  <argument name="domain" required="true">
239  <para>Voicemail domain</para>
240  </argument>
241  </parameter>
242  <parameter name="options" required="false">
243  <optionlist>
244  <option name="b">
245  <para>Play the <literal>busy</literal> greeting to the calling party.</para>
246  </option>
247  <option name="s">
248  <para>Skip the playback of instructions for leaving a message to the calling party.</para>
249  </option>
250  <option name="u">
251  <para>Play the <literal>unavailable</literal> greeting.</para>
252  </option>
253  </optionlist>
254  </parameter>
255  </syntax>
256  <description>
257  <para>This application is part of the Mini-Voicemail system, configured in minivm.conf.</para>
258  <para>MinivmGreet() plays default prompts or user specific prompts for an account.</para>
259  <para>Busy and unavailable messages can be choosen, but will be overridden if a temporary
260  message exists for the account.</para>
261  <variablelist>
262  <variable name="MVM_GREET_STATUS">
263  <para>This is the status of the greeting playback.</para>
264  <value name="SUCCESS" />
265  <value name="USEREXIT" />
266  <value name="FAILED" />
267  </variable>
268  </variablelist>
269  </description>
270 </application>
271 <application name="MinivmNotify" language="en_US">
272  <synopsis>
273  Notify voicemail owner about new messages.
274  </synopsis>
275  <syntax>
276  <parameter name="mailbox" required="true" argsep="@">
277  <argument name="username" required="true">
278  <para>Voicemail username</para>
279  </argument>
280  <argument name="domain" required="true">
281  <para>Voicemail domain</para>
282  </argument>
283  </parameter>
284  <parameter name="options" required="false">
285  <optionlist>
286  <option name="template">
287  <para>E-mail template to use for voicemail notification</para>
288  </option>
289  </optionlist>
290  </parameter>
291  </syntax>
292  <description>
293  <para>This application is part of the Mini-Voicemail system, configured in minivm.conf.</para>
294  <para>MiniVMnotify forwards messages about new voicemail to e-mail and pager. If there's no user
295  account for that address, a temporary account will be used with default options (set in
296  <filename>minivm.conf</filename>).</para>
297  <para>If the channel variable <variable>MVM_COUNTER</variable> is set, this will be used in the message
298  file name and available in the template for the message.</para>
299  <para>If no template is given, the default email template will be used to send email and default pager
300  template to send paging message (if the user account is configured with a paging address.</para>
301  <variablelist>
302  <variable name="MVM_NOTIFY_STATUS">
303  <para>This is the status of the notification attempt</para>
304  <value name="SUCCESS" />
305  <value name="FAILED" />
306  </variable>
307  </variablelist>
308  </description>
309 </application>
310 <application name="MinivmDelete" language="en_US">
311  <synopsis>
312  Delete Mini-Voicemail voicemail messages.
313  </synopsis>
314  <syntax>
315  <parameter name="filename" required="true">
316  <para>File to delete</para>
317  </parameter>
318  </syntax>
319  <description>
320  <para>This application is part of the Mini-Voicemail system, configured in <filename>minivm.conf</filename>.</para>
321  <para>It deletes voicemail file set in MVM_FILENAME or given filename.</para>
322  <variablelist>
323  <variable name="MVM_DELETE_STATUS">
324  <para>This is the status of the delete operation.</para>
325  <value name="SUCCESS" />
326  <value name="FAILED" />
327  </variable>
328  </variablelist>
329  </description>
330 </application>
331 
332 <application name="MinivmAccMess" language="en_US">
333  <synopsis>
334  Record account specific messages.
335  </synopsis>
336  <syntax>
337  <parameter name="mailbox" required="true" argsep="@">
338  <argument name="username" required="true">
339  <para>Voicemail username</para>
340  </argument>
341  <argument name="domain" required="true">
342  <para>Voicemail domain</para>
343  </argument>
344  </parameter>
345  <parameter name="options" required="false">
346  <optionlist>
347  <option name="u">
348  <para>Record the <literal>unavailable</literal> greeting.</para>
349  </option>
350  <option name="b">
351  <para>Record the <literal>busy</literal> greeting.</para>
352  </option>
353  <option name="t">
354  <para>Record the temporary greeting.</para>
355  </option>
356  <option name="n">
357  <para>Account name.</para>
358  </option>
359  </optionlist>
360  </parameter>
361  </syntax>
362  <description>
363  <para>This application is part of the Mini-Voicemail system, configured in <filename>minivm.conf</filename>.</para>
364  <para>Use this application to record account specific audio/video messages for busy, unavailable
365  and temporary messages.</para>
366  <para>Account specific directories will be created if they do not exist.</para>
367  <variablelist>
368  <variable name="MVM_ACCMESS_STATUS">
369  <para>This is the result of the attempt to record the specified greeting.</para>
370  <para><literal>FAILED</literal> is set if the file can't be created.</para>
371  <value name="SUCCESS" />
372  <value name="FAILED" />
373  </variable>
374  </variablelist>
375  </description>
376 </application>
377 <application name="MinivmMWI" language="en_US">
378  <synopsis>
379  Send Message Waiting Notification to subscriber(s) of mailbox.
380  </synopsis>
381  <syntax>
382  <parameter name="mailbox" required="true" argsep="@">
383  <argument name="username" required="true">
384  <para>Voicemail username</para>
385  </argument>
386  <argument name="domain" required="true">
387  <para>Voicemail domain</para>
388  </argument>
389  </parameter>
390  <parameter name="urgent" required="true">
391  <para>Number of urgent messages in mailbox.</para>
392  </parameter>
393  <parameter name="new" required="true">
394  <para>Number of new messages in mailbox.</para>
395  </parameter>
396  <parameter name="old" required="true">
397  <para>Number of old messages in mailbox.</para>
398  </parameter>
399  </syntax>
400  <description>
401  <para>This application is part of the Mini-Voicemail system, configured in <filename>minivm.conf</filename>.</para>
402  <para>MinivmMWI is used to send message waiting indication to any devices whose channels have
403  subscribed to the mailbox passed in the first parameter.</para>
404  </description>
405 </application>
406 <function name="MINIVMCOUNTER" language="en_US">
407  <synopsis>
408  Reads or sets counters for MiniVoicemail message.
409  </synopsis>
410  <syntax argsep=":">
411  <parameter name="account" required="true">
412  <para>If account is given and it exists, the counter is specific for the account.</para>
413  <para>If account is a domain and the domain directory exists, counters are specific for a domain.</para>
414  </parameter>
415  <parameter name="name" required="true">
416  <para>The name of the counter is a string, up to 10 characters.</para>
417  </parameter>
418  <parameter name="operand">
419  <para>The counters never goes below zero. Valid operands for changing the value of a counter when assigning a value are:</para>
420  <enumlist>
421  <enum name="i"><para>Increment by value.</para></enum>
422  <enum name="d"><para>Decrement by value.</para></enum>
423  <enum name="s"><para>Set to value.</para></enum>
424  </enumlist>
425  </parameter>
426  </syntax>
427  <description>
428  <para>The operation is atomic and the counter is locked while changing the value. The counters are stored as text files in the minivm account directories. It might be better to use realtime functions if you are using a database to operate your Asterisk.</para>
429  </description>
430  <see-also>
431  <ref type="application">MinivmRecord</ref>
432  <ref type="application">MinivmGreet</ref>
433  <ref type="application">MinivmNotify</ref>
434  <ref type="application">MinivmDelete</ref>
435  <ref type="application">MinivmAccMess</ref>
436  <ref type="application">MinivmMWI</ref>
437  <ref type="function">MINIVMACCOUNT</ref>
438  </see-also>
439 </function>
440 <function name="MINIVMACCOUNT" language="en_US">
441  <synopsis>
442  Gets MiniVoicemail account information.
443  </synopsis>
444  <syntax argsep=":">
445  <parameter name="account" required="true" />
446  <parameter name="item" required="true">
447  <para>Valid items are:</para>
448  <enumlist>
449  <enum name="path">
450  <para>Path to account mailbox (if account exists, otherwise temporary mailbox).</para>
451  </enum>
452  <enum name="hasaccount">
453  <para>1 is static Minivm account exists, 0 otherwise.</para>
454  </enum>
455  <enum name="fullname">
456  <para>Full name of account owner.</para>
457  </enum>
458  <enum name="email">
459  <para>Email address used for account.</para>
460  </enum>
461  <enum name="etemplate">
462  <para>Email template for account (default template if none is configured).</para>
463  </enum>
464  <enum name="ptemplate">
465  <para>Pager template for account (default template if none is configured).</para>
466  </enum>
467  <enum name="accountcode">
468  <para>Account code for the voicemail account.</para>
469  </enum>
470  <enum name="pincode">
471  <para>Pin code for voicemail account.</para>
472  </enum>
473  <enum name="timezone">
474  <para>Time zone for voicemail account.</para>
475  </enum>
476  <enum name="language">
477  <para>Language for voicemail account.</para>
478  </enum>
479  <enum name="&lt;channel variable name&gt;">
480  <para>Channel variable value (set in configuration for account).</para>
481  </enum>
482  </enumlist>
483  </parameter>
484  </syntax>
485  <description>
486  <para />
487  </description>
488  <see-also>
489  <ref type="application">MinivmRecord</ref>
490  <ref type="application">MinivmGreet</ref>
491  <ref type="application">MinivmNotify</ref>
492  <ref type="application">MinivmDelete</ref>
493  <ref type="application">MinivmAccMess</ref>
494  <ref type="application">MinivmMWI</ref>
495  <ref type="function">MINIVMCOUNTER</ref>
496  </see-also>
497 </function>
498  <managerEvent language="en_US" name="MiniVoiceMail">
499  <managerEventInstance class="EVENT_FLAG_CALL">
500  <synopsis>Raised when a notification is sent out by a MiniVoiceMail application</synopsis>
501  <syntax>
502  <channel_snapshot/>
503  <parameter name="Action">
504  <para>What action was taken. Currently, this will always be <literal>SentNotification</literal></para>
505  </parameter>
506  <parameter name="Mailbox">
507  <para>The mailbox that the notification was about, specified as <literal>mailbox</literal>@<literal>context</literal></para>
508  </parameter>
509  <parameter name="Counter">
510  <para>A message counter derived from the <literal>MVM_COUNTER</literal> channel variable.</para>
511  </parameter>
512  </syntax>
513  </managerEventInstance>
514  </managerEvent>
515 ***/
516 
517 #ifndef TRUE
518 #define TRUE 1
519 #endif
520 #ifndef FALSE
521 #define FALSE 0
522 #endif
523 
524 
525 #define MVM_REVIEW (1 << 0) /*!< Review message */
526 #define MVM_OPERATOR (1 << 1) /*!< Operator exit during voicemail recording */
527 #define MVM_REALTIME (1 << 2) /*!< This user is a realtime account */
528 #define MVM_SVMAIL (1 << 3)
529 #define MVM_ENVELOPE (1 << 4)
530 #define MVM_PBXSKIP (1 << 9)
531 #define MVM_ALLOCED (1 << 13)
532 
533 /*! \brief Default mail command to mail voicemail. Change it with the
534  mailcmd= command in voicemail.conf */
535 #define SENDMAIL "/usr/sbin/sendmail -t"
536 
537 #define SOUND_INTRO "vm-intro"
538 #define B64_BASEMAXINLINE 256 /*!< Buffer size for Base 64 attachment encoding */
539 #define B64_BASELINELEN 72 /*!< Line length for Base 64 endoded messages */
540 #define EOL "\r\n"
541 
542 #define MAX_DATETIME_FORMAT 512
543 #define MAX_NUM_CID_CONTEXTS 10
544 
545 #define ERROR_LOCK_PATH -100
546 #define VOICEMAIL_DIR_MODE 0700
547 
548 #define VOICEMAIL_CONFIG "minivm.conf"
549 #define ASTERISK_USERNAME "asterisk" /*!< Default username for sending mail is asterisk\@localhost */
550 
551 /*! \brief Message types for notification */
555  /* For trunk: MVM_MESSAGE_JABBER, */
556 };
557 
558 static char MVM_SPOOL_DIR[PATH_MAX];
559 
560 /* Module declarations */
561 static char *app_minivm_record = "MinivmRecord"; /* Leave a message */
562 static char *app_minivm_greet = "MinivmGreet"; /* Play voicemail prompts */
563 static char *app_minivm_notify = "MinivmNotify"; /* Notify about voicemail by using one of several methods */
564 static char *app_minivm_delete = "MinivmDelete"; /* Notify about voicemail by using one of several methods */
565 static char *app_minivm_accmess = "MinivmAccMess"; /* Record personal voicemail messages */
566 static char *app_minivm_mwi = "MinivmMWI";
567 
568 
569 
571  OPT_SILENT = (1 << 0),
572  OPT_BUSY_GREETING = (1 << 1),
574  OPT_TEMP_GREETING = (1 << 3),
575  OPT_NAME_GREETING = (1 << 4),
576  OPT_RECORDGAIN = (1 << 5),
577 };
578 
582 };
583 
589 });
590 
596 });
597 
598 /*!\internal
599  * \brief Structure for linked list of Mini-Voicemail users: \ref minivm_accounts */
601  char username[AST_MAX_CONTEXT]; /*!< Mailbox username */
602  char domain[AST_MAX_CONTEXT]; /*!< Voicemail domain */
603 
604  char pincode[10]; /*!< Secret pin code, numbers only */
605  char fullname[120]; /*!< Full name, for directory app */
606  char email[80]; /*!< E-mail address - override */
607  char pager[80]; /*!< E-mail address to pager (no attachment) */
608  char accountcode[AST_MAX_ACCOUNT_CODE]; /*!< Voicemail account account code */
609  char serveremail[80]; /*!< From: Mail address */
610  char externnotify[160]; /*!< Configurable notification command */
611  char language[MAX_LANGUAGE]; /*!< Config: Language setting */
612  char zonetag[80]; /*!< Time zone */
613  char uniqueid[20]; /*!< Unique integer identifier */
614  char exit[80]; /*!< Options for exiting from voicemail() */
615  char attachfmt[80]; /*!< Format for voicemail audio file attachment */
616  char etemplate[80]; /*!< Pager template */
617  char ptemplate[80]; /*!< Voicemail format */
618  unsigned int flags; /*!< MVM_ flags */
619  struct ast_variable *chanvars; /*!< Variables for e-mail template */
620  double volgain; /*!< Volume gain for voicemails sent via e-mail */
622 };
623 
624 /*!\internal
625  * \brief The list of e-mail accounts */
627 
628 /*!\internal
629  * \brief Linked list of e-mail templates in various languages
630  * These are used as templates for e-mails, pager messages and jabber messages
631  * \ref message_templates
632 */
634  char name[80]; /*!< Template name */
635  char *body; /*!< Body of this template */
636  char fromaddress[100]; /*!< Who's sending the e-mail? */
637  char serveremail[80]; /*!< From: Mail address */
638  char subject[100]; /*!< Subject line */
639  char charset[32]; /*!< Default character set for this template */
640  char locale[20]; /*!< Locale for setlocale() */
641  char dateformat[80]; /*!< Date format to use in this attachment */
642  int attachment; /*!< Attachment of media yes/no - no for pager messages */
643  AST_LIST_ENTRY(minivm_template) list; /*!< List mechanics */
644 };
645 
646 /*! \brief The list of e-mail templates */
648 
649 /*! \brief Options for leaving voicemail with the voicemail() application */
651  unsigned int flags;
652  signed char record_gain;
653 };
654 
655 /*! \brief Structure for base64 encoding */
656 struct b64_baseio {
657  int iocp;
658  int iolen;
660  int ateof;
661  unsigned char iobuf[B64_BASEMAXINLINE];
662 };
663 
664 /*! \brief Voicemail time zones */
665 struct minivm_zone {
666  char name[80]; /*!< Name of this time zone */
667  char timezone[80]; /*!< Timezone definition */
668  char msg_format[BUFSIZ]; /*!< Not used in minivm ...yet */
669  AST_LIST_ENTRY(minivm_zone) list; /*!< List mechanics */
670 };
671 
672 /*! \brief The list of e-mail time zones */
674 
675 /*! \brief Structure for gathering statistics */
676 struct minivm_stats {
677  int voicemailaccounts; /*!< Number of static accounts */
678  int timezones; /*!< Number of time zones */
679  int templates; /*!< Number of templates */
680 
681  struct timeval reset; /*!< Time for last reset */
682  int receivedmessages; /*!< Number of received messages since reset */
683  struct timeval lastreceived; /*!< Time for last voicemail sent */
684 };
685 
686 /*! \brief Statistics for voicemail */
688 
689 AST_MUTEX_DEFINE_STATIC(minivmlock); /*!< Lock to protect voicemail system */
690 AST_MUTEX_DEFINE_STATIC(minivmloglock); /*!< Lock to protect voicemail system log file */
691 
692 static FILE *minivmlogfile; /*!< The minivm log file */
693 
694 static int global_vmminmessage; /*!< Minimum duration of messages */
695 static int global_vmmaxmessage; /*!< Maximum duration of message */
696 static int global_maxsilence; /*!< Maximum silence during recording */
697 static int global_maxgreet; /*!< Maximum length of prompts */
698 static int global_silencethreshold = 128;
699 static char global_mailcmd[160]; /*!< Configurable mail cmd */
700 static char global_externnotify[160]; /*!< External notification application */
701 static char global_logfile[PATH_MAX]; /*!< Global log file for messages */
702 static char default_vmformat[80];
703 
704 static struct ast_flags globalflags = {0}; /*!< Global voicemail flags */
706 
707 static double global_volgain; /*!< Volume gain for voicmemail via e-mail */
708 
709 /*!\internal
710  * \brief Default dateformat, can be overridden in configuration file */
711 #define DEFAULT_DATEFORMAT "%A, %B %d, %Y at %r"
712 #define DEFAULT_CHARSET "ISO-8859-1"
713 
714 /* Forward declarations */
715 static char *message_template_parse_filebody(const char *filename);
716 static char *message_template_parse_emailbody(const char *body);
717 static int create_vmaccount(char *name, struct ast_variable *var, int realtime);
718 static struct minivm_account *find_user_realtime(const char *domain, const char *username);
719 static char *handle_minivm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
720 
721 /*!\internal
722  * \brief Create message template */
723 static struct minivm_template *message_template_create(const char *name)
724 {
725  struct minivm_template *template;
726 
727  template = ast_calloc(1, sizeof(*template));
728  if (!template)
729  return NULL;
730 
731  /* Set some defaults for templates */
732  ast_copy_string(template->name, name, sizeof(template->name));
733  ast_copy_string(template->dateformat, DEFAULT_DATEFORMAT, sizeof(template->dateformat));
734  ast_copy_string(template->charset, DEFAULT_CHARSET, sizeof(template->charset));
735  ast_copy_string(template->subject, "New message in mailbox ${MVM_USERNAME}@${MVM_DOMAIN}", sizeof(template->subject));
736  template->attachment = TRUE;
737 
738  return template;
739 }
740 
741 /*!\internal
742  * \brief Release memory allocated by message template */
743 static void message_template_free(struct minivm_template *template)
744 {
745  if (template->body)
746  ast_free(template->body);
747 
748  ast_free (template);
749 }
750 
751 /*!\internal
752  * \brief Build message template from configuration */
753 static int message_template_build(const char *name, struct ast_variable *var)
754 {
755  struct minivm_template *template;
756  int error = 0;
757 
758  template = message_template_create(name);
759  if (!template) {
760  ast_log(LOG_ERROR, "Out of memory, can't allocate message template object %s.\n", name);
761  return -1;
762  }
763 
764  while (var) {
765  ast_debug(3, "Configuring template option %s = \"%s\" for template %s\n", var->name, var->value, name);
766  if (!strcasecmp(var->name, "fromaddress")) {
767  ast_copy_string(template->fromaddress, var->value, sizeof(template->fromaddress));
768  } else if (!strcasecmp(var->name, "fromemail")) {
769  ast_copy_string(template->serveremail, var->value, sizeof(template->serveremail));
770  } else if (!strcasecmp(var->name, "subject")) {
771  ast_copy_string(template->subject, var->value, sizeof(template->subject));
772  } else if (!strcasecmp(var->name, "locale")) {
773  ast_copy_string(template->locale, var->value, sizeof(template->locale));
774  } else if (!strcasecmp(var->name, "attachmedia")) {
775  template->attachment = ast_true(var->value);
776  } else if (!strcasecmp(var->name, "dateformat")) {
777  ast_copy_string(template->dateformat, var->value, sizeof(template->dateformat));
778  } else if (!strcasecmp(var->name, "charset")) {
779  ast_copy_string(template->charset, var->value, sizeof(template->charset));
780  } else if (!strcasecmp(var->name, "templatefile")) {
781  if (template->body)
782  ast_free(template->body);
783  template->body = message_template_parse_filebody(var->value);
784  if (!template->body) {
785  ast_log(LOG_ERROR, "Error reading message body definition file %s\n", var->value);
786  error++;
787  }
788  } else if (!strcasecmp(var->name, "messagebody")) {
789  if (template->body)
790  ast_free(template->body);
791  template->body = message_template_parse_emailbody(var->value);
792  if (!template->body) {
793  ast_log(LOG_ERROR, "Error parsing message body definition:\n %s\n", var->value);
794  error++;
795  }
796  } else {
797  ast_log(LOG_ERROR, "Unknown message template configuration option \"%s=%s\"\n", var->name, var->value);
798  error++;
799  }
800  var = var->next;
801  }
802  if (error)
803  ast_log(LOG_ERROR, "-- %d errors found parsing message template definition %s\n", error, name);
804 
808 
810 
811  return error;
812 }
813 
814 /*!\internal
815  * \brief Find named template */
816 static struct minivm_template *message_template_find(const char *name)
817 {
818  struct minivm_template *this, *res = NULL;
819 
820  if (ast_strlen_zero(name))
821  return NULL;
822 
825  if (!strcasecmp(this->name, name)) {
826  res = this;
827  break;
828  }
829  }
831 
832  return res;
833 }
834 
835 
836 /*!\internal
837  * \brief Clear list of templates */
838 static void message_destroy_list(void)
839 {
840  struct minivm_template *this;
842  while ((this = AST_LIST_REMOVE_HEAD(&message_templates, list))) {
843  message_template_free(this);
844  }
845 
847 }
848 
849 /*!\internal
850  * \brief read buffer from file (base64 conversion) */
851 static int b64_inbuf(struct b64_baseio *bio, FILE *fi)
852 {
853  int l;
854 
855  if (bio->ateof)
856  return 0;
857 
858  if ((l = fread(bio->iobuf, 1, B64_BASEMAXINLINE, fi)) != B64_BASEMAXINLINE) {
859  bio->ateof = 1;
860  if (l == 0) {
861  /* Assume EOF */
862  return 0;
863  }
864  }
865 
866  bio->iolen = l;
867  bio->iocp = 0;
868 
869  return 1;
870 }
871 
872 /*!\internal
873  * \brief read character from file to buffer (base64 conversion) */
874 static int b64_inchar(struct b64_baseio *bio, FILE *fi)
875 {
876  if (bio->iocp >= bio->iolen) {
877  if (!b64_inbuf(bio, fi))
878  return EOF;
879  }
880 
881  return bio->iobuf[bio->iocp++];
882 }
883 
884 /*!\internal
885  * \brief write buffer to file (base64 conversion) */
886 static int b64_ochar(struct b64_baseio *bio, int c, FILE *so)
887 {
888  if (bio->linelength >= B64_BASELINELEN) {
889  if (fputs(EOL,so) == EOF)
890  return -1;
891 
892  bio->linelength= 0;
893  }
894 
895  if (putc(((unsigned char) c), so) == EOF)
896  return -1;
897 
898  bio->linelength++;
899 
900  return 1;
901 }
902 
903 /*!\internal
904  * \brief Encode file to base64 encoding for email attachment (base64 conversion) */
905 static int base_encode(char *filename, FILE *so)
906 {
907  unsigned char dtable[B64_BASEMAXINLINE];
908  int i,hiteof= 0;
909  FILE *fi;
910  struct b64_baseio bio;
911 
912  memset(&bio, 0, sizeof(bio));
913  bio.iocp = B64_BASEMAXINLINE;
914 
915  if (!(fi = fopen(filename, "rb"))) {
916  ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
917  return -1;
918  }
919 
920  for (i= 0; i<9; i++) {
921  dtable[i]= 'A'+i;
922  dtable[i+9]= 'J'+i;
923  dtable[26+i]= 'a'+i;
924  dtable[26+i+9]= 'j'+i;
925  }
926  for (i= 0; i < 8; i++) {
927  dtable[i+18]= 'S'+i;
928  dtable[26+i+18]= 's'+i;
929  }
930  for (i= 0; i < 10; i++) {
931  dtable[52+i]= '0'+i;
932  }
933  dtable[62]= '+';
934  dtable[63]= '/';
935 
936  while (!hiteof){
937  unsigned char igroup[3], ogroup[4];
938  int c,n;
939 
940  igroup[0]= igroup[1]= igroup[2]= 0;
941 
942  for (n= 0; n < 3; n++) {
943  if ((c = b64_inchar(&bio, fi)) == EOF) {
944  hiteof= 1;
945  break;
946  }
947  igroup[n]= (unsigned char)c;
948  }
949 
950  if (n> 0) {
951  ogroup[0]= dtable[igroup[0]>>2];
952  ogroup[1]= dtable[((igroup[0]&3)<<4) | (igroup[1]>>4)];
953  ogroup[2]= dtable[((igroup[1]&0xF)<<2) | (igroup[2]>>6)];
954  ogroup[3]= dtable[igroup[2]&0x3F];
955 
956  if (n<3) {
957  ogroup[3]= '=';
958 
959  if (n<2)
960  ogroup[2]= '=';
961  }
962 
963  for (i= 0;i<4;i++)
964  b64_ochar(&bio, ogroup[i], so);
965  }
966  }
967 
968  /* Put end of line - line feed */
969  if (fputs(EOL, so) == EOF)
970  return 0;
971 
972  fclose(fi);
973 
974  return 1;
975 }
976 
977 static int get_date(char *s, int len)
978 {
979  struct ast_tm tm;
980  struct timeval now = ast_tvnow();
981 
982  ast_localtime(&now, &tm, NULL);
983  return ast_strftime(s, len, "%a %b %e %r %Z %Y", &tm);
984 }
985 
986 
987 /*!\internal
988  * \brief Free user structure - if it's allocated */
989 static void free_user(struct minivm_account *vmu)
990 {
991  if (vmu->chanvars)
993  ast_free(vmu);
994 }
995 
996 
997 
998 /*!\internal
999  * \brief Prepare for voicemail template by adding channel variables
1000  * to the channel
1001 */
1002 static void prep_email_sub_vars(struct ast_channel *channel, const struct minivm_account *vmu, const char *cidnum, const char *cidname, const char *dur, const char *date, const char *counter)
1003 {
1004  char callerid[256];
1005  struct ast_variable *var;
1006 
1007  if (!channel) {
1008  ast_log(LOG_ERROR, "No allocated channel, giving up...\n");
1009  return;
1010  }
1011 
1012  for (var = vmu->chanvars ; var ; var = var->next) {
1013  pbx_builtin_setvar_helper(channel, var->name, var->value);
1014  }
1015 
1016  /* Prepare variables for substition in email body and subject */
1017  pbx_builtin_setvar_helper(channel, "MVM_NAME", vmu->fullname);
1018  pbx_builtin_setvar_helper(channel, "MVM_DUR", dur);
1019  pbx_builtin_setvar_helper(channel, "MVM_DOMAIN", vmu->domain);
1020  pbx_builtin_setvar_helper(channel, "MVM_USERNAME", vmu->username);
1021  pbx_builtin_setvar_helper(channel, "MVM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
1022  pbx_builtin_setvar_helper(channel, "MVM_CIDNAME", (cidname ? cidname : "an unknown caller"));
1023  pbx_builtin_setvar_helper(channel, "MVM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
1024  pbx_builtin_setvar_helper(channel, "MVM_DATE", date);
1025  if (!ast_strlen_zero(counter))
1026  pbx_builtin_setvar_helper(channel, "MVM_COUNTER", counter);
1027 }
1028 
1029 /*!\internal
1030  * \brief Set default values for Mini-Voicemail users */
1031 static void populate_defaults(struct minivm_account *vmu)
1032 {
1033  ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
1034  ast_copy_string(vmu->attachfmt, default_vmformat, sizeof(vmu->attachfmt));
1035  vmu->volgain = global_volgain;
1036 }
1037 
1038 /*!\internal
1039  * \brief Allocate new vm user and set default values */
1040 static struct minivm_account *mvm_user_alloc(void)
1041 {
1042  struct minivm_account *new;
1043 
1044  new = ast_calloc(1, sizeof(*new));
1045  if (!new)
1046  return NULL;
1047  populate_defaults(new);
1048 
1049  return new;
1050 }
1051 
1052 
1053 /*!\internal
1054  * \brief Clear list of users */
1055 static void vmaccounts_destroy_list(void)
1056 {
1057  struct minivm_account *this;
1059  while ((this = AST_LIST_REMOVE_HEAD(&minivm_accounts, list)))
1060  ast_free(this);
1062 }
1063 
1064 
1065 /*!\internal
1066  * \brief Find user from static memory object list */
1067 static struct minivm_account *find_account(const char *domain, const char *username, int createtemp)
1068 {
1069  struct minivm_account *vmu = NULL, *cur;
1070 
1071 
1072  if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
1073  ast_log(LOG_NOTICE, "No username or domain? \n");
1074  return NULL;
1075  }
1076  ast_debug(3, "Looking for voicemail user %s in domain %s\n", username, domain);
1077 
1080  /* Is this the voicemail account we're looking for? */
1081  if (!strcasecmp(domain, cur->domain) && !strcasecmp(username, cur->username))
1082  break;
1083  }
1085 
1086  if (cur) {
1087  ast_debug(3, "Found account for %s@%s\n", username, domain);
1088  vmu = cur;
1089 
1090  } else
1091  vmu = find_user_realtime(domain, username);
1092 
1093  if (createtemp && !vmu) {
1094  /* Create a temporary user, send e-mail and be gone */
1095  vmu = mvm_user_alloc();
1097  if (vmu) {
1098  ast_copy_string(vmu->username, username, sizeof(vmu->username));
1099  ast_copy_string(vmu->domain, domain, sizeof(vmu->domain));
1100  ast_debug(1, "Created temporary account\n");
1101  }
1102 
1103  }
1104  return vmu;
1105 }
1106 
1107 /*!\internal
1108  * \brief Find user in realtime storage
1109  * \return pointer to minivm_account structure
1110 */
1111 static struct minivm_account *find_user_realtime(const char *domain, const char *username)
1112 {
1113  struct ast_variable *var;
1114  struct minivm_account *retval;
1115  char name[MAXHOSTNAMELEN];
1116 
1117  retval = mvm_user_alloc();
1118  if (!retval)
1119  return NULL;
1120 
1121  if (username)
1122  ast_copy_string(retval->username, username, sizeof(retval->username));
1123 
1124  populate_defaults(retval);
1125  var = ast_load_realtime("minivm", "username", username, "domain", domain, SENTINEL);
1126 
1127  if (!var) {
1128  ast_free(retval);
1129  return NULL;
1130  }
1131 
1132  snprintf(name, sizeof(name), "%s@%s", username, domain);
1133  create_vmaccount(name, var, TRUE);
1134 
1135  ast_variables_destroy(var);
1136  return retval;
1137 }
1138 
1139 /*!\internal
1140  * \brief Check if the string would need encoding within the MIME standard, to
1141  * avoid confusing certain mail software that expects messages to be 7-bit
1142  * clean.
1143  */
1144 static int check_mime(const char *str)
1145 {
1146  for (; *str; str++) {
1147  if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
1148  return 1;
1149  }
1150  }
1151  return 0;
1152 }
1153 
1154 /*!\internal
1155  * \brief Encode a string according to the MIME rules for encoding strings
1156  * that are not 7-bit clean or contain control characters.
1157  *
1158  * Additionally, if the encoded string would exceed the MIME limit of 76
1159  * characters per line, then the encoding will be broken up into multiple
1160  * sections, separated by a space character, in order to facilitate
1161  * breaking up the associated header across multiple lines.
1162  *
1163  * \param end An expandable buffer for holding the result
1164  * \param maxlen \see ast_str
1165  * \param charset Character set in which the result should be encoded
1166  * \param start A string to be encoded
1167  * \param preamble The length of the first line already used for this string,
1168  * to ensure that each line maintains a maximum length of 76 chars.
1169  * \param postamble the length of any additional characters appended to the
1170  * line, used to ensure proper field wrapping.
1171  * \return The encoded string.
1172  */
1173 static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *charset, const char *start, size_t preamble, size_t postamble)
1174 {
1175  struct ast_str *tmp = ast_str_alloca(80);
1176  int first_section = 1;
1177 
1178  ast_str_reset(*end);
1179  ast_str_set(&tmp, -1, "=?%s?Q?", charset);
1180  for (; *start; start++) {
1181  int need_encoding = 0;
1182  if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
1183  need_encoding = 1;
1184  }
1185  if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
1186  (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
1187  (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
1188  (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
1189  /* Start new line */
1190  ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
1191  ast_str_set(&tmp, -1, "=?%s?Q?", charset);
1192  first_section = 0;
1193  }
1194  if (need_encoding && *start == ' ') {
1195  ast_str_append(&tmp, -1, "_");
1196  } else if (need_encoding) {
1197  ast_str_append(&tmp, -1, "=%hhX", *start);
1198  } else {
1199  ast_str_append(&tmp, -1, "%c", *start);
1200  }
1201  }
1202  ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
1203  return ast_str_buffer(*end);
1204 }
1205 
1206 /*!\internal
1207  * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
1208  * \param from The string to work with.
1209  * \param buf The destination buffer to write the modified quoted string.
1210  * \param maxlen Always zero. \see ast_str
1211  *
1212  * \return The destination string with quotes wrapped on it (the to field).
1213  */
1214 static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
1215 {
1216  const char *ptr;
1217 
1218  /* We're only ever passing 0 to maxlen, so short output isn't possible */
1219  ast_str_set(buf, maxlen, "\"");
1220  for (ptr = from; *ptr; ptr++) {
1221  if (*ptr == '"' || *ptr == '\\') {
1222  ast_str_append(buf, maxlen, "\\%c", *ptr);
1223  } else {
1224  ast_str_append(buf, maxlen, "%c", *ptr);
1225  }
1226  }
1227  ast_str_append(buf, maxlen, "\"");
1228 
1229  return ast_str_buffer(*buf);
1230 }
1231 
1232 /*!\internal
1233  * \brief Send voicemail with audio file as an attachment */
1234 static int sendmail(struct minivm_template *template, struct minivm_account *vmu, char *cidnum, char *cidname, const char *filename, char *format, int duration, int attach_user_voicemail, enum mvm_messagetype type, const char *counter)
1235 {
1236  RAII_VAR(struct ast_str *, str1, ast_str_create(16), ast_free);
1237  RAII_VAR(struct ast_str *, str2, ast_str_create(16), ast_free);
1238  FILE *p = NULL;
1239  int pfd;
1240  char email[256] = "";
1241  char who[256] = "";
1242  char date[256];
1243  char bound[256];
1244  char fname[PATH_MAX];
1245  char dur[PATH_MAX];
1246  char tmp[80] = "/tmp/astmail-XXXXXX";
1247  char mail_cmd_buffer[PATH_MAX];
1248  char sox_gain_tmpdir[PATH_MAX] = ""; /* Only used with volgain */
1249  char *file_to_delete = NULL, *dir_to_delete = NULL;
1250  struct timeval now;
1251  struct ast_tm tm;
1252  struct minivm_zone *the_zone = NULL;
1253  struct ast_channel *chan = NULL;
1254  char *fromaddress;
1255  char *fromemail;
1256  int res = -1;
1257 
1258  if (!str1 || !str2) {
1259  return -1;
1260  }
1261 
1262  if (type == MVM_MESSAGE_EMAIL) {
1263  if (vmu && !ast_strlen_zero(vmu->email)) {
1264  ast_copy_string(email, vmu->email, sizeof(email));
1265  } else if (!ast_strlen_zero(vmu->username) && !ast_strlen_zero(vmu->domain))
1266  snprintf(email, sizeof(email), "%s@%s", vmu->username, vmu->domain);
1267  } else if (type == MVM_MESSAGE_PAGE) {
1268  ast_copy_string(email, vmu->pager, sizeof(email));
1269  }
1270 
1271  if (ast_strlen_zero(email)) {
1272  ast_log(LOG_WARNING, "No address to send message to.\n");
1273  return -1;
1274  }
1275 
1276  ast_debug(3, "Sending mail to %s@%s - Using template %s\n", vmu->username, vmu->domain, template->name);
1277 
1278  if (!strcmp(format, "wav49"))
1279  format = "WAV";
1280 
1281  /* If we have a gain option, process it now with sox */
1282  if (type == MVM_MESSAGE_EMAIL && (vmu->volgain < -.001 || vmu->volgain > .001) ) {
1283  char sox_gain_cmd[PATH_MAX];
1284 
1285  ast_copy_string(sox_gain_tmpdir, "/tmp/minivm-gain-XXXXXX", sizeof(sox_gain_tmpdir));
1286  ast_debug(3, "sox_gain_tmpdir: %s\n", sox_gain_tmpdir);
1287  if (!mkdtemp(sox_gain_tmpdir)) {
1288  ast_log(LOG_WARNING, "Failed to create temporary directory for volgain: %d\n", errno);
1289  return -1;
1290  }
1291  snprintf(fname, sizeof(fname), "%s/output.%s", sox_gain_tmpdir, format);
1292  snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s", vmu->volgain, filename, format, fname);
1293  ast_safe_system(sox_gain_cmd);
1294  ast_debug(3, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", filename, format, vmu->volgain, vmu->username);
1295 
1296  /* Mark some things for deletion */
1297  file_to_delete = fname;
1298  dir_to_delete = sox_gain_tmpdir;
1299  } else {
1300  snprintf(fname, sizeof(fname), "%s.%s", filename, format);
1301  }
1302 
1303  if (template->attachment)
1304  ast_debug(1, "Attaching file '%s', format '%s', uservm is '%d'\n", fname, format, attach_user_voicemail);
1305 
1306  /* Make a temporary file instead of piping directly to sendmail, in case the mail
1307  command hangs */
1308  pfd = mkstemp(tmp);
1309  if (pfd > -1) {
1310  p = fdopen(pfd, "w");
1311  if (!p) {
1312  close(pfd);
1313  pfd = -1;
1314  }
1315  ast_debug(1, "Opening temp file for e-mail: %s\n", tmp);
1316  }
1317  if (!p) {
1318  ast_log(LOG_WARNING, "Unable to open temporary file '%s'\n", tmp);
1319  goto out;
1320  }
1321  /* Allocate channel used for chanvar substitution */
1322  chan = ast_dummy_channel_alloc();
1323  if (!chan) {
1324  goto out;
1325  }
1326 
1327  snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
1328 
1329  /* Does this user have a timezone specified? */
1330  if (!ast_strlen_zero(vmu->zonetag)) {
1331  /* Find the zone in the list */
1332  struct minivm_zone *z;
1335  if (strcmp(z->name, vmu->zonetag))
1336  continue;
1337  the_zone = z;
1338  }
1340  }
1341 
1342  now = ast_tvnow();
1343  ast_localtime(&now, &tm, the_zone ? the_zone->timezone : NULL);
1344  ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", &tm);
1345 
1346  /* Start printing the email to the temporary file */
1347  fprintf(p, "Date: %s\n", date);
1348 
1349  /* Set date format for voicemail mail */
1350  ast_strftime(date, sizeof(date), template->dateformat, &tm);
1351 
1352  /* Populate channel with channel variables for substitution */
1353  prep_email_sub_vars(chan, vmu, cidnum, cidname, dur, date, counter);
1354 
1355  /* Find email address to use */
1356  /* If there's a server e-mail address in the account, use that, otherwise template */
1357  fromemail = ast_strlen_zero(vmu->serveremail) ? template->serveremail : vmu->serveremail;
1358 
1359  /* Find name to user for server e-mail */
1360  fromaddress = ast_strlen_zero(template->fromaddress) ? "" : template->fromaddress;
1361 
1362  /* If needed, add hostname as domain */
1363  if (ast_strlen_zero(fromemail))
1364  fromemail = "asterisk";
1365 
1366  if (strchr(fromemail, '@'))
1367  ast_copy_string(who, fromemail, sizeof(who));
1368  else {
1369  char host[MAXHOSTNAMELEN];
1370  gethostname(host, sizeof(host)-1);
1371  snprintf(who, sizeof(who), "%s@%s", fromemail, host);
1372  }
1373 
1374  if (ast_strlen_zero(fromaddress)) {
1375  fprintf(p, "From: Asterisk PBX <%s>\n", who);
1376  } else {
1377  ast_debug(4, "Fromaddress template: %s\n", fromaddress);
1378  ast_str_substitute_variables(&str1, 0, chan, fromaddress);
1379  if (check_mime(ast_str_buffer(str1))) {
1380  int first_line = 1;
1381  char *ptr;
1382  ast_str_encode_mime(&str2, 0, template->charset, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
1383  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
1384  *ptr = '\0';
1385  fprintf(p, "%s %s\n", first_line ? "From:" : "", ast_str_buffer(str2));
1386  first_line = 0;
1387  /* Substring is smaller, so this will never grow */
1388  ast_str_set(&str2, 0, "%s", ptr + 1);
1389  }
1390  fprintf(p, "%s %s <%s>\n", first_line ? "From:" : "", ast_str_buffer(str2), who);
1391  } else {
1392  fprintf(p, "From: %s <%s>\n", ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
1393  }
1394  }
1395 
1396  fprintf(p, "Message-ID: <Asterisk-%u-%s-%d-%s>\n", (unsigned int)ast_random(), vmu->username, (int)getpid(), who);
1397 
1398  if (ast_strlen_zero(vmu->email)) {
1399  snprintf(email, sizeof(email), "%s@%s", vmu->username, vmu->domain);
1400  } else {
1401  ast_copy_string(email, vmu->email, sizeof(email));
1402  }
1403 
1404  if (check_mime(vmu->fullname)) {
1405  int first_line = 1;
1406  char *ptr;
1407  ast_str_encode_mime(&str2, 0, template->charset, vmu->fullname, strlen("To: "), strlen(email) + 3);
1408  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
1409  *ptr = '\0';
1410  fprintf(p, "%s %s\n", first_line ? "To:" : "", ast_str_buffer(str2));
1411  first_line = 0;
1412  /* Substring is smaller, so this will never grow */
1413  ast_str_set(&str2, 0, "%s", ptr + 1);
1414  }
1415  fprintf(p, "%s %s <%s>\n", first_line ? "To:" : "", ast_str_buffer(str2), email);
1416  } else {
1417  fprintf(p, "To: %s <%s>\n", ast_str_quote(&str2, 0, vmu->fullname), email);
1418  }
1419 
1420  if (!ast_strlen_zero(template->subject)) {
1421  ast_str_substitute_variables(&str1, 0, chan, template->subject);
1422  if (check_mime(ast_str_buffer(str1))) {
1423  int first_line = 1;
1424  char *ptr;
1425  ast_str_encode_mime(&str2, 0, template->charset, ast_str_buffer(str1), strlen("Subject: "), 0);
1426  while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
1427  *ptr = '\0';
1428  fprintf(p, "%s %s\n", first_line ? "Subject:" : "", ast_str_buffer(str2));
1429  first_line = 0;
1430  /* Substring is smaller, so this will never grow */
1431  ast_str_set(&str2, 0, "%s", ptr + 1);
1432  }
1433  fprintf(p, "%s %s\n", first_line ? "Subject:" : "", ast_str_buffer(str2));
1434  } else {
1435  fprintf(p, "Subject: %s\n", ast_str_buffer(str1));
1436  }
1437  } else {
1438  fprintf(p, "Subject: New message in mailbox %s@%s\n", vmu->username, vmu->domain);
1439  ast_debug(1, "Using default subject for this email \n");
1440  }
1441 
1442  if (DEBUG_ATLEAST(3))
1443  fprintf(p, "X-Asterisk-debug: template %s user account %s@%s\n", template->name, vmu->username, vmu->domain);
1444  fprintf(p, "MIME-Version: 1.0\n");
1445 
1446  /* Something unique. */
1447  snprintf(bound, sizeof(bound), "voicemail_%s%d%u", vmu->username, (int)getpid(), (unsigned int)ast_random());
1448 
1449  fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"\n\n\n", bound);
1450 
1451  fprintf(p, "--%s\n", bound);
1452  fprintf(p, "Content-Type: text/plain; charset=%s\nContent-Transfer-Encoding: 8bit\n\n", template->charset);
1453  if (!ast_strlen_zero(template->body)) {
1454  ast_str_substitute_variables(&str1, 0, chan, template->body);
1455  ast_debug(3, "Message now: %s\n-----\n", ast_str_buffer(str1));
1456  fprintf(p, "%s\n", ast_str_buffer(str1));
1457  } else {
1458  fprintf(p, "Dear %s:\n\n\tJust wanted to let you know you were just left a %s long message \n"
1459  "in mailbox %s from %s, on %s so you might\n"
1460  "want to check it when you get a chance. Thanks!\n\n\t\t\t\t--Asterisk\n\n", vmu->fullname,
1461  dur, vmu->username, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
1462  ast_debug(3, "Using default message body (no template)\n-----\n");
1463  }
1464  /* Eww. We want formats to tell us their own MIME type */
1465  if (template->attachment) {
1466  char *ctype = "audio/x-";
1467  ast_debug(3, "Attaching file to message: %s\n", fname);
1468  if (!strcasecmp(format, "ogg"))
1469  ctype = "application/";
1470 
1471  fprintf(p, "--%s\n", bound);
1472  fprintf(p, "Content-Type: %s%s; name=\"voicemailmsg.%s\"\n", ctype, format, format);
1473  fprintf(p, "Content-Transfer-Encoding: base64\n");
1474  fprintf(p, "Content-Description: Voicemail sound attachment.\n");
1475  fprintf(p, "Content-Disposition: attachment; filename=\"voicemail%s.%s\"\n\n", counter ? counter : "", format);
1476 
1477  base_encode(fname, p);
1478  fprintf(p, "\n\n--%s--\n.\n", bound);
1479  }
1480  fclose(p);
1481 
1482  chan = ast_channel_unref(chan);
1483 
1484  if (file_to_delete && dir_to_delete) {
1485  /* We can't delete these files ourselves because the mail command will execute in
1486  the background and we'll end up deleting them out from under it. */
1487  res = snprintf(mail_cmd_buffer, sizeof(mail_cmd_buffer),
1488  "( %s < %s ; rm -f %s %s ; rmdir %s ) &",
1489  global_mailcmd, tmp, tmp, file_to_delete, dir_to_delete);
1490  } else {
1491  res = snprintf(mail_cmd_buffer, sizeof(mail_cmd_buffer),
1492  "( %s < %s ; rm -f %s ) &",
1493  global_mailcmd, tmp, tmp);
1494  }
1495 
1496  if (res < sizeof(mail_cmd_buffer)) {
1497  file_to_delete = dir_to_delete = NULL;
1498  } else {
1499  ast_log(LOG_ERROR, "Could not send message, command line too long\n");
1500  res = -1;
1501  goto out;
1502  }
1503 
1504  ast_safe_system(mail_cmd_buffer);
1505  ast_debug(1, "Sent message to %s with command '%s'%s\n", vmu->email, global_mailcmd, template->attachment ? " - (media attachment)" : "");
1506  ast_debug(3, "Actual command used: %s\n", mail_cmd_buffer);
1507 
1508  res = 0;
1509 
1510 out:
1511  if (file_to_delete) {
1512  unlink(file_to_delete);
1513  }
1514 
1515  if (dir_to_delete) {
1516  rmdir(dir_to_delete);
1517  }
1518 
1519  return res;
1520 }
1521 
1522 /*!\internal
1523  * \brief Create directory based on components */
1524 static int make_dir(char *dest, int len, const char *domain, const char *username, const char *folder)
1525 {
1526  return snprintf(dest, len, "%s%s/%s%s%s", MVM_SPOOL_DIR, domain, username, ast_strlen_zero(folder) ? "" : "/", folder ? folder : "");
1527 }
1528 
1529 /*!\internal
1530  * \brief Checks if directory exists. Does not create directory, but builds string in dest
1531  * \param dest String. base directory.
1532  * \param len Int. Length base directory string.
1533  * \param domain String. Ignored if is null or empty string.
1534  * \param username String. Ignored if is null or empty string.
1535  * \param folder String. Ignored if is null or empty string.
1536  * \return 0 on failure, 1 on success.
1537  */
1538 static int check_dirpath(char *dest, int len, char *domain, char *username, char *folder)
1539 {
1540  struct stat filestat;
1541  make_dir(dest, len, domain, username, folder ? folder : "");
1542  if (stat(dest, &filestat)== -1)
1543  return FALSE;
1544  else
1545  return TRUE;
1546 }
1547 
1548 /*!\internal
1549  * \brief basically mkdir -p $dest/$domain/$username/$folder
1550  * \param dest String. base directory.
1551  * \param len Length of directory string
1552  * \param domain String. Ignored if is null or empty string.
1553  * \param folder String. Ignored if is null or empty string.
1554  * \param username String. Ignored if is null or empty string.
1555  * \return -1 on failure, 0 on success.
1556  */
1557 static int create_dirpath(char *dest, int len, char *domain, char *username, char *folder)
1558 {
1559  int res;
1560  make_dir(dest, len, domain, username, folder);
1561  if ((res = ast_mkdir(dest, 0777))) {
1562  ast_log(LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
1563  return -1;
1564  }
1565  ast_debug(2, "Creating directory for %s@%s folder %s : %s\n", username, domain, folder, dest);
1566  return 0;
1567 }
1568 
1569 
1570 /*!\internal
1571  * \brief Play intro message before recording voicemail
1572  */
1573 static int invent_message(struct ast_channel *chan, char *domain, char *username, int busy, char *ecodes)
1574 {
1575  int res;
1576  char fn[PATH_MAX];
1577 
1578  ast_debug(2, "Still preparing to play message ...\n");
1579 
1580  snprintf(fn, sizeof(fn), "%s%s/%s/greet", MVM_SPOOL_DIR, domain, username);
1581 
1582  if (ast_fileexists(fn, NULL, NULL) > 0) {
1583  res = ast_streamfile(chan, fn, ast_channel_language(chan));
1584  if (res)
1585  return -1;
1586  res = ast_waitstream(chan, ecodes);
1587  if (res)
1588  return res;
1589  } else {
1590  int numericusername = 1;
1591  char *i = username;
1592 
1593  ast_debug(2, "No personal prompts. Using default prompt set for language\n");
1594 
1595  while (*i) {
1596  ast_debug(2, "Numeric? Checking %c\n", *i);
1597  if (!isdigit(*i)) {
1598  numericusername = FALSE;
1599  break;
1600  }
1601  i++;
1602  }
1603 
1604  if (numericusername) {
1605  if (ast_streamfile(chan, "vm-theperson", ast_channel_language(chan)))
1606  return -1;
1607  if ((res = ast_waitstream(chan, ecodes)))
1608  return res;
1609 
1610  res = ast_say_digit_str(chan, username, ecodes, ast_channel_language(chan));
1611  if (res)
1612  return res;
1613  } else {
1614  if (ast_streamfile(chan, "vm-theextensionis", ast_channel_language(chan)))
1615  return -1;
1616  if ((res = ast_waitstream(chan, ecodes)))
1617  return res;
1618  }
1619  }
1620 
1621  res = ast_streamfile(chan, busy ? "vm-isonphone" : "vm-isunavail", ast_channel_language(chan));
1622  if (res)
1623  return -1;
1624  res = ast_waitstream(chan, ecodes);
1625  return res;
1626 }
1627 
1628 /*!\internal
1629  * \brief Delete media files and attribute file */
1630 static int vm_delete(char *file)
1631 {
1632  int res;
1633 
1634  ast_debug(1, "Deleting voicemail file %s\n", file);
1635 
1636  res = unlink(file); /* Remove the meta data file */
1637  res |= ast_filedelete(file, NULL); /* remove the media file */
1638  return res;
1639 }
1640 
1641 
1642 /*!\internal
1643  * \brief Record voicemail message & let caller review or re-record it, or set options if applicable */
1644 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
1645  int outsidecaller, struct minivm_account *vmu, int *duration, int *sound_duration, const char *unlockdir,
1646  signed char record_gain)
1647 {
1648  int cmd = 0;
1649  int max_attempts = 3;
1650  int attempts = 0;
1651  int recorded = 0;
1652  int message_exists = 0;
1653  signed char zero_gain = 0;
1654  char *acceptdtmf = "#";
1655  char *canceldtmf = "";
1656 
1657  /* Note that urgent and private are for flagging messages as such in the future */
1658 
1659  /* barf if no pointer passed to store duration in */
1660  if (duration == NULL) {
1661  ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
1662  return -1;
1663  }
1664 
1665  cmd = '3'; /* Want to start by recording */
1666 
1667  while ((cmd >= 0) && (cmd != 't')) {
1668  switch (cmd) {
1669  case '1':
1670  ast_verb(3, "Saving message as is\n");
1671  ast_stream_and_wait(chan, "vm-msgsaved", "");
1672  cmd = 't';
1673  break;
1674  case '2':
1675  /* Review */
1676  ast_verb(3, "Reviewing the message\n");
1677  ast_streamfile(chan, recordfile, ast_channel_language(chan));
1678  cmd = ast_waitstream(chan, AST_DIGIT_ANY);
1679  break;
1680  case '3':
1681  message_exists = 0;
1682  /* Record */
1683  if (recorded == 1)
1684  ast_verb(3, "Re-recording the message\n");
1685  else
1686  ast_verb(3, "Recording the message\n");
1687  if (recorded && outsidecaller)
1688  cmd = ast_play_and_wait(chan, "beep");
1689  recorded = 1;
1690  /* 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 */
1691  if (record_gain)
1692  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
1693  if (ast_test_flag(vmu, MVM_OPERATOR))
1694  canceldtmf = "0";
1695  cmd = ast_play_and_record_full(chan, playfile, recordfile, maxtime, fmt, duration, sound_duration, 0, global_silencethreshold, global_maxsilence, unlockdir, acceptdtmf, canceldtmf, 0, AST_RECORD_IF_EXISTS_OVERWRITE);
1696  if (record_gain)
1697  ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
1698  if (cmd == -1) /* User has hung up, no options to give */
1699  return cmd;
1700  if (cmd == '0')
1701  break;
1702  else if (cmd == '*')
1703  break;
1704  else {
1705  /* If all is well, a message exists */
1706  message_exists = 1;
1707  cmd = 0;
1708  }
1709  break;
1710  case '4':
1711  case '5':
1712  case '6':
1713  case '7':
1714  case '8':
1715  case '9':
1716  case '*':
1717  case '#':
1718  cmd = ast_play_and_wait(chan, "vm-sorry");
1719  break;
1720  case '0':
1721  if(!ast_test_flag(vmu, MVM_OPERATOR)) {
1722  cmd = ast_play_and_wait(chan, "vm-sorry");
1723  break;
1724  }
1725  if (message_exists || recorded) {
1726  cmd = ast_play_and_wait(chan, "vm-saveoper");
1727  if (!cmd)
1728  cmd = ast_waitfordigit(chan, 3000);
1729  if (cmd == '1') {
1730  ast_play_and_wait(chan, "vm-msgsaved");
1731  cmd = '0';
1732  } else {
1733  ast_play_and_wait(chan, "vm-deleted");
1734  vm_delete(recordfile);
1735  cmd = '0';
1736  }
1737  }
1738  return cmd;
1739  default:
1740  /* If the caller is an ouside caller, and the review option is enabled,
1741  allow them to review the message, but let the owner of the box review
1742  their OGM's */
1743  if (outsidecaller && !ast_test_flag(vmu, MVM_REVIEW))
1744  return cmd;
1745  if (message_exists) {
1746  cmd = ast_play_and_wait(chan, "vm-review");
1747  } else {
1748  cmd = ast_play_and_wait(chan, "vm-torerecord");
1749  if (!cmd)
1750  cmd = ast_waitfordigit(chan, 600);
1751  }
1752 
1753  if (!cmd && outsidecaller && ast_test_flag(vmu, MVM_OPERATOR)) {
1754  cmd = ast_play_and_wait(chan, "vm-reachoper");
1755  if (!cmd)
1756  cmd = ast_waitfordigit(chan, 600);
1757  }
1758  if (!cmd)
1759  cmd = ast_waitfordigit(chan, 6000);
1760  if (!cmd) {
1761  attempts++;
1762  }
1763  if (attempts > max_attempts) {
1764  cmd = 't';
1765  }
1766  }
1767  }
1768  if (outsidecaller)
1769  ast_play_and_wait(chan, "vm-goodbye");
1770  if (cmd == 't')
1771  cmd = 0;
1772  return cmd;
1773 }
1774 
1775 /*! \brief Run external notification for voicemail message */
1776 static void run_externnotify(struct ast_channel *chan, struct minivm_account *vmu)
1777 {
1778  char fquser[AST_MAX_CONTEXT * 2];
1779  char *argv[5] = { NULL };
1780  struct ast_party_caller *caller;
1781  char *cid;
1782  int idx;
1783 
1784  if (ast_strlen_zero(vmu->externnotify) && ast_strlen_zero(global_externnotify)) {
1785  return;
1786  }
1787 
1788  snprintf(fquser, sizeof(fquser), "%s@%s", vmu->username, vmu->domain);
1789 
1790  caller = ast_channel_caller(chan);
1791  idx = 0;
1792  argv[idx++] = ast_strlen_zero(vmu->externnotify) ? global_externnotify : vmu->externnotify;
1793  argv[idx++] = fquser;
1794  cid = S_COR(caller->id.name.valid, caller->id.name.str, NULL);
1795  if (cid) {
1796  argv[idx++] = cid;
1797  }
1798  cid = S_COR(caller->id.number.valid, caller->id.number.str, NULL);
1799  if (cid) {
1800  argv[idx++] = cid;
1801  }
1802  argv[idx] = NULL;
1803 
1804  ast_debug(1, "Executing: %s %s %s %s\n",
1805  argv[0], argv[1], argv[2] ?: "", argv[3] ?: "");
1806  ast_safe_execvp(1, argv[0], argv);
1807 }
1808 
1809 /*!\internal
1810  * \brief Send message to voicemail account owner */
1811 static int notify_new_message(struct ast_channel *chan, const char *templatename, struct minivm_account *vmu, const char *filename, long duration, const char *format, char *cidnum, char *cidname)
1812 {
1813  RAII_VAR(struct ast_json *, json_object, NULL, ast_json_unref);
1815  RAII_VAR(struct ast_mwi_state *, mwi_state, NULL, ao2_cleanup);
1816  char *stringp;
1817  struct minivm_template *etemplate;
1818  char *messageformat;
1819  int res = 0;
1820  char oldlocale[100];
1821  const char *counter;
1822 
1823  if (!ast_strlen_zero(vmu->attachfmt)) {
1824  if (strstr(format, vmu->attachfmt)) {
1825  format = vmu->attachfmt;
1826  } else {
1827  ast_log(LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->attachfmt, format, vmu->username, vmu->domain);
1828  }
1829  }
1830 
1831  etemplate = message_template_find(vmu->etemplate);
1832  if (!etemplate)
1833  etemplate = message_template_find(templatename);
1834  if (!etemplate)
1835  etemplate = message_template_find("email-default");
1836 
1837  /* Attach only the first format */
1838  stringp = messageformat = ast_strdupa(format);
1839  strsep(&stringp, "|");
1840 
1841  if (!ast_strlen_zero(etemplate->locale)) {
1842  char *new_locale;
1843  ast_copy_string(oldlocale, setlocale(LC_TIME, NULL), sizeof(oldlocale));
1844  ast_debug(2, "Changing locale from %s to %s\n", oldlocale, etemplate->locale);
1845  new_locale = setlocale(LC_TIME, etemplate->locale);
1846  if (new_locale == NULL) {
1847  ast_log(LOG_WARNING, "-_-_- Changing to new locale did not work. Locale: %s\n", etemplate->locale);
1848  }
1849  }
1850 
1851 
1852 
1853  /* Read counter if available */
1854  ast_channel_lock(chan);
1855  if ((counter = pbx_builtin_getvar_helper(chan, "MVM_COUNTER"))) {
1856  counter = ast_strdupa(counter);
1857  }
1858  ast_channel_unlock(chan);
1859 
1860  if (ast_strlen_zero(counter)) {
1861  ast_debug(2, "MVM_COUNTER not found\n");
1862  } else {
1863  ast_debug(2, "MVM_COUNTER found - will use it with value %s\n", counter);
1864  }
1865 
1866  res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->attachment, MVM_MESSAGE_EMAIL, counter);
1867 
1868  if (res == 0 && !ast_strlen_zero(vmu->pager)) {
1869  /* Find template for paging */
1870  etemplate = message_template_find(vmu->ptemplate);
1871  if (!etemplate)
1872  etemplate = message_template_find("pager-default");
1873 
1874  if (!ast_strlen_zero(etemplate->locale)) {
1875  ast_copy_string(oldlocale, setlocale(LC_TIME, ""), sizeof(oldlocale));
1876  setlocale(LC_TIME, etemplate->locale);
1877  }
1878 
1879  res = sendmail(etemplate, vmu, cidnum, cidname, filename, messageformat, duration, etemplate->attachment, MVM_MESSAGE_PAGE, counter);
1880  }
1881 
1882  mwi_state = ast_mwi_create(vmu->username, vmu->domain);
1883  if (!mwi_state) {
1884  goto notify_cleanup;
1885  }
1886  mwi_state->snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(chan));
1887 
1888  json_object = ast_json_pack("{s: s, s: s, s: s}",
1889  "Event", "MiniVoiceMail",
1890  "Action", "SentNotification",
1891  "Counter", counter ?: "");
1892  if (!json_object) {
1893  goto notify_cleanup;
1894  }
1895  message = ast_mwi_blob_create(mwi_state, ast_mwi_vm_app_type(), json_object);
1896  if (!message) {
1897  goto notify_cleanup;
1898  }
1899  stasis_publish(ast_mwi_topic(mwi_state->uniqueid), message);
1900 
1901 notify_cleanup:
1902  run_externnotify(chan, vmu); /* Run external notification */
1903  if (!ast_strlen_zero(etemplate->locale)) {
1904  setlocale(LC_TIME, oldlocale); /* Reset to old locale */
1905  }
1906  return res;
1907 }
1908 
1909 
1910 /*!\internal
1911  * \brief Record voicemail message, store into file prepared for sending e-mail */
1912 static int leave_voicemail(struct ast_channel *chan, char *username, struct leave_vm_options *options)
1913 {
1914  char tmptxtfile[PATH_MAX];
1915  char callerid[256];
1916  FILE *txt;
1917  int res = 0, txtdes;
1918  int duration = 0;
1919  int sound_duration = 0;
1920  char date[256];
1921  char tmpdir[PATH_MAX];
1922  char ext_context[256] = "";
1923  char fmt[80];
1924  char *domain;
1925  char tmp[256] = "";
1926  struct minivm_account *vmu;
1927  int userdir;
1928 
1929  ast_copy_string(tmp, username, sizeof(tmp));
1930  username = tmp;
1931  domain = strchr(tmp, '@');
1932  if (domain) {
1933  *domain = '\0';
1934  domain++;
1935  }
1936 
1937  if (!(vmu = find_account(domain, username, TRUE))) {
1938  /* We could not find user, let's exit */
1939  ast_log(LOG_ERROR, "Can't allocate temporary account for '%s@%s'\n", username, domain);
1940  pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
1941  return 0;
1942  }
1943 
1944  /* Setup pre-file if appropriate */
1945  if (strcmp(vmu->domain, "localhost"))
1946  snprintf(ext_context, sizeof(ext_context), "%s@%s", username, vmu->domain);
1947  else
1948  ast_copy_string(ext_context, vmu->domain, sizeof(ext_context));
1949 
1950  /* The meat of recording the message... All the announcements and beeps have been played*/
1951  if (ast_strlen_zero(vmu->attachfmt))
1952  ast_copy_string(fmt, default_vmformat, sizeof(fmt));
1953  else
1954  ast_copy_string(fmt, vmu->attachfmt, sizeof(fmt));
1955 
1956  if (ast_strlen_zero(fmt)) {
1957  ast_log(LOG_WARNING, "No format for saving voicemail? Default %s\n", default_vmformat);
1958  pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
1959  return res;
1960  }
1961 
1962  userdir = check_dirpath(tmpdir, sizeof(tmpdir), vmu->domain, username, "tmp");
1963 
1964  /* If we have no user directory, use generic temporary directory */
1965  if (!userdir) {
1966  create_dirpath(tmpdir, sizeof(tmpdir), "0000_minivm_temp", "mediafiles", "");
1967  ast_debug(3, "Creating temporary directory %s\n", tmpdir);
1968  }
1969 
1970 
1971  snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
1972 
1973  /* XXX This file needs to be in temp directory */
1974  txtdes = mkstemp(tmptxtfile);
1975  if (txtdes < 0) {
1976  ast_log(LOG_ERROR, "Unable to create message file %s: %s\n", tmptxtfile, strerror(errno));
1977  res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
1978  if (!res)
1979  res = ast_waitstream(chan, "");
1980  pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
1981  return res;
1982  }
1983 
1984  if (res >= 0) {
1985  /* Unless we're *really* silent, try to send the beep */
1986  res = ast_streamfile(chan, "beep", ast_channel_language(chan));
1987  if (!res)
1988  res = ast_waitstream(chan, "");
1989  }
1990 
1991  /* OEJ XXX Maybe this can be turned into a log file? Hmm. */
1992  /* Store information */
1993  ast_debug(2, "Open file for metadata: %s\n", tmptxtfile);
1994 
1995  res = play_record_review(chan, NULL, tmptxtfile, global_vmmaxmessage, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain);
1996 
1997  txt = fdopen(txtdes, "w+");
1998  if (!txt) {
1999  ast_log(LOG_WARNING, "Error opening text file for output\n");
2000  } else {
2001  struct ast_tm tm;
2002  struct timeval now = ast_tvnow();
2003  char timebuf[30];
2004  char logbuf[BUFSIZ];
2005  get_date(date, sizeof(date));
2006  ast_localtime(&now, &tm, NULL);
2007  ast_strftime(timebuf, sizeof(timebuf), "%H:%M:%S", &tm);
2008 
2009  ast_callerid_merge(callerid, sizeof(callerid),
2010  S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
2011  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
2012  "Unknown");
2013  snprintf(logbuf, sizeof(logbuf),
2014  /* "Mailbox:domain:macrocontext:exten:priority:callerchan:callerid:origdate:origtime:duration:durationstatus:accountcode" */
2015  "%s:%s:%s:%s:%d:%s:%s:%s:%s:%d:%s:%s\n",
2016  username,
2017  ast_channel_context(chan),
2019  ast_channel_exten(chan),
2020  ast_channel_priority(chan),
2021  ast_channel_name(chan),
2022  callerid,
2023  date,
2024  timebuf,
2025  duration,
2026  duration < global_vmminmessage ? "IGNORED" : "OK",
2027  vmu->accountcode
2028  );
2029  fprintf(txt, "%s", logbuf);
2030  if (minivmlogfile) {
2032  fprintf(minivmlogfile, "%s", logbuf);
2034  }
2035 
2036  if (sound_duration < global_vmminmessage) {
2037  ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, global_vmminmessage);
2038  fclose(txt);
2039  ast_filedelete(tmptxtfile, NULL);
2040  unlink(tmptxtfile);
2041  pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
2042  return 0;
2043  }
2044  fclose(txt); /* Close log file */
2045  if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
2046  ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
2047  unlink(tmptxtfile);
2048  pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
2049  if(ast_test_flag(vmu, MVM_ALLOCED))
2050  free_user(vmu);
2051  return 0;
2052  }
2053 
2054  /* Set channel variables for the notify application */
2055  pbx_builtin_setvar_helper(chan, "MVM_FILENAME", tmptxtfile);
2056  snprintf(timebuf, sizeof(timebuf), "%d", duration);
2057  pbx_builtin_setvar_helper(chan, "MVM_DURATION", timebuf);
2058  pbx_builtin_setvar_helper(chan, "MVM_FORMAT", fmt);
2059 
2060  }
2063 #if 0
2064  /* Go ahead and delete audio files from system, they're not needed any more */
2065  if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
2066  ast_filedelete(tmptxtfile, NULL);
2067  /* Even not being used at the moment, it's better to convert ast_log to ast_debug anyway */
2068  ast_debug(2, "-_-_- Deleted audio file after notification :: %s \n", tmptxtfile);
2069  }
2070 #endif
2071 
2072  if (res > 0)
2073  res = 0;
2074 
2075  if(ast_test_flag(vmu, MVM_ALLOCED))
2076  free_user(vmu);
2077 
2078  pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "SUCCESS");
2079  return res;
2080 }
2081 
2082 /*!\internal
2083  * \brief Queue a message waiting event */
2084 static void queue_mwi_event(const char *channel_id, const char *mbx, const char *ctx, int urgent, int new, int old)
2085 {
2086  char *mailbox, *context;
2087 
2088  mailbox = ast_strdupa(mbx);
2089  context = ast_strdupa(ctx);
2090  if (ast_strlen_zero(context)) {
2091  context = "default";
2092  }
2093 
2094  ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
2095 }
2096 
2097 /*!\internal
2098  * \brief Send MWI using interal Asterisk event subsystem */
2099 static int minivm_mwi_exec(struct ast_channel *chan, const char *data)
2100 {
2101  int argc;
2102  char *argv[4];
2103  int res = 0;
2104  char *tmpptr;
2105  char tmp[PATH_MAX];
2106  char *mailbox;
2107  char *domain;
2108  if (ast_strlen_zero(data)) {
2109  ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
2110  return -1;
2111  }
2112  tmpptr = ast_strdupa((char *)data);
2113  argc = ast_app_separate_args(tmpptr, ',', argv, ARRAY_LEN(argv));
2114  if (argc < 4) {
2115  ast_log(LOG_ERROR, "%d arguments passed to MiniVM_MWI, need 4.\n", argc);
2116  return -1;
2117  }
2118  ast_copy_string(tmp, argv[0], sizeof(tmp));
2119  mailbox = tmp;
2120  domain = strchr(tmp, '@');
2121  if (domain) {
2122  *domain = '\0';
2123  domain++;
2124  }
2125  if (ast_strlen_zero(domain) || ast_strlen_zero(mailbox)) {
2126  ast_log(LOG_ERROR, "Need mailbox@context as argument. Sorry. Argument 0 %s\n", argv[0]);
2127  return -1;
2128  }
2129  queue_mwi_event(ast_channel_uniqueid(chan), mailbox, domain, atoi(argv[1]), atoi(argv[2]), atoi(argv[3]));
2130 
2131  return res;
2132 }
2133 
2134 
2135 /*!\internal
2136  * \brief Notify voicemail account owners - either generic template or user specific */
2137 static int minivm_notify_exec(struct ast_channel *chan, const char *data)
2138 {
2139  int argc;
2140  char *argv[2];
2141  int res = 0;
2142  char tmp[PATH_MAX];
2143  char *domain;
2144  char *tmpptr;
2145  struct minivm_account *vmu;
2146  char *username;
2147  const char *template = "";
2148  const char *filename;
2149  const char *format;
2150  const char *duration_string;
2151  if (ast_strlen_zero(data)) {
2152  ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
2153  return -1;
2154  }
2155  tmpptr = ast_strdupa((char *)data);
2156  argc = ast_app_separate_args(tmpptr, ',', argv, ARRAY_LEN(argv));
2157 
2158  if (argc == 2 && !ast_strlen_zero(argv[1]))
2159  template = argv[1];
2160 
2161  ast_copy_string(tmp, argv[0], sizeof(tmp));
2162  username = tmp;
2163  domain = strchr(tmp, '@');
2164  if (domain) {
2165  *domain = '\0';
2166  domain++;
2167  }
2168  if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
2169  ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument 0 %s\n", argv[0]);
2170  return -1;
2171  }
2172 
2173  if(!(vmu = find_account(domain, username, TRUE))) {
2174  /* We could not find user, let's exit */
2175  ast_log(LOG_WARNING, "Could not allocate temporary memory for '%s@%s'\n", username, domain);
2176  pbx_builtin_setvar_helper(chan, "MVM_NOTIFY_STATUS", "FAILED");
2177  return -1;
2178  }
2179 
2180  ast_channel_lock(chan);
2181  if ((filename = pbx_builtin_getvar_helper(chan, "MVM_FILENAME"))) {
2182  filename = ast_strdupa(filename);
2183  }
2184  ast_channel_unlock(chan);
2185  /* Notify of new message to e-mail and pager */
2186  if (!ast_strlen_zero(filename)) {
2187  ast_channel_lock(chan);
2188  if ((format = pbx_builtin_getvar_helper(chan, "MVM_FORMAT"))) {
2189  format = ast_strdupa(format);
2190  }
2191  if ((duration_string = pbx_builtin_getvar_helper(chan, "MVM_DURATION"))) {
2192  duration_string = ast_strdupa(duration_string);
2193  }
2194  ast_channel_unlock(chan);
2195  res = notify_new_message(chan, template, vmu, filename, atoi(duration_string),
2196  format,
2197  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
2198  S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL));
2199  }
2200 
2201  pbx_builtin_setvar_helper(chan, "MVM_NOTIFY_STATUS", res == 0 ? "SUCCESS" : "FAILED");
2202 
2203 
2204  if(ast_test_flag(vmu, MVM_ALLOCED))
2205  free_user(vmu);
2206 
2207  /* Ok, we're ready to rock and roll. Return to dialplan */
2208 
2209  return res;
2210 
2211 }
2212 
2213 /*!\internal
2214  * \brief Dialplan function to record voicemail */
2215 static int minivm_record_exec(struct ast_channel *chan, const char *data)
2216 {
2217  int res = 0;
2218  char *tmp;
2219  struct leave_vm_options leave_options;
2220  int argc;
2221  char *argv[2];
2222  struct ast_flags flags = { 0 };
2223  char *opts[OPT_ARG_ARRAY_SIZE];
2224 
2225  memset(&leave_options, 0, sizeof(leave_options));
2226 
2227  /* Answer channel if it's not already answered */
2228  if (ast_channel_state(chan) != AST_STATE_UP)
2229  ast_answer(chan);
2230 
2231  if (ast_strlen_zero(data)) {
2232  ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
2233  return -1;
2234  }
2235  tmp = ast_strdupa((char *)data);
2236  argc = ast_app_separate_args(tmp, ',', argv, ARRAY_LEN(argv));
2237  if (argc == 2) {
2238  if (ast_app_parse_options(minivm_app_options, &flags, opts, argv[1])) {
2239  return -1;
2240  }
2241  ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING );
2242  if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
2243  int gain;
2244 
2245  if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
2246  ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
2247  return -1;
2248  } else
2249  leave_options.record_gain = (signed char) gain;
2250  }
2251  }
2252 
2253  /* Now run the appliation and good luck to you! */
2254  res = leave_voicemail(chan, argv[0], &leave_options);
2255 
2256  if (res == ERROR_LOCK_PATH) {
2257  ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
2258  pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "FAILED");
2259  res = 0;
2260  }
2261  pbx_builtin_setvar_helper(chan, "MVM_RECORD_STATUS", "SUCCESS");
2262 
2263  return res;
2264 }
2265 
2266 /*!\internal
2267  * \brief Play voicemail prompts - either generic or user specific */
2268 static int minivm_greet_exec(struct ast_channel *chan, const char *data)
2269 {
2270  struct leave_vm_options leave_options = { 0, '\0'};
2271  int argc;
2272  char *argv[2];
2273  struct ast_flags flags = { 0 };
2274  char *opts[OPT_ARG_ARRAY_SIZE];
2275  int res = 0;
2276  int ausemacro = 0;
2277  int ousemacro = 0;
2278  int ouseexten = 0;
2279  char tmp[PATH_MAX];
2280  char dest[PATH_MAX];
2281  char prefile[PATH_MAX] = "";
2282  char tempfile[PATH_MAX] = "";
2283  char ext_context[256] = "";
2284  char *domain;
2285  char ecodes[16] = "#";
2286  char *tmpptr;
2287  struct minivm_account *vmu;
2288  char *username;
2289 
2290  if (ast_strlen_zero(data)) {
2291  ast_log(LOG_ERROR, "Minivm needs at least an account argument \n");
2292  return -1;
2293  }
2294  tmpptr = ast_strdupa((char *)data);
2295  argc = ast_app_separate_args(tmpptr, ',', argv, ARRAY_LEN(argv));
2296 
2297  if (argc == 2) {
2298  if (ast_app_parse_options(minivm_app_options, &flags, opts, argv[1]))
2299  return -1;
2300  ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING );
2301  }
2302 
2303  ast_copy_string(tmp, argv[0], sizeof(tmp));
2304  username = tmp;
2305  domain = strchr(tmp, '@');
2306  if (domain) {
2307  *domain = '\0';
2308  domain++;
2309  }
2310  if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
2311  ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument: %s\n", argv[0]);
2312  return -1;
2313  }
2314  ast_debug(1, "Trying to find configuration for user %s in domain %s\n", username, domain);
2315 
2316  if (!(vmu = find_account(domain, username, TRUE))) {
2317  ast_log(LOG_ERROR, "Could not allocate memory. \n");
2318  return -1;
2319  }
2320 
2321  /* Answer channel if it's not already answered */
2322  if (ast_channel_state(chan) != AST_STATE_UP)
2323  ast_answer(chan);
2324 
2325  /* Setup pre-file if appropriate */
2326  if (strcmp(vmu->domain, "localhost"))
2327  snprintf(ext_context, sizeof(ext_context), "%s@%s", username, vmu->domain);
2328  else
2329  ast_copy_string(ext_context, vmu->domain, sizeof(ext_context));
2330 
2331  if (ast_test_flag(&leave_options, OPT_BUSY_GREETING)) {
2332  res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "busy");
2333  if (res)
2334  snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", MVM_SPOOL_DIR, vmu->domain, username);
2335  } else if (ast_test_flag(&leave_options, OPT_UNAVAIL_GREETING)) {
2336  res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "unavail");
2337  if (res)
2338  snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", MVM_SPOOL_DIR, vmu->domain, username);
2339  }
2340  /* Check for temporary greeting - it overrides busy and unavail */
2341  snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", MVM_SPOOL_DIR, vmu->domain, username);
2342  if (!(res = check_dirpath(dest, sizeof(dest), vmu->domain, username, "temp"))) {
2343  ast_debug(2, "Temporary message directory does not exist, using default (%s)\n", tempfile);
2344  ast_copy_string(prefile, tempfile, sizeof(prefile));
2345  }
2346  ast_debug(2, "Preparing to play message ...\n");
2347 
2348  /* Check current or macro-calling context for special extensions */
2349  if (ast_test_flag(vmu, MVM_OPERATOR)) {
2350  if (!ast_strlen_zero(vmu->exit)) {
2351  if (ast_exists_extension(chan, vmu->exit, "o", 1,
2352  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
2353  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2354  ouseexten = 1;
2355  }
2356  } else if (ast_exists_extension(chan, ast_channel_context(chan), "o", 1,
2357  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
2358  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2359  ouseexten = 1;
2360  }
2361  else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
2362  && ast_exists_extension(chan, ast_channel_macrocontext(chan), "o", 1,
2363  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
2364  strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
2365  ousemacro = 1;
2366  }
2367  }
2368 
2369  if (!ast_strlen_zero(vmu->exit)) {
2370  if (ast_exists_extension(chan, vmu->exit, "a", 1,
2371  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
2372  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2373  }
2374  } else if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
2375  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
2376  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2377  } else if (!ast_strlen_zero(ast_channel_macrocontext(chan))
2378  && ast_exists_extension(chan, ast_channel_macrocontext(chan), "a", 1,
2379  S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
2380  strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
2381  ausemacro = 1;
2382  }
2383 
2384  res = 0; /* Reset */
2385  /* Play the beginning intro if desired */
2386  if (!ast_strlen_zero(prefile)) {
2387  if (ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1)
2388  res = ast_waitstream(chan, ecodes);
2389  } else {
2390  ast_debug(2, "%s doesn't exist, doing what we can\n", prefile);
2391  res = invent_message(chan, vmu->domain, username, ast_test_flag(&leave_options, OPT_BUSY_GREETING), ecodes);
2392  }
2393  if (res < 0) {
2394  ast_debug(2, "Hang up during prefile playback\n");
2395  pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "FAILED");
2396  if(ast_test_flag(vmu, MVM_ALLOCED))
2397  free_user(vmu);
2398  return -1;
2399  }
2400  if (res == '#') {
2401  /* On a '#' we skip the instructions */
2402  ast_set_flag(&leave_options, OPT_SILENT);
2403  res = 0;
2404  }
2405  if (!res && !ast_test_flag(&leave_options, OPT_SILENT)) {
2406  res = ast_streamfile(chan, SOUND_INTRO, ast_channel_language(chan));
2407  if (!res)
2408  res = ast_waitstream(chan, ecodes);
2409  if (res == '#') {
2410  ast_set_flag(&leave_options, OPT_SILENT);
2411  res = 0;
2412  }
2413  }
2414  if (res > 0)
2415  ast_stopstream(chan);
2416  /* Check for a '*' here in case the caller wants to escape from voicemail to something
2417  other than the operator -- an automated attendant or mailbox login for example */
2418  if (res == '*') {
2419  ast_channel_exten_set(chan, "a");
2420  if (!ast_strlen_zero(vmu->exit)) {
2421  ast_channel_context_set(chan, vmu->exit);
2422  } else if (ausemacro && !ast_strlen_zero(ast_channel_macrocontext(chan))) {
2424  }
2425  ast_channel_priority_set(chan, 0);
2426  pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "USEREXIT");
2427  res = 0;
2428  } else if (res == '0') { /* Check for a '0' here */
2429  if(ouseexten || ousemacro) {
2430  ast_channel_exten_set(chan, "o");
2431  if (!ast_strlen_zero(vmu->exit)) {
2432  ast_channel_context_set(chan, vmu->exit);
2433  } else if (ousemacro && !ast_strlen_zero(ast_channel_macrocontext(chan))) {
2435  }
2436  ast_play_and_wait(chan, "transfer");
2437  ast_channel_priority_set(chan, 0);
2438  pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "USEREXIT");
2439  }
2440  res = 0;
2441  } else if (res < 0) {
2442  pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "FAILED");
2443  res = -1;
2444  } else
2445  pbx_builtin_setvar_helper(chan, "MVM_GREET_STATUS", "SUCCESS");
2446 
2447  if(ast_test_flag(vmu, MVM_ALLOCED))
2448  free_user(vmu);
2449 
2450 
2451  /* Ok, we're ready to rock and roll. Return to dialplan */
2452  return res;
2453 
2454 }
2455 
2456 /*!\internal
2457  * \brief Dialplan application to delete voicemail */
2458 static int minivm_delete_exec(struct ast_channel *chan, const char *data)
2459 {
2460  int res = 0;
2461  char filename[BUFSIZ];
2462 
2463  if (!ast_strlen_zero(data)) {
2464  ast_copy_string(filename, (char *) data, sizeof(filename));
2465  } else {
2466  ast_channel_lock(chan);
2467  ast_copy_string(filename, pbx_builtin_getvar_helper(chan, "MVM_FILENAME"), sizeof(filename));
2468  ast_channel_unlock(chan);
2469  }
2470 
2471  if (ast_strlen_zero(filename)) {
2472  ast_log(LOG_ERROR, "No filename given in application arguments or channel variable MVM_FILENAME\n");
2473  return res;
2474  }
2475 
2476  /* Go ahead and delete audio files from system, they're not needed any more */
2477  /* We should look for both audio and text files here */
2478  if (ast_fileexists(filename, NULL, NULL) > 0) {
2479  res = vm_delete(filename);
2480  if (res) {
2481  ast_debug(2, "Can't delete file: %s\n", filename);
2482  pbx_builtin_setvar_helper(chan, "MVM_DELETE_STATUS", "FAILED");
2483  } else {
2484  ast_debug(2, "Deleted voicemail file :: %s \n", filename);
2485  pbx_builtin_setvar_helper(chan, "MVM_DELETE_STATUS", "SUCCESS");
2486  }
2487  } else {
2488  ast_debug(2, "Filename does not exist: %s\n", filename);
2489  pbx_builtin_setvar_helper(chan, "MVM_DELETE_STATUS", "FAILED");
2490  }
2491 
2492  return res;
2493 }
2494 
2495 /*! \brief Record specific messages for voicemail account */
2496 static int minivm_accmess_exec(struct ast_channel *chan, const char *data)
2497 {
2498  int argc = 0;
2499  char *argv[2];
2500  char filename[PATH_MAX];
2501  char tmp[PATH_MAX];
2502  char *domain;
2503  char *tmpptr = NULL;
2504  struct minivm_account *vmu;
2505  char *username;
2506  struct ast_flags flags = { 0 };
2507  char *opts[OPT_ARG_ARRAY_SIZE];
2508  int error = FALSE;
2509  char *message = NULL;
2510  char *prompt = NULL;
2511  int duration;
2512 
2513  if (ast_strlen_zero(data)) {
2514  ast_log(LOG_ERROR, "MinivmAccmess needs at least two arguments: account and option\n");
2515  error = TRUE;
2516  } else {
2517  tmpptr = ast_strdupa((char *)data);
2518  argc = ast_app_separate_args(tmpptr, ',', argv, ARRAY_LEN(argv));
2519  }
2520 
2521  if (argc <=1) {
2522  ast_log(LOG_ERROR, "MinivmAccmess needs at least two arguments: account and option\n");
2523  error = TRUE;
2524  }
2525  if (!error && strlen(argv[1]) > 1) {
2526  ast_log(LOG_ERROR, "MinivmAccmess can only handle one option at a time. Bad option string: %s\n", argv[1]);
2527  error = TRUE;
2528  }
2529 
2530  if (!error && ast_app_parse_options(minivm_accmess_options, &flags, opts, argv[1])) {
2531  ast_log(LOG_ERROR, "Can't parse option %s\n", argv[1]);
2532  error = TRUE;
2533  }
2534 
2535  if (error) {
2536  pbx_builtin_setvar_helper(chan, "MVM_ACCMESS_STATUS", "FAILED");
2537  return -1;
2538  }
2539 
2540  ast_copy_string(tmp, argv[0], sizeof(tmp));
2541  username = tmp;
2542  domain = strchr(tmp, '@');
2543  if (domain) {
2544  *domain = '\0';
2545  domain++;
2546  }
2547  if (ast_strlen_zero(domain) || ast_strlen_zero(username)) {
2548  ast_log(LOG_ERROR, "Need username@domain as argument. Sorry. Argument 0 %s\n", argv[0]);
2549  pbx_builtin_setvar_helper(chan, "MVM_ACCMESS_STATUS", "FAILED");
2550  return -1;
2551  }
2552 
2553  if(!(vmu = find_account(domain, username, TRUE))) {
2554  /* We could not find user, let's exit */
2555  ast_log(LOG_WARNING, "Could not allocate temporary memory for '%s@%s'\n", username, domain);
2556  pbx_builtin_setvar_helper(chan, "MVM_ACCMESS_STATUS", "FAILED");
2557  return -1;
2558  }
2559 
2560  /* Answer channel if it's not already answered */
2561  if (ast_channel_state(chan) != AST_STATE_UP)
2562  ast_answer(chan);
2563 
2564  /* Here's where the action is */
2565  if (ast_test_flag(&flags, OPT_BUSY_GREETING)) {
2566  message = "busy";
2567  prompt = "vm-rec-busy";
2568  } else if (ast_test_flag(&flags, OPT_UNAVAIL_GREETING)) {
2569  message = "unavailable";
2570  prompt = "vm-rec-unv";
2571  } else if (ast_test_flag(&flags, OPT_TEMP_GREETING)) {
2572  message = "temp";
2573  prompt = "vm-rec-temp";
2574  } else if (ast_test_flag(&flags, OPT_NAME_GREETING)) {
2575  message = "greet";
2576  prompt = "vm-rec-name";
2577  }
2578  snprintf(filename,sizeof(filename), "%s%s/%s/%s", MVM_SPOOL_DIR, vmu->domain, vmu->username, message);
2579  /* Maybe we should check the result of play_record_review ? */
2580  play_record_review(chan, prompt, filename, global_maxgreet, default_vmformat, 0, vmu, &duration, NULL, NULL, FALSE);
2581 
2582  ast_debug(1, "Recorded new %s message in %s (duration %d)\n", message, filename, duration);
2583 
2584  if(ast_test_flag(vmu, MVM_ALLOCED))
2585  free_user(vmu);
2586 
2587  pbx_builtin_setvar_helper(chan, "MVM_ACCMESS_STATUS", "SUCCESS");
2588 
2589  /* Ok, we're ready to rock and roll. Return to dialplan */
2590  return 0;
2591 }
2592 
2593 /*! \brief Append new mailbox to mailbox list from configuration file */
2594 static int create_vmaccount(char *name, struct ast_variable *var, int realtime)
2595 {
2596  struct minivm_account *vmu;
2597  char *domain;
2598  char *username;
2599  char accbuf[BUFSIZ];
2600 
2601  ast_debug(3, "Creating %s account for [%s]\n", realtime ? "realtime" : "static", name);
2602 
2603  ast_copy_string(accbuf, name, sizeof(accbuf));
2604  username = accbuf;
2605  domain = strchr(accbuf, '@');
2606  if (domain) {
2607  *domain = '\0';
2608  domain++;
2609  }
2610  if (ast_strlen_zero(domain)) {
2611  ast_log(LOG_ERROR, "No domain given for mini-voicemail account %s. Not configured.\n", name);
2612  return 0;
2613  }
2614 
2615  ast_debug(3, "Creating static account for user %s domain %s\n", username, domain);
2616 
2617  /* Allocate user account */
2618  vmu = ast_calloc(1, sizeof(*vmu));
2619  if (!vmu)
2620  return 0;
2621 
2622  ast_copy_string(vmu->domain, domain, sizeof(vmu->domain));
2623  ast_copy_string(vmu->username, username, sizeof(vmu->username));
2624 
2625  populate_defaults(vmu);
2626 
2627  ast_debug(3, "...Configuring account %s\n", name);
2628 
2629  while (var) {
2630  ast_debug(3, "Configuring %s = \"%s\" for account %s\n", var->name, var->value, name);
2631  if (!strcasecmp(var->name, "serveremail")) {
2632  ast_copy_string(vmu->serveremail, var->value, sizeof(vmu->serveremail));
2633  } else if (!strcasecmp(var->name, "email")) {
2634  ast_copy_string(vmu->email, var->value, sizeof(vmu->email));
2635  } else if (!strcasecmp(var->name, "accountcode")) {
2636  ast_copy_string(vmu->accountcode, var->value, sizeof(vmu->accountcode));
2637  } else if (!strcasecmp(var->name, "pincode")) {
2638  ast_copy_string(vmu->pincode, var->value, sizeof(vmu->pincode));
2639  } else if (!strcasecmp(var->name, "domain")) {
2640  ast_copy_string(vmu->domain, var->value, sizeof(vmu->domain));
2641  } else if (!strcasecmp(var->name, "language")) {
2642  ast_copy_string(vmu->language, var->value, sizeof(vmu->language));
2643  } else if (!strcasecmp(var->name, "timezone")) {
2644  ast_copy_string(vmu->zonetag, var->value, sizeof(vmu->zonetag));
2645  } else if (!strcasecmp(var->name, "externnotify")) {
2646  ast_copy_string(vmu->externnotify, var->value, sizeof(vmu->externnotify));
2647  } else if (!strcasecmp(var->name, "etemplate")) {
2648  ast_copy_string(vmu->etemplate, var->value, sizeof(vmu->etemplate));
2649  } else if (!strcasecmp(var->name, "ptemplate")) {
2650  ast_copy_string(vmu->ptemplate, var->value, sizeof(vmu->ptemplate));
2651  } else if (!strcasecmp(var->name, "fullname")) {
2652  ast_copy_string(vmu->fullname, var->value, sizeof(vmu->fullname));
2653  } else if (!strcasecmp(var->name, "setvar")) {
2654  char *varval;
2655  char varname[strlen(var->value) + 1];
2656  struct ast_variable *tmpvar;
2657 
2658  strcpy(varname, var->value); /* safe */
2659  if ((varval = strchr(varname, '='))) {
2660  *varval = '\0';
2661  varval++;
2662  if ((tmpvar = ast_variable_new(varname, varval, ""))) {
2663  tmpvar->next = vmu->chanvars;
2664  vmu->chanvars = tmpvar;
2665  }
2666  }
2667  } else if (!strcasecmp(var->name, "pager")) {
2668  ast_copy_string(vmu->pager, var->value, sizeof(vmu->pager));
2669  } else if (!strcasecmp(var->name, "volgain")) {
2670  sscanf(var->value, "%30lf", &vmu->volgain);
2671  } else {
2672  ast_log(LOG_ERROR, "Unknown configuration option for minivm account %s : %s\n", name, var->name);
2673  }
2674  var = var->next;
2675  }
2676  ast_debug(3, "...Linking account %s\n", name);
2677 
2681 
2683 
2684  ast_debug(2, "MVM :: Created account %s@%s - tz %s etemplate %s %s\n", username, domain, ast_strlen_zero(vmu->zonetag) ? "" : vmu->zonetag, ast_strlen_zero(vmu->etemplate) ? "" : vmu->etemplate, realtime ? "(realtime)" : "");
2685  return 0;
2686 }
2687 
2688 /*! \brief Free Mini Voicemail timezone */
2689 static void free_zone(struct minivm_zone *z)
2690 {
2691  ast_free(z);
2692 }
2693 
2694 /*! \brief Clear list of timezones */
2695 static void timezone_destroy_list(void)
2696 {
2697  struct minivm_zone *this;
2698 
2700  while ((this = AST_LIST_REMOVE_HEAD(&minivm_zones, list)))
2701  free_zone(this);
2702 
2704 }
2705 
2706 /*! \brief Add time zone to memory list */
2707 static int timezone_add(const char *zonename, const char *config)
2708 {
2709  struct minivm_zone *newzone;
2710  char *msg_format, *timezone_str;
2711 
2712  newzone = ast_calloc(1, sizeof(*newzone));
2713  if (newzone == NULL)
2714  return 0;
2715 
2716  msg_format = ast_strdupa(config);
2717 
2718  timezone_str = strsep(&msg_format, "|");
2719  if (!msg_format) {
2720  ast_log(LOG_WARNING, "Invalid timezone definition : %s\n", zonename);
2721  ast_free(newzone);
2722  return 0;
2723  }
2724 
2725  ast_copy_string(newzone->name, zonename, sizeof(newzone->name));
2726  ast_copy_string(newzone->timezone, timezone_str, sizeof(newzone->timezone));
2727  ast_copy_string(newzone->msg_format, msg_format, sizeof(newzone->msg_format));
2728 
2732 
2734 
2735  return 0;
2736 }
2737 
2738 /*! \brief Read message template from file */
2739 static char *message_template_parse_filebody(const char *filename) {
2740  char buf[BUFSIZ * 6];
2741  char readbuf[BUFSIZ];
2742  char filenamebuf[BUFSIZ];
2743  char *writepos;
2744  char *messagebody;
2745  FILE *fi;
2746  int lines = 0;
2747 
2748  if (ast_strlen_zero(filename))
2749  return NULL;
2750  if (*filename == '/')
2751  ast_copy_string(filenamebuf, filename, sizeof(filenamebuf));
2752  else
2753  snprintf(filenamebuf, sizeof(filenamebuf), "%s/%s", ast_config_AST_CONFIG_DIR, filename);
2754 
2755  if (!(fi = fopen(filenamebuf, "r"))) {
2756  ast_log(LOG_ERROR, "Can't read message template from file: %s\n", filenamebuf);
2757  return NULL;
2758  }
2759  writepos = buf;
2760  while (fgets(readbuf, sizeof(readbuf), fi)) {
2761  lines ++;
2762  if (writepos != buf) {
2763  *writepos = '\n'; /* Replace EOL with new line */
2764  writepos++;
2765  }
2766  ast_copy_string(writepos, readbuf, sizeof(buf) - (writepos - buf));
2767  writepos += strlen(readbuf) - 1;
2768  }
2769  fclose(fi);
2770  messagebody = ast_calloc(1, strlen(buf + 1));
2771  ast_copy_string(messagebody, buf, strlen(buf) + 1);
2772  ast_debug(4, "---> Size of allocation %d\n", (int) strlen(buf + 1) );
2773  ast_debug(4, "---> Done reading message template : \n%s\n---- END message template--- \n", messagebody);
2774 
2775  return messagebody;
2776 }
2777 
2778 /*! \brief Parse emailbody template from configuration file */
2779 static char *message_template_parse_emailbody(const char *configuration)
2780 {
2781  char *tmpread, *tmpwrite;
2782  char *emailbody = ast_strdup(configuration);
2783 
2784  /* substitute strings \t and \n into the apropriate characters */
2785  tmpread = tmpwrite = emailbody;
2786  while ((tmpwrite = strchr(tmpread,'\\'))) {
2787  int len = strlen("\n");
2788  switch (tmpwrite[1]) {
2789  case 'n':
2790  memmove(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
2791  tmpwrite[0] = '\n';
2792  break;
2793  case 't':
2794  memmove(tmpwrite + len, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
2795  tmpwrite[0] = '\t';
2796  break;
2797  default:
2798  ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
2799  }
2800  tmpread = tmpwrite + len;
2801  }
2802  return emailbody;
2803 }
2804 
2805 /*! \brief Apply general configuration options */
2807 {
2808  int error = 0;
2809 
2810  while (var) {
2811  /* Mail command */
2812  if (!strcmp(var->name, "mailcmd")) {
2813  ast_copy_string(global_mailcmd, var->value, sizeof(global_mailcmd)); /* User setting */
2814  } else if (!strcmp(var->name, "maxgreet")) {
2815  global_maxgreet = atoi(var->value);
2816  } else if (!strcmp(var->name, "maxsilence")) {
2817  global_maxsilence = atoi(var->value);
2818  if (global_maxsilence > 0)
2819  global_maxsilence *= 1000;
2820  } else if (!strcmp(var->name, "logfile")) {
2821  if (!ast_strlen_zero(var->value) ) {
2822  if(*(var->value) == '/')
2823  ast_copy_string(global_logfile, var->value, sizeof(global_logfile));
2824  else
2825  snprintf(global_logfile, sizeof(global_logfile), "%s/%s", ast_config_AST_LOG_DIR, var->value);
2826  }
2827  } else if (!strcmp(var->name, "externnotify")) {
2828  /* External voicemail notify application */
2829  ast_copy_string(global_externnotify, var->value, sizeof(global_externnotify));
2830  } else if (!strcmp(var->name, "silencetreshold")) {
2831  /* Silence treshold */
2832  global_silencethreshold = atoi(var->value);
2833  } else if (!strcmp(var->name, "maxmessage")) {
2834  int x;
2835  if (sscanf(var->value, "%30d", &x) == 1) {
2836  global_vmmaxmessage = x;
2837  } else {
2838  error ++;
2839  ast_log(LOG_WARNING, "Invalid max message time length\n");
2840  }
2841  } else if (!strcmp(var->name, "minmessage")) {
2842  int x;
2843  if (sscanf(var->value, "%30d", &x) == 1) {
2844  global_vmminmessage = x;
2845  if (global_maxsilence <= global_vmminmessage)
2846  ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
2847  } else {
2848  error ++;
2849  ast_log(LOG_WARNING, "Invalid min message time length\n");
2850  }
2851  } else if (!strcmp(var->name, "format")) {
2852  ast_copy_string(default_vmformat, var->value, sizeof(default_vmformat));
2853  } else if (!strcmp(var->name, "review")) {
2854  ast_set2_flag((&globalflags), ast_true(var->value), MVM_REVIEW);
2855  } else if (!strcmp(var->name, "operator")) {
2856  ast_set2_flag((&globalflags), ast_true(var->value), MVM_OPERATOR);
2857  }
2858  var = var->next;
2859  }
2860  return error;
2861 }
2862 
2863 /*! \brief Load minivoicemail configuration */
2864 static int load_config(int reload)
2865 {
2866  struct ast_config *cfg;
2867  struct ast_variable *var;
2868  char *cat;
2869  const char *chanvar;
2870  int error = 0;
2871  struct minivm_template *template;
2872  struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
2873 
2874  cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags);
2875  if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
2876  return 0;
2877  } else if (cfg == CONFIG_STATUS_FILEINVALID) {
2878  ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
2879  return 0;
2880  }
2881 
2883 
2884  /* Destroy lists to reconfigure */
2885  message_destroy_list(); /* Destroy list of voicemail message templates */
2886  timezone_destroy_list(); /* Destroy list of timezones */
2887  vmaccounts_destroy_list(); /* Destroy list of voicemail accounts */
2888  ast_debug(2, "Destroyed memory objects...\n");
2889 
2890  /* First, set some default settings */
2891  global_externnotify[0] = '\0';
2892  global_logfile[0] = '\0';
2893  global_vmmaxmessage = 2000;
2894  global_maxgreet = 2000;
2895  global_vmminmessage = 0;
2896  strcpy(global_mailcmd, SENDMAIL);
2897  global_maxsilence = 0;
2898  global_saydurationminfo = 2;
2899  ast_copy_string(default_vmformat, "wav", sizeof(default_vmformat));
2900  ast_set2_flag((&globalflags), FALSE, MVM_REVIEW);
2901  ast_set2_flag((&globalflags), FALSE, MVM_OPERATOR);
2902  /* Reset statistics */
2903  memset(&global_stats, 0, sizeof(global_stats));
2905 
2906  global_silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
2907 
2908  /* Make sure we could load configuration file */
2909  if (!cfg) {
2910  ast_log(LOG_WARNING, "Failed to load configuration file. Module activated with default settings.\n");
2912  return 0;
2913  }
2914 
2915  ast_debug(2, "Loaded configuration file, now parsing\n");
2916 
2917  /* General settings */
2918 
2919  cat = ast_category_browse(cfg, NULL);
2920  while (cat) {
2921  ast_debug(3, "Found configuration section [%s]\n", cat);
2922  if (!strcasecmp(cat, "general")) {
2923  /* Nothing right now */
2924  error += apply_general_options(ast_variable_browse(cfg, cat));
2925  } else if (!strncasecmp(cat, "template-", 9)) {
2926  /* Template */
2927  char *name = cat + 9;
2928 
2929  /* Now build and link template to list */
2930  error += message_template_build(name, ast_variable_browse(cfg, cat));
2931  } else {
2932  var = ast_variable_browse(cfg, cat);
2933  if (!strcasecmp(cat, "zonemessages")) {
2934  /* Timezones in this context */
2935  while (var) {
2936  timezone_add(var->name, var->value);
2937  var = var->next;
2938  }
2939  } else {
2940  /* Create mailbox from this */
2941  error += create_vmaccount(cat, var, FALSE);
2942  }
2943  }
2944  /* Find next section in configuration file */
2945  cat = ast_category_browse(cfg, cat);
2946  }
2947 
2948  /* Configure the default email template */
2949  message_template_build("email-default", NULL);
2950  template = message_template_find("email-default");
2951 
2952  /* Load date format config for voicemail mail */
2953  if ((chanvar = ast_variable_retrieve(cfg, "general", "emaildateformat")))
2954  ast_copy_string(template->dateformat, chanvar, sizeof(template->dateformat));
2955  if ((chanvar = ast_variable_retrieve(cfg, "general", "emailfromstring")))
2956  ast_copy_string(template->fromaddress, chanvar, sizeof(template->fromaddress));
2957  if ((chanvar = ast_variable_retrieve(cfg, "general", "emailaaddress")))
2958  ast_copy_string(template->serveremail, chanvar, sizeof(template->serveremail));
2959  if ((chanvar = ast_variable_retrieve(cfg, "general", "emailcharset")))
2960  ast_copy_string(template->charset, chanvar, sizeof(template->charset));
2961  if ((chanvar = ast_variable_retrieve(cfg, "general", "emailsubject")))
2962  ast_copy_string(template->subject, chanvar, sizeof(template->subject));
2963  if ((chanvar = ast_variable_retrieve(cfg, "general", "emailbody")))
2964  template->body = message_template_parse_emailbody(chanvar);
2965  template->attachment = TRUE;
2966 
2967  message_template_build("pager-default", NULL);
2968  template = message_template_find("pager-default");
2969  if ((chanvar = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
2970  ast_copy_string(template->fromaddress, chanvar, sizeof(template->fromaddress));
2971  if ((chanvar = ast_variable_retrieve(cfg, "general", "pageraddress")))
2972  ast_copy_string(template->serveremail, chanvar, sizeof(template->serveremail));
2973  if ((chanvar = ast_variable_retrieve(cfg, "general", "pagercharset")))
2974  ast_copy_string(template->charset, chanvar, sizeof(template->charset));
2975  if ((chanvar = ast_variable_retrieve(cfg, "general", "pagersubject")))
2976  ast_copy_string(template->subject, chanvar,sizeof(template->subject));
2977  if ((chanvar = ast_variable_retrieve(cfg, "general", "pagerbody")))
2978  template->body = message_template_parse_emailbody(chanvar);
2979  template->attachment = FALSE;
2980 
2981  if (error)
2982  ast_log(LOG_ERROR, "--- A total of %d errors found in mini-voicemail configuration\n", error);
2983 
2985  ast_config_destroy(cfg);
2986 
2987  /* Close log file if it's open and disabled */
2988  if(minivmlogfile)
2989  fclose(minivmlogfile);
2990 
2991  /* Open log file if it's enabled */
2992  if(!ast_strlen_zero(global_logfile)) {
2993  minivmlogfile = fopen(global_logfile, "a");
2994  if(!minivmlogfile)
2995  ast_log(LOG_ERROR, "Failed to open minivm log file %s : %s\n", global_logfile, strerror(errno));
2996  if (minivmlogfile)
2997  ast_debug(3, "Opened log file %s \n", global_logfile);
2998  }
2999 
3000  return 0;
3001 }
3002 
3003 /*! \brief CLI routine for listing templates */
3004 static char *handle_minivm_list_templates(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3005 {
3006  struct minivm_template *this;
3007 #define HVLT_OUTPUT_FORMAT "%-15s %-10s %-10s %-15.15s %-50s\n"
3008  int count = 0;
3009 
3010  switch (cmd) {
3011  case CLI_INIT:
3012  e->command = "minivm list templates";
3013  e->usage =
3014  "Usage: minivm list templates\n"
3015  " Lists message templates for e-mail, paging and IM\n";
3016  return NULL;
3017  case CLI_GENERATE:
3018  return NULL;
3019  }
3020 
3021  if (a->argc > 3)
3022  return CLI_SHOWUSAGE;
3023 
3026  ast_cli(a->fd, "There are no message templates defined\n");
3028  return CLI_FAILURE;
3029  }
3030  ast_cli(a->fd, HVLT_OUTPUT_FORMAT, "Template name", "Charset", "Locale", "Attach media", "Subject");
3031  ast_cli(a->fd, HVLT_OUTPUT_FORMAT, "-------------", "-------", "------", "------------", "-------");
3033  ast_cli(a->fd, HVLT_OUTPUT_FORMAT, this->name,
3034  S_OR(this->charset, "-"),
3035  S_OR(this->locale, "-"),
3036  this->attachment ? "Yes" : "No",
3037  S_OR(this->subject, "-"));
3038  count++;
3039  }
3041  ast_cli(a->fd, "\n * Total: %d minivoicemail message templates\n", count);
3042  return CLI_SUCCESS;
3043 }
3044 
3045 static char *complete_minivm_show_users(const char *line, const char *word, int pos, int state)
3046 {
3047  int which = 0;
3048  int wordlen;
3049  struct minivm_account *vmu;
3050  const char *domain = "";
3051 
3052  /* 0 - minivm; 1 - list; 2 - accounts; 3 - for; 4 - <domain> */
3053  if (pos > 4)
3054  return NULL;
3055  wordlen = strlen(word);
3057  if (!strncasecmp(word, vmu->domain, wordlen)) {
3058  if (domain && strcmp(domain, vmu->domain) && ++which > state)
3059  return ast_strdup(vmu->domain);
3060  /* ignore repeated domains ? */
3061  domain = vmu->domain;
3062  }
3063  }
3064  return NULL;
3065 }
3066 
3067 /*! \brief CLI command to list voicemail accounts */
3068 static char *handle_minivm_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3069 {
3070  struct minivm_account *vmu;
3071 #define HMSU_OUTPUT_FORMAT "%-23s %-15s %-15s %-10s %-10s %-50s\n"
3072  int count = 0;
3073 
3074  switch (cmd) {
3075  case CLI_INIT:
3076  e->command = "minivm list accounts [for]";
3077  e->usage =
3078  "Usage: minivm list accounts [for <domain>]\n"
3079  " Lists all mailboxes currently set up\n";
3080  return NULL;
3081  case CLI_GENERATE:
3082  return complete_minivm_show_users(a->line, a->word, a->pos, a->n);
3083  }
3084 
3085  if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
3086  return CLI_SHOWUSAGE;
3087  if ((a->argc == 5) && strcmp(a->argv[3],"for"))
3088  return CLI_SHOWUSAGE;
3089 
3092  ast_cli(a->fd, "There are no voicemail users currently defined\n");
3094  return CLI_FAILURE;
3095  }
3096  ast_cli(a->fd, HMSU_OUTPUT_FORMAT, "User", "E-Template", "P-template", "Zone", "Format", "Full name");
3097  ast_cli(a->fd, HMSU_OUTPUT_FORMAT, "----", "----------", "----------", "----", "------", "---------");
3099  char tmp[256] = "";
3100  if ((a->argc == 3) || ((a->argc == 5) && !strcmp(a->argv[4], vmu->domain))) {
3101  count++;
3102  snprintf(tmp, sizeof(tmp), "%s@%s", vmu->username, vmu->domain);
3103  ast_cli(a->fd, HMSU_OUTPUT_FORMAT, tmp, S_OR(vmu->etemplate, "-"),
3104  S_OR(vmu->ptemplate, "-"),
3105  S_OR(vmu->zonetag, "-"),
3106  S_OR(vmu->attachfmt, "-"),
3107  vmu->fullname);
3108  }
3109  }
3111  ast_cli(a->fd, "\n * Total: %d minivoicemail accounts\n", count);
3112  return CLI_SUCCESS;
3113 }
3114 
3115 /*! \brief Show a list of voicemail zones in the CLI */
3116 static char *handle_minivm_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3117 {
3118  struct minivm_zone *zone;
3119 #define HMSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
3120  char *res = CLI_SUCCESS;
3121 
3122  switch (cmd) {
3123  case CLI_INIT:
3124  e->command = "minivm list zones";
3125  e->usage =
3126  "Usage: minivm list zones\n"
3127  " Lists zone message formats\n";
3128  return NULL;
3129  case CLI_GENERATE:
3130  return NULL;
3131  }
3132 
3133  if (a->argc != e->args)
3134  return CLI_SHOWUSAGE;
3135 
3137  if (!AST_LIST_EMPTY(&minivm_zones)) {
3138  ast_cli(a->fd, HMSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
3139  ast_cli(a->fd, HMSZ_OUTPUT_FORMAT, "----", "--------", "--------------");
3141  ast_cli(a->fd, HMSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
3142  }
3143  } else {
3144  ast_cli(a->fd, "There are no voicemail zones currently defined\n");
3145  res = CLI_FAILURE;
3146  }
3148 
3149  return res;
3150 }
3151 
3152 /*! \brief CLI Show settings */
3153 static char *handle_minivm_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3154 {
3155  switch (cmd) {
3156  case CLI_INIT:
3157  e->command = "minivm show settings";
3158  e->usage =
3159  "Usage: minivm show settings\n"
3160  " Display Mini-Voicemail general settings\n";
3161  return NULL;
3162  case CLI_GENERATE:
3163  return NULL;
3164  }
3165 
3166  ast_cli(a->fd, "* Mini-Voicemail general settings\n");
3167  ast_cli(a->fd, " -------------------------------\n");
3168  ast_cli(a->fd, "\n");
3169  ast_cli(a->fd, " Mail command (shell): %s\n", global_mailcmd);
3170  ast_cli(a->fd, " Max silence: %d\n", global_maxsilence);
3171  ast_cli(a->fd, " Silence threshold: %d\n", global_silencethreshold);
3172  ast_cli(a->fd, " Max message length (secs): %d\n", global_vmmaxmessage);
3173  ast_cli(a->fd, " Min message length (secs): %d\n", global_vmminmessage);
3174  ast_cli(a->fd, " Default format: %s\n", default_vmformat);
3175  ast_cli(a->fd, " Extern notify (shell): %s\n", global_externnotify);
3176  ast_cli(a->fd, " Logfile: %s\n", global_logfile[0] ? global_logfile : "<disabled>");
3177  ast_cli(a->fd, " Operator exit: %s\n", ast_test_flag(&globalflags, MVM_OPERATOR) ? "Yes" : "No");
3178  ast_cli(a->fd, " Message review: %s\n", ast_test_flag(&globalflags, MVM_REVIEW) ? "Yes" : "No");
3179 
3180  ast_cli(a->fd, "\n");
3181  return CLI_SUCCESS;
3182 }
3183 
3184 /*! \brief Show stats */
3185 static char *handle_minivm_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3186 {
3187  struct ast_tm timebuf;
3188  char buf[BUFSIZ];
3189 
3190  switch (cmd) {
3191 
3192  case CLI_INIT:
3193  e->command = "minivm show stats";
3194  e->usage =
3195  "Usage: minivm show stats\n"
3196  " Display Mini-Voicemail counters\n";
3197  return NULL;
3198  case CLI_GENERATE:
3199  return NULL;
3200  }
3201 
3202  ast_cli(a->fd, "* Mini-Voicemail statistics\n");
3203  ast_cli(a->fd, " -------------------------\n");
3204  ast_cli(a->fd, "\n");
3205  ast_cli(a->fd, " Voicemail accounts: %5d\n", global_stats.voicemailaccounts);
3206  ast_cli(a->fd, " Templates: %5d\n", global_stats.templates);
3207  ast_cli(a->fd, " Timezones: %5d\n", global_stats.timezones);
3208  if (global_stats.receivedmessages == 0) {
3209  ast_cli(a->fd, " Received messages since last reset: <none>\n");
3210  } else {
3211  ast_cli(a->fd, " Received messages since last reset: %d\n", global_stats.receivedmessages);
3213  ast_strftime(buf, sizeof(buf), "%a %b %e %r %Z %Y", &timebuf);
3214  ast_cli(a->fd, " Last received voicemail: %s\n", buf);
3215  }
3216  ast_localtime(&global_stats.reset, &timebuf, NULL);
3217  ast_strftime(buf, sizeof(buf), "%a %b %e %r %Z %Y", &timebuf);
3218  ast_cli(a->fd, " Last reset: %s\n", buf);
3219 
3220  ast_cli(a->fd, "\n");
3221  return CLI_SUCCESS;
3222 }
3223 
3224 /*! \brief ${MINIVMACCOUNT()} Dialplan function - reads account data */
3225 static int minivm_account_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
3226 {
3227  struct minivm_account *vmu;
3228  char *username, *domain, *colname;
3229 
3230  username = ast_strdupa(data);
3231 
3232  if ((colname = strchr(username, ':'))) {
3233  *colname = '\0';
3234  colname++;
3235  } else {
3236  colname = "path";
3237  }
3238  if ((domain = strchr(username, '@'))) {
3239  *domain = '\0';
3240  domain++;
3241  }
3242  if (ast_strlen_zero(username) || ast_strlen_zero(domain)) {
3243  ast_log(LOG_ERROR, "This function needs a username and a domain: username@domain\n");
3244  return 0;
3245  }
3246 
3247  if (!(vmu = find_account(domain, username, TRUE)))
3248  return 0;
3249 
3250  if (!strcasecmp(colname, "hasaccount")) {
3251  ast_copy_string(buf, (ast_test_flag(vmu, MVM_ALLOCED) ? "0" : "1"), len);
3252  } else if (!strcasecmp(colname, "fullname")) {
3253  ast_copy_string(buf, vmu->fullname, len);
3254  } else if (!strcasecmp(colname, "email")) {
3255  if (!ast_strlen_zero(vmu->email))
3256  ast_copy_string(buf, vmu->email, len);
3257  else
3258  snprintf(buf, len, "%s@%s", vmu->username, vmu->domain);
3259  } else if (!strcasecmp(colname, "pager")) {
3260  ast_copy_string(buf, vmu->pager, len);
3261  } else if (!strcasecmp(colname, "etemplate")) {
3262  if (!ast_strlen_zero(vmu->etemplate))
3263  ast_copy_string(buf, vmu->etemplate, len);
3264  else
3265  ast_copy_string(buf, "email-default", len);
3266  } else if (!strcasecmp(colname, "language")) {
3267  ast_copy_string(buf, vmu->language, len);
3268  } else if (!strcasecmp(colname, "timezone")) {
3269  ast_copy_string(buf, vmu->zonetag, len);
3270  } else if (!strcasecmp(colname, "ptemplate")) {
3271  if (!ast_strlen_zero(vmu->ptemplate))
3272  ast_copy_string(buf, vmu->ptemplate, len);
3273  else
3274  ast_copy_string(buf, "email-default", len);
3275  } else if (!strcasecmp(colname, "accountcode")) {
3276  ast_copy_string(buf, vmu->accountcode, len);
3277  } else if (!strcasecmp(colname, "pincode")) {
3278  ast_copy_string(buf, vmu->pincode, len);
3279  } else if (!strcasecmp(colname, "path")) {
3280  check_dirpath(buf, len, vmu->domain, vmu->username, NULL);
3281  } else { /* Look in channel variables */
3282  struct ast_variable *var;
3283 
3284  for (var = vmu->chanvars ; var ; var = var->next)
3285  if (!strcmp(var->name, colname)) {
3286  ast_copy_string(buf, var->value, len);
3287  break;
3288  }
3289  }
3290 
3291  if(ast_test_flag(vmu, MVM_ALLOCED))
3292  free_user(vmu);
3293 
3294  return 0;
3295 }
3296 
3297 /*! \brief lock directory
3298 
3299  only return failure if ast_lock_path returns 'timeout',
3300  not if the path does not exist or any other reason
3301 */
3302 static int vm_lock_path(const char *path)
3303 {
3304  switch (ast_lock_path(path)) {
3305  case AST_LOCK_TIMEOUT:
3306  return -1;
3307  default:
3308  return 0;
3309  }
3310 }
3311 
3312 /*! \brief Access counter file, lock directory, read and possibly write it again changed
3313  \param directory Directory to crate file in
3314  \param countername filename
3315  \param value If set to zero, we only read the variable
3316  \param operand 0 to read, 1 to set new value, 2 to change
3317  \return -1 on error, otherwise counter value
3318 */
3319 static int access_counter_file(char *directory, char *countername, int value, int operand)
3320 {
3321  char filename[BUFSIZ];
3322  char readbuf[BUFSIZ];
3323  FILE *counterfile;
3324  int old = 0, counter = 0;
3325 
3326  /* Lock directory */
3327  if (vm_lock_path(directory)) {
3328  return -1; /* Could not lock directory */
3329  }
3330  snprintf(filename, sizeof(filename), "%s/%s.counter", directory, countername);
3331  if (operand != 1) {
3332  counterfile = fopen(filename, "r");
3333  if (counterfile) {
3334  if(fgets(readbuf, sizeof(readbuf), counterfile)) {
3335  ast_debug(3, "Read this string from counter file: %s\n", readbuf);
3336  old = counter = atoi(readbuf);
3337  }
3338  fclose(counterfile);
3339  }
3340  }
3341  switch (operand) {
3342  case 0: /* Read only */
3343  ast_unlock_path(directory);
3344  ast_debug(2, "MINIVM Counter %s/%s: Value %d\n", directory, countername, counter);
3345  return counter;
3346  break;
3347  case 1: /* Set new value */
3348  counter = value;
3349  break;
3350  case 2: /* Change value */
3351  counter += value;
3352  if (counter < 0) /* Don't allow counters to fall below zero */
3353  counter = 0;
3354  break;
3355  }
3356 
3357  /* Now, write the new value to the file */
3358  counterfile = fopen(filename, "w");
3359  if (!counterfile) {
3360  ast_log(LOG_ERROR, "Could not open counter file for writing : %s - %s\n", filename, strerror(errno));
3361  ast_unlock_path(directory);
3362  return -1; /* Could not open file for writing */
3363  }
3364  fprintf(counterfile, "%d\n\n", counter);
3365  fclose(counterfile);
3366  ast_unlock_path(directory);
3367  ast_debug(2, "MINIVM Counter %s/%s: Old value %d New value %d\n", directory, countername, old, counter);
3368  return counter;
3369 }
3370 
3371 /*! \brief ${MINIVMCOUNTER()} Dialplan function - read counters */
3372 static int minivm_counter_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
3373 {
3374  char *username, *domain, *countername;
3375  char userpath[BUFSIZ];
3376  int res;
3377 
3378  *buf = '\0';
3379 
3380  username = ast_strdupa(data);
3381 
3382  if ((countername = strchr(username, ':'))) {
3383  *countername = '\0';
3384  countername++;
3385  }
3386 
3387  if ((domain = strchr(username, '@'))) {
3388  *domain = '\0';
3389  domain++;
3390  }
3391 
3392  /* If we have neither username nor domain now, let's give up */
3393  if (ast_strlen_zero(username) && ast_strlen_zero(domain)) {
3394  ast_log(LOG_ERROR, "No account given\n");
3395  return -1;
3396  }
3397 
3398  if (ast_strlen_zero(countername)) {
3399  ast_log(LOG_ERROR, "This function needs two arguments: Account:countername\n");
3400  return -1;
3401  }
3402 
3403  /* We only have a domain, no username */
3404  if (!ast_strlen_zero(username) && ast_strlen_zero(domain)) {
3405  domain = username;
3406  username = NULL;
3407  }
3408 
3409  /* If we can't find account or if the account is temporary, return. */
3410  if (!ast_strlen_zero(username) && !find_account(domain, username, FALSE)) {
3411  ast_log(LOG_ERROR, "Minivm account does not exist: %s@%s\n", username, domain);
3412  return 0;
3413  }
3414 
3415  create_dirpath(userpath, sizeof(userpath), domain, username, NULL);
3416 
3417  /* We have the path, now read the counter file */
3418  res = access_counter_file(userpath, countername, 0, 0);
3419  if (res >= 0)
3420  snprintf(buf, len, "%d", res);
3421  return 0;
3422 }
3423 
3424 /*! \brief ${MINIVMCOUNTER()} Dialplan function - changes counter data */
3425 static int minivm_counter_func_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
3426 {
3427  char *username, *domain, *countername, *operand;
3428  char userpath[BUFSIZ];
3429  int change = 0;
3430  int operation = 0;
3431 
3432  if(!value)
3433  return -1;
3434  change = atoi(value);
3435 
3436  username = ast_strdupa(data);
3437 
3438  if ((countername = strchr(username, ':'))) {
3439  *countername = '\0';
3440  countername++;
3441  }
3442  if ((operand = strchr(countername, ':'))) {
3443  *operand = '\0';
3444  operand++;
3445  }
3446 
3447  if ((domain = strchr(username, '@'))) {
3448  *domain = '\0';
3449  domain++;
3450  }
3451 
3452  /* If we have neither username nor domain now, let's give up */
3453  if (ast_strlen_zero(username) && ast_strlen_zero(domain)) {
3454  ast_log(LOG_ERROR, "No account given\n");
3455  return -1;
3456  }
3457 
3458  /* We only have a domain, no username */
3459  if (!ast_strlen_zero(username) && ast_strlen_zero(domain)) {
3460  domain = username;
3461  username = NULL;
3462  }
3463 
3464  if (ast_strlen_zero(operand) || ast_strlen_zero(countername)) {
3465  ast_log(LOG_ERROR, "Writing to this function requires three arguments: Account:countername:operand\n");
3466  return -1;
3467  }
3468 
3469  /* If we can't find account or if the account is temporary, return. */
3470  if (!ast_strlen_zero(username) && !find_account(domain, username, FALSE)) {
3471  ast_log(LOG_ERROR, "Minivm account does not exist: %s@%s\n", username, domain);
3472  return 0;
3473  }
3474 
3475  create_dirpath(userpath, sizeof(userpath), domain, username, NULL);
3476  /* Now, find out our operator */
3477  if (*operand == 'i') /* Increment */
3478  operation = 2;
3479  else if (*operand == 'd') {
3480  change = change * -1;
3481  operation = 2;
3482  } else if (*operand == 's')
3483  operation = 1;
3484  else {
3485  ast_log(LOG_ERROR, "Unknown operator: %s\n", operand);
3486  return -1;
3487  }
3488 
3489  /* We have the path, now read the counter file */
3490  access_counter_file(userpath, countername, change, operation);
3491  return 0;
3492 }
3493 
3494 
3495 /*! \brief CLI commands for Mini-voicemail */
3496 static struct ast_cli_entry cli_minivm[] = {
3497  AST_CLI_DEFINE(handle_minivm_show_users, "List defined mini-voicemail boxes"),
3498  AST_CLI_DEFINE(handle_minivm_show_zones, "List zone message formats"),
3499  AST_CLI_DEFINE(handle_minivm_list_templates, "List message templates"),
3500  AST_CLI_DEFINE(handle_minivm_reload, "Reload Mini-voicemail configuration"),
3501  AST_CLI_DEFINE(handle_minivm_show_stats, "Show some mini-voicemail statistics"),
3502  AST_CLI_DEFINE(handle_minivm_show_settings, "Show mini-voicemail general settings"),
3503 };
3504 
3506  .name = "MINIVMCOUNTER",
3507  .read = minivm_counter_func_read,
3508  .write = minivm_counter_func_write,
3509 };
3510 
3512  .name = "MINIVMACCOUNT",
3513  .read = minivm_account_func_read,
3514 };
3515 
3516 /*! \brief Load mini voicemail module */
3517 static int load_module(void)
3518 {
3519  int res;
3520 
3527 
3528  ast_custom_function_register(&minivm_account_function);
3529  ast_custom_function_register(&minivm_counter_function);
3530  if (res)
3531  return(res);
3532 
3533  if ((res = load_config(0)))
3534  return(res);
3535 
3536  ast_cli_register_multiple(cli_minivm, ARRAY_LEN(cli_minivm));
3537 
3538  /* compute the location of the voicemail spool directory */
3539  snprintf(MVM_SPOOL_DIR, sizeof(MVM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
3540 
3541  return res;
3542 }
3543 
3544 /*! \brief Reload mini voicemail module */
3545 static int reload(void)
3546 {
3547  return(load_config(1));
3548 }
3549 
3550 /*! \brief Reload cofiguration */
3551 static char *handle_minivm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
3552 {
3553 
3554  switch (cmd) {
3555  case CLI_INIT:
3556  e->command = "minivm reload";
3557  e->usage =
3558  "Usage: minivm reload\n"
3559  " Reload mini-voicemail configuration and reset statistics\n";
3560  return NULL;
3561  case CLI_GENERATE:
3562  return NULL;
3563  }
3564 
3565  reload();
3566  ast_cli(a->fd, "\n-- Mini voicemail re-configured \n");
3567  return CLI_SUCCESS;
3568 }
3569 
3570 /*! \brief Unload mini voicemail module */
3571 static int unload_module(void)
3572 {
3573  int res;
3574 
3581 
3582  ast_cli_unregister_multiple(cli_minivm, ARRAY_LEN(cli_minivm));
3583  ast_custom_function_unregister(&minivm_account_function);
3584  ast_custom_function_unregister(&minivm_counter_function);
3585 
3586  message_destroy_list(); /* Destroy list of voicemail message templates */
3587  timezone_destroy_list(); /* Destroy list of timezones */
3588  vmaccounts_destroy_list(); /* Destroy list of voicemail accounts */
3589 
3590  return res;
3591 }
3592 
3593 
3594 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mini VoiceMail (A minimal Voicemail e-mail System)",
3595  .support_level = AST_MODULE_SUPPORT_EXTENDED,
3596  .load = load_module,
3597  .unload = unload_module,
3598  .reload = reload,
3599 );
const char * name
Definition: pbx.h:119
#define DEFAULT_DATEFORMAT
Definition: app_minivm.c:711
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3339
static struct minivm_template * message_template_find(const char *name)
Definition: app_minivm.c:816
struct ast_variable * next
static const char type[]
Definition: chan_ooh323.c:109
char attachfmt[80]
Definition: app_minivm.c:615
static int reload(void)
Reload mini voicemail module.
Definition: app_minivm.c:3545
static int get_date(char *s, int len)
Definition: app_minivm.c:977
#define ast_channel_lock(chan)
Definition: channel.h:2902
Main Channel structure associated with a channel.
struct timeval reset
Definition: app_minivm.c:681
static int vm_delete(char *file)
Definition: app_minivm.c:1630
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
minivm_option_args
Definition: app_minivm.c:579
char * str
Subscriber phone number (Malloced)
Definition: channel.h:292
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1250
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:39
Asterisk locking-related definitions:
Asterisk main include file. File version handling, generic pbx functions.
static char * handle_minivm_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI command to list voicemail accounts.
Definition: app_minivm.c:3068
#define FALSE
Definition: app_minivm.c:521
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
static struct minivm_account * find_user_realtime(const char *domain, const char *username)
Definition: app_minivm.c:1111
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:591
#define MVM_REVIEW
Definition: app_minivm.c:525
static int create_vmaccount(char *name, struct ast_variable *var, int realtime)
Append new mailbox to mailbox list from configuration file.
Definition: app_minivm.c:2594
char etemplate[80]
Definition: app_minivm.c:616
unsigned int flags
Definition: app_minivm.c:651
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1263
char name[80]
Definition: app_minivm.c:666
char * config
Definition: conf2ael.c:66
#define MVM_ALLOCED
Definition: app_minivm.c:531
static struct ast_custom_function minivm_counter_function
Definition: app_minivm.c:3505
#define AST_DIGIT_ANY
Definition: file.h:48
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
static int b64_inchar(struct b64_baseio *bio, FILE *fi)
Definition: app_minivm.c:874
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2938
static char * handle_minivm_list_templates(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI routine for listing templates.
Definition: app_minivm.c:3004
static int message_template_build(const char *name, struct ast_variable *var)
Definition: app_minivm.c:753
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define ast_test_flag(p, flag)
Definition: utils.h:63
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1216
Time-related functions and macros.
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_party_name name
Subscriber name.
Definition: channel.h:341
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
Convenient Signal Processing routines.
static int global_vmminmessage
Definition: app_minivm.c:694
#define ast_set_flag(p, flag)
Definition: utils.h:70
struct ast_mwi_state * ast_mwi_create(const char *mailbox, const char *context)
Create a ast_mwi_state object.
Definition: mwi.c:148
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
descriptor for a cli entry.
Definition: cli.h:171
#define HMSU_OUTPUT_FORMAT
const int argc
Definition: cli.h:160
#define LOG_WARNING
Definition: logger.h:274
static char * handle_minivm_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Reload cofiguration.
Definition: app_minivm.c:3551
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:139
struct stasis_message * ast_mwi_blob_create(struct ast_mwi_state *mwi_state, struct stasis_message_type *message_type, struct ast_json *blob)
Creates a ast_mwi_blob message.
Definition: mwi.c:462
static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct minivm_account *vmu, int *duration, int *sound_duration, const char *unlockdir, signed char record_gain)
Definition: app_minivm.c:1644
static int minivm_counter_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
${MINIVMCOUNTER()} Dialplan function - read counters
Definition: app_minivm.c:3372
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:714
#define CONFIG_STATUS_FILEINVALID
static void free_user(struct minivm_account *vmu)
Definition: app_minivm.c:989
char language[MAX_LANGUAGE]
Definition: app_minivm.c:611
static int tmp()
Definition: bt_open.c:389
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
static const struct ast_app_option minivm_accmess_options[128]
Definition: app_minivm.c:596
mvm_messagetype
Message types for notification.
Definition: app_minivm.c:552
Structure for variables, used for configurations and for channel variables.
#define var
Definition: ast_expr2f.c:614
static char * app_minivm_notify
Definition: app_minivm.c:563
char username[AST_MAX_CONTEXT]
Definition: app_minivm.c:601
static int timezone_add(const char *zonename, const char *config)
Add time zone to memory list.
Definition: app_minivm.c:2707
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
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:8254
static void vmaccounts_destroy_list(void)
Definition: app_minivm.c:1055
Definition: cli.h:152
ast_channel_state
ast_channel states
Definition: channelstate.h:35
char * str
Subscriber name (Malloced)
Definition: channel.h:265
static void free_zone(struct minivm_zone *z)
Free Mini Voicemail timezone.
Definition: app_minivm.c:2689
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1091
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:150
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:449
static char locale[20]
static int global_vmmaxmessage
Definition: app_minivm.c:695
#define ast_mutex_lock(a)
Definition: lock.h:187
static struct test_val c
Definition: muted.c:95
#define ast_copy_flags(dest, src, flagz)
Definition: utils.h:84
#define ast_str_alloca(init_len)
Definition: strings.h:800
static int global_silencethreshold
Definition: app_minivm.c:698
#define MAXHOSTNAMELEN
Definition: network.h:69
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:243
static char global_mailcmd[160]
Definition: app_minivm.c:699
char accountcode[AST_MAX_ACCOUNT_CODE]
Definition: app_minivm.c:608
const char * str
Definition: app_jack.c:147
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3328
static int base_encode(char *filename, FILE *so)
Definition: app_minivm.c:905
int ast_unlock_path(const char *path)
Unlock a path.
Definition: main/app.c:2396
#define NULL
Definition: resample.c:96
static char * handle_minivm_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail zones in the CLI.
Definition: app_minivm.c:3116
Domain data structure.
Definition: sip.h:888
char * end
Definition: eagi_proxy.c:73
static int minivm_mwi_exec(struct ast_channel *chan, const char *data)
Definition: app_minivm.c:2099
static char * chanvar
Definition: app_system.c:113
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1098
int value
Definition: syslog.c:37
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
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:7421
static int notify_new_message(struct ast_channel *chan, const char *templatename, struct minivm_account *vmu, const char *filename, long duration, const char *format, char *cidnum, char *cidname)
Definition: app_minivm.c:1811
struct timeval lastreceived
Definition: app_minivm.c:683
static int create_dirpath(char *dest, int len, char *domain, char *username, char *folder)
Definition: app_minivm.c:1557
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
int ast_channel_priority(const struct ast_channel *chan)
#define ast_verb(level,...)
Definition: logger.h:455
char domain[AST_MAX_CONTEXT]
Definition: app_minivm.c:602
const char * line
Definition: cli.h:162
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
#define MAX_LANGUAGE
Definition: channel.h:173
Utility functions.
int args
This gets set in ast_cli_register()
Definition: cli.h:185
static int leave_voicemail(struct ast_channel *chan, char *username, struct leave_vm_options *options)
Definition: app_minivm.c:1912
static char * emailbody
int voicemailaccounts
Definition: app_minivm.c:677
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
char pincode[10]
Definition: app_minivm.c:604
static void message_template_free(struct minivm_template *template)
Definition: app_minivm.c:743
static int b64_ochar(struct b64_baseio *bio, int c, FILE *so)
Definition: app_minivm.c:886
Number structure.
Definition: app_followme.c:154
#define AST_MAX_ACCOUNT_CODE
Definition: channel.h:171
Custom localtime functions for multiple timezones.
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1065
static struct minivm_stats global_stats
Statistics for voicemail.
Definition: app_minivm.c:687
struct ast_party_id id
Caller party ID.
Definition: channel.h:421
char locale[20]
Definition: app_minivm.c:640
enum AST_LOCK_RESULT ast_lock_path(const char *path)
Lock a filesystem path.
Definition: main/app.c:2380
Configuration File Parser.
static char mailbox[AST_MAX_MAILBOX_UNIQUEID]
Definition: chan_mgcp.c:204
#define ast_debug(level,...)
Log a DEBUG message.
Definition: logger.h:444
#define ast_log
Definition: astobj2.c:42
static int check_dirpath(char *dest, int len, char *domain, char *username, char *folder)
Definition: app_minivm.c:1538
static void timezone_destroy_list(void)
Clear list of timezones.
Definition: app_minivm.c:2695
#define SENTINEL
Definition: compiler.h:87
static int invent_message(struct ast_channel *chan, char *domain, char *username, int busy, char *ecodes)
Definition: app_minivm.c:1573
static char * app_minivm_record
Definition: app_minivm.c:561
#define ast_config_load(filename, flags)
Load a config file.
char exit[80]
Definition: app_minivm.c:614
static char host[256]
Definition: muted.c:77
char email[80]
Definition: app_minivm.c:606
char * ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown)
Definition: callerid.c:1073
General Asterisk PBX channel definitions.
Asterisk JSON abstraction layer.
unsigned int flags
Definition: app_minivm.c:618
static int minivm_notify_exec(struct ast_channel *chan, const char *data)
Definition: app_minivm.c:2137
Asterisk file paths, configured in asterisk.conf.
#define MVM_OPERATOR
Definition: app_minivm.c:526
#define B64_BASEMAXINLINE
Definition: app_minivm.c:538
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:851
const int fd
Definition: cli.h:159
static int unload_module(void)
Unload mini voicemail module.
Definition: app_minivm.c:3571
#define B64_BASELINELEN
Definition: app_minivm.c:539
const int n
Definition: cli.h:165
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Definition: channel.h:1283
struct minivm_account::@54 list
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
static int global_maxgreet
Definition: app_minivm.c:697
Caller Party information.
Definition: channel.h:419
int ast_play_and_wait(struct ast_channel *chan, const char *fn)
Play a stream and wait for a digit, returning the digit that was pressed.
Definition: main/app.c:1393
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:85
static struct ast_cli_entry cli_minivm[]
CLI commands for Mini-voicemail.
Definition: app_minivm.c:3496
int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence_ms, const char *path, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists)
Record a file based on input from a channel This function will play "auth-thankyou" upon successful r...
Definition: main/app.c:1915
void ast_config_destroy(struct ast_config *config)
Destroys a config.
Definition: extconf.c:1290
long int ast_random(void)
Definition: main/utils.c:1935
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:300
char zonetag[80]
Definition: app_minivm.c:612
char timezone[80]
Definition: app_minivm.c:667
A set of macros to manage forward-linked lists.
int linelength
Definition: app_minivm.c:659
static struct minivm_account * find_account(const char *domain, const char *username, int createtemp)
Definition: app_minivm.c:1067
#define ast_variable_new(name, value, filename)
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:832
static char * handle_minivm_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
CLI Show settings.
Definition: app_minivm.c:3153
static const struct ast_app_option minivm_app_options[128]
Definition: app_minivm.c:589
static void populate_defaults(struct minivm_account *vmu)
Definition: app_minivm.c:1031
static char * message_template_parse_filebody(const char *filename)
Read message template from file.
Definition: app_minivm.c:2739
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:4179
static const char * ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
Definition: app_minivm.c:1214
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:2829
static struct minivm_template * message_template_create(const char *name)
Definition: app_minivm.c:723
const char * ast_channel_exten(const struct ast_channel *chan)
Core PBX routines and definitions.
static int load_module(void)
Load mini voicemail module.
Definition: app_minivm.c:3517
static int global_maxsilence
Definition: app_minivm.c:696
#define CONFIG_STATUS_FILEUNCHANGED
#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 struct minivm_account * mvm_user_alloc(void)
Definition: app_minivm.c:1040
const char * ast_channel_uniqueid(const struct ast_channel *chan)
const char *const * argv
Definition: cli.h:161
static FILE * minivmlogfile
Definition: app_minivm.c:692
double volgain
Definition: app_minivm.c:620
static char * complete_minivm_show_users(const char *line, const char *word, int pos, int state)
Definition: app_minivm.c:3045
#define HMSZ_OUTPUT_FORMAT
static char default_vmformat[80]
Definition: app_minivm.c:702
static int sendmail(struct minivm_template *template, struct minivm_account *vmu, char *cidnum, char *cidname, const char *filename, char *format, int duration, int attach_user_voicemail, enum mvm_messagetype type, const char *counter)
Definition: app_minivm.c:1234
#define LOG_ERROR
Definition: logger.h:285
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:730
struct ast_variable * chanvars
Definition: app_minivm.c:619
static int check_mime(const char *str)
Definition: app_minivm.c:1144
static char * handle_minivm_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show stats.
Definition: app_minivm.c:3185
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true". This function checks to see whether a string passed to it is an indication of an "true" value. It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
Definition: main/utils.c:1822
The descriptor of a dynamic string XXX storage will be optimized later if needed We use the ts field ...
Definition: strings.h:584
unsigned char iobuf[B64_BASEMAXINLINE]
Definition: app_minivm.c:661
int ast_safe_system(const char *s)
Safely spawn an OS shell command while closing file descriptors.
Definition: extconf.c:829
char uniqueid[20]
Definition: app_minivm.c:613
#define AST_OPTION_RXGAIN
char * mkdtemp(char *template_s)
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
#define CLI_SHOWUSAGE
Definition: cli.h:45
const char * ast_config_AST_CONFIG_DIR
Definition: options.c:151
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
int errno
char msg_format[BUFSIZ]
Definition: app_minivm.c:668
Voicemail time zones.
Definition: app_minivm.c:665
int receivedmessages
Definition: app_minivm.c:682
#define LOG_NOTICE
Definition: logger.h:263
const char * ast_config_AST_LOG_DIR
Definition: options.c:159
static char * app_minivm_mwi
Definition: app_minivm.c:566
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:490
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:409
#define ast_strlen_zero(a)
Definition: muted.c:73
#define ast_channel_unlock(chan)
Definition: channel.h:2903
#define CLI_FAILURE
Definition: cli.h:46
static int global_saydurationminfo
Definition: app_minivm.c:705
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic&#39;s subscribers.
Definition: stasis.c:1510
static ast_mutex_t minivmloglock
Definition: app_minivm.c:690
#define AST_MAX_CONTEXT
Definition: channel.h:136
static const char name[]
Definition: cdr_mysql.c:74
#define ast_free(a)
Definition: astmm.h:182
char * command
Definition: cli.h:186
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:204
#define DEFAULT_CHARSET
Definition: app_minivm.c:712
minivm_option_flags
Definition: app_minivm.c:570
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:1813
#define AST_FLAGS_ALL
Definition: utils.h:196
static char * message_template_parse_emailbody(const char *body)
Parse emailbody template from configuration file.
Definition: app_minivm.c:2779
const char * word
Definition: cli.h:163
const char * ast_config_AST_SPOOL_DIR
Definition: options.c:154
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
#define ast_publish_mwi_state_channel(mailbox, context, new_msgs, old_msgs, channel_id)
Publish a MWI state update associated with some channel.
Definition: mwi.h:397
static char global_externnotify[160]
Definition: app_minivm.c:700
charset
Definition: chan_unistim.c:336
static double global_volgain
Definition: app_minivm.c:707
Structure used to handle boolean flags.
Definition: utils.h:199
The list of e-mail time zones.
Definition: app_minivm.c:673
AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS|AST_MODFLAG_LOAD_ORDER, "HTTP Phone Provisioning",.support_level=AST_MODULE_SUPPORT_EXTENDED,.load=load_module,.unload=unload_module,.reload=reload,.load_pri=AST_MODPRI_CHANNEL_DEPEND,.requires="http",)
char fullname[120]
Definition: app_minivm.c:605
static int vm_lock_path(const char *path)
lock directory
Definition: app_minivm.c:3302
Options for leaving voicemail with the voicemail() application.
Definition: app_minivm.c:650
const char * usage
Definition: cli.h:177
void ast_channel_exten_set(struct ast_channel *chan, const char *value)
Structure for base64 encoding.
Definition: app_minivm.c:656
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name...
#define SENDMAIL
Default mail command to mail voicemail. Change it with the mailcmd= command in voicemail.conf.
Definition: app_minivm.c:535
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
Definition: channel.c:3170
The list of e-mail templates.
Definition: app_minivm.c:647
signed char record_gain
Definition: app_minivm.c:652
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:653
#define CLI_SUCCESS
Definition: cli.h:44
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:694
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:688
#define EOL
Definition: app_minivm.c:540
static int minivm_counter_func_write(struct ast_channel *chan, const char *cmd, char *data, const char *value)
${MINIVMCOUNTER()} Dialplan function - changes counter data
Definition: app_minivm.c:3425
char ptemplate[80]
Definition: app_minivm.c:617
static char dateformat[256]
Definition: logger.c:77
char * strsep(char **str, const char *delims)
FILE * out
Definition: utils/frame.c:33
char pager[80]
Definition: app_minivm.c:607
struct ast_channel_snapshot * ast_channel_snapshot_get_latest(const char *uniqueid)
Obtain the latest ast_channel_snapshot from the Stasis Message Bus API cache. This is an ao2 object...
#define ao2_cleanup(obj)
Definition: astobj2.h:1958
Standard Command Line Interface.
static int load_config(int reload)
Load minivoicemail configuration.
Definition: app_minivm.c:2864
char externnotify[160]
Definition: app_minivm.c:610
void ast_channel_context_set(struct ast_channel *chan, const char *value)
#define ERROR_LOCK_PATH
Definition: app_minivm.c:545
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:401
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one...
Definition: strings.h:79
static void queue_mwi_event(const char *channel_id, const char *mbx, const char *ctx, int urgent, int new, int old)
Definition: app_minivm.c:2084
const char * ast_channel_name(const struct ast_channel *chan)
const int pos
Definition: cli.h:164
#define TRUE
Definition: app_minivm.c:518
static int minivm_account_func_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
${MINIVMACCOUNT()} Dialplan function - reads account data
Definition: app_minivm.c:3225
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1775
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1086
Asterisk MWI API.
#define VOICEMAIL_CONFIG
Definition: app_minivm.c:548
static ENTRY retval
Definition: hsearch.c:50
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2800
static int minivm_accmess_exec(struct ast_channel *chan, const char *data)
Record specific messages for voicemail account.
Definition: app_minivm.c:2496
static char * app_minivm_accmess
Definition: app_minivm.c:565
const char * ast_channel_language(const struct ast_channel *chan)
Abstract JSON element (object, array, string, int, ...).
const char * ast_channel_context(const struct ast_channel *chan)
int error(const char *format,...)
Definition: utils/frame.c:999
#define PATH_MAX
Definition: asterisk.h:40
static const char * ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *charset, const char *start, size_t preamble, size_t postamble)
Definition: app_minivm.c:1173
static int access_counter_file(char *directory, char *countername, int value, int operand)
Access counter file, lock directory, read and possibly write it again changed.
Definition: app_minivm.c:3319
static int minivm_greet_exec(struct ast_channel *chan, const char *data)
Definition: app_minivm.c:2268
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:280
static struct test_options options
static struct ast_str * prompt
Definition: asterisk.c:2725
static char context[AST_MAX_CONTEXT]
Definition: chan_alsa.c:116
static int b64_inbuf(struct b64_baseio *bio, FILE *fi)
Definition: app_minivm.c:851
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
static char * app_minivm_greet
Definition: app_minivm.c:562
Structure for gathering statistics.
Definition: app_minivm.c:676
static void run_externnotify(struct ast_channel *chan, struct minivm_account *vmu)
Run external notification for voicemail message.
Definition: app_minivm.c:1776
const char * ast_channel_macrocontext(const struct ast_channel *chan)
static ast_mutex_t minivmlock
Definition: app_minivm.c:689
static int make_dir(char *dest, int len, const char *domain, const char *username, const char *folder)
Definition: app_minivm.c:1524
The structure that contains MWI state.
Definition: mwi.h:457
Say numbers and dates (maybe words one day too)
struct stasis_topic * ast_mwi_topic(const char *uniqueid)
Get the Stasis Message Bus API topic for MWI messages on a unique ID.
Definition: mwi.c:100
#define HVLT_OUTPUT_FORMAT
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
#define DEBUG_ATLEAST(level)
Definition: logger.h:433
void ast_channel_priority_set(struct ast_channel *chan, int value)
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:1959
Asterisk module definitions.
#define SOUND_INTRO
Definition: app_minivm.c:537
static void message_destroy_list(void)
Definition: app_minivm.c:838
static snd_pcm_format_t format
Definition: chan_alsa.c:102
static struct ast_flags globalflags
Definition: app_minivm.c:704
int ast_safe_execvp(int dualfork, const char *file, char *const argv[])
Safely spawn an external program while closing file descriptors.
Definition: asterisk.c:1180
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:298
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1508
static int minivm_record_exec(struct ast_channel *chan, const char *data)
Definition: app_minivm.c:2215
static void prep_email_sub_vars(struct ast_channel *channel, const struct minivm_account *vmu, const char *cidnum, const char *cidname, const char *dur, const char *date, const char *counter)
Definition: app_minivm.c:1002
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:518
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:187
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:626
static char * app_minivm_delete
Definition: app_minivm.c:564
static int apply_general_options(struct ast_variable *var)
Apply general configuration options.
Definition: app_minivm.c:2806
char serveremail[80]
Definition: app_minivm.c:609
#define ast_app_separate_args(a, b, c, d)
static int minivm_delete_exec(struct ast_channel *chan, const char *data)
Definition: app_minivm.c:2458
static char global_logfile[PATH_MAX]
Definition: app_minivm.c:701
#define ast_str_create(init_len)
Create a malloc&#39;ed dynamic length string.
Definition: strings.h:620
short word
#define ast_mutex_unlock(a)
Definition: lock.h:188
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: main/utils.c:2102
static struct test_val a
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:343
static struct ast_custom_function minivm_account_function
Definition: app_minivm.c:3511
static char MVM_SPOOL_DIR[PATH_MAX]
Definition: app_minivm.c:558
struct stasis_message_type * ast_mwi_vm_app_type(void)
Get the Stasis Message Bus API message type for voicemail application specific messages.