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