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