Wed Jan 8 2020 09:49:40

Asterisk developer's documentation


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