Asterisk - The Open Source Telephony Project GIT-master-8924258
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
app_voicemail_imap.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 1999 - 2006, Digium, Inc.
5 *
6 * Mark Spencer <markster@digium.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*!
20 * \file
21 * \author Mark Spencer <markster@digium.com>
22 * \brief Comedian Mail - Voicemail System
23 *
24 * unixODBC (http://www.unixodbc.org/)
25 * A source distribution of University of Washington's IMAP c-client
26 * (http://www.washington.edu/imap/)
27 *
28 * \par See also
29 * \arg \ref voicemail.conf "Config_voicemail"
30 * \note For information about voicemail IMAP storage, https://docs.asterisk.org/Configuration/Applications/Voicemail/IMAP-Voicemail-Storage/
31 * \ingroup applications
32 * \todo This module requires res_adsi to load. This needs to be optional
33 * during compilation.
34 *
35 * \todo This file is now almost impossible to work with, due to all \#ifdefs.
36 * Feels like the database code before realtime. Someone - please come up
37 * with a plan to clean this up.
38 */
39
40/*! \li \ref app_voicemail.c uses configuration file \ref voicemail.conf
41 * \addtogroup configuration_file Configuration Files
42 */
43
44/*!
45 * \page voicemail.conf voicemail.conf
46 * \verbinclude voicemail.conf.sample
47 */
48
49#include "asterisk.h"
50
51#ifdef IMAP_STORAGE
52#include <ctype.h>
53#include <signal.h>
54#include <pwd.h>
55#ifdef USE_SYSTEM_IMAP
56#include <imap/c-client.h>
57#include <imap/imap4r1.h>
58#include <imap/linkage.h>
59#elif defined (USE_SYSTEM_CCLIENT)
60#include <c-client/c-client.h>
61#include <c-client/imap4r1.h>
62#include <c-client/linkage.h>
63#else
64#include "c-client.h"
65#include "imap4r1.h"
66#include "linkage.h"
67#endif
68#endif
69
70#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
71#include <sys/time.h>
72#include <sys/stat.h>
73#include <sys/mman.h>
74#include <time.h>
75#include <dirent.h>
76#if defined(__FreeBSD__) || defined(__OpenBSD__)
77#include <sys/wait.h>
78#endif
79
80#include "asterisk/logger.h"
81#include "asterisk/lock.h"
82#include "asterisk/file.h"
83#include "asterisk/channel.h"
84#include "asterisk/pbx.h"
85#include "asterisk/config.h"
86#include "asterisk/say.h"
87#include "asterisk/module.h"
88#include "asterisk/adsi.h"
89#include "asterisk/app.h"
90#include "asterisk/mwi.h"
91#include "asterisk/manager.h"
92#include "asterisk/dsp.h"
93#include "asterisk/localtime.h"
94#include "asterisk/cli.h"
95#include "asterisk/utils.h"
97#include "asterisk/strings.h"
98#include "asterisk/smdi.h"
99#include "asterisk/astobj2.h"
101#include "asterisk/test.h"
103
104#ifdef ODBC_STORAGE
105#include "asterisk/res_odbc.h"
106#endif
107
108#ifdef IMAP_STORAGE
110#endif
111
112/*** DOCUMENTATION
113 <application name="VoiceMail" language="en_US">
114 <since>
115 <version>0.1.0</version>
116 </since>
117 <synopsis>
118 Leave a Voicemail message.
119 </synopsis>
120 <syntax>
121 <parameter name="mailboxs" argsep="&amp;" required="true">
122 <argument name="mailbox1" argsep="@" required="true">
123 <argument name="mailbox" required="true" />
124 <argument name="context" />
125 </argument>
126 <argument name="mailbox2" argsep="@" multiple="true">
127 <argument name="mailbox" required="true" />
128 <argument name="context" />
129 </argument>
130 </parameter>
131 <parameter name="options">
132 <optionlist>
133 <option name="b">
134 <para>Play the <literal>busy</literal> greeting to the calling party.</para>
135 </option>
136 <option name="d">
137 <argument name="c" />
138 <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
139 if played during the greeting. Context defaults to the current context.</para>
140 </option>
141 <option name="e">
142 <para>Play greetings as early media -- only answer the channel just
143 before accepting the voice message.</para>
144 </option>
145 <option name="g">
146 <argument name="#" required="true" />
147 <para>Use the specified amount of gain when recording the voicemail
148 message. The units are whole-number decibels (dB). Only works on supported
149 technologies, which is DAHDI only.</para>
150 </option>
151 <option name="s">
152 <para>Skip the playback of instructions for leaving a message to the
153 calling party.</para>
154 </option>
155 <option name="S">
156 <para>Skip the playback of instructions for leaving a message to the
157 calling party, but only if a greeting has been recorded by the
158 mailbox user.</para>
159 </option>
160 <option name="t">
161 <argument name="x" required="false" />
162 <para>Play a custom beep tone to the caller instead of the default one.
163 If this option is used but no file is specified, the beep is suppressed.</para>
164 </option>
165 <option name="u">
166 <para>Play the <literal>unavailable</literal> greeting.</para>
167 </option>
168 <option name="U">
169 <para>Mark message as <literal>URGENT</literal>.</para>
170 </option>
171 <option name="P">
172 <para>Mark message as <literal>PRIORITY</literal>.</para>
173 </option>
174 </optionlist>
175 </parameter>
176 </syntax>
177 <description>
178 <para>This application allows the calling party to leave a message for the specified
179 list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
180 the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
181 exist.</para>
182 <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
183 <enumlist>
184 <enum name="0">
185 <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
186 </enum>
187 <enum name="*">
188 <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
189 </enum>
190 </enumlist>
191 <para>This application will set the following channel variable upon completion:</para>
192 <variablelist>
193 <variable name="VMSTATUS">
194 <para>This indicates the status of the execution of the VoiceMail application.</para>
195 <value name="SUCCESS" />
196 <value name="USEREXIT" />
197 <value name="FAILED" />
198 </variable>
199 </variablelist>
200 </description>
201 <see-also>
202 <ref type="application">VoiceMailMain</ref>
203 </see-also>
204 </application>
205 <application name="VoiceMailMain" language="en_US">
206 <since>
207 <version>0.1.0</version>
208 </since>
209 <synopsis>
210 Check Voicemail messages.
211 </synopsis>
212 <syntax>
213 <parameter name="mailbox" required="true" argsep="@">
214 <argument name="mailbox" />
215 <argument name="context" />
216 </parameter>
217 <parameter name="options">
218 <optionlist>
219 <option name="p">
220 <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
221 the mailbox that is entered by the caller.</para>
222 </option>
223 <option name="g">
224 <argument name="#" required="true" />
225 <para>Use the specified amount of gain when recording a voicemail message.
226 The units are whole-number decibels (dB).</para>
227 </option>
228 <option name="r">
229 <para>"Read only". Prevent user from deleting any messages.</para>
230 <para>This applies only to specific executions of VoiceMailMain, NOT the mailbox itself.</para>
231 </option>
232 <option name="s">
233 <para>Skip checking the passcode for the mailbox.</para>
234 </option>
235 <option name="a">
236 <argument name="folder" required="true" />
237 <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
238 Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
239 <enumlist>
240 <enum name="0"><para>INBOX</para></enum>
241 <enum name="1"><para>Old</para></enum>
242 <enum name="2"><para>Work</para></enum>
243 <enum name="3"><para>Family</para></enum>
244 <enum name="4"><para>Friends</para></enum>
245 <enum name="5"><para>Cust1</para></enum>
246 <enum name="6"><para>Cust2</para></enum>
247 <enum name="7"><para>Cust3</para></enum>
248 <enum name="8"><para>Cust4</para></enum>
249 <enum name="9"><para>Cust5</para></enum>
250 </enumlist>
251 </option>
252 </optionlist>
253 </parameter>
254 </syntax>
255 <description>
256 <para>This application allows the calling party to check voicemail messages. A specific
257 <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
258 may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
259 be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
260 <literal>default</literal> context will be used.</para>
261 <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
262 or Password, and the extension exists:</para>
263 <enumlist>
264 <enum name="*">
265 <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
266 </enum>
267 </enumlist>
268 </description>
269 <see-also>
270 <ref type="application">VoiceMail</ref>
271 </see-also>
272 </application>
273 <application name="VMAuthenticate" language="en_US">
274 <since>
275 <version>1.2.0</version>
276 </since>
277 <synopsis>
278 Authenticate with Voicemail passwords.
279 </synopsis>
280 <syntax>
281 <parameter name="mailbox" required="true" argsep="@">
282 <argument name="mailbox" />
283 <argument name="context" />
284 </parameter>
285 <parameter name="options">
286 <optionlist>
287 <option name="s">
288 <para>Skip playing the initial prompts.</para>
289 </option>
290 </optionlist>
291 </parameter>
292 </syntax>
293 <description>
294 <para>This application behaves the same way as the Authenticate application, but the passwords
295 are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
296 specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
297 is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
298 mailbox.</para>
299 <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
300 or Password, and the extension exists:</para>
301 <enumlist>
302 <enum name="*">
303 <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
304 </enum>
305 </enumlist>
306 </description>
307 </application>
308 <application name="VoiceMailPlayMsg" language="en_US">
309 <since>
310 <version>11.0.0</version>
311 </since>
312 <synopsis>
313 Play a single voice mail msg from a mailbox by msg id.
314 </synopsis>
315 <syntax>
316 <parameter name="mailbox" required="true" argsep="@">
317 <argument name="mailbox" />
318 <argument name="context" />
319 </parameter>
320 <parameter name="msg_id" required="true">
321 <para>The msg id of the msg to play back. </para>
322 </parameter>
323 </syntax>
324 <description>
325 <para>This application sets the following channel variable upon completion:</para>
326 <variablelist>
327 <variable name="VOICEMAIL_PLAYBACKSTATUS">
328 <para>The status of the playback attempt as a text string.</para>
329 <value name="SUCCESS"/>
330 <value name="FAILED"/>
331 </variable>
332 </variablelist>
333 </description>
334 </application>
335 <application name="VMSayName" language="en_US">
336 <since>
337 <version>1.8.0</version>
338 </since>
339 <synopsis>
340 Play the name of a voicemail user
341 </synopsis>
342 <syntax>
343 <parameter name="mailbox" required="true" argsep="@">
344 <argument name="mailbox" />
345 <argument name="context" />
346 </parameter>
347 </syntax>
348 <description>
349 <para>This application will say the recorded name of the voicemail user specified as the
350 argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
351 <para>Similar to the Background() application, playback of the recorded
352 name can be interrupted by entering an extension, which will be searched
353 for in the current context.</para>
354 </description>
355 </application>
356 <function name="VM_INFO" language="en_US">
357 <since>
358 <version>11.0.0</version>
359 </since>
360 <synopsis>
361 Returns the selected attribute from a mailbox.
362 </synopsis>
363 <syntax argsep=",">
364 <parameter name="mailbox" argsep="@" required="true">
365 <argument name="mailbox" required="true" />
366 <argument name="context" />
367 </parameter>
368 <parameter name="attribute" required="true">
369 <optionlist>
370 <option name="count">
371 <para>Count of messages in specified <replaceable>folder</replaceable>.
372 If <replaceable>folder</replaceable> is not specified, defaults to <literal>INBOX</literal>.</para>
373 </option>
374 <option name="email">
375 <para>E-mail address associated with the mailbox.</para>
376 </option>
377 <option name="exists">
378 <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.</para>
379 </option>
380 <option name="fullname">
381 <para>Full name associated with the mailbox.</para>
382 </option>
383 <option name="language">
384 <para>Mailbox language if overridden, otherwise the language of the channel.</para>
385 </option>
386 <option name="locale">
387 <para>Mailbox locale if overridden, otherwise global locale.</para>
388 </option>
389 <option name="pager">
390 <para>Pager e-mail address associated with the mailbox.</para>
391 </option>
392 <option name="password">
393 <para>Mailbox access password.</para>
394 </option>
395 <option name="tz">
396 <para>Mailbox timezone if overridden, otherwise global timezone</para>
397 </option>
398 </optionlist>
399 </parameter>
400 <parameter name="folder" required="false">
401 <para>If not specified, <literal>INBOX</literal> is assumed.</para>
402 </parameter>
403 </syntax>
404 <description>
405 <para>Returns the selected attribute from the specified <replaceable>mailbox</replaceable>.
406 If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
407 context. Where the <replaceable>folder</replaceable> can be specified, common folders
408 include <literal>INBOX</literal>, <literal>Old</literal>, <literal>Work</literal>,
409 <literal>Family</literal> and <literal>Friends</literal>.</para>
410 </description>
411 </function>
412 <manager name="VoicemailUsersList" language="en_US">
413 <since>
414 <version>1.6.0</version>
415 </since>
416 <synopsis>
417 List All Voicemail User Information.
418 </synopsis>
419 <syntax>
420 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
421 </syntax>
422 <description>
423 </description>
424 </manager>
425 <manager name="VoicemailUserStatus" language="en_US">
426 <since>
427 <version>16.0.0</version>
428 </since>
429 <synopsis>
430 Show the status of given voicemail user's info.
431 </synopsis>
432 <syntax>
433 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
434 <parameter name="Context" required="true">
435 <para>The context you want to check.</para>
436 </parameter>
437 <parameter name="Mailbox" required="true">
438 <para>The mailbox you want to check.</para>
439 </parameter>
440 </syntax>
441 <description>
442 <para>Retrieves the status of the given voicemail user.</para>
443 </description>
444 </manager>
445 <manager name="VoicemailRefresh" language="en_US">
446 <since>
447 <version>12.0.0</version>
448 </since>
449 <synopsis>
450 Tell Asterisk to poll mailboxes for a change
451 </synopsis>
452 <syntax>
453 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
454 <parameter name="Context" />
455 <parameter name="Mailbox" />
456 </syntax>
457 <description>
458 <para>Normally, MWI indicators are only sent when Asterisk itself
459 changes a mailbox. With external programs that modify the content
460 of a mailbox from outside the application, an option exists called
461 <literal>pollmailboxes</literal> that will cause voicemail to
462 continually scan all mailboxes on a system for changes. This can
463 cause a large amount of load on a system. This command allows
464 external applications to signal when a particular mailbox has
465 changed, thus permitting external applications to modify mailboxes
466 and MWI to work without introducing considerable CPU load.</para>
467 <para>If <replaceable>Context</replaceable> is not specified, all
468 mailboxes on the system will be polled for changes. If
469 <replaceable>Context</replaceable> is specified, but
470 <replaceable>Mailbox</replaceable> is omitted, then all mailboxes
471 within <replaceable>Context</replaceable> will be polled.
472 Otherwise, only a single mailbox will be polled for changes.</para>
473 </description>
474 </manager>
475 <manager name="VoicemailBoxSummary" language="en_US">
476 <since>
477 <version>20.5.0</version>
478 <version>18.20.0</version>
479 </since>
480 <synopsis>
481 Show the mailbox contents of given voicemail user.
482 </synopsis>
483 <syntax>
484 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
485 <parameter name="Context" required="true">
486 <para>The context you want to check.</para>
487 </parameter>
488 <parameter name="Mailbox" required="true">
489 <para>The mailbox you want to check.</para>
490 </parameter>
491 </syntax>
492 <description>
493 <para>Retrieves the contents of the given voicemail user's mailbox.</para>
494 </description>
495 </manager>
496 <manager name="VoicemailMove" language="en_US">
497 <since>
498 <version>20.5.0</version>
499 <version>18.20.0</version>
500 </since>
501 <synopsis>
502 Move Voicemail between mailbox folders of given user.
503 </synopsis>
504 <syntax>
505 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
506 <parameter name="Context" required="true">
507 <para>The context of the Voicemail you want to move.</para>
508 </parameter>
509 <parameter name="Mailbox" required="true">
510 <para>The mailbox of the Voicemail you want to move.</para>
511 </parameter>
512 <parameter name="Folder" required="true">
513 <para>The Folder containing the Voicemail you want to move.</para>
514 </parameter>
515 <parameter name="ID" required="true">
516 <para>The ID of the Voicemail you want to move.</para>
517 </parameter>
518 <parameter name="ToFolder" required="true">
519 <para>The Folder you want to move the Voicemail to.</para>
520 </parameter>
521 </syntax>
522 <description>
523 <para>Move a given Voicemail between Folders within a user's Mailbox.</para>
524 </description>
525 </manager>
526 <manager name="VoicemailRemove" language="en_US">
527 <since>
528 <version>20.5.0</version>
529 <version>18.20.0</version>
530 </since>
531 <synopsis>
532 Remove Voicemail from mailbox folder.
533 </synopsis>
534 <syntax>
535 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
536 <parameter name="Context" required="true">
537 <para>The context of the Voicemail you want to remove.</para>
538 </parameter>
539 <parameter name="Mailbox" required="true">
540 <para>The mailbox of the Voicemail you want to remove.</para>
541 </parameter>
542 <parameter name="Folder" required="true">
543 <para>The Folder containing the Voicemail you want to remove.</para>
544 </parameter>
545 <parameter name="ID" required="true">
546 <para>The ID of the Voicemail you want to remove.</para>
547 </parameter>
548 </syntax>
549 <description>
550 <para>Remove a given Voicemail from a user's Mailbox Folder.</para>
551 </description>
552 </manager>
553 <manager name="VoicemailForward" language="en_US">
554 <since>
555 <version>20.5.0</version>
556 <version>18.20.0</version>
557 </since>
558 <synopsis>
559 Forward Voicemail from one mailbox folder to another between given users.
560 </synopsis>
561 <syntax>
562 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
563 <parameter name="Context" required="true">
564 <para>The context of the Voicemail you want to move.</para>
565 </parameter>
566 <parameter name="Mailbox" required="true">
567 <para>The mailbox of the Voicemail you want to move.</para>
568 </parameter>
569 <parameter name="Folder" required="true">
570 <para>The Folder containing the Voicemail you want to move.</para>
571 </parameter>
572 <parameter name="ID" required="true">
573 <para>The ID of the Voicemail you want to move.</para>
574 </parameter>
575 <parameter name="ToContext" required="true">
576 <para>The context you want to move the Voicemail to.</para>
577 </parameter>
578 <parameter name="ToMailbox" required="true">
579 <para>The mailbox you want to move the Voicemail to.</para>
580 </parameter>
581 <parameter name="ToFolder" required="true">
582 <para>The Folder you want to move the Voicemail to.</para>
583 </parameter>
584 </syntax>
585 <description>
586 <para>Forward a given Voicemail from a user's Mailbox Folder to
587 another user's Mailbox Folder. Can be used to copy between
588 Folders within a mailbox by specifying the to context and user
589 as the same as the from.</para>
590 </description>
591 </manager>
592 <managerEvent language="en_US" name="VoicemailPasswordChange">
593 <managerEventInstance class="EVENT_FLAG_USER">
594 <since>
595 <version>18.21.0</version>
596 <version>20.6.0</version>
597 <version>21.1.0</version>
598 </since>
599 <synopsis>Raised in response to a mailbox password change.</synopsis>
600 <syntax>
601 <parameter name="Context">
602 <para>Mailbox context.</para>
603 </parameter>
604 <parameter name="Mailbox">
605 <para>Mailbox name.</para>
606 </parameter>
607 <parameter name="NewPassword">
608 <para>New password for mailbox.</para>
609 </parameter>
610 </syntax>
611 </managerEventInstance>
612 </managerEvent>
613 ***/
614
615#ifdef IMAP_STORAGE
616static char imapserver[48] = "localhost";
617static char imapport[8] = "143";
618static char imapflags[128];
619static char imapfolder[64] = "INBOX";
620static char imapparentfolder[64];
621static char greetingfolder[64] = "INBOX";
622static char authuser[32];
623static char authpassword[42];
624static int imapversion = 1;
625
626static int expungeonhangup = 1;
627static int imapgreetings;
628static int imap_poll_logout;
629static char delimiter;
630
631/* mail_open cannot be protected on a stream basis */
632ast_mutex_t mail_open_lock;
633
634struct vm_state;
635struct ast_vm_user;
636
637AST_THREADSTORAGE(ts_vmstate);
638
639/* Forward declarations for IMAP */
640static int init_mailstream(struct vm_state *vms, int box);
641static void write_file(char *filename, char *buffer, unsigned long len);
642static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
643static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
644static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
645static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
646static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
647static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
648static void vmstate_insert(struct vm_state *vms);
649static void vmstate_delete(struct vm_state *vms);
650static void set_update(MAILSTREAM * stream);
651static void init_vm_state(struct vm_state *vms);
652static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
653static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream);
654static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
655static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
656static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag, const char *msg_id);
657static void vm_imap_update_msg_id(char *dir, int msgnum, const char *msg_id, struct ast_vm_user *vmu, struct ast_config *msg_cfg, int folder);
658static void update_messages_by_imapuser(const char *user, unsigned long number);
659static int vm_delete(char *file);
660
661static int imap_remove_file (char *dir, int msgnum);
662static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
663static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
664static void check_quota(struct vm_state *vms, char *mailbox);
665static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
666static void imap_logout(const char *mailbox_id);
667
668struct vmstate {
669 struct vm_state *vms;
670 AST_LIST_ENTRY(vmstate) list;
671};
672
673static AST_LIST_HEAD_STATIC(vmstates, vmstate);
674
675#endif
676
677#define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
678
679#define COMMAND_TIMEOUT 5000
680/* Don't modify these here; set your umask at runtime instead */
681#define VOICEMAIL_DIR_MODE 0777
682#define VOICEMAIL_FILE_MODE 0666
683#define CHUNKSIZE 65536
684
685#define VOICEMAIL_CONFIG "voicemail.conf"
686#define ASTERISK_USERNAME "asterisk"
687
688/* Define fast-forward, pause, restart, and reverse keys
689 * while listening to a voicemail message - these are
690 * strings, not characters */
691#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
692#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
693#define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
694#define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
695#define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
696#define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
697
698/* Default mail command to mail voicemail. Change it with the
699 * mailcmd= command in voicemail.conf */
700#define SENDMAIL "/usr/sbin/sendmail -t"
701#define INTRO "vm-intro"
702
703#define MAX_MAIL_BODY_CONTENT_SIZE 134217728L // 128 Mbyte
704
705#define MAXMSG 100
706#define MAXMSGLIMIT 9999
707
708#define MINPASSWORD 0 /*!< Default minimum mailbox password length */
709
710#ifdef IMAP_STORAGE
711#define ENDL "\r\n"
712#else
713#define ENDL "\n"
714#endif
715
716#define MAX_DATETIME_FORMAT 512
717#define MAX_NUM_CID_CONTEXTS 10
718
719#define VM_REVIEW (1 << 0) /*!< After recording, permit the caller to review the recording before saving */
720#define VM_OPERATOR (1 << 1) /*!< Allow 0 to be pressed to go to 'o' extension */
721#define VM_SAYCID (1 << 2) /*!< Repeat the CallerID info during envelope playback */
722#define VM_SVMAIL (1 << 3) /*!< Allow the user to compose a new VM from within VoicemailMain */
723#define VM_ENVELOPE (1 << 4) /*!< Play the envelope information (who-from, time received, etc.) */
724#define VM_SAYDURATION (1 << 5) /*!< Play the length of the message during envelope playback */
725#define VM_SKIPAFTERCMD (1 << 6) /*!< After deletion, assume caller wants to go to the next message */
726#define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
727#define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
728#define VM_PBXSKIP (1 << 9) /*!< Skip the [PBX] preamble in the Subject line of emails */
729#define VM_DIRECTFORWARD (1 << 10) /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
730#define VM_ATTACH (1 << 11) /*!< Attach message to voicemail notifications? */
731#define VM_DELETE (1 << 12) /*!< Delete message after sending notification */
732#define VM_ALLOCED (1 << 13) /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
733#define VM_SEARCH (1 << 14) /*!< Search all contexts for a matching mailbox */
734#define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
735#define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
736#define VM_MESSAGEWRAP (1 << 17) /*!< Wrap around from the last message to the first, and vice-versa */
737#define VM_FWDURGAUTO (1 << 18) /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
738#define VM_EMAIL_EXT_RECS (1 << 19) /*!< Send voicemail emails when an external recording is added to a mailbox */
739#define VM_MARK_URGENT (1 << 20) /*!< After recording, permit the caller to mark the message as urgent */
740#define VM_ODBC_AUDIO_ON_DISK (1 << 21) /*!< When using ODBC, leave the message and greeting recordings on disk instead of moving them into the table */
741
742#define ERROR_LOCK_PATH -100
743#define ERROR_MAX_MSGS -101
744#define OPERATOR_EXIT 300
745
746#define MSGFILE_LEN (7) /*!< Length of the message file name: msgXXXX */
747
748enum vm_box {
756
758 OPT_SILENT = (1 << 0),
761 OPT_RECORDGAIN = (1 << 3),
763 OPT_AUTOPLAY = (1 << 6),
764 OPT_DTMFEXIT = (1 << 7),
768 OPT_BEEP = (1 << 11),
770 OPT_READONLY = (1 << 13),
771};
772
778 /* This *must* be the last value in this enum! */
780};
781
786};
787
802});
803
804static const char * const mailbox_folders[] = {
805#ifdef IMAP_STORAGE
806 imapfolder,
807#else
808 "INBOX",
809#endif
810 "Old",
811 "Work",
812 "Family",
813 "Friends",
814 "Cust1",
815 "Cust2",
816 "Cust3",
817 "Cust4",
818 "Cust5",
819 "Deleted",
820 "Urgent",
821};
822
823/*!
824 * \brief Reload voicemail.conf
825 * \param reload Whether this is a reload as opposed to module load
826 * \param force Forcefully reload the config, even it has not changed
827 * \retval 0 on success, nonzero on failure
828 */
829static int load_config_force(int reload, int force);
830
831/*! \brief Forcibly reload voicemail.conf, even if it has not changed.
832 * This is necessary after running unit tests. */
833#define force_reload_config() load_config_force(1, 1)
834
835static int load_config(int reload);
836#ifdef TEST_FRAMEWORK
837static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
838#endif
839static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
840
841/*! \page vmlang Voicemail Language Syntaxes Supported
842
843 \par Syntaxes supported, not really language codes.
844 \arg \b en - English
845 \arg \b de - German
846 \arg \b es - Spanish
847 \arg \b fr - French
848 \arg \b it - Italian
849 \arg \b nl - Dutch
850 \arg \b pt - Portuguese
851 \arg \b pt_BR - Portuguese (Brazil)
852 \arg \b gr - Greek
853 \arg \b no - Norwegian
854 \arg \b se - Swedish
855 \arg \b tw - Chinese (Taiwan)
856 \arg \b ua - Ukrainian
857
858German requires the following additional soundfile:
859\arg \b 1F einE (feminine)
860
861Spanish requires the following additional soundfile:
862\arg \b 1M un (masculine)
863
864Dutch, Portuguese & Spanish require the following additional soundfiles:
865\arg \b vm-INBOXs singular of 'new'
866\arg \b vm-Olds singular of 'old/heard/read'
867
868NB these are plural:
869\arg \b vm-INBOX nieuwe (nl)
870\arg \b vm-Old oude (nl)
871
872Polish uses:
873\arg \b vm-new-a 'new', feminine singular accusative
874\arg \b vm-new-e 'new', feminine plural accusative
875\arg \b vm-new-ych 'new', feminine plural genitive
876\arg \b vm-old-a 'old', feminine singular accusative
877\arg \b vm-old-e 'old', feminine plural accusative
878\arg \b vm-old-ych 'old', feminine plural genitive
879\arg \b digits/1-a 'one', not always same as 'digits/1'
880\arg \b digits/2-ie 'two', not always same as 'digits/2'
881
882Swedish uses:
883\arg \b vm-nytt singular of 'new'
884\arg \b vm-nya plural of 'new'
885\arg \b vm-gammalt singular of 'old'
886\arg \b vm-gamla plural of 'old'
887\arg \b digits/ett 'one', not always same as 'digits/1'
888
889Norwegian uses:
890\arg \b vm-ny singular of 'new'
891\arg \b vm-nye plural of 'new'
892\arg \b vm-gammel singular of 'old'
893\arg \b vm-gamle plural of 'old'
894
895Dutch also uses:
896\arg \b nl-om 'at'?
897
898Spanish also uses:
899\arg \b vm-youhaveno
900
901Italian requires the following additional soundfile:
902
903For vm_intro_it:
904\arg \b vm-nuovo new
905\arg \b vm-nuovi new plural
906\arg \b vm-vecchio old
907\arg \b vm-vecchi old plural
908
909Japanese requires the following additional soundfile:
910\arg \b jp-arimasu there is
911\arg \b jp-arimasen there is not
912\arg \b jp-oshitekudasai please press
913\arg \b jp-ni article ni
914\arg \b jp-ga article ga
915\arg \b jp-wa article wa
916\arg \b jp-wo article wo
917
918Chinese (Taiwan) requires the following additional soundfile:
919\arg \b vm-tong A class-word for call (tong1)
920\arg \b vm-ri A class-word for day (ri4)
921\arg \b vm-you You (ni3)
922\arg \b vm-haveno Have no (mei2 you3)
923\arg \b vm-have Have (you3)
924\arg \b vm-listen To listen (yao4 ting1)
925
926
927\note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
928spelled among others when you have to change folder. For the above reasons, vm-INBOX
929and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
930
931*/
932
933#define MAX_VM_MBOX_ID_LEN (AST_MAX_EXTENSION)
934#define MAX_VM_CONTEXT_LEN (AST_MAX_CONTEXT)
935/* MAX_VM_MAILBOX_LEN allows enough room for the '@' and NULL terminator */
936#define MAX_VM_MAILBOX_LEN (MAX_VM_MBOX_ID_LEN + MAX_VM_CONTEXT_LEN)
937
938/*! Structure for linked list of users
939 * Use ast_vm_user_destroy() to free one of these structures. */
940struct ast_vm_user {
941 char context[MAX_VM_CONTEXT_LEN];/*!< Voicemail context */
942 char mailbox[MAX_VM_MBOX_ID_LEN];/*!< Mailbox id, unique within vm context */
943 char password[80]; /*!< Secret pin code, numbers only */
944 char fullname[80]; /*!< Full name, for directory app */
945 char *email; /*!< E-mail address */
946 char *emailsubject; /*!< E-mail subject */
947 char *emailbody; /*!< E-mail body */
948 char pager[80]; /*!< E-mail address to pager (no attachment) */
949 char serveremail[80]; /*!< From: Mail address */
950 char fromstring[100]; /*!< From: Username */
951 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
952 char zonetag[80]; /*!< Time zone */
953 char locale[20]; /*!< The locale (for presentation of date/time) */
954 char callback[80];
955 char dialout[80];
956 char uniqueid[80]; /*!< Unique integer identifier */
957 char exit[80];
958 char attachfmt[20]; /*!< Attachment format */
959 unsigned int flags; /*!< VM_ flags */
960 int saydurationm;
961 int minsecs; /*!< Minimum number of seconds per message for this mailbox */
962 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
963 int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
964 int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
965 int passwordlocation; /*!< Storage location of the password */
966#ifdef IMAP_STORAGE
967 char imapserver[48]; /*!< IMAP server address */
968 char imapport[8]; /*!< IMAP server port */
969 char imapflags[128]; /*!< IMAP optional flags */
970 char imapuser[80]; /*!< IMAP server login */
971 char imappassword[80]; /*!< IMAP server password if authpassword not defined */
972 char imapfolder[64]; /*!< IMAP voicemail folder */
973 char imapvmshareid[80]; /*!< Shared mailbox ID to use rather than the dialed one */
974 int imapversion; /*!< If configuration changes, use the new values */
975#endif
976 double volgain; /*!< Volume gain for voicemails sent via email */
978};
979
980/*! Voicemail time zones */
981struct vm_zone {
983 char name[80];
984 char timezone[80];
985 char msg_format[512];
986};
987
988#define VMSTATE_MAX_MSG_ARRAY 256
989
990/*! Voicemail mailbox state */
991struct vm_state {
992 char curbox[80];
993 char username[80];
994 char context[80];
995 char curdir[PATH_MAX];
996 char vmbox[PATH_MAX];
997 char fn[PATH_MAX];
998 char intro[PATH_MAX];
999 int *deleted;
1000 int *heard;
1001 int dh_arraysize; /* used for deleted / heard allocation */
1002 int curmsg;
1003 int lastmsg;
1004 int newmessages;
1005 int oldmessages;
1006 int urgentmessages;
1007 int starting;
1008 int repeats;
1009#ifdef IMAP_STORAGE
1011 int updated; /*!< decremented on each mail check until 1 -allows delay */
1012 long *msgArray;
1013 unsigned msg_array_max;
1014 MAILSTREAM *mailstream;
1015 int vmArrayIndex;
1016 char imapuser[80]; /*!< IMAP server login */
1017 char imapfolder[64]; /*!< IMAP voicemail folder */
1018 char imapserver[48]; /*!< IMAP server address */
1019 char imapport[8]; /*!< IMAP server port */
1020 char imapflags[128]; /*!< IMAP optional flags */
1021 int imapversion;
1022 int interactive;
1023 char introfn[PATH_MAX]; /*!< Name of prepended file */
1024 unsigned int quota_limit;
1025 unsigned int quota_usage;
1026 struct vm_state *persist_vms;
1027#endif
1028};
1029
1030#ifdef ODBC_STORAGE
1031static char odbc_database[80] = "asterisk";
1032static char odbc_table[80] = "voicemessages";
1033size_t odbc_table_len = sizeof(odbc_table);
1034#define COUNT(a, b) odbc_count_messages(a,b)
1035#define LAST_MSG_INDEX(a) odbc_last_message_index(a)
1036#define RETRIEVE(a,b,c,d) odbc_retrieve_message(a,b)
1037#define DISPOSE(a,b) odbc_remove_files(a,b)
1038#define STORE(a,b,c,d,e,f,g,h,i,j,k) odbc_store_message(a,b,c,d)
1039#define EXISTS(a,b,c,d) (odbc_message_exists(a,b))
1040#define RENAME(a,b,c,d,e,f,g,h) (odbc_rename_message(a,b,c,d,e,f))
1041#define COPY(a,b,c,d,e,f,g,h) (odbc_copy_message(a,b,c,d,e,f))
1042#define DELETE(a,b,c,d) (odbc_delete_message(a,b))
1043#define UPDATE_MSG_ID(a, b, c, d, e, f) (odbc_update_msg_id((a), (b), (c)))
1044#else
1045#ifdef IMAP_STORAGE
1046#define DISPOSE(a,b) (imap_remove_file(a,b))
1047#define STORE(a,b,c,d,e,f,g,h,i,j,k) (imap_store_file(a,b,c,d,e,f,g,h,i,j,k))
1048#define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
1049#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
1050#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
1051#define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
1052#define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
1053#define UPDATE_MSG_ID(a, b, c, d, e, f) (vm_imap_update_msg_id((a), (b), (c), (d), (e), (f)))
1054#else
1055#define COUNT(a, b) count_messages(a,b)
1056#define LAST_MSG_INDEX(a) last_message_index(a)
1057#define RETRIEVE(a,b,c,d)
1058#define DISPOSE(a,b)
1059#define STORE(a,b,c,d,e,f,g,h,i,j,k)
1060#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
1061#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
1062#define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
1063#define DELETE(a,b,c,d) (vm_delete(c))
1064#define UPDATE_MSG_ID(a, b, c, d, e, f)
1065#endif
1066#endif
1067
1069
1070static char ext_pass_cmd[128];
1071static char ext_pass_check_cmd[128];
1072
1073static int my_umask;
1074
1075#define PWDCHANGE_INTERNAL (1 << 1)
1076#define PWDCHANGE_EXTERNAL (1 << 2)
1078
1079#ifdef ODBC_STORAGE
1080#define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
1081#else
1082# ifdef IMAP_STORAGE
1083# define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
1084# else
1085# define tdesc "Comedian Mail (Voicemail System)"
1086# endif
1087#endif
1088
1089static char userscontext[AST_MAX_EXTENSION] = "default";
1090
1091static char *addesc = "Comedian Mail";
1092
1093/* Leave a message */
1094static char *voicemail_app = "VoiceMail";
1095
1096/* Check mail, control, etc */
1097static char *voicemailmain_app = "VoiceMailMain";
1098
1099static char *vmauthenticate_app = "VMAuthenticate";
1100
1101static char *playmsg_app = "VoiceMailPlayMsg";
1102
1103static char *sayname_app = "VMSayName";
1104
1107static char zonetag[80];
1108static char locale[20];
1109static int maxsilence;
1110static int maxmsg = MAXMSG;
1111static int maxdeletedmsg;
1112static int silencethreshold = 128;
1114static char mailcmd[160] = SENDMAIL; /* Configurable mail cmd */
1115static char externnotify[160];
1117static char vmfmts[80] = "wav";
1118static double volgain;
1119static int vmminsecs;
1120static int vmmaxsecs;
1121static int maxgreet;
1122static int skipms = 3000;
1123static int maxlogins = 3;
1127
1128/*! Poll mailboxes for changes since there is something external to
1129 * app_voicemail that may change them. */
1130static unsigned int poll_mailboxes;
1131
1132/*! By default, poll every 30 seconds */
1133#define DEFAULT_POLL_FREQ 30
1134/*! Polling frequency */
1135static unsigned int poll_freq = DEFAULT_POLL_FREQ;
1136
1138static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
1140static unsigned char poll_thread_run;
1141
1143
1144struct alias_mailbox_mapping {
1145 char *alias;
1146 char *mailbox;
1147 char buf[0];
1148};
1149
1150struct mailbox_alias_mapping {
1151 char *alias;
1152 char *mailbox;
1153 char buf[0];
1154};
1155
1156#define MAPPING_BUCKETS 511
1160
1164
1165/* custom audio control prompts for voicemail playback */
1171
1172/* custom password sounds */
1173static char vm_login[80] = "vm-login";
1174static char vm_newuser[80] = "vm-newuser";
1175static char vm_password[80] = "vm-password";
1176static char vm_newpassword[80] = "vm-newpassword";
1177static char vm_passchanged[80] = "vm-passchanged";
1178static char vm_reenterpassword[80] = "vm-reenterpassword";
1179static char vm_mismatch[80] = "vm-mismatch";
1180static char vm_invalid_password[80] = "vm-invalid-password";
1181static char vm_pls_try_again[80] = "vm-pls-try-again";
1182
1183/*
1184 * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
1185 * 1. create a sound along the lines of "Please try again. When done, press the pound key" which could be spliced
1186 * from existing sound clips. This would require some programming changes in the area of vm_forward options and also
1187 * app.c's __ast_play_and_record function
1188 * 2. create a sound prompt saying "Please try again. When done recording, press any key to stop and send the prepended
1189 * message." At the time of this comment, I think this would require new voice work to be commissioned.
1190 * 3. Something way different like providing instructions before a time out or a post-recording menu. This would require
1191 * more effort than either of the other two.
1192 */
1193static char vm_prepend_timeout[80] = "vm-then-pound";
1194
1195static struct ast_flags globalflags = {0};
1196
1197static int saydurationminfo = 2;
1198
1202
1204
1205
1206static char *emailbody;
1207static char *emailsubject;
1208static char *pagerbody;
1209static char *pagersubject;
1210static char fromstring[100];
1211static char pagerfromstring[100];
1212static char charset[32] = "ISO-8859-1";
1213
1214static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
1215static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
1216static int adsiver = 1;
1217static char emaildateformat[32] = "%A, %B %d, %Y at %r";
1218static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
1219
1220/* Forward declarations - generic */
1221static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
1222static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu);
1223static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
1224static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
1225static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
1226 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
1227 signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro);
1228static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
1229static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
1230static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag);
1231static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag, const char *msg_id);
1232static void apply_options(struct ast_vm_user *vmu, const char *options);
1233static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum);
1234static int is_valid_dtmf(const char *key);
1235static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
1236static int write_password_to_file(const char *secretfn, const char *password);
1237static const char *substitute_escapes(const char *value);
1238static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids [], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu);
1239static void notify_new_state(struct ast_vm_user *vmu);
1240static int append_vmu_info_astman(struct mansession *s, struct ast_vm_user *vmu, const char* event_name, const char* actionid);
1241static int append_vmbox_info_astman(struct mansession *s, const struct message *m, struct ast_vm_user *vmu, const char* event_name, const char* actionid);
1242
1243
1244/*!
1245 * Place a message in the indicated folder
1246 *
1247 * \param vmu Voicemail user
1248 * \param vms Current voicemail state for the user
1249 * \param msg The message number to save
1250 * \param box The folder into which the message should be saved
1251 * \param[out] newmsg The new message number of the saved message
1252 * \param move Tells whether to copy or to move the message
1253 *
1254 * \note the "move" parameter is only honored for IMAP voicemail presently
1255 * \retval 0 Success
1256 * \retval other Failure
1257 */
1258static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move);
1259
1260static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_create(const char *mailbox, const char *context, const char *folder, int descending, enum ast_vm_snapshot_sort_val sort_val, int combine_INBOX_and_OLD);
1261static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot);
1262
1263static int vm_msg_forward(const char *from_mailbox, const char *from_context, const char *from_folder, const char *to_mailbox, const char *to_context, const char *to_folder, size_t num_msgs, const char *msg_ids[], int delete_old);
1264static int vm_msg_move(const char *mailbox, const char *context, size_t num_msgs, const char *oldfolder, const char *old_msg_ids[], const char *newfolder);
1265static int vm_msg_remove(const char *mailbox, const char *context, size_t num_msgs, const char *folder, const char *msgs[]);
1266static int vm_msg_play(struct ast_channel *chan, const char *mailbox, const char *context, const char *folder, const char *msg_num, ast_vm_msg_play_cb cb);
1267
1268#ifdef TEST_FRAMEWORK
1269static int vm_test_destroy_user(const char *context, const char *mailbox);
1270static int vm_test_create_user(const char *context, const char *mailbox);
1271#endif
1272
1273/*!
1274 * \internal
1275 * \brief Parse the given mailbox_id into mailbox and context.
1276 * \since 12.0.0
1277 *
1278 * \param mailbox_id The mailbox\@context string to separate.
1279 * \param mailbox Where the mailbox part will start.
1280 * \param context Where the context part will start. ("default" if not present)
1281 *
1282 * \retval 0 on success.
1283 * \retval -1 on error.
1284 */
1285static int separate_mailbox(char *mailbox_id, char **mailbox, char **context)
1286{
1287 if (ast_strlen_zero(mailbox_id) || !mailbox || !context) {
1288 return -1;
1289 }
1290 *context = mailbox_id;
1291 *mailbox = strsep(context, "@");
1292 if (ast_strlen_zero(*mailbox)) {
1293 return -1;
1294 }
1295 if (ast_strlen_zero(*context)) {
1296 *context = "default";
1297 }
1298 return 0;
1299}
1300
1302
1303struct inprocess {
1304 int count;
1305 char *context;
1306 char mailbox[0];
1307};
1308
1309static int inprocess_hash_fn(const void *obj, const int flags)
1310{
1311 const struct inprocess *i = obj;
1312 return atoi(i->mailbox);
1313}
1314
1315static int inprocess_cmp_fn(void *obj, void *arg, int flags)
1316{
1317 struct inprocess *i = obj, *j = arg;
1318 if (strcmp(i->mailbox, j->mailbox)) {
1319 return 0;
1320 }
1321 return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
1322}
1323
1324static int inprocess_count(const char *context, const char *mailbox, int delta)
1325{
1326 int context_len = strlen(context) + 1;
1327 int mailbox_len = strlen(mailbox) + 1;
1328 struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + context_len + mailbox_len);
1329 arg->context = arg->mailbox + mailbox_len;
1330 ast_copy_string(arg->mailbox, mailbox, mailbox_len); /* SAFE */
1331 ast_copy_string(arg->context, context, context_len); /* SAFE */
1333 if ((i = ao2_find(inprocess_container, arg, 0))) {
1334 int ret = ast_atomic_fetchadd_int(&i->count, delta);
1336 ao2_ref(i, -1);
1337 return ret;
1338 }
1339 if (delta < 0) {
1340 ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
1341 }
1342 if (!(i = ao2_alloc(sizeof(*i) + context_len + mailbox_len, NULL))) {
1344 return 0;
1345 }
1346 i->context = i->mailbox + mailbox_len;
1347 ast_copy_string(i->mailbox, mailbox, mailbox_len); /* SAFE */
1348 ast_copy_string(i->context, context, context_len); /* SAFE */
1349 i->count = delta;
1352 ao2_ref(i, -1);
1353 return 0;
1354}
1355
1356#if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
1357static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
1358#endif
1359
1360/*!
1361 * \brief Strips control and non 7-bit clean characters from input string.
1362 *
1363 * \note To map control and none 7-bit characters to a 7-bit clean characters
1364 * please use ast_str_encode_mine().
1365 */
1366static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
1367{
1368 char *bufptr = buf;
1369 for (; *input; input++) {
1370 if (*input < 32) {
1371 continue;
1372 }
1373 *bufptr++ = *input;
1374 if (bufptr == buf + buflen - 1) {
1375 break;
1376 }
1377 }
1378 *bufptr = '\0';
1379 return buf;
1380}
1381
1382/*
1383 * These macros are currently only used by the ODBC code but
1384 * should be used anyplace we're allocating a PATH_MAX
1385 * sized buffer for something which will be much smaller
1386 * in practice.
1387 */
1388#ifdef ODBC_STORAGE
1389/*!
1390 * \internal
1391 * \brief Get the length needed for a message path base.
1392 *
1393 * \param dir The absolute path to a directory
1394 *
1395 * \note The directory provided should include all components
1396 * down to the folder level. For example:
1397 * /var/spool/asterisk/voicemail/default/1234/INBOX
1398 *
1399 * \returns Number of bytes needed for the message path base.
1400 */
1401static size_t get_msg_path_len(const char *dir)
1402{
1403 if (ast_strlen_zero(dir)) {
1404 return 0;
1405 }
1406 /* dir + '/' + MSGFILE_LEN + '\0' */
1407 return strlen(dir) + 1 + MSGFILE_LEN + 1;
1408}
1409
1410/*
1411 * The longest sound file extension we currently
1412 * have is "siren14" which is 7 characters long
1413 * but we'll make this 12 to be extra safe.
1414 */
1415#define MAX_SOUND_EXTEN_LEN 12
1416/*!
1417 * \internal
1418 * \brief Get the length needed for a message sound or metadata file path.
1419 *
1420 * \param dir The absolute path to a directory
1421 *
1422 * \note The directory provided should include all components
1423 * down to the folder level. For example:
1424 * /var/spool/asterisk/voicemail/default/1234/INBOX
1425 *
1426 * \returns Number of bytes needed for the message sound or metadata file path.
1427 */
1428static size_t get_msg_path_ext_len(const char *dir)
1429{
1430 if (ast_strlen_zero(dir)) {
1431 return 0;
1432 }
1433 /* dir + '/' + MSGFILE_LEN + extlen(including '.') + '\0' */
1434 return strlen(dir) + 1 + MSGFILE_LEN + MAX_SOUND_EXTEN_LEN + 1;
1435}
1436
1437/*!
1438 * \internal
1439 * \brief Allocate a buffer on the stack containing a message file path base.
1440 *
1441 * \param dir The absolute path to a directory
1442 * \param msgnum The message number or -1 for a prompt file
1443 *
1444 * \note The returned buffer will automatically be freed when it
1445 * goes out of scope.
1446 *
1447 * If passed (/var/spool/asterisk/voicemail/default/1234/INBOX, 0)
1448 * the returned path would be:
1449 * /var/spool/asterisk/voicemail/default/1234/INBOX/msg0000
1450 *
1451 * If passed (/var/spool/asterisk/voicemail/default/1234/unavail, -1)
1452 * the returned path would be:
1453 * /var/spool/asterisk/voicemail/default/1234/unavail
1454 * (no change)
1455 *
1456 * \returns A pointer to the buffer containing the path.
1457 */
1458#define MAKE_FILE_PTRA(dir, msgnum) \
1459({ \
1460 size_t __len = get_msg_path_len(dir); \
1461 char *__var; \
1462 if (msgnum < 0) { \
1463 __var = ast_strdupa(dir); \
1464 } else { \
1465 __var = ast_alloca(__len); \
1466 snprintf(__var, __len, "%s/msg%04d", dir, msgnum); \
1467 } \
1468 __var; \
1469})
1470
1471/*!
1472 * \internal
1473 * \brief Allocate a buffer on the stack for a message sound or metadata file path.
1474 *
1475 * \param dir The absolute path to a directory
1476 * \param msgnum The message number or -1 for a prompt file
1477 *
1478 * \note The returned buffer will automatically be freed when it
1479 * goes out of scope.
1480 *
1481 * If passed (/var/spool/asterisk/voicemail/default/1234/INBOX, 0, "txt")
1482 * the returned path would be:
1483 * /var/spool/asterisk/voicemail/default/1234/INBOX/msg0000.txt
1484 *
1485 * If passed (/var/spool/asterisk/voicemail/default/1234/unavail, -1, "WAV")
1486 * the returned path would be:
1487 * /var/spool/asterisk/voicemail/default/1234/unavail.WAV
1488 *
1489 * The buffer returned has enough space that you can safely re-use it
1490 * for other sound file extensions. For instance:
1491 *
1492 * char *fn = MAKE_FILE_PTRA(dir, msgnum);
1493 * char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
1494 * char *fmt = "g722";
1495 * ...do domething with full_fn then re-use it...
1496 * sprintf(full_fn, "%s.%s", fn, fmt); * Safe since the same dir and msgnum were used *
1497 *
1498 * \returns A pointer to the allocated buffer
1499 */
1500#define MAKE_FILE_EXT_PTRA(dir, msgnum, ext) \
1501({ \
1502 size_t __len = get_msg_path_ext_len(dir); \
1503 char *__var = ast_alloca(__len); \
1504 if (msgnum < 0) { \
1505 snprintf(__var, __len, "%s.%s", dir, ext); \
1506 } else { \
1507 snprintf(__var, __len, "%s/msg%04d.%s", dir, msgnum, ext); \
1508 } \
1509 __var; \
1510})
1511#endif
1512
1513/*!
1514 * \brief Sets default voicemail system options to a voicemail user.
1515 *
1516 * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
1517 * - all the globalflags
1518 * - the saydurationminfo
1519 * - the callcontext
1520 * - the dialcontext
1521 * - the exitcontext
1522 * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
1523 * - volume gain.
1524 * - emailsubject, emailbody set to NULL
1525 */
1526static void populate_defaults(struct ast_vm_user *vmu)
1527{
1530 if (saydurationminfo) {
1532 }
1533 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
1534 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
1535 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
1536 ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
1537 ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
1538 if (vmminsecs) {
1539 vmu->minsecs = vmminsecs;
1540 }
1541 if (vmmaxsecs) {
1542 vmu->maxsecs = vmmaxsecs;
1543 }
1544 if (maxmsg) {
1545 vmu->maxmsg = maxmsg;
1546 }
1547 if (maxdeletedmsg) {
1549 }
1550 vmu->volgain = volgain;
1551 ast_free(vmu->email);
1552 vmu->email = NULL;
1553 ast_free(vmu->emailsubject);
1554 vmu->emailsubject = NULL;
1555 ast_free(vmu->emailbody);
1556 vmu->emailbody = NULL;
1557#ifdef IMAP_STORAGE
1558 ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
1559 ast_copy_string(vmu->imapserver, imapserver, sizeof(vmu->imapserver));
1560 ast_copy_string(vmu->imapport, imapport, sizeof(vmu->imapport));
1561 ast_copy_string(vmu->imapflags, imapflags, sizeof(vmu->imapflags));
1562#endif
1563}
1564
1565/*!
1566 * \brief Sets a specific property value.
1567 * \param vmu The voicemail user object to work with.
1568 * \param var The name of the property to be set.
1569 * \param value The value to be set to the property.
1570 *
1571 * The property name must be one of the understood properties. See the source for details.
1572 */
1573static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
1574{
1575 int x;
1576 if (!strcasecmp(var, "attach")) {
1578 } else if (!strcasecmp(var, "attachfmt")) {
1579 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
1580 } else if (!strcasecmp(var, "attachextrecs")) {
1582 } else if (!strcasecmp(var, "serveremail")) {
1583 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
1584 } else if (!strcasecmp(var, "fromstring")) {
1585 ast_copy_string(vmu->fromstring, value, sizeof(vmu->fromstring));
1586 } else if (!strcasecmp(var, "emailbody")) {
1587 ast_free(vmu->emailbody);
1589 } else if (!strcasecmp(var, "emailsubject")) {
1590 ast_free(vmu->emailsubject);
1592 } else if (!strcasecmp(var, "language")) {
1593 ast_copy_string(vmu->language, value, sizeof(vmu->language));
1594 } else if (!strcasecmp(var, "tz")) {
1595 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
1596 } else if (!strcasecmp(var, "locale")) {
1597 ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
1598#ifdef IMAP_STORAGE
1599 } else if (!strcasecmp(var, "imapuser")) {
1600 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
1601 vmu->imapversion = imapversion;
1602 } else if (!strcasecmp(var, "imapserver")) {
1603 ast_copy_string(vmu->imapserver, value, sizeof(vmu->imapserver));
1604 vmu->imapversion = imapversion;
1605 } else if (!strcasecmp(var, "imapport")) {
1606 ast_copy_string(vmu->imapport, value, sizeof(vmu->imapport));
1607 vmu->imapversion = imapversion;
1608 } else if (!strcasecmp(var, "imapflags")) {
1609 ast_copy_string(vmu->imapflags, value, sizeof(vmu->imapflags));
1610 vmu->imapversion = imapversion;
1611 } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
1612 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
1613 vmu->imapversion = imapversion;
1614 } else if (!strcasecmp(var, "imapfolder")) {
1615 ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
1616 vmu->imapversion = imapversion;
1617 } else if (!strcasecmp(var, "imapvmshareid")) {
1618 ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
1619 vmu->imapversion = imapversion;
1620#endif
1621 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
1623 } else if (!strcasecmp(var, "saycid")){
1625 } else if (!strcasecmp(var, "sendvoicemail")){
1627 } else if (!strcasecmp(var, "review")){
1629 } else if (!strcasecmp(var, "leaveurgent")){
1631 } else if (!strcasecmp(var, "tempgreetwarn")){
1633 } else if (!strcasecmp(var, "messagewrap")){
1635 } else if (!strcasecmp(var, "operator")) {
1637 } else if (!strcasecmp(var, "envelope")){
1639 } else if (!strcasecmp(var, "moveheard")){
1641 } else if (!strcasecmp(var, "sayduration")){
1643 } else if (!strcasecmp(var, "saydurationm")){
1644 if (sscanf(value, "%30d", &x) == 1) {
1645 vmu->saydurationm = x;
1646 } else {
1647 ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
1648 }
1649 } else if (!strcasecmp(var, "forcename")){
1651 } else if (!strcasecmp(var, "forcegreetings")){
1653 } else if (!strcasecmp(var, "callback")) {
1654 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
1655 } else if (!strcasecmp(var, "dialout")) {
1656 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
1657 } else if (!strcasecmp(var, "exitcontext")) {
1658 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
1659 } else if (!strcasecmp(var, "minsecs")) {
1660 if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
1661 vmu->minsecs = x;
1662 } else {
1663 ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
1664 vmu->minsecs = vmminsecs;
1665 }
1666 } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
1667 vmu->maxsecs = atoi(value);
1668 if (vmu->maxsecs <= 0) {
1669 ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
1670 vmu->maxsecs = vmmaxsecs;
1671 } else {
1672 vmu->maxsecs = atoi(value);
1673 }
1674 if (!strcasecmp(var, "maxmessage"))
1675 ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
1676 } else if (!strcasecmp(var, "maxmsg")) {
1677 vmu->maxmsg = atoi(value);
1678 /* Accept maxmsg=0 (Greetings only voicemail) */
1679 if (vmu->maxmsg < 0) {
1680 ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
1681 vmu->maxmsg = MAXMSG;
1682 } else if (vmu->maxmsg > MAXMSGLIMIT) {
1683 ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
1684 vmu->maxmsg = MAXMSGLIMIT;
1685 }
1686 } else if (!strcasecmp(var, "nextaftercmd")) {
1688 } else if (!strcasecmp(var, "backupdeleted")) {
1689 if (sscanf(value, "%30d", &x) == 1)
1690 vmu->maxdeletedmsg = x;
1691 else if (ast_true(value))
1692 vmu->maxdeletedmsg = MAXMSG;
1693 else
1694 vmu->maxdeletedmsg = 0;
1695
1696 if (vmu->maxdeletedmsg < 0) {
1697 ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
1698 vmu->maxdeletedmsg = MAXMSG;
1699 } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
1700 ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
1702 }
1703 } else if (!strcasecmp(var, "volgain")) {
1704 sscanf(value, "%30lf", &vmu->volgain);
1705 } else if (!strcasecmp(var, "passwordlocation")) {
1706 if (!strcasecmp(value, "spooldir")) {
1708 } else {
1710 }
1711 } else if (!strcasecmp(var, "options")) {
1712 apply_options(vmu, value);
1713 }
1714}
1715
1716static char *vm_check_password_shell(char *command, char *buf, size_t len)
1717{
1718 int fds[2], pid = 0;
1719
1720 memset(buf, 0, len);
1721
1722 if (pipe(fds)) {
1723 snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
1724 } else {
1725 /* good to go*/
1726 pid = ast_safe_fork(0);
1727
1728 if (pid < 0) {
1729 /* ok maybe not */
1730 close(fds[0]);
1731 close(fds[1]);
1732 snprintf(buf, len, "FAILURE: Fork failed");
1733 } else if (pid) {
1734 /* parent */
1735 close(fds[1]);
1736 if (read(fds[0], buf, len) < 0) {
1737 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
1738 }
1739 close(fds[0]);
1740 } else {
1741 /* child */
1743 AST_APP_ARG(v)[20];
1744 );
1745 char *mycmd = ast_strdupa(command);
1746
1747 close(fds[0]);
1748 dup2(fds[1], STDOUT_FILENO);
1749 close(fds[1]);
1750 ast_close_fds_above_n(STDOUT_FILENO);
1751
1752 AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
1753
1754 execv(arg.v[0], arg.v);
1755 printf("FAILURE: %s", strerror(errno));
1756 _exit(0);
1757 }
1758 }
1759 return buf;
1760}
1761
1762/*!
1763 * \brief Check that password meets minimum required length
1764 * \param vmu The voicemail user to change the password for.
1765 * \param password The password string to check
1766 *
1767 * \return zero on ok, 1 on not ok.
1768 */
1769static int check_password(struct ast_vm_user *vmu, char *password)
1770{
1771 /* check minimum length */
1772 if (strlen(password) < minpassword)
1773 return 1;
1774 /* check that password does not contain '*' character */
1775 if (!ast_strlen_zero(password) && password[0] == '*')
1776 return 1;
1778 char cmd[255], buf[255];
1779
1780 ast_debug(1, "Verify password policies for %s\n", password);
1781
1782 snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
1783 if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
1784 ast_debug(5, "Result: %s\n", buf);
1785 if (!strncasecmp(buf, "VALID", 5)) {
1786 ast_debug(3, "Passed password check: '%s'\n", buf);
1787 return 0;
1788 } else if (!strncasecmp(buf, "FAILURE", 7)) {
1789 ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
1790 return 0;
1791 } else {
1792 ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
1793 return 1;
1794 }
1795 }
1796 }
1797 return 0;
1798}
1799
1800/*!
1801 * \brief Performs a change of the voicemail passowrd in the realtime engine.
1802 * \param vmu The voicemail user to change the password for.
1803 * \param password The new value to be set to the password for this user.
1804 *
1805 * This only works if there is a realtime engine configured.
1806 * This is called from the (top level) vm_change_password.
1807 *
1808 * \return zero on success, -1 on error.
1809 */
1810static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
1811{
1812 int res = -1;
1813 if (!strcmp(vmu->password, password)) {
1814 /* No change (but an update would return 0 rows updated, so we opt out here) */
1815 return 0;
1816 }
1817
1818 if (strlen(password) > 10) {
1819 ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
1820 }
1821 if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
1822 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
1823 ast_copy_string(vmu->password, password, sizeof(vmu->password));
1824 res = 0;
1825 }
1826 return res;
1827}
1828
1829/*!
1830 * \brief Destructively Parse options and apply.
1831 */
1832static void apply_options(struct ast_vm_user *vmu, const char *options)
1833{
1834 char *stringp;
1835 char *s;
1836 char *var, *value;
1837 stringp = ast_strdupa(options);
1838 while ((s = strsep(&stringp, "|"))) {
1839 value = s;
1840 if ((var = strsep(&value, "=")) && value) {
1841 apply_option(vmu, var, value);
1842 }
1843 }
1844}
1845
1846/*!
1847 * \brief Loads the options specific to a voicemail user.
1848 *
1849 * This is called when a vm_user structure is being set up, such as from load_options.
1850 */
1851static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
1852{
1853 for (; var; var = var->next) {
1854 if (!strcasecmp(var->name, "vmsecret")) {
1855 ast_copy_string(retval->password, var->value, sizeof(retval->password));
1856 } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
1857 if (ast_strlen_zero(retval->password)) {
1858 if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
1859 ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
1860 "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
1861 } else {
1862 ast_copy_string(retval->password, var->value, sizeof(retval->password));
1863 }
1864 }
1865 } else if (!strcasecmp(var->name, "uniqueid")) {
1866 ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
1867 } else if (!strcasecmp(var->name, "pager")) {
1868 ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
1869 } else if (!strcasecmp(var->name, "email")) {
1870 ast_free(retval->email);
1871 retval->email = ast_strdup(var->value);
1872 } else if (!strcasecmp(var->name, "fullname")) {
1873 ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
1874 } else if (!strcasecmp(var->name, "context")) {
1875 ast_copy_string(retval->context, var->value, sizeof(retval->context));
1876 } else if (!strcasecmp(var->name, "emailsubject")) {
1877 ast_free(retval->emailsubject);
1878 retval->emailsubject = ast_strdup(substitute_escapes(var->value));
1879 } else if (!strcasecmp(var->name, "emailbody")) {
1880 ast_free(retval->emailbody);
1881 retval->emailbody = ast_strdup(substitute_escapes(var->value));
1882#ifdef IMAP_STORAGE
1883 } else if (!strcasecmp(var->name, "imapuser")) {
1884 ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
1885 retval->imapversion = imapversion;
1886 } else if (!strcasecmp(var->name, "imapserver")) {
1887 ast_copy_string(retval->imapserver, var->value, sizeof(retval->imapserver));
1888 retval->imapversion = imapversion;
1889 } else if (!strcasecmp(var->name, "imapport")) {
1890 ast_copy_string(retval->imapport, var->value, sizeof(retval->imapport));
1891 retval->imapversion = imapversion;
1892 } else if (!strcasecmp(var->name, "imapflags")) {
1893 ast_copy_string(retval->imapflags, var->value, sizeof(retval->imapflags));
1894 retval->imapversion = imapversion;
1895 } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
1896 ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
1897 retval->imapversion = imapversion;
1898 } else if (!strcasecmp(var->name, "imapfolder")) {
1899 ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
1900 retval->imapversion = imapversion;
1901 } else if (!strcasecmp(var->name, "imapvmshareid")) {
1902 ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
1903 retval->imapversion = imapversion;
1904#endif
1905 } else
1906 apply_option(retval, var->name, var->value);
1907 }
1908}
1909
1910/*!
1911 * \brief Determines if a DTMF key entered is valid.
1912 * \param key The character to be compared. expects a single character. Though is capable of handling a string, this is internally copies using ast_strdupa.
1913 *
1914 * Tests the character entered against the set of valid DTMF characters.
1915 * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1916 */
1917static int is_valid_dtmf(const char *key)
1918{
1919 int i;
1920 char *local_key = ast_strdupa(key);
1921
1922 for (i = 0; i < strlen(key); ++i) {
1923 if (!strchr(VALID_DTMF, *local_key)) {
1924 ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1925 return 0;
1926 }
1927 local_key++;
1928 }
1929 return 1;
1930}
1931
1932/*!
1933 * \brief Finds a voicemail user from the realtime engine.
1934 * \param ivm
1935 * \param context
1936 * \param mailbox
1937 *
1938 * This is called as a fall through case when the normal find_user() was not able to find a user. That is, the default it so look in the usual voicemail users file first.
1939 *
1940 * \return The ast_vm_user structure for the user that was found.
1941 */
1942static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1943{
1944 struct ast_variable *var;
1945 struct ast_vm_user *retval;
1946
1947 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1948 if (ivm) {
1949 memset(retval, 0, sizeof(*retval));
1950 }
1951 populate_defaults(retval);
1952 if (!ivm) {
1953 ast_set_flag(retval, VM_ALLOCED);
1954 }
1955 if (mailbox) {
1956 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1957 }
1959 var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1960 } else {
1961 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1962 }
1963 if (var) {
1964 apply_options_full(retval, var);
1966 } else {
1967 if (!ivm)
1968 ast_free(retval);
1969 retval = NULL;
1970 }
1971 }
1972 return retval;
1973}
1974
1975/*!
1976 * \brief Finds a voicemail user from the users file or the realtime engine.
1977 * \param ivm
1978 * \param context
1979 * \param mailbox
1980 *
1981 * \return The ast_vm_user structure for the user that was found.
1982 */
1983static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1984{
1985 /* This function could be made to generate one from a database, too */
1986 struct ast_vm_user *vmu = NULL, *cur;
1988
1990 context = "default";
1991
1992 AST_LIST_TRAVERSE(&users, cur, list) {
1993#ifdef IMAP_STORAGE
1994 if (cur->imapversion != imapversion) {
1995 continue;
1996 }
1997#endif
1998 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1999 break;
2000 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
2001 break;
2002 }
2003 if (cur) {
2004 /* Make a copy, so that on a reload, we have no race */
2005 if ((vmu = (ivm ? ivm : ast_calloc(1, sizeof(*vmu))))) {
2006 ast_free(vmu->email);
2007 ast_free(vmu->emailbody);
2008 ast_free(vmu->emailsubject);
2009 *vmu = *cur;
2010 vmu->email = ast_strdup(cur->email);
2011 vmu->emailbody = ast_strdup(cur->emailbody);
2012 vmu->emailsubject = ast_strdup(cur->emailsubject);
2013 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
2014 AST_LIST_NEXT(vmu, list) = NULL;
2015 }
2016 }
2018 if (!vmu) {
2019 vmu = find_user_realtime(ivm, context, mailbox);
2020 }
2021 if (!vmu && !ast_strlen_zero(aliasescontext)) {
2022 struct alias_mailbox_mapping *mapping;
2023 char *search_string = ast_alloca(MAX_VM_MAILBOX_LEN);
2024
2025 snprintf(search_string, MAX_VM_MAILBOX_LEN, "%s%s%s",
2026 mailbox,
2027 ast_strlen_zero(context) ? "" : "@",
2028 S_OR(context, ""));
2029
2030 mapping = ao2_find(alias_mailbox_mappings, search_string, OBJ_SEARCH_KEY);
2031 if (mapping) {
2032 char *search_mailbox = NULL;
2033 char *search_context = NULL;
2034
2035 separate_mailbox(ast_strdupa(mapping->mailbox), &search_mailbox, &search_context);
2036 ao2_ref(mapping, -1);
2037 vmu = find_user(ivm, search_mailbox, search_context);
2038 }
2039 }
2040
2041 return vmu;
2042}
2043
2044/*!
2045 * \brief Resets a user password to a specified password.
2046 * \param context
2047 * \param mailbox
2048 * \param newpass
2049 *
2050 * This does the actual change password work, called by the vm_change_password() function.
2051 *
2052 * \return zero on success, -1 on error.
2053 */
2054static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
2055{
2056 /* This function could be made to generate one from a database, too */
2057 struct ast_vm_user *cur;
2058 int res = -1;
2060 AST_LIST_TRAVERSE(&users, cur, list) {
2061 if ((!context || !strcasecmp(context, cur->context)) &&
2062 (!strcasecmp(mailbox, cur->mailbox)))
2063 break;
2064 }
2065 if (cur) {
2066 ast_copy_string(cur->password, newpass, sizeof(cur->password));
2067 res = 0;
2068 }
2070 if (!res) {
2071 struct ast_json *json_object;
2072
2073 json_object = ast_json_pack("{s: s, s: s, s: s}",
2074 "Context", S_OR(context, "default"),
2075 "Mailbox", mailbox,
2076 "NewPassword", newpass);
2077 ast_manager_publish_event("VoicemailPasswordChange", EVENT_FLAG_SYSTEM | EVENT_FLAG_USER, json_object);
2078 ast_json_unref(json_object);
2079 }
2080 return res;
2081}
2082
2083/*!
2084 * \brief Check if configuration file is valid
2085 */
2086static inline int valid_config(const struct ast_config *cfg)
2087{
2088 return cfg && cfg != CONFIG_STATUS_FILEINVALID;
2089}
2090
2091/*!
2092 * \brief The handler for the change password option.
2093 * \param vmu The voicemail user to work with.
2094 * \param newpassword The new password (that has been gathered from the appropriate prompting).
2095 * This is called when a new user logs in for the first time and the option to force them to change their password is set.
2096 * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
2097 */
2098static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
2099{
2100 struct ast_config *cfg = NULL;
2101 struct ast_variable *var = NULL;
2102 struct ast_category *cat = NULL;
2103 char *category = NULL;
2104 const char *tmp = NULL;
2105 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
2106 char secretfn[PATH_MAX] = "";
2107 int found = 0;
2108
2109 if (!change_password_realtime(vmu, newpassword))
2110 return;
2111
2112 /* check if we should store the secret in the spool directory next to the messages */
2113 switch (vmu->passwordlocation) {
2114 case OPT_PWLOC_SPOOLDIR:
2115 snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
2116 if (write_password_to_file(secretfn, newpassword) == 0) {
2117 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
2118 ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
2119 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2120 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2121 break;
2122 } else {
2123 ast_log(LOG_WARNING, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
2124 }
2125 /* Fall-through */
2127 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
2128 while ((category = ast_category_browse(cfg, category))) {
2129 if (!strcasecmp(category, vmu->context)) {
2130 char *value = NULL;
2131 char *new = NULL;
2132 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
2133 ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
2134 break;
2135 }
2136 value = strstr(tmp, ",");
2137 if (!value) {
2138 new = ast_malloc(strlen(newpassword) + 1);
2139 sprintf(new, "%s", newpassword);
2140 } else {
2141 new = ast_malloc((strlen(value) + strlen(newpassword) + 1));
2142 sprintf(new, "%s%s", newpassword, value);
2143 }
2144 if (!(cat = ast_category_get(cfg, category, NULL))) {
2145 ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
2146 ast_free(new);
2147 break;
2148 }
2149 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
2150 found = 1;
2151 ast_free(new);
2152 }
2153 }
2154 /* save the results */
2155 if (found) {
2156 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
2157 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2158 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2159 ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "app_voicemail");
2160 ast_config_destroy(cfg);
2161 break;
2162 }
2163
2164 ast_config_destroy(cfg);
2165 }
2166 /* Fall-through */
2168 /* check users.conf and update the password stored for the mailbox */
2169 /* if no vmsecret entry exists create one. */
2170 if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
2171 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
2172 for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
2173 ast_debug(4, "users.conf: %s\n", category);
2174 if (!strcasecmp(category, vmu->mailbox)) {
2175 char new[strlen(newpassword) + 1];
2176 if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
2177 ast_debug(3, "looks like we need to make vmsecret!\n");
2178 var = ast_variable_new("vmsecret", newpassword, "");
2179 } else {
2180 var = NULL;
2181 }
2182
2183 sprintf(new, "%s", newpassword);
2184 if (!(cat = ast_category_get(cfg, category, NULL))) {
2185 ast_debug(4, "failed to get category!\n");
2186 ast_free(var);
2187 break;
2188 }
2189 if (!var) {
2190 ast_variable_update(cat, "vmsecret", new, NULL, 0);
2191 } else {
2193 }
2194 found = 1;
2195 break;
2196 }
2197 }
2198 /* save the results and clean things up */
2199 if (found) {
2200 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
2201 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2202 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2203 ast_config_text_file_save("users.conf", cfg, "app_voicemail");
2204 }
2205
2206 ast_config_destroy(cfg);
2207 }
2208 }
2209}
2210
2211static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
2212{
2213 char buf[255];
2214 snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
2215 ast_debug(1, "External password: %s\n",buf);
2216 if (!ast_safe_system(buf)) {
2217 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
2218 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2219 /* Reset the password in memory, too */
2220 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2221 }
2222}
2223
2224/*!
2225 * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
2226 * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
2227 * \param len The length of the path string that was written out.
2228 * \param context
2229 * \param ext
2230 * \param folder
2231 *
2232 * The path is constructed as
2233 * VM_SPOOL_DIRcontext/ext/folder
2234 *
2235 * \return zero on success, -1 on error.
2236 */
2237static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
2238{
2239 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
2240}
2241
2242/*!
2243 * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
2244 * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
2245 * \param len The length of the path string that was written out.
2246 * \param dir
2247 * \param num
2248 *
2249 * The path is constructed as
2250 * VM_SPOOL_DIRcontext/ext/folder
2251 *
2252 * \return zero on success, -1 on error.
2253 */
2254static int make_file(char *dest, const int len, const char *dir, const int num)
2255{
2256 return snprintf(dest, len, "%s/msg%04d", dir, num);
2257}
2258
2259/*! \brief basically mkdir -p $dest/$context/$ext/$folder
2260 * \param dest String. base directory.
2261 * \param len Length of dest.
2262 * \param context String. Ignored if is null or empty string.
2263 * \param ext String. Ignored if is null or empty string.
2264 * \param folder String. Ignored if is null or empty string.
2265 * \return -1 on failure, 0 on success.
2266 */
2267static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
2268{
2269 mode_t mode = VOICEMAIL_DIR_MODE;
2270 int res;
2271
2272 make_dir(dest, len, context, ext, folder);
2273 if ((res = ast_mkdir(dest, mode))) {
2274 ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
2275 return -1;
2276 }
2277 return 0;
2278}
2279
2280static const char *mbox(struct ast_vm_user *vmu, int id)
2281{
2282#ifdef IMAP_STORAGE
2283 if (vmu && id == 0) {
2284 return vmu->imapfolder;
2285 }
2286#endif
2287 return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
2288}
2289
2290static const char *vm_index_to_foldername(int id)
2291{
2292 return mbox(NULL, id);
2293}
2294
2295
2296static int get_folder_by_name(const char *name)
2297{
2298 size_t i;
2299
2300 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
2301 if (strcasecmp(name, mailbox_folders[i]) == 0) {
2302 return i;
2303 }
2304 }
2305
2306 return -1;
2307}
2308
2309static void free_user(struct ast_vm_user *vmu)
2310{
2311 if (!vmu) {
2312 return;
2313 }
2314
2315 ast_free(vmu->email);
2316 vmu->email = NULL;
2317 ast_free(vmu->emailbody);
2318 vmu->emailbody = NULL;
2319 ast_free(vmu->emailsubject);
2320 vmu->emailsubject = NULL;
2321
2322 if (ast_test_flag(vmu, VM_ALLOCED)) {
2323 ast_free(vmu);
2324 }
2325}
2326
2327static void free_user_final(struct ast_vm_user *vmu)
2328{
2329 if (!vmu) {
2330 return;
2331 }
2332
2333 if (!ast_strlen_zero(vmu->mailbox)) {
2335 }
2336
2337 free_user(vmu);
2338}
2339
2340static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
2341
2342 int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
2343
2344 /* remove old allocation */
2345 if (vms->deleted) {
2346 ast_free(vms->deleted);
2347 vms->deleted = NULL;
2348 }
2349 if (vms->heard) {
2350 ast_free(vms->heard);
2351 vms->heard = NULL;
2352 }
2353 vms->dh_arraysize = 0;
2354
2355 if (arraysize > 0) {
2356 if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
2357 return -1;
2358 }
2359 if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
2360 ast_free(vms->deleted);
2361 vms->deleted = NULL;
2362 return -1;
2363 }
2364 vms->dh_arraysize = arraysize;
2365 }
2366
2367 return 0;
2368}
2369
2370/* All IMAP-specific functions should go in this block. This
2371 * keeps them from being spread out all over the code */
2372#ifdef IMAP_STORAGE
2373static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
2374{
2375 char arg[10];
2376 struct vm_state *vms;
2377 unsigned long messageNum;
2378
2379 /* If greetings aren't stored in IMAP, just delete the file */
2380 if (msgnum < 0 && !imapgreetings) {
2382 return;
2383 }
2384
2385 if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2386 ast_log(LOG_WARNING, "Couldn't find a vm_state for mailbox %s. Unable to set \\DELETED flag for message %d\n", vmu->mailbox, msgnum);
2387 return;
2388 }
2389
2390 if (msgnum < 0) {
2391 imap_delete_old_greeting(file, vms);
2392 return;
2393 }
2394
2395 /* find real message number based on msgnum */
2396 /* this may be an index into vms->msgArray based on the msgnum. */
2397 messageNum = vms->msgArray[msgnum];
2398 if (messageNum == 0) {
2399 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
2400 return;
2401 }
2402 ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
2403 /* delete message */
2404 snprintf (arg, sizeof(arg), "%lu", messageNum);
2405 ast_mutex_lock(&vms->lock);
2406 mail_setflag (vms->mailstream, arg, "\\DELETED");
2407 mail_expunge(vms->mailstream);
2408 ast_mutex_unlock(&vms->lock);
2409}
2410
2411static void vm_imap_update_msg_id(char *dir, int msgnum, const char *msg_id, struct ast_vm_user *vmu, struct ast_config *msg_cfg, int folder)
2412{
2413 struct ast_channel *chan;
2414 char *cid;
2415 char *cid_name;
2416 char *cid_num;
2417 struct vm_state *vms;
2418 const char *duration_str;
2419 int duration = 0;
2420
2421 /*
2422 * First, get things initially set up. If any of this fails, then
2423 * back out before doing anything substantial
2424 */
2425 vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0);
2426 if (!vms) {
2427 return;
2428 }
2429
2430 if (open_mailbox(vms, vmu, folder)) {
2431 return;
2432 }
2433
2434 chan = ast_dummy_channel_alloc();
2435 if (!chan) {
2436 close_mailbox(vms, vmu);
2437 return;
2438 }
2439
2440 /*
2441 * We need to make sure the new message we save has the same
2442 * callerid, flag, and duration as the original message
2443 */
2444 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
2445
2446 if (!ast_strlen_zero(cid)) {
2447 ast_callerid_parse(cid, &cid_name, &cid_num);
2449 if (!ast_strlen_zero(cid_name)) {
2450 ast_channel_caller(chan)->id.name.valid = 1;
2451 ast_channel_caller(chan)->id.name.str = ast_strdup(cid_name);
2452 }
2453 if (!ast_strlen_zero(cid_num)) {
2454 ast_channel_caller(chan)->id.number.valid = 1;
2455 ast_channel_caller(chan)->id.number.str = ast_strdup(cid_num);
2456 }
2457 }
2458
2459 duration_str = ast_variable_retrieve(msg_cfg, "message", "duration");
2460
2461 if (!ast_strlen_zero(duration_str)) {
2462 sscanf(duration_str, "%30d", &duration);
2463 }
2464
2465 /*
2466 * IMAP messages cannot be altered once delivered. So we have to delete the
2467 * current message and then re-add it with the updated message ID.
2468 *
2469 * Furthermore, there currently is no atomic way to create a new message and to
2470 * store it in an arbitrary folder. So we have to save it to the INBOX and then
2471 * move to the appropriate folder.
2472 */
2473 if (!imap_store_file(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, vmfmts,
2474 duration, vms, ast_variable_retrieve(msg_cfg, "message", "flag"), msg_id)) {
2475 if (folder != NEW_FOLDER) {
2476 save_to_folder(vmu, vms, msgnum, folder, NULL, 1);
2477 }
2478 vm_imap_delete(dir, msgnum, vmu);
2479 }
2480 close_mailbox(vms, vmu);
2481 ast_channel_unref(chan);
2482}
2483
2484static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
2485{
2486 struct vm_state *vms_p;
2487 char *file, *filename;
2488 char dest[PATH_MAX];
2489 int i;
2490 BODY *body;
2491 int ret = 0;
2492 int curr_mbox;
2493
2494 /* This function is only used for retrieval of IMAP greetings
2495 * regular messages are not retrieved this way, nor are greetings
2496 * if they are stored locally*/
2497 if (msgnum > -1 || !imapgreetings) {
2498 return 0;
2499 } else {
2500 file = strrchr(ast_strdupa(dir), '/');
2501 if (file)
2502 *file++ = '\0';
2503 else {
2504 ast_debug(1, "Failed to procure file name from directory passed.\n");
2505 return -1;
2506 }
2507 }
2508
2509 /* check if someone is accessing this box right now... */
2510 if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) &&
2511 !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2512 /* Unlike when retrieving a message, it is reasonable not to be able to find a
2513 * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
2514 * that's all we need to do.
2515 */
2516 if (!(vms_p = create_vm_state_from_user(vmu))) {
2517 ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
2518 return -1;
2519 }
2520 }
2521
2522 /* Greetings will never have a prepended message */
2523 *vms_p->introfn = '\0';
2524
2525 ast_mutex_lock(&vms_p->lock);
2526
2527 /* get the current mailbox so that we can point the mailstream back to it later */
2528 curr_mbox = get_folder_by_name(vms_p->curbox);
2529
2530 if (init_mailstream(vms_p, GREETINGS_FOLDER) || !vms_p->mailstream) {
2531 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2532 ast_mutex_unlock(&vms_p->lock);
2533 return -1;
2534 }
2535
2536 /*XXX Yuck, this could probably be done a lot better */
2537 for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
2538 mail_fetchstructure(vms_p->mailstream, i + 1, &body);
2539 /* We have the body, now we extract the file name of the first attachment. */
2540 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2541 char *attachment = body->nested.part->next->body.parameter->value;
2542 char copy[strlen(attachment) + 1];
2543
2544 strcpy(copy, attachment); /* safe */
2545 attachment = copy;
2546
2547 filename = strsep(&attachment, ".");
2548 if (!strcmp(filename, file)) {
2549 ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
2550 vms_p->msgArray[vms_p->curmsg] = i + 1;
2551 create_dirpath(dest, sizeof(dest), vmu->context, vms_p->username, "");
2552 save_body(body, vms_p, "2", attachment, 0);
2553 ret = 0;
2554 break;
2555 }
2556 } else {
2557 ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
2558 ret = -1;
2559 break;
2560 }
2561 }
2562
2563 if (curr_mbox != -1) {
2564 /* restore previous mbox stream */
2565 if (init_mailstream(vms_p, curr_mbox) || !vms_p->mailstream) {
2566 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2567 ret = -1;
2568 }
2569 }
2570 ast_mutex_unlock(&vms_p->lock);
2571 return ret;
2572}
2573
2574static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
2575{
2576 BODY *body;
2577 char *header_content;
2578 char *attachedfilefmt;
2579 char buf[80];
2580 struct vm_state *vms;
2581 char text_file[PATH_MAX];
2582 FILE *text_file_ptr;
2583 int res = 0;
2584 struct ast_vm_user *vmu;
2585 int curr_mbox;
2586
2587 if (!(vmu = find_user(NULL, context, mailbox))) {
2588 ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
2589 return -1;
2590 }
2591
2592 if (msgnum < 0) {
2593 if (imapgreetings) {
2594 res = imap_retrieve_greeting(dir, msgnum, vmu);
2595 goto exit;
2596 } else {
2597 res = 0;
2598 goto exit;
2599 }
2600 }
2601
2602 /* Before anything can happen, we need a vm_state so that we can
2603 * actually access the imap server through the vms->mailstream
2604 */
2605 if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2606 /* This should not happen. If it does, then I guess we'd
2607 * need to create the vm_state, extract which mailbox to
2608 * open, and then set up the msgArray so that the correct
2609 * IMAP message could be accessed. If I have seen correctly
2610 * though, the vms should be obtainable from the vmstates list
2611 * and should have its msgArray properly set up.
2612 */
2613 ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
2614 res = -1;
2615 goto exit;
2616 }
2617
2618 /* Ensure we have the correct mailbox open and have a valid mailstream for it */
2619 curr_mbox = get_folder_by_name(vms->curbox);
2620 if (curr_mbox < 0) {
2621 ast_debug(3, "Mailbox folder curbox not set, defaulting to Inbox\n");
2622 curr_mbox = 0;
2623 }
2624 init_mailstream(vms, curr_mbox);
2625 if (!vms->mailstream) {
2626 ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmu->mailbox);
2627 res = -1;
2628 goto exit;
2629 }
2630
2631 make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
2632 snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
2633
2634 /* Don't try to retrieve a message from IMAP if it already is on the file system */
2635 if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
2636 res = 0;
2637 goto exit;
2638 }
2639
2640 ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
2641 if (vms->msgArray[msgnum] == 0) {
2642 ast_log(LOG_WARNING, "Trying to access unknown message\n");
2643 res = -1;
2644 goto exit;
2645 }
2646
2647 /* This will only work for new messages... */
2648 ast_mutex_lock(&vms->lock);
2649 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
2650 ast_mutex_unlock(&vms->lock);
2651 /* empty string means no valid header */
2652 if (ast_strlen_zero(header_content)) {
2653 ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
2654 res = -1;
2655 goto exit;
2656 }
2657
2658 ast_mutex_lock(&vms->lock);
2659 mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
2660 ast_mutex_unlock(&vms->lock);
2661
2662 /* We have the body, now we extract the file name of the first attachment. */
2663 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2664 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
2665 } else {
2666 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
2667 res = -1;
2668 goto exit;
2669 }
2670
2671 /* Find the format of the attached file */
2672
2673 strsep(&attachedfilefmt, ".");
2674 if (!attachedfilefmt) {
2675 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
2676 res = -1;
2677 goto exit;
2678 }
2679
2680 save_body(body, vms, "2", attachedfilefmt, 0);
2681 if (save_body(body, vms, "3", attachedfilefmt, 1)) {
2682 *vms->introfn = '\0';
2683 }
2684
2685 /* Get info from headers!! */
2686 snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
2687
2688 if (!(text_file_ptr = fopen(text_file, "w"))) {
2689 ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
2690 goto exit;
2691 }
2692
2693 fprintf(text_file_ptr, "%s\n", "[message]");
2694
2695 if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))) {
2696 fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
2697 }
2698 if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))) {
2699 fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
2700 }
2701 if (get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))) {
2702 fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
2703 }
2704 if (get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))) {
2705 fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
2706 }
2707 if (get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))) {
2708 fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
2709 }
2710 if (get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))) {
2711 fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
2712 }
2713 if (get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf))) {
2714 fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
2715 }
2716 if (get_header_by_tag(header_content, "X-Asterisk-VM-Message-ID:", buf, sizeof(buf))) {
2717 fprintf(text_file_ptr, "msg_id=%s\n", S_OR(buf, ""));
2718 }
2719 fclose(text_file_ptr);
2720
2721exit:
2722 free_user(vmu);
2723 return res;
2724}
2725
2726static int folder_int(const char *folder)
2727{
2728 /*assume a NULL folder means INBOX*/
2729 if (!folder) {
2730 return 0;
2731 }
2732 if (!strcasecmp(folder, imapfolder)) {
2733 return 0;
2734 } else if (!strcasecmp(folder, "Old")) {
2735 return 1;
2736 } else if (!strcasecmp(folder, "Work")) {
2737 return 2;
2738 } else if (!strcasecmp(folder, "Family")) {
2739 return 3;
2740 } else if (!strcasecmp(folder, "Friends")) {
2741 return 4;
2742 } else if (!strcasecmp(folder, "Cust1")) {
2743 return 5;
2744 } else if (!strcasecmp(folder, "Cust2")) {
2745 return 6;
2746 } else if (!strcasecmp(folder, "Cust3")) {
2747 return 7;
2748 } else if (!strcasecmp(folder, "Cust4")) {
2749 return 8;
2750 } else if (!strcasecmp(folder, "Cust5")) {
2751 return 9;
2752 } else if (!strcasecmp(folder, "Urgent")) {
2753 return 11;
2754 } else { /*assume they meant INBOX if folder is not found otherwise*/
2755 return 0;
2756 }
2757}
2758
2759static int __messagecount(const char *context, const char *mailbox, const char *folder)
2760{
2761 SEARCHPGM *pgm;
2762 SEARCHHEADER *hdr;
2763
2764 struct ast_vm_user *vmu, vmus;
2765 struct vm_state *vms_p;
2766 int ret = 0;
2767 int fold = folder_int(folder);
2768 int urgent = 0;
2769
2770 /* If URGENT, then look at INBOX */
2771 if (fold == 11) {
2772 fold = NEW_FOLDER;
2773 urgent = 1;
2774 }
2775
2777 return 0;
2778
2779 /* We have to get the user before we can open the stream! */
2780 memset(&vmus, 0, sizeof(vmus));
2781 vmu = find_user(&vmus, context, mailbox);
2782 if (!vmu) {
2783 ast_log(AST_LOG_WARNING, "Couldn't find mailbox %s in context %s\n", mailbox, context);
2784 free_user(vmu);
2785 return -1;
2786 } else {
2787 /* No IMAP account available */
2788 if (vmu->imapuser[0] == '\0') {
2789 ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2790 free_user(vmu);
2791 return -1;
2792 }
2793 }
2794
2795 /* No IMAP account available */
2796 if (vmu->imapuser[0] == '\0') {
2797 ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2798 free_user(vmu);
2799 return -1;
2800 }
2801
2802 /* check if someone is accessing this box right now... */
2803 vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
2804 if (!vms_p) {
2805 vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
2806 }
2807 if (vms_p) {
2808 ast_debug(3, "Returning before search - user is logged in\n");
2809 if (fold == 0) { /* INBOX */
2810 free_user(vmu);
2811 return urgent ? vms_p->urgentmessages : vms_p->newmessages;
2812 }
2813 if (fold == 1) { /* Old messages */
2814 free_user(vmu);
2815 return vms_p->oldmessages;
2816 }
2817 }
2818
2819 /* add one if not there... */
2820 vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
2821 if (!vms_p) {
2822 vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
2823 }
2824
2825 if (!vms_p) {
2826 vms_p = create_vm_state_from_user(vmu);
2827 }
2828 ret = init_mailstream(vms_p, fold);
2829 if (!vms_p->mailstream) {
2830 ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
2831 free_user(vmu);
2832 return -1;
2833 }
2834 if (ret == 0) {
2835 ast_mutex_lock(&vms_p->lock);
2836 pgm = mail_newsearchpgm ();
2837 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
2838 hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
2839 pgm->header = hdr;
2840 if (fold != OLD_FOLDER) {
2841 pgm->unseen = 1;
2842 pgm->seen = 0;
2843 }
2844 /* In the special case where fold is 1 (old messages) we have to do things a bit
2845 * differently. Old messages are stored in the INBOX but are marked as "seen"
2846 */
2847 else {
2848 pgm->unseen = 0;
2849 pgm->seen = 1;
2850 }
2851 /* look for urgent messages */
2852 if (fold == NEW_FOLDER) {
2853 if (urgent) {
2854 pgm->flagged = 1;
2855 pgm->unflagged = 0;
2856 } else {
2857 pgm->flagged = 0;
2858 pgm->unflagged = 1;
2859 }
2860 }
2861 pgm->undeleted = 1;
2862 pgm->deleted = 0;
2863
2864 vms_p->vmArrayIndex = 0;
2865 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2866 if (fold == 0 && urgent == 0)
2867 vms_p->newmessages = vms_p->vmArrayIndex;
2868 if (fold == 1)
2869 vms_p->oldmessages = vms_p->vmArrayIndex;
2870 if (fold == 0 && urgent == 1)
2871 vms_p->urgentmessages = vms_p->vmArrayIndex;
2872 /*Freeing the searchpgm also frees the searchhdr*/
2873 mail_free_searchpgm(&pgm);
2874 ast_mutex_unlock(&vms_p->lock);
2875 free_user(vmu);
2876 vms_p->updated = 0;
2877 return vms_p->vmArrayIndex;
2878 } else {
2879 ast_mutex_lock(&vms_p->lock);
2880 mail_ping(vms_p->mailstream);
2881 ast_mutex_unlock(&vms_p->lock);
2882 }
2883 free_user(vmu);
2884 return 0;
2885}
2886
2887static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
2888{
2889 /* Check if mailbox is full */
2890 check_quota(vms, vmu->imapfolder);
2891 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
2892 ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
2893 if (chan) {
2894 ast_play_and_wait(chan, "vm-mailboxfull");
2895 }
2896 return -1;
2897 }
2898
2899 /* Check if we have exceeded maxmsg */
2900 ast_debug(3, "Checking message number quota: mailbox has %d messages, maximum is set to %d, current messages %d\n", msgnum, vmu->maxmsg, inprocess_count(vmu->mailbox, vmu->context, 0));
2901 if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
2902 ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
2903 if (chan) {
2904 ast_play_and_wait(chan, "vm-mailboxfull");
2905 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2906 }
2907 return -1;
2908 }
2909
2910 return 0;
2911}
2912
2913/*!
2914 * \brief Gets the number of messages that exist in a mailbox folder.
2915 * \param mailbox_id
2916 * \param folder
2917 *
2918 * This method is used when IMAP backend is used.
2919 * \return The number of messages in this mailbox folder (zero or more).
2920 */
2921static int messagecount(const char *mailbox_id, const char *folder)
2922{
2923 char *context;
2924 char *mailbox;
2925 int count;
2926
2927 if (ast_strlen_zero(mailbox_id)
2928 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
2929 return 0;
2930 }
2931
2932 if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
2933 count = __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
2934 } else {
2935 count = __messagecount(context, mailbox, folder);
2936 }
2937 return count < 0 ? 0 : count;
2938}
2939
2940static int imap_store_file(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms, const char *flag, const char *msg_id)
2941{
2942 char *myserveremail = serveremail;
2943 char fn[PATH_MAX];
2944 char introfn[PATH_MAX];
2945 char mailbox[256];
2946 char *stringp;
2947 FILE *p = NULL;
2948 char tmp[80] = "/tmp/astmail-XXXXXX";
2949 long len;
2950 void *buf;
2951 int tempcopy = 0;
2952 STRING str;
2953 int ret; /* for better error checking */
2954 char *imap_flags = NIL;
2955 int msgcount;
2956 int box = NEW_FOLDER;
2957
2958 snprintf(mailbox, sizeof(mailbox), "%s@%s", vmu->mailbox, vmu->context);
2959 msgcount = messagecount(mailbox, "INBOX") + messagecount(mailbox, "Old");
2960
2961 /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
2962 if (msgnum < 0) {
2963 if(!imapgreetings) {
2964 return 0;
2965 } else {
2966 box = GREETINGS_FOLDER;
2967 }
2968 }
2969
2970 if (imap_check_limits(chan, vms, vmu, msgcount)) {
2971 return -1;
2972 }
2973
2974 /* Set urgent flag for IMAP message */
2975 if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
2976 ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
2977 imap_flags = "\\FLAGGED";
2978 }
2979
2980 /* Attach only the first format */
2981 fmt = ast_strdupa(fmt);
2982 stringp = fmt;
2983 strsep(&stringp, "|");
2984
2985 if (!ast_strlen_zero(vmu->serveremail))
2986 myserveremail = vmu->serveremail;
2987
2988 if (msgnum > -1)
2989 make_file(fn, sizeof(fn), dir, msgnum);
2990 else
2991 ast_copy_string (fn, dir, sizeof(fn));
2992
2993 snprintf(introfn, sizeof(introfn), "%sintro", fn);
2994 if (ast_fileexists(introfn, NULL, NULL) <= 0) {
2995 *introfn = '\0';
2996 }
2997
2998 if (ast_strlen_zero(vmu->email)) {
2999 /* We need the vmu->email to be set when we call make_email_file, but
3000 * if we keep it set, a duplicate e-mail will be created. So at the end
3001 * of this function, we will revert back to an empty string if tempcopy
3002 * is 1.
3003 */
3004 vmu->email = ast_strdup(vmu->imapuser);
3005 tempcopy = 1;
3006 }
3007
3008 if (!strcmp(fmt, "wav49"))
3009 fmt = "WAV";
3010 ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
3011
3012 /* Make a temporary file instead of piping directly to sendmail, in case the mail
3013 command hangs. */
3014 if (!(p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask))) {
3015 ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
3016 if (tempcopy) {
3017 ast_free(vmu->email);
3018 vmu->email = NULL;
3019 }
3020 return -1;
3021 }
3022
3023 if (msgnum < 0 && imapgreetings) {
3024 if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
3025 ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
3026 return -1;
3027 }
3028 imap_delete_old_greeting(fn, vms);
3029 }
3030
3031 make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
3032 chan ? S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL) : NULL,
3033 chan ? S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL) : NULL,
3034 fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
3035 /* read mail file to memory */
3036 len = ftell(p);
3037 rewind(p);
3038 if (!(buf = ast_malloc(len + 1))) {
3039 ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
3040 fclose(p);
3041 if (tempcopy)
3042 *(vmu->email) = '\0';
3043 return -1;
3044 }
3045 if (fread(buf, 1, len, p) != len) {
3046 if (ferror(p)) {
3047 ast_log(LOG_ERROR, "Error while reading mail file: %s\n", tmp);
3048 return -1;
3049 }
3050 }
3051 ((char *) buf)[len] = '\0';
3052 INIT(&str, mail_string, buf, len);
3053 ret = init_mailstream(vms, box);
3054 if (ret == 0) {
3055 imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
3056 ast_mutex_lock(&vms->lock);
3057 if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
3058 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
3059 ast_mutex_unlock(&vms->lock);
3060 fclose(p);
3061 unlink(tmp);
3062 ast_free(buf);
3063 } else {
3064 ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
3065 fclose(p);
3066 unlink(tmp);
3067 ast_free(buf);
3068 return -1;
3069 }
3070 ast_debug(3, "%s stored\n", fn);
3071
3072 if (tempcopy)
3073 *(vmu->email) = '\0';
3074 inprocess_count(vmu->mailbox, vmu->context, -1);
3075 return 0;
3076
3077}
3078
3079/*!
3080 * \brief Gets the number of messages that exist in the inbox folder.
3081 * \param mailbox_context
3082 * \param newmsgs The variable that is updated with the count of new messages within this inbox.
3083 * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
3084 * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
3085 *
3086 * This method is used when IMAP backend is used.
3087 * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
3088 *
3089 * \return zero on success, -1 on error.
3090 */
3091
3092static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
3093{
3094 char tmp[PATH_MAX] = "";
3095 char *mailboxnc;
3096 char *context;
3097 char *mb;
3098 char *cur;
3099 int count = 0;
3100 if (newmsgs)
3101 *newmsgs = 0;
3102 if (oldmsgs)
3103 *oldmsgs = 0;
3104 if (urgentmsgs)
3105 *urgentmsgs = 0;
3106
3107 ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
3108 /* If no mailbox, return immediately */
3109 if (ast_strlen_zero(mailbox_context))
3110 return 0;
3111
3112 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
3113 context = strchr(tmp, '@');
3114 if (strchr(mailbox_context, ',')) {
3115 int tmpnew, tmpold, tmpurgent;
3116 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
3117 mb = tmp;
3118 while ((cur = strsep(&mb, ", "))) {
3119 if (!ast_strlen_zero(cur)) {
3120 if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
3121 return -1;
3122 else {
3123 if (newmsgs)
3124 *newmsgs += tmpnew;
3125 if (oldmsgs)
3126 *oldmsgs += tmpold;
3127 if (urgentmsgs)
3128 *urgentmsgs += tmpurgent;
3129 }
3130 }
3131 }
3132 return 0;
3133 }
3134 if (context) {
3135 *context = '\0';
3136 mailboxnc = tmp;
3137 context++;
3138 } else {
3139 context = "default";
3140 mailboxnc = (char *) mailbox_context;
3141 }
3142
3143 if (newmsgs) {
3144 struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
3145 if (!vmu) {
3146 ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
3147 return -1;
3148 }
3149 if ((count = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
3150 free_user(vmu);
3151 return -1;
3152 }
3153 *newmsgs = count;
3154 free_user(vmu);
3155 }
3156 if (oldmsgs) {
3157 if ((count = __messagecount(context, mailboxnc, "Old")) < 0) {
3158 return -1;
3159 }
3160 *oldmsgs = count;
3161 }
3162 if (urgentmsgs) {
3163 if ((count = __messagecount(context, mailboxnc, "Urgent")) < 0) {
3164 return -1;
3165 }
3166 *urgentmsgs = count;
3167 }
3168 return 0;
3169}
3170
3171/*!
3172 * \brief Determines if the given folder has messages.
3173 * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
3174 * \param folder the folder to look in
3175 *
3176 * This function is used when the mailbox is stored in an IMAP back end.
3177 * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
3178 * \return 1 if the folder has one or more messages. zero otherwise.
3179 */
3180
3181static int has_voicemail(const char *mailbox, const char *folder)
3182{
3183 char tmp[256], *tmp2, *box, *context;
3184 ast_copy_string(tmp, mailbox, sizeof(tmp));
3185 tmp2 = tmp;
3186 if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
3187 while ((box = strsep(&tmp2, ",&"))) {
3188 if (!ast_strlen_zero(box)) {
3189 if (has_voicemail(box, folder)) {
3190 return 1;
3191 }
3192 }
3193 }
3194 }
3195 if ((context = strchr(tmp, '@'))) {
3196 *context++ = '\0';
3197 } else {
3198 context = "default";
3199 }
3200 return __messagecount(context, tmp, folder) > 0 ? 1 : 0;
3201}
3202
3203/*!
3204 * \brief Copies a message from one mailbox to another.
3205 * \param chan
3206 * \param vmu
3207 * \param imbox
3208 * \param msgnum
3209 * \param duration
3210 * \param recip
3211 * \param fmt
3212 * \param dir
3213 * \param flag, dest_folder
3214 *
3215 * This works with IMAP storage based mailboxes.
3216 *
3217 * \return zero on success, -1 on error.
3218 */
3219static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, char *flag, const char *dest_folder)
3220{
3221 struct vm_state *sendvms = NULL;
3222 char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
3223 if (msgnum >= recip->maxmsg) {
3224 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
3225 return -1;
3226 }
3227 if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
3228 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
3229 return -1;
3230 }
3231 if (!get_vm_state_by_imapuser(recip->imapuser, 0)) {
3232 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
3233 return -1;
3234 }
3235 snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
3236 ast_mutex_lock(&sendvms->lock);
3237 if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
3238 ast_mutex_unlock(&sendvms->lock);
3239 return 0;
3240 }
3241 ast_mutex_unlock(&sendvms->lock);
3242 ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
3243 return -1;
3244}
3245
3246static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
3247{
3248 char tmp[256], *t = tmp;
3249 size_t left = sizeof(tmp);
3250
3251 if (box == OLD_FOLDER) {
3252 ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
3253 } else {
3254 ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
3255 }
3256
3257 if (box == NEW_FOLDER) {
3258 ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
3259 } else {
3260 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
3261 }
3262
3263 /* Build up server information */
3264 ast_build_string(&t, &left, "{%s:%s/imap", S_OR(vms->imapserver, imapserver), S_OR(vms->imapport, imapport));
3265
3266 /* Add authentication user if present */
3267 if (!ast_strlen_zero(authuser))
3268 ast_build_string(&t, &left, "/authuser=%s", authuser);
3269
3270 /* Add flags if present */
3271 if (!ast_strlen_zero(imapflags) || !(ast_strlen_zero(vms->imapflags))) {
3272 ast_build_string(&t, &left, "/%s", S_OR(vms->imapflags, imapflags));
3273 }
3274
3275 /* End with username */
3276#if 1
3277 ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
3278#else
3279 ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
3280#endif
3281 if (box == NEW_FOLDER || box == OLD_FOLDER)
3282 snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
3283 else if (box == GREETINGS_FOLDER)
3284 snprintf(spec, len, "%s%s", tmp, greetingfolder);
3285 else { /* Other folders such as Friends, Family, etc... */
3286 if (!ast_strlen_zero(imapparentfolder)) {
3287 /* imapparentfolder would typically be set to INBOX */
3288 snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
3289 } else {
3290 snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
3291 }
3292 }
3293}
3294
3295static int init_mailstream(struct vm_state *vms, int box)
3296{
3297 MAILSTREAM *stream = NIL;
3298 long debug;
3299 char tmp[256];
3300
3301 if (!vms) {
3302 ast_log(LOG_ERROR, "vm_state is NULL!\n");
3303 return -1;
3304 }
3305 ast_debug(3, "vm_state user is:%s\n", vms->imapuser);
3306 if (vms->mailstream == NIL || !vms->mailstream) {
3307 ast_debug(1, "mailstream not set.\n");
3308 } else {
3309 stream = vms->mailstream;
3310 }
3311 /* debug = T; user wants protocol telemetry? */
3312 debug = NIL; /* NO protocol telemetry? */
3313
3314 if (delimiter == '\0') { /* did not probe the server yet */
3315 char *cp;
3316#ifdef USE_SYSTEM_IMAP
3317#include <imap/linkage.c>
3318#elif defined(USE_SYSTEM_CCLIENT)
3319#include <c-client/linkage.c>
3320#else
3321#include "linkage.c"
3322#endif
3323 /* Connect to INBOX first to get folders delimiter */
3324 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
3325 ast_mutex_lock(&vms->lock);
3326 ast_mutex_lock(&mail_open_lock);
3327 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
3328 ast_mutex_unlock(&mail_open_lock);
3329 ast_mutex_unlock(&vms->lock);
3330 if (stream == NIL) {
3331 ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
3332 return -1;
3333 }
3334 get_mailbox_delimiter(vms, stream);
3335 /* update delimiter in imapfolder */
3336 for (cp = vms->imapfolder; *cp; cp++)
3337 if (*cp == '/')
3338 *cp = delimiter;
3339 }
3340 /* Now connect to the target folder */
3341 imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
3342 ast_debug(3, "Before mail_open, server: %s, box:%d\n", tmp, box);
3343 ast_mutex_lock(&vms->lock);
3344 ast_mutex_lock(&mail_open_lock);
3345 vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
3346 /* Create the folder if it doesn't exist */
3347 if (vms->mailstream && !mail_status(vms->mailstream, tmp, SA_UIDNEXT)) {
3348 mail_create(vms->mailstream, tmp);
3349 }
3350 ast_mutex_unlock(&mail_open_lock);
3351 ast_mutex_unlock(&vms->lock);
3352 if (vms->mailstream == NIL) {
3353 return -1;
3354 } else {
3355 return 0;
3356 }
3357}
3358
3359static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
3360{
3361 SEARCHPGM *pgm;
3362 SEARCHHEADER *hdr;
3363 int urgent = 0;
3364
3365 /* If Urgent, then look at INBOX */
3366 if (box == 11) {
3367 box = NEW_FOLDER;
3368 urgent = 1;
3369 }
3370
3371 ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
3372 ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
3373 ast_copy_string(vms->imapserver, vmu->imapserver, sizeof(vms->imapserver));
3374 ast_copy_string(vms->imapport, vmu->imapport, sizeof(vms->imapport));
3375 ast_copy_string(vms->imapflags, vmu->imapflags, sizeof(vms->imapflags));
3376 vms->imapversion = vmu->imapversion;
3377 ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
3378
3379 if (init_mailstream(vms, box) || !vms->mailstream) {
3380 ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
3381 return -1;
3382 }
3383
3384 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
3385
3386 /* Check Quota */
3387 if (box == 0) {
3388 ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
3389 check_quota(vms, (char *) mbox(vmu, box));
3390 }
3391
3392 ast_mutex_lock(&vms->lock);
3393 pgm = mail_newsearchpgm();
3394
3395 /* Check IMAP folder for Asterisk messages only... */
3396 hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
3397 hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
3398 pgm->header = hdr;
3399 pgm->deleted = 0;
3400 pgm->undeleted = 1;
3401
3402 /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
3403 if (box == NEW_FOLDER && urgent == 1) {
3404 pgm->unseen = 1;
3405 pgm->seen = 0;
3406 pgm->flagged = 1;
3407 pgm->unflagged = 0;
3408 } else if (box == NEW_FOLDER && urgent == 0) {
3409 pgm->unseen = 1;
3410 pgm->seen = 0;
3411 pgm->flagged = 0;
3412 pgm->unflagged = 1;
3413 } else if (box == OLD_FOLDER) {
3414 pgm->seen = 1;
3415 pgm->unseen = 0;
3416 }
3417
3418 ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
3419
3420 vms->vmArrayIndex = 0;
3421 mail_search_full (vms->mailstream, NULL, pgm, NIL);
3422 vms->lastmsg = vms->vmArrayIndex - 1;
3423 mail_free_searchpgm(&pgm);
3424 /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
3425 * ensure to allocate enough space to account for all of them. Warn if old messages
3426 * have not been checked first as that is required.
3427 */
3428 if (box == 0 && !vms->dh_arraysize) {
3429 ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
3430 }
3431 if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
3432 ast_mutex_unlock(&vms->lock);
3433 return -1;
3434 }
3435
3436 ast_mutex_unlock(&vms->lock);
3437 return 0;
3438}
3439
3440static void write_file(char *filename, char *buffer, unsigned long len)
3441{
3442 FILE *output;
3443
3444 if (!filename || !buffer) {
3445 return;
3446 }
3447
3448 if (!(output = fopen(filename, "w"))) {
3449 ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", filename, strerror(errno));
3450 return;
3451 }
3452
3453 if (fwrite(buffer, len, 1, output) != 1) {
3454 if (ferror(output)) {
3455 ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
3456 }
3457 }
3458 fclose (output);
3459}
3460
3461static void update_messages_by_imapuser(const char *user, unsigned long number)
3462{
3463 struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
3464
3465 if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
3466 return;
3467 }
3468
3469 ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
3470
3471 /* Ensure we have room for the next message. */
3472 if (vms->vmArrayIndex >= vms->msg_array_max) {
3473 long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
3474 if (!new_mem) {
3475 return;
3476 }
3477 vms->msgArray = new_mem;
3478 vms->msg_array_max *= 2;
3479 }
3480
3481 vms->msgArray[vms->vmArrayIndex++] = number;
3482}
3483
3484void mm_searched(MAILSTREAM *stream, unsigned long number)
3485{
3486 char *mailbox = stream->mailbox, buf[1024] = "", *user;
3487
3488 if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
3489 return;
3490
3491 update_messages_by_imapuser(user, number);
3492}
3493
3494static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
3495{
3496 struct ast_variable *var;
3497 struct ast_vm_user *vmu;
3498
3499 vmu = ast_calloc(1, sizeof *vmu);
3500 if (!vmu)
3501 return NULL;
3502
3503 populate_defaults(vmu);
3505
3506 var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
3507 if (var) {
3508 apply_options_full(vmu, var);
3510 return vmu;
3511 } else {
3512 ast_free(vmu);
3513 return NULL;
3514 }
3515}
3516
3517/* Interfaces to C-client */
3518
3519void mm_exists(MAILSTREAM * stream, unsigned long number)
3520{
3521 /* mail_ping will callback here if new mail! */
3522 ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
3523 if (number == 0) return;
3524 set_update(stream);
3525}
3526
3527
3528void mm_expunged(MAILSTREAM * stream, unsigned long number)
3529{
3530 /* mail_ping will callback here if expunged mail! */
3531 ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
3532 if (number == 0) return;
3533 set_update(stream);
3534}
3535
3536
3537void mm_flags(MAILSTREAM * stream, unsigned long number)
3538{
3539 /* mail_ping will callback here if read mail! */
3540 ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
3541 if (number == 0) return;
3542 set_update(stream);
3543}
3544
3545
3546void mm_notify(MAILSTREAM * stream, char *string, long errflg)
3547{
3548 ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
3549 mm_log (string, errflg);
3550}
3551
3552
3553void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
3554{
3555 if (delimiter == '\0') {
3556 delimiter = delim;
3557 }
3558
3559 ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
3560 if (attributes & LATT_NOINFERIORS)
3561 ast_debug(5, "no inferiors\n");
3562 if (attributes & LATT_NOSELECT)
3563 ast_debug(5, "no select\n");
3564 if (attributes & LATT_MARKED)
3565 ast_debug(5, "marked\n");
3566 if (attributes & LATT_UNMARKED)
3567 ast_debug(5, "unmarked\n");
3568}
3569
3570
3571void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
3572{
3573 ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
3574 if (attributes & LATT_NOINFERIORS)
3575 ast_debug(5, "no inferiors\n");
3576 if (attributes & LATT_NOSELECT)
3577 ast_debug(5, "no select\n");
3578 if (attributes & LATT_MARKED)
3579 ast_debug(5, "marked\n");
3580 if (attributes & LATT_UNMARKED)
3581 ast_debug(5, "unmarked\n");
3582}
3583
3584
3585void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
3586{
3587 struct ast_str *str;
3588
3589 if (!DEBUG_ATLEAST(5) || !(str = ast_str_create(256))) {
3590 return;
3591 }
3592
3593 ast_str_append(&str, 0, " Mailbox %s", mailbox);
3594 if (status->flags & SA_MESSAGES) {
3595 ast_str_append(&str, 0, ", %lu messages", status->messages);
3596 }
3597 if (status->flags & SA_RECENT) {
3598 ast_str_append(&str, 0, ", %lu recent", status->recent);
3599 }
3600 if (status->flags & SA_UNSEEN) {
3601 ast_str_append(&str, 0, ", %lu unseen", status->unseen);
3602 }
3603 if (status->flags & SA_UIDVALIDITY) {
3604 ast_str_append(&str, 0, ", %lu UID validity", status->uidvalidity);
3605 }
3606 if (status->flags & SA_UIDNEXT) {
3607 ast_str_append(&str, 0, ", %lu next UID", status->uidnext);
3608 }
3610
3611 ast_free(str);
3612}
3613
3614
3615void mm_log(char *string, long errflg)
3616{
3617 switch ((short) errflg) {
3618 case NIL:
3619 ast_debug(1, "IMAP Info: %s\n", string);
3620 break;
3621 case PARSE:
3622 case WARN:
3623 ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
3624 break;
3625 case ERROR:
3626 ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
3627 break;
3628 }
3629}
3630
3631
3632void mm_dlog(char *string)
3633{
3634 ast_log(AST_LOG_NOTICE, "%s\n", string);
3635}
3636
3637
3638void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
3639{
3640 struct ast_vm_user *vmu;
3641
3642 ast_debug(4, "Entering callback mm_login\n");
3643
3644 ast_copy_string(user, mb->user, MAILTMPLEN);
3645
3646 /* We should only do this when necessary */
3647 if (!ast_strlen_zero(authpassword)) {
3648 ast_copy_string(pwd, authpassword, MAILTMPLEN);
3649 } else {
3650 AST_LIST_TRAVERSE(&users, vmu, list) {
3651 if (!strcasecmp(mb->user, vmu->imapuser)) {
3652 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
3653 break;
3654 }
3655 }
3656 if (!vmu) {
3657 if ((vmu = find_user_realtime_imapuser(mb->user))) {
3658 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
3659 free_user(vmu);
3660 }
3661 }
3662 }
3663}
3664
3665
3666void mm_critical(MAILSTREAM * stream)
3667{
3668}
3669
3670
3671void mm_nocritical(MAILSTREAM * stream)
3672{
3673}
3674
3675
3676long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
3677{
3678 kill (getpid (), SIGSTOP);
3679 return NIL;
3680}
3681
3682
3683void mm_fatal(char *string)
3684{
3685 ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
3686}
3687
3688/* C-client callback to handle quota */
3689static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
3690{
3691 struct vm_state *vms;
3692 char *mailbox = stream->mailbox, *user;
3693 char buf[1024] = "";
3694 unsigned long usage = 0, limit = 0;
3695
3696 while (pquota) {
3697 usage = pquota->usage;
3698 limit = pquota->limit;
3699 pquota = pquota->next;
3700 }
3701
3702 if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || (!(vms = get_vm_state_by_imapuser(user, 2)) && !(vms = get_vm_state_by_imapuser(user, 0)))) {
3703 ast_log(AST_LOG_ERROR, "No state found.\n");
3704 return;
3705 }
3706
3707 ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
3708
3709 vms->quota_usage = usage;
3710 vms->quota_limit = limit;
3711}
3712
3713static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
3714{
3715 char *start, *eol_pnt;
3716 int taglen;
3717
3719 return NULL;
3720
3721 taglen = strlen(tag) + 1;
3722 if (taglen < 1)
3723 return NULL;
3724
3725 if (!(start = strcasestr(header, tag)))
3726 return NULL;
3727
3728 /* Since we can be called multiple times we should clear our buffer */
3729 memset(buf, 0, len);
3730
3731 ast_copy_string(buf, start+taglen, len);
3732 if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
3733 *eol_pnt = '\0';
3734 return buf;
3735}
3736
3737static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
3738{
3739 char *start, *eol_pnt, *quote;
3740
3742 return NULL;
3743
3744 if (!(start = strstr(mailbox, "/user=")))
3745 return NULL;
3746
3747 ast_copy_string(buf, start+6, len);
3748
3749 if (!(quote = strchr(buf, '"'))) {
3750 if ((eol_pnt = strchr(buf, '/')) || (eol_pnt = strchr(buf, '}'))) {
3751 *eol_pnt = '\0';
3752 }
3753 return buf;
3754 } else {
3755 if ((eol_pnt = strchr(quote + 1, '"'))) {
3756 *eol_pnt = '\0';
3757 }
3758 return quote + 1;
3759 }
3760}
3761
3762static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
3763{
3764 struct vm_state *vms_p;
3765
3766 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3767 if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
3768 return vms_p;
3769 }
3770 ast_debug(5, "Adding new vmstate for %s\n", vmu->imapuser);
3771 /* XXX: Is this correctly freed always? */
3772 if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
3773 return NULL;
3774 ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
3775 ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
3776 ast_copy_string(vms_p->imapserver, vmu->imapserver, sizeof(vms_p->imapserver));
3777 ast_copy_string(vms_p->imapport, vmu->imapport, sizeof(vms_p->imapport));
3778 ast_copy_string(vms_p->imapflags, vmu->imapflags, sizeof(vms_p->imapflags));
3779 ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
3780 ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
3781 vms_p->mailstream = NIL; /* save for access from interactive entry point */
3782 vms_p->imapversion = vmu->imapversion;
3783 ast_debug(5, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
3784 vms_p->updated = 1;
3785 /* set mailbox to INBOX! */
3786 ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
3787 init_vm_state(vms_p);
3788 vmstate_insert(vms_p);
3789 return vms_p;
3790}
3791
3792static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
3793{
3794 struct vmstate *vlist = NULL;
3795
3796 if (interactive) {
3797 struct vm_state *vms;
3798 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3799 if ((vms = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms->imapuser, user)) {
3800 return vms;
3801 }
3802 }
3803
3804 AST_LIST_LOCK(&vmstates);
3805 AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3806 if (!vlist->vms) {
3807 ast_debug(3, "error: vms is NULL for %s\n", user);
3808 continue;
3809 }
3810 if (vlist->vms->imapversion != imapversion) {
3811 continue;
3812 }
3813
3814 if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
3815 AST_LIST_UNLOCK(&vmstates);
3816 return vlist->vms;
3817 }
3818 }
3819 AST_LIST_UNLOCK(&vmstates);
3820
3821 ast_debug(3, "%s not found in vmstates\n", user);
3822
3823 return NULL;
3824}
3825
3826static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
3827{
3828
3829 struct vmstate *vlist = NULL;
3830 const char *local_context = S_OR(context, "default");
3831
3832 if (interactive) {
3833 struct vm_state *vms;
3834 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3835 if ((vms = pthread_getspecific(ts_vmstate.key)) &&
3836 !strcmp(vms->username,mailbox) && !strcmp(vms->context, local_context)) {
3837 return vms;
3838 }
3839 }
3840
3841 AST_LIST_LOCK(&vmstates);
3842 AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3843 if (!vlist->vms) {
3844 ast_debug(3, "error: vms is NULL for %s\n", mailbox);
3845 continue;
3846 }
3847 if (vlist->vms->imapversion != imapversion) {
3848 continue;
3849 }
3850
3851 ast_debug(3, "comparing mailbox %s@%s (i=%d) to vmstate mailbox %s@%s (i=%d)\n", mailbox, local_context, interactive, vlist->vms->username, vlist->vms->context, vlist->vms->interactive);
3852
3853 if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
3854 ast_debug(3, "Found it!\n");
3855 AST_LIST_UNLOCK(&vmstates);
3856 return vlist->vms;
3857 }
3858 }
3859 AST_LIST_UNLOCK(&vmstates);
3860
3861 ast_debug(3, "%s not found in vmstates\n", mailbox);
3862
3863 return NULL;
3864}
3865
3866static void vmstate_insert(struct vm_state *vms)
3867{
3868 struct vmstate *v;
3869 struct vm_state *altvms;
3870
3871 /* If interactive, it probably already exists, and we should
3872 use the one we already have since it is more up to date.
3873 We can compare the username to find the duplicate */
3874 if (vms->interactive == 1) {
3875 altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
3876 if (altvms) {
3877 ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3878 vms->newmessages = altvms->newmessages;
3879 vms->oldmessages = altvms->oldmessages;
3880 vms->vmArrayIndex = altvms->vmArrayIndex;
3881 /* XXX: no msgArray copying? */
3882 vms->lastmsg = altvms->lastmsg;
3883 vms->curmsg = altvms->curmsg;
3884 /* get a pointer to the persistent store */
3885 vms->persist_vms = altvms;
3886 /* Reuse the mailstream? */
3887#ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
3888 vms->mailstream = altvms->mailstream;
3889#else
3890 vms->mailstream = NIL;
3891#endif
3892 }
3893 return;
3894 }
3895
3896 if (!(v = ast_calloc(1, sizeof(*v))))
3897 return;
3898
3899 v->vms = vms;
3900
3901 ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3902
3903 AST_LIST_LOCK(&vmstates);
3904 AST_LIST_INSERT_TAIL(&vmstates, v, list);
3905 AST_LIST_UNLOCK(&vmstates);
3906}
3907
3908static void vmstate_delete(struct vm_state *vms)
3909{
3910 struct vmstate *vc = NULL;
3911 struct vm_state *altvms = NULL;
3912
3913 /* If interactive, we should copy pertinent info
3914 back to the persistent state (to make update immediate) */
3915 if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
3916 ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3917 altvms->newmessages = vms->newmessages;
3918 altvms->oldmessages = vms->oldmessages;
3919 altvms->updated = 1;
3920 vms->mailstream = mail_close(vms->mailstream);
3921
3922 /* Interactive states are not stored within the persistent list */
3923 return;
3924 }
3925
3926 ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3927
3928 AST_LIST_LOCK(&vmstates);
3929 AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
3930 if (vc->vms == vms) {
3932 break;
3933 }
3934 }
3936 AST_LIST_UNLOCK(&vmstates);
3937
3938 if (vc) {
3939 ast_mutex_destroy(&vc->vms->lock);
3940 ast_free(vc->vms->msgArray);
3941 vc->vms->msgArray = NULL;
3942 vc->vms->msg_array_max = 0;
3943 /* XXX: is no one supposed to free vms itself? */
3944 ast_free(vc);
3945 } else {
3946 ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3947 }
3948}
3949
3950static void set_update(MAILSTREAM * stream)
3951{
3952 struct vm_state *vms;
3953 char *mailbox = stream->mailbox, *user;
3954 char buf[1024] = "";
3955
3956 if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
3957 if (user && DEBUG_ATLEAST(3))
3958 ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
3959 return;
3960 }
3961
3962 ast_debug(3, "User %s mailbox set for update.\n", user);
3963
3964 vms->updated = 1; /* Set updated flag since mailbox changed */
3965}
3966
3967static void init_vm_state(struct vm_state *vms)
3968{
3969 vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
3970 vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
3971 if (!vms->msgArray) {
3972 /* Out of mem? This can't be good. */
3973 vms->msg_array_max = 0;
3974 }
3975 vms->vmArrayIndex = 0;
3976 ast_mutex_init(&vms->lock);
3977}
3978
3979static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro)
3980{
3981 char *body_content;
3982 char *body_decoded;
3983 char *fn = is_intro ? vms->introfn : vms->fn;
3984 unsigned long len = 0;
3985 unsigned long newlen = 0;
3986 char filename[256];
3987
3988 if (!body || body == NIL)
3989 return -1;
3990
3991 ast_mutex_lock(&vms->lock);
3992 body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
3993 ast_mutex_unlock(&vms->lock);
3996 "Msgno %ld, section %s. The body's content size %ld is huge (max %ld). User:%s, mailbox %s\n",
3997 vms->msgArray[vms->curmsg], section, len, MAX_MAIL_BODY_CONTENT_SIZE, vms->imapuser, vms->username);
3998 return -1;
3999 }
4000 if (body_content != NIL && len) {
4001 snprintf(filename, sizeof(filename), "%s.%s", fn, format);
4002 /* ast_debug(1, body_content); */
4003 body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
4004 /* If the body of the file is empty, return an error */
4005 if (!newlen || !body_decoded) {
4006 return -1;
4007 }
4008 write_file(filename, (char *) body_decoded, newlen);
4009 } else {
4010 ast_debug(5, "Body of message is NULL.\n");
4011 return -1;
4012 }
4013 return 0;
4014}
4015
4016/*!
4017 * \brief Get delimiter via mm_list callback
4018 * \param vms The voicemail state object
4019 * \param stream
4020 *
4021 * Determines the delimiter character that is used by the underlying IMAP based mail store.
4022 */
4023/* MUTEX should already be held */
4024static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream) {
4025 char tmp[50];
4026 snprintf(tmp, sizeof(tmp), "{%s}", S_OR(vms->imapserver, imapserver));
4027 mail_list(stream, tmp, "*");
4028}
4029
4030/*!
4031 * \brief Check Quota for user
4032 * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
4033 * \param mailbox the mailbox to check the quota for.
4034 *
4035 * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
4036 */
4037static void check_quota(struct vm_state *vms, char *mailbox) {
4038 ast_mutex_lock(&vms->lock);
4039 mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
4040 ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
4041 if (vms && vms->mailstream != NULL) {
4042 imap_getquotaroot(vms->mailstream, mailbox);
4043 } else {
4044 ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
4045 }
4046 ast_mutex_unlock(&vms->lock);
4047}
4048
4049#endif /* IMAP_STORAGE */
4050
4051/*! \brief Lock file path
4052 * only return failure if ast_lock_path returns 'timeout',
4053 * not if the path does not exist or any other reason
4054 */
4055static int vm_lock_path(const char *path)
4056{
4057 switch (ast_lock_path(path)) {
4058 case AST_LOCK_TIMEOUT:
4059 return -1;
4060 default:
4061 return 0;
4062 }
4063}
4064
4065#define MSG_ID_LEN 256
4066
4067/* Used to attach a unique identifier to an msg_id */
4069
4070/*!
4071 * \brief Sets the destination string to a uniquely identifying msg_id string
4072 * \param dst pointer to a character buffer that should contain MSG_ID_LEN characters.
4073 */
4074static void generate_msg_id(char *dst);
4075
4076#ifdef ODBC_STORAGE
4077
4078/*!
4079 * \internal
4080 * \brief Create a buffer containing the SQL statement with the table name inserted.
4081 *
4082 * \param __sql_fmt The SQL statement with a single '%s' where the table name should be inserted.
4083 *
4084 * \note The buffer is allocated on the stack and should not be freed.
4085 *
4086 * \return A pointer to a buffer containing the SQL statement with the table name inserted.
4087 */
4088#define MAKE_SQL_PTRA(__sql_fmt) \
4089({ \
4090 /* The NULL terminator is included in odbc_table_len. */ \
4091 char *__sql = ast_alloca(strlen(__sql_fmt) + odbc_table_len); \
4092 sprintf(__sql, __sql_fmt, odbc_table); /* Safe */ \
4093 __sql; \
4094})
4095
4096/*!
4097 * \internal
4098 * \brief Create a buffer containing the SQL statement with the table name inserted twice.
4099 *
4100 * \param __sql_fmt The SQL statement with two '%s' where the table name should be inserted.
4101 *
4102 * \note The buffer is allocated on the stack and should not be freed.
4103 *
4104 * \return A pointer to a buffer containing the SQL statement with the table name inserted.
4105 */
4106#define MAKE_SQL_PTRA2(__sql_fmt) \
4107({ \
4108 /* The NULL terminator is included in odbc_table_len. */ \
4109 char *__sql = ast_alloca(strlen(__sql_fmt) + (odbc_table_len * 2)); \
4110 sprintf(__sql, __sql_fmt, odbc_table, odbc_table); /* Safe */ \
4111 __sql; \
4112})
4113
4114struct generic_prepare_struct {
4115 char *sql;
4116 int argc;
4117 char **argv;
4118};
4119
4120static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
4121{
4122 struct generic_prepare_struct *gps = data;
4123 int res, i;
4124 SQLHSTMT stmt;
4125
4126 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4127 if (!SQL_SUCCEEDED(res)) {
4128 ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
4129 return NULL;
4130 }
4131 res = ast_odbc_prepare(obj, stmt, gps->sql);
4132 if (!SQL_SUCCEEDED(res)) {
4133 ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
4134 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4135 return NULL;
4136 }
4137 for (i = 0; i < gps->argc; i++)
4138 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
4139
4140 return stmt;
4141}
4142
4143static void odbc_update_msg_id(char *dir, int msg_num, char *msg_id)
4144{
4145 SQLHSTMT stmt;
4146 char *sql = MAKE_SQL_PTRA("UPDATE %s SET msg_id=? WHERE dir=? AND msgnum=?");
4147 struct odbc_obj *obj;
4148 char msg_num_str[20];
4149 char *argv[] = { msg_id, dir, msg_num_str };
4150 struct generic_prepare_struct gps = { .sql = sql, .argc = 3, .argv = argv };
4151 SCOPE_ENTER(3, "dir: %s msg_num: %d msg_id: %s\n", dir, msg_num, msg_id);
4152
4153 obj = ast_odbc_request_obj(odbc_database, 0);
4154 if (!obj) {
4155 SCOPE_EXIT_LOG_RTN(LOG_WARNING, "Unable to update message ID for message %d in %s\n", msg_num, dir);
4156 }
4157
4158 snprintf(msg_num_str, sizeof(msg_num_str), "%d", msg_num);
4160 if (!stmt) {
4161 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4162 } else {
4163 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4164 }
4167}
4168
4169#define AUDIO_ON_DISK_MAGIC "AUDMAGIC"
4170#define AUDIO_ON_DISK_MAGIC_LEN 8
4171
4172static void odbc_update_set_audmagic(char *dir, int msg_num)
4173{
4174 SQLHSTMT stmt;
4175 char *sql = MAKE_SQL_PTRA("UPDATE %s SET recording=? WHERE dir=? AND msgnum=?");
4176 struct odbc_obj *obj;
4177 SQLLEN datalen = AUDIO_ON_DISK_MAGIC_LEN;
4178 SQLLEN indlen = datalen;
4179 int res;
4180 char msg_num_str[20];
4181 SCOPE_ENTER(3, "dir: %s msg_num: %d\n", dir, msg_num);
4182
4183 obj = ast_odbc_request_obj(odbc_database, 0);
4184 if (!obj) {
4185 SCOPE_EXIT_LOG_RTN(LOG_WARNING, "Unable to request obj for message %d in %s\n", msg_num, dir);
4186 }
4187
4188 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4189 if (!SQL_SUCCEEDED(res)) {
4191 SCOPE_EXIT_LOG_RTN(LOG_WARNING, "Unable to allocate stmt for message %d in %s\n", msg_num, dir);
4192 }
4193
4194 snprintf(msg_num_str, sizeof(msg_num_str), "%d", msg_num);
4195
4196 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
4197 datalen, 0, (void *) AUDIO_ON_DISK_MAGIC,
4198 datalen, &indlen);
4199
4200 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
4201 strlen(dir), 0, (void *) dir, 0, NULL);
4202
4203 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
4204 strlen(msg_num_str), 0, (void *) msg_num_str, 0, NULL);
4205
4206 res = ast_odbc_execute_sql(obj, stmt, sql);
4207 if (!SQL_SUCCEEDED(res)) {
4208 ast_log(LOG_WARNING, "Unable to execute stmt for message %d in %s\n", msg_num, dir);
4209 ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute");
4210 }
4211 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4212 stmt = NULL;
4213
4215 SCOPE_EXIT_RTN("Done\n");
4216}
4217
4218static int odbc_store_message(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum);
4219
4220/*!
4221 * \brief Retrieves a file from an ODBC data store.
4222 * \param dir the path to the file to be retrieved.
4223 * \param msgnum the message number, such as within a mailbox folder.
4224 *
4225 * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
4226 * The purpose is to get the message from the database store to the local file system, so that the message may be played, or the information file may be read.
4227 *
4228 * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
4229 * The output is the message information file with the name msgnum and the extension .txt
4230 * and the message file with the extension of its format, in the directory with base file name of the msgnum.
4231 *
4232 * \return 0 on success, -1 on error.
4233 */
4234static int odbc_retrieve_message(char *dir, int msgnum)
4235{
4236 int x = 0;
4237 int res;
4238 int fd = -1;
4239 size_t fdlen = 0;
4240 void *fdm = MAP_FAILED;
4241 SQLSMALLINT colcount = 0;
4242 SQLHSTMT stmt;
4243 char *sql = MAKE_SQL_PTRA("SELECT * FROM %s WHERE dir=? AND msgnum=?");
4244 char fmt[80] = "";
4245 char *c;
4246 char coltitle[256];
4247 SQLSMALLINT collen;
4248 SQLSMALLINT datatype;
4249 SQLSMALLINT decimaldigits;
4250 SQLSMALLINT nullable;
4251 SQLULEN colsize;
4252 SQLLEN colsize2;
4253 FILE *f = NULL;
4254 char rowdata[80];
4255 char *fn = MAKE_FILE_PTRA(dir, msgnum);
4256 char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
4257 char msgnums[80];
4258 char *mailboxuser = NULL;
4259 char *mailboxcontext = NULL;
4260 char msg_id[MSG_ID_LEN] = "";
4261 char *argv[] = { dir, msgnums };
4262 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4263 struct odbc_obj *obj;
4264 int storage_conversion_to_disk = 0;
4265 int storage_conversion_to_odbc = 0;
4266 SCOPE_ENTER(3, "dir: %s msgnum: %d msgtype: %s\n", dir, msgnum, msgnum < 0 ? "Greeting" : "Message");
4267
4268 obj = ast_odbc_request_obj(odbc_database, 0);
4269 if (!obj) {
4270 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4271 }
4272
4273 ast_copy_string(fmt, vmfmts, sizeof(fmt));
4274 c = strchr(fmt, '|');
4275 if (c)
4276 *c = '\0';
4277 if (!strcasecmp(fmt, "wav49"))
4278 strcpy(fmt, "WAV");
4279
4280 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4281
4282 ast_trace(-1, "Opening '%s' for writing\n", full_fn);
4283 if (!(f = fopen(full_fn, "w+"))) {
4284 ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
4285 goto bail;
4286 }
4287
4288 sprintf(full_fn, "%s.%s", fn, fmt); /* Safe. We're just replacing the file exten. */
4289
4291 if (!stmt) {
4292 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4293 goto bail;
4294 }
4295
4296 res = SQLFetch(stmt);
4297 if (!SQL_SUCCEEDED(res)) {
4298 if (res != SQL_NO_DATA) {
4299 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4300 }
4301 goto bail_with_handle;
4302 }
4303
4304 res = SQLNumResultCols(stmt, &colcount);
4305 if (!SQL_SUCCEEDED(res)) {
4306 ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
4307 goto bail_with_handle;
4308 }
4309
4310 fprintf(f, "[message]\n");
4311 for (x = 0; x < colcount; x++) {
4312 rowdata[0] = '\0';
4313 colsize = 0;
4314 collen = sizeof(coltitle);
4315 res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen,
4316 &datatype, &colsize, &decimaldigits, &nullable);
4317 if (!SQL_SUCCEEDED(res)) {
4318 ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
4319 goto bail_with_handle;
4320 }
4321
4322 if (!strcasecmp(coltitle, "recording")) {
4323 off_t offset;
4324 char tmp[1] = "";
4325
4326 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
4327 fdlen = colsize2;
4328 ast_trace(-1, "Audio size: %ld\n", colsize2);
4329 if (colsize2 == AUDIO_ON_DISK_MAGIC_LEN) {
4330 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, AUDIO_ON_DISK_MAGIC_LEN, NULL);
4331 if (memcmp(rowdata, AUDIO_ON_DISK_MAGIC, AUDIO_ON_DISK_MAGIC_LEN) != 0) {
4332 ast_log(AST_LOG_WARNING, "Invalid audio magic number '0x%02X%02X%02X%02X%02X%02X%02X%02X' for '%s'\n",
4333 rowdata[0], rowdata[1], rowdata[2], rowdata[3], rowdata[4], rowdata[5], rowdata[6],
4334 rowdata[7], full_fn);
4335 goto bail_with_handle;
4336 }
4337 ast_trace(-1, "Audio is stored on disk. No need to write '%s'\n", full_fn);
4339 storage_conversion_to_odbc = 1;
4340 }
4341
4342 continue;
4343 }
4344
4345 ast_trace(-1, "Opening '%s' for writing\n", full_fn);
4346 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
4347 if (fd < 0) {
4348 ast_log(AST_LOG_WARNING, "Failed to open '%s' for writing: %s\n", full_fn, strerror(errno));
4349 goto bail_with_handle;
4350 }
4352 storage_conversion_to_disk = 1;
4353 }
4354
4355 lseek(fd, fdlen - 1, SEEK_SET);
4356 if (write(fd, tmp, 1) != 1) {
4357 close(fd);
4358 fd = -1;
4359 continue;
4360 }
4361 /* Read out in small chunks */
4362 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
4363 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
4364 ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
4365 goto bail_with_handle;
4366 }
4367 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
4368 munmap(fdm, CHUNKSIZE);
4369 if (!SQL_SUCCEEDED(res)) {
4370 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4371 unlink(full_fn);
4372 goto bail_with_handle;
4373 }
4374 }
4375 if (truncate(full_fn, fdlen) < 0) {
4376 ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
4377 }
4378 ast_trace(-1, "Wrote %d bytes to '%s'\n", (int)fdlen, full_fn);
4379 } else {
4380 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4381 if (res == SQL_NULL_DATA && !strcasecmp(coltitle, "msg_id")) {
4382 /*
4383 * Generate msg_id if there wasn't one, but don't store it until we're
4384 * done with this connection.
4385 */
4386 generate_msg_id(msg_id);
4387 ast_trace(-1, "msg_id was NULL. Generating new one: %s\n", msg_id);
4388 snprintf(rowdata, sizeof(rowdata), "%s", msg_id);
4389 } else if (!strcasecmp(coltitle, "mailboxuser")) {
4390 mailboxuser = ast_strdupa(rowdata);
4391 } else if (!strcasecmp(coltitle, "mailboxcontext")) {
4392 mailboxcontext = ast_strdupa(rowdata);
4393 } else if (res == SQL_NULL_DATA && !strcasecmp(coltitle, "category")) {
4394 /* Ignore null column value for category */
4395 ast_trace(-1, "Ignoring null category column in ODBC voicemail retrieve_file.\n");
4396 continue;
4397 } else if (!SQL_SUCCEEDED(res)) {
4398 ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
4399 goto bail_with_handle;
4400 }
4401 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir")) {
4402 fprintf(f, "%s=%s\n", coltitle, rowdata);
4403 }
4404 }
4405 }
4406
4407bail_with_handle:
4408 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4409
4410bail:
4411 if (f)
4412 fclose(f);
4413 if (fd > -1)
4414 close(fd);
4415
4417
4418 /* If res_odbc is configured to only allow a single database connection, we
4419 will deadlock if we try to do this before releasing the connection we
4420 were just using. */
4421 if (!ast_strlen_zero(msg_id)) {
4422 odbc_update_msg_id(dir, msgnum, msg_id);
4423 }
4424
4425 if (SQL_SUCCEEDED(res)) {
4426 if (storage_conversion_to_disk) {
4427 /*
4428 * If we're currently storing audio on disk but there was pre-existing
4429 * audio in the database, we need to update the database to set the
4430 * 'recording' column to AUDIO_ON_DISK_MAGIC.
4431 */
4432 SCOPE_CALL(-1, odbc_update_set_audmagic, dir, msgnum);
4433 }
4434 if (storage_conversion_to_odbc) {
4435 /*
4436 * If we're currently storing audio in the database but there was
4437 * pre-existing audio on disk, we need to add the audio back
4438 * into the database overwriting the AUDIO_ON_DISK_MAGIC
4439 * magic number.
4440 */
4441 SCOPE_CALL(-1, odbc_store_message, dir, mailboxuser, mailboxcontext, msgnum);
4442 }
4443 }
4444
4445 SCOPE_EXIT_RTN_VALUE(x - 1, "Done. msg_id: %s RC: %d\n", msg_id, x - 1);
4446}
4447
4448/*!
4449 * \brief Determines the highest message number in use for a given user and mailbox folder.
4450 * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
4451 *
4452 * This method is used when mailboxes are stored in an ODBC back end.
4453 * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
4454 *
4455 * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
4456 */
4457static int odbc_last_message_index(char *dir)
4458{
4459 int x = -1;
4460 int res;
4461 SQLHSTMT stmt;
4462 char *sql = MAKE_SQL_PTRA("SELECT msgnum FROM %s WHERE dir=? order by msgnum desc");
4463 char rowdata[20];
4464 char *argv[] = { dir };
4465 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
4466 struct odbc_obj *obj;
4467 SCOPE_ENTER(3, "dir: %s\n", dir);
4468
4469 obj = ast_odbc_request_obj(odbc_database, 0);
4470 if (!obj) {
4471 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4472 }
4473
4475 if (!stmt) {
4476 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4477 goto bail;
4478 }
4479
4480 res = SQLFetch(stmt);
4481 if (!SQL_SUCCEEDED(res)) {
4482 if (res == SQL_NO_DATA) {
4483 ast_trace(-1, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
4484 } else {
4485 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4486 }
4487 goto bail_with_handle;
4488 }
4489
4490 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4491 if (!SQL_SUCCEEDED(res)) {
4492 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4493 goto bail_with_handle;
4494 }
4495
4496 if (sscanf(rowdata, "%30d", &x) != 1) {
4497 ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
4498 }
4499
4500bail_with_handle:
4501 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4502
4503bail:
4505
4506 SCOPE_EXIT_RTN_VALUE(x, "Done. Last message index: %d\n", x);
4507}
4508
4509/*!
4510 * \brief Determines if the specified message exists.
4511 * \param dir the folder the mailbox folder to look for messages.
4512 * \param msgnum the message index to query for.
4513 *
4514 * This method is used when mailboxes are stored in an ODBC back end.
4515 *
4516 * \return greater than zero if the message exists, zero when the message does not exist or on error.
4517 */
4518static int odbc_message_exists(char *dir, int msgnum)
4519{
4520 int x = 0;
4521 int res;
4522 SQLHSTMT stmt;
4523 char *sql = MAKE_SQL_PTRA("SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?");
4524 char rowdata[20];
4525 char msgnums[20];
4526 char *argv[] = { dir, msgnums };
4527 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4528 struct odbc_obj *obj;
4529 SCOPE_ENTER(3, "dir: %s msgnum: %d\n", dir, msgnum);
4530
4531 obj = ast_odbc_request_obj(odbc_database, 0);
4532 if (!obj) {
4533 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4534 }
4535
4536 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4538 if (!stmt) {
4539 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4540 goto bail;
4541 }
4542
4543 res = SQLFetch(stmt);
4544 if (!SQL_SUCCEEDED(res)) {
4545 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4546 goto bail_with_handle;
4547 }
4548
4549 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4550 if (!SQL_SUCCEEDED(res)) {
4551 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4552 goto bail_with_handle;
4553 }
4554
4555 if (sscanf(rowdata, "%30d", &x) != 1) {
4556 ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
4557 }
4558
4559bail_with_handle:
4560 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4561
4562bail:
4564 SCOPE_EXIT_RTN_VALUE(x, "Done. Msg %s\n", x ? "exists" : "does not exist");
4565}
4566
4567/*!
4568 * \brief returns the number of messages found.
4569 * \param vmu
4570 * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
4571 *
4572 * This method is used when mailboxes are stored in an ODBC back end.
4573 *
4574 * \return The count of messages being zero or more, less than zero on error.
4575 */
4576static int odbc_count_messages(struct ast_vm_user *vmu, char *dir)
4577{
4578 int x = -1;
4579 int res;
4580 SQLHSTMT stmt;
4581 char *sql = MAKE_SQL_PTRA("SELECT COUNT(*) FROM %s WHERE dir=?");
4582 char rowdata[20];
4583 char *argv[] = { dir };
4584 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
4585 struct odbc_obj *obj;
4586 SCOPE_ENTER(3, "dir: %s\n", dir);
4587
4588 obj = ast_odbc_request_obj(odbc_database, 0);
4589 if (!obj) {
4590 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4591 }
4592
4594 if (!stmt) {
4595 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4596 goto bail;
4597 }
4598
4599 res = SQLFetch(stmt);
4600 if (!SQL_SUCCEEDED(res)) {
4601 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4602 goto bail_with_handle;
4603 }
4604
4605 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4606 if (!SQL_SUCCEEDED(res)) {
4607 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4608 goto bail_with_handle;
4609 }
4610
4611 if (sscanf(rowdata, "%30d", &x) != 1) {
4612 ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
4613 }
4614
4615bail_with_handle:
4616 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4617
4618bail:
4620 SCOPE_EXIT_RTN_VALUE(x, "Done. Count %d\n", x);
4621}
4622
4623/*!
4624 * \brief Deletes a message from the mailbox folder.
4625 * \param sdir The mailbox folder to work in.
4626 * \param smsg The message index to be deleted.
4627 *
4628 * This method is used when mailboxes are stored in an ODBC back end.
4629 * The specified message is directly deleted from the database 'voicemessages' table.
4630 */
4631#define DELETE_SQL_FMT "DELETE FROM %s WHERE dir=? AND msgnum=?"
4632static void odbc_delete_message(const char *sdir, int smsg)
4633{
4634 SQLHSTMT stmt;
4635 char *sql = MAKE_SQL_PTRA(DELETE_SQL_FMT);
4636 char msgnums[20];
4637 char *argv[] = { NULL, msgnums };
4638 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4639 struct odbc_obj *obj;
4640 SCOPE_ENTER(3, "sdir: %s smsg: %d\n", sdir, smsg);
4641
4642 obj = ast_odbc_request_obj(odbc_database, 0);
4643 if (!obj) {
4644 SCOPE_EXIT_LOG_RTN(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4645 }
4646
4647 argv[0] = ast_strdupa(sdir);
4648
4649 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4651 if (!stmt) {
4652 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4653 } else {
4654 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4655 }
4657
4659 char *src_fn = MAKE_FILE_PTRA(sdir, smsg);
4660 ast_trace(-1, "Audio stored on disk. Deleting '%s'\n", src_fn);
4661 ast_filedelete(src_fn, NULL);
4662 }
4663
4664 SCOPE_EXIT_RTN("Done\n");
4665}
4666
4667/*!
4668 * \brief Copies a voicemail from one mailbox to another.
4669 * \param sdir the folder for which to look for the message to be copied.
4670 * \param smsg the index of the message to be copied.
4671 * \param ddir the destination folder to copy the message into.
4672 * \param dmsg the index to be used for the copied message.
4673 * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
4674 * \param dmailboxcontext The context for the destination user.
4675 *
4676 * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
4677 */
4678#define COPY_SQL_FMT "INSERT INTO %s (dir, msgnum, msg_id, context, callerid, origtime, " \
4679 "duration, recording, flag, mailboxuser, mailboxcontext) " \
4680 "SELECT ?,?,msg_id,context,callerid,origtime,duration,recording,flag,?,? " \
4681 "FROM %s WHERE dir=? AND msgnum=?"
4682static void odbc_copy_message(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
4683{
4684 SQLHSTMT stmt;
4685 char *sql = MAKE_SQL_PTRA2(COPY_SQL_FMT);
4686 char msgnums[20];
4687 char msgnumd[20];
4688 struct odbc_obj *obj;
4689 char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
4690 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
4691 SCOPE_ENTER(3, "sdir: %s smsg: %d duser: %s dcontext: %s ddir: %s dmsg: %d\n",
4692 sdir, smsg, dmailboxuser, dmailboxcontext, ddir, dmsg);
4693
4694 SCOPE_CALL(-1, odbc_delete_message, ddir, dmsg);
4695
4696 obj = ast_odbc_request_obj(odbc_database, 0);
4697 if (!obj) {
4698 SCOPE_EXIT_LOG_RTN(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4699 }
4700
4701 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4702 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
4703
4705 if (!stmt)
4706 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
4707 else
4708 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4710
4712 char *src_fn = MAKE_FILE_PTRA(sdir, smsg);
4713 char *dst_fn = MAKE_FILE_PTRA(ddir, dmsg);
4714
4715 ast_trace(-1, "Audio stored on disk. Copying '%s' to '%s'\n", src_fn, dst_fn);
4716 ast_filecopy(src_fn, dst_fn, NULL);
4717 }
4718
4719 SCOPE_EXIT_RTN("Done\n");
4720}
4721#undef COPY_SQL_FMT
4722
4723struct insert_data {
4724 const char *dir;
4725 const char *msgnums;
4726 void *data;
4727 SQLLEN datalen;
4728 SQLLEN indlen;
4729 const char *context;
4730 const char *callerid;
4731 const char *origtime;
4732 const char *duration;
4733 const char *mailboxuser;
4734 const char *mailboxcontext;
4735 const char *category;
4736 const char *flag;
4737 const char *msg_id;
4738};
4739
4740#define STORE_SQL_FMT_CAT "INSERT INTO %s (dir, msgnum, recording, context, callerid, " \
4741 "origtime, duration, mailboxuser, mailboxcontext, flag, msg_id, category) " \
4742 "VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"
4743#define STORE_SQL_FMT "INSERT INTO %s (dir, msgnum, recording, context, callerid, "\
4744 "origtime, duration, mailboxuser, mailboxcontext, flag, msg_id) "\
4745 "VALUES (?,?,?,?,?,?,?,?,?,?,?)"
4746
4747static SQLHSTMT odbc_insert_data_cb(struct odbc_obj *obj, void *vdata)
4748{
4749 struct insert_data *data = vdata;
4750 char *insert_sql;
4751 char *delete_sql;
4752 int res;
4753 SQLHSTMT stmt;
4754 SCOPE_ENTER(3, "dir: %s msgnums: %s msg_id: %s\n", data->dir, data->msgnums,
4755 data->msg_id);
4756
4757 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4758 if (!SQL_SUCCEEDED(res)) {
4759 SCOPE_EXIT_LOG_RTN_VALUE(NULL, AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
4760 }
4761
4762 /* Delete any existing row with the same dir and msgnum */
4763 delete_sql = MAKE_SQL_PTRA(DELETE_SQL_FMT);
4764 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
4765 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
4766 res = ast_odbc_execute_sql(obj, stmt, delete_sql);
4767 if (!SQL_SUCCEEDED(res)) {
4768 ast_trace(-1, "There wasn't an existing row. Good.\n");
4769 } else {
4770 ast_trace(-1, "There WAS an existing row. This is OK if we're replacing a message.\n");
4771 }
4772 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4773 stmt = NULL;
4774
4775 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4776 if (!SQL_SUCCEEDED(res)) {
4777 SCOPE_EXIT_LOG_RTN_VALUE(NULL, AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
4778 }
4779
4780 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
4781 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
4782 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
4783 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
4784 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
4785 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
4786 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
4787 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
4788 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
4789 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
4790 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msg_id), 0, (void *) data->msg_id, 0, NULL);
4791 if (!ast_strlen_zero(data->category)) {
4792 insert_sql = MAKE_SQL_PTRA(STORE_SQL_FMT_CAT);
4793 SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
4794 } else {
4795 insert_sql = MAKE_SQL_PTRA(STORE_SQL_FMT);
4796 }
4797 res = ast_odbc_execute_sql(obj, stmt, insert_sql);
4798 if (!SQL_SUCCEEDED(res)) {
4799 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n", insert_sql);
4800 ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute");
4801 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4802 stmt = NULL;
4803 }
4804
4805 SCOPE_EXIT_RTN_VALUE(stmt, "%s\n", stmt ? "Success" : "Failed");
4806}
4807
4808/*!
4809 * \brief Stores a voicemail into the database.
4810 * \param dir the folder the mailbox folder to store the message.
4811 * \param mailboxuser the user owning the mailbox folder.
4812 * \param mailboxcontext
4813 * \param msgnum the message index for the message to be stored.
4814 *
4815 * This method is used when mailboxes are stored in an ODBC back end.
4816 * The message sound file and information file is looked up on the file system.
4817 * A SQL query is invoked to store the message into the (MySQL) database.
4818 *
4819 * \return the zero on success -1 on error.
4820 */
4821static int odbc_store_message(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
4822{
4823 int res = 0;
4824 int fd = -1;
4825 void *fdm = MAP_FAILED;
4826 off_t fdlen = -1;
4827 SQLHSTMT stmt;
4828 char msgnums[20];
4829 char *fn = MAKE_FILE_PTRA(dir, msgnum);
4830 char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
4831 char fmt[80]="";
4832 char *c;
4833 struct ast_config *cfg = NULL;
4834 struct odbc_obj *obj;
4835 struct insert_data idata = { .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
4836 .context = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "", .msg_id = "" };
4837 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
4838 SCOPE_ENTER(3, "dir: %s user: %s context: %s msgnum: %d msgtype: %s\n",
4839 dir, mailboxuser, mailboxcontext, msgnum, msgnum < 0 ? "Greeting" : "Message");
4840
4841 obj = ast_odbc_request_obj(odbc_database, 0);
4842 if (!obj) {
4843 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4844 }
4845
4846 do {
4847 ast_copy_string(fmt, vmfmts, sizeof(fmt));
4848 c = strchr(fmt, '|');
4849 if (c)
4850 *c = '\0';
4851 if (!strcasecmp(fmt, "wav49"))
4852 strcpy(fmt, "WAV");
4853
4854 ast_trace(-1, "Formats: %s Using format: '%s'\n", vmfmts, fmt);
4855 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4856
4857 ast_trace(-1, "Base path: '%s'\n", fn);
4858 ast_trace(-1, "Opening '%s'\n", full_fn);
4859 cfg = ast_config_load(full_fn, config_flags);
4860 if (!valid_config(cfg)) {
4861 if (msgnum < 0) {
4862 ast_trace(-1, "No information file found for '%s'. This is a greeting so this is OK.\n", full_fn);
4863 } else {
4864 ast_log(AST_LOG_WARNING, "Failed to open '%s'\n", full_fn);
4865 res = -1;
4866 break;
4867 }
4868 }
4869
4870 sprintf(full_fn, "%s.%s", fn, fmt); /* Safe */
4872 ast_trace(-1, "Audio stored on disk. No need to open '%s'\n", full_fn);
4873 } else {
4874 ast_trace(-1, "Opening '%s'\n", full_fn);
4875 fd = open(full_fn, O_RDWR);
4876 if (fd < 0) {
4877 ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
4878 res = -1;
4879 break;
4880 }
4881 }
4882
4883 if (valid_config(cfg)) {
4884 ast_trace(-1, "Using information file '%s'\n", fn);
4885 if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
4886 idata.context = "";
4887 }
4888 if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
4889 idata.callerid = "";
4890 }
4891 if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
4892 idata.origtime = "";
4893 }
4894 if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
4895 idata.duration = "";
4896 }
4897 if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
4898 idata.category = "";
4899 }
4900 if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
4901 idata.flag = "";
4902 }
4903 if (!(idata.msg_id = ast_variable_retrieve(cfg, "message", "msg_id"))) {
4904 idata.msg_id = "";
4905 }
4906 }
4907
4908 if (fd < 0) {
4909 ast_trace(-1, "Audio stored on disk. Not reading sound file '%s' but setting magic number.\n", full_fn);
4910 idata.data = AUDIO_ON_DISK_MAGIC;
4911 idata.datalen = idata.indlen = AUDIO_ON_DISK_MAGIC_LEN;
4912 } else {
4913 ast_trace(-1, "Reading sound file '%s'\n", full_fn);
4914 fdlen = lseek(fd, 0, SEEK_END);
4915 if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
4916 ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
4917 res = -1;
4918 break;
4919 }
4920 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
4921 if (fdm == MAP_FAILED) {
4922 ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
4923 res = -1;
4924 break;
4925 }
4926 idata.data = fdm;
4927 idata.datalen = idata.indlen = fdlen;
4928 }
4929
4930 if (ast_strlen_zero(idata.origtime)) {
4931 idata.origtime = "0";
4932 }
4933
4934 if (ast_strlen_zero(idata.duration)) {
4935 idata.duration = "0";
4936 }
4937
4938 if ((stmt = ast_odbc_direct_execute(obj, odbc_insert_data_cb, &idata))) {
4939 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4940 } else {
4941 res = -1;
4942 }
4943 } while (0);
4944
4946
4947 if (valid_config(cfg))
4948 ast_config_destroy(cfg);
4949 if (fdm != MAP_FAILED)
4950 munmap(fdm, fdlen);
4951 if (fd > -1)
4952 close(fd);
4953 SCOPE_EXIT_RTN_VALUE(res, "%s\n", res ? "Failed" : "Success");
4954}
4955#undef STORE_SQL_FMT
4956#undef STORE_SQL_FMT_CAT
4957
4958/*!
4959 * \brief Renames a message in a mailbox folder.
4960 * \param sdir The folder of the message to be renamed.
4961 * \param smsg The index of the message to be renamed.
4962 * \param mailboxuser The user to become the owner of the message after it is renamed. Usually this will be the same as the original owner.
4963 * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
4964 * \param ddir The destination folder for the message to be renamed into
4965 * \param dmsg The destination message for the message to be renamed.
4966 *
4967 * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
4968 * The is usually used to resequence the messages in the mailbox, such as to delete messag index 0, it would be called successively to slide all the other messages down one index.
4969 * But in theory, because the SQL query performs an update on (dir, msgnum, mailboxuser, mailboxcontext) in the database, it should be possible to have the message relocated to another mailbox or context as well.
4970 */
4971static void odbc_rename_message(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
4972{
4973 SQLHSTMT stmt;
4974 char *sql = MAKE_SQL_PTRA("UPDATE %s SET dir=?, msgnum=? WHERE mailboxuser=? AND mailboxcontext=? AND dir=? AND msgnum=?");
4975 char msgnums[20];
4976 char msgnumd[20];
4977 struct odbc_obj *obj;
4978 char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
4979 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
4980 SCOPE_ENTER(3, "sdir: %s smsg: %d user: %s context: %s ddir: %s dmsg: %d\n", sdir, smsg,
4981 mailboxuser, mailboxcontext, ddir, dmsg);
4982
4983 SCOPE_CALL(-1, odbc_delete_message, ddir, dmsg);
4984
4985 obj = ast_odbc_request_obj(odbc_database, 0);
4986 if (!obj) {
4987 SCOPE_EXIT_LOG_RTN(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4988 }
4989
4990 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4991 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
4992
4994 if (!stmt)
4995 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4996 else
4997 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4999
5001 char *src_fn = MAKE_FILE_PTRA(sdir, smsg);
5002 char *dst_fn = MAKE_FILE_PTRA(ddir, dmsg);
5003
5004 ast_trace(-1, "Recordings stored on disk. Renaming '%s' to '%s'\n", src_fn, dst_fn);
5005 ast_filerename(src_fn, dst_fn, NULL);
5006 }
5007
5008 SCOPE_EXIT_RTN("Done.\n");
5009}
5010
5011/*!
5012 * \brief Removes a voicemail message file.
5013 * \param dir the path to the message file.
5014 * \param msgnum the unique number for the message within the mailbox.
5015 *
5016 * Removes the message content file and the information file.
5017 * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
5018 * Typical use is to clean up after a RETRIEVE operation.
5019 * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
5020 * \return zero on success, -1 on error.
5021 */
5022static int odbc_remove_files(char *dir, int msgnum)
5023{
5024 char *fn = MAKE_FILE_PTRA(dir, msgnum);
5025 char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
5026 SCOPE_ENTER(3, "dir: %s msgnum: %d\n", dir, msgnum);
5027
5029 ast_trace(-1, "Audio stored on disk. Keeping '%s' sound files\n", fn);
5030 } else {
5031 ast_trace(-1, "Audio stored in ODBC. Removing '%s' sound files\n", fn);
5032 ast_filedelete(fn, NULL);
5033 }
5034
5035 /* Always remove the information file */
5036 ast_trace(-1, "Removing '%s' information file\n", full_fn);
5037 unlink(full_fn);
5038 SCOPE_EXIT_RTN_VALUE(0, "Done.\n");
5039}
5040#else
5041#ifndef IMAP_STORAGE
5042/*!
5043 * \brief Find all .txt files - even if they are not in sequence from 0000.
5044 * \param vmu
5045 * \param dir
5046 *
5047 * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
5048 *
5049 * \return the count of messages, zero or more.
5050 */
5051static int count_messages(struct ast_vm_user *vmu, char *dir)
5052{
5053
5054 int vmcount = 0;
5055 DIR *vmdir = NULL;
5056 struct dirent *vment = NULL;
5057
5058 if (vm_lock_path(dir))
5059 return ERROR_LOCK_PATH;
5060
5061 if ((vmdir = opendir(dir))) {
5062 while ((vment = readdir(vmdir))) {
5063 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
5064 vmcount++;
5065 }
5066 }
5067 closedir(vmdir);
5068 }
5069 ast_unlock_path(dir);
5070
5071 return vmcount;
5072}
5073
5074/*!
5075 * \brief Renames a message in a mailbox folder.
5076 * \param sfn The path to the mailbox information and data file to be renamed.
5077 * \param dfn The path for where the message data and information files will be renamed to.
5078 *
5079 * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
5080 */
5081static void rename_file(char *sfn, char *dfn)
5082{
5083 char stxt[PATH_MAX];
5084 char dtxt[PATH_MAX];
5085 ast_filerename(sfn, dfn, NULL);
5086 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
5087 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
5088 if (ast_check_realtime("voicemail_data")) {
5089 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
5090 }
5091 rename(stxt, dtxt);
5092}
5093
5094/*!
5095 * \brief Determines the highest message number in use for a given user and mailbox folder.
5096 * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
5097 *
5098 * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
5099 * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
5100 *
5101 * \note Should always be called with a lock already set on dir.
5102 * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
5103 */
5104static int last_message_index(char *dir)
5105{
5106 int x;
5107 unsigned char map[MAXMSGLIMIT] = "";
5108 DIR *msgdir;
5109 struct dirent *msgdirent;
5110 int msgdirint;
5111 char extension[4];
5112 int stopcount = 0;
5113
5114 /* Reading the entire directory into a file map scales better than
5115 * doing a stat repeatedly on a predicted sequence. I suspect this
5116 * is partially due to stat(2) internally doing a readdir(2) itself to
5117 * find each file. */
5118 if (!(msgdir = opendir(dir))) {
5119 return -1;
5120 }
5121
5122 while ((msgdirent = readdir(msgdir))) {
5123 if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
5124 map[msgdirint] = 1;
5125 stopcount++;
5126 ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
5127 }
5128 }
5129 closedir(msgdir);
5130
5131 for (x = 0; x < MAXMSGLIMIT && stopcount; x++) {
5132 stopcount -= map[x];
5133 }
5134
5135 return x - 1;
5136}
5137
5138#endif /* #ifndef IMAP_STORAGE */
5139#endif /* #else of #ifdef ODBC_STORAGE */
5140#ifndef IMAP_STORAGE
5141/*!
5142 * \brief Utility function to copy a file.
5143 * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
5144 * \param outfile The path for which to copy the file to. The directory permissions must allow the creation (or truncation) of the file, and allow for opening the file in write only mode.
5145 *
5146 * When the compiler option HARDLINK_WHEN_POSSIBLE is set, the copy operation will attempt to use the hard link facility instead of copy the file (to save disk space). If the link operation fails, it falls back to the copy operation.
5147 * The copy operation copies up to 4096 bytes at once.
5148 *
5149 * \return zero on success, -1 on error.
5150 */
5151static int copy(char *infile, char *outfile)
5152{
5153 int ifd;
5154 int ofd;
5155 int res = -1;
5156 int len;
5157 char buf[4096];
5158
5159#ifdef HARDLINK_WHEN_POSSIBLE
5160 /* Hard link if possible; saves disk space & is faster */
5161 if (!link(infile, outfile)) {
5162 return 0;
5163 }
5164#endif
5165
5166 if ((ifd = open(infile, O_RDONLY)) < 0) {
5167 ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
5168 return -1;
5169 }
5170
5171 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
5172 ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
5173 close(ifd);
5174 return -1;
5175 }
5176
5177 for (;;) {
5178 int wrlen;
5179
5180 len = read(ifd, buf, sizeof(buf));
5181 if (!len) {
5182 res = 0;
5183 break;
5184 }
5185
5186 if (len < 0) {
5187 ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
5188 break;
5189 }
5190
5191 wrlen = write(ofd, buf, len);
5192 if (errno == ENOMEM || errno == ENOSPC || wrlen != len) {
5193 ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, wrlen, len, strerror(errno));
5194 break;
5195 }
5196 }
5197
5198 close(ifd);
5199 close(ofd);
5200 if (res) {
5201 unlink(outfile);
5202 }
5203
5204 return res;
5205}
5206
5207/*!
5208 * \brief Copies a voicemail information (envelope) file.
5209 * \param frompath
5210 * \param topath
5211 *
5212 * Every voicemail has the data (.wav) file, and the information file.
5213 * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
5214 * This is used by the COPY macro when not using IMAP storage.
5215 */
5216static void copy_plain_file(char *frompath, char *topath)
5217{
5218 char frompath2[PATH_MAX], topath2[PATH_MAX];
5219 struct ast_variable *tmp, *var = NULL;
5220 const char *origmailbox = "", *context = "", *exten = "";
5221 const char *priority = "", *callerchan = "", *callerid = "", *origdate = "";
5222 const char *origtime = "", *category = "", *duration = "";
5223
5224 ast_filecopy(frompath, topath, NULL);
5225 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
5226 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
5227
5228 if (ast_check_realtime("voicemail_data")) {
5229 var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
5230 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
5231 for (tmp = var; tmp; tmp = tmp->next) {
5232 if (!strcasecmp(tmp->name, "origmailbox")) {
5233 origmailbox = tmp->value;
5234 } else if (!strcasecmp(tmp->name, "context")) {
5235 context = tmp->value;
5236 } else if (!strcasecmp(tmp->name, "exten")) {
5237 exten = tmp->value;
5238 } else if (!strcasecmp(tmp->name, "priority")) {
5239 priority = tmp->value;
5240 } else if (!strcasecmp(tmp->name, "callerchan")) {
5241 callerchan = tmp->value;
5242 } else if (!strcasecmp(tmp->name, "callerid")) {
5243 callerid = tmp->value;
5244 } else if (!strcasecmp(tmp->name, "origdate")) {
5245 origdate = tmp->value;
5246 } else if (!strcasecmp(tmp->name, "origtime")) {
5247 origtime = tmp->value;
5248 } else if (!strcasecmp(tmp->name, "category")) {
5249 category = tmp->value;
5250 } else if (!strcasecmp(tmp->name, "duration")) {
5251 duration = tmp->value;
5252 }
5253 }
5254 ast_store_realtime("voicemail_data", "filename", topath, "origmailbox", origmailbox, "context", context, "exten", exten, "priority", priority, "callerchan", callerchan, "callerid", callerid, "origdate", origdate, "origtime", origtime, "category", category, "duration", duration, SENTINEL);
5255 }
5256 copy(frompath2, topath2);
5258}
5259#endif
5260
5261/*!
5262 * \brief Removes the voicemail sound and information file.
5263 * \param file The path to the sound file. This will be the folder and message index, without the extension.
5264 *
5265 * This is used by the DELETE macro when voicemails are stored on the file system.
5266 *
5267 * \return zero on success, -1 on error.
5268 */
5269static int vm_delete(char *file)
5270{
5271 char *txt;
5272 int txtsize = 0;
5273 int res = 0;
5274 SCOPE_ENTER(3, "file: %s\n", file);
5275
5276 txtsize = (strlen(file) + 5)*sizeof(char);
5277 txt = ast_alloca(txtsize);
5278 /* Sprintf here would safe because we alloca'd exactly the right length,
5279 * but trying to eliminate all sprintf's anyhow
5280 */
5281 if (ast_check_realtime("voicemail_data")) {
5282 ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
5283 }
5284 snprintf(txt, txtsize, "%s.txt", file);
5285 ast_trace(-1, "unlinking '%s'\n", txt);
5286 unlink(txt);
5287 ast_trace(-1, "deleting sound files '%s'\n", file);
5288 res = ast_filedelete(file, NULL);
5289 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d\n", res);
5290}
5291
5292static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, const char *category, const char *flag)
5293{
5294 char callerid[256];
5295 char num[12];
5296 char fromdir[256], fromfile[256];
5297 struct ast_config *msg_cfg;
5298 const char *origcallerid, *origtime;
5299 char origcidname[80], origcidnum[80], origdate[80];
5300 int inttime;
5301 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
5302
5303 /* Prepare variables for substitution in email body and subject */
5304 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
5305 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
5306 snprintf(num, sizeof(num), "%d", msgnum);
5307 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
5308 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
5309 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
5310 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
5311 ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
5312 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
5313 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
5314 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
5315 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
5316 pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
5317
5318 /* Retrieve info from VM attribute file */
5319 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
5320 make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
5321 if (strlen(fromfile) < sizeof(fromfile) - 5) {
5322 strcat(fromfile, ".txt");
5323 }
5324 if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
5325 ast_debug(1, "Config load for message text file '%s' failed\n", fromfile);
5326 return;
5327 }
5328
5329 if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
5330 pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
5331 ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
5332 pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
5333 pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
5334 }
5335
5336 if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
5337 struct timeval tv = { inttime, };
5338 struct ast_tm tm;
5339 ast_localtime(&tv, &tm, NULL);
5340 ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5341 pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
5342 }
5343 ast_config_destroy(msg_cfg);
5344}
5345
5346/*!
5347 * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
5348 * \param from The string to work with.
5349 * \param buf The buffer into which to write the modified quoted string.
5350 * \param maxlen Always zero, but see \see ast_str
5351 *
5352 * \return The destination string with quotes wrapped on it (the to field).
5353 */
5354static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
5355{
5356 const char *ptr;
5357
5358 /* We're only ever passing 0 to maxlen, so short output isn't possible */
5359 ast_str_set(buf, maxlen, "\"");
5360 for (ptr = from; *ptr; ptr++) {
5361 if (*ptr == '"' || *ptr == '\\') {
5362 ast_str_append(buf, maxlen, "\\%c", *ptr);
5363 } else {
5364 ast_str_append(buf, maxlen, "%c", *ptr);
5365 }
5366 }
5367 ast_str_append(buf, maxlen, "\"");
5368
5369 return ast_str_buffer(*buf);
5370}
5371
5372/*! \brief
5373 * fill in *tm for current time according to the proper timezone, if any.
5374 * \return tm so it can be used as a function argument.
5375 */
5376static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
5377{
5378 const struct vm_zone *z = NULL;
5379 struct timeval t = ast_tvnow();
5380
5381 /* Does this user have a timezone specified? */
5382 if (!ast_strlen_zero(vmu->zonetag)) {
5383 /* Find the zone in the list */
5385 AST_LIST_TRAVERSE(&zones, z, list) {
5386 if (!strcmp(z->name, vmu->zonetag))
5387 break;
5388 }
5390 }
5391 ast_localtime(&t, tm, z ? z->timezone : NULL);
5392 return tm;
5393}
5394
5395/*!\brief Check if the string would need encoding within the MIME standard, to
5396 * avoid confusing certain mail software that expects messages to be 7-bit
5397 * clean.
5398 */
5399static int check_mime(const char *str)
5400{
5401 for (; *str; str++) {
5402 if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
5403 return 1;
5404 }
5405 }
5406 return 0;
5407}
5408
5409/*!\brief Encode a string according to the MIME rules for encoding strings
5410 * that are not 7-bit clean or contain control characters.
5411 *
5412 * Additionally, if the encoded string would exceed the MIME limit of 76
5413 * characters per line, then the encoding will be broken up into multiple
5414 * sections, separated by a space character, in order to facilitate
5415 * breaking up the associated header across multiple lines.
5416 *
5417 * \param end An expandable buffer for holding the result
5418 * \param maxlen Always zero, but see \see ast_str
5419 * \param start A string to be encoded
5420 * \param preamble The length of the first line already used for this string,
5421 * to ensure that each line maintains a maximum length of 76 chars.
5422 * \param postamble the length of any additional characters appended to the
5423 * line, used to ensure proper field wrapping.
5424 * \retval The encoded string.
5425 */
5426static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
5427{
5428 struct ast_str *tmp = ast_str_alloca(80);
5429 int first_section = 1;
5430
5432 ast_str_set(&tmp, -1, "=?%s?Q?", charset);
5433 for (; *start; start++) {
5434 int need_encoding = 0;
5435 if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
5436 need_encoding = 1;
5437 }
5438 if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
5439 (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
5440 (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
5441 (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
5442 /* Start new line */
5443 ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
5444 ast_str_set(&tmp, -1, "=?%s?Q?", charset);
5445 first_section = 0;
5446 }
5447 if (need_encoding && *start == ' ') {
5448 ast_str_append(&tmp, -1, "_");
5449 } else if (need_encoding) {
5450 ast_str_append(&tmp, -1, "=%hhX", *start);
5451 } else {
5452 ast_str_append(&tmp, -1, "%c", *start);
5453 }
5454 }
5455 ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
5456 return ast_str_buffer(*end);
5457}
5458
5459/*!
5460 * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
5461 * \param p The output file to generate the email contents into.
5462 * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
5463 * \param vmu The voicemail user who is sending the voicemail.
5464 * \param msgnum The message index in the mailbox folder.
5465 * \param context
5466 * \param mailbox The voicemail box to read the voicemail to be notified in this email.
5467 * \param fromfolder
5468 * \param cidnum The caller ID number.
5469 * \param cidname The caller ID name.
5470 * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
5471 * \param attach2
5472 * \param format The message sound file format. i.e. .wav
5473 * \param duration The time of the message content, in seconds.
5474 * \param attach_user_voicemail if 1, the sound file is attached to the email.
5475 * \param chan
5476 * \param category
5477 * \param imap if == 1, indicates the target folder for the email notification to be sent to will be an IMAP mailstore. This causes additional mailbox headers to be set, which would facilitate searching for the email in the destination IMAP folder.
5478 * \param flag, msg_id
5479 *
5480 * The email body, and base 64 encoded attachment (if any) are stored to the file identified by *p. This method does not actually send the email. That is done by invoking the configure 'mailcmd' and piping this generated file into it, or with the sendemail() function.
5481 */
5482static void make_email_file(FILE *p,
5483 char *srcemail,
5484 struct ast_vm_user *vmu,
5485 int msgnum,
5486 char *context,
5487 char *mailbox,
5488 const char *fromfolder,
5489 char *cidnum,
5490 char *cidname,
5491 char *attach,
5492 char *attach2,
5493 char *format,
5494 int duration,
5495 int attach_user_voicemail,
5496 struct ast_channel *chan,
5497 const char *category,
5498 int imap,
5499 const char *flag,
5500 const char *msg_id)
5501{
5502 char date[256];
5503 char host[MAXHOSTNAMELEN] = "";
5504 char who[256];
5505 char bound[256];
5506 char dur[256];
5507 struct ast_tm tm;
5508 char enc_cidnum[256] = "", enc_cidname[256] = "";
5509 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
5510 char *greeting_attachment;
5511 char filename[256];
5512 int first_line;
5513 char *emailsbuf;
5514 char *email;
5515
5516 if (!str1 || !str2) {
5517 ast_free(str1);
5518 ast_free(str2);
5519 return;
5520 }
5521
5522 if (cidnum) {
5523 strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
5524 }
5525 if (cidname) {
5526 strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
5527 }
5528 gethostname(host, sizeof(host) - 1);
5529
5530 if (strchr(srcemail, '@')) {
5531 ast_copy_string(who, srcemail, sizeof(who));
5532 } else {
5533 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
5534 }
5535
5536 greeting_attachment = strrchr(ast_strdupa(attach), '/');
5537 if (greeting_attachment) {
5538 *greeting_attachment++ = '\0';
5539 }
5540
5541 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
5542 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
5543 fprintf(p, "Date: %s" ENDL, date);
5544
5545 /* Set date format for voicemail mail */
5546 ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5547
5549 struct ast_channel *ast;
5550 char *e_fromstring = !ast_strlen_zero(vmu->fromstring) ? vmu->fromstring : fromstring;
5551 if ((ast = ast_dummy_channel_alloc())) {
5552 char *ptr;
5553 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
5554 ast_str_substitute_variables(&str1, 0, ast, e_fromstring);
5555
5556 if (check_mime(ast_str_buffer(str1))) {
5557 first_line = 1;
5558 ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
5559 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5560 *ptr = '\0';
5561 fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
5562 first_line = 0;
5563 /* Substring is smaller, so this will never grow */
5564 ast_str_set(&str2, 0, "%s", ptr + 1);
5565 }
5566 fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
5567 } else {
5568 fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
5569 }
5570 ast = ast_channel_unref(ast);
5571 } else {
5572 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5573 }
5574 } else {
5575 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
5576 }
5577
5578 emailsbuf = ast_strdupa(vmu->email);
5579 fprintf(p, "To:");
5580 first_line = 1;
5581 while ((email = strsep(&emailsbuf, "|"))) {
5582 char *next = emailsbuf;
5583 if (check_mime(vmu->fullname)) {
5584 char *ptr;
5585 ast_str_encode_mime(&str2, 0, vmu->fullname, first_line ? strlen("To: ") : 0, strlen(email) + 3 + (next ? strlen(",") : 0));
5586 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5587 *ptr = '\0';
5588 fprintf(p, " %s" ENDL, ast_str_buffer(str2));
5589 /* Substring is smaller, so this will never grow */
5590 ast_str_set(&str2, 0, "%s", ptr + 1);
5591 }
5592 fprintf(p, " %s <%s>%s" ENDL, ast_str_buffer(str2), email, next ? "," : "");
5593 } else {
5594 fprintf(p, " %s <%s>%s" ENDL, ast_str_quote(&str2, 0, vmu->fullname), email, next ? "," : "");
5595 }
5596 first_line = 0;
5597 }
5598
5599 if (msgnum <= -1) {
5600 fprintf(p, "Subject: New greeting '%s' on %s." ENDL, greeting_attachment, date);
5602 char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
5603 struct ast_channel *ast;
5604 if ((ast = ast_dummy_channel_alloc())) {
5605 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5606 ast_str_substitute_variables(&str1, 0, ast, e_subj);
5607 if (check_mime(ast_str_buffer(str1))) {
5608 char *ptr;
5609 first_line = 1;
5610 ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
5611 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5612 *ptr = '\0';
5613 fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5614 first_line = 0;
5615 /* Substring is smaller, so this will never grow */
5616 ast_str_set(&str2, 0, "%s", ptr + 1);
5617 }
5618 fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5619 } else {
5620 fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
5621 }
5622 ast = ast_channel_unref(ast);
5623 } else {
5624 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5625 }
5626 } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
5627 if (ast_strlen_zero(flag)) {
5628 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
5629 } else {
5630 fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
5631 }
5632 } else {
5633 if (ast_strlen_zero(flag)) {
5634 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
5635 } else {
5636 fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
5637 }
5638 }
5639
5640 fprintf(p, "Message-ID: <Asterisk-%d-%u-%s-%d@%s>" ENDL, msgnum + 1,
5641 (unsigned int) ast_random(), mailbox, (int) getpid(), host);
5642 if (imap) {
5643 /* additional information needed for IMAP searching */
5644 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
5645 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
5646 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
5647 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
5648#ifdef IMAP_STORAGE
5649 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
5650#else
5651 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
5652#endif
5653 /* flag added for Urgent */
5654 fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, S_OR(flag, ""));
5655 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan ? ast_channel_priority(chan) : 0);
5656 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
5657 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
5658 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
5659 if (!ast_strlen_zero(category)) {
5660 fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
5661 } else {
5662 fprintf(p, "X-Asterisk-VM-Category: " ENDL);
5663 }
5664 fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
5665 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
5666 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
5667 fprintf(p, "X-Asterisk-VM-Message-ID: %s" ENDL, msg_id);
5668 }
5669 if (!ast_strlen_zero(cidnum)) {
5670 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
5671 }
5672 if (!ast_strlen_zero(cidname)) {
5673 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
5674 }
5675 fprintf(p, "MIME-Version: 1.0" ENDL);
5676 if (attach_user_voicemail) {
5677 /* Something unique. */
5678 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%u", msgnum + 1, mailbox,
5679 (int) getpid(), (unsigned int) ast_random());
5680
5681 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
5682 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
5683 fprintf(p, "--%s" ENDL, bound);
5684 }
5685 fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
5686 if (msgnum <= -1) {
5687 fprintf(p, "This message is to let you know that your greeting '%s' was changed on %s." ENDL
5688 "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL,
5689 greeting_attachment, date);
5690 } else if (emailbody || vmu->emailbody) {
5691 char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
5692 struct ast_channel *ast;
5693 if ((ast = ast_dummy_channel_alloc())) {
5694 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5695 ast_str_substitute_variables(&str1, 0, ast, e_body);
5696#ifdef IMAP_STORAGE
5697 {
5698 /* Convert body to native line terminators for IMAP backend */
5699 char *line = ast_str_buffer(str1), *next;
5700 do {
5701 /* Terminate line before outputting it to the file */
5702 if ((next = strchr(line, '\n'))) {
5703 *next++ = '\0';
5704 }
5705 fprintf(p, "%s" ENDL, line);
5706 line = next;
5707 } while (!ast_strlen_zero(line));
5708 }
5709#else
5710 fprintf(p, "%s" ENDL, ast_str_buffer(str1));
5711#endif
5712 ast = ast_channel_unref(ast);
5713 } else {
5714 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5715 }
5716 } else {
5717 if (strcmp(vmu->mailbox, mailbox)) {
5718 /* Forwarded type */
5719 struct ast_config *msg_cfg;
5720 const char *v;
5721 int inttime;
5722 char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
5723 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
5724 /* Retrieve info from VM attribute file */
5725 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
5726 make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
5727 if (strlen(fromfile) < sizeof(fromfile) - 5) {
5728 strcat(fromfile, ".txt");
5729 }
5730 if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
5731 if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
5732 ast_copy_string(origcallerid, v, sizeof(origcallerid));
5733 }
5734
5735 /* You might be tempted to do origdate, except that a) it's in the wrong
5736 * format, and b) it's missing for IMAP recordings. */
5737 if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
5738 struct timeval tv = { inttime, };
5739 struct ast_tm tm;
5740 ast_localtime(&tv, &tm, NULL);
5741 ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5742 }
5743 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
5744 " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
5745 "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
5746 " chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
5747 msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
5748 date, origcallerid, origdate);
5749 ast_config_destroy(msg_cfg);
5750 } else {
5751 goto plain_message;
5752 }
5753 } else {
5754plain_message:
5755 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
5756 "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
5757 "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
5758 ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
5759 (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
5760 }
5761 }
5762
5763 if (imap || attach_user_voicemail) {
5764 if (!ast_strlen_zero(attach2)) {
5765 snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
5766 ast_debug(5, "creating second attachment filename %s\n", filename);
5767 add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
5768 snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
5769 ast_debug(5, "creating attachment filename %s\n", filename);
5770 add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
5771 } else {
5772 snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
5773 ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
5774 add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
5775 }
5776 }
5777 ast_free(str1);
5778 ast_free(str2);
5779}
5780
5781static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
5782{
5783 char fname[PATH_MAX] = "";
5784 char sox_gain_tmpdir[PATH_MAX];
5785 char *file_to_delete = NULL, *dir_to_delete = NULL;
5786 int res;
5787 char altfname[PATH_MAX] = "";
5788 int altused = 0;
5789 char altformat[80] = "";
5790 char *c = NULL;
5791
5792 /* Eww. We want formats to tell us their own MIME type */
5793 char *mime_type = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
5794
5795 /* Users of multiple file formats need special attention. */
5796 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
5797 if (!ast_file_is_readable(fname)) {
5798 ast_copy_string(altformat, vmfmts, sizeof(altformat));
5799 c = strchr(altformat, '|');
5800 if (c) {
5801 *c = '\0';
5802 }
5803 ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s - trying first/alternate format %s\n", fname, strerror(errno), altformat);
5804 snprintf(altfname, sizeof(altfname), "%s.%s", attach, altformat);
5805 if (!ast_file_is_readable(altfname)) {
5806 ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s - alternate format %s failure\n", altfname, strerror(errno), altformat);
5807 } else {
5808 altused = 1;
5809 }
5810 }
5811
5812 /* This 'while' loop will only execute once. We use it so that we can 'break' */
5813 while (vmu->volgain < -.001 || vmu->volgain > .001 || altused) {
5814 char tmpdir[PATH_MAX];
5815
5816 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
5817
5818 res = snprintf(sox_gain_tmpdir, sizeof(sox_gain_tmpdir), "%s/vm-gain-XXXXXX", tmpdir);
5819 if (res >= sizeof(sox_gain_tmpdir)) {
5820 ast_log(LOG_ERROR, "Failed to create temporary directory path %s: Out of buffer space\n", tmpdir);
5821 break;
5822 }
5823
5824 if (mkdtemp(sox_gain_tmpdir)) {
5825 int soxstatus = 0;
5826 char sox_gain_cmd[PATH_MAX];
5827
5828 ast_debug(3, "sox_gain_tmpdir: %s\n", sox_gain_tmpdir);
5829
5830 /* Save for later */
5831 dir_to_delete = sox_gain_tmpdir;
5832
5833 res = snprintf(fname, sizeof(fname), "%s/output.%s", sox_gain_tmpdir, format);
5834 if (res >= sizeof(fname)) {
5835 ast_log(LOG_ERROR, "Failed to create filename buffer for %s/output.%s: Too long\n", sox_gain_tmpdir, format);
5836 break;
5837 }
5838
5839 if (!altused) {
5840 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s",
5841 vmu->volgain, attach, format, fname);
5842 } else {
5843 if (!strcasecmp(format, "wav")) {
5844 if (vmu->volgain < -.001 || vmu->volgain > .001) {
5845 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s -e signed-integer -b 16 %s",
5846 vmu->volgain, attach, altformat, fname);
5847 } else {
5848 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox %s.%s -e signed-integer -b 16 %s",
5849 attach, altformat, fname);
5850 }
5851 } else {
5852 if (vmu->volgain < -.001 || vmu->volgain > .001) {
5853 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s",
5854 vmu->volgain, attach, altformat, fname);
5855 } else {
5856 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox %s.%s %s",
5857 attach, altformat, fname);
5858 }
5859 }
5860 }
5861
5862 if (res >= sizeof(sox_gain_cmd)) {
5863 ast_log(LOG_ERROR, "Failed to generate sox command, out of buffer space\n");
5864 break;
5865 }
5866
5867 soxstatus = ast_safe_system(sox_gain_cmd);
5868 if (!soxstatus) {
5869 /* Save for later */
5870 file_to_delete = fname;
5871 ast_debug(3, "VOLGAIN: Stored at: %s - Level: %.4f - Mailbox: %s\n", fname, vmu->volgain, mailbox);
5872 } else {
5873 ast_log(LOG_WARNING, "Sox failed to re-encode %s: %s (have you installed support for all sox file formats?)\n",
5874 fname,
5875 soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
5876 ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
5877 }
5878 }
5879
5880 break;
5881 }
5882
5883 if (!file_to_delete) {
5884 res = snprintf(fname, sizeof(fname), "%s.%s", attach, format);
5885 if (res >= sizeof(fname)) {
5886 ast_log(LOG_ERROR, "Failed to create filename buffer for %s.%s: Too long\n", attach, format);
5887 return -1;
5888 }
5889 }
5890
5891 fprintf(p, "--%s" ENDL, bound);
5892 if (msgnum > -1)
5893 fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, mime_type, format, filename);
5894 else
5895 fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, mime_type, format, greeting_attachment, format);
5896 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
5897 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
5898 if (msgnum > -1)
5899 fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
5900 else
5901 fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
5903 if (last)
5904 fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
5905
5906 if (file_to_delete) {
5907 unlink(file_to_delete);
5908 }
5909
5910 if (dir_to_delete) {
5911 rmdir(dir_to_delete);
5912 }
5913
5914 return 0;
5915}
5916
5917static int sendmail(char *srcemail,
5918 struct ast_vm_user *vmu,
5919 int msgnum,
5920 char *context,
5921 char *mailbox,
5922 const char *fromfolder,
5923 char *cidnum,
5924 char *cidname,
5925 char *attach,
5926 char *attach2,
5927 char *format,
5928 int duration,
5929 int attach_user_voicemail,
5930 struct ast_channel *chan,
5931 const char *category,
5932 const char *flag,
5933 const char *msg_id)
5934{
5935 FILE *p = NULL;
5936 char tmp[80] = "/tmp/astmail-XXXXXX";
5937 char tmp2[256];
5938 char *stringp;
5939
5940 if (vmu && ast_strlen_zero(vmu->email)) {
5941 ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
5942 return(0);
5943 }
5944
5945 /* Mail only the first format */
5946 format = ast_strdupa(format);
5947 stringp = format;
5948 strsep(&stringp, "|");
5949
5950 if (!strcmp(format, "wav49"))
5951 format = "WAV";
5952 ast_debug(3, "Attaching file '%s', format '%s', uservm is '%d', global is %u\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
5953 /* Make a temporary file instead of piping directly to sendmail, in case the mail
5954 command hangs */
5955 if ((p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask)) == NULL) {
5956 ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
5957 return -1;
5958 } else {
5959 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, fromfolder, cidnum, cidname, attach, attach2, format, duration, attach_user_voicemail, chan, category, 0, flag, msg_id);
5960 fclose(p);
5961 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
5962 ast_safe_system(tmp2);
5963 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
5964 }
5965 return 0;
5966}
5967
5968static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category, const char *flag)
5969{
5970 char enc_cidnum[256], enc_cidname[256];
5971 char date[256];
5972 char host[MAXHOSTNAMELEN] = "";
5973 char who[256];
5974 char dur[PATH_MAX];
5975 char tmp[80] = "/tmp/astmail-XXXXXX";
5976 char tmp2[PATH_MAX];
5977 struct ast_tm tm;
5978 FILE *p;
5979 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
5980
5981 if (!str1 || !str2) {
5982 ast_free(str1);
5983 ast_free(str2);
5984 return -1;
5985 }
5986
5987 if (cidnum) {
5988 strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
5989 }
5990 if (cidname) {
5991 strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
5992 }
5993
5994 if ((p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask)) == NULL) {
5995 ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
5996 ast_free(str1);
5997 ast_free(str2);
5998 return -1;
5999 }
6000 gethostname(host, sizeof(host)-1);
6001 if (strchr(srcemail, '@')) {
6002 ast_copy_string(who, srcemail, sizeof(who));
6003 } else {
6004 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
6005 }
6006 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
6007 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
6008 fprintf(p, "Date: %s\n", date);
6009
6010 /* Reformat for custom pager format */
6011 ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
6012
6014 struct ast_channel *ast;
6015 if ((ast = ast_dummy_channel_alloc())) {
6016 char *ptr;
6017 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
6019
6020 if (check_mime(ast_str_buffer(str1))) {
6021 int first_line = 1;
6022 ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
6023 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
6024 *ptr = '\0';
6025 fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
6026 first_line = 0;
6027 /* Substring is smaller, so this will never grow */
6028 ast_str_set(&str2, 0, "%s", ptr + 1);
6029 }
6030 fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
6031 } else {
6032 fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
6033 }
6034 ast = ast_channel_unref(ast);
6035 } else {
6036 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
6037 }
6038 } else {
6039 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
6040 }
6041
6042 if (check_mime(vmu->fullname)) {
6043 int first_line = 1;
6044 char *ptr;
6045 ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
6046 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
6047 *ptr = '\0';
6048 fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
6049 first_line = 0;
6050 /* Substring is smaller, so this will never grow */
6051 ast_str_set(&str2, 0, "%s", ptr + 1);
6052 }
6053 fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
6054 } else {
6055 fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
6056 }
6057
6059 struct ast_channel *ast;
6060 if ((ast = ast_dummy_channel_alloc())) {
6061 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
6063 if (check_mime(ast_str_buffer(str1))) {
6064 int first_line = 1;
6065 char *ptr;
6066 ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
6067 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
6068 *ptr = '\0';
6069 fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
6070 first_line = 0;
6071 /* Substring is smaller, so this will never grow */
6072 ast_str_set(&str2, 0, "%s", ptr + 1);
6073 }
6074 fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
6075 } else {
6076 fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
6077 }
6078 ast = ast_channel_unref(ast);
6079 } else {
6080 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
6081 }
6082 } else {
6083 if (ast_strlen_zero(flag)) {
6084 fprintf(p, "Subject: New VM" ENDL);
6085 } else {
6086 fprintf(p, "Subject: New %s VM" ENDL, flag);
6087 }
6088 }
6089
6090 /* End of headers */
6091 fputs(ENDL, p);
6092
6093 if (pagerbody) {
6094 struct ast_channel *ast;
6095 if ((ast = ast_dummy_channel_alloc())) {
6096 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
6098 fprintf(p, "%s" ENDL, ast_str_buffer(str1));
6099 ast = ast_channel_unref(ast);
6100 } else {
6101 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
6102 }
6103 } else {
6104 fprintf(p, "New %s long %s msg in box %s\n"
6105 "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
6106 }
6107
6108 fclose(p);
6109 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
6110 ast_safe_system(tmp2);
6111 ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
6112 ast_free(str1);
6113 ast_free(str2);
6114 return 0;
6115}
6116
6117/*!
6118 * \brief Gets the current date and time, as formatted string.
6119 * \param s The buffer to hold the output formatted date.
6120 * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
6121 *
6122 * The date format string used is "%a %b %e %r UTC %Y".
6123 *
6124 * \return zero on success, -1 on error.
6125 */
6126static int get_date(char *s, int len)
6127{
6128 struct ast_tm tm;
6129 struct timeval t = ast_tvnow();
6130
6131 ast_localtime(&t, &tm, "UTC");
6132
6133 return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
6134}
6135
6136static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
6137{
6138 int res;
6139 char fn[PATH_MAX];
6140 char dest[PATH_MAX];
6141
6142 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
6143
6144 if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
6145 ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
6146 return -1;
6147 }
6148
6149 RETRIEVE(fn, -1, ext, context);
6150 if (ast_fileexists(fn, NULL, NULL) > 0) {
6151 res = ast_stream_and_wait(chan, fn, ecodes);
6152 if (res) {
6153 DISPOSE(fn, -1);
6154 return res;
6155 }
6156 } else {
6157 /* Dispose just in case */
6158 DISPOSE(fn, -1);
6159 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
6160 if (res)
6161 return res;
6162 res = ast_say_digit_str(chan, ext, ecodes, ast_channel_language(chan));
6163 if (res)
6164 return res;
6165 }
6166 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
6167 return res;
6168}
6169
6170static void free_zone(struct vm_zone *z)
6171{
6172 ast_free(z);
6173}
6174
6175#ifdef ODBC_STORAGE
6176#define COUNT_MSGS_SQL_FMT "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'"
6177static int count_messages_in_folder(struct odbc_obj *odbc, const char *context, const char *mailbox, const char *folder, int *messages)
6178{
6179 int res;
6180 char sql[sizeof(COUNT_MSGS_SQL_FMT) + odbc_table_len + strlen(VM_SPOOL_DIR)
6181 + strlen(context) + strlen(mailbox) + strlen(folder)];
6182 char rowdata[20];
6183 SQLHSTMT stmt = NULL;
6184 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
6185 SCOPE_ENTER(3, "context: %s mb: %s folder: %s", context, mailbox, folder);
6186
6187 if (!messages) {
6188 SCOPE_EXIT_RTN_VALUE(0, "No messages pointer\n");
6189 }
6190
6191 snprintf(sql, sizeof(sql), COUNT_MSGS_SQL_FMT, odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
6192 if (!(stmt = ast_odbc_prepare_and_execute(odbc, generic_prepare, &gps))) {
6193 SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
6194 }
6195 res = SQLFetch(stmt);
6196 if (!SQL_SUCCEEDED(res)) {
6197 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6198 SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
6199 }
6200 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
6201 if (!SQL_SUCCEEDED(res)) {
6202 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6203 SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
6204 }
6205
6206 *messages = atoi(rowdata);
6207 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6208
6209 SCOPE_EXIT_RTN_VALUE(0, "messages: %d\n", *messages);
6210}
6211#undef COUNT_MSGS_SQL_FMT
6212
6213static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
6214{
6215 char tmp[PATH_MAX] = "";
6216 struct odbc_obj *obj;
6217 char *context;
6218 SCOPE_ENTER(3, "mb: %s", mailbox);
6219
6220 if (newmsgs)
6221 *newmsgs = 0;
6222 if (oldmsgs)
6223 *oldmsgs = 0;
6224 if (urgentmsgs)
6225 *urgentmsgs = 0;
6226
6227 /* If no mailbox, return immediately */
6228 if (ast_strlen_zero(mailbox)) {
6229 SCOPE_EXIT_RTN_VALUE(0, "No mailbox\n");
6230 }
6231
6232 ast_copy_string(tmp, mailbox, sizeof(tmp));
6233
6234 if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
6235 int u, n, o;
6236 char *next, *remaining = tmp;
6237 while ((next = strsep(&remaining, " ,"))) {
6238 if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
6239 SCOPE_EXIT_RTN_VALUE(-1, "Failed to obtain message count for mailbox %s\n", next);
6240 }
6241 if (urgentmsgs) {
6242 *urgentmsgs += u;
6243 }
6244 if (newmsgs) {
6245 *newmsgs += n;
6246 }
6247 if (oldmsgs) {
6248 *oldmsgs += o;
6249 }
6250 }
6251 SCOPE_EXIT_RTN_VALUE(0, "Urgent: %d New: %d Old: %d\n", urgentmsgs ? *urgentmsgs : 0, newmsgs ? *newmsgs : 0, oldmsgs ? *oldmsgs : 0);
6252 }
6253
6254 context = strchr(tmp, '@');
6255 if (context) {
6256 *context = '\0';
6257 context++;
6258 } else
6259 context = "default";
6260
6261 obj = ast_odbc_request_obj(odbc_database, 0);
6262 if (!obj) {
6263 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
6264 }
6265
6266 if (count_messages_in_folder(obj, context, tmp, "INBOX", newmsgs)
6267 || count_messages_in_folder(obj, context, tmp, "Old", oldmsgs)
6268 || count_messages_in_folder(obj, context, tmp, "Urgent", urgentmsgs)) {
6269 ast_log(AST_LOG_WARNING, "Failed to obtain message count for mailbox %s@%s\n",
6270 tmp, context);
6271 }
6272
6274 SCOPE_EXIT_RTN_VALUE(0, "Urgent: %d New: %d Old: %d\n", urgentmsgs ? *urgentmsgs : 0, newmsgs ? *newmsgs : 0, oldmsgs ? *oldmsgs : 0);
6275}
6276
6277/*!
6278 * \brief Gets the number of messages that exist in a mailbox folder.
6279 * \param mailbox_id
6280 * \param folder
6281 *
6282 * This method is used when ODBC backend is used.
6283 * \return The number of messages in this mailbox folder (zero or more).
6284 */
6285#define MSGCOUNT_SQL_FMT_INBOX "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/INBOX' OR dir = '%s%s/%s/Urgent'"
6286#define MSGCOUNT_SQL_FMT "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'"
6287static int messagecount(const char *mailbox_id, const char *folder)
6288{
6289 struct odbc_obj *obj = NULL;
6290 char *context;
6291 char *mailbox;
6292 int nummsgs = 0;
6293 int res;
6294 SQLHSTMT stmt = NULL;
6295 char rowdata[20];
6296 struct generic_prepare_struct gps = { .argc = 0 };
6297 SCOPE_ENTER(3, "mb: %s folder: %s", mailbox_id, folder);
6298
6299 /* If no mailbox, return immediately */
6300 if (ast_strlen_zero(mailbox_id)
6301 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
6302 SCOPE_EXIT_RTN_VALUE(0, "Couldn't parse mailbox\n");
6303 }
6304
6305 if (ast_strlen_zero(folder)) {
6306 folder = "INBOX";
6307 }
6308
6309 obj = ast_odbc_request_obj(odbc_database, 0);
6310 if (!obj) {
6311 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
6312 }
6313
6314 if (!strcmp(folder, "INBOX")) {
6315 res = ast_asprintf(&gps.sql, MSGCOUNT_SQL_FMT_INBOX, odbc_table, VM_SPOOL_DIR, context, mailbox, VM_SPOOL_DIR, context, mailbox);
6316 } else {
6317 res = ast_asprintf(&gps.sql, MSGCOUNT_SQL_FMT, odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
6318 }
6319 if (res <= 0) {
6320 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "Failed to allocate memory for SQL statement for '%s'!\n", odbc_database);
6321 }
6322 ast_trace(-1, "SQL: %s\n", gps.sql);
6323
6325 if (!stmt) {
6326 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", gps.sql);
6327 goto bail;
6328 }
6329 res = SQLFetch(stmt);
6330 if (!SQL_SUCCEEDED(res)) {
6331 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", gps.sql);
6332 goto bail_with_handle;
6333 }
6334 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
6335 if (!SQL_SUCCEEDED(res)) {
6336 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", gps.sql);
6337 goto bail_with_handle;
6338 }
6339 nummsgs = atoi(rowdata);
6340
6341bail_with_handle:
6342 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6343
6344bail:
6346 ast_free(gps.sql);
6347 SCOPE_EXIT_RTN_VALUE(nummsgs, "Messages: %d\n", nummsgs);
6348}
6349#undef MSGCOUNT_SQL_FMT
6350#undef MSGCOUNT_SQL_FMT_INBOX
6351
6352/*!
6353 * \brief Determines if the given folder has messages.
6354 * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
6355 *
6356 * This function is used when the mailbox is stored in an ODBC back end.
6357 * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
6358 * \return 1 if the folder has one or more messages. zero otherwise.
6359 */
6360static int has_voicemail(const char *mailboxes, const char *folder)
6361{
6362 char *parse;
6363 char *mailbox;
6364
6365 parse = ast_strdupa(mailboxes);
6366 while ((mailbox = strsep(&parse, ",&"))) {
6367 if (messagecount(mailbox, folder)) {
6368 return 1;
6369 }
6370 }
6371 return 0;
6372}
6373#endif
6374#ifndef IMAP_STORAGE
6375/*!
6376 * \brief Copies a message from one mailbox to another.
6377 * \param chan
6378 * \param vmu
6379 * \param imbox
6380 * \param msgnum
6381 * \param duration
6382 * \param recip
6383 * \param fmt
6384 * \param dir
6385 * \param flag, dest_folder
6386 *
6387 * This is only used by file storage based mailboxes.
6388 *
6389 * \return zero on success, -1 on error.
6390 */
6391static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum,
6392 long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag,
6393 const char *dest_folder)
6394{
6395 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
6396 const char *frombox = mbox(vmu, imbox);
6397 const char *userfolder;
6398 int recipmsgnum;
6399 int res = 0;
6400 SCOPE_ENTER(3, "mb: %s imb: %d msgnum: %d recip: %s dir: %s dest_folder: %s",
6401 vmu->mailbox, imbox, msgnum, recip->mailbox, dir, dest_folder);
6402
6403 ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
6404
6405 if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
6406 userfolder = "Urgent";
6407 } else if (!ast_strlen_zero(dest_folder)) {
6408 userfolder = dest_folder;
6409 } else {
6410 userfolder = "INBOX";
6411 }
6412
6413 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
6414 ast_trace(-1, "todir: %s\n", todir);
6415
6416 if (!dir)
6417 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
6418 else
6419 ast_copy_string(fromdir, dir, sizeof(fromdir));
6420
6421 ast_trace(-1, "fromdir: %s\n", fromdir);
6422
6423 make_file(frompath, sizeof(frompath), fromdir, msgnum);
6424 ast_trace(-1, "frompath: %s\n", frompath);
6425
6426 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
6427 ast_trace(-1, "todir: %s\n", todir);
6428
6429 if (vm_lock_path(todir)) {
6431 }
6432
6433 recipmsgnum = LAST_MSG_INDEX(todir) + 1;
6434 ast_trace(-1, "recip msgnum: %d\n", recipmsgnum);
6435
6436 if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
6437 int exists = 0;
6438
6439 make_file(topath, sizeof(topath), todir, recipmsgnum);
6440 ast_trace(-1, "topath: %s\n", topath);
6441
6442 exists = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, fromdir, msgnum, frompath, chan ? ast_channel_language(chan) : "");
6443 if (exists) {
6444 SCOPE_CALL(-1, COPY, fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
6445 } else {
6446 SCOPE_CALL(-1, copy_plain_file,frompath, topath);
6447 SCOPE_CALL(-1, STORE, todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL, NULL);
6448 SCOPE_CALL(-1, vm_delete, topath);
6449
6450 }
6451 } else {
6452 ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
6453 res = -1;
6454 }
6455 ast_unlock_path(todir);
6456 if (chan) {
6457 struct ast_party_caller *caller = ast_channel_caller(chan);
6458 notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
6459 S_COR(caller->id.number.valid, caller->id.number.str, NULL),
6460 S_COR(caller->id.name.valid, caller->id.name.str, NULL),
6461 flag);
6462 }
6463
6464 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d\n", res);
6465}
6466#endif
6467#if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
6468
6469static int messagecount(const char *mailbox_id, const char *folder)
6470{
6471 char *context;
6472 char *mailbox;
6473
6474 if (ast_strlen_zero(mailbox_id)
6475 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
6476 return 0;
6477 }
6478
6479 return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
6480}
6481
6482static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
6483{
6484 DIR *dir;
6485 struct dirent *de;
6486 char fn[256];
6487 int ret = 0;
6488 struct alias_mailbox_mapping *mapping;
6489 char *c;
6490 char *m;
6491
6492 /* If no mailbox, return immediately */
6494 return 0;
6495
6496 if (ast_strlen_zero(folder))
6497 folder = "INBOX";
6499 context = "default";
6500
6501 c = (char *)context;
6502 m = (char *)mailbox;
6503
6505 char tmp[MAX_VM_MAILBOX_LEN];
6506
6507 snprintf(tmp, MAX_VM_MAILBOX_LEN, "%s@%s", mailbox, context);
6509 if (mapping) {
6510 separate_mailbox(ast_strdupa(mapping->mailbox), &m, &c);
6511 ao2_ref(mapping, -1);
6512 }
6513 }
6514
6515 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, c, m, folder);
6516
6517 if (!(dir = opendir(fn)))
6518 return 0;
6519
6520 while ((de = readdir(dir))) {
6521 if (!strncasecmp(de->d_name, "msg", 3)) {
6522 if (shortcircuit) {
6523 ret = 1;
6524 break;
6525 } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
6526 ret++;
6527 }
6528 }
6529 }
6530
6531 closedir(dir);
6532
6533 return ret;
6534}
6535
6536/*!
6537 * \brief Determines if the given folder has messages.
6538 * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
6539 * \param folder the folder to look in
6540 *
6541 * This function is used when the mailbox is stored in a filesystem back end.
6542 * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
6543 * \return 1 if the folder has one or more messages. zero otherwise.
6544 */
6545static int has_voicemail(const char *mailbox, const char *folder)
6546{
6547 char tmp[256], *tmp2 = tmp, *box, *context;
6548 ast_copy_string(tmp, mailbox, sizeof(tmp));
6549 if (ast_strlen_zero(folder)) {
6550 folder = "INBOX";
6551 }
6552 while ((box = strsep(&tmp2, ",&"))) {
6553 if ((context = strchr(box, '@')))
6554 *context++ = '\0';
6555 else
6556 context = "default";
6557 if (__has_voicemail(context, box, folder, 1))
6558 return 1;
6559 /* If we are checking INBOX, we should check Urgent as well */
6560 if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
6561 return 1;
6562 }
6563 }
6564 return 0;
6565}
6566
6567/*!
6568 * \brief Check the given mailbox's message count.
6569 * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
6570 * \param urgentmsgs urgent message count.
6571 * \param newmsgs new message count.
6572 * \param oldmsgs old message count pointer
6573 * \return -1 if error occurred, 0 otherwise.
6574 */
6575static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
6576{
6577 char tmp[256];
6578 char *context;
6579
6580 /* If no mailbox, return immediately */
6581 if (ast_strlen_zero(mailbox)) {
6582 return 0;
6583 }
6584
6585 if (newmsgs) {
6586 *newmsgs = 0;
6587 }
6588 if (oldmsgs) {
6589 *oldmsgs = 0;
6590 }
6591 if (urgentmsgs) {
6592 *urgentmsgs = 0;
6593 }
6594
6595 if (strchr(mailbox, ',')) {
6596 int tmpnew, tmpold, tmpurgent;
6597 char *mb, *cur;
6598
6599 ast_copy_string(tmp, mailbox, sizeof(tmp));
6600 mb = tmp;
6601 while ((cur = strsep(&mb, ", "))) {
6602 if (!ast_strlen_zero(cur)) {
6603 if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL)) {
6604 return -1;
6605 } else {
6606 if (newmsgs) {
6607 *newmsgs += tmpnew;
6608 }
6609 if (oldmsgs) {
6610 *oldmsgs += tmpold;
6611 }
6612 if (urgentmsgs) {
6613 *urgentmsgs += tmpurgent;
6614 }
6615 }
6616 }
6617 }
6618 return 0;
6619 }
6620
6621 ast_copy_string(tmp, mailbox, sizeof(tmp));
6622
6623 if ((context = strchr(tmp, '@'))) {
6624 *context++ = '\0';
6625 } else {
6626 context = "default";
6627 }
6628
6629 if (newmsgs) {
6630 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
6631 }
6632 if (oldmsgs) {
6633 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
6634 }
6635 if (urgentmsgs) {
6636 *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
6637 }
6638
6639 return 0;
6640}
6641
6642#endif
6643
6644/* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
6645static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
6646{
6647 int urgentmsgs = 0;
6648 int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
6649 if (newmsgs) {
6650 *newmsgs += urgentmsgs;
6651 }
6652 return res;
6653}
6654
6655static void run_externnotify(const char *context, const char *extension, const char *flag)
6656{
6657 char arguments[255];
6658 char ext_context[256] = "";
6659 int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
6660 struct ast_smdi_mwi_message *mwi_msg;
6661
6663 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
6664 else
6665 ast_copy_string(ext_context, extension, sizeof(ext_context));
6666
6667 if (smdi_iface) {
6668 if (ast_app_has_voicemail(ext_context, NULL))
6670 else
6672
6674 ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
6675 if (!strncmp(mwi_msg->cause, "INV", 3))
6676 ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
6677 else if (!strncmp(mwi_msg->cause, "BLK", 3))
6678 ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
6679 ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
6680 ao2_ref(mwi_msg, -1);
6681 } else {
6682 ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
6683 }
6684 }
6685
6687 if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
6688 ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
6689 } else {
6690 snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
6691 externnotify, S_OR(context, "\"\""),
6692 extension, newvoicemails,
6693 oldvoicemails, urgentvoicemails);
6694 ast_debug(1, "Executing %s\n", arguments);
6695 ast_safe_system(arguments);
6696 }
6697 }
6698}
6699
6700/*!
6701 * \brief Variables used for saving a voicemail.
6702 *
6703 * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
6704 */
6705struct leave_vm_options {
6706 unsigned int flags;
6707 signed char record_gain;
6708 char *exitcontext;
6709 char *beeptone;
6710};
6711
6712static void generate_msg_id(char *dst)
6713{
6714 /* msg id is time of msg_id generation plus an incrementing value
6715 * called each time a new msg_id is generated. This should achieve uniqueness,
6716 * but only in single system solutions.
6717 */
6718 unsigned int unique_counter = ast_atomic_fetchadd_int(&msg_id_incrementor, +1);
6719 snprintf(dst, MSG_ID_LEN, "%ld-%08x", (long) time(NULL), unique_counter);
6720}
6721
6722/*!
6723 * \internal
6724 * \brief Creates a voicemail based on a specified file to a mailbox.
6725 * \param recdata A vm_recording_data containing filename and voicemail txt info.
6726 * \retval -1 failure
6727 * \retval 0 success
6728 *
6729 * This is installed to the app.h voicemail functions and accommodates all voicemail
6730 * storage methods. It should probably be broken out along with leave_voicemail at
6731 * some point in the future.
6732 *
6733 * This function currently only works for a single recipient and only uses the format
6734 * specified in recording_ext.
6735 */
6737{
6738 /* voicemail recipient structure */
6739 struct ast_vm_user *recipient; /* points to svm once it's been created */
6740 struct ast_vm_user svm; /* struct storing the voicemail recipient */
6741
6742 /* File paths */
6743 char tmpdir[PATH_MAX]; /* directory temp files are stored in */
6744 char tmptxtfile[PATH_MAX]; /* tmp file for voicemail txt file */
6745 char desttxtfile[PATH_MAX]; /* final destination for txt file */
6746 char tmpaudiofile[PATH_MAX]; /* tmp file where audio is stored */
6747 char dir[PATH_MAX]; /* destination for tmp files on completion */
6748 char destination[PATH_MAX]; /* destination with msgXXXX. Basically <dir>/msgXXXX */
6749
6750 /* stuff that only seems to be needed for IMAP */
6751 #ifdef IMAP_STORAGE
6752 struct vm_state *vms = NULL;
6753 char ext_context[256] = "";
6754 int newmsgs = 0;
6755 int oldmsgs = 0;
6756 #endif
6757
6758 /* miscellaneous operational variables */
6759 int res = 0; /* Used to store error codes from functions */
6760 int txtdes /* File descriptor for the text file used to write the voicemail info */;
6761 FILE *txt; /* FILE pointer to text file used to write the voicemail info */
6762 char date[256]; /* string used to hold date of the voicemail (only used for ODBC) */
6763 int msgnum; /* the 4 digit number designated to the voicemail */
6764 int duration = 0; /* Length of the audio being recorded in seconds */
6765 struct ast_filestream *recording_fs; /*used to read the recording to get duration data */
6766
6767 /* We aren't currently doing anything with category, since it comes from a channel variable and
6768 * this function doesn't use channels, but this function could add that as an argument later. */
6769 const char *category = NULL; /* pointless for now */
6770 char msg_id[MSG_ID_LEN];
6771
6772 /* Start by checking to see if the file actually exists... */
6773 if (!(ast_fileexists(recdata->recording_file, recdata->recording_ext, NULL))) {
6774 ast_log(LOG_ERROR, "File: %s not found.\n", recdata->recording_file);
6775 return -1;
6776 }
6777
6778 memset(&svm, 0, sizeof(svm));
6779 if (!(recipient = find_user(&svm, recdata->context, recdata->mailbox))) {
6780 ast_log(LOG_ERROR, "No entry in voicemail config file for '%s@%s'\n", recdata->mailbox, recdata->context);
6781 return -1;
6782 }
6783
6784 /* determine duration in seconds */
6785 if ((recording_fs = ast_readfile(recdata->recording_file, recdata->recording_ext, NULL, 0, 0, VOICEMAIL_DIR_MODE))) {
6786 if (!ast_seekstream(recording_fs, 0, SEEK_END)) {
6787 long framelength = ast_tellstream(recording_fs);
6788 int sample_rate = ast_ratestream(recording_fs);
6789 if (sample_rate) {
6790 duration = (int) (framelength / sample_rate);
6791 } else {
6792 ast_log(LOG_ERROR,"Unable to determine sample rate of recording %s\n", recdata->recording_file);
6793 }
6794 }
6795 ast_closestream(recording_fs);
6796 }
6797
6798 /* If the duration was below the minimum duration for the user, let's just drop the whole thing now */
6799 if (duration < recipient->minsecs) {
6800 ast_log(LOG_NOTICE, "Copying recording to voicemail %s@%s skipped because duration was shorter than "
6801 "minmessage of recipient\n", recdata->mailbox, recdata->context);
6802 return -1;
6803 }
6804
6805 /* Note that this number must be dropped back to a net sum of zero before returning from this function */
6806
6807 if ((res = create_dirpath(tmpdir, sizeof(tmpdir), recipient->context, recdata->mailbox, "tmp"))) {
6808 ast_log(LOG_ERROR, "Failed to make directory.\n");
6809 }
6810
6811 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
6812 txtdes = mkstemp(tmptxtfile);
6813 if (txtdes < 0) {
6814 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
6815 /* Something screwed up. Abort. */
6816 ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
6817 free_user(recipient);
6818 return -1;
6819 }
6820
6821 /* Store information */
6822 txt = fdopen(txtdes, "w+");
6823 if (txt) {
6824 generate_msg_id(msg_id);
6825 get_date(date, sizeof(date));
6826 fprintf(txt,
6827 ";\n"
6828 "; Message Information file\n"
6829 ";\n"
6830 "[message]\n"
6831 "origmailbox=%s\n"
6832 "context=%s\n"
6833 "exten=%s\n"
6834 "rdnis=Unknown\n"
6835 "priority=%d\n"
6836 "callerchan=%s\n"
6837 "callerid=%s\n"
6838 "origdate=%s\n"
6839 "origtime=%ld\n"
6840 "category=%s\n"
6841 "msg_id=%s\n"
6842 "flag=\n" /* flags not supported in copy from file yet */
6843 "duration=%d\n", /* Don't have any reliable way to get duration of file. */
6844
6845 recdata->mailbox,
6846 S_OR(recdata->call_context, ""),
6847 S_OR(recdata->call_extension, ""),
6848 recdata->call_priority,
6849 S_OR(recdata->call_callerchan, "Unknown"),
6850 S_OR(recdata->call_callerid, "Unknown"),
6851 date, (long) time(NULL),
6852 S_OR(category, ""),
6853 msg_id,
6854 duration);
6855
6856 /* Since we are recording from a file, we shouldn't need to do anything else with
6857 * this txt file */
6858 fclose(txt);
6859
6860 } else {
6861 ast_log(LOG_WARNING, "Error opening text file for output\n");
6862 if (ast_check_realtime("voicemail_data")) {
6863 ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6864 }
6865 free_user(recipient);
6866 return -1;
6867 }
6868
6869 /* At this point, the actual creation of a voicemail message should be finished.
6870 * Now we just need to copy the files being recorded into the receiving folder. */
6871
6872 create_dirpath(dir, sizeof(dir), recipient->context, recipient->mailbox, recdata->folder);
6873
6874#ifdef IMAP_STORAGE
6875 /* make recipient info into an inboxcount friendly string */
6876 snprintf(ext_context, sizeof(ext_context), "%s@%s", recipient->mailbox, recipient->context);
6877
6878 /* Is ext a mailbox? */
6879 /* must open stream for this user to get info! */
6880 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
6881 if (res < 0) {
6882 ast_log(LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
6883 free_user(recipient);
6884 unlink(tmptxtfile);
6885 return -1;
6886 }
6887 if (!(vms = get_vm_state_by_mailbox(recipient->mailbox, recipient->context, 0))) {
6888 /* It is possible under certain circumstances that inboxcount did not
6889 * create a vm_state when it was needed. This is a catchall which will
6890 * rarely be used.
6891 */
6892 if (!(vms = create_vm_state_from_user(recipient))) {
6893 ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
6894 free_user(recipient);
6895 unlink(tmptxtfile);
6896 return -1;
6897 }
6898 }
6899 vms->newmessages++;
6900
6901 /* here is a big difference! We add one to it later */
6902 msgnum = newmsgs + oldmsgs;
6903 ast_debug(3, "Messagecount set to %d\n", msgnum);
6904 snprintf(destination, sizeof(destination), "%simap/msg%s%04d", VM_SPOOL_DIR, recipient->mailbox, msgnum);
6905
6906 /* Check to see if we have enough room in the mailbox. If not, spit out an error and end
6907 * Note that imap_check_limits raises inprocess_count if successful */
6908 if ((res = imap_check_limits(NULL, vms, recipient, msgnum))) {
6909 ast_log(LOG_NOTICE, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
6910 inprocess_count(recipient->mailbox, recipient->context, -1);
6911 free_user(recipient);
6912 unlink(tmptxtfile);
6913 return -1;
6914 }
6915
6916#else
6917
6918 /* Check to see if the mailbox is full for ODBC/File storage */
6919 ast_debug(3, "mailbox = %d : inprocess = %d\n", COUNT(recipient, dir),
6920 inprocess_count(recipient->mailbox, recipient->context, 0));
6921 if (COUNT(recipient, dir) > recipient->maxmsg - inprocess_count(recipient->mailbox, recipient->context, +1)) {
6922 ast_log(AST_LOG_WARNING, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
6923 inprocess_count(recipient->mailbox, recipient->context, -1);
6924 free_user(recipient);
6925 unlink(tmptxtfile);
6926 return -1;
6927 }
6928
6929 msgnum = LAST_MSG_INDEX(dir) + 1;
6930#endif
6931
6932 /* Lock the directory receiving the voicemail since we want it to still exist when we attempt to copy the voicemail.
6933 * We need to unlock it before we return. */
6934 if (vm_lock_path(dir)) {
6935 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
6936 /* Delete files */
6937 ast_filedelete(tmptxtfile, NULL);
6938 unlink(tmptxtfile);
6939 free_user(recipient);
6940 return -1;
6941 }
6942
6943 make_file(destination, sizeof(destination), dir, msgnum);
6944
6945 make_file(tmpaudiofile, sizeof(tmpaudiofile), tmpdir, msgnum);
6946
6947 if (ast_filecopy(recdata->recording_file, tmpaudiofile, recdata->recording_ext)) {
6948 ast_log(LOG_ERROR, "Audio file failed to copy to tmp dir. Probably low disk space.\n");
6949
6950 inprocess_count(recipient->mailbox, recipient->context, -1);
6951 ast_unlock_path(dir);
6952 free_user(recipient);
6953 unlink(tmptxtfile);
6954 return -1;
6955 }
6956
6957 /* Alright, try to copy to the destination folder now. */
6958 if (ast_filerename(tmpaudiofile, destination, recdata->recording_ext)) {
6959 ast_log(LOG_ERROR, "Audio file failed to move to destination directory. Permissions/Overlap?\n");
6960 inprocess_count(recipient->mailbox, recipient->context, -1);
6961 ast_unlock_path(dir);
6962 free_user(recipient);
6963 unlink(tmptxtfile);
6964 return -1;
6965 }
6966
6967 snprintf(desttxtfile, sizeof(desttxtfile), "%s.txt", destination);
6968 rename(tmptxtfile, desttxtfile);
6969
6970 if (chmod(desttxtfile, VOICEMAIL_FILE_MODE) < 0) {
6971 ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", desttxtfile, strerror(errno));
6972 }
6973
6974
6975 ast_unlock_path(dir);
6976 inprocess_count(recipient->mailbox, recipient->context, -1);
6977
6978 /* If we copied something, we should store it either to ODBC or IMAP if we are using those. The STORE macro allows us
6979 * to do both with one line and is also safe to use with file storage mode. Also, if we are using ODBC, now is a good
6980 * time to create the voicemail database entry. */
6981 if (ast_fileexists(destination, NULL, NULL) > 0) {
6982 struct ast_channel *chan = NULL;
6983 char fmt[80];
6984 char clid[80];
6985 char cidnum[80], cidname[80];
6986 int send_email;
6987
6988 if (ast_check_realtime("voicemail_data")) {
6989 get_date(date, sizeof(date));
6990 ast_store_realtime("voicemail_data",
6991 "origmailbox", recdata->mailbox,
6992 "context", S_OR(recdata->context, ""),
6993 "exten", S_OR(recdata->call_extension, ""),
6994 "priority", recdata->call_priority,
6995 "callerchan", S_OR(recdata->call_callerchan, "Unknown"),
6996 "callerid", S_OR(recdata->call_callerid, "Unknown"),
6997 "origdate", date,
6998 "origtime", time(NULL),
6999 "category", S_OR(category, ""),
7000 "filename", tmptxtfile,
7001 "duration", duration,
7002 SENTINEL);
7003 }
7004
7005 STORE(dir, recipient->mailbox, recipient->context, msgnum, NULL, recipient, fmt, 0, vms, "", msg_id);
7006
7007 send_email = ast_test_flag(recipient, VM_EMAIL_EXT_RECS);
7008
7009 if (send_email) {
7010 /* Send an email if possible, fall back to just notifications if not. */
7011 ast_copy_string(fmt, recdata->recording_ext, sizeof(fmt));
7012 ast_copy_string(clid, recdata->call_callerid, sizeof(clid));
7013 ast_callerid_split(clid, cidname, sizeof(cidname), cidnum, sizeof(cidnum));
7014
7015 /* recdata->call_callerchan itself no longer exists, so we can't use the real channel. Use a dummy one. */
7016 chan = ast_dummy_channel_alloc();
7017 }
7018 if (chan) {
7019 notify_new_message(chan, recipient, NULL, msgnum, duration, fmt, cidnum, cidname, "");
7020 ast_channel_unref(chan);
7021 } else {
7022 if (send_email) { /* We tried and failed. */
7023 ast_log(LOG_WARNING, "Failed to allocate dummy channel, email will not be sent\n");
7024 }
7025 notify_new_state(recipient);
7026 }
7027 }
7028
7029 free_user(recipient);
7030 unlink(tmptxtfile);
7031 return 0;
7032}
7033
7034/*!
7035 * \brief Prompts the user and records a voicemail to a mailbox.
7036 * \param chan
7037 * \param ext
7038 * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
7039 *
7040 *
7041 *
7042 * \return zero on success, -1 on error.
7043 */
7044static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
7045{
7046#ifdef IMAP_STORAGE
7047 int newmsgs, oldmsgs;
7048#endif
7049 char txtfile[PATH_MAX];
7050 char tmptxtfile[PATH_MAX];
7051 struct vm_state *vms = NULL;
7052 char callerid[256];
7053 FILE *txt;
7054 char date[256];
7055 int txtdes;
7056 int res = 0;
7057 int msgnum;
7058 int duration = 0;
7059 int sound_duration = 0;
7060 int ouseexten = 0;
7061 int greeting_only = 0;
7062 char tmpdur[16];
7063 char priority[16];
7064 char origtime[16];
7065 char dir[PATH_MAX];
7066 char tmpdir[PATH_MAX];
7067 char fn[PATH_MAX];
7068 char prefile[PATH_MAX] = "";
7069 char tempfile[PATH_MAX] = "";
7070 char ext_context[256] = "";
7071 char fmt[80];
7072 char *context;
7073 char ecodes[17] = "#";
7074 struct ast_str *tmp = ast_str_create(16);
7075 char *tmpptr;
7076 struct ast_vm_user *vmu;
7077 struct ast_vm_user svm;
7078 const char *category = NULL;
7079 const char *code;
7080 const char *alldtmf = "0123456789ABCD*#";
7081 char flag[80];
7082 SCOPE_ENTER(3, "%s: %s\n", ast_channel_name(chan), ext);
7083
7084 if (!tmp) {
7085 SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for tmp\n");
7086 }
7087
7088 ast_str_set(&tmp, 0, "%s", ext);
7089 ext = ast_str_buffer(tmp);
7090 if ((context = strchr(ext, '@'))) {
7091 *context++ = '\0';
7092 tmpptr = strchr(context, '&');
7093 } else {
7094 tmpptr = strchr(ext, '&');
7095 }
7096
7097 if (tmpptr)
7098 *tmpptr++ = '\0';
7099
7100 ast_channel_lock(chan);
7101 if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
7102 category = ast_strdupa(category);
7103 }
7104 ast_channel_unlock(chan);
7105
7107 ast_copy_string(flag, "Urgent", sizeof(flag));
7109 ast_copy_string(flag, "PRIORITY", sizeof(flag));
7110 } else {
7111 flag[0] = '\0';
7112 }
7113
7114 ast_debug(3, "Before find_user\n");
7115 memset(&svm, 0, sizeof(svm));
7116 if (!(vmu = find_user(&svm, context, ext))) {
7117 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7118 ast_free(tmp);
7120 "%s: Exten: %s: No entry in voicemail config file for '%s'\n", ast_channel_name(chan), ext, ext);
7121 }
7122
7123 /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
7124 if (vmu->maxmsg == 0) {
7125 greeting_only = 1;
7127 }
7128
7129 /* Setup pre-file if appropriate */
7130 if (strcmp(vmu->context, "default"))
7131 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
7132 else
7133 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
7134
7135 /* Set the path to the prefile. Will be one of
7136 VM_SPOOL_DIRcontext/ext/busy
7137 VM_SPOOL_DIRcontext/ext/unavail
7138 Depending on the flag set in options.
7139 */
7141 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
7143 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
7144 }
7145 ast_trace(-1, "prefile: %s\n", prefile);
7146 /* Set the path to the tmpfile as
7147 VM_SPOOL_DIR/context/ext/temp
7148 and attempt to create the folder structure.
7149 */
7150 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
7151 ast_trace(-1, "tempfile: %s\n", tempfile);
7152 if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
7153 free_user(vmu);
7154 ast_free(tmp);
7156 "%s: Exten: %s: Failed to make directory (%s)\n", ast_channel_name(chan), ext, tempfile);
7157 }
7158 SCOPE_CALL(-1, RETRIEVE, tempfile, -1, vmu->mailbox, vmu->context);
7159 if (ast_fileexists(tempfile, NULL, NULL) > 0) {
7160 ast_copy_string(prefile, tempfile, sizeof(prefile));
7161 ast_trace(-1, "new prefile: %s\n", prefile);
7162 }
7163
7164 SCOPE_CALL(-1, DISPOSE, tempfile, -1);
7165
7166 /* It's easier just to try to make it than to check for its existence */
7167#ifndef IMAP_STORAGE
7168 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
7169#else
7170 snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
7171 if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
7172 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
7173 }
7174#endif
7175
7176 /* Check current context for special extensions */
7177 if (ast_test_flag(vmu, VM_OPERATOR)) {
7178 if (!ast_strlen_zero(vmu->exit)) {
7179 if (ast_exists_extension(chan, vmu->exit, "o", 1,
7180 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7181 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
7182 ouseexten = 1;
7183 }
7184 } else if (ast_exists_extension(chan, ast_channel_context(chan), "o", 1,
7185 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7186 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
7187 ouseexten = 1;
7188 }
7189 }
7190
7191 if (!ast_strlen_zero(vmu->exit)) {
7192 if (ast_exists_extension(chan, vmu->exit, "a", 1,
7193 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7194 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
7195 }
7196 } else if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
7197 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7198 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
7199 }
7200
7202 for (code = alldtmf; *code; code++) {
7203 char e[2] = "";
7204 e[0] = *code;
7205 if (strchr(ecodes, e[0]) == NULL
7206 && ast_canmatch_extension(chan,
7207 (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : ast_channel_context(chan)),
7208 e, 1, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7209 strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
7210 }
7211 }
7212 }
7213
7214 /* Play the beginning intro if desired */
7215 if (!ast_strlen_zero(prefile)) {
7216#if defined(ODBC_STORAGE)
7217 int success = SCOPE_CALL_WITH_INT_RESULT(-1, RETRIEVE, prefile, -1, ext, context);
7218#elif defined(IMAP_STORAGE)
7219 SCOPE_CALL(-1, RETRIEVE, prefile, -1, ext, context);
7220#endif
7221
7222 if (ast_fileexists(prefile, NULL, NULL) > 0) {
7223 if (ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1) {
7224 /* We know we have a greeting at this point, so squelch the instructions
7225 * if that is what is being asked of us */
7228 }
7229 res = ast_waitstream(chan, ecodes);
7230 }
7231#ifdef ODBC_STORAGE
7232 if (success == -1) {
7233 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
7234 ast_trace(-1, "Greeting '%s' not retrieved from database, but found in file storage. Inserting into database\n", prefile);
7235 SCOPE_CALL(-1, odbc_store_message, prefile, vmu->mailbox, vmu->context, -1);
7236 }
7237#endif
7238 } else {
7239 ast_trace(-1, "%s doesn't exist, doing what we can\n", prefile);
7240 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
7241 }
7242 SCOPE_CALL(-1, DISPOSE, prefile, -1);
7243 if (res < 0) {
7244 free_user(vmu);
7245 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7246 ast_free(tmp);
7247 SCOPE_EXIT_RTN_VALUE(-1, "Hang up during prefile playback\n");
7248 }
7249 }
7250 if (res == '#') {
7251 /* On a '#' we skip the instructions */
7253 res = 0;
7254 }
7255 if (!res && !ast_test_flag(options, OPT_SILENT)) {
7256 res = ast_stream_and_wait(chan, INTRO, ecodes);
7257 if (res == '#') {
7259 res = 0;
7260 }
7261 }
7262 if (res > 0)
7263 ast_stopstream(chan);
7264 /* Check for a '*' here in case the caller wants to escape from voicemail to something
7265 other than the operator -- an automated attendant or mailbox login for example */
7266 if (res == '*') {
7267 ast_channel_exten_set(chan, "a");
7268 if (!ast_strlen_zero(vmu->exit)) {
7269 ast_channel_context_set(chan, vmu->exit);
7270 }
7271 ast_channel_priority_set(chan, 0);
7272 free_user(vmu);
7273 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
7274 ast_free(tmp);
7275 SCOPE_EXIT_RTN_VALUE(0, "User escaped with '*'\n");
7276 }
7277
7278 /* Check for a '0' here */
7279 if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
7280 transfer:
7281 if (ouseexten) {
7282 ast_channel_exten_set(chan, "o");
7283 if (!ast_strlen_zero(vmu->exit)) {
7284 ast_channel_context_set(chan, vmu->exit);
7285 }
7286 ast_play_and_wait(chan, "transfer");
7287 ast_channel_priority_set(chan, 0);
7288 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
7289 }
7290 free_user(vmu);
7291 ast_free(tmp);
7292 SCOPE_EXIT_RTN_VALUE(OPERATOR_EXIT, "User escaped with '0'\n");
7293 }
7294
7295 /* Allow all other digits to exit Voicemail and return to the dialplan */
7296 if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
7297 if (!ast_strlen_zero(options->exitcontext)) {
7298 ast_channel_context_set(chan, options->exitcontext);
7299 }
7300 free_user(vmu);
7301 ast_free(tmp);
7302 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
7303 SCOPE_EXIT_RTN_VALUE(res, "User escaped back to dialplan '%c'\n", res);
7304 }
7305
7306 if (greeting_only) {
7307 ast_debug(3, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
7308 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
7309 res = 0;
7310 goto leave_vm_out;
7311 }
7312
7313 if (res < 0) {
7314 free_user(vmu);
7315 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7316 ast_free(tmp);
7317 SCOPE_EXIT_RTN_VALUE(-1, "Other error '%d'\n", res);
7318 }
7319 /* The meat of recording the message... All the announcements and beeps have been played*/
7320 if (ast_channel_state(chan) != AST_STATE_UP) {
7321 ast_answer(chan);
7322 }
7323 ast_copy_string(fmt, vmfmts, sizeof(fmt));
7324 if (!ast_strlen_zero(fmt)) {
7325 char msg_id[MSG_ID_LEN] = "";
7326 msgnum = 0;
7327
7328#ifdef IMAP_STORAGE
7329 /* Is ext a mailbox? */
7330 /* must open stream for this user to get info! */
7331 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
7332 if (res < 0) {
7333 ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
7334 free_user(vmu);
7335 ast_free(tmp);
7336 return -1;
7337 }
7338 if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
7339 /* It is possible under certain circumstances that inboxcount did not
7340 * create a vm_state when it was needed. This is a catchall which will
7341 * rarely be used.
7342 */
7343 if (!(vms = create_vm_state_from_user(vmu))) {
7344 ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
7345 free_user(vmu);
7346 ast_free(tmp);
7347 return -1;
7348 }
7349 }
7350 vms->newmessages++;
7351
7352 /* here is a big difference! We add one to it later */
7353 msgnum = newmsgs + oldmsgs;
7354 ast_debug(3, "Messagecount set to %d\n", msgnum);
7355 snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
7356 /* set variable for compatibility */
7357 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
7358
7359 if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
7360 goto leave_vm_out;
7361 }
7362#else
7363 if (COUNT(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
7364 res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
7365 if (!res)
7366 res = ast_waitstream(chan, "");
7367 ast_log(AST_LOG_WARNING, "No more messages possible\n");
7368 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7369 inprocess_count(vmu->mailbox, vmu->context, -1);
7370 goto leave_vm_out;
7371 }
7372
7373#endif
7374 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
7375 ast_trace(-1, "Tempfile: %s\n", tmptxtfile);
7376 txtdes = mkstemp(tmptxtfile);
7377 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
7378 if (txtdes < 0) {
7379 res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
7380 if (!res)
7381 res = ast_waitstream(chan, "");
7382 ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
7383 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7384 inprocess_count(vmu->mailbox, vmu->context, -1);
7385 goto leave_vm_out;
7386 }
7387
7388 /* Now play the beep once we have the message number for our next message. */
7389 if (res >= 0) {
7390 /* Unless we're *really* silent, try to send the beep */
7391 /* Play default or custom beep, unless no beep desired */
7392 if (!ast_strlen_zero(options->beeptone)) {
7393 res = ast_stream_and_wait(chan, options->beeptone, "");
7394 }
7395 }
7396
7397 /* Store information in real-time storage */
7398 if (ast_check_realtime("voicemail_data")) {
7399 snprintf(priority, sizeof(priority), "%d", ast_channel_priority(chan));
7400 snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
7401 get_date(date, sizeof(date));
7402 ast_callerid_merge(callerid, sizeof(callerid),
7403 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7404 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7405 "Unknown");
7406 ast_store_realtime("voicemail_data",
7407 "origmailbox", ext,
7408 "context", ast_channel_context(chan),
7409 "exten", ast_channel_exten(chan),
7410 "priority", priority,
7411 "callerchan", ast_channel_name(chan),
7412 "callerid", callerid,
7413 "origdate", date,
7414 "origtime", origtime,
7415 "category", S_OR(category, ""),
7416 "filename", tmptxtfile,
7417 SENTINEL);
7418 }
7419
7420 /* Store information */
7421 txt = fdopen(txtdes, "w+");
7422 if (txt) {
7423 generate_msg_id(msg_id);
7424 get_date(date, sizeof(date));
7425 ast_callerid_merge(callerid, sizeof(callerid),
7426 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7427 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7428 "Unknown");
7429 fprintf(txt,
7430 ";\n"
7431 "; Message Information file\n"
7432 ";\n"
7433 "[message]\n"
7434 "origmailbox=%s\n"
7435 "context=%s\n"
7436 "exten=%s\n"
7437 "rdnis=%s\n"
7438 "priority=%d\n"
7439 "callerchan=%s\n"
7440 "callerid=%s\n"
7441 "origdate=%s\n"
7442 "origtime=%ld\n"
7443 "category=%s\n"
7444 "msg_id=%s\n",
7445 ext,
7446 ast_channel_context(chan),
7447 ast_channel_exten(chan),
7448 S_COR(ast_channel_redirecting(chan)->from.number.valid,
7449 ast_channel_redirecting(chan)->from.number.str, "unknown"),
7451 ast_channel_name(chan),
7452 callerid,
7453 date, (long) time(NULL),
7454 category ? category : "",
7455 msg_id);
7456 ast_trace(-1, "Saving txt file mbox: %s msg_id: %s\n", ext, msg_id);
7457 } else {
7458 ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
7459 inprocess_count(vmu->mailbox, vmu->context, -1);
7460 if (ast_check_realtime("voicemail_data")) {
7461 ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7462 }
7463 res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
7464 goto leave_vm_out;
7465 }
7466
7467 res = SCOPE_CALL_WITH_INT_RESULT(-1, play_record_review, chan, NULL, tmptxtfile, vmu->maxsecs, fmt, 1, vmu, &duration, &sound_duration, NULL, options->record_gain, vms, flag, msg_id, 0);
7468
7469 /* At this point, either we were instructed to make the message Urgent
7470 by arguments to VoiceMail or during the review process by the person
7471 leaving the message. So we update the directory where we want this
7472 message to go. */
7473 if (!strcmp(flag, "Urgent")) {
7474 create_dirpath(dir, sizeof(dir), vmu->context, ext, "Urgent");
7475 }
7476
7477 if (txt) {
7478 fprintf(txt, "flag=%s\n", flag);
7479 if (sound_duration < vmu->minsecs) {
7480 fclose(txt);
7481 ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
7482 ast_filedelete(tmptxtfile, NULL);
7483 unlink(tmptxtfile);
7484 if (ast_check_realtime("voicemail_data")) {
7485 ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7486 }
7487 inprocess_count(vmu->mailbox, vmu->context, -1);
7488 } else {
7489 fprintf(txt, "duration=%d\n", duration);
7490 fclose(txt);
7491 if (vm_lock_path(dir)) {
7492 ast_log(AST_LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
7493 /* Delete files */
7494 ast_filedelete(tmptxtfile, NULL);
7495 unlink(tmptxtfile);
7496 inprocess_count(vmu->mailbox, vmu->context, -1);
7497 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
7498 ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
7499 unlink(tmptxtfile);
7500 ast_unlock_path(dir);
7501 inprocess_count(vmu->mailbox, vmu->context, -1);
7502 if (ast_check_realtime("voicemail_data")) {
7503 ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7504 }
7505 } else {
7506#ifndef IMAP_STORAGE
7507 msgnum = LAST_MSG_INDEX(dir) + 1;
7508#endif
7509 make_file(fn, sizeof(fn), dir, msgnum);
7510
7511 /* assign a variable with the name of the voicemail file */
7512#ifndef IMAP_STORAGE
7513 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
7514#else
7515 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
7516#endif
7517
7518 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
7519
7520 ast_trace(-1, "Renaming recordings '%s' -> fn '%s'\n", tmptxtfile, fn);
7521 /* ast_filerename renames the recordings but not the txt file */
7522 ast_filerename(tmptxtfile, fn, NULL);
7523
7524 ast_trace(-1, "Renaming txt file '%s' -> fn '%s'\n", tmptxtfile, txtfile);
7525 rename(tmptxtfile, txtfile);
7526 inprocess_count(vmu->mailbox, vmu->context, -1);
7527
7528 /* Properly set permissions on voicemail text descriptor file.
7529 Unfortunately mkstemp() makes this file 0600 on most unix systems. */
7530 if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
7531 ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
7532
7533 ast_unlock_path(dir);
7534 if (ast_check_realtime("voicemail_data")) {
7535 snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
7536 ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
7537 }
7538 /* We must store the file first, before copying the message, because
7539 * ODBC storage does the entire copy with SQL.
7540 */
7541 if (ast_fileexists(fn, NULL, NULL) > 0) {
7542 SCOPE_CALL(-1, STORE, dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag, msg_id);
7543 }
7544
7545 /* Are there to be more recipients of this message? */
7546 while (tmpptr) {
7547 struct ast_vm_user recipu, *recip;
7548 char *exten, *cntx;
7549
7550 exten = strsep(&tmpptr, "&");
7551 cntx = strchr(exten, '@');
7552 if (cntx) {
7553 *cntx = '\0';
7554 cntx++;
7555 }
7556 memset(&recipu, 0, sizeof(recipu));
7557 if ((recip = find_user(&recipu, cntx, exten))) {
7558 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag, NULL);
7559 free_user(recip);
7560 }
7561 }
7562
7563 /* Notification needs to happen after the copy, though. */
7564 if (ast_fileexists(fn, NULL, NULL)) {
7565#ifdef IMAP_STORAGE
7566 notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
7567 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7568 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7569 flag);
7570#else
7571 notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
7572 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7573 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7574 flag);
7575#endif
7576 }
7577
7578 /* Disposal needs to happen after the optional move and copy */
7579 if (ast_fileexists(fn, NULL, NULL)) {
7580 SCOPE_CALL(-1, DISPOSE, dir, msgnum);
7581 }
7582 }
7583 }
7584 } else {
7585 inprocess_count(vmu->mailbox, vmu->context, -1);
7586 }
7587 if (res == '0') {
7588 goto transfer;
7589 } else if (res > 0 && res != 't')
7590 res = 0;
7591
7592 if (sound_duration < vmu->minsecs)
7593 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
7594 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7595 else
7596 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
7597 } else
7598 ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
7599leave_vm_out:
7600 free_user(vmu);
7601
7602#ifdef IMAP_STORAGE
7603 /* expunge message - use UID Expunge if supported on IMAP server*/
7604 ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
7605 if (expungeonhangup == 1 && vms->mailstream != NULL) {
7606 ast_mutex_lock(&vms->lock);
7607#ifdef HAVE_IMAP_TK2006
7608 if (LEVELUIDPLUS (vms->mailstream)) {
7609 mail_expunge_full(vms->mailstream, NIL, EX_UID);
7610 } else
7611#endif
7612 mail_expunge(vms->mailstream);
7613 ast_mutex_unlock(&vms->lock);
7614 }
7615#endif
7616
7617 ast_free(tmp);
7618 SCOPE_EXIT_RTN_VALUE(res, "Done: '%d'\n", res);
7619}
7620
7621#if !defined(IMAP_STORAGE)
7622static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
7623{
7624 /* we know the actual number of messages, so stop process when number is hit */
7625
7626 int x, dest;
7627 char sfn[PATH_MAX];
7628 char dfn[PATH_MAX];
7629
7630 if (vm_lock_path(dir)) {
7631 return ERROR_LOCK_PATH;
7632 }
7633
7634 for (x = 0, dest = 0; dest != stopcount && x < MAXMSGLIMIT; x++) {
7635 make_file(sfn, sizeof(sfn), dir, x);
7636 if (EXISTS(dir, x, sfn, NULL)) {
7637
7638 if (x != dest) {
7639 make_file(dfn, sizeof(dfn), dir, dest);
7640 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
7641 }
7642
7643 dest++;
7644 }
7645 }
7646 ast_unlock_path(dir);
7647
7648 return dest;
7649}
7650#endif
7651
7652static int say_and_wait(struct ast_channel *chan, int num, const char *language)
7653{
7654 int d;
7655 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
7656 return d;
7657}
7658
7659static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move)
7660{
7661#ifdef IMAP_STORAGE
7662 /* we must use mbox(x) folder names, and copy the message there */
7663 /* simple. huh? */
7664 char sequence[10];
7665 char mailbox[256];
7666 int res;
7667 int curr_mbox;
7668
7669 /* get the real IMAP message number for this message */
7670 snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
7671
7672 ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
7673 ast_mutex_lock(&vms->lock);
7674 /* if save to Old folder, put in INBOX as read */
7675 if (box == OLD_FOLDER) {
7676 mail_setflag(vms->mailstream, sequence, "\\Seen");
7677 } else if (box == NEW_FOLDER) {
7678 mail_clearflag(vms->mailstream, sequence, "\\Seen");
7679 }
7680 if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
7681 ast_mutex_unlock(&vms->lock);
7682 return 0;
7683 }
7684
7685 /* get the current mailbox so that we can point the mailstream back to it later */
7686 curr_mbox = get_folder_by_name(vms->curbox);
7687
7688 /* Create the folder if it doesn't exist */
7689 imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
7690 if (vms->mailstream && !mail_status(vms->mailstream, mailbox, SA_UIDNEXT)) {
7691 if (mail_create(vms->mailstream, mailbox) != NIL) {
7692 ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
7693 }
7694 }
7695
7696 /* restore previous mbox stream */
7697 if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
7698 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
7699 res = -1;
7700 } else {
7701 if (move) {
7702 res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
7703 } else {
7704 res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
7705 }
7706 }
7707 ast_mutex_unlock(&vms->lock);
7708 return res;
7709#else
7710 char *dir = vms->curdir;
7711 char *username = vms->username;
7712 char *context = vmu->context;
7713 char sfn[PATH_MAX];
7714 char dfn[PATH_MAX];
7715 char ddir[PATH_MAX];
7716 const char *dbox = mbox(vmu, box);
7717 int x, i;
7718 SCOPE_ENTER(3, "dir: %s msg: %d box: %d dbox: %s move? %d \n", dir, msg, box, dbox, move);
7719
7720 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
7721 ast_trace(-1, "ddir: %s\n", ddir);
7722
7723 if (vm_lock_path(ddir)) {
7724 SCOPE_EXIT_RTN_VALUE(ERROR_LOCK_PATH, "Failed to lock path %s\n", ddir);
7725 }
7726
7727 x = LAST_MSG_INDEX(ddir) + 1;
7728
7729 if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
7730 ast_trace(-1, "Deleting message %d\n", msg);
7731 x--;
7732 for (i = 1; i <= x; i++) {
7733 /* Push files down a "slot". The oldest file (msg0000) will be deleted. */
7734 make_file(sfn, sizeof(sfn), ddir, i);
7735 make_file(dfn, sizeof(dfn), ddir, i - 1);
7736 if (EXISTS(ddir, i, sfn, NULL)) {
7737 SCOPE_CALL(-1, RENAME, ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
7738 } else
7739 break;
7740 }
7741 } else {
7742 if (x >= vmu->maxmsg) {
7743 ast_unlock_path(ddir);
7744 SCOPE_EXIT_RTN_VALUE(ERROR_MAX_MSGS, "Max messages reached\n");
7745 }
7746 }
7747 make_file(sfn, sizeof(sfn), dir, msg);
7748 make_file(dfn, sizeof(dfn), ddir, x);
7749 if (strcmp(sfn, dfn)) {
7750 ast_trace(-1, "Copying message '%s' to '%s'\n", sfn, dfn);
7751 SCOPE_CALL(-1, COPY, dir, msg, ddir, x, username, context, sfn, dfn);
7752 }
7753 ast_unlock_path(ddir);
7754
7755 if (newmsg) {
7756 *newmsg = x;
7757 }
7758 SCOPE_EXIT_RTN_VALUE(0, "Done\n");
7759#endif
7760}
7761
7762static int adsi_logo(unsigned char *buf)
7763{
7764 int bytes = 0;
7765 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
7766 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
7767 return bytes;
7768}
7769
7770static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
7771{
7772 unsigned char buf[256];
7773 int bytes = 0;
7774 int x;
7775 char num[5];
7776
7777 *useadsi = 0;
7778 bytes += ast_adsi_data_mode(buf + bytes);
7780
7781 bytes = 0;
7782 bytes += adsi_logo(buf);
7783 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
7784#ifdef DISPLAY
7785 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
7786#endif
7787 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7788 bytes += ast_adsi_data_mode(buf + bytes);
7790
7792 bytes = 0;
7793 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
7794 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
7795 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7796 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7798 return 0;
7799 }
7800
7801#ifdef DISPLAY
7802 /* Add a dot */
7803 bytes = 0;
7804 bytes += ast_adsi_logo(buf);
7805 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
7806 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
7807 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7809#endif
7810 bytes = 0;
7811 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
7812 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
7813 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advanced", "3", 1);
7814 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
7815 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
7816 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
7818
7819#ifdef DISPLAY
7820 /* Add another dot */
7821 bytes = 0;
7822 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
7823 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7824
7825 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7827#endif
7828
7829 bytes = 0;
7830 /* These buttons we load but don't use yet */
7831 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
7832 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
7833 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
7834 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
7835 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
7836 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
7838
7839#ifdef DISPLAY
7840 /* Add another dot */
7841 bytes = 0;
7842 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
7843 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7845#endif
7846
7847 bytes = 0;
7848 for (x = 0; x < 5; x++) {
7849 snprintf(num, sizeof(num), "%d", x);
7850 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
7851 }
7852 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
7854
7855#ifdef DISPLAY
7856 /* Add another dot */
7857 bytes = 0;
7858 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
7859 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7861#endif
7862
7863 if (ast_adsi_end_download(chan)) {
7864 bytes = 0;
7865 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
7866 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
7867 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7868 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7870 return 0;
7871 }
7872 bytes = 0;
7873 bytes += ast_adsi_download_disconnect(buf + bytes);
7874 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7876
7877 ast_debug(1, "Done downloading scripts...\n");
7878
7879#ifdef DISPLAY
7880 /* Add last dot */
7881 bytes = 0;
7882 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
7883 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7884#endif
7885 ast_debug(1, "Restarting session...\n");
7886
7887 bytes = 0;
7888 /* Load the session now */
7889 if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
7890 *useadsi = 1;
7891 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
7892 } else
7893 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
7894
7896 return 0;
7897}
7898
7899static void adsi_begin(struct ast_channel *chan, int *useadsi)
7900{
7901 int x;
7902 if (!ast_adsi_available(chan))
7903 return;
7904 x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
7905 if (x < 0) {
7906 *useadsi = 0;
7908 return;
7909 }
7910 if (!x) {
7911 if (adsi_load_vmail(chan, useadsi)) {
7912 ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
7913 return;
7914 }
7915 } else
7916 *useadsi = 1;
7917}
7918
7919static void adsi_login(struct ast_channel *chan)
7920{
7921 unsigned char buf[256];
7922 int bytes = 0;
7923 unsigned char keys[8];
7924 int x;
7925 if (!ast_adsi_available(chan))
7926 return;
7927
7928 for (x = 0; x < 8; x++)
7929 keys[x] = 0;
7930 /* Set one key for next */
7931 keys[3] = ADSI_KEY_APPS + 3;
7932
7933 bytes += adsi_logo(buf + bytes);
7934 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
7935 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
7936 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7937 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
7938 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
7939 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
7940 bytes += ast_adsi_set_keys(buf + bytes, keys);
7941 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7943}
7944
7945static void adsi_password(struct ast_channel *chan)
7946{
7947 unsigned char buf[256];
7948 int bytes = 0;
7949 unsigned char keys[8];
7950 int x;
7951 if (!ast_adsi_available(chan))
7952 return;
7953
7954 for (x = 0; x < 8; x++)
7955 keys[x] = 0;
7956 /* Set one key for next */
7957 keys[3] = ADSI_KEY_APPS + 3;
7958
7959 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7960 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
7961 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
7962 bytes += ast_adsi_set_keys(buf + bytes, keys);
7963 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7965}
7966
7967static void adsi_folders(struct ast_channel *chan, int start, char *label)
7968{
7969 unsigned char buf[256];
7970 int bytes = 0;
7971 unsigned char keys[8];
7972 int x, y;
7973
7974 if (!ast_adsi_available(chan))
7975 return;
7976
7977 for (x = 0; x < 5; x++) {
7978 y = ADSI_KEY_APPS + 12 + start + x;
7979 if (y > ADSI_KEY_APPS + 12 + 4)
7980 y = 0;
7981 keys[x] = ADSI_KEY_SKT | y;
7982 }
7983 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
7984 keys[6] = 0;
7985 keys[7] = 0;
7986
7987 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
7988 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
7989 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7990 bytes += ast_adsi_set_keys(buf + bytes, keys);
7991 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7992
7994}
7995
7996static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
7997{
7998 int bytes = 0;
7999 unsigned char buf[256];
8000 char buf1[256], buf2[256];
8001 char fn2[PATH_MAX];
8002
8003 char cid[256] = "";
8004 char *val;
8005 char *name, *num;
8006 char datetime[21] = "";
8007 FILE *f;
8008
8009 unsigned char keys[8];
8010
8011 int x;
8012
8013 if (!ast_adsi_available(chan))
8014 return;
8015
8016 /* Retrieve important info */
8017 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
8018 f = fopen(fn2, "r");
8019 if (f) {
8020 while (!feof(f)) {
8021 if (!fgets((char *) buf, sizeof(buf), f)) {
8022 continue;
8023 }
8024 if (!feof(f)) {
8025 char *stringp = NULL;
8026 stringp = (char *) buf;
8027 strsep(&stringp, "=");
8028 val = strsep(&stringp, "=");
8029 if (!ast_strlen_zero(val)) {
8030 if (!strcmp((char *) buf, "callerid"))
8031 ast_copy_string(cid, val, sizeof(cid));
8032 if (!strcmp((char *) buf, "origdate"))
8033 ast_copy_string(datetime, val, sizeof(datetime));
8034 }
8035 }
8036 }
8037 fclose(f);
8038 }
8039 /* New meaning for keys */
8040 for (x = 0; x < 5; x++)
8041 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
8042 keys[6] = 0x0;
8043 keys[7] = 0x0;
8044
8045 if (!vms->curmsg) {
8046 /* No prev key, provide "Folder" instead */
8047 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8048 }
8049 if (vms->curmsg >= vms->lastmsg) {
8050 /* If last message ... */
8051 if (vms->curmsg) {
8052 /* but not only message, provide "Folder" instead */
8053 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8054 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8055
8056 } else {
8057 /* Otherwise if only message, leave blank */
8058 keys[3] = 1;
8059 }
8060 }
8061
8062 if (!ast_strlen_zero(cid)) {
8063 ast_callerid_parse(cid, &name, &num);
8064 if (!name)
8065 name = num;
8066 } else {
8067 name = "Unknown Caller";
8068 }
8069
8070 /* If deleted, show "undeleted" */
8071#ifdef IMAP_STORAGE
8072 ast_mutex_lock(&vms->lock);
8073#endif
8074 if (vms->deleted[vms->curmsg]) {
8075 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
8076 }
8077#ifdef IMAP_STORAGE
8078 ast_mutex_unlock(&vms->lock);
8079#endif
8080
8081 /* Except "Exit" */
8082 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
8083 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
8084 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
8085 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
8086
8087 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8088 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8089 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
8090 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
8091 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8092 bytes += ast_adsi_set_keys(buf + bytes, keys);
8093 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8094
8096}
8097
8098static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
8099{
8100 int bytes = 0;
8101 unsigned char buf[256];
8102 unsigned char keys[8];
8103
8104 int x;
8105
8106 if (!ast_adsi_available(chan))
8107 return;
8108
8109 /* New meaning for keys */
8110 for (x = 0; x < 5; x++)
8111 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
8112
8113 keys[6] = 0x0;
8114 keys[7] = 0x0;
8115
8116 if (!vms->curmsg) {
8117 /* No prev key, provide "Folder" instead */
8118 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8119 }
8120 if (vms->curmsg >= vms->lastmsg) {
8121 /* If last message ... */
8122 if (vms->curmsg) {
8123 /* but not only message, provide "Folder" instead */
8124 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8125 } else {
8126 /* Otherwise if only message, leave blank */
8127 keys[3] = 1;
8128 }
8129 }
8130
8131 /* If deleted, show "undeleted" */
8132#ifdef IMAP_STORAGE
8133 ast_mutex_lock(&vms->lock);
8134#endif
8135 if (vms->deleted[vms->curmsg]) {
8136 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
8137 }
8138#ifdef IMAP_STORAGE
8139 ast_mutex_unlock(&vms->lock);
8140#endif
8141
8142 /* Except "Exit" */
8143 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
8144 bytes += ast_adsi_set_keys(buf + bytes, keys);
8145 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8146
8148}
8149
8150static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
8151{
8152 unsigned char buf[256] = "";
8153 char buf1[256] = "", buf2[256] = "";
8154 int bytes = 0;
8155 unsigned char keys[8];
8156 int x;
8157
8158 char *newm = (vms->newmessages == 1) ? "message" : "messages";
8159 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
8160 if (!ast_adsi_available(chan))
8161 return;
8162 if (vms->newmessages) {
8163 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
8164 if (vms->oldmessages) {
8165 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
8166 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
8167 } else {
8168 snprintf(buf2, sizeof(buf2), "%s.", newm);
8169 }
8170 } else if (vms->oldmessages) {
8171 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
8172 snprintf(buf2, sizeof(buf2), "%s.", oldm);
8173 } else {
8174 strcpy(buf1, "You have no messages.");
8175 buf2[0] = ' ';
8176 buf2[1] = '\0';
8177 }
8178 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8179 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8180 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8181
8182 for (x = 0; x < 6; x++)
8183 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
8184 keys[6] = 0;
8185 keys[7] = 0;
8186
8187 /* Don't let them listen if there are none */
8188 if (vms->lastmsg < 0)
8189 keys[0] = 1;
8190 bytes += ast_adsi_set_keys(buf + bytes, keys);
8191
8192 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8193
8195}
8196
8197static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
8198{
8199 unsigned char buf[256] = "";
8200 char buf1[256] = "", buf2[256] = "";
8201 int bytes = 0;
8202 unsigned char keys[8];
8203 int x;
8204
8205 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
8206
8207 if (!ast_adsi_available(chan))
8208 return;
8209
8210 /* Original command keys */
8211 for (x = 0; x < 6; x++)
8212 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
8213
8214 keys[6] = 0;
8215 keys[7] = 0;
8216
8217 if ((vms->lastmsg + 1) < 1)
8218 keys[0] = 0;
8219
8220 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
8221 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
8222
8223 if (vms->lastmsg + 1)
8224 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
8225 else
8226 strcpy(buf2, "no messages.");
8227 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8228 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8229 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
8230 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8231 bytes += ast_adsi_set_keys(buf + bytes, keys);
8232
8233 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8234
8236
8237}
8238
8239/*
8240static void adsi_clear(struct ast_channel *chan)
8241{
8242 char buf[256];
8243 int bytes=0;
8244 if (!ast_adsi_available(chan))
8245 return;
8246 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8247 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8248
8249 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
8250}
8251*/
8252
8253static void adsi_goodbye(struct ast_channel *chan)
8254{
8255 unsigned char buf[256];
8256 int bytes = 0;
8257
8258 if (!ast_adsi_available(chan))
8259 return;
8260 bytes += adsi_logo(buf + bytes);
8261 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
8262 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
8263 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8264 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8265
8267}
8268
8269/*!\brief get_folder: Folder menu
8270 * Plays "press 1 for INBOX messages" etc.
8271 * Should possibly be internationalized
8272 */
8273static int get_folder(struct ast_channel *chan, int start)
8274{
8275 int x;
8276 int d;
8277 char fn[PATH_MAX];
8278 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
8279 if (d)
8280 return d;
8281 for (x = start; x < 5; x++) { /* For all folders */
8282 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), NULL)))
8283 return d;
8284 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
8285 if (d)
8286 return d;
8287 snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
8288
8289 /* The inbox folder can have its name changed under certain conditions
8290 * so this checks if the sound file exists for the inbox folder name and
8291 * if it doesn't, plays the default name instead. */
8292 if (x == 0) {
8293 if (ast_fileexists(fn, NULL, NULL)) {
8294 d = vm_play_folder_name(chan, fn);
8295 } else {
8296 ast_verb(4, "Failed to find file %s; falling back to INBOX\n", fn);
8297 d = vm_play_folder_name(chan, "vm-INBOX");
8298 }
8299 } else {
8300 ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
8301 d = vm_play_folder_name(chan, fn);
8302 }
8303
8304 if (d)
8305 return d;
8306 d = ast_waitfordigit(chan, 500);
8307 if (d)
8308 return d;
8309 }
8310
8311 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
8312 if (d)
8313 return d;
8314 d = ast_waitfordigit(chan, 4000);
8315 return d;
8316}
8317
8318/* Japanese Syntax */
8319static int get_folder_ja(struct ast_channel *chan, int start)
8320{
8321 int x;
8322 int d;
8323 char fn[256];
8324 for (x = start; x < 5; x++) { /* For all folders */
8325 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL))) {
8326 return d;
8327 }
8328 snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
8329 d = vm_play_folder_name(chan, fn);
8330 if (d) {
8331 return d;
8332 }
8333 d = ast_waitfordigit(chan, 500);
8334 if (d) {
8335 return d;
8336 }
8337 }
8338 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
8339 if (d) {
8340 return d;
8341 }
8342 d = ast_waitfordigit(chan, 4000);
8343 return d;
8344}
8345
8346/*!
8347 * \brief plays a prompt and waits for a keypress.
8348 * \param chan
8349 * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
8350 * \param start Does not appear to be used at this time.
8351 *
8352 * This is used by the main menu option to move a message to a folder or to save a message into a folder.
8353 * After playing the message identified by the fn parameter value, it calls get_folder(), which plays the
8354 * prompting for the number inputs that correspond to the available folders.
8355 *
8356 * \return zero on success, or -1 on error.
8357 */
8358static int get_folder2(struct ast_channel *chan, char *fn, int start)
8359{
8360 int res = 0;
8361 int loops = 0;
8362
8363 res = ast_play_and_wait(chan, fn); /* Folder name */
8364 while (((res < '0') || (res > '9')) &&
8365 (res != '#') && (res >= 0) &&
8366 loops < 4) {
8367 /* res = get_folder(chan, 0); */
8368 if (!strcasecmp(ast_channel_language(chan), "ja")) { /* Japanese syntax */
8369 res = get_folder_ja(chan, 0);
8370 } else { /* Default syntax */
8371 res = get_folder(chan, 0);
8372 }
8373 loops++;
8374 }
8375 if (loops == 4) { /* give up */
8376 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
8377 return '#';
8378 }
8379 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8380 isprint(res) ? res : '?', isprint(res) ? res : '?');
8381 return res;
8382}
8383
8384/*!
8385 * \brief presents the option to prepend to an existing message when forwarding it.
8386 * \param chan
8387 * \param vmu
8388 * \param curdir
8389 * \param curmsg
8390 * \param vm_fmts
8391 * \param context
8392 * \param record_gain
8393 * \param duration
8394 * \param vms
8395 * \param flag
8396 *
8397 * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
8398 *
8399 * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
8400 * \return zero on success, -1 on error.
8401 */
8402static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir,
8403 int curmsg, char *vm_fmts, char *context, signed char record_gain, long *duration,
8404 struct vm_state *vms, char *flag)
8405{
8406 int cmd = 0;
8407 int retries = 0, prepend_duration = 0, already_recorded = 0;
8408 char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
8409 char textfile[PATH_MAX];
8410 struct ast_config *msg_cfg;
8411 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8412#ifndef IMAP_STORAGE
8413 signed char zero_gain = 0;
8414#else
8415 const char *msg_id = NULL;
8416#endif
8417 const char *duration_str;
8418 SCOPE_ENTER(3, "mbox: %s msgnum: %d curdir: %s", vmu->mailbox, curmsg, curdir);
8419
8420 /* Must always populate duration correctly */
8421 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
8422 ast_trace(-1, "msgfile: %s\n", msgfile);
8423 strcpy(textfile, msgfile);
8424 strcpy(backup, msgfile);
8425 strcpy(backup_textfile, msgfile);
8426 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
8427 strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
8428 strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
8429
8430 if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
8431 *duration = atoi(duration_str);
8432 } else {
8433 *duration = 0;
8434 }
8435
8436 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
8437 if (cmd)
8438 retries = 0;
8439 switch (cmd) {
8440 case '1':
8441
8442#ifdef IMAP_STORAGE
8443 /* Record new intro file */
8444 if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
8445 msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
8446 }
8447 make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
8448 strncat(vms->introfn, "intro", sizeof(vms->introfn));
8449 ast_play_and_wait(chan, "vm-record-prepend");
8450 ast_play_and_wait(chan, "beep");
8451 cmd = play_record_review(chan, NULL, vms->introfn, vmu->maxsecs, vm_fmts, 1, vmu, (int *) duration, NULL, NULL, record_gain, vms, flag, msg_id, 1);
8452 if (cmd == -1) {
8453 break;
8454 }
8455 cmd = 't';
8456#else
8457
8458 /* prepend a message to the current message, update the metadata and return */
8459 ast_trace(-1, "Prepending to message %d\n", curmsg);
8460
8461 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
8462 ast_trace(-1, "msgfile: %s\n", msgfile);
8463
8464 strcpy(textfile, msgfile);
8465 strncat(textfile, ".txt", sizeof(textfile) - 1);
8466 *duration = 0;
8467
8468 /* if we can't read the message metadata, stop now */
8469 if (!valid_config(msg_cfg)) {
8470 cmd = 0;
8471 break;
8472 }
8473
8474 /* Back up the original file, so we can retry the prepend and restore it after forward. */
8475#ifndef IMAP_STORAGE
8476 if (already_recorded) {
8477 ast_trace(-1, "Restoring '%s' to '%s'\n", backup, msgfile);
8478 ast_filecopy(backup, msgfile, NULL);
8479 copy(backup_textfile, textfile);
8480 }
8481 else {
8482 ast_trace(-1, "Backing up '%s' to '%s'\n", backup, msgfile);
8483 ast_filecopy(msgfile, backup, NULL);
8484 copy(textfile, backup_textfile);
8485 }
8486#endif
8487 already_recorded = 1;
8488
8489 if (record_gain)
8490 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
8491
8492 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, ast_play_and_prepend, chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
8493
8494 if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
8495 ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
8497 ast_filerename(backup, msgfile, NULL);
8498 }
8499
8500 if (record_gain)
8501 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
8502
8503
8504 if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
8505 *duration = atoi(duration_str);
8506
8507 if (prepend_duration) {
8508 struct ast_category *msg_cat;
8509 /* need enough space for a maximum-length message duration */
8510 char duration_buf[12];
8511
8512 *duration += prepend_duration;
8513 ast_trace(-1, "Prepending duration: %d total duration: %ld\n", prepend_duration, *duration);
8514 msg_cat = ast_category_get(msg_cfg, "message", NULL);
8515 snprintf(duration_buf, sizeof(duration_buf), "%ld", *duration);
8516 if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
8517 ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
8518 }
8519 }
8520
8521#endif
8522 break;
8523 case '2':
8524 /* NULL out introfile so we know there is no intro! */
8525#ifdef IMAP_STORAGE
8526 *vms->introfn = '\0';
8527#endif
8528 cmd = 't';
8529 break;
8530 case '*':
8531 cmd = '*';
8532 break;
8533 default:
8534 /* If time_out and return to menu, reset already_recorded */
8535 already_recorded = 0;
8536
8537 cmd = ast_play_and_wait(chan, "vm-forwardoptions");
8538 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
8539 if (!cmd) {
8540 cmd = ast_play_and_wait(chan, "vm-starmain");
8541 /* "press star to return to the main menu" */
8542 }
8543 if (!cmd) {
8544 cmd = ast_waitfordigit(chan, 6000);
8545 }
8546 if (!cmd) {
8547 retries++;
8548 }
8549 if (retries > 3) {
8550 cmd = '*'; /* Let's cancel this beast */
8551 }
8552 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8553 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
8554 }
8555 }
8556
8557 if (valid_config(msg_cfg))
8558 ast_config_destroy(msg_cfg);
8559 if (prepend_duration)
8560 *duration = prepend_duration;
8561
8562 if (already_recorded && cmd == -1) {
8563 /* restore original message if prepention cancelled */
8564 ast_trace(-1, "Restoring '%s' to '%s'\n", backup, msgfile);
8565 ast_filerename(backup, msgfile, NULL);
8566 rename(backup_textfile, textfile);
8567 }
8568
8569 if (cmd == 't' || cmd == 'S') { /* XXX entering this block with a value of 'S' is probably no longer possible. */
8570 cmd = 0;
8571 }
8572 SCOPE_EXIT_RTN_VALUE(cmd, "Done. CMD: %d %c\n", cmd, cmd >= 32 && cmd < 127 ? cmd : '?');
8573}
8574
8575static void queue_mwi_event(const char *channel_id, const char *box, int urgent, int new, int old)
8576{
8577 char *mailbox;
8578 char *context;
8579
8581 return;
8582 }
8583
8584 ast_debug(3, "Queueing event for mailbox %s New: %d Old: %d\n", box, new + urgent, old);
8585 ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
8586
8588 struct ao2_iterator *aliases;
8589 struct mailbox_alias_mapping *mapping;
8590
8592 while ((mapping = ao2_iterator_next(aliases))) {
8593 char alias[strlen(mapping->alias) + 1];
8594 strcpy(alias, mapping->alias); /* safe */
8595 mailbox = NULL;
8596 context = NULL;
8597 ast_debug(3, "Found alias mapping: %s -> %s\n", mapping->alias, box);
8599 ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
8600 ao2_ref(mapping, -1);
8601 }
8603 }
8604}
8605
8606/*!
8607 * \brief Sends email notification that a user has a new voicemail waiting for them.
8608 * \param chan
8609 * \param vmu
8610 * \param vms
8611 * \param msgnum
8612 * \param duration
8613 * \param fmt
8614 * \param cidnum The Caller ID phone number value.
8615 * \param cidname The Caller ID name value.
8616 * \param flag
8617 *
8618 * \return zero on success, -1 on error.
8619 */
8620static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag)
8621{
8622 char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
8623 int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
8624 const char *category;
8625 char *myserveremail = serveremail;
8626
8627 ast_channel_lock(chan);
8628 if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
8629 category = ast_strdupa(category);
8630 }
8631 ast_channel_unlock(chan);
8632
8633#ifndef IMAP_STORAGE
8634 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
8635#else
8636 snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
8637#endif
8638 make_file(fn, sizeof(fn), todir, msgnum);
8639 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
8640
8641 if (!ast_strlen_zero(vmu->attachfmt)) {
8642 if (strstr(fmt, vmu->attachfmt))
8643 fmt = vmu->attachfmt;
8644 else
8645 ast_log(AST_LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->attachfmt, fmt, vmu->mailbox, vmu->context);
8646 }
8647
8648 /* Attach only the first format */
8649 fmt = ast_strdupa(fmt);
8650 stringp = fmt;
8651 strsep(&stringp, "|");
8652
8653 if (!ast_strlen_zero(vmu->serveremail))
8654 myserveremail = vmu->serveremail;
8655
8656 if (!ast_strlen_zero(vmu->email)) {
8657 int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
8658 char *msg_id = NULL;
8659#ifdef IMAP_STORAGE
8660 struct ast_config *msg_cfg;
8661 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8662 char filename[PATH_MAX];
8663
8664 snprintf(filename, sizeof(filename), "%s.txt", fn);
8665 msg_cfg = ast_config_load(filename, config_flags);
8666 if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
8667 msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
8668 ast_config_destroy(msg_cfg);
8669 }
8670#endif
8671
8672 if (attach_user_voicemail)
8673 RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
8674
8675 /* XXX possible imap issue, should category be NULL XXX */
8676 sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, fn, NULL, fmt, duration, attach_user_voicemail, chan, category, flag, msg_id);
8677
8678 if (attach_user_voicemail)
8679 DISPOSE(todir, msgnum);
8680 }
8681
8682 if (!ast_strlen_zero(vmu->pager)) {
8683 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
8684 }
8685
8686 if (ast_test_flag(vmu, VM_DELETE))
8687 DELETE(todir, msgnum, fn, vmu);
8688
8689 /* Leave voicemail for someone */
8690 if (ast_app_has_voicemail(ext_context, NULL))
8691 ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
8692
8693 queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgentmsgs, newmsgs, oldmsgs);
8694 run_externnotify(vmu->context, vmu->mailbox, flag);
8695
8696#ifdef IMAP_STORAGE
8697 vm_delete(fn); /* Delete the file, but not the IMAP message */
8698 if (ast_test_flag(vmu, VM_DELETE)) { /* Delete the IMAP message if delete = yes */
8699 vm_imap_delete(NULL, vms->curmsg, vmu);
8700 vms->newmessages--; /* Fix new message count */
8701 }
8702#endif
8703
8704 return 0;
8705}
8706
8707/*!
8708 * \brief Sends a voicemail message to a mailbox recipient.
8709 * \param chan
8710 * \param context
8711 * \param vms
8712 * \param sender
8713 * \param fmt
8714 * \param is_new_message Used to indicate the mode for which this method was invoked.
8715 * Will be 0 when called to forward an existing message (option 8)
8716 * Will be 1 when called to leave a message (option 3->5)
8717 * \param record_gain
8718 * \param urgent
8719 *
8720 * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
8721 *
8722 * When in the leave message mode (is_new_message == 1):
8723 * - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
8724 * - attempt to determine the context and mailbox, and then invoke leave_message() function to record and store the message.
8725 *
8726 * When in the forward message mode (is_new_message == 0):
8727 * - retrieves the current message to be forwarded
8728 * - copies the original message to a temporary file, so updates to the envelope can be done.
8729 * - determines the target mailbox and folders
8730 * - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
8731 *
8732 * \return zero on success, -1 on error.
8733 */
8734static int forward_message(struct ast_channel *chan, char *context, struct vm_state *vms, struct ast_vm_user *sender, char *fmt, int is_new_message, signed char record_gain, int urgent)
8735{
8736#ifdef IMAP_STORAGE
8737 int todircount = 0;
8738 struct vm_state *dstvms;
8739#endif
8740 char username[70]="";
8741 char fn[PATH_MAX]; /* for playback of name greeting */
8742 char ecodes[16] = "#";
8743 int res = 0, cmd = 0;
8744 struct ast_vm_user *receiver = NULL, *vmtmp;
8746 char *stringp;
8747 const char *s;
8748 const char mailbox_context[256];
8749 int saved_messages = 0;
8750 int valid_extensions = 0;
8751 char *dir;
8752 int curmsg;
8753 char urgent_str[7] = "";
8754 int prompt_played = 0;
8755#ifndef IMAP_STORAGE
8756 char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
8757#endif
8758 SCOPE_ENTER(3, "%s: user: %s dir: %s msg: %d\n", ast_channel_name(chan),
8759 vms->username, vms->curdir, vms->curmsg);
8760
8762 ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
8763 }
8764
8765 if (vms == NULL) {
8766 SCOPE_EXIT_RTN_VALUE(-1, "vms is NULL\n");
8767 }
8768 dir = vms->curdir;
8769 curmsg = vms->curmsg;
8770
8771 ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
8772 while (!res && !valid_extensions) {
8773 int use_directory = 0;
8775 int done = 0;
8776 int retries = 0;
8777 cmd = 0;
8778 while ((cmd >= 0) && !done ){
8779 if (cmd)
8780 retries = 0;
8781 switch (cmd) {
8782 case '1':
8783 use_directory = 0;
8784 done = 1;
8785 break;
8786 case '2':
8787 use_directory = 1;
8788 done = 1;
8789 break;
8790 case '*':
8791 cmd = 't';
8792 done = 1;
8793 break;
8794 default:
8795 /* Press 1 to enter an extension press 2 to use the directory */
8796 cmd = ast_play_and_wait(chan, "vm-forward");
8797 if (!cmd) {
8798 cmd = ast_waitfordigit(chan, 3000);
8799 }
8800 if (!cmd) {
8801 retries++;
8802 }
8803 if (retries > 3) {
8804 cmd = 't';
8805 done = 1;
8806 }
8807 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8808 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
8809 }
8810 }
8811 if (cmd < 0 || cmd == 't')
8812 break;
8813 }
8814
8815 if (use_directory) {
8816 /* use app_directory */
8817
8818 struct ast_app* directory_app;
8819
8820 directory_app = pbx_findapp("Directory");
8821 if (directory_app) {
8822 char vmcontext[256];
8823 char old_context[strlen(ast_channel_context(chan)) + 1];
8824 char old_exten[strlen(ast_channel_exten(chan)) + 1];
8825 int old_priority;
8826 /* make backup copies */
8827 strcpy(old_context, ast_channel_context(chan)); /* safe */
8828 strcpy(old_exten, ast_channel_exten(chan)); /* safe */
8829 old_priority = ast_channel_priority(chan);
8830
8831 /* call the Directory, changes the channel */
8832 snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
8833 res = pbx_exec(chan, directory_app, vmcontext);
8834
8835 ast_copy_string(username, ast_channel_exten(chan), sizeof(username));
8836
8837 /* restore the old context, exten, and priority */
8838 ast_channel_context_set(chan, old_context);
8839 ast_channel_exten_set(chan, old_exten);
8840 ast_channel_priority_set(chan, old_priority);
8841 } else {
8842 ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
8844 }
8845 } else {
8846 /* Ask for an extension */
8847 res = ast_streamfile(chan, "vm-extension", ast_channel_language(chan)); /* "extension" */
8848 prompt_played++;
8849 if (res || prompt_played > 4)
8850 break;
8851 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#")) < 0)
8852 break;
8853 }
8854
8855 /* start all over if no username */
8856 if (ast_strlen_zero(username))
8857 continue;
8858 stringp = username;
8859 s = strsep(&stringp, "*");
8860 /* start optimistic */
8861 valid_extensions = 1;
8862 while (s) {
8863 snprintf((char*)mailbox_context, sizeof(mailbox_context), "%s@%s", s, context ? context : "default");
8864 if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
8865 int oldmsgs;
8866 int newmsgs;
8867 int capacity;
8868
8869 if (inboxcount(mailbox_context, &newmsgs, &oldmsgs)) {
8870 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", mailbox_context);
8871 /* Shouldn't happen, but allow trying another extension if it does */
8872 res = ast_play_and_wait(chan, "pbx-invalid");
8873 valid_extensions = 0;
8874 break;
8875 }
8876#ifdef IMAP_STORAGE
8877 if (!(dstvms = get_vm_state_by_mailbox(s, context, 0))) {
8878 if (!(dstvms = create_vm_state_from_user(receiver))) {
8879 ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
8880 /* Shouldn't happen, but allow trying another extension if it does */
8881 res = ast_play_and_wait(chan, "pbx-invalid");
8882 valid_extensions = 0;
8883 break;
8884 }
8885 }
8886 check_quota(dstvms, imapfolder);
8887 if (dstvms->quota_limit && dstvms->quota_usage >= dstvms->quota_limit) {
8888 ast_log(LOG_NOTICE, "Mailbox '%s' is exceeded quota %u >= %u\n", mailbox_context, dstvms->quota_usage, dstvms->quota_limit);
8889 res = ast_play_and_wait(chan, "vm-mailboxfull");
8890 valid_extensions = 0;
8891 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8892 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8893 free_user(vmtmp);
8894 }
8895 break;
8896 }
8897#endif
8898 capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
8899 if ((newmsgs + oldmsgs) >= capacity) {
8900 ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", mailbox_context, capacity);
8901 res = ast_play_and_wait(chan, "vm-mailboxfull");
8902 valid_extensions = 0;
8903 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8904 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8905 free_user(vmtmp);
8906 }
8907 inprocess_count(receiver->mailbox, receiver->context, -1);
8908 break;
8909 }
8910 AST_LIST_INSERT_HEAD(&extensions, receiver, list);
8911 } else {
8912 /* XXX Optimization for the future. When we encounter a single bad extension,
8913 * bailing out on all of the extensions may not be the way to go. We should
8914 * probably just bail on that single extension, then allow the user to enter
8915 * several more. XXX
8916 */
8917 while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8918 free_user(receiver);
8919 }
8920 ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", mailbox_context);
8921 /* "I am sorry, that's not a valid extension. Please try again." */
8922 res = ast_play_and_wait(chan, "pbx-invalid");
8923 valid_extensions = 0;
8924 break;
8925 }
8926
8927 /* play name if available, else play extension number */
8928 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
8929 SCOPE_CALL(-1, RETRIEVE, fn, -1, s, receiver->context);
8930 if (ast_fileexists(fn, NULL, NULL) > 0) {
8931 res = ast_stream_and_wait(chan, fn, ecodes);
8932 if (res) {
8933 SCOPE_CALL(-1, DISPOSE, fn, -1);
8934 return res;
8935 }
8936 } else {
8937 res = ast_say_digit_str(chan, s, ecodes, ast_channel_language(chan));
8938 }
8939 SCOPE_CALL(-1, DISPOSE, fn, -1);
8940
8941 s = strsep(&stringp, "*");
8942 }
8943 /* break from the loop of reading the extensions */
8944 if (valid_extensions)
8945 break;
8946 }
8947 /* check if we're clear to proceed */
8948 if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
8949 return res;
8950 if (is_new_message == 1) {
8951 struct leave_vm_options leave_options;
8952 char mailbox[AST_MAX_EXTENSION * 2 + 2];
8953 snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
8954
8955 /* Send VoiceMail */
8956 memset(&leave_options, 0, sizeof(leave_options));
8957 leave_options.record_gain = record_gain;
8958 leave_options.beeptone = "beep";
8959 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, leave_voicemail, chan, mailbox, &leave_options);
8960 } else {
8961 /* Forward VoiceMail */
8962 long duration = 0;
8963 struct vm_state vmstmp;
8964 int copy_msg_result = 0;
8965#ifdef IMAP_STORAGE
8966 char filename[PATH_MAX];
8967 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8968 const char *msg_id = NULL;
8969 struct ast_config *msg_cfg;
8970#endif
8971 memcpy(&vmstmp, vms, sizeof(vmstmp));
8972
8973 SCOPE_CALL(-1, RETRIEVE, dir, curmsg, sender->mailbox, sender->context);
8974#ifdef IMAP_STORAGE
8975 make_file(filename, sizeof(filename), dir, curmsg);
8976 strncat(filename, ".txt", sizeof(filename) - strlen(filename) - 1);
8977 msg_cfg = ast_config_load(filename, config_flags);
8978 if (msg_cfg && msg_cfg == CONFIG_STATUS_FILEINVALID) {
8979 msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
8980 ast_config_destroy(msg_cfg);
8981 }
8982#endif
8983
8984 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, vm_forwardoptions, chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp, urgent_str);
8985 if (!cmd) {
8986 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
8987#ifdef IMAP_STORAGE
8988 int attach_user_voicemail;
8989 char *myserveremail = serveremail;
8990
8991 /* get destination mailbox */
8992 dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
8993 if (!dstvms) {
8994 dstvms = create_vm_state_from_user(vmtmp);
8995 }
8996 if (dstvms) {
8997 init_mailstream(dstvms, 0);
8998 if (!dstvms->mailstream) {
8999 ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
9000 } else {
9001 copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
9002 run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str);
9003 }
9004 } else {
9005 ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
9006 }
9007 if (!ast_strlen_zero(vmtmp->serveremail))
9008 myserveremail = vmtmp->serveremail;
9009 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
9010 /* NULL category for IMAP storage */
9011 sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
9012 dstvms->curbox,
9013 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
9014 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
9015 vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
9016 NULL, urgent_str, msg_id);
9017#else
9018 copy_msg_result = SCOPE_CALL_WITH_INT_RESULT(-1, copy_message, chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str, NULL);
9019#endif
9020 saved_messages++;
9022 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
9023 free_user(vmtmp);
9024 if (res)
9025 break;
9026 }
9028 if (saved_messages > 0 && !copy_msg_result) {
9029 /* give confirmation that the message was saved */
9030 /* commented out since we can't forward batches yet
9031 if (saved_messages == 1)
9032 res = ast_play_and_wait(chan, "vm-message");
9033 else
9034 res = ast_play_and_wait(chan, "vm-messages");
9035 if (!res)
9036 res = ast_play_and_wait(chan, "vm-saved"); */
9037 res = ast_play_and_wait(chan, "vm-msgforwarded");
9038 }
9039#ifndef IMAP_STORAGE
9040 else {
9041 /* with IMAP, mailbox full warning played by imap_check_limits */
9042 res = ast_play_and_wait(chan, "vm-mailboxfull");
9043 }
9044 /* Restore original message without prepended message if backup exists */
9045 make_file(msgfile, sizeof(msgfile), dir, curmsg);
9046 strcpy(textfile, msgfile);
9047 strcpy(backup, msgfile);
9048 strcpy(backup_textfile, msgfile);
9049 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
9050 strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
9051 strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
9052 if (ast_fileexists(backup, NULL, NULL) > 0) {
9053 ast_filerename(backup, msgfile, NULL);
9054 rename(backup_textfile, textfile);
9055 }
9056#endif
9057 }
9058 SCOPE_CALL(-1, DISPOSE, dir, curmsg);
9059#ifndef IMAP_STORAGE
9060 if (cmd) { /* assuming hangup, cleanup backup file */
9061 make_file(msgfile, sizeof(msgfile), dir, curmsg);
9062 strcpy(textfile, msgfile);
9063 strcpy(backup_textfile, msgfile);
9064 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
9065 strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
9066 rename(backup_textfile, textfile);
9067 }
9068#endif
9069 }
9070
9071 /* If anything failed above, we still have this list to free */
9072 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
9073 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
9074 free_user(vmtmp);
9075 }
9076 SCOPE_EXIT_RTN_VALUE(res ? res : cmd, "Done. res: %d cmd: %d\n", res, cmd);
9077}
9078
9079static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
9080{
9081 int res;
9082 if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
9083 ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file);
9084 return res;
9085}
9086
9087static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
9088{
9089 ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
9091}
9092
9093static int play_message_category(struct ast_channel *chan, const char *category)
9094{
9095 int res = 0;
9096
9097 if (!ast_strlen_zero(category))
9098 res = ast_play_and_wait(chan, category);
9099
9100 if (res) {
9101 ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
9102 res = 0;
9103 }
9104
9105 return res;
9106}
9107
9108static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
9109{
9110 int res = 0;
9111 struct vm_zone *the_zone = NULL;
9112 time_t t;
9113
9114 if (ast_get_time_t(origtime, &t, 0, NULL)) {
9115 ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
9116 return 0;
9117 }
9118
9119 /* Does this user have a timezone specified? */
9120 if (!ast_strlen_zero(vmu->zonetag)) {
9121 /* Find the zone in the list */
9122 struct vm_zone *z;
9125 if (!strcmp(z->name, vmu->zonetag)) {
9126 the_zone = z;
9127 break;
9128 }
9129 }
9131 }
9132
9133/* No internal variable parsing for now, so we'll comment it out for the time being */
9134#if 0
9135 /* Set the DIFF_* variables */
9136 ast_localtime(&t, &time_now, NULL);
9137 tv_now = ast_tvnow();
9138 ast_localtime(&tv_now, &time_then, NULL);
9139
9140 /* Day difference */
9141 if (time_now.tm_year == time_then.tm_year)
9142 snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
9143 else
9144 snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
9145 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
9146
9147 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
9148#endif
9149 if (the_zone) {
9150 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), the_zone->msg_format, the_zone->timezone);
9151 } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
9152 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9153 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
9154 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q H 'digits/kai' M ", NULL);
9155 } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */
9156 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9157 } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
9158 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
9159 } else if (!strcasecmp(ast_channel_language(chan),"ja")) { /* Japanese syntax */
9160 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "PHM q 'jp-ni' 'vm-received'", NULL);
9161 } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
9162 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/nl-om' HM", NULL);
9163 } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
9164 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9165 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
9166 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q HM", NULL);
9167 } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* Brazilian PORTUGUESE syntax */
9168 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
9169 } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
9170 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' dB 'digits/at' k 'and' M", NULL);
9171 } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
9172 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "qR 'vm-received'", NULL);
9173 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
9174 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' A 'digits/day' dB 'digits/year' Y 'digits/at' k 'hours' M 'minutes'", NULL);
9175 } else {
9176 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' IMp", NULL);
9177 }
9178#if 0
9179 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
9180#endif
9181 return res;
9182}
9183
9184
9185
9186static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback, int saycidnumber)
9187{
9188 int res = 0;
9189 int i;
9190 char *callerid, *name;
9191 char prefile[PATH_MAX] = "";
9192
9193 /* If voicemail cid is not enabled, or we didn't get cid or context from
9194 * the attribute file, leave now.
9195 *
9196 * TODO Still need to change this so that if this function is called by the
9197 * message envelope (and someone is explicitly requesting to hear the CID),
9198 * it does not check to see if CID is enabled in the config file.
9199 */
9200 if ((cid == NULL)||(context == NULL))
9201 return res;
9202
9203 /* Strip off caller ID number from name */
9204 ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
9205 ast_callerid_parse(cid, &name, &callerid);
9206 if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
9207 /* Check for internal contexts and only */
9208 /* say extension when the call didn't come from an internal context in the list */
9209 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
9210 ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
9211 if ((strcmp(cidinternalcontexts[i], context) == 0))
9212 break;
9213 }
9214 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
9215 if (!res) {
9216 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
9217 if (!ast_strlen_zero(prefile)) {
9218 /* See if we can find a recorded name for this callerid
9219 * and if found, use that instead of saying number. */
9220 if (ast_fileexists(prefile, NULL, NULL) > 0) {
9221 ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
9222 if (!callback)
9223 res = wait_file2(chan, vms, "vm-from");
9224 res = ast_stream_and_wait(chan, prefile, "");
9225 } else {
9226 ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
9227 /* Say "from extension" as one saying to sound smoother */
9228 if (!callback)
9229 res = wait_file2(chan, vms, "vm-from-extension");
9230 res = ast_say_digit_str(chan, callerid, "", ast_channel_language(chan));
9231 }
9232 }
9233 }
9234 } else if (!res) {
9235 ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
9236 /* If there is a recording for this numeric callerid then play that */
9237 if (!callback) {
9238 /* See if we can find a recorded name for this person instead of their extension number */
9239 snprintf(prefile, sizeof(prefile), "%s/recordings/callerids/%s", ast_config_AST_SPOOL_DIR, callerid);
9240 if (!saycidnumber && ast_fileexists(prefile, NULL, NULL) > 0) {
9241 ast_verb(3, "Playing recorded name for CID number '%s' - '%s'\n", callerid,prefile);
9242 wait_file2(chan, vms, "vm-from");
9243 res = ast_stream_and_wait(chan, prefile, "");
9244 ast_verb(3, "Played recorded name result '%d'\n", res);
9245 } else {
9246 /* Since this is all nicely figured out, why not say "from phone number" in this case" */
9247 wait_file2(chan, vms, "vm-from-phonenumber");
9248 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
9249 }
9250 } else {
9251 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
9252 }
9253 }
9254 } else {
9255 /* Number unknown */
9256 ast_debug(1, "VM-CID: From an unknown number\n");
9257 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
9258 res = wait_file2(chan, vms, "vm-unknown-caller");
9259 }
9260 return res;
9261}
9262
9263static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
9264{
9265 int res = 0;
9266 int durationm;
9267 int durations;
9268 /* Verify that we have a duration for the message */
9269 if (duration == NULL)
9270 return res;
9271
9272 /* Convert from seconds to minutes */
9273 durations = atoi(duration);
9274 durationm = (durations / 60);
9275
9276 ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
9277
9278 if ((!res) && (durationm >= minduration)) {
9279 res = wait_file2(chan, vms, "vm-duration");
9280
9281 /* POLISH syntax */
9282 if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9283 div_t num = div(durationm, 10);
9284
9285 if (durationm == 1) {
9286 res = ast_play_and_wait(chan, "digits/1z");
9287 res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
9288 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
9289 if (num.rem == 2) {
9290 if (!num.quot) {
9291 res = ast_play_and_wait(chan, "digits/2-ie");
9292 } else {
9293 res = say_and_wait(chan, durationm - 2 , ast_channel_language(chan));
9294 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
9295 }
9296 } else {
9297 res = say_and_wait(chan, durationm, ast_channel_language(chan));
9298 }
9299 res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
9300 } else {
9301 res = say_and_wait(chan, durationm, ast_channel_language(chan));
9302 res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
9303 }
9304 /* DEFAULT syntax */
9305 } else {
9306 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
9307 res = wait_file2(chan, vms, "vm-minutes");
9308 }
9309 }
9310 return res;
9311}
9312
9313static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
9314{
9315 int res = 0;
9316 char filename[PATH_MAX], *cid;
9317 const char *origtime, *context, *category, *duration, *flag;
9318 struct ast_config *msg_cfg;
9319 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
9320 SCOPE_ENTER(3, "%s: user: %s dir: %s msg: %d\n", ast_channel_name(chan),
9321 vms->username, vms->curdir, vms->curmsg);
9322
9323 vms->starting = 0;
9324 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
9325 adsi_message(chan, vms);
9326 if (!vms->curmsg) {
9327 res = wait_file2(chan, vms, "vm-first"); /* "First" */
9328 } else if (vms->curmsg == vms->lastmsg) {
9329 res = wait_file2(chan, vms, "vm-last"); /* "last" */
9330 }
9331
9332 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
9333 SCOPE_CALL(-1, RETRIEVE, vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
9334 msg_cfg = ast_config_load(filename, config_flags);
9335 if (!valid_config(msg_cfg)) {
9336 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
9337 return 0;
9338 }
9339 flag = ast_variable_retrieve(msg_cfg, "message", "flag");
9340
9341 /* Play the word urgent if we are listening to urgent messages */
9342 if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
9343 res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
9344 }
9345
9346 if (!res) {
9347 /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
9348 /* POLISH syntax */
9349 if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9350 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9351 int ten, one;
9352 char nextmsg[256];
9353 ten = (vms->curmsg + 1) / 10;
9354 one = (vms->curmsg + 1) % 10;
9355
9356 if (vms->curmsg < 20) {
9357 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
9358 res = wait_file2(chan, vms, nextmsg);
9359 } else {
9360 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
9361 res = wait_file2(chan, vms, nextmsg);
9362 if (one > 0) {
9363 if (!res) {
9364 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
9365 res = wait_file2(chan, vms, nextmsg);
9366 }
9367 }
9368 }
9369 }
9370 if (!res)
9371 res = wait_file2(chan, vms, "vm-message");
9372 /* HEBREW syntax */
9373 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
9374 if (!vms->curmsg) {
9375 res = wait_file2(chan, vms, "vm-message");
9376 res = wait_file2(chan, vms, "vm-first");
9377 } else if (vms->curmsg == vms->lastmsg) {
9378 res = wait_file2(chan, vms, "vm-message");
9379 res = wait_file2(chan, vms, "vm-last");
9380 } else {
9381 res = wait_file2(chan, vms, "vm-message");
9382 res = wait_file2(chan, vms, "vm-number");
9383 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9384 }
9385 /* ICELANDIC syntax */
9386 } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) {
9387 res = wait_file2(chan, vms, "vm-message");
9388 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9389 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "n");
9390 }
9391 /* VIETNAMESE syntax */
9392 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
9393 if (!vms->curmsg) {
9394 res = wait_file2(chan, vms, "vm-message");
9395 res = wait_file2(chan, vms, "vm-first");
9396 } else if (vms->curmsg == vms->lastmsg) {
9397 res = wait_file2(chan, vms, "vm-message");
9398 res = wait_file2(chan, vms, "vm-last");
9399 } else {
9400 res = wait_file2(chan, vms, "vm-message");
9401 res = wait_file2(chan, vms, "vm-number");
9402 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9403 }
9404 } else {
9405 if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
9406 res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
9407 } else { /* DEFAULT syntax */
9408 res = wait_file2(chan, vms, "vm-message");
9409 }
9410 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9411 if (!res) {
9412 ast_test_suite_event_notify("PLAYBACK", "Message: message number");
9413 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
9414 }
9415 }
9416 }
9417 }
9418
9419 if (!valid_config(msg_cfg)) {
9420 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
9421 }
9422
9423 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
9424 SCOPE_CALL(-1, DISPOSE, vms->curdir, vms->curmsg);
9425 ast_config_destroy(msg_cfg);
9426 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "No origtime?!\n");
9427 }
9428
9429 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
9430 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
9431 category = ast_variable_retrieve(msg_cfg, "message", "category");
9432
9433 context = ast_variable_retrieve(msg_cfg, "message", "context");
9434 if (!res) {
9435 res = play_message_category(chan, category);
9436 }
9437 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
9438 res = play_message_datetime(chan, vmu, origtime, filename);
9439 }
9440 if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
9441 res = play_message_callerid(chan, vms, cid, context, 0, 0);
9442 }
9443 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
9444 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
9445 }
9446 /* Allow pressing '1' to skip envelope / callerid */
9447 if (res == '1') {
9448 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
9449 res = 0;
9450 }
9451 ast_config_destroy(msg_cfg);
9452
9453 if (!res) {
9454 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
9455#ifdef IMAP_STORAGE
9456 ast_mutex_lock(&vms->lock);
9457#endif
9458 vms->heard[vms->curmsg] = 1;
9459#ifdef IMAP_STORAGE
9460 ast_mutex_unlock(&vms->lock);
9461 /*IMAP storage stores any prepended message from a forward
9462 * as a separate file from the rest of the message
9463 */
9464 if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
9465 wait_file(chan, vms, vms->introfn);
9466 }
9467#endif
9468 if ((res = wait_file(chan, vms, vms->fn)) < 0) {
9469 ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
9470 res = 0;
9471 }
9472 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
9473 isprint(res) ? res : '?', isprint(res) ? res : '?');
9474 }
9475 SCOPE_CALL(-1, DISPOSE, vms->curdir, vms->curmsg);
9476 SCOPE_EXIT_RTN_VALUE(res, "Done: RC: %d\n", res);
9477}
9478
9479#ifdef IMAP_STORAGE
9480static int imap_remove_file(char *dir, int msgnum)
9481{
9482 char fn[PATH_MAX];
9483 char full_fn[PATH_MAX];
9484 char intro[PATH_MAX] = {0,};
9485
9486 if (msgnum > -1) {
9487 make_file(fn, sizeof(fn), dir, msgnum);
9488 snprintf(intro, sizeof(intro), "%sintro", fn);
9489 } else
9490 ast_copy_string(fn, dir, sizeof(fn));
9491
9492 if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
9493 ast_filedelete(fn, NULL);
9494 if (!ast_strlen_zero(intro)) {
9496 }
9497 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
9498 unlink(full_fn);
9499 }
9500 return 0;
9501}
9502
9503
9504
9505static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
9506{
9507 char *file, *filename;
9508 char arg[11];
9509 int i;
9510 BODY* body;
9511 int curr_mbox;
9512
9513 file = strrchr(ast_strdupa(dir), '/');
9514 if (file) {
9515 *file++ = '\0';
9516 } else {
9517 ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
9518 return -1;
9519 }
9520
9521 ast_mutex_lock(&vms->lock);
9522
9523 /* get the current mailbox so that we can point the mailstream back to it later */
9524 curr_mbox = get_folder_by_name(vms->curbox);
9525
9526 if (init_mailstream(vms, GREETINGS_FOLDER) || !vms->mailstream) {
9527 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
9528 ast_mutex_unlock(&vms->lock);
9529 return -1;
9530 }
9531
9532 for (i = 0; i < vms->mailstream->nmsgs; i++) {
9533 mail_fetchstructure(vms->mailstream, i + 1, &body);
9534 /* We have the body, now we extract the file name of the first attachment. */
9535 if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
9536 char *attachment = body->nested.part->next->body.parameter->value;
9537 char copy[strlen(attachment) + 1];
9538
9539 strcpy(copy, attachment); /* safe */
9540 attachment = copy;
9541
9542 filename = strsep(&attachment, ".");
9543 if (!strcmp(filename, file)) {
9544 snprintf(arg, sizeof(arg), "%d", i + 1);
9545 mail_setflag(vms->mailstream, arg, "\\DELETED");
9546 }
9547 } else {
9548 ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
9549 ast_mutex_unlock(&vms->lock);
9550 return -1;
9551 }
9552 }
9553 mail_expunge(vms->mailstream);
9554
9555 if (curr_mbox != -1) {
9556 /* restore previous mbox stream */
9557 if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
9558 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
9559 }
9560 }
9561
9562 ast_mutex_unlock(&vms->lock);
9563 return 0;
9564}
9565
9566#elif !defined(IMAP_STORAGE)
9567static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
9568{
9569 int count_msg, last_msg;
9570 SCOPE_ENTER(3, "user: %s dir: %s msg: %d box %d\n",
9571 vms->username, vms->curdir, vms->curmsg, box);
9572
9573 ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
9574
9575 /* Rename the member vmbox HERE so that we don't try to return before
9576 * we know what's going on.
9577 */
9578 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
9579
9580 /* Faster to make the directory than to check if it exists. */
9581 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
9582
9583 /* traverses directory using readdir (or select query for ODBC) */
9584 count_msg = COUNT(vmu, vms->curdir);
9585 if (count_msg < 0) {
9586 SCOPE_EXIT_RTN_VALUE(count_msg, "msgs: %d\n", count_msg);
9587 } else {
9588 vms->lastmsg = count_msg - 1;
9589 }
9590
9591 if (vm_allocate_dh(vms, vmu, count_msg)) {
9592 SCOPE_EXIT_RTN_VALUE(-1, "failed to allocate dh\n");
9593 }
9594
9595 /*
9596 The following test is needed in case sequencing gets messed up.
9597 There appears to be more than one way to mess up sequence, so
9598 we will not try to find all of the root causes--just fix it when
9599 detected.
9600 */
9601
9602 if (vm_lock_path(vms->curdir)) {
9603 SCOPE_EXIT_LOG_RTN_VALUE(ERROR_LOCK_PATH, AST_LOG_ERROR, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
9604 }
9605
9606 /* for local storage, checks directory for messages up to MAXMSGLIMIT */
9607 last_msg = LAST_MSG_INDEX(vms->curdir);
9608 ast_unlock_path(vms->curdir);
9609
9610 if (last_msg < -1) {
9611 SCOPE_EXIT_RTN_VALUE(last_msg, "last msg: %d\n", last_msg);
9612 } else if (vms->lastmsg != last_msg) {
9613 ast_log(LOG_NOTICE, "Resequencing Mailbox: %s, expected %d but found %d message(s) in box with max threshold of %d.\n", vms->curdir, last_msg + 1, vms->lastmsg + 1, vmu->maxmsg);
9614 resequence_mailbox(vmu, vms->curdir, count_msg);
9615 }
9616
9617 SCOPE_EXIT_RTN_VALUE(0, "Done\n");
9618}
9619#endif
9620
9621static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
9622{
9623 int x = 0;
9624 int last_msg_idx = 0;
9625
9626#ifndef IMAP_STORAGE
9627 int res = 0, nummsg;
9628 char fn2[PATH_MAX];
9629#endif
9630 SCOPE_ENTER(3, "user: %s dir: %s msg: %d\n",
9631 vms->username, vms->curdir, vms->curmsg);
9632
9633 if (vms->lastmsg <= -1) {
9634 ast_trace(-1, "No messages in mailbox\n");
9635 goto done;
9636 }
9637
9638 vms->curmsg = -1;
9639#ifndef IMAP_STORAGE
9640 /* Get the deleted messages fixed */
9641 if (vm_lock_path(vms->curdir)) {
9642 SCOPE_EXIT_RTN_VALUE(ERROR_LOCK_PATH, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
9643 }
9644
9645 /* update count as message may have arrived while we've got mailbox open */
9646 last_msg_idx = LAST_MSG_INDEX(vms->curdir);
9647 if (last_msg_idx != vms->lastmsg) {
9648 ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
9649 }
9650
9651 /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
9652 for (x = 0; x < last_msg_idx + 1; x++) {
9653 if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
9654 /* Save this message. It's not in INBOX or hasn't been heard */
9655 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9656 if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
9657 break;
9658 }
9659 vms->curmsg++;
9660 make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
9661 if (strcmp(vms->fn, fn2)) {
9662 SCOPE_CALL(-1, RENAME, vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
9663 }
9664 } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
9665 /* Move to old folder before deleting */
9666 res = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, vms, x, 1, NULL, 0);
9667 if (res == ERROR_LOCK_PATH || res == ERROR_MAX_MSGS) {
9668 /* If save failed do not delete the message */
9669 ast_log(AST_LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
9670 vms->deleted[x] = 0;
9671 vms->heard[x] = 0;
9672 --x;
9673 }
9674 } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
9675 /* Move to deleted folder */
9676 res = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, vms, x, 10, NULL, 0);
9677 if (res == ERROR_LOCK_PATH) {
9678 ast_trace(-1, "Unable to lock path. Not moving message to deleted folder.\n");
9679 /* If save failed do not delete the message */
9680 vms->deleted[x] = 0;
9681 vms->heard[x] = 0;
9682 --x;
9683 }
9684 } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
9685 /* If realtime storage enabled - we should explicitly delete this message,
9686 cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
9687 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9688 res = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, vms->curdir, x, vms->fn, NULL);
9689 if (res) {
9690 SCOPE_CALL(-1, DELETE, vms->curdir, x, vms->fn, vmu);
9691 }
9692 }
9693 }
9694
9695 /* Delete ALL remaining messages */
9696 nummsg = x - 1;
9697 for (x = vms->curmsg + 1; x <= nummsg; x++) {
9698 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9699 res = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, vms->curdir, x, vms->fn, NULL);
9700 if (res) {
9701 SCOPE_CALL(-1, DELETE, vms->curdir, x, vms->fn, vmu);
9702 }
9703 }
9704 ast_unlock_path(vms->curdir);
9705#else /* defined(IMAP_STORAGE) */
9706 ast_mutex_lock(&vms->lock);
9707 if (vms->deleted) {
9708 /* Since we now expunge after each delete, deleting in reverse order
9709 * ensures that no reordering occurs between each step. */
9710 last_msg_idx = vms->dh_arraysize;
9711 for (x = last_msg_idx - 1; x >= 0; x--) {
9712 if (vms->deleted[x]) {
9713 ast_debug(3, "IMAP delete of %d\n", x);
9714 DELETE(vms->curdir, x, vms->fn, vmu);
9715 }
9716 }
9717 }
9718#endif
9719
9720done:
9721 if (vms->deleted) {
9722 ast_free(vms->deleted);
9723 vms->deleted = NULL;
9724 }
9725 if (vms->heard) {
9726 ast_free(vms->heard);
9727 vms->heard = NULL;
9728 }
9729 vms->dh_arraysize = 0;
9730#ifdef IMAP_STORAGE
9731 ast_mutex_unlock(&vms->lock);
9732#endif
9733
9734 SCOPE_EXIT_RTN_VALUE(0, "Done\n");
9735}
9736
9737/* In Greek even though we CAN use a syntax like "friends messages"
9738 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
9739 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
9740 * syntax for the above three categories which is more elegant.
9741 */
9742
9743static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
9744{
9745 int cmd;
9746 char *buf;
9747
9748 buf = ast_alloca(strlen(box) + 2);
9749 strcpy(buf, box);
9750 strcat(buf, "s");
9751
9752 if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
9753 cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
9754 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
9755 } else {
9756 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
9757 return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
9758 }
9759}
9760
9761static int vm_play_folder_name_ja(struct ast_channel *chan, char *box)
9762{
9763 int cmd;
9764
9765 if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
9766 cmd = ast_play_and_wait(chan, box);
9767 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9768 } else {
9769 cmd = ast_play_and_wait(chan, box);
9770 return cmd;
9771 }
9772}
9773
9774static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
9775{
9776 int cmd;
9777
9778 if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
9779 if (!strcasecmp(box, "vm-INBOX"))
9780 cmd = ast_play_and_wait(chan, "vm-new-e");
9781 else
9782 cmd = ast_play_and_wait(chan, "vm-old-e");
9783 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9784 } else {
9785 cmd = ast_play_and_wait(chan, "vm-messages");
9786 return cmd ? cmd : ast_play_and_wait(chan, box);
9787 }
9788}
9789
9790static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
9791{
9792 int cmd;
9793
9794 if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
9795 cmd = ast_play_and_wait(chan, "vm-messages");
9796 return cmd ? cmd : ast_play_and_wait(chan, box);
9797 } else {
9798 cmd = ast_play_and_wait(chan, box);
9799 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9800 }
9801}
9802
9803static int vm_play_folder_name(struct ast_channel *chan, char *box)
9804{
9805 int cmd;
9806
9807 if ( !strncasecmp(ast_channel_language(chan), "it", 2) ||
9808 !strncasecmp(ast_channel_language(chan), "es", 2) ||
9809 !strncasecmp(ast_channel_language(chan), "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
9810 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
9811 return cmd ? cmd : ast_play_and_wait(chan, box);
9812 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {
9813 return vm_play_folder_name_gr(chan, box);
9814 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* Hebrew syntax */
9815 return ast_play_and_wait(chan, box);
9816 } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
9817 return vm_play_folder_name_ja(chan, box);
9818 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9819 return vm_play_folder_name_pl(chan, box);
9820 } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian syntax */
9821 return vm_play_folder_name_ua(chan, box);
9822 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
9823 return ast_play_and_wait(chan, box);
9824 } else { /* Default English */
9825 cmd = ast_play_and_wait(chan, box);
9826 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
9827 }
9828}
9829
9830/* GREEK SYNTAX
9831 In greek the plural for old/new is
9832 different so we need the following files
9833 We also need vm-denExeteMynhmata because
9834 this syntax is different.
9835
9836 -> vm-Olds.wav : "Palia"
9837 -> vm-INBOXs.wav : "Nea"
9838 -> vm-denExeteMynhmata : "den exete mynhmata"
9839*/
9840
9841
9842static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
9843{
9844 int res = 0;
9845
9846 if (vms->newmessages) {
9847 res = ast_play_and_wait(chan, "vm-youhave");
9848 if (!res)
9850 if (!res) {
9851 if (vms->newmessages == 1) {
9852 res = ast_play_and_wait(chan, "vm-INBOX");
9853 if (!res)
9854 res = ast_play_and_wait(chan, "vm-message");
9855 } else {
9856 res = ast_play_and_wait(chan, "vm-INBOXs");
9857 if (!res)
9858 res = ast_play_and_wait(chan, "vm-messages");
9859 }
9860 }
9861 } else if (vms->oldmessages){
9862 res = ast_play_and_wait(chan, "vm-youhave");
9863 if (!res)
9865 if (vms->oldmessages == 1){
9866 res = ast_play_and_wait(chan, "vm-Old");
9867 if (!res)
9868 res = ast_play_and_wait(chan, "vm-message");
9869 } else {
9870 res = ast_play_and_wait(chan, "vm-Olds");
9871 if (!res)
9872 res = ast_play_and_wait(chan, "vm-messages");
9873 }
9874 } else if (!vms->oldmessages && !vms->newmessages)
9875 res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
9876 return res;
9877}
9878
9879/* Version of vm_intro() designed to work for many languages.
9880 *
9881 * It is hoped that this function can prevent the proliferation of
9882 * language-specific vm_intro() functions and in time replace the language-
9883 * specific functions which already exist. An examination of the language-
9884 * specific functions revealed that they all corrected the same deficiencies
9885 * in vm_intro_en() (which was the default function). Namely:
9886 *
9887 * 1) The vm-Old and vm-INBOX sound files were overloaded. The English
9888 * wording of the voicemail greeting hides this problem. For example,
9889 * vm-INBOX contains only the word "new". This means that both of these
9890 * sequences produce valid utterances:
9891 * * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
9892 * * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
9893 * However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
9894 * in many languages) the first utterance becomes "you have 1 the new message".
9895 * 2) The function contains hardcoded rules for pluralizing the word "message".
9896 * These rules are correct for English, but not for many other languages.
9897 * 3) No attempt is made to pluralize the adjectives ("old" and "new") as
9898 * required in many languages.
9899 * 4) The gender of the word for "message" is not specified. This is a problem
9900 * because in many languages the gender of the number in phrases such
9901 * as "you have one new message" must match the gender of the word
9902 * meaning "message".
9903 *
9904 * Fixing these problems for each new language has meant duplication of effort.
9905 * This new function solves the problems in the following general ways:
9906 * 1) Add new sound files vm-new and vm-old. These can be linked to vm-INBOX
9907 * and vm-Old respectively for those languages where it makes sense.
9908 * 2) Call ast_say_counted_noun() to put the proper gender and number prefix
9909 * on vm-message.
9910 * 3) Call ast_say_counted_adjective() to put the proper gender and number
9911 * prefix on vm-new and vm-old (none for English).
9912 * 4) Pass the gender of the language's word for "message" as an argument to
9913 * this function which is can in turn pass on to the functions which
9914 * say numbers and put endings on nouns and adjectives.
9915 *
9916 * All languages require these messages:
9917 * vm-youhave "You have..."
9918 * vm-and "and"
9919 * vm-no "no" (in the sense of "none", as in "you have no messages")
9920 *
9921 * To use it for English, you will need these additional sound files:
9922 * vm-new "new"
9923 * vm-message "message", singular
9924 * vm-messages "messages", plural
9925 *
9926 * If you use it for Russian and other slavic languages, you will need these additional sound files:
9927 *
9928 * vm-newn "novoye" (singular, neuter)
9929 * vm-newx "novikh" (counting plural form, genative plural)
9930 * vm-message "sobsheniye" (singular form)
9931 * vm-messagex1 "sobsheniya" (first counting plural form, genative singular)
9932 * vm-messagex2 "sobsheniy" (second counting plural form, genative plural)
9933 * digits/1n "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
9934 * digits/2n "dva" (neuter singular)
9935 */
9936static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
9937{
9938 int res;
9939 int lastnum = 0;
9940
9941 res = ast_play_and_wait(chan, "vm-youhave");
9942
9943 if (!res && vms->newmessages) {
9944 lastnum = vms->newmessages;
9945
9946 if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
9947 res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
9948 }
9949
9950 if (!res && vms->oldmessages) {
9951 res = ast_play_and_wait(chan, "vm-and");
9952 }
9953 }
9954
9955 if (!res && vms->oldmessages) {
9956 lastnum = vms->oldmessages;
9957
9958 if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
9959 res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
9960 }
9961 }
9962
9963 if (!res) {
9964 if (lastnum == 0) {
9965 res = ast_play_and_wait(chan, "vm-no");
9966 }
9967 if (!res) {
9968 res = ast_say_counted_noun(chan, lastnum, "vm-message");
9969 }
9970 }
9971
9972 return res;
9973}
9974
9975/* Default Hebrew syntax */
9976static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
9977{
9978 int res = 0;
9979
9980 /* Introduce messages they have */
9981 if (!res) {
9982 if ((vms->newmessages) || (vms->oldmessages)) {
9983 res = ast_play_and_wait(chan, "vm-youhave");
9984 }
9985 /*
9986 * The word "shtei" refers to the number 2 in hebrew when performing a count
9987 * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
9988 * an element, this is one of them.
9989 */
9990 if (vms->newmessages) {
9991 if (!res) {
9992 if (vms->newmessages == 1) {
9993 res = ast_play_and_wait(chan, "vm-INBOX1");
9994 } else {
9995 if (vms->newmessages == 2) {
9996 res = ast_play_and_wait(chan, "vm-shtei");
9997 } else {
9998 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9999 }
10000 res = ast_play_and_wait(chan, "vm-INBOX");
10001 }
10002 }
10003 if (vms->oldmessages && !res) {
10004 res = ast_play_and_wait(chan, "vm-and");
10005 if (vms->oldmessages == 1) {
10006 res = ast_play_and_wait(chan, "vm-Old1");
10007 } else {
10008 if (vms->oldmessages == 2) {
10009 res = ast_play_and_wait(chan, "vm-shtei");
10010 } else {
10011 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10012 }
10013 res = ast_play_and_wait(chan, "vm-Old");
10014 }
10015 }
10016 }
10017 if (!res && vms->oldmessages && !vms->newmessages) {
10018 if (!res) {
10019 if (vms->oldmessages == 1) {
10020 res = ast_play_and_wait(chan, "vm-Old1");
10021 } else {
10022 if (vms->oldmessages == 2) {
10023 res = ast_play_and_wait(chan, "vm-shtei");
10024 } else {
10025 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10026 }
10027 res = ast_play_and_wait(chan, "vm-Old");
10028 }
10029 }
10030 }
10031 if (!res) {
10032 if (!vms->oldmessages && !vms->newmessages) {
10033 if (!res) {
10034 res = ast_play_and_wait(chan, "vm-nomessages");
10035 }
10036 }
10037 }
10038 }
10039 return res;
10040}
10041
10042/* Japanese syntax */
10043static int vm_intro_ja(struct ast_channel *chan,struct vm_state *vms)
10044{
10045 /* Introduce messages they have */
10046 int res;
10047 if (vms->newmessages) {
10048 res = ast_play_and_wait(chan, "vm-INBOX");
10049 if (!res)
10050 res = ast_play_and_wait(chan, "vm-message");
10051 if (!res)
10052 res = ast_play_and_wait(chan, "jp-ga");
10053 if (!res)
10054 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10055 if (vms->oldmessages && !res)
10056 res = ast_play_and_wait(chan, "silence/1");
10057
10058 }
10059 if (vms->oldmessages) {
10060 res = ast_play_and_wait(chan, "vm-Old");
10061 if (!res)
10062 res = ast_play_and_wait(chan, "vm-message");
10063 if (!res)
10064 res = ast_play_and_wait(chan, "jp-ga");
10065 if (!res)
10066 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10067 }
10068 if (!vms->oldmessages && !vms->newmessages) {
10069 res = ast_play_and_wait(chan, "vm-messages");
10070 if (!res)
10071 res = ast_play_and_wait(chan, "jp-wa");
10072 if (!res)
10073 res = ast_play_and_wait(chan, "jp-arimasen");
10074 }
10075 else {
10076 res = ast_play_and_wait(chan, "jp-arimasu");
10077 }
10078 return res;
10079} /* Japanese */
10080
10081/* Default English syntax */
10082static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
10083{
10084 int res;
10085
10086 /* Introduce messages they have */
10087 res = ast_play_and_wait(chan, "vm-youhave");
10088 if (!res) {
10089 if (vms->urgentmessages) {
10090 res = say_and_wait(chan, vms->urgentmessages, ast_channel_language(chan));
10091 if (!res)
10092 res = ast_play_and_wait(chan, "vm-Urgent");
10093 if ((vms->oldmessages || vms->newmessages) && !res) {
10094 res = ast_play_and_wait(chan, "vm-and");
10095 } else if (!res) {
10096 if (vms->urgentmessages == 1)
10097 res = ast_play_and_wait(chan, "vm-message");
10098 else
10099 res = ast_play_and_wait(chan, "vm-messages");
10100 }
10101 }
10102 if (vms->newmessages) {
10103 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10104 if (!res)
10105 res = ast_play_and_wait(chan, "vm-INBOX");
10106 if (vms->oldmessages && !res)
10107 res = ast_play_and_wait(chan, "vm-and");
10108 else if (!res) {
10109 if (vms->newmessages == 1)
10110 res = ast_play_and_wait(chan, "vm-message");
10111 else
10112 res = ast_play_and_wait(chan, "vm-messages");
10113 }
10114
10115 }
10116 if (!res && vms->oldmessages) {
10117 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10118 if (!res)
10119 res = ast_play_and_wait(chan, "vm-Old");
10120 if (!res) {
10121 if (vms->oldmessages == 1)
10122 res = ast_play_and_wait(chan, "vm-message");
10123 else
10124 res = ast_play_and_wait(chan, "vm-messages");
10125 }
10126 }
10127 if (!res) {
10128 if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
10129 res = ast_play_and_wait(chan, "vm-no");
10130 if (!res)
10131 res = ast_play_and_wait(chan, "vm-messages");
10132 }
10133 }
10134 }
10135 return res;
10136}
10137
10138/* ICELANDIC syntax */
10139static int vm_intro_is(struct ast_channel *chan, struct vm_state *vms)
10140{
10141 int res;
10142
10143 /* Introduce messages they have */
10144 res = ast_play_and_wait(chan, "vm-youhave");
10145 if (!res) {
10146 if (vms->urgentmessages) {
10147 /* Digits 1-4 are spoken in neutral and plural when talking about messages,
10148 however, feminine is used for 1 as it is the same as the neutral for plural,
10149 and singular neutral is the same after 1. */
10150 if (vms->urgentmessages < 5) {
10151 char recname[16];
10152 if (vms->urgentmessages == 1)
10153 snprintf(recname, sizeof(recname), "digits/1kvk");
10154 else
10155 snprintf(recname, sizeof(recname), "digits/%dhk", vms->urgentmessages);
10156 res = ast_play_and_wait(chan, recname);
10157 } else if (!res)
10158 res = ast_play_and_wait(chan, "vm-Urgent");
10159 if ((vms->oldmessages || vms->newmessages) && !res) {
10160 res = ast_play_and_wait(chan, "vm-and");
10161 } else if (!res)
10162 res = ast_play_and_wait(chan, "vm-messages");
10163 }
10164 if (vms->newmessages) {
10165 if (vms->newmessages < 5) {
10166 char recname[16];
10167 if (vms->newmessages == 1)
10168 snprintf(recname, sizeof(recname), "digits/1kvk");
10169 else
10170 snprintf(recname, sizeof(recname), "digits/%dhk", vms->newmessages);
10171 res = ast_play_and_wait(chan, recname);
10172 } else
10173 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10174 if (!res)
10175 res = ast_play_and_wait(chan, "vm-INBOX");
10176 if (vms->oldmessages && !res)
10177 res = ast_play_and_wait(chan, "vm-and");
10178 else if (!res)
10179 res = ast_play_and_wait(chan, "vm-messages");
10180 }
10181 if (!res && vms->oldmessages) {
10182 if (vms->oldmessages < 5) {
10183 char recname[16];
10184 if (vms->oldmessages == 1)
10185 snprintf(recname, sizeof(recname), "digits/1kvk");
10186 else
10187 snprintf(recname, sizeof(recname), "digits/%dhk", vms->oldmessages);
10188 res = ast_play_and_wait(chan, recname);
10189 } else
10190 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10191 if (!res)
10192 res = ast_play_and_wait(chan, "vm-Old");
10193 if (!res)
10194 res = ast_play_and_wait(chan, "vm-messages");
10195 }
10196 if (!res) {
10197 if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
10198 res = ast_play_and_wait(chan, "vm-no");
10199 if (!res)
10200 res = ast_play_and_wait(chan, "vm-messages");
10201 }
10202 }
10203 }
10204 return res;
10205}
10206
10207/* ITALIAN syntax */
10208static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
10209{
10210 /* Introduce messages they have */
10211 int res;
10212 if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
10213 res = ast_play_and_wait(chan, "vm-no") ||
10214 ast_play_and_wait(chan, "vm-message");
10215 else
10216 res = ast_play_and_wait(chan, "vm-youhave");
10217 if (!res && vms->newmessages) {
10218 res = (vms->newmessages == 1) ?
10219 ast_play_and_wait(chan, "digits/un") ||
10220 ast_play_and_wait(chan, "vm-nuovo") ||
10221 ast_play_and_wait(chan, "vm-message") :
10222 /* 2 or more new messages */
10223 say_and_wait(chan, vms->newmessages, ast_channel_language(chan)) ||
10224 ast_play_and_wait(chan, "vm-nuovi") ||
10225 ast_play_and_wait(chan, "vm-messages");
10226 if (!res && vms->oldmessages)
10227 res = ast_play_and_wait(chan, "vm-and");
10228 }
10229 if (!res && vms->oldmessages) {
10230 res = (vms->oldmessages == 1) ?
10231 ast_play_and_wait(chan, "digits/un") ||
10232 ast_play_and_wait(chan, "vm-vecchio") ||
10233 ast_play_and_wait(chan, "vm-message") :
10234 /* 2 or more old messages */
10235 say_and_wait(chan, vms->oldmessages, ast_channel_language(chan)) ||
10236 ast_play_and_wait(chan, "vm-vecchi") ||
10237 ast_play_and_wait(chan, "vm-messages");
10238 }
10239 return res;
10240}
10241
10242/* POLISH syntax */
10243static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
10244{
10245 /* Introduce messages they have */
10246 int res;
10247 div_t num;
10248
10249 if (!vms->oldmessages && !vms->newmessages) {
10250 res = ast_play_and_wait(chan, "vm-no");
10251 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10252 return res;
10253 } else {
10254 res = ast_play_and_wait(chan, "vm-youhave");
10255 }
10256
10257 if (vms->newmessages) {
10258 num = div(vms->newmessages, 10);
10259 if (vms->newmessages == 1) {
10260 res = ast_play_and_wait(chan, "digits/1-a");
10261 res = res ? res : ast_play_and_wait(chan, "vm-new-a");
10262 res = res ? res : ast_play_and_wait(chan, "vm-message");
10263 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
10264 if (num.rem == 2) {
10265 if (!num.quot) {
10266 res = ast_play_and_wait(chan, "digits/2-ie");
10267 } else {
10268 res = say_and_wait(chan, vms->newmessages - 2 , ast_channel_language(chan));
10269 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
10270 }
10271 } else {
10272 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10273 }
10274 res = res ? res : ast_play_and_wait(chan, "vm-new-e");
10275 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10276 } else {
10277 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10278 res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
10279 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10280 }
10281 if (!res && vms->oldmessages)
10282 res = ast_play_and_wait(chan, "vm-and");
10283 }
10284 if (!res && vms->oldmessages) {
10285 num = div(vms->oldmessages, 10);
10286 if (vms->oldmessages == 1) {
10287 res = ast_play_and_wait(chan, "digits/1-a");
10288 res = res ? res : ast_play_and_wait(chan, "vm-old-a");
10289 res = res ? res : ast_play_and_wait(chan, "vm-message");
10290 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
10291 if (num.rem == 2) {
10292 if (!num.quot) {
10293 res = ast_play_and_wait(chan, "digits/2-ie");
10294 } else {
10295 res = say_and_wait(chan, vms->oldmessages - 2 , ast_channel_language(chan));
10296 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
10297 }
10298 } else {
10299 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10300 }
10301 res = res ? res : ast_play_and_wait(chan, "vm-old-e");
10302 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10303 } else {
10304 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10305 res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
10306 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10307 }
10308 }
10309
10310 return res;
10311}
10312
10313/* SWEDISH syntax */
10314static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
10315{
10316 /* Introduce messages they have */
10317 int res;
10318
10319 res = ast_play_and_wait(chan, "vm-youhave");
10320 if (res)
10321 return res;
10322
10323 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10324 res = ast_play_and_wait(chan, "vm-no");
10325 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10326 return res;
10327 }
10328
10329 if (vms->newmessages) {
10330 if (vms->newmessages == 1) {
10331 res = ast_play_and_wait(chan, "digits/ett");
10332 res = res ? res : ast_play_and_wait(chan, "vm-nytt");
10333 res = res ? res : ast_play_and_wait(chan, "vm-message");
10334 } else {
10335 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10336 res = res ? res : ast_play_and_wait(chan, "vm-nya");
10337 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10338 }
10339 if (!res && vms->oldmessages)
10340 res = ast_play_and_wait(chan, "vm-and");
10341 }
10342 if (!res && vms->oldmessages) {
10343 if (vms->oldmessages == 1) {
10344 res = ast_play_and_wait(chan, "digits/ett");
10345 res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
10346 res = res ? res : ast_play_and_wait(chan, "vm-message");
10347 } else {
10348 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10349 res = res ? res : ast_play_and_wait(chan, "vm-gamla");
10350 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10351 }
10352 }
10353
10354 return res;
10355}
10356
10357/* NORWEGIAN syntax */
10358static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
10359{
10360 /* Introduce messages they have */
10361 int res;
10362
10363 res = ast_play_and_wait(chan, "vm-youhave");
10364 if (res)
10365 return res;
10366
10367 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10368 res = ast_play_and_wait(chan, "vm-no");
10369 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10370 return res;
10371 }
10372
10373 if (vms->newmessages) {
10374 if (vms->newmessages == 1) {
10375 res = ast_play_and_wait(chan, "digits/1");
10376 res = res ? res : ast_play_and_wait(chan, "vm-ny");
10377 res = res ? res : ast_play_and_wait(chan, "vm-message");
10378 } else {
10379 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10380 res = res ? res : ast_play_and_wait(chan, "vm-nye");
10381 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10382 }
10383 if (!res && vms->oldmessages)
10384 res = ast_play_and_wait(chan, "vm-and");
10385 }
10386 if (!res && vms->oldmessages) {
10387 if (vms->oldmessages == 1) {
10388 res = ast_play_and_wait(chan, "digits/1");
10389 res = res ? res : ast_play_and_wait(chan, "vm-gamel");
10390 res = res ? res : ast_play_and_wait(chan, "vm-message");
10391 } else {
10392 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10393 res = res ? res : ast_play_and_wait(chan, "vm-gamle");
10394 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10395 }
10396 }
10397
10398 return res;
10399}
10400
10401/* Danish syntax */
10402static int vm_intro_da(struct ast_channel *chan, struct vm_state *vms)
10403{
10404 /* Introduce messages they have */
10405 int res;
10406
10407 res = ast_play_and_wait(chan, "vm-youhave");
10408 if (res)
10409 return res;
10410
10411 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10412 res = ast_play_and_wait(chan, "vm-no");
10413 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10414 return res;
10415 }
10416
10417 if (vms->newmessages) {
10418 if ((vms->newmessages == 1)) {
10419 res = ast_play_and_wait(chan, "digits/1");
10420 res = res ? res : ast_play_and_wait(chan, "vm-INBOX");
10421 res = res ? res : ast_play_and_wait(chan, "vm-message");
10422 } else {
10423 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10424 res = res ? res : ast_play_and_wait(chan, "vm-INBOXs");
10425 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10426 }
10427 if (!res && vms->oldmessages)
10428 res = ast_play_and_wait(chan, "vm-and");
10429 }
10430 if (!res && vms->oldmessages) {
10431 if (vms->oldmessages == 1) {
10432 res = ast_play_and_wait(chan, "digits/1");
10433 res = res ? res : ast_play_and_wait(chan, "vm-Old");
10434 res = res ? res : ast_play_and_wait(chan, "vm-message");
10435 } else {
10436 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10437 res = res ? res : ast_play_and_wait(chan, "vm-Olds");
10438 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10439 }
10440 }
10441
10442 return res;
10443}
10444
10445
10446/* GERMAN syntax */
10447static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
10448{
10449 /* Introduce messages they have */
10450 int res;
10451 res = ast_play_and_wait(chan, "vm-youhave");
10452 if (!res) {
10453 if (vms->newmessages) {
10454 if (vms->newmessages == 1)
10455 res = ast_play_and_wait(chan, "digits/1F");
10456 else
10457 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10458 if (!res)
10459 res = ast_play_and_wait(chan, "vm-INBOX");
10460 if (vms->oldmessages && !res)
10461 res = ast_play_and_wait(chan, "vm-and");
10462 else if (!res) {
10463 if (vms->newmessages == 1)
10464 res = ast_play_and_wait(chan, "vm-message");
10465 else
10466 res = ast_play_and_wait(chan, "vm-messages");
10467 }
10468
10469 }
10470 if (!res && vms->oldmessages) {
10471 if (vms->oldmessages == 1)
10472 res = ast_play_and_wait(chan, "digits/1F");
10473 else
10474 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10475 if (!res)
10476 res = ast_play_and_wait(chan, "vm-Old");
10477 if (!res) {
10478 if (vms->oldmessages == 1)
10479 res = ast_play_and_wait(chan, "vm-message");
10480 else
10481 res = ast_play_and_wait(chan, "vm-messages");
10482 }
10483 }
10484 if (!res) {
10485 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10486 res = ast_play_and_wait(chan, "vm-no");
10487 if (!res)
10488 res = ast_play_and_wait(chan, "vm-messages");
10489 }
10490 }
10491 }
10492 return res;
10493}
10494
10495/* SPANISH syntax */
10496static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
10497{
10498 /* Introduce messages they have */
10499 int res;
10500 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10501 res = ast_play_and_wait(chan, "vm-youhaveno");
10502 if (!res)
10503 res = ast_play_and_wait(chan, "vm-messages");
10504 } else {
10505 res = ast_play_and_wait(chan, "vm-youhave");
10506 }
10507 if (!res) {
10508 if (vms->newmessages) {
10509 if (!res) {
10510 if (vms->newmessages == 1) {
10511 res = ast_play_and_wait(chan, "digits/1M");
10512 if (!res)
10513 res = ast_play_and_wait(chan, "vm-message");
10514 if (!res)
10515 res = ast_play_and_wait(chan, "vm-INBOXs");
10516 } else {
10517 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10518 if (!res)
10519 res = ast_play_and_wait(chan, "vm-messages");
10520 if (!res)
10521 res = ast_play_and_wait(chan, "vm-INBOX");
10522 }
10523 }
10524 if (vms->oldmessages && !res)
10525 res = ast_play_and_wait(chan, "vm-and");
10526 }
10527 if (vms->oldmessages) {
10528 if (!res) {
10529 if (vms->oldmessages == 1) {
10530 res = ast_play_and_wait(chan, "digits/1M");
10531 if (!res)
10532 res = ast_play_and_wait(chan, "vm-message");
10533 if (!res)
10534 res = ast_play_and_wait(chan, "vm-Olds");
10535 } else {
10536 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10537 if (!res)
10538 res = ast_play_and_wait(chan, "vm-messages");
10539 if (!res)
10540 res = ast_play_and_wait(chan, "vm-Old");
10541 }
10542 }
10543 }
10544 }
10545return res;
10546}
10547
10548/* BRAZILIAN PORTUGUESE syntax */
10549static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
10550 /* Introduce messages they have */
10551 int res;
10552 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10553 res = ast_play_and_wait(chan, "vm-nomessages");
10554 return res;
10555 } else {
10556 res = ast_play_and_wait(chan, "vm-youhave");
10557 }
10558 if (vms->newmessages) {
10559 if (!res)
10560 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10561 if (vms->newmessages == 1) {
10562 if (!res)
10563 res = ast_play_and_wait(chan, "vm-message");
10564 if (!res)
10565 res = ast_play_and_wait(chan, "vm-INBOXs");
10566 } else {
10567 if (!res)
10568 res = ast_play_and_wait(chan, "vm-messages");
10569 if (!res)
10570 res = ast_play_and_wait(chan, "vm-INBOX");
10571 }
10572 if (vms->oldmessages && !res)
10573 res = ast_play_and_wait(chan, "vm-and");
10574 }
10575 if (vms->oldmessages) {
10576 if (!res)
10577 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10578 if (vms->oldmessages == 1) {
10579 if (!res)
10580 res = ast_play_and_wait(chan, "vm-message");
10581 if (!res)
10582 res = ast_play_and_wait(chan, "vm-Olds");
10583 } else {
10584 if (!res)
10585 res = ast_play_and_wait(chan, "vm-messages");
10586 if (!res)
10587 res = ast_play_and_wait(chan, "vm-Old");
10588 }
10589 }
10590 return res;
10591}
10592
10593/* FRENCH syntax */
10594static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
10595{
10596 /* Introduce messages they have */
10597 int res;
10598 res = ast_play_and_wait(chan, "vm-youhave");
10599 if (!res) {
10600 if (vms->newmessages) {
10601 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10602 if (!res)
10603 res = ast_play_and_wait(chan, "vm-INBOX");
10604 if (vms->oldmessages && !res)
10605 res = ast_play_and_wait(chan, "vm-and");
10606 else if (!res) {
10607 if (vms->newmessages == 1)
10608 res = ast_play_and_wait(chan, "vm-message");
10609 else
10610 res = ast_play_and_wait(chan, "vm-messages");
10611 }
10612
10613 }
10614 if (!res && vms->oldmessages) {
10615 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10616 if (!res)
10617 res = ast_play_and_wait(chan, "vm-Old");
10618 if (!res) {
10619 if (vms->oldmessages == 1)
10620 res = ast_play_and_wait(chan, "vm-message");
10621 else
10622 res = ast_play_and_wait(chan, "vm-messages");
10623 }
10624 }
10625 if (!res) {
10626 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10627 res = ast_play_and_wait(chan, "vm-no");
10628 if (!res)
10629 res = ast_play_and_wait(chan, "vm-messages");
10630 }
10631 }
10632 }
10633 return res;
10634}
10635
10636/* DUTCH syntax */
10637static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
10638{
10639 /* Introduce messages they have */
10640 int res;
10641 res = ast_play_and_wait(chan, "vm-youhave");
10642 if (!res) {
10643 if (vms->newmessages) {
10644 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10645 if (!res) {
10646 if (vms->newmessages == 1)
10647 res = ast_play_and_wait(chan, "vm-INBOXs");
10648 else
10649 res = ast_play_and_wait(chan, "vm-INBOX");
10650 }
10651 if (vms->oldmessages && !res)
10652 res = ast_play_and_wait(chan, "vm-and");
10653 else if (!res) {
10654 if (vms->newmessages == 1)
10655 res = ast_play_and_wait(chan, "vm-message");
10656 else
10657 res = ast_play_and_wait(chan, "vm-messages");
10658 }
10659
10660 }
10661 if (!res && vms->oldmessages) {
10662 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10663 if (!res) {
10664 if (vms->oldmessages == 1)
10665 res = ast_play_and_wait(chan, "vm-Olds");
10666 else
10667 res = ast_play_and_wait(chan, "vm-Old");
10668 }
10669 if (!res) {
10670 if (vms->oldmessages == 1)
10671 res = ast_play_and_wait(chan, "vm-message");
10672 else
10673 res = ast_play_and_wait(chan, "vm-messages");
10674 }
10675 }
10676 if (!res) {
10677 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10678 res = ast_play_and_wait(chan, "vm-no");
10679 if (!res)
10680 res = ast_play_and_wait(chan, "vm-messages");
10681 }
10682 }
10683 }
10684 return res;
10685}
10686
10687/* PORTUGUESE syntax */
10688static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
10689{
10690 /* Introduce messages they have */
10691 int res;
10692 res = ast_play_and_wait(chan, "vm-youhave");
10693 if (!res) {
10694 if (vms->newmessages) {
10695 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10696 if (!res) {
10697 if (vms->newmessages == 1) {
10698 res = ast_play_and_wait(chan, "vm-message");
10699 if (!res)
10700 res = ast_play_and_wait(chan, "vm-INBOXs");
10701 } else {
10702 res = ast_play_and_wait(chan, "vm-messages");
10703 if (!res)
10704 res = ast_play_and_wait(chan, "vm-INBOX");
10705 }
10706 }
10707 if (vms->oldmessages && !res)
10708 res = ast_play_and_wait(chan, "vm-and");
10709 }
10710 if (!res && vms->oldmessages) {
10711 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10712 if (!res) {
10713 if (vms->oldmessages == 1) {
10714 res = ast_play_and_wait(chan, "vm-message");
10715 if (!res)
10716 res = ast_play_and_wait(chan, "vm-Olds");
10717 } else {
10718 res = ast_play_and_wait(chan, "vm-messages");
10719 if (!res)
10720 res = ast_play_and_wait(chan, "vm-Old");
10721 }
10722 }
10723 }
10724 if (!res) {
10725 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10726 res = ast_play_and_wait(chan, "vm-no");
10727 if (!res)
10728 res = ast_play_and_wait(chan, "vm-messages");
10729 }
10730 }
10731 }
10732 return res;
10733}
10734
10735
10736/* CZECH syntax */
10737/* in czech there must be declension of word new and message
10738 * czech : english : czech : english
10739 * --------------------------------------------------------
10740 * vm-youhave : you have
10741 * vm-novou : one new : vm-zpravu : message
10742 * vm-nove : 2-4 new : vm-zpravy : messages
10743 * vm-novych : 5-infinite new : vm-zprav : messages
10744 * vm-starou : one old
10745 * vm-stare : 2-4 old
10746 * vm-starych : 5-infinite old
10747 * jednu : one - falling 4.
10748 * vm-no : no ( no messages )
10749 */
10750
10751static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
10752{
10753 int res;
10754 res = ast_play_and_wait(chan, "vm-youhave");
10755 if (!res) {
10756 if (vms->newmessages) {
10757 if (vms->newmessages == 1) {
10758 res = ast_play_and_wait(chan, "digits/jednu");
10759 } else {
10760 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10761 }
10762 if (!res) {
10763 if (vms->newmessages == 1)
10764 res = ast_play_and_wait(chan, "vm-novou");
10765 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
10766 res = ast_play_and_wait(chan, "vm-nove");
10767 if (vms->newmessages > 4)
10768 res = ast_play_and_wait(chan, "vm-novych");
10769 }
10770 if (vms->oldmessages && !res)
10771 res = ast_play_and_wait(chan, "vm-and");
10772 else if (!res) {
10773 if (vms->newmessages == 1)
10774 res = ast_play_and_wait(chan, "vm-zpravu");
10775 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
10776 res = ast_play_and_wait(chan, "vm-zpravy");
10777 if (vms->newmessages > 4)
10778 res = ast_play_and_wait(chan, "vm-zprav");
10779 }
10780 }
10781 if (!res && vms->oldmessages) {
10782 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10783 if (!res) {
10784 if (vms->oldmessages == 1)
10785 res = ast_play_and_wait(chan, "vm-starou");
10786 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
10787 res = ast_play_and_wait(chan, "vm-stare");
10788 if (vms->oldmessages > 4)
10789 res = ast_play_and_wait(chan, "vm-starych");
10790 }
10791 if (!res) {
10792 if (vms->oldmessages == 1)
10793 res = ast_play_and_wait(chan, "vm-zpravu");
10794 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
10795 res = ast_play_and_wait(chan, "vm-zpravy");
10796 if (vms->oldmessages > 4)
10797 res = ast_play_and_wait(chan, "vm-zprav");
10798 }
10799 }
10800 if (!res) {
10801 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10802 res = ast_play_and_wait(chan, "vm-no");
10803 if (!res)
10804 res = ast_play_and_wait(chan, "vm-zpravy");
10805 }
10806 }
10807 }
10808 return res;
10809}
10810
10811/* CHINESE (Taiwan) syntax */
10812static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
10813{
10814 int res;
10815 /* Introduce messages they have */
10816 res = ast_play_and_wait(chan, "vm-you");
10817
10818 if (!res && vms->newmessages) {
10819 res = ast_play_and_wait(chan, "vm-have");
10820 if (!res)
10821 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10822 if (!res)
10823 res = ast_play_and_wait(chan, "vm-tong");
10824 if (!res)
10825 res = ast_play_and_wait(chan, "vm-INBOX");
10826 if (vms->oldmessages && !res)
10827 res = ast_play_and_wait(chan, "vm-and");
10828 else if (!res)
10829 res = ast_play_and_wait(chan, "vm-messages");
10830 }
10831 if (!res && vms->oldmessages) {
10832 res = ast_play_and_wait(chan, "vm-have");
10833 if (!res)
10834 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10835 if (!res)
10836 res = ast_play_and_wait(chan, "vm-tong");
10837 if (!res)
10838 res = ast_play_and_wait(chan, "vm-Old");
10839 if (!res)
10840 res = ast_play_and_wait(chan, "vm-messages");
10841 }
10842 if (!res && !vms->oldmessages && !vms->newmessages) {
10843 res = ast_play_and_wait(chan, "vm-haveno");
10844 if (!res)
10845 res = ast_play_and_wait(chan, "vm-messages");
10846 }
10847 return res;
10848}
10849
10850/* Vietnamese syntax */
10851static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
10852{
10853 int res;
10854
10855 /* Introduce messages they have */
10856 res = ast_play_and_wait(chan, "vm-youhave");
10857 if (!res) {
10858 if (vms->newmessages) {
10859 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10860 if (!res)
10861 res = ast_play_and_wait(chan, "vm-INBOX");
10862 if (vms->oldmessages && !res)
10863 res = ast_play_and_wait(chan, "vm-and");
10864 }
10865 if (!res && vms->oldmessages) {
10866 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10867 if (!res)
10868 res = ast_play_and_wait(chan, "vm-Old");
10869 }
10870 if (!res) {
10871 if (!vms->oldmessages && !vms->newmessages) {
10872 res = ast_play_and_wait(chan, "vm-no");
10873 if (!res)
10874 res = ast_play_and_wait(chan, "vm-message");
10875 }
10876 }
10877 }
10878 return res;
10879}
10880
10881static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
10882{
10883 char prefile[256];
10884
10885 /* Notify the user that the temp greeting is set and give them the option to remove it */
10886 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
10887 if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10888 RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
10889 if (ast_fileexists(prefile, NULL, NULL) > 0) {
10890 ast_play_and_wait(chan, "vm-tempgreetactive");
10891 }
10892 DISPOSE(prefile, -1);
10893 }
10894
10895 /* Play voicemail intro - syntax is different for different languages */
10896 if (0) {
10897 return 0;
10898 } else if (!strncasecmp(ast_channel_language(chan), "cs", 2)) { /* CZECH syntax */
10899 return vm_intro_cs(chan, vms);
10900 } else if (!strncasecmp(ast_channel_language(chan), "cz", 2)) { /* deprecated CZECH syntax */
10901 static int deprecation_warning = 0;
10902 if (deprecation_warning++ % 10 == 0) {
10903 ast_log(LOG_WARNING, "cz is not a standard language code. Please switch to using cs instead.\n");
10904 }
10905 return vm_intro_cs(chan, vms);
10906 } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
10907 return vm_intro_de(chan, vms);
10908 } else if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH syntax */
10909 return vm_intro_es(chan, vms);
10910 } else if (!strncasecmp(ast_channel_language(chan), "fr", 2)) { /* FRENCH syntax */
10911 return vm_intro_fr(chan, vms);
10912 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
10913 return vm_intro_gr(chan, vms);
10914 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW syntax */
10915 return vm_intro_he(chan, vms);
10916 } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */
10917 return vm_intro_is(chan, vms);
10918 } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
10919 return vm_intro_it(chan, vms);
10920 } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE syntax */
10921 return vm_intro_ja(chan, vms);
10922 } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
10923 return vm_intro_nl(chan, vms);
10924 } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
10925 return vm_intro_no(chan, vms);
10926 } else if (!strncasecmp(ast_channel_language(chan), "da", 2)) { /* DANISH syntax */
10927 return vm_intro_da(chan, vms);
10928 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
10929 return vm_intro_pl(chan, vms);
10930 } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* BRAZILIAN PORTUGUESE syntax */
10931 return vm_intro_pt_BR(chan, vms);
10932 } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE syntax */
10933 return vm_intro_pt(chan, vms);
10934 } else if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* RUSSIAN syntax */
10935 return vm_intro_multilang(chan, vms, "n");
10936 } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
10937 return vm_intro_se(chan, vms);
10938 } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* UKRAINIAN syntax */
10939 return vm_intro_multilang(chan, vms, "n");
10940 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
10941 return vm_intro_vi(chan, vms);
10942 } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
10943 return vm_intro_zh(chan, vms);
10944 } else { /* Default to ENGLISH */
10945 return vm_intro_en(chan, vms);
10946 }
10947}
10948
10949static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
10950{
10951 int res = 0;
10952 /* Play instructions and wait for new command */
10953 while (!res) {
10954 if (vms->starting) {
10955 if (vms->lastmsg > -1) {
10956 if (skipadvanced)
10957 res = ast_play_and_wait(chan, "vm-onefor-full");
10958 else
10959 res = ast_play_and_wait(chan, "vm-onefor");
10960 if (!res)
10961 res = vm_play_folder_name(chan, vms->vmbox);
10962 }
10963 if (!res) {
10964 if (skipadvanced)
10965 res = ast_play_and_wait(chan, "vm-opts-full");
10966 else
10967 res = ast_play_and_wait(chan, "vm-opts");
10968 }
10969 } else {
10970 /* Added for additional help */
10971 if (skipadvanced) {
10972 res = ast_play_and_wait(chan, "vm-onefor-full");
10973 if (!res)
10974 res = vm_play_folder_name(chan, vms->vmbox);
10975 res = ast_play_and_wait(chan, "vm-opts-full");
10976 }
10977 /* Logic:
10978 * If the current message is not the first OR
10979 * if we're listening to the first new message and there are
10980 * also urgent messages, then prompt for navigation to the
10981 * previous message
10982 */
10983 if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
10984 res = ast_play_and_wait(chan, "vm-prev");
10985 }
10986 if (!res && !skipadvanced)
10987 res = ast_play_and_wait(chan, "vm-advopts");
10988 if (!res)
10989 res = ast_play_and_wait(chan, "vm-repeat");
10990 /* Logic:
10991 * If we're not listening to the last message OR
10992 * we're listening to the last urgent message and there are
10993 * also new non-urgent messages, then prompt for navigation
10994 * to the next message
10995 */
10996 if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
10997 (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
10998 res = ast_play_and_wait(chan, "vm-next");
10999 }
11000 if (!res) {
11001 int curmsg_deleted;
11002#ifdef IMAP_STORAGE
11003 ast_mutex_lock(&vms->lock);
11004#endif
11005 curmsg_deleted = vms->deleted[vms->curmsg];
11006#ifdef IMAP_STORAGE
11007 ast_mutex_unlock(&vms->lock);
11008#endif
11009 if (!nodelete) {
11010 if (!curmsg_deleted) {
11011 res = ast_play_and_wait(chan, "vm-delete");
11012 } else {
11013 res = ast_play_and_wait(chan, "vm-undelete");
11014 }
11015 }
11016 if (!res) {
11017 res = ast_play_and_wait(chan, "vm-toforward");
11018 }
11019 if (!res) {
11020 res = ast_play_and_wait(chan, "vm-savemessage");
11021 }
11022 }
11023 }
11024 if (!res) {
11025 res = ast_play_and_wait(chan, "vm-helpexit");
11026 }
11027 if (!res)
11028 res = ast_waitfordigit(chan, 6000);
11029 if (!res) {
11030 vms->repeats++;
11031 if (vms->repeats > 2) {
11032 res = 't';
11033 }
11034 }
11035 }
11036 return res;
11037}
11038
11039static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
11040{
11041 int res = 0;
11042 /* Play instructions and wait for new command */
11043 while (!res) {
11044 if (vms->starting) {
11045 if (vms->lastmsg > -1) {
11046 res = vm_play_folder_name(chan, vms->vmbox);
11047 if (!res)
11048 res = ast_play_and_wait(chan, "jp-wa");
11049 if (!res)
11050 res = ast_play_and_wait(chan, "digits/1");
11051 if (!res)
11052 res = ast_play_and_wait(chan, "jp-wo");
11053 if (!res)
11054 res = ast_play_and_wait(chan, "silence/1");
11055 }
11056 if (!res)
11057 res = ast_play_and_wait(chan, "vm-opts");
11058 } else {
11059 /* Added for additional help */
11060 if (skipadvanced) {
11061 res = vm_play_folder_name(chan, vms->vmbox);
11062 if (!res)
11063 res = ast_play_and_wait(chan, "jp-wa");
11064 if (!res)
11065 res = ast_play_and_wait(chan, "digits/1");
11066 if (!res)
11067 res = ast_play_and_wait(chan, "jp-wo");
11068 if (!res)
11069 res = ast_play_and_wait(chan, "silence/1");
11070 res = ast_play_and_wait(chan, "vm-opts-full");
11071 }
11072 /* Logic:
11073 * If the current message is not the first OR
11074 * if we're listening to the first new message and there are
11075 * also urgent messages, then prompt for navigation to the
11076 * previous message
11077 */
11078 if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
11079 res = ast_play_and_wait(chan, "vm-prev");
11080 }
11081 if (!res && !skipadvanced)
11082 res = ast_play_and_wait(chan, "vm-advopts");
11083 if (!res)
11084 res = ast_play_and_wait(chan, "vm-repeat");
11085 /* Logic:
11086 * If we're not listening to the last message OR
11087 * we're listening to the last urgent message and there are
11088 * also new non-urgent messages, then prompt for navigation
11089 * to the next message
11090 */
11091 if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
11092 (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
11093 res = ast_play_and_wait(chan, "vm-next");
11094 }
11095 if (!res) {
11096 int curmsg_deleted;
11097#ifdef IMAP_STORAGE
11098 ast_mutex_lock(&vms->lock);
11099#endif
11100 curmsg_deleted = vms->deleted[vms->curmsg];
11101#ifdef IMAP_STORAGE
11102 ast_mutex_unlock(&vms->lock);
11103#endif
11104 if (!curmsg_deleted) {
11105 res = ast_play_and_wait(chan, "vm-delete");
11106 } else {
11107 res = ast_play_and_wait(chan, "vm-undelete");
11108 }
11109 if (!res) {
11110 res = ast_play_and_wait(chan, "vm-toforward");
11111 }
11112 if (!res) {
11113 res = ast_play_and_wait(chan, "vm-savemessage");
11114 }
11115 }
11116 }
11117
11118 if (!res) {
11119 res = ast_play_and_wait(chan, "vm-helpexit");
11120 }
11121 if (!res)
11122 res = ast_waitfordigit(chan, 6000);
11123 if (!res) {
11124 vms->repeats++;
11125 if (vms->repeats > 2) {
11126 res = 't';
11127 }
11128 }
11129
11130 }
11131
11132 return res;
11133}
11134
11135static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
11136{
11137 int res = 0;
11138 /* Play instructions and wait for new command */
11139 while (!res) {
11140 if (vms->lastmsg > -1) {
11141 res = ast_play_and_wait(chan, "vm-listen");
11142 if (!res)
11143 res = vm_play_folder_name(chan, vms->vmbox);
11144 if (!res)
11145 res = ast_play_and_wait(chan, "press");
11146 if (!res)
11147 res = ast_play_and_wait(chan, "digits/1");
11148 }
11149 if (!res)
11150 res = ast_play_and_wait(chan, "vm-opts");
11151 if (!res) {
11152 vms->starting = 0;
11153 return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11154 }
11155 }
11156 return res;
11157}
11158
11159static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
11160{
11161 if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
11162 return vm_instructions_ja(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11163 } else if (vms->starting && !strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
11164 return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11165 } else { /* Default to ENGLISH */
11166 return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11167 }
11168}
11169
11170static int vm_newuser_setup(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11171{
11172 int cmd = 0;
11173 int duration = 0;
11174 int tries = 0;
11175 char newpassword[80] = "";
11176 char newpassword2[80] = "";
11177 char prefile[PATH_MAX] = "";
11178 unsigned char buf[256];
11179 int bytes = 0;
11180
11181 ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
11182 if (ast_adsi_available(chan)) {
11183 bytes += adsi_logo(buf + bytes);
11184 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
11185 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11186 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11187 bytes += ast_adsi_voice_mode(buf + bytes, 0);
11189 }
11190
11191 /* If forcename is set, have the user record their name */
11192 if (ast_test_flag(vmu, VM_FORCENAME)) {
11193 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
11194 if (ast_fileexists(prefile, NULL, NULL) < 1) {
11195 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11196 if (cmd < 0 || cmd == 't' || cmd == '#')
11197 return cmd;
11198 }
11199 }
11200
11201 /* If forcegreetings is set, have the user record their greetings */
11202 if (ast_test_flag(vmu, VM_FORCEGREET)) {
11203 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
11204 if (ast_fileexists(prefile, NULL, NULL) < 1) {
11205 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11206 if (cmd < 0 || cmd == 't' || cmd == '#')
11207 return cmd;
11208 }
11209
11210 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
11211 if (ast_fileexists(prefile, NULL, NULL) < 1) {
11212 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11213 if (cmd < 0 || cmd == 't' || cmd == '#')
11214 return cmd;
11215 }
11216 }
11217
11218 /*
11219 * Change the password last since new users will be able to skip over any steps this one comes before
11220 * by hanging up and calling back to voicemail main since the password is used to verify new user status.
11221 */
11222 for (;;) {
11223 newpassword[1] = '\0';
11224 newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
11225 if (cmd == '#')
11226 newpassword[0] = '\0';
11227 if (cmd < 0 || cmd == 't' || cmd == '#')
11228 return cmd;
11229 cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
11230 if (cmd < 0 || cmd == 't' || cmd == '#')
11231 return cmd;
11232 cmd = check_password(vmu, newpassword); /* perform password validation */
11233 if (cmd != 0) {
11234 ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
11236 } else {
11237 newpassword2[1] = '\0';
11238 newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
11239 if (cmd == '#')
11240 newpassword2[0] = '\0';
11241 if (cmd < 0 || cmd == 't' || cmd == '#')
11242 return cmd;
11243 cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
11244 if (cmd < 0 || cmd == 't' || cmd == '#')
11245 return cmd;
11246 if (!strcmp(newpassword, newpassword2))
11247 break;
11248 ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
11249 cmd = ast_play_and_wait(chan, vm_mismatch);
11250 }
11251 if (++tries == 3)
11252 return -1;
11253 if (cmd != 0) {
11255 }
11256 }
11258 vm_change_password(vmu, newpassword);
11260 vm_change_password_shell(vmu, newpassword);
11261
11262 ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
11263 cmd = ast_play_and_wait(chan, vm_passchanged);
11264
11265 return cmd;
11266}
11267
11268static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11269{
11270 int cmd = 0;
11271 int retries = 0;
11272 int duration = 0;
11273 char newpassword[80] = "";
11274 char newpassword2[80] = "";
11275 char prefile[PATH_MAX] = "";
11276 unsigned char buf[256];
11277 int bytes = 0;
11278 SCOPE_ENTER(3, "%s: %s entering mailbox options", ast_channel_name(chan), vms->username);
11279
11280 ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
11281 if (ast_adsi_available(chan)) {
11282 bytes += adsi_logo(buf + bytes);
11283 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
11284 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11285 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11286 bytes += ast_adsi_voice_mode(buf + bytes, 0);
11288 }
11289 while ((cmd >= 0) && (cmd != 't')) {
11290 if (cmd)
11291 retries = 0;
11292 switch (cmd) {
11293 case '1': /* Record your unavailable message */
11294 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
11295 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_record_review, chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11296 break;
11297 case '2': /* Record your busy message */
11298 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
11299 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_record_review, chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11300 break;
11301 case '3': /* Record greeting */
11302 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
11303 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_record_review, chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11304 break;
11305 case '4': /* manage the temporary greeting */
11306 cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
11307 break;
11308 case '5': /* change password */
11309 if (vmu->password[0] == '-') {
11310 cmd = ast_play_and_wait(chan, "vm-no");
11311 break;
11312 }
11313 newpassword[1] = '\0';
11314 newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
11315 if (cmd == '#')
11316 newpassword[0] = '\0';
11317 else {
11318 if (cmd < 0)
11319 break;
11320 if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
11321 break;
11322 }
11323 }
11324 cmd = check_password(vmu, newpassword); /* perform password validation */
11325 if (cmd != 0) {
11326 ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
11328 if (!cmd) {
11330 }
11331 break;
11332 }
11333 newpassword2[1] = '\0';
11334 newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
11335 if (cmd == '#')
11336 newpassword2[0] = '\0';
11337 else {
11338 if (cmd < 0)
11339 break;
11340
11341 if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
11342 break;
11343 }
11344 }
11345 if (strcmp(newpassword, newpassword2)) {
11346 ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
11347 cmd = ast_play_and_wait(chan, vm_mismatch);
11348 if (!cmd) {
11350 }
11351 break;
11352 }
11353
11355 vm_change_password(vmu, newpassword);
11356 }
11358 vm_change_password_shell(vmu, newpassword);
11359 }
11360
11361 ast_debug(1, "User %s set password to %s of length %d\n",
11362 vms->username, newpassword, (int) strlen(newpassword));
11363 cmd = ast_play_and_wait(chan, vm_passchanged);
11364 break;
11365 case '*':
11366 cmd = 't';
11367 break;
11368 default:
11369 cmd = 0;
11370 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
11371 SCOPE_CALL(-1, RETRIEVE, prefile, -1, vmu->mailbox, vmu->context);
11372 if (ast_fileexists(prefile, NULL, NULL)) {
11373 cmd = ast_play_and_wait(chan, "vm-tmpexists");
11374 }
11375 SCOPE_CALL(-1, DISPOSE, prefile, -1);
11376 if (!cmd) {
11377 cmd = ast_play_and_wait(chan, "vm-options");
11378 }
11379 if (!cmd) {
11380 cmd = ast_waitfordigit(chan, 6000);
11381 }
11382 if (!cmd) {
11383 retries++;
11384 }
11385 if (retries > 3) {
11386 cmd = 't';
11387 }
11388 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
11389 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
11390 }
11391 }
11392 if (cmd == 't')
11393 cmd = 0;
11394 SCOPE_EXIT_RTN_VALUE(cmd, "Done\n");
11395}
11396
11397/*!
11398 * \brief The handler for 'record a temporary greeting'.
11399 * \param chan
11400 * \param vmu
11401 * \param vms
11402 * \param fmtc
11403 * \param record_gain
11404 *
11405 * This is option 4 from the mailbox options menu.
11406 * This function manages the following promptings:
11407 * 1: play / record / review the temporary greeting. : invokes play_record_review().
11408 * 2: remove (delete) the temporary greeting.
11409 * *: return to the main menu.
11410 *
11411 * \return zero on success, -1 on error.
11412 */
11413static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11414{
11415 int cmd = 0;
11416 int retries = 0;
11417 int duration = 0;
11418 char prefile[PATH_MAX] = "";
11419 unsigned char buf[256];
11420 int bytes = 0;
11421
11422 if (ast_adsi_available(chan)) {
11423 bytes += adsi_logo(buf + bytes);
11424 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
11425 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11426 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11427 bytes += ast_adsi_voice_mode(buf + bytes, 0);
11429 }
11430
11431 ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
11432 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
11433 while ((cmd >= 0) && (cmd != 't')) {
11434 if (cmd)
11435 retries = 0;
11436 RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
11437 if (ast_fileexists(prefile, NULL, NULL) <= 0) {
11438 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11439 if (cmd == -1) {
11440 break;
11441 }
11442 cmd = 't';
11443 } else {
11444 switch (cmd) {
11445 case '1':
11446 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11447 break;
11448 case '2':
11449 DELETE(prefile, -1, prefile, vmu);
11450 ast_play_and_wait(chan, "vm-tempremoved");
11451 cmd = 't';
11452 break;
11453 case '*':
11454 cmd = 't';
11455 break;
11456 default:
11457 cmd = ast_play_and_wait(chan,
11458 ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
11459 "vm-tempgreeting2" : "vm-tempgreeting");
11460 if (!cmd) {
11461 cmd = ast_waitfordigit(chan, 6000);
11462 }
11463 if (!cmd) {
11464 retries++;
11465 }
11466 if (retries > 3) {
11467 cmd = 't';
11468 }
11469 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
11470 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
11471 }
11472 }
11473 DISPOSE(prefile, -1);
11474 }
11475 if (cmd == 't')
11476 cmd = 0;
11477 return cmd;
11478}
11479
11480/*!
11481 * \brief Greek syntax for 'You have N messages' greeting.
11482 * \param chan
11483 * \param vms
11484 * \param vmu
11485 *
11486 * \return zero on success, -1 on error.
11487 */
11488static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11489{
11490 int cmd = 0;
11491
11492 if (vms->lastmsg > -1) {
11493 cmd = play_message(chan, vmu, vms);
11494 } else {
11495 cmd = ast_play_and_wait(chan, "vm-youhaveno");
11496 if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
11497 if (!cmd) {
11498 snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
11499 cmd = ast_play_and_wait(chan, vms->fn);
11500 }
11501 if (!cmd)
11502 cmd = ast_play_and_wait(chan, "vm-messages");
11503 } else {
11504 if (!cmd)
11505 cmd = ast_play_and_wait(chan, "vm-messages");
11506 if (!cmd) {
11507 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11508 cmd = ast_play_and_wait(chan, vms->fn);
11509 }
11510 }
11511 }
11512 return cmd;
11513}
11514
11515/* Hebrew Syntax */
11516static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11517{
11518 int cmd = 0;
11519
11520 if (vms->lastmsg > -1) {
11521 cmd = play_message(chan, vmu, vms);
11522 } else {
11523 if (!strcasecmp(vms->fn, "INBOX")) {
11524 cmd = ast_play_and_wait(chan, "vm-nonewmessages");
11525 } else {
11526 cmd = ast_play_and_wait(chan, "vm-nomessages");
11527 }
11528 }
11529 return cmd;
11530}
11531
11532/*!
11533 * \brief Default English syntax for 'You have N messages' greeting.
11534 * \param chan
11535 * \param vms
11536 * \param vmu
11537 *
11538 * \return zero on success, -1 on error.
11539 */
11540static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11541{
11542 int cmd = 0;
11543
11544 if (vms->lastmsg > -1) {
11545 cmd = play_message(chan, vmu, vms);
11546 } else {
11547 cmd = ast_play_and_wait(chan, "vm-youhave");
11548 if (!cmd)
11549 cmd = ast_play_and_wait(chan, "vm-no");
11550 if (!cmd) {
11551 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11552 cmd = ast_play_and_wait(chan, vms->fn);
11553 }
11554 if (!cmd)
11555 cmd = ast_play_and_wait(chan, "vm-messages");
11556 }
11557 return cmd;
11558}
11559
11560/*!
11561 *\brief Italian syntax for 'You have N messages' greeting.
11562 * \param chan
11563 * \param vms
11564 * \param vmu
11565 *
11566 * \return zero on success, -1 on error.
11567 */
11568static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11569{
11570 int cmd;
11571
11572 if (vms->lastmsg > -1) {
11573 cmd = play_message(chan, vmu, vms);
11574 } else {
11575 cmd = ast_play_and_wait(chan, "vm-no");
11576 if (!cmd)
11577 cmd = ast_play_and_wait(chan, "vm-message");
11578 if (!cmd) {
11579 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11580 cmd = ast_play_and_wait(chan, vms->fn);
11581 }
11582 }
11583 return cmd;
11584}
11585
11586/*!
11587 * \brief Japanese syntax for 'You have N messages' greeting.
11588 * \param chan
11589 * \param vms
11590 * \param vmu
11591 *
11592 * \return zero on success, -1 on error.
11593 */
11594static int vm_browse_messages_ja(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11595{
11596 int cmd = 0;
11597
11598 if (vms->lastmsg > -1) {
11599 cmd = play_message(chan, vmu, vms);
11600 } else {
11601 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11602 cmd = ast_play_and_wait(chan, vms->fn);
11603 if (!cmd)
11604 cmd = ast_play_and_wait(chan, "vm-messages");
11605 if (!cmd)
11606 cmd = ast_play_and_wait(chan, "jp-wa");
11607 if (!cmd)
11608 cmd = ast_play_and_wait(chan, "jp-arimasen");
11609 }
11610 return cmd;
11611}
11612
11613/*!
11614 * \brief Spanish syntax for 'You have N messages' greeting.
11615 * \param chan
11616 * \param vms
11617 * \param vmu
11618 *
11619 * \return zero on success, -1 on error.
11620 */
11621static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11622{
11623 int cmd;
11624
11625 if (vms->lastmsg > -1) {
11626 cmd = play_message(chan, vmu, vms);
11627 } else {
11628 cmd = ast_play_and_wait(chan, "vm-youhaveno");
11629 if (!cmd)
11630 cmd = ast_play_and_wait(chan, "vm-messages");
11631 if (!cmd) {
11632 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11633 cmd = ast_play_and_wait(chan, vms->fn);
11634 }
11635 }
11636 return cmd;
11637}
11638
11639/*!
11640 * \brief Portuguese syntax for 'You have N messages' greeting.
11641 * \param chan
11642 * \param vms
11643 * \param vmu
11644 *
11645 * \return zero on success, -1 on error.
11646 */
11647static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11648{
11649 int cmd;
11650
11651 if (vms->lastmsg > -1) {
11652 cmd = play_message(chan, vmu, vms);
11653 } else {
11654 cmd = ast_play_and_wait(chan, "vm-no");
11655 if (!cmd) {
11656 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11657 cmd = ast_play_and_wait(chan, vms->fn);
11658 }
11659 if (!cmd)
11660 cmd = ast_play_and_wait(chan, "vm-messages");
11661 }
11662 return cmd;
11663}
11664
11665/*!
11666 * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
11667 * \param chan
11668 * \param vms
11669 * \param vmu
11670 *
11671 * \return zero on success, -1 on error.
11672 */
11673static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11674{
11675 int cmd;
11676
11677 if (vms->lastmsg > -1) {
11678 cmd = play_message(chan, vmu, vms);
11679 } else {
11680 cmd = ast_play_and_wait(chan, "vm-you");
11681 if (!cmd)
11682 cmd = ast_play_and_wait(chan, "vm-haveno");
11683 if (!cmd)
11684 cmd = ast_play_and_wait(chan, "vm-messages");
11685 if (!cmd) {
11686 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11687 cmd = ast_play_and_wait(chan, vms->fn);
11688 }
11689 }
11690 return cmd;
11691}
11692
11693/*!
11694 * \brief Vietnamese syntax for 'You have N messages' greeting.
11695 * \param chan
11696 * \param vms
11697 * \param vmu
11698 *
11699 * \return zero on success, -1 on error.
11700 */
11701static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11702{
11703 int cmd = 0;
11704
11705 if (vms->lastmsg > -1) {
11706 cmd = play_message(chan, vmu, vms);
11707 } else {
11708 cmd = ast_play_and_wait(chan, "vm-no");
11709 if (!cmd) {
11710 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11711 cmd = ast_play_and_wait(chan, vms->fn);
11712 }
11713 }
11714 return cmd;
11715}
11716
11717/*!
11718 * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
11719 * \param chan The channel for the current user. We read the language property from this.
11720 * \param vms passed into the language-specific vm_browse_messages function.
11721 * \param vmu passed into the language-specific vm_browse_messages function.
11722 *
11723 * The method to be invoked is determined by the value of language code property in the user's channel.
11724 * The default (when unable to match) is to use english.
11725 *
11726 * \return zero on success, -1 on error.
11727 */
11728static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11729{
11730 if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH */
11731 return vm_browse_messages_es(chan, vms, vmu);
11732 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK */
11733 return vm_browse_messages_gr(chan, vms, vmu);
11734 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW */
11735 return vm_browse_messages_he(chan, vms, vmu);
11736 } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN */
11737 return vm_browse_messages_it(chan, vms, vmu);
11738 } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE */
11739 return vm_browse_messages_ja(chan, vms, vmu);
11740 } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE */
11741 return vm_browse_messages_pt(chan, vms, vmu);
11742 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE */
11743 return vm_browse_messages_vi(chan, vms, vmu);
11744 } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) */
11745 return vm_browse_messages_zh(chan, vms, vmu);
11746 } else { /* Default to English syntax */
11747 return vm_browse_messages_en(chan, vms, vmu);
11748 }
11749}
11750
11751static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
11752 struct ast_vm_user *res_vmu, const char *context, const char *prefix,
11753 int skipuser, int max_logins, int silent)
11754{
11755 int useadsi = 0, valid = 0, logretries = 0;
11756 char password[AST_MAX_EXTENSION], *passptr = NULL;
11757 struct ast_vm_user vmus, *vmu = NULL;
11758
11759 /* If ADSI is supported, setup login screen */
11760 adsi_begin(chan, &useadsi);
11761 if (!skipuser && useadsi)
11762 adsi_login(chan);
11763 if (!silent && !skipuser && ast_streamfile(chan, vm_login, ast_channel_language(chan))) {
11764 ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
11765 return -1;
11766 }
11767
11768 /* Authenticate them and get their mailbox/password */
11769
11770 while (!valid && (logretries < max_logins)) {
11771 /* Prompt for, and read in the username */
11772 if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
11773 ast_log(AST_LOG_WARNING, "Couldn't read username\n");
11774 return -1;
11775 }
11776 if (ast_strlen_zero(mailbox)) {
11777 if (ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str) {
11778 ast_copy_string(mailbox, ast_channel_caller(chan)->id.number.str, mailbox_size);
11779 } else {
11780 ast_verb(3, "Username not entered\n");
11781 return -1;
11782 }
11783 } else if (mailbox[0] == '*') {
11784 /* user entered '*' */
11785 ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
11786 if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
11787 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
11788 return -1;
11789 }
11790 ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
11791 mailbox[0] = '\0';
11792 }
11793
11794 if (useadsi)
11795 adsi_password(chan);
11796
11797 if (!ast_strlen_zero(prefix)) {
11798 char fullusername[80];
11799
11800 ast_copy_string(fullusername, prefix, sizeof(fullusername));
11801 strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
11802 ast_copy_string(mailbox, fullusername, mailbox_size);
11803 }
11804
11805 ast_debug(1, "Before find user for mailbox %s\n", mailbox);
11806 memset(&vmus, 0, sizeof(vmus));
11807 vmu = find_user(&vmus, context, mailbox);
11808 if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
11809 /* saved password is blank, so don't bother asking */
11810 password[0] = '\0';
11811 } else {
11813 if (!ast_check_hangup(chan)) {
11814 ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
11815 }
11816 free_user(vmu);
11817 return -1;
11818 }
11819 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
11820 ast_log(AST_LOG_NOTICE, "Unable to read password\n");
11821 free_user(vmu);
11822 return -1;
11823 } else if (password[0] == '*') {
11824 /* user entered '*' */
11825 ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
11826 if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
11827 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
11828 mailbox[0] = '*';
11829 free_user(vmu);
11830 return -1;
11831 }
11832 ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
11833 mailbox[0] = '\0';
11834 /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
11835 free_user(vmu);
11836 vmu = NULL;
11837 }
11838 }
11839
11840 if (vmu) {
11841 passptr = vmu->password;
11842 if (passptr[0] == '-') passptr++;
11843 }
11844 if (vmu && !strcmp(passptr, password))
11845 valid++;
11846 else {
11847 ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
11848 if (!ast_strlen_zero(prefix))
11849 mailbox[0] = '\0';
11850 }
11851 logretries++;
11852 if (!valid) {
11853 if (skipuser || logretries >= max_logins) {
11854 if (ast_streamfile(chan, "vm-incorrect", ast_channel_language(chan))) {
11855 ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
11856 free_user(vmu);
11857 return -1;
11858 }
11859 if (ast_waitstream(chan, "")) { /* Channel is hung up */
11860 free_user(vmu);
11861 return -1;
11862 }
11863 } else {
11864 if (useadsi)
11865 adsi_login(chan);
11866 if (ast_streamfile(chan, "vm-incorrect-mailbox", ast_channel_language(chan))) {
11867 ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
11868 free_user(vmu);
11869 return -1;
11870 }
11871 }
11872 }
11873 }
11874 if (!valid && (logretries >= max_logins)) {
11875 ast_stopstream(chan);
11876 ast_play_and_wait(chan, "vm-goodbye");
11877 free_user(vmu);
11878 return -1;
11879 }
11880 if (vmu && !skipuser) {
11881 *res_vmu = *vmu;
11882 }
11883 return 0;
11884}
11885
11887 struct ast_vm_user *vmu,
11888 struct vm_state *vms,
11889 const char *msg_id)
11890{
11891 if (message_range_and_existence_check(vms, &msg_id, 1, &vms->curmsg, vmu)) {
11892 return -1;
11893 }
11894 /* Found the msg, so play it back */
11895
11896 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
11897
11898#ifdef IMAP_STORAGE
11899 /*IMAP storage stores any prepended message from a forward
11900 * as a separate file from the rest of the message
11901 */
11902 if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
11903 wait_file(chan, vms, vms->introfn);
11904 }
11905#endif
11906 RETRIEVE(vms->curdir,vms->curmsg,vmu->mailbox, vmu->context);
11907
11908 if ((wait_file(chan, vms, vms->fn)) < 0) {
11909 ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
11910 } else {
11911#ifdef IMAP_STORAGE
11912 ast_mutex_lock(&vms->lock);
11913#endif
11914 vms->heard[vms->curmsg] = 1;
11915#ifdef IMAP_STORAGE
11916 ast_mutex_unlock(&vms->lock);
11917#endif
11918 }
11919 DISPOSE(vms->curdir, vms->curmsg);
11920 return 0;
11921}
11922
11923/*!
11924 * \brief Finds a message in a specific mailbox by msg_id and plays it to the channel
11925 *
11926 * \retval 0 Success
11927 * \retval -1 Failure
11928 */
11929static int play_message_by_id(struct ast_channel *chan, const char *mailbox, const char *context, const char *msg_id)
11930{
11931 struct vm_state vms;
11932 struct ast_vm_user *vmu = NULL, vmus;
11933 int res = 0;
11934 int open = 0;
11935 int played = 0;
11936 int i;
11937
11938 memset(&vmus, 0, sizeof(vmus));
11939 memset(&vms, 0, sizeof(vms));
11940
11941 if (!(vmu = find_user(&vmus, context, mailbox))) {
11942 goto play_msg_cleanup;
11943 }
11944
11945 /* Iterate through every folder, find the msg, and play it */
11946 for (i = 0; i < ARRAY_LEN(mailbox_folders) && !played; i++) {
11947 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
11948 vms.lastmsg = -1;
11949
11950 /* open the mailbox state */
11951 if ((res = open_mailbox(&vms, vmu, i)) < 0) {
11952 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
11953 res = -1;
11954 goto play_msg_cleanup;
11955 }
11956 open = 1;
11957
11958 /* play msg if it exists in this mailbox */
11959 if ((vms.lastmsg != -1) && !(play_message_by_id_helper(chan, vmu, &vms, msg_id))) {
11960 played = 1;
11961 }
11962
11963 /* close mailbox */
11964 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
11965 res = -1;
11966 goto play_msg_cleanup;
11967 }
11968 open = 0;
11969 }
11970
11971play_msg_cleanup:
11972 if (!played) {
11973 res = -1;
11974 }
11975
11976 if (vmu && open) {
11977 close_mailbox(&vms, vmu);
11978 }
11979
11980#ifdef IMAP_STORAGE
11981 if (vmu) {
11982 vmstate_delete(&vms);
11983 }
11984#endif
11985
11986 free_user(vmu);
11987
11988 return res;
11989}
11990
11991static int vm_playmsgexec(struct ast_channel *chan, const char *data)
11992{
11993 char *parse;
11994 char *mailbox = NULL;
11995 char *context = NULL;
11996 int res;
11997
12000 AST_APP_ARG(msg_id);
12001 );
12002
12003 if (ast_channel_state(chan) != AST_STATE_UP) {
12004 ast_debug(1, "Before ast_answer\n");
12005 ast_answer(chan);
12006 }
12007
12008 if (ast_strlen_zero(data)) {
12009 return -1;
12010 }
12011
12012 parse = ast_strdupa(data);
12014
12015 if (ast_strlen_zero(args.mailbox) || ast_strlen_zero(args.msg_id)) {
12016 return -1;
12017 }
12018
12019 if ((context = strchr(args.mailbox, '@'))) {
12020 *context++ = '\0';
12021 }
12022 mailbox = args.mailbox;
12023
12024 res = play_message_by_id(chan, mailbox, context, args.msg_id);
12025 pbx_builtin_setvar_helper(chan, "VOICEMAIL_PLAYBACKSTATUS", res ? "FAILED" : "SUCCESS");
12026
12027 return 0;
12028}
12029
12031{
12032#define VMBOX_STRING_HEADER_FORMAT "%-32.32s %-32.32s %-16.16s %-16.16s %-16.16s %-16.16s\n"
12033#define VMBOX_STRING_DATA_FORMAT "%-32.32s %-32.32s %-16.16s %-16.16s %-16.16s %-16.16s\n"
12034
12035 const char *mailbox = a->argv[3];
12036 const char *context = a->argv[4];
12037 struct vm_state vms;
12038 struct ast_vm_user *vmu = NULL, vmus;
12039 memset(&vmus, 0, sizeof(vmus));
12040 memset(&vms, 0, sizeof(vms));
12041
12042 if (!(vmu = find_user(&vmus, context, mailbox))) {
12043 ast_cli(a->fd, "Can't find voicemail user %s@%s\n", mailbox, context);
12044 return -1;
12045 }
12046
12047 ast_cli(a->fd, VMBOX_STRING_HEADER_FORMAT, "Full Name", "Email", "Pager", "Language", "Locale", "Time Zone");
12048 ast_cli(a->fd, VMBOX_STRING_DATA_FORMAT, vmu->fullname, vmu->email, vmu->pager, vmu->language, vmu->locale, vmu->zonetag);
12049
12050 return 0;
12051}
12052
12054{
12055#define VM_STRING_HEADER_FORMAT "%-8.8s %-32.32s %-32.32s %-9.9s %-6.6s %-30.30s\n"
12056 const char *mailbox = a->argv[3];
12057 const char *context = a->argv[4];
12058 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
12059 struct ast_vm_msg_snapshot *msg;
12060 int i;
12061
12062 /* Take a snapshot of the mailbox and walk through each folder's contents */
12064 if (!mailbox_snapshot) {
12065 ast_cli(a->fd, "Can't create snapshot for voicemail user %s@%s\n", mailbox, context);
12066 return -1;
12067 }
12068
12069 ast_cli(a->fd, VM_STRING_HEADER_FORMAT, "Folder", "Caller ID", "Date", "Duration", "Flag", "ID");
12070
12071 for (i = 0; i < mailbox_snapshot->folders; i++) {
12072 AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
12073 ast_cli(a->fd, VM_STRING_HEADER_FORMAT, msg->folder_name, msg->callerid, msg->origdate, msg->duration,
12074 msg->flag, msg->msg_id);
12075 }
12076 }
12077
12078 ast_cli(a->fd, "%d Message%s Total\n", mailbox_snapshot->total_msg_num, ESS(mailbox_snapshot->total_msg_num));
12079 /* done, destroy. */
12080 mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
12081
12082 return 0;
12083}
12084
12086{
12087 if (show_mailbox_details(a)){
12088 return -1;
12089 }
12090 ast_cli(a->fd, "\n");
12091 return show_mailbox_snapshot(a);
12092}
12093
12095{
12096 const char *from_mailbox = a->argv[2];
12097 const char *from_context = a->argv[3];
12098 const char *from_folder = a->argv[4];
12099 const char *id[] = { a->argv[5] };
12100 const char *to_mailbox = a->argv[6];
12101 const char *to_context = a->argv[7];
12102 const char *to_folder = a->argv[8];
12103 int ret = vm_msg_forward(from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder, 1, id, 0);
12104 if (ret) {
12105 ast_cli(a->fd, "Error forwarding message %s from mailbox %s@%s %s to mailbox %s@%s %s\n",
12106 id[0], from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder);
12107 } else {
12108 ast_cli(a->fd, "Forwarded message %s from mailbox %s@%s %s to mailbox %s@%s %s\n",
12109 id[0], from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder);
12110 }
12111 return ret;
12112}
12113
12115{
12116 const char *mailbox = a->argv[2];
12117 const char *context = a->argv[3];
12118 const char *from_folder = a->argv[4];
12119 const char *id[] = { a->argv[5] };
12120 const char *to_folder = a->argv[6];
12121 int ret = vm_msg_move(mailbox, context, 1, from_folder, id, to_folder);
12122 if (ret) {
12123 ast_cli(a->fd, "Error moving message %s from mailbox %s@%s %s to %s\n",
12124 id[0], mailbox, context, from_folder, to_folder);
12125 } else {
12126 ast_cli(a->fd, "Moved message %s from mailbox %s@%s %s to %s\n",
12127 id[0], mailbox, context, from_folder, to_folder);
12128 }
12129 return ret;
12130}
12131
12133{
12134 const char *mailbox = a->argv[2];
12135 const char *context = a->argv[3];
12136 const char *folder = a->argv[4];
12137 const char *id[] = { a->argv[5] };
12138 int ret = vm_msg_remove(mailbox, context, 1, folder, id);
12139 if (ret) {
12140 ast_cli(a->fd, "Error removing message %s from mailbox %s@%s %s\n",
12141 id[0], mailbox, context, folder);
12142 } else {
12143 ast_cli(a->fd, "Removed message %s from mailbox %s@%s %s\n",
12144 id[0], mailbox, context, folder);
12145 }
12146 return ret;
12147}
12148
12150{
12151 const char *word = a->word;
12152 int pos = a->pos;
12153 int state = a->n;
12154 int which = 0;
12155 int wordlen;
12156 struct ast_vm_user *vmu;
12157 const char *context = "", *mailbox = "";
12158 char *ret = NULL;
12159
12160 /* 0 - voicemail; 1 - show; 2 - mailbox; 3 - <mailbox>; 4 - <context> */
12161 if (pos == 3) {
12162 wordlen = strlen(word);
12164 AST_LIST_TRAVERSE(&users, vmu, list) {
12165 if (!strncasecmp(word, vmu->mailbox , wordlen)) {
12166 if (mailbox && strcmp(mailbox, vmu->mailbox) && ++which > state) {
12167 ret = ast_strdup(vmu->mailbox);
12169 return ret;
12170 }
12171 mailbox = vmu->mailbox;
12172 }
12173 }
12175 } else if (pos == 4) {
12176 /* Only display contexts that match the user in pos 3 */
12177 const char *box = a->argv[3];
12178 wordlen = strlen(word);
12180 AST_LIST_TRAVERSE(&users, vmu, list) {
12181 if (!strncasecmp(word, vmu->context, wordlen) && !strcasecmp(box, vmu->mailbox)) {
12182 if (context && strcmp(context, vmu->context) && ++which > state) {
12183 ret = ast_strdup(vmu->context);
12185 return ret;
12186 }
12187 context = vmu->context;
12188 }
12189 }
12191 }
12192
12193 return ret;
12194}
12195
12196static char *handle_voicemail_show_mailbox(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12197{
12198 switch (cmd) {
12199 case CLI_INIT:
12200 e->command = "voicemail show mailbox";
12201 e->usage =
12202 "Usage: voicemail show mailbox <mailbox> <context>\n"
12203 " Show contents of mailbox <mailbox>@<context>\n";
12204 return NULL;
12205 case CLI_GENERATE:
12207 case CLI_HANDLER:
12208 break;
12209 }
12210
12211 if (a->argc != 5) {
12212 return CLI_SHOWUSAGE;
12213 }
12214
12216 return CLI_FAILURE;
12217 }
12218
12219 return CLI_SUCCESS;
12220}
12221
12222/* Handles filling in data for one of the following three formats (based on maxpos = 5|6|8):
12223
12224 maxpos = 5
12225 0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12226 maxpos = 6
12227 0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12228 6 - <to_folder>;
12229 maxpos = 8
12230 0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12231 6 - <to_mailbox>; 7 - <to_context>; 8 - <to_folder>;
12232
12233 Passing in the maximum expected position 'maxpos' helps us fill in the missing entries in one function
12234 instead of three by taking advantage of the overlap in the command sequence between forward, move and
12235 remove as each of these use nearly the same syntax up until their maximum number of arguments.
12236 The value of pos = 6 changes to be either <messageid> or <folder> based on maxpos being 6 or 8.
12237*/
12238
12239static char *complete_voicemail_move_message(struct ast_cli_args *a, int maxpos)
12240{
12241 const char *word = a->word;
12242 int pos = a->pos;
12243 int state = a->n;
12244 int which = 0;
12245 int wordlen;
12246 struct ast_vm_user *vmu;
12247 const char *context = "", *mailbox = "", *folder = "", *id = "";
12248 char *ret = NULL;
12249
12250 if (pos > maxpos) {
12251 /* If the passed in pos is above the max, return NULL to avoid 'over-filling' the cli */
12252 return NULL;
12253 }
12254
12255 /* if we are in pos 2 or pos 6 in 'forward' mode */
12256 if (pos == 2 || (pos == 6 && maxpos == 8)) {
12257 /* find users */
12258 wordlen = strlen(word);
12260 AST_LIST_TRAVERSE(&users, vmu, list) {
12261 if (!strncasecmp(word, vmu->mailbox , wordlen)) {
12262 if (mailbox && strcmp(mailbox, vmu->mailbox) && ++which > state) {
12263 ret = ast_strdup(vmu->mailbox);
12265 return ret;
12266 }
12267 mailbox = vmu->mailbox;
12268 }
12269 }
12271 } else if (pos == 3 || pos == 7) {
12272 /* find contexts that match the user */
12273 mailbox = (pos == 3) ? a->argv[2] : a->argv[6];
12274 wordlen = strlen(word);
12276 AST_LIST_TRAVERSE(&users, vmu, list) {
12277 if (!strncasecmp(word, vmu->context, wordlen) && !strcasecmp(mailbox, vmu->mailbox)) {
12278 if (context && strcmp(context, vmu->context) && ++which > state) {
12279 ret = ast_strdup(vmu->context);
12281 return ret;
12282 }
12283 context = vmu->context;
12284 }
12285 }
12287 } else if (pos == 4 || pos == 8 || (pos == 6 && maxpos == 6) ) {
12288 int i;
12289 /* Walk through the standard folders */
12290 wordlen = strlen(word);
12291 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
12292 if (folder && !strncasecmp(word, mailbox_folders[i], wordlen) && ++which > state) {
12293 return ast_strdup(mailbox_folders[i]);
12294 }
12295 folder = mailbox_folders[i];
12296 }
12297 } else if (pos == 5) {
12298 /* find messages in the folder */
12299 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
12300 struct ast_vm_msg_snapshot *msg;
12301 mailbox = a->argv[2];
12302 context = a->argv[3];
12303 folder = a->argv[4];
12304 wordlen = strlen(word);
12305
12306 /* Take a snapshot of the mailbox and snag the individual info */
12307 if ((mailbox_snapshot = ast_vm_mailbox_snapshot_create(mailbox, context, folder, 0, AST_VM_SNAPSHOT_SORT_BY_ID, 0))) {
12308 int i;
12309 /* we are only requesting the one folder, but we still need to know it's index */
12310 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
12311 if (!strcasecmp(mailbox_folders[i], folder)) {
12312 break;
12313 }
12314 }
12315 AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
12316 if (id && !strncasecmp(word, msg->msg_id, wordlen) && ++which > state) {
12317 ret = ast_strdup(msg->msg_id);
12318 break;
12319 }
12320 id = msg->msg_id;
12321 }
12322 /* done, destroy. */
12323 mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
12324 }
12325 }
12326
12327 return ret;
12328}
12329
12330static char *handle_voicemail_forward_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12331{
12332 switch (cmd) {
12333 case CLI_INIT:
12334 e->command = "voicemail forward";
12335 e->usage =
12336 "Usage: voicemail forward <from_mailbox> <from_context> <from_folder> <messageid> <to_mailbox> <to_context> <to_folder>\n"
12337 " Forward message <messageid> in mailbox <mailbox>@<context> <from_folder>\n"
12338 " to mailbox <mailbox>@<context> <to_folder>\n";
12339 return NULL;
12340 case CLI_GENERATE:
12342 case CLI_HANDLER:
12343 break;
12344 }
12345
12346 if (a->argc != 9) {
12347 return CLI_SHOWUSAGE;
12348 }
12349
12351 return CLI_FAILURE;
12352 }
12353
12354 return CLI_SUCCESS;
12355}
12356
12357static char *handle_voicemail_move_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12358{
12359 switch (cmd) {
12360 case CLI_INIT:
12361 e->command = "voicemail move";
12362 e->usage =
12363 "Usage: voicemail move <mailbox> <context> <from_folder> <messageid> <to_folder>\n"
12364 " Move message <messageid> in mailbox <mailbox>&<context> from <from_folder> to <to_folder>\n";
12365 return NULL;
12366 case CLI_GENERATE:
12368 case CLI_HANDLER:
12369 break;
12370 }
12371
12372 if (a->argc != 7) {
12373 return CLI_SHOWUSAGE;
12374 }
12375
12377 return CLI_FAILURE;
12378 }
12379
12380 return CLI_SUCCESS;
12381}
12382
12383static char *handle_voicemail_remove_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12384{
12385 switch (cmd) {
12386 case CLI_INIT:
12387 e->command = "voicemail remove";
12388 e->usage =
12389 "Usage: voicemail remove <mailbox> <context> <from_folder> <messageid>\n"
12390 " Remove message <messageid> from <from_folder> in mailbox <mailbox>@<context>\n";
12391 return NULL;
12392 case CLI_GENERATE:
12394 case CLI_HANDLER:
12395 break;
12396 }
12397
12398 if (a->argc != 6) {
12399 return CLI_SHOWUSAGE;
12400 }
12401
12403 return CLI_FAILURE;
12404 }
12405
12406 return CLI_SUCCESS;
12407}
12408
12409static int vm_execmain(struct ast_channel *chan, const char *data)
12410{
12411 /* XXX This is, admittedly, some pretty horrendous code. For some
12412 reason it just seemed a lot easier to do with GOTO's. I feel
12413 like I'm back in my GWBASIC days. XXX */
12414 int res = -1;
12415 int cmd = 0;
12416 int valid = 0;
12417 char prefixstr[80] ="";
12418 char ext_context[256]="";
12419 int box;
12420 int useadsi = 0;
12421 int skipuser = 0;
12422 struct vm_state vms = {{0}};
12423 struct ast_vm_user *vmu = NULL, vmus = {{0}};
12424 char *context = NULL;
12425 int silentexit = 0;
12426 struct ast_flags flags = { 0 };
12427 signed char record_gain = 0;
12428 int play_auto = 0;
12429 int play_folder = 0;
12430 int in_urgent = 0;
12431 int nodelete = 0;
12432#ifdef IMAP_STORAGE
12433 int deleted = 0;
12434#endif
12435 SCOPE_ENTER(3, "%s:\n", ast_channel_name(chan));
12436
12437 /* Add the vm_state to the active list and keep it active */
12438 vms.lastmsg = -1;
12439
12440 ast_test_suite_event_notify("START", "Message: vm_execmain started");
12441 if (ast_channel_state(chan) != AST_STATE_UP) {
12442 ast_debug(1, "Before ast_answer\n");
12443 ast_answer(chan);
12444 }
12445
12446 if (!ast_strlen_zero(data)) {
12447 char *opts[OPT_ARG_ARRAY_SIZE];
12448 char *parse;
12450 AST_APP_ARG(argv0);
12451 AST_APP_ARG(argv1);
12452 );
12453
12454 parse = ast_strdupa(data);
12455
12457
12458 if (args.argc == 2) {
12459 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
12460 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid option string '%s'\n", args.argv1);
12461 }
12463 int gain;
12464 if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
12465 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
12466 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
12467 } else {
12468 record_gain = (signed char) gain;
12469 }
12470 } else {
12471 ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
12472 }
12473 }
12475 play_auto = 1;
12476 if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
12477 /* See if it is a folder name first */
12478 if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
12479 if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
12480 play_folder = -1;
12481 }
12482 } else {
12483 play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
12484 }
12485 } else {
12486 ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
12487 }
12488 if (play_folder > 9 || play_folder < 0) {
12490 "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
12491 opts[OPT_ARG_PLAYFOLDER]);
12492 play_folder = 0;
12493 }
12494 }
12496 nodelete = 1;
12497 }
12498 } else {
12499 /* old style options parsing */
12500 while (*(args.argv0)) {
12501 if (*(args.argv0) == 's')
12503 else if (*(args.argv0) == 'p')
12505 else
12506 break;
12507 (args.argv0)++;
12508 }
12509
12510 }
12511
12512 valid = ast_test_flag(&flags, OPT_SILENT);
12513
12514 if ((context = strchr(args.argv0, '@')))
12515 *context++ = '\0';
12516
12518 ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
12519 else
12520 ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
12521
12522 if (!ast_strlen_zero(vms.username)) {
12523 if ((vmu = find_user(&vmus, context ,vms.username))) {
12524 skipuser++;
12525 } else {
12526 ast_log(LOG_WARNING, "Mailbox '%s%s%s' doesn't exist\n", vms.username, context ? "@": "", context ? context : "");
12527 valid = 0;
12528 }
12529 } else {
12530 valid = 0;
12531 }
12532 }
12533
12534 if (!valid)
12535 res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
12536
12537 ast_trace(-1, "vm_authenticate user: %s\n", vms.username);
12538
12539 if (vms.username[0] == '*') {
12540 ast_trace(-1, "user pressed * in context '%s'\n", ast_channel_context(chan));
12541
12542 /* user entered '*' */
12543 if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
12544 ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
12545 res = 0; /* prevent hangup */
12546 goto out;
12547 }
12548 }
12549
12550 if (!res) {
12551 valid = 1;
12552 if (!skipuser)
12553 vmu = &vmus;
12554 } else {
12555 res = 0;
12556 }
12557
12558 /* If ADSI is supported, setup login screen */
12559 adsi_begin(chan, &useadsi);
12560
12561 if (!valid) {
12562 ast_trace(-1, "Invalid user\n");
12563 goto out;
12564 }
12565 ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
12566
12567#ifdef IMAP_STORAGE
12568 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
12569 pthread_setspecific(ts_vmstate.key, &vms);
12570
12571 vms.interactive = 1;
12572 vms.updated = 1;
12573 if (vmu)
12574 ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
12575 vmstate_insert(&vms);
12576 init_vm_state(&vms);
12577#endif
12578
12579 /* Set language from config to override channel language */
12580 if (!ast_strlen_zero(vmu->language)) {
12581 ast_channel_lock(chan);
12582 ast_channel_language_set(chan, vmu->language);
12583 ast_channel_unlock(chan);
12584 }
12585
12586 /* Retrieve urgent, old and new message counts */
12587 ast_trace(-1, "Before open_mailbox\n");
12588 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
12589 if (res < 0) {
12590 ast_trace(-1, "open mailbox: %d\n", res);
12591 goto out;
12592 }
12593 vms.oldmessages = vms.lastmsg + 1;
12594 ast_trace(-1, "Number of old messages: %d\n", vms.oldmessages);
12595 /* check INBOX */
12596 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12597 if (res < 0) {
12598 ast_trace(-1, "open mailbox: %d\n", res);
12599 goto out;
12600 }
12601 vms.newmessages = vms.lastmsg + 1;
12602 ast_trace(-1, "Number of new messages: %d\n", vms.newmessages);
12603 /* Start in Urgent */
12604 in_urgent = 1;
12605 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11); /*11 is the Urgent folder */
12606 if (res < 0) {
12607 ast_trace(-1, "open mailbox: %d\n", res);
12608 goto out;
12609 }
12610 vms.urgentmessages = vms.lastmsg + 1;
12611 ast_trace(-1, "Number of urgent messages: %d\n", vms.urgentmessages);
12612
12613 /* Select proper mailbox FIRST!! */
12614 if (play_auto) {
12615 ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
12616 if (vms.urgentmessages) {
12617 in_urgent = 1;
12618 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11);
12619 } else {
12620 in_urgent = 0;
12621 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, play_folder);
12622 }
12623 if (res < 0) {
12624 ast_trace(-1, "open mailbox: %d\n", res);
12625 goto out;
12626 }
12627
12628 /* If there are no new messages, inform the user and hangup */
12629 if (vms.lastmsg == -1) {
12630 in_urgent = 0;
12631 cmd = vm_browse_messages(chan, &vms, vmu);
12632 res = 0;
12633 goto out;
12634 }
12635 } else {
12636 if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
12637 /* If we only have old messages start here */
12638 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
12639 in_urgent = 0;
12640 play_folder = 1;
12641 if (res < 0)
12642 goto out;
12643 } else if (!vms.urgentmessages && vms.newmessages) {
12644 /* If we have new messages but none are urgent */
12645 in_urgent = 0;
12646 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12647 if (res < 0)
12648 goto out;
12649 }
12650 }
12651
12652 if (useadsi)
12653 adsi_status(chan, &vms);
12654 res = 0;
12655
12656 /* Check to see if this is a new user */
12657 if (!strcasecmp(vmu->mailbox, vmu->password) &&
12659 if (ast_play_and_wait(chan, vm_newuser) == -1)
12660 ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
12661 cmd = vm_newuser_setup(chan, vmu, &vms, vmfmts, record_gain);
12662 if ((cmd == 't') || (cmd == '#')) {
12663 /* Timeout */
12664 ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
12665 res = 0;
12666 ast_trace(-1, "Timeout\n");
12667 goto out;
12668 } else if (cmd < 0) {
12669 /* Hangup */
12670 ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
12671 res = -1;
12672 ast_trace(-1, "Hangup\n");
12673 goto out;
12674 }
12675 }
12676#ifdef IMAP_STORAGE
12677 ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
12678 if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
12679 ast_debug(1, "*** QUOTA EXCEEDED!!\n");
12680 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
12681 }
12682 ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
12683 if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
12684 ast_log(AST_LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
12685 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
12686 }
12687#endif
12688
12689 ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
12690 if (play_auto) {
12691 cmd = '1';
12692 } else {
12693 cmd = vm_intro(chan, vmu, &vms);
12694 }
12695 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12696 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12697
12698 vms.repeats = 0;
12699 vms.starting = 1;
12700 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
12701 /* Run main menu */
12702 ast_trace(-1, "Main menu: %d %c\n", cmd, (cmd >= 32 && cmd <= 126 ? cmd : ' '));
12703 switch (cmd) {
12704 case '1': /* First message */
12705 vms.curmsg = 0;
12706 /* Fall through */
12707 case '5': /* Play current message */
12708 ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
12709 cmd = vm_browse_messages(chan, &vms, vmu);
12710 break;
12711 case '2': /* Change folders */
12712 ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
12713 if (useadsi)
12714 adsi_folders(chan, 0, "Change to folder...");
12715
12716 cmd = get_folder2(chan, "vm-changeto", 0);
12717 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12718 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12719 if (cmd == '#') {
12720 cmd = 0;
12721 } else if (cmd > 0) {
12722 cmd = cmd - '0';
12723 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12724 if (res == ERROR_LOCK_PATH) {
12725 ast_trace(-1, "close mailbox: %d\n", res);
12726 goto out;
12727 }
12728 /* If folder is not urgent, set in_urgent to zero! */
12729 if (cmd != 11) in_urgent = 0;
12730 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, cmd);
12731 if (res < 0) {
12732 ast_trace(-1, "open mailbox: %d\n", res);
12733 goto out;
12734 }
12735 play_folder = cmd;
12736 cmd = 0;
12737 }
12738 if (useadsi)
12739 adsi_status2(chan, &vms);
12740
12741 if (!cmd) {
12742 cmd = vm_play_folder_name(chan, vms.vmbox);
12743 }
12744
12745 vms.starting = 1;
12746 vms.curmsg = 0;
12747 break;
12748 case '3': /* Advanced options */
12749 ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
12750 cmd = 0;
12751 vms.repeats = 0;
12752 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
12753 switch (cmd) {
12754 case '1': /* Reply */
12755 if (vms.lastmsg > -1 && !vms.starting) {
12756 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
12757 if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
12758 res = cmd;
12759 ast_trace(-1, "advanced options: %d\n", cmd);
12760 goto out;
12761 }
12762 } else {
12763 cmd = ast_play_and_wait(chan, "vm-sorry");
12764 }
12765 cmd = 't';
12766 break;
12767 case '2': /* Callback */
12768 if (!vms.starting)
12769 ast_verb(3, "Callback Requested\n");
12770 if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
12771 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
12772 ast_trace(-1, "advanced options: %d\n", cmd);
12773 if (cmd == 9) {
12774 silentexit = 1;
12775 goto out;
12776 } else if (cmd == ERROR_LOCK_PATH) {
12777 res = cmd;
12778 goto out;
12779 }
12780 } else {
12781 cmd = ast_play_and_wait(chan, "vm-sorry");
12782 }
12783 cmd = 't';
12784 break;
12785 case '3': /* Envelope */
12786 if (vms.lastmsg > -1 && !vms.starting) {
12787 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
12788 if (cmd == ERROR_LOCK_PATH) {
12789 res = cmd;
12790 ast_trace(-1, "advanced options: %d\n", cmd);
12791 goto out;
12792 }
12793 } else {
12794 cmd = ast_play_and_wait(chan, "vm-sorry");
12795 }
12796 cmd = 't';
12797 break;
12798 case '4': /* Dialout */
12799 if (!ast_strlen_zero(vmu->dialout)) {
12800 cmd = dialout(chan, vmu, NULL, vmu->dialout);
12801 if (cmd == 9) {
12802 silentexit = 1;
12803 ast_trace(-1, "dialout: %d\n", cmd);
12804 goto out;
12805 }
12806 } else {
12807 cmd = ast_play_and_wait(chan, "vm-sorry");
12808 }
12809 cmd = 't';
12810 break;
12811
12812 case '5': /* Leave VoiceMail */
12813 if (ast_test_flag(vmu, VM_SVMAIL)) {
12814 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, forward_message, chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
12815 if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
12816 res = cmd;
12817 ast_trace(-1, "forward message: %d\n", cmd);
12818 goto out;
12819 }
12820 } else {
12821 cmd = ast_play_and_wait(chan, "vm-sorry");
12822 }
12823 cmd = 't';
12824 break;
12825
12826 case '*': /* Return to main menu */
12827 cmd = 't';
12828 break;
12829
12830 default:
12831 cmd = 0;
12832 if (!vms.starting) {
12833 cmd = ast_play_and_wait(chan, "vm-toreply");
12834 }
12835 if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
12836 cmd = ast_play_and_wait(chan, "vm-tocallback");
12837 }
12838 if (!cmd && !vms.starting) {
12839 cmd = ast_play_and_wait(chan, "vm-tohearenv");
12840 }
12841 if (!ast_strlen_zero(vmu->dialout) && !cmd) {
12842 cmd = ast_play_and_wait(chan, "vm-tomakecall");
12843 }
12844 if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
12845 cmd = ast_play_and_wait(chan, "vm-leavemsg");
12846 }
12847 if (!cmd) {
12848 cmd = ast_play_and_wait(chan, "vm-starmain");
12849 }
12850 if (!cmd) {
12851 cmd = ast_waitfordigit(chan, 6000);
12852 }
12853 if (!cmd) {
12854 vms.repeats++;
12855 }
12856 if (vms.repeats > 3) {
12857 cmd = 't';
12858 }
12859 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12860 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12861 }
12862 }
12863 if (cmd == 't') {
12864 cmd = 0;
12865 vms.repeats = 0;
12866 }
12867 break;
12868 case '4': /* Go to the previous message */
12869 ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
12870 if (vms.curmsg > 0) {
12871 vms.curmsg--;
12872 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12873 } else {
12874 /* Check if we were listening to new
12875 messages. If so, go to Urgent messages
12876 instead of saying "no more messages"
12877 */
12878 if (in_urgent == 0 && vms.urgentmessages > 0) {
12879 /* Check for Urgent messages */
12880 in_urgent = 1;
12881 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12882 if (res == ERROR_LOCK_PATH) {
12883 ast_trace(-1, "close mailbox: %d\n", res);
12884 goto out;
12885 }
12886 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11); /* Open Urgent folder */
12887 if (res < 0) {
12888 ast_trace(-1, "open mailbox: %d\n", res);
12889 goto out;
12890 }
12891 ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
12892 vms.curmsg = vms.lastmsg;
12893 if (vms.lastmsg < 0) {
12894 cmd = ast_play_and_wait(chan, "vm-nomore");
12895 }
12896 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12897 vms.curmsg = vms.lastmsg;
12898 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12899 } else {
12900 cmd = ast_play_and_wait(chan, "vm-nomore");
12901 }
12902 }
12903 break;
12904 case '6': /* Go to the next message */
12905 ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
12906 if (vms.curmsg < vms.lastmsg) {
12907 vms.curmsg++;
12908 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12909 } else {
12910 if (in_urgent && vms.newmessages > 0) {
12911 /* Check if we were listening to urgent
12912 * messages. If so, go to regular new messages
12913 * instead of saying "no more messages"
12914 */
12915 in_urgent = 0;
12916 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12917 if (res == ERROR_LOCK_PATH) {
12918 ast_trace(-1, "close mailbox: %d\n", res);
12919 goto out;
12920 }
12921 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12922 if (res < 0) {
12923 ast_trace(-1, "open mailbox: %d\n", res);
12924 goto out;
12925 }
12926 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12927 vms.curmsg = -1;
12928 if (vms.lastmsg < 0) {
12929 cmd = ast_play_and_wait(chan, "vm-nomore");
12930 }
12931 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12932 vms.curmsg = 0;
12933 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12934 } else {
12935 cmd = ast_play_and_wait(chan, "vm-nomore");
12936 }
12937 }
12938 break;
12939 case '7': /* Delete the current message */
12940 if (!nodelete && vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
12941 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
12942 if (useadsi)
12943 adsi_delete(chan, &vms);
12944 if (vms.deleted[vms.curmsg]) {
12945 if (play_folder == 0) {
12946 if (in_urgent) {
12947 vms.urgentmessages--;
12948 } else {
12949 vms.newmessages--;
12950 }
12951 }
12952 else if (play_folder == 1)
12953 vms.oldmessages--;
12954 cmd = ast_play_and_wait(chan, "vm-deleted");
12955 } else {
12956 if (play_folder == 0) {
12957 if (in_urgent) {
12958 vms.urgentmessages++;
12959 } else {
12960 vms.newmessages++;
12961 }
12962 }
12963 else if (play_folder == 1)
12964 vms.oldmessages++;
12965 cmd = ast_play_and_wait(chan, "vm-undeleted");
12966 }
12967 if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
12968 if (vms.curmsg < vms.lastmsg) {
12969 vms.curmsg++;
12970 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12971 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12972 vms.curmsg = 0;
12973 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12974 } else {
12975 /* Check if we were listening to urgent
12976 messages. If so, go to regular new messages
12977 instead of saying "no more messages"
12978 */
12979 if (in_urgent == 1) {
12980 /* Check for new messages */
12981 in_urgent = 0;
12982 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12983 if (res == ERROR_LOCK_PATH) {
12984 ast_trace(-1, "close mailbox: %d\n", res);
12985 goto out;
12986 }
12987 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12988 if (res < 0) {
12989 ast_trace(-1, "open mailbox: %d\n", res);
12990 goto out;
12991 }
12992 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12993 vms.curmsg = -1;
12994 if (vms.lastmsg < 0) {
12995 cmd = ast_play_and_wait(chan, "vm-nomore");
12996 }
12997 } else {
12998 cmd = ast_play_and_wait(chan, "vm-nomore");
12999 }
13000 }
13001 }
13002 } else /* Delete not valid if we haven't selected a message */
13003 cmd = 0;
13004#ifdef IMAP_STORAGE
13005 deleted = 1;
13006#endif
13007 break;
13008
13009 case '8': /* Forward the current message */
13010 if (vms.lastmsg > -1) {
13011 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, forward_message, chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
13012 if (cmd == ERROR_LOCK_PATH) {
13013 res = cmd;
13014 ast_trace(-1, "forward message: %d\n", res);
13015 goto out;
13016 }
13017 } else {
13018 /* Check if we were listening to urgent
13019 messages. If so, go to regular new messages
13020 instead of saying "no more messages"
13021 */
13022 if (in_urgent == 1 && vms.newmessages > 0) {
13023 /* Check for new messages */
13024 in_urgent = 0;
13025 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
13026 if (res == ERROR_LOCK_PATH) {
13027 ast_trace(-1, "close mailbox: %d\n", res);
13028 goto out;
13029 }
13030 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
13031 if (res < 0) {
13032 ast_trace(-1, "open mailbox: %d\n", res);
13033 goto out;
13034 }
13035 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
13036 vms.curmsg = -1;
13037 if (vms.lastmsg < 0) {
13038 cmd = ast_play_and_wait(chan, "vm-nomore");
13039 }
13040 } else {
13041 cmd = ast_play_and_wait(chan, "vm-nomore");
13042 }
13043 }
13044 break;
13045 case '9': /* Save message to folder */
13046 ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
13047 if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
13048 /* No message selected */
13049 cmd = 0;
13050 break;
13051 }
13052 if (useadsi)
13053 adsi_folders(chan, 1, "Save to folder...");
13054 cmd = get_folder2(chan, "vm-savefolder", 1);
13055 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
13056 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
13057 box = 0; /* Shut up compiler */
13058 if (cmd == '#') {
13059 cmd = 0;
13060 break;
13061 } else if (cmd > 0) {
13062 box = cmd = cmd - '0';
13063 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, &vms, vms.curmsg, cmd, NULL, 0);
13064 if (cmd == ERROR_LOCK_PATH) {
13065 res = cmd;
13066 ast_trace(-1, "save to folder: %d\n", res);
13067 goto out;
13068#ifndef IMAP_STORAGE
13069 } else if (!cmd) {
13070 vms.deleted[vms.curmsg] = 1;
13071#endif
13072 } else {
13073 vms.deleted[vms.curmsg] = 0;
13074 vms.heard[vms.curmsg] = 0;
13075 }
13076 }
13077 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
13078 if (useadsi)
13079 adsi_message(chan, &vms);
13080 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
13081 if (!cmd) {
13082 cmd = ast_play_and_wait(chan, "vm-message");
13083 if (!cmd)
13084 cmd = say_and_wait(chan, vms.curmsg + 1, ast_channel_language(chan));
13085 if (!cmd)
13086 cmd = ast_play_and_wait(chan, "vm-savedto");
13087 if (!cmd)
13088 cmd = vm_play_folder_name(chan, vms.fn);
13089 } else {
13090 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
13091 }
13093 if (vms.curmsg < vms.lastmsg) {
13094 vms.curmsg++;
13095 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
13096 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
13097 vms.curmsg = 0;
13098 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
13099 } else {
13100 /* Check if we were listening to urgent
13101 messages. If so, go to regular new messages
13102 instead of saying "no more messages"
13103 */
13104 if (in_urgent == 1 && vms.newmessages > 0) {
13105 /* Check for new messages */
13106 in_urgent = 0;
13107 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
13108 if (res == ERROR_LOCK_PATH) {
13109 ast_trace(-1, "close mailbox: %d\n", res);
13110 goto out;
13111 }
13112 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
13113 if (res < 0) {
13114 ast_trace(-1, "open mailbox: %d\n", res);
13115 goto out;
13116 }
13117 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
13118 vms.curmsg = -1;
13119 if (vms.lastmsg < 0) {
13120 cmd = ast_play_and_wait(chan, "vm-nomore");
13121 }
13122 } else {
13123 cmd = ast_play_and_wait(chan, "vm-nomore");
13124 }
13125 }
13126 }
13127 break;
13128 case '*': /* Help */
13129 if (!vms.starting) {
13130 if (!strncasecmp(ast_channel_language(chan), "ja", 2)) {
13131 cmd = vm_play_folder_name(chan, vms.vmbox);
13132 if (!cmd)
13133 cmd = ast_play_and_wait(chan, "jp-wa");
13134 if (!cmd)
13135 cmd = ast_play_and_wait(chan, "digits/1");
13136 if (!cmd)
13137 cmd = ast_play_and_wait(chan, "jp-wo");
13138 if (!cmd)
13139 cmd = ast_play_and_wait(chan, "silence/1");
13140 if (!cmd)
13141 cmd = ast_play_and_wait(chan, "vm-opts");
13142 if (!cmd)
13143 cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent, nodelete);
13144 break;
13145 }
13146 cmd = ast_play_and_wait(chan, "vm-onefor");
13147 if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
13148 cmd = ast_play_and_wait(chan, "vm-for");
13149 }
13150 if (!cmd)
13151 cmd = vm_play_folder_name(chan, vms.vmbox);
13152 if (!cmd)
13153 cmd = ast_play_and_wait(chan, "vm-opts");
13154 if (!cmd)
13155 cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent, nodelete);
13156 } else
13157 cmd = 0;
13158 break;
13159 case '0': /* Mailbox options */
13160 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, vm_options,chan, vmu, &vms, vmfmts, record_gain);
13161 if (useadsi)
13162 adsi_status(chan, &vms);
13163 /* Reopen play_folder */
13164 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, play_folder);
13165 if (res < 0) {
13166 ast_trace(-1, "open mailbox: %d\n", res);
13167 goto out;
13168 }
13169 vms.starting = 1;
13170 break;
13171 default: /* Nothing */
13172 ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
13173 cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent, nodelete);
13174 break;
13175 }
13176 }
13177 if ((cmd == 't') || (cmd == '#')) {
13178 /* Timeout */
13179 res = 0;
13180 } else {
13181 /* Hangup */
13182 res = -1;
13183 }
13184
13185out:
13186 if (res > -1) {
13187 ast_stopstream(chan);
13188 adsi_goodbye(chan);
13189 if (valid && res != OPERATOR_EXIT) {
13190 if (silentexit)
13191 res = ast_play_and_wait(chan, "vm-dialout");
13192 else
13193 res = ast_play_and_wait(chan, "vm-goodbye");
13194 }
13195 if ((valid && res > 0) || res == OPERATOR_EXIT) {
13196 res = 0;
13197 }
13198 if (useadsi)
13200 }
13201 if (vmu) {
13202 SCOPE_CALL(-1, close_mailbox, &vms, vmu);
13203 }
13204 if (valid) {
13205 int new = 0, old = 0, urgent = 0;
13206 snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
13207 /* Urgent flag not passwd to externnotify here */
13208 run_externnotify(vmu->context, vmu->mailbox, NULL);
13209 ast_app_inboxcount2(ext_context, &urgent, &new, &old);
13210 queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgent, new, old);
13211 }
13212#ifdef IMAP_STORAGE
13213 /* expunge message - use UID Expunge if supported on IMAP server*/
13214 ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
13215 if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
13216 ast_mutex_lock(&vms.lock);
13217#ifdef HAVE_IMAP_TK2006
13218 if (LEVELUIDPLUS (vms.mailstream)) {
13219 mail_expunge_full(vms.mailstream, NIL, EX_UID);
13220 } else
13221#endif
13222 mail_expunge(vms.mailstream);
13223 ast_mutex_unlock(&vms.lock);
13224 }
13225 /* before we delete the state, we should copy pertinent info
13226 * back to the persistent model */
13227 if (vmu) {
13228 vmstate_delete(&vms);
13229 }
13230#endif
13231 if (vmu)
13232 free_user(vmu);
13233
13234#ifdef IMAP_STORAGE
13235 pthread_setspecific(ts_vmstate.key, NULL);
13236#endif
13237 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d\n", res);
13238}
13239
13240static int vm_exec(struct ast_channel *chan, const char *data)
13241{
13242 int res = 0;
13243 char *tmp;
13244 struct leave_vm_options leave_options;
13245 struct ast_flags flags = { 0 };
13246 char *opts[OPT_ARG_ARRAY_SIZE];
13248 AST_APP_ARG(argv0);
13249 AST_APP_ARG(argv1);
13250 );
13251 SCOPE_ENTER(3, "%s\n", ast_channel_name(chan));
13252
13253 memset(&leave_options, 0, sizeof(leave_options));
13254
13255 if (!ast_strlen_zero(data)) {
13256 tmp = ast_strdupa(data);
13258 if (args.argc == 2) {
13259 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
13260 SCOPE_EXIT_RTN_VALUE(-1, "parse options failed for '%s'\n", args.argv1);
13261 }
13264 int gain;
13265
13266 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
13267 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
13268 } else {
13269 leave_options.record_gain = (signed char) gain;
13270 }
13271 }
13274 leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
13275 }
13276 }
13277 if (ast_test_flag(&flags, OPT_BEEP)) { /* Use custom beep (or none at all) */
13278 leave_options.beeptone = opts[OPT_ARG_BEEP_TONE];
13279 } else { /* Use default beep */
13280 leave_options.beeptone = "beep";
13281 }
13282 } else {
13283 char temp[256];
13284 res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
13285 if (res < 0) {
13286 SCOPE_EXIT_RTN_VALUE(res, "getdata failed. RC: %d", res);
13287 }
13288 if (ast_strlen_zero(temp)) {
13290 }
13291 args.argv0 = ast_strdupa(temp);
13292 }
13293
13294 if (ast_channel_state(chan) != AST_STATE_UP) {
13297 } else {
13298 ast_answer(chan);
13299 }
13300 }
13301
13302 res = SCOPE_CALL_WITH_INT_RESULT(-1, leave_voicemail, chan, args.argv0, &leave_options);
13303 if (res == 't') {
13304 ast_play_and_wait(chan, "vm-goodbye");
13305 res = 0;
13306 }
13307
13308 if (res == OPERATOR_EXIT) {
13309 res = 0;
13310 }
13311
13312 if (res == ERROR_LOCK_PATH) {
13313 ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
13314 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
13315 res = 0;
13316 }
13317
13318 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d", res);
13319}
13320
13321static int add_message_id(struct ast_config *msg_cfg, char *dir, int msg, char *filename, char *id, size_t id_size, struct ast_vm_user *vmu, int folder)
13322{
13323 struct ast_variable *var;
13324 struct ast_category *cat;
13325 generate_msg_id(id);
13326
13327 var = ast_variable_new("msg_id", id, "");
13328 if (!var) {
13329 return -1;
13330 }
13331
13332 cat = ast_category_get(msg_cfg, "message", NULL);
13333 if (!cat) {
13334 ast_log(LOG_ERROR, "Voicemail data file %s/%d.txt has no [message] category?\n", dir, msg);
13336 return -1;
13337 }
13338
13340
13341 if (ast_config_text_file_save(filename, msg_cfg, "app_voicemail")) {
13342 ast_log(LOG_WARNING, "Unable to update %s to have a message ID\n", filename);
13343 return -1;
13344 }
13345
13346 UPDATE_MSG_ID(dir, msg, id, vmu, msg_cfg, folder);
13347 return 0;
13348}
13349
13350static struct ast_vm_user *find_or_create(const char *context, const char *box)
13351{
13352 struct ast_vm_user *vmu;
13353
13354 if (!ast_strlen_zero(box) && box[0] == '*') {
13355 ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character. The '*' character,"
13356 "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
13357 "\n\tpredefined extension 'a'. A mailbox or password beginning with '*' is not valid"
13358 "\n\tand will be ignored.\n", box, context);
13359 return NULL;
13360 }
13361
13362 AST_LIST_TRAVERSE(&users, vmu, list) {
13363 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
13364 if (strcasecmp(vmu->context, context)) {
13365 ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
13366 \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
13367 \n\tconfiguration creates an ambiguity that you likely do not want. Please\
13368 \n\tamend your voicemail.conf file to avoid this situation.\n", box);
13369 }
13370 ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
13371 return NULL;
13372 }
13373 if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
13374 ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
13375 return NULL;
13376 }
13377 }
13378
13379 if (!(vmu = ast_calloc(1, sizeof(*vmu))))
13380 return NULL;
13381
13382 ast_copy_string(vmu->context, context, sizeof(vmu->context));
13383 ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
13384
13386
13387 return vmu;
13388}
13389
13390static int append_mailbox(const char *context, const char *box, const char *data)
13391{
13392 /* Assumes lock is already held */
13393 char *tmp;
13394 char *stringp;
13395 char *s;
13396 struct ast_vm_user *vmu;
13397 char mailbox_full[MAX_VM_MAILBOX_LEN];
13398 int new = 0, old = 0, urgent = 0;
13399 char secretfn[PATH_MAX] = "";
13400
13401 tmp = ast_strdupa(data);
13402
13403 if (!(vmu = find_or_create(context, box)))
13404 return -1;
13405
13406 populate_defaults(vmu);
13407
13408 stringp = tmp;
13409 if ((s = strsep(&stringp, ","))) {
13410 if (!ast_strlen_zero(s) && s[0] == '*') {
13411 ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
13412 "\n\tmust be reset in voicemail.conf.\n", box);
13413 }
13414 /* assign password regardless of validity to prevent NULL password from being assigned */
13415 ast_copy_string(vmu->password, s, sizeof(vmu->password));
13416 }
13417 if (stringp && (s = strsep(&stringp, ","))) {
13418 ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
13419 }
13420 if (stringp && (s = strsep(&stringp, ","))) {
13421 vmu->email = ast_strdup(s);
13422 }
13423 if (stringp && (s = strsep(&stringp, ","))) {
13424 ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
13425 }
13426 if (stringp) {
13427 apply_options(vmu, stringp);
13428 }
13429
13430 switch (vmu->passwordlocation) {
13431 case OPT_PWLOC_SPOOLDIR:
13432 snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
13433 read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
13434 }
13435
13436 snprintf(mailbox_full, MAX_VM_MAILBOX_LEN, "%s%s%s",
13437 box,
13438 ast_strlen_zero(context) ? "" : "@",
13439 context);
13440
13441 inboxcount2(mailbox_full, &urgent, &new, &old);
13442#ifdef IMAP_STORAGE
13443 imap_logout(mailbox_full);
13444#endif
13445 queue_mwi_event(NULL, mailbox_full, urgent, new, old);
13446
13447 return 0;
13448}
13449
13450#ifdef TEST_FRAMEWORK
13451AST_TEST_DEFINE(test_voicemail_vmuser)
13452{
13453 int res = 0;
13454 struct ast_vm_user *vmu;
13455 /* language parameter seems to only be used for display in manager action */
13456 static const char options_string[] = "attach=yes|attachfmt=wav49|"
13457 "serveremail=someguy@digium.com|fromstring=Voicemail System|tz=central|delete=yes|saycid=yes|"
13458 "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|leaveurgent=yes|"
13459 "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
13460 "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
13461 "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
13462 "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
13463 "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
13464 "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
13465#ifdef IMAP_STORAGE
13466 static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
13467 "imapfolder=INBOX|imapvmshareid=6000|imapserver=imapserver|imapport=1234|imapflags=flagged";
13468#endif
13469
13470 switch (cmd) {
13471 case TEST_INIT:
13472 info->name = "vmuser";
13473 info->category = "/apps/app_voicemail/";
13474 info->summary = "Vmuser unit test";
13475 info->description =
13476 "This tests passing all supported parameters to apply_options, the voicemail user config parser";
13477 return AST_TEST_NOT_RUN;
13478 case TEST_EXECUTE:
13479 break;
13480 }
13481
13482 if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
13483 return AST_TEST_NOT_RUN;
13484 }
13485 populate_defaults(vmu);
13487
13488 apply_options(vmu, options_string);
13489
13490 if (!ast_test_flag(vmu, VM_ATTACH)) {
13491 ast_test_status_update(test, "Parse failure for attach option\n");
13492 res = 1;
13493 }
13494 if (strcasecmp(vmu->attachfmt, "wav49")) {
13495 ast_test_status_update(test, "Parse failure for attachfmt option\n");
13496 res = 1;
13497 }
13498 if (strcasecmp(vmu->fromstring, "Voicemail System")) {
13499 ast_test_status_update(test, "Parse failure for fromstring option\n");
13500 res = 1;
13501 }
13502 if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
13503 ast_test_status_update(test, "Parse failure for serveremail option\n");
13504 res = 1;
13505 }
13506 if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
13507 ast_test_status_update(test, "Parse failure for emailsubject option\n");
13508 res = 1;
13509 }
13510 if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
13511 ast_test_status_update(test, "Parse failure for emailbody option\n");
13512 res = 1;
13513 }
13514 if (strcasecmp(vmu->zonetag, "central")) {
13515 ast_test_status_update(test, "Parse failure for tz option\n");
13516 res = 1;
13517 }
13518 if (!ast_test_flag(vmu, VM_DELETE)) {
13519 ast_test_status_update(test, "Parse failure for delete option\n");
13520 res = 1;
13521 }
13522 if (!ast_test_flag(vmu, VM_SAYCID)) {
13523 ast_test_status_update(test, "Parse failure for saycid option\n");
13524 res = 1;
13525 }
13526 if (!ast_test_flag(vmu, VM_SVMAIL)) {
13527 ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
13528 res = 1;
13529 }
13530 if (!ast_test_flag(vmu, VM_REVIEW)) {
13531 ast_test_status_update(test, "Parse failure for review option\n");
13532 res = 1;
13533 }
13534 if (!ast_test_flag(vmu, VM_MARK_URGENT)) {
13535 ast_test_status_update(test, "Parse failure for leaveurgent option\n");
13536 res = 1;
13537 }
13538 if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
13539 ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
13540 res = 1;
13541 }
13542 if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
13543 ast_test_status_update(test, "Parse failure for messagewrap option\n");
13544 res = 1;
13545 }
13546 if (!ast_test_flag(vmu, VM_OPERATOR)) {
13547 ast_test_status_update(test, "Parse failure for operator option\n");
13548 res = 1;
13549 }
13550 if (!ast_test_flag(vmu, VM_ENVELOPE)) {
13551 ast_test_status_update(test, "Parse failure for envelope option\n");
13552 res = 1;
13553 }
13554 if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
13555 ast_test_status_update(test, "Parse failure for moveheard option\n");
13556 res = 1;
13557 }
13558 if (!ast_test_flag(vmu, VM_SAYDURATION)) {
13559 ast_test_status_update(test, "Parse failure for sayduration option\n");
13560 res = 1;
13561 }
13562 if (vmu->saydurationm != 5) {
13563 ast_test_status_update(test, "Parse failure for saydurationm option\n");
13564 res = 1;
13565 }
13566 if (!ast_test_flag(vmu, VM_FORCENAME)) {
13567 ast_test_status_update(test, "Parse failure for forcename option\n");
13568 res = 1;
13569 }
13570 if (!ast_test_flag(vmu, VM_FORCEGREET)) {
13571 ast_test_status_update(test, "Parse failure for forcegreetings option\n");
13572 res = 1;
13573 }
13574 if (strcasecmp(vmu->callback, "somecontext")) {
13575 ast_test_status_update(test, "Parse failure for callbacks option\n");
13576 res = 1;
13577 }
13578 if (strcasecmp(vmu->dialout, "somecontext2")) {
13579 ast_test_status_update(test, "Parse failure for dialout option\n");
13580 res = 1;
13581 }
13582 if (strcasecmp(vmu->exit, "somecontext3")) {
13583 ast_test_status_update(test, "Parse failure for exitcontext option\n");
13584 res = 1;
13585 }
13586 if (vmu->minsecs != 10) {
13587 ast_test_status_update(test, "Parse failure for minsecs option\n");
13588 res = 1;
13589 }
13590 if (vmu->maxsecs != 100) {
13591 ast_test_status_update(test, "Parse failure for maxsecs option\n");
13592 res = 1;
13593 }
13594 if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
13595 ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
13596 res = 1;
13597 }
13598 if (vmu->maxdeletedmsg != 50) {
13599 ast_test_status_update(test, "Parse failure for backupdeleted option\n");
13600 res = 1;
13601 }
13602 if (vmu->volgain != 1.3) {
13603 ast_test_status_update(test, "Parse failure for volgain option\n");
13604 res = 1;
13605 }
13607 ast_test_status_update(test, "Parse failure for passwordlocation option\n");
13608 res = 1;
13609 }
13610#ifdef IMAP_STORAGE
13611 apply_options(vmu, option_string2);
13612
13613 if (strcasecmp(vmu->imapuser, "imapuser")) {
13614 ast_test_status_update(test, "Parse failure for imapuser option\n");
13615 res = 1;
13616 }
13617 if (strcasecmp(vmu->imappassword, "imappasswd")) {
13618 ast_test_status_update(test, "Parse failure for imappasswd option\n");
13619 res = 1;
13620 }
13621 if (strcasecmp(vmu->imapfolder, "INBOX")) {
13622 ast_test_status_update(test, "Parse failure for imapfolder option\n");
13623 res = 1;
13624 }
13625 if (strcasecmp(vmu->imapvmshareid, "6000")) {
13626 ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
13627 res = 1;
13628 }
13629 if (strcasecmp(vmu->imapserver, "imapserver")) {
13630 ast_test_status_update(test, "Parse failure for imapserver option\n");
13631 res = 1;
13632 }
13633 if (strcasecmp(vmu->imapport, "1234")) {
13634 ast_test_status_update(test, "Parse failure for imapport option\n");
13635 res = 1;
13636 }
13637 if (strcasecmp(vmu->imapflags, "flagged")) {
13638 ast_test_status_update(test, "Parse failure for imapflags option\n");
13639 res = 1;
13640 }
13641#endif
13642
13643 free_user(vmu);
13644 force_reload_config(); /* Restore original config */
13645 return res ? AST_TEST_FAIL : AST_TEST_PASS;
13646}
13647#endif
13648
13649static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
13650{
13651 struct ast_vm_user svm;
13652 struct ast_vm_user *vmu = NULL;
13653 char *parse;
13654 char *mailbox;
13655 char *context;
13656 int res = 0;
13657
13659 AST_APP_ARG(mailbox_context);
13660 AST_APP_ARG(attribute);
13661 AST_APP_ARG(folder);
13662 );
13663
13664 buf[0] = '\0';
13665
13666 if (ast_strlen_zero(args)) {
13667 ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
13668 return -1;
13669 }
13670
13671 parse = ast_strdupa(args);
13672 AST_STANDARD_APP_ARGS(arg, parse);
13673
13674 if (ast_strlen_zero(arg.mailbox_context)
13675 || ast_strlen_zero(arg.attribute)
13676 || separate_mailbox(ast_strdupa(arg.mailbox_context), &mailbox, &context)) {
13677 ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
13678 return -1;
13679 }
13680
13681 memset(&svm, 0, sizeof(svm));
13682 vmu = find_user(&svm, context, mailbox);
13683
13684 if (!strncasecmp(arg.attribute, "exists", 5)) {
13685 ast_copy_string(buf, vmu ? "1" : "0", len);
13686 free_user(vmu);
13687 return 0;
13688 }
13689
13690 if (vmu) {
13691 if (!strncasecmp(arg.attribute, "password", 8)) {
13693 } else if (!strncasecmp(arg.attribute, "fullname", 8)) {
13695 } else if (!strncasecmp(arg.attribute, "email", 5)) {
13696 ast_copy_string(buf, vmu->email, len);
13697 } else if (!strncasecmp(arg.attribute, "pager", 5)) {
13698 ast_copy_string(buf, vmu->pager, len);
13699 } else if (!strncasecmp(arg.attribute, "language", 8)) {
13701 } else if (!strncasecmp(arg.attribute, "locale", 6)) {
13702 ast_copy_string(buf, vmu->locale, len);
13703 } else if (!strncasecmp(arg.attribute, "tz", 2)) {
13705 } else if (!strncasecmp(arg.attribute, "count", 5)) {
13706 char *mailbox_id;
13707
13708 mailbox_id = ast_alloca(strlen(mailbox) + strlen(context) + 2);
13709 sprintf(mailbox_id, "%s@%s", mailbox, context);/* Safe */
13710
13711 /* If mbxfolder is empty messagecount will default to INBOX */
13712 res = messagecount(mailbox_id, arg.folder);
13713 if (res < 0) {
13714 ast_log(LOG_ERROR, "Unable to retrieve message count for mailbox %s\n", arg.mailbox_context);
13715 free_user(vmu);
13716 return -1;
13717 }
13718 snprintf(buf, len, "%d", res);
13719 } else {
13720 ast_log(LOG_ERROR, "Unknown attribute '%s' for VM_INFO\n", arg.attribute);
13721 free_user(vmu);
13722 return -1;
13723 }
13724 free_user(vmu);
13725 }
13726
13727 return 0;
13728}
13729
13731 .name = "VM_INFO",
13732 .read = acf_vm_info,
13733};
13734
13735static int vmauthenticate(struct ast_channel *chan, const char *data)
13736{
13737 char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
13738 struct ast_vm_user vmus = {{0}};
13739 char *options = NULL;
13740 int silent = 0, skipuser = 0;
13741 int res = -1;
13742
13743 if (data) {
13744 s = ast_strdupa(data);
13745 user = strsep(&s, ",");
13746 options = strsep(&s, ",");
13747 if (user) {
13748 s = user;
13749 user = strsep(&s, "@");
13750 context = strsep(&s, "");
13751 if (!ast_strlen_zero(user))
13752 skipuser++;
13754 }
13755 }
13756
13757 if (options) {
13758 silent = (strchr(options, 's')) != NULL;
13759 }
13760
13761 if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
13762 pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
13763 pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
13764 ast_play_and_wait(chan, "auth-thankyou");
13765 res = 0;
13766 } else if (mailbox[0] == '*') {
13767 /* user entered '*' */
13768 if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
13769 res = 0; /* prevent hangup */
13770 }
13771 }
13772
13773 return res;
13774}
13775
13776static char *show_users_realtime(int fd, const char *context)
13777{
13778 struct ast_config *cfg;
13779 const char *cat = NULL;
13780
13781 if (!(cfg = ast_load_realtime_multientry("voicemail",
13782 "context", context, SENTINEL))) {
13783 return CLI_FAILURE;
13784 }
13785
13786 ast_cli(fd,
13787 "\n"
13788 "=============================================================\n"
13789 "=== Configured Voicemail Users ==============================\n"
13790 "=============================================================\n"
13791 "===\n");
13792
13793 while ((cat = ast_category_browse(cfg, cat))) {
13794 struct ast_variable *var = NULL;
13795 ast_cli(fd,
13796 "=== Mailbox ...\n"
13797 "===\n");
13798 for (var = ast_variable_browse(cfg, cat); var; var = var->next)
13799 ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
13800 ast_cli(fd,
13801 "===\n"
13802 "=== ---------------------------------------------------------\n"
13803 "===\n");
13804 }
13805
13806 ast_cli(fd,
13807 "=============================================================\n"
13808 "\n");
13809
13810 ast_config_destroy(cfg);
13811
13812 return CLI_SUCCESS;
13813}
13814
13815static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
13816{
13817 int which = 0;
13818 int wordlen;
13819 struct ast_vm_user *vmu;
13820 const char *context = "";
13821 char *ret;
13822
13823 /* 0 - voicemail; 1 - show; 2 - users; 3 - for; 4 - <context> */
13824 if (pos > 4)
13825 return NULL;
13826 wordlen = strlen(word);
13828 AST_LIST_TRAVERSE(&users, vmu, list) {
13829 if (!strncasecmp(word, vmu->context, wordlen)) {
13830 if (context && strcmp(context, vmu->context) && ++which > state) {
13831 ret = ast_strdup(vmu->context);
13833 return ret;
13834 }
13835 /* ignore repeated contexts ? */
13836 context = vmu->context;
13837 }
13838 }
13840 return NULL;
13841}
13842
13843/*! \brief Show a list of voicemail users in the CLI */
13844static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13845{
13846 struct ast_vm_user *vmu;
13847#define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
13848 const char *context = NULL;
13849 int users_counter = 0;
13850
13851 switch (cmd) {
13852 case CLI_INIT:
13853 e->command = "voicemail show users [for]";
13854 e->usage =
13855 "Usage: voicemail show users [for <context>]\n"
13856 " Lists all mailboxes currently set up\n";
13857 return NULL;
13858 case CLI_GENERATE:
13859 return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
13860 }
13861
13862 if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
13863 return CLI_SHOWUSAGE;
13864 if (a->argc == 5) {
13865 if (strcmp(a->argv[3],"for"))
13866 return CLI_SHOWUSAGE;
13867 context = a->argv[4];
13868 }
13869
13870 if (ast_check_realtime("voicemail")) {
13871 if (!context) {
13872 ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
13873 return CLI_SHOWUSAGE;
13874 }
13875 return show_users_realtime(a->fd, context);
13876 }
13877
13879 if (AST_LIST_EMPTY(&users)) {
13880 ast_cli(a->fd, "There are no voicemail users currently defined\n");
13882 return CLI_FAILURE;
13883 }
13884 if (!context) {
13885 ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
13886 } else {
13887 int count = 0;
13888 AST_LIST_TRAVERSE(&users, vmu, list) {
13889 if (!strcmp(context, vmu->context)) {
13890 count++;
13891 break;
13892 }
13893 }
13894 if (count) {
13895 ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
13896 } else {
13897 ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
13899 return CLI_FAILURE;
13900 }
13901 }
13902 AST_LIST_TRAVERSE(&users, vmu, list) {
13903 int newmsgs = 0, oldmsgs = 0;
13904 char count[12], tmp[256] = "";
13905
13906 if (!context || !strcmp(context, vmu->context)) {
13907 snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
13908 inboxcount(tmp, &newmsgs, &oldmsgs);
13909 snprintf(count, sizeof(count), "%d", newmsgs);
13910 ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
13911 users_counter++;
13912 }
13913 }
13915 ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
13916 return CLI_SUCCESS;
13917}
13918
13919/*! \brief Show a list of voicemail zones in the CLI */
13920static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13921{
13922 struct vm_zone *zone;
13923#define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
13924 char *res = CLI_SUCCESS;
13925
13926 switch (cmd) {
13927 case CLI_INIT:
13928 e->command = "voicemail show zones";
13929 e->usage =
13930 "Usage: voicemail show zones\n"
13931 " Lists zone message formats\n";
13932 return NULL;
13933 case CLI_GENERATE:
13934 return NULL;
13935 }
13936
13937 if (a->argc != 3)
13938 return CLI_SHOWUSAGE;
13939
13941 if (!AST_LIST_EMPTY(&zones)) {
13942 ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
13943 AST_LIST_TRAVERSE(&zones, zone, list) {
13944 ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
13945 }
13946 } else {
13947 ast_cli(a->fd, "There are no voicemail zones currently defined\n");
13948 res = CLI_FAILURE;
13949 }
13951
13952 return res;
13953}
13954
13955/*! \brief Show a list of voicemail zones in the CLI */
13956static char *handle_voicemail_show_aliases(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13957{
13958 struct ao2_iterator aliases;
13959 struct alias_mailbox_mapping *mapping;
13960#define ALIASES_OUTPUT_FORMAT "%-32s %-32s\n"
13961 char *res = CLI_SUCCESS;
13962
13963 switch (cmd) {
13964 case CLI_INIT:
13965 e->command = "voicemail show aliases";
13966 e->usage =
13967 "Usage: voicemail show aliases\n"
13968 " Lists mailbox aliases\n";
13969 return NULL;
13970 case CLI_GENERATE:
13971 return NULL;
13972 }
13973
13974 if (a->argc != 3)
13975 return CLI_SHOWUSAGE;
13976
13978 ast_cli(a->fd, "Aliases are not enabled\n");
13979 return res;
13980 }
13981
13982 ast_cli(a->fd, "Aliases context: %s\n", aliasescontext);
13983 ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, "Alias", "Mailbox");
13984
13986 while ((mapping = ao2_iterator_next(&aliases))) {
13987 ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, mapping->alias, mapping->mailbox);
13988 ao2_ref(mapping, -1);
13989 }
13991
13992 return res;
13993}
13994
13995/*! \brief Reload voicemail configuration from the CLI */
13996static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13997{
13998 switch (cmd) {
13999 case CLI_INIT:
14000 e->command = "voicemail reload";
14001 e->usage =
14002 "Usage: voicemail reload\n"
14003 " Reload voicemail configuration\n";
14004 return NULL;
14005 case CLI_GENERATE:
14006 return NULL;
14007 }
14008
14009 if (a->argc != 2)
14010 return CLI_SHOWUSAGE;
14011
14012 ast_cli(a->fd, "Reloading voicemail configuration...\n");
14013 load_config(1);
14014
14015 return CLI_SUCCESS;
14016}
14017
14018static struct ast_cli_entry cli_voicemail[] = {
14019 AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
14020 AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
14021 AST_CLI_DEFINE(handle_voicemail_show_aliases, "List mailbox aliases"),
14022 AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
14023 AST_CLI_DEFINE(handle_voicemail_show_mailbox, "Display a mailbox's content details"),
14024 AST_CLI_DEFINE(handle_voicemail_forward_message, "Forward message to another folder"),
14025 AST_CLI_DEFINE(handle_voicemail_move_message, "Move message to another folder"),
14027};
14028
14029static int poll_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
14030{
14031 int new = 0, old = 0, urgent = 0;
14032
14033 if (!mwi_state) {
14034 /* This should only occur due to allocation failure of a default mwi state object */
14035 return 0;
14036 }
14037
14038 inboxcount2(mwi_state->uniqueid, &urgent, &new, &old);
14039
14040#ifdef IMAP_STORAGE
14041 if (imap_poll_logout) {
14042 imap_logout(mwi_state->uniqueid);
14043 }
14044#endif
14045
14046 if (urgent != mwi_state->urgent_msgs || new != mwi_state->new_msgs || old != mwi_state->old_msgs) {
14047 queue_mwi_event(NULL, mwi_state->uniqueid, urgent, new, old);
14048 run_externnotify(NULL, mwi_state->uniqueid, NULL);
14049 }
14050
14051 return 0;
14052}
14053
14054static void *mb_poll_thread(void *data)
14055{
14056 while (poll_thread_run) {
14057 struct timespec ts = { 0, };
14058 struct timeval wait;
14059
14061
14062 if (!poll_thread_run) {
14063 break;
14064 }
14065
14067 ts.tv_sec = wait.tv_sec;
14068 ts.tv_nsec = wait.tv_usec * 1000;
14069
14073 }
14074
14075 return NULL;
14076}
14077
14078#ifdef IMAP_STORAGE
14079static void imap_logout(const char *mailbox_id)
14080{
14081 char *context;
14082 char *mailbox;
14083 struct ast_vm_user vmus;
14084 RAII_VAR(struct ast_vm_user *, vmu, NULL, free_user);
14085 struct vm_state *vms = NULL;
14086
14087 if (ast_strlen_zero(mailbox_id)
14088 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
14089 return;
14090 }
14091
14092 memset(&vmus, 0, sizeof(vmus));
14093
14094 if (!(vmu = find_user(&vmus, context, mailbox)) || vmu->imapuser[0] == '\0') {
14095 return;
14096 }
14097
14098 vms = get_vm_state_by_imapuser(vmu->imapuser, 0);
14099 if (!vms) {
14100 vms = get_vm_state_by_mailbox(mailbox, context, 0);
14101 }
14102 if (!vms) {
14103 return;
14104 }
14105
14106 ast_mutex_lock(&vms->lock);
14107 vms->mailstream = mail_close(vms->mailstream);
14108 ast_mutex_unlock(&vms->lock);
14109
14110 vmstate_delete(vms);
14111}
14112
14113static int imap_close_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
14114{
14115 if (mwi_state && !ast_strlen_zero(mwi_state->uniqueid)) {
14116 imap_logout(mwi_state->uniqueid);
14117 }
14118
14119 return 0;
14120}
14121
14122#endif
14123
14124static int mwi_handle_unsubscribe2(void *data)
14125{
14126 struct ast_mwi_state *mwi_state = data;
14127
14128 /*
14129 * Go ahead and clear the implicit MWI publisher here to avoid a leak. If a backing
14130 * configuration is available it'll re-initialize (reset the cached state) on its
14131 * next publish.
14132 */
14134
14135#ifdef IMAP_STORAGE
14136 imap_close_subscribed_mailbox(mwi_state, NULL);
14137#endif
14138
14139 ao2_ref(mwi_state, -1);
14140 return 0;
14141}
14142
14143static void mwi_handle_unsubscribe(const char *id, struct ast_mwi_subscriber *sub)
14144{
14145 void *data = ast_mwi_subscriber_data(sub);
14146
14147 /* Don't bump data's reference. We'll just use the one returned above */
14149 /* A reference was returned for data when retrieving, so remove it on error */
14150 ao2_ref(data, -1);
14151 }
14152}
14153
14154static int mwi_handle_subscribe2(void *data)
14155{
14157 ao2_ref(data, -1);
14158 return 0;
14159}
14160
14161static void mwi_handle_subscribe(const char *id, struct ast_mwi_subscriber *sub)
14162{
14163 void *data = ast_mwi_subscriber_data(sub);
14164
14165 /* Don't bump data's reference. We'll just use the one returned above */
14167 /* A reference was returned for data when retrieving, so remove it on error */
14168 ao2_ref(data, -1);
14169 }
14170}
14171
14174 .on_unsubscribe = mwi_handle_unsubscribe,
14175};
14176
14177static void start_poll_thread(void)
14178{
14179 int errcode;
14181
14182 poll_thread_run = 1;
14183
14184 if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
14185 ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
14186 }
14187}
14188
14189static void stop_poll_thread(void)
14190{
14191 poll_thread_run = 0;
14192
14196
14197 pthread_join(poll_thread, NULL);
14199
14201}
14202
14203/*!
14204 * \brief Append vmu info string into given astman with event_name.
14205 * \return 0 failed. 1 otherwise.
14206*/
14208 struct mansession *s,
14209 struct ast_vm_user *vmu,
14210 const char* event_name,
14211 const char* actionid
14212 )
14213{
14214 int new;
14215 int old;
14216 char *mailbox;
14217 int ret;
14218
14219 if((s == NULL) || (vmu == NULL) || (event_name == NULL) || (actionid == NULL)) {
14220 ast_log(LOG_ERROR, "Wrong input parameter.");
14221 return 0;
14222 }
14223
14224 /* create mailbox string */
14225 if (!ast_strlen_zero(vmu->context)) {
14226 ret = ast_asprintf(&mailbox, "%s@%s", vmu->mailbox, vmu->context);
14227 } else {
14228 ret = ast_asprintf(&mailbox, "%s", vmu->mailbox);
14229 }
14230 if (ret == -1) {
14231 ast_log(LOG_ERROR, "Could not create mailbox string. err[%s]\n", strerror(errno));
14232 return 0;
14233 }
14234
14235 /* get mailbox count */
14236 ret = inboxcount(mailbox, &new, &old);
14238 if (ret == -1) {
14239 ast_log(LOG_ERROR, "Could not get mailbox count. user[%s], context[%s]\n",
14240 vmu->mailbox ?: "", vmu->context ?: "");
14241 return 0;
14242 }
14243
14244 astman_append(s,
14245 "Event: %s\r\n"
14246 "%s"
14247 "VMContext: %s\r\n"
14248 "VoiceMailbox: %s\r\n"
14249 "Fullname: %s\r\n"
14250 "Email: %s\r\n"
14251 "Pager: %s\r\n"
14252 "ServerEmail: %s\r\n"
14253 "FromString: %s\r\n"
14254 "MailCommand: %s\r\n"
14255 "Language: %s\r\n"
14256 "TimeZone: %s\r\n"
14257 "Callback: %s\r\n"
14258 "Dialout: %s\r\n"
14259 "UniqueID: %s\r\n"
14260 "ExitContext: %s\r\n"
14261 "SayDurationMinimum: %d\r\n"
14262 "SayEnvelope: %s\r\n"
14263 "SayCID: %s\r\n"
14264 "AttachMessage: %s\r\n"
14265 "AttachmentFormat: %s\r\n"
14266 "DeleteMessage: %s\r\n"
14267 "VolumeGain: %.2f\r\n"
14268 "CanReview: %s\r\n"
14269 "CanMarkUrgent: %s\r\n"
14270 "CallOperator: %s\r\n"
14271 "MaxMessageCount: %d\r\n"
14272 "MaxMessageLength: %d\r\n"
14273 "NewMessageCount: %d\r\n"
14274 "OldMessageCount: %d\r\n"
14275#ifdef IMAP_STORAGE
14276 "IMAPUser: %s\r\n"
14277 "IMAPServer: %s\r\n"
14278 "IMAPPort: %s\r\n"
14279 "IMAPFlags: %s\r\n"
14280#endif
14281 "\r\n",
14282
14283 event_name,
14284 actionid,
14285 vmu->context,
14286 vmu->mailbox,
14287 vmu->fullname,
14288 vmu->email,
14289 vmu->pager,
14292 mailcmd,
14293 vmu->language,
14294 vmu->zonetag,
14295 vmu->callback,
14296 vmu->dialout,
14297 vmu->uniqueid,
14298 vmu->exit,
14299 vmu->saydurationm,
14300 ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
14301 ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
14302 ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
14303 vmu->attachfmt,
14304 ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
14305 vmu->volgain,
14306 ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
14307 ast_test_flag(vmu, VM_MARK_URGENT) ? "Yes" : "No",
14308 ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
14309 vmu->maxmsg,
14310 vmu->maxsecs,
14311 new,
14312 old
14313#ifdef IMAP_STORAGE
14314 ,
14315 vmu->imapuser,
14316 vmu->imapserver,
14317 vmu->imapport,
14318 vmu->imapflags
14319#endif
14320 );
14321
14322 return 1;
14323
14324}
14325
14326
14327/*!
14328 * \brief Append vmbox info string into given astman with event_name.
14329 * \return 0 if unable to append details, 1 otherwise.
14330*/
14332 struct mansession *s,
14333 const struct message *m,
14334 struct ast_vm_user *vmu,
14335 const char* event_name,
14336 const char* actionid)
14337{
14338 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
14339 struct ast_vm_msg_snapshot *msg;
14340 int nummessages = 0;
14341 int i;
14342
14343 /* Take a snapshot of the mailbox */
14344 mailbox_snapshot = ast_vm_mailbox_snapshot_create(vmu->mailbox, vmu->context, NULL, 0, AST_VM_SNAPSHOT_SORT_BY_ID, 0);
14345 if (!mailbox_snapshot) {
14346 ast_log(LOG_ERROR, "Could not append voicemail box info for box %s@%s.",
14347 vmu->mailbox, vmu->context);
14348 return 0;
14349 }
14350
14351 astman_send_listack(s, m, "Voicemail box detail will follow", "start");
14352 /* walk through each folder's contents and append info for each message */
14353 for (i = 0; i < mailbox_snapshot->folders; i++) {
14354 AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
14355 astman_append(s,
14356 "Event: %s\r\n"
14357 "%s"
14358 "Folder: %s\r\n"
14359 "CallerID: %s\r\n"
14360 "Date: %s\r\n"
14361 "Duration: %s\r\n"
14362 "Flag: %s\r\n"
14363 "ID: %s\r\n"
14364 "\r\n",
14365 event_name,
14366 actionid,
14367 msg->folder_name,
14368 msg->callerid,
14369 msg->origdate,
14370 msg->duration,
14371 msg->flag,
14372 msg->msg_id
14373 );
14374 nummessages++;
14375 }
14376 }
14377
14378 /* done, destroy. */
14379 mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
14380 astman_send_list_complete_start(s, m, "VoicemailBoxDetailComplete", nummessages);
14382
14383 return 1;
14384}
14385
14386static int manager_match_mailbox(struct ast_mwi_state *mwi_state, void *data)
14387{
14388 const char *context = astman_get_header(data, "Context");
14389 const char *mailbox = astman_get_header(data, "Mailbox");
14390 const char *at;
14391
14392 if (!ast_strlen_zero(mwi_state->uniqueid)) {
14393 if (
14394 /* First case: everything matches */
14396 /* Second case: match the mailbox only */
14398 (at = strchr(mwi_state->uniqueid, '@')) &&
14399 strncmp(mailbox, mwi_state->uniqueid, at - mwi_state->uniqueid) == 0) ||
14400 /* Third case: match the context only */
14402 (at = strchr(mwi_state->uniqueid, '@')) &&
14403 strcmp(context, at + 1) == 0) ||
14404 /* Final case: match an exact specified mailbox */
14406 (at = strchr(mwi_state->uniqueid, '@')) &&
14407 strncmp(mailbox, mwi_state->uniqueid, at - mwi_state->uniqueid) == 0 &&
14408 strcmp(context, at + 1) == 0)
14409 ) {
14410 poll_subscribed_mailbox(mwi_state, NULL);
14411 }
14412 }
14413
14414 return 0;
14415}
14416
14417static int manager_voicemail_refresh(struct mansession *s, const struct message *m)
14418{
14420 astman_send_ack(s, m, "Refresh sent");
14421 return RESULT_SUCCESS;
14422}
14423
14424static int manager_status_voicemail_user(struct mansession *s, const struct message *m)
14425{
14426 struct ast_vm_user *vmu = NULL;
14427 const char *id = astman_get_header(m, "ActionID");
14428 char actionid[128];
14429 struct ast_vm_user svm;
14430 int ret;
14431
14432 const char *context = astman_get_header(m, "Context");
14433 const char *mailbox = astman_get_header(m, "Mailbox");
14434
14436 astman_send_error(s, m, "Need 'Context' and 'Mailbox' parameters.");
14437 return RESULT_SUCCESS;
14438 }
14439
14440 actionid[0] = '\0';
14441 if (!ast_strlen_zero(id)) {
14442 snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14443 }
14444
14445 /* find user */
14446 memset(&svm, 0, sizeof(svm));
14447 vmu = find_user(&svm, context, mailbox);
14448 if (!vmu) {
14449 /* could not find it */
14450 astman_send_ack(s, m, "There is no voicemail user of the given info.");
14451 return RESULT_SUCCESS;
14452 }
14453
14454 astman_send_listack(s, m, "Voicemail user detail will follow", "start");
14455
14456 /* append vmu info event */
14457 ret = append_vmu_info_astman(s, vmu, "VoicemailUserDetail", actionid);
14458 free_user(vmu);
14459 if(ret == 0) {
14460 ast_log(LOG_ERROR, "Could not append voicemail user info.");
14461 }
14462
14463 astman_send_list_complete_start(s, m, "VoicemailUserDetailComplete", 1);
14465
14466 return RESULT_SUCCESS;
14467}
14468
14469/*! \brief Manager list voicemail users command */
14470static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
14471{
14472 struct ast_vm_user *vmu = NULL;
14473 const char *id = astman_get_header(m, "ActionID");
14474 char actionid[128];
14475 int num_users = 0;
14476 int ret;
14477
14478 actionid[0] = '\0';
14479 if (!ast_strlen_zero(id)) {
14480 snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14481 }
14482
14484
14485 if (AST_LIST_EMPTY(&users)) {
14486 astman_send_ack(s, m, "There are no voicemail users currently defined.");
14488 return RESULT_SUCCESS;
14489 }
14490
14491 astman_send_listack(s, m, "Voicemail user list will follow", "start");
14492
14493 AST_LIST_TRAVERSE(&users, vmu, list) {
14494 /* append vmu info event */
14495 ret = append_vmu_info_astman(s, vmu, "VoicemailUserEntry", actionid);
14496 if(ret == 0) {
14497 ast_log(LOG_ERROR, "Could not append voicemail user info.");
14498 continue;
14499 }
14500 ++num_users;
14501 }
14502
14503 astman_send_list_complete_start(s, m, "VoicemailUserEntryComplete", num_users);
14505
14507
14508 return RESULT_SUCCESS;
14509}
14510
14511static int manager_get_mailbox_summary(struct mansession *s, const struct message *m)
14512{
14513 struct ast_vm_user *vmu = NULL;
14514 const char *id = astman_get_header(m, "ActionID");
14515 char actionid[128];
14516 struct ast_vm_user svm;
14517
14518 const char *context = astman_get_header(m, "Context");
14519 const char *mailbox = astman_get_header(m, "Mailbox");
14520
14522 astman_send_error(s, m, "Need 'Context' and 'Mailbox' parameters.");
14523 return 0;
14524 }
14525
14526 actionid[0] = '\0';
14527 if (!ast_strlen_zero(id)) {
14528 snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14529 }
14530
14531 /* find user */
14532 memset(&svm, 0, sizeof(svm));
14533 vmu = find_user(&svm, context, mailbox);
14534 if (!vmu) {
14535 /* could not find it */
14536 astman_send_ack(s, m, "There is no voicemail user matching the given user.");
14537 return 0;
14538 }
14539
14540 /* Append the mailbox details */
14541 if (!append_vmbox_info_astman(s, m, vmu, "VoicemailBoxDetail", actionid)) {
14542 astman_send_error(s, m, "Unable to get mailbox info for the given user.");
14543 }
14544
14545 free_user(vmu);
14546 return 0;
14547}
14548
14549static int manager_voicemail_move(struct mansession *s, const struct message *m)
14550{
14551 const char *mailbox = astman_get_header(m, "Mailbox");
14552 const char *context = astman_get_header(m, "Context");
14553 const char *from_folder = astman_get_header(m, "Folder");
14554 const char *id[] = { astman_get_header(m, "ID") };
14555 const char *to_folder = astman_get_header(m, "ToFolder");
14556
14557 if (ast_strlen_zero(mailbox)) {
14558 astman_send_error(s, m, "Mailbox not specified, required");
14559 return 0;
14560 }
14561 if (ast_strlen_zero(context)) {
14562 astman_send_error(s, m, "Context not specified, required");
14563 return 0;
14564 }
14565 if (ast_strlen_zero(from_folder)) {
14566 astman_send_error(s, m, "Folder not specified, required");
14567 return 0;
14568 }
14569 if (ast_strlen_zero(id[0])) {
14570 astman_send_error(s, m, "ID not specified, required");
14571 return 0;
14572 }
14573 if (ast_strlen_zero(to_folder)) {
14574 astman_send_error(s, m, "ToFolder not specified, required");
14575 return 0;
14576 }
14577
14578 if (vm_msg_move(mailbox, context, 1, from_folder, id, to_folder)) {
14579 astman_send_ack(s, m, "Message move failed\n");
14580 } else {
14581 astman_send_ack(s, m, "Message move successful\n");
14582 }
14583
14584 return 0;
14585}
14586
14587static int manager_voicemail_remove(struct mansession *s, const struct message *m)
14588{
14589 const char *mailbox = astman_get_header(m, "Mailbox");
14590 const char *context = astman_get_header(m, "Context");
14591 const char *folder = astman_get_header(m, "Folder");
14592 const char *id[] = { astman_get_header(m, "ID") };
14593
14594 if (ast_strlen_zero(mailbox)) {
14595 astman_send_error(s, m, "Mailbox not specified, required");
14596 return 0;
14597 }
14598 if (ast_strlen_zero(context)) {
14599 astman_send_error(s, m, "Context not specified, required");
14600 return 0;
14601 }
14602 if (ast_strlen_zero(folder)) {
14603 astman_send_error(s, m, "Folder not specified, required");
14604 return 0;
14605 }
14606 if (ast_strlen_zero(id[0])) {
14607 astman_send_error(s, m, "ID not specified, required");
14608 return 0;
14609 }
14610
14611 if (vm_msg_remove(mailbox, context, 1, folder, id)) {
14612 astman_send_ack(s, m, "Message remove failed\n");
14613 } else {
14614 astman_send_ack(s, m, "Message remove successful\n");
14615 }
14616
14617 return 0;
14618}
14619
14620static int manager_voicemail_forward(struct mansession *s, const struct message *m)
14621{
14622 const char *from_mailbox = astman_get_header(m, "Mailbox");
14623 const char *from_context = astman_get_header(m, "Context");
14624 const char *from_folder = astman_get_header(m, "Folder");
14625 const char *id[] = { astman_get_header(m, "ID") };
14626 const char *to_mailbox = astman_get_header(m, "ToMailbox");
14627 const char *to_context = astman_get_header(m, "ToContext");
14628 const char *to_folder = astman_get_header(m, "ToFolder");
14629
14631 astman_send_error(s, m, "Mailbox not specified, required");
14632 return 0;
14633 }
14634 if (ast_strlen_zero(from_context)) {
14635 astman_send_error(s, m, "Context not specified, required");
14636 return 0;
14637 }
14638 if (ast_strlen_zero(from_folder)) {
14639 astman_send_error(s, m, "Folder not specified, required");
14640 return 0;
14641 }
14642 if (ast_strlen_zero(id[0])) {
14643 astman_send_error(s, m, "ID not specified, required");
14644 return 0;
14645 }
14646 if (ast_strlen_zero(to_mailbox)) {
14647 astman_send_error(s, m, "ToMailbox not specified, required");
14648 return 0;
14649 }
14650 if (ast_strlen_zero(to_context)) {
14651 astman_send_error(s, m, "ToContext not specified, required");
14652 return 0;
14653 }
14654 if (ast_strlen_zero(to_folder)) {
14655 astman_send_error(s, m, "ToFolder not specified, required");
14656 return 0;
14657 }
14658
14659 if (vm_msg_forward(from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder, 1, id, 0)) {
14660 astman_send_ack(s, m, "Message forward failed\n");
14661 } else {
14662 astman_send_ack(s, m, "Message forward successful\n");
14663 }
14664
14665 return 0;
14666}
14667
14668/*! \brief Free the users structure. */
14669static void free_vm_users(void)
14670{
14671 struct ast_vm_user *current;
14673 while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
14676 }
14678}
14679
14680/*! \brief Free the zones structure. */
14681static void free_vm_zones(void)
14682{
14683 struct vm_zone *zcur;
14685 while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
14686 free_zone(zcur);
14688}
14689
14690static const char *substitute_escapes(const char *value)
14691{
14692 char *current;
14693
14694 /* Add 16 for fudge factor */
14695 struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
14696
14698
14699 /* Substitute strings \r, \n, and \t into the appropriate characters */
14700 for (current = (char *) value; *current; current++) {
14701 if (*current == '\\') {
14702 current++;
14703 if (!*current) {
14704 ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
14705 break;
14706 }
14707 switch (*current) {
14708 case '\\':
14709 ast_str_append(&str, 0, "\\");
14710 break;
14711 case 'r':
14712 ast_str_append(&str, 0, "\r");
14713 break;
14714 case 'n':
14715#ifdef IMAP_STORAGE
14716 if (!str->used || str->str[str->used - 1] != '\r') {
14717 ast_str_append(&str, 0, "\r");
14718 }
14719#endif
14720 ast_str_append(&str, 0, "\n");
14721 break;
14722 case 't':
14723 ast_str_append(&str, 0, "\t");
14724 break;
14725 default:
14726 ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
14727 break;
14728 }
14729 } else {
14730 ast_str_append(&str, 0, "%c", *current);
14731 }
14732 }
14733
14734 return ast_str_buffer(str);
14735}
14736
14737static int load_config_force(int reload, int force)
14738{
14739 struct ast_config *cfg, *ucfg;
14740 struct ast_flags config_flags = { reload && !force ? CONFIG_FLAG_FILEUNCHANGED : 0 };
14741 int res;
14742
14743 ast_unload_realtime("voicemail");
14744 ast_unload_realtime("voicemail_data");
14745
14746 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
14747 if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
14748 return 0;
14749 } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
14750 ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
14751 ucfg = NULL;
14752 }
14754 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
14755 ast_config_destroy(ucfg);
14756 ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
14757 return 0;
14758 }
14759 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
14760 ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
14761 return 0;
14762 } else {
14764 if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
14765 ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
14766 ucfg = NULL;
14767 }
14768 }
14769
14770 res = actual_load_config(reload, cfg, ucfg);
14771
14772 ast_config_destroy(cfg);
14773 ast_config_destroy(ucfg);
14774
14775 return res;
14776}
14777
14778static int load_config(int reload)
14779{
14780 return load_config_force(reload, 0);
14781}
14782
14783#ifdef TEST_FRAMEWORK
14784static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
14785{
14786 ast_unload_realtime("voicemail");
14787 ast_unload_realtime("voicemail_data");
14788 return actual_load_config(reload, cfg, ucfg);
14789}
14790#endif
14791
14792static struct alias_mailbox_mapping *alias_mailbox_mapping_create(const char *alias, const char *mailbox)
14793{
14794 struct alias_mailbox_mapping *mapping;
14795 size_t from_len = strlen(alias) + 1;
14796 size_t to_len = strlen(mailbox) + 1;
14797
14798 mapping = ao2_alloc(sizeof(*mapping) + from_len + to_len, NULL);
14799 if (!mapping) {
14800 return NULL;
14801 }
14802 mapping->alias = mapping->buf;
14803 mapping->mailbox = mapping->buf + from_len;
14804 ast_copy_string(mapping->alias, alias, from_len); /* Safe */
14805 ast_copy_string(mapping->mailbox, mailbox, to_len); /* Safe */
14806
14807 return mapping;
14808}
14809
14810static void load_aliases(struct ast_config *cfg)
14811{
14812 struct ast_variable *var;
14813
14815 return;
14816 }
14818 while (var) {
14819 struct alias_mailbox_mapping *mapping = alias_mailbox_mapping_create(var->name, var->value);
14820 if (mapping) {
14823 ao2_ref(mapping, -1);
14824 }
14825 var = var->next;
14826 }
14827}
14828
14829static void load_zonemessages(struct ast_config *cfg)
14830{
14831 struct ast_variable *var;
14832
14833 var = ast_variable_browse(cfg, "zonemessages");
14834 while (var) {
14835 if (var->value) {
14836 struct vm_zone *z;
14837 char *msg_format, *tzone;
14838 char storage[strlen(var->value) + 1];
14839
14840 z = ast_malloc(sizeof(*z));
14841 if (!z) {
14842 return;
14843 }
14844
14845 strcpy(storage, var->value); /* safe */
14846 msg_format = storage;
14847 tzone = strsep(&msg_format, "|,");
14848 if (msg_format) {
14849 ast_copy_string(z->name, var->name, sizeof(z->name));
14850 ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
14855 } else {
14856 ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
14857 ast_free(z);
14858 }
14859 }
14860 var = var->next;
14861 }
14862}
14863
14864static void load_users(struct ast_config *cfg)
14865{
14866 struct ast_variable *var;
14867 char *cat = NULL;
14868
14869 while ((cat = ast_category_browse(cfg, cat))) {
14870 if (strcasecmp(cat, "general") == 0
14871 || strcasecmp(cat, aliasescontext) == 0
14872 || strcasecmp(cat, "zonemessages") == 0) {
14873 continue;
14874 }
14875
14876 var = ast_variable_browse(cfg, cat);
14877 while (var) {
14878 append_mailbox(cat, var->name, var->value);
14879 var = var->next;
14880 }
14881 }
14882}
14883
14884static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
14885{
14886 struct ast_vm_user *current;
14887 char *cat;
14888 const char *val;
14889 char *q, *stringp, *tmp;
14890 int x;
14891 unsigned int tmpadsi[4];
14892 char secretfn[PATH_MAX] = "";
14893 long tps_queue_low;
14894 long tps_queue_high;
14895
14896#ifdef IMAP_STORAGE
14897 ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
14898#endif
14899 /* set audio control prompts */
14905
14906#ifdef IMAP_STORAGE
14907 ast_mwi_state_callback_all(imap_close_subscribed_mailbox, NULL);
14908#endif
14909
14910 /* Free all the users structure */
14911 free_vm_users();
14912
14913 /* Free all the zones structure */
14914 free_vm_zones();
14915
14916 /* Remove all aliases */
14919
14921
14922 memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
14923 memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
14924
14925 if (cfg) {
14926 /* General settings */
14927
14928 if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
14929 val = "default";
14931
14932 aliasescontext[0] = '\0';
14933 val = ast_variable_retrieve(cfg, "general", "aliasescontext");
14935
14936 /* Attach voice message to mail message ? */
14937 if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
14938 val = "yes";
14940
14941 if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
14942 val = "no";
14944
14945 volgain = 0.0;
14946 if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
14947 sscanf(val, "%30lf", &volgain);
14948
14949#ifdef ODBC_STORAGE
14950 strcpy(odbc_database, "asterisk");
14951 if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
14952 ast_copy_string(odbc_database, val, sizeof(odbc_database));
14953 }
14954
14955 strcpy(odbc_table, "voicemessages");
14956 if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
14957 ast_copy_string(odbc_table, val, sizeof(odbc_table));
14958 }
14959 odbc_table_len = strlen(odbc_table);
14960
14962 if (!(val = ast_variable_retrieve(cfg, "general", "odbc_audio_on_disk")))
14963 val = "no";
14965
14966#endif
14967 /* Mail command */
14968 strcpy(mailcmd, SENDMAIL);
14969 if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
14970 ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
14971
14972 maxsilence = 0;
14973 if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
14974 maxsilence = atoi(val);
14975 if (maxsilence > 0)
14976 maxsilence *= 1000;
14977 }
14978
14979 if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
14980 maxmsg = MAXMSG;
14981 } else {
14982 maxmsg = atoi(val);
14983 if (maxmsg < 0) {
14984 ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
14985 maxmsg = MAXMSG;
14986 } else if (maxmsg > MAXMSGLIMIT) {
14987 ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
14989 }
14990 }
14991
14992 if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
14993 maxdeletedmsg = 0;
14994 } else {
14995 if (sscanf(val, "%30d", &x) == 1)
14996 maxdeletedmsg = x;
14997 else if (ast_true(val))
14999 else
15000 maxdeletedmsg = 0;
15001
15002 if (maxdeletedmsg < 0) {
15003 ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
15005 } else if (maxdeletedmsg > MAXMSGLIMIT) {
15006 ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
15008 }
15009 }
15010
15011 /* Load date format config for voicemail mail */
15012 if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
15014 }
15015
15016 /* Load date format config for voicemail pager mail */
15017 if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
15019 }
15020
15021 /* External password changing command */
15022 if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
15025 } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
15028 }
15029
15030 /* External password validation command */
15031 if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
15033 ast_debug(1, "found externpasscheck: %s\n", ext_pass_check_cmd);
15034 }
15035
15036#ifdef IMAP_STORAGE
15037 /* IMAP server address */
15038 if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
15039 ast_copy_string(imapserver, val, sizeof(imapserver));
15040 } else {
15041 ast_copy_string(imapserver, "localhost", sizeof(imapserver));
15042 }
15043 /* IMAP server port */
15044 if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
15045 ast_copy_string(imapport, val, sizeof(imapport));
15046 } else {
15047 ast_copy_string(imapport, "143", sizeof(imapport));
15048 }
15049 /* IMAP server flags */
15050 if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
15051 ast_copy_string(imapflags, val, sizeof(imapflags));
15052 }
15053 /* IMAP server master username */
15054 if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
15055 ast_copy_string(authuser, val, sizeof(authuser));
15056 }
15057 /* IMAP server master password */
15058 if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
15059 ast_copy_string(authpassword, val, sizeof(authpassword));
15060 }
15061 /* Expunge on exit */
15062 if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
15063 if (ast_false(val))
15064 expungeonhangup = 0;
15065 else
15066 expungeonhangup = 1;
15067 } else {
15068 expungeonhangup = 1;
15069 }
15070 /* IMAP voicemail folder */
15071 if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
15072 ast_copy_string(imapfolder, val, sizeof(imapfolder));
15073 } else {
15074 ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
15075 }
15076 if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
15077 ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
15078 }
15079 if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
15080 imapgreetings = ast_true(val);
15081 } else {
15082 imapgreetings = 0;
15083 }
15084 if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
15085 ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
15086 } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
15087 /* Also support greetingsfolder as documented in voicemail.conf.sample */
15088 ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
15089 } else {
15090 ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
15091 }
15092 if ((val = ast_variable_retrieve(cfg, "general", "imap_poll_logout"))) {
15093 imap_poll_logout = ast_true(val);
15094 } else {
15095 imap_poll_logout = 0;
15096 }
15097
15098 /* There is some very unorthodox casting done here. This is due
15099 * to the way c-client handles the argument passed in. It expects a
15100 * void pointer and casts the pointer directly to a long without
15101 * first dereferencing it. */
15102 if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
15103 mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
15104 } else {
15105 mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
15106 }
15107
15108 if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
15109 mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
15110 } else {
15111 mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
15112 }
15113
15114 if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
15115 mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
15116 } else {
15117 mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
15118 }
15119
15120 if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
15121 mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
15122 } else {
15123 mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
15124 }
15125
15126 /* Increment configuration version */
15127 imapversion++;
15128#endif
15129 /* External voicemail notify application */
15130 if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
15132 ast_debug(1, "found externnotify: %s\n", externnotify);
15133 } else {
15134 externnotify[0] = '\0';
15135 }
15136
15137 /* SMDI voicemail notification */
15138 if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
15139 ast_debug(1, "Enabled SMDI voicemail notification\n");
15140 if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
15142 } else {
15143 ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
15144 smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
15145 }
15146 if (!smdi_iface) {
15147 ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
15148 }
15149 }
15150
15151 /* Silence treshold */
15153 if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
15154 silencethreshold = atoi(val);
15155
15156 if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
15159
15160 vmmaxsecs = 0;
15161 if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
15162 if (sscanf(val, "%30d", &x) == 1) {
15163 vmmaxsecs = x;
15164 } else {
15165 ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
15166 }
15167 } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
15168 static int maxmessage_deprecate = 0;
15169 if (maxmessage_deprecate == 0) {
15170 maxmessage_deprecate = 1;
15171 ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
15172 }
15173 if (sscanf(val, "%30d", &x) == 1) {
15174 vmmaxsecs = x;
15175 } else {
15176 ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
15177 }
15178 }
15179
15180 vmminsecs = 0;
15181 if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
15182 if (sscanf(val, "%30d", &x) == 1) {
15183 vmminsecs = x;
15184 if (maxsilence / 1000 >= vmminsecs) {
15185 ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
15186 }
15187 } else {
15188 ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
15189 }
15190 } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
15191 static int maxmessage_deprecate = 0;
15192 if (maxmessage_deprecate == 0) {
15193 maxmessage_deprecate = 1;
15194 ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
15195 }
15196 if (sscanf(val, "%30d", &x) == 1) {
15197 vmminsecs = x;
15198 if (maxsilence / 1000 >= vmminsecs) {
15199 ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
15200 }
15201 } else {
15202 ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
15203 }
15204 }
15205
15206 val = ast_variable_retrieve(cfg, "general", "format");
15207 if (!val) {
15208 val = "wav";
15209 } else {
15210 tmp = ast_strdupa(val);
15212 if (!val) {
15213 ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
15214 val = "wav";
15215 }
15216 }
15217 ast_copy_string(vmfmts, val, sizeof(vmfmts));
15218
15219 skipms = 3000;
15220 if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
15221 if (sscanf(val, "%30d", &x) == 1) {
15222 maxgreet = x;
15223 } else {
15224 ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
15225 }
15226 }
15227
15228 if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
15229 if (sscanf(val, "%30d", &x) == 1) {
15230 skipms = x;
15231 } else {
15232 ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
15233 }
15234 }
15235
15236 maxlogins = 3;
15237 if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
15238 if (sscanf(val, "%30d", &x) == 1) {
15239 maxlogins = x;
15240 } else {
15241 ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
15242 }
15243 }
15244
15246 if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
15247 if (sscanf(val, "%30d", &x) == 1) {
15248 minpassword = x;
15249 } else {
15250 ast_log(AST_LOG_WARNING, "Invalid minimum password length. Default to %d\n", minpassword);
15251 }
15252 }
15253
15254 /* Force new user to record name ? */
15255 if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
15256 val = "no";
15258
15259 /* Force new user to record greetings ? */
15260 if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
15261 val = "no";
15263
15264 if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
15265 ast_debug(1, "VM_CID Internal context string: %s\n", val);
15266 stringp = ast_strdupa(val);
15267 for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
15268 if (!ast_strlen_zero(stringp)) {
15269 q = strsep(&stringp, ",");
15270 while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
15271 q++;
15273 ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
15274 } else {
15275 cidinternalcontexts[x][0] = '\0';
15276 }
15277 }
15278 }
15279 if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
15280 ast_debug(1, "VM Review Option disabled globally\n");
15281 val = "no";
15282 }
15284
15285 if (!(val = ast_variable_retrieve(cfg, "general", "leaveurgent"))){
15286 val = "yes";
15287 } else if (ast_false(val)) {
15288 ast_debug(1, "VM leave urgent messages disabled globally\n");
15289 val = "no";
15290 }
15292
15293 /* Temporary greeting reminder */
15294 if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
15295 ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
15296 val = "no";
15297 } else {
15298 ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
15299 }
15301 if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
15302 ast_debug(1, "VM next message wrap disabled globally\n");
15303 val = "no";
15304 }
15306
15307 if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
15308 ast_debug(1, "VM Operator break disabled globally\n");
15309 val = "no";
15310 }
15312
15313 if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
15314 ast_debug(1, "VM CID Info before msg disabled globally\n");
15315 val = "no";
15316 }
15318
15319 if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
15320 ast_debug(1, "Send Voicemail msg disabled globally\n");
15321 val = "no";
15322 }
15324
15325 if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
15326 ast_debug(1, "ENVELOPE before msg enabled globally\n");
15327 val = "yes";
15328 }
15330
15331 if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
15332 ast_debug(1, "Move Heard enabled globally\n");
15333 val = "yes";
15334 }
15336
15337 if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
15338 ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
15339 val = "no";
15340 }
15342
15343 if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
15344 ast_debug(1, "Duration info before msg enabled globally\n");
15345 val = "yes";
15346 }
15348
15349 saydurationminfo = 2;
15350 if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
15351 if (sscanf(val, "%30d", &x) == 1) {
15352 saydurationminfo = x;
15353 } else {
15354 ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
15355 }
15356 }
15357
15358 if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
15359 ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
15360 val = "no";
15361 }
15363
15364 if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
15366 ast_debug(1, "found dialout context: %s\n", dialcontext);
15367 } else {
15368 dialcontext[0] = '\0';
15369 }
15370
15371 if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
15373 ast_debug(1, "found callback context: %s\n", callcontext);
15374 } else {
15375 callcontext[0] = '\0';
15376 }
15377
15378 if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
15380 ast_debug(1, "found operator context: %s\n", exitcontext);
15381 } else {
15382 exitcontext[0] = '\0';
15383 }
15384
15385 /* load password sounds configuration */
15386 if ((val = ast_variable_retrieve(cfg, "general", "vm-login")))
15388 if ((val = ast_variable_retrieve(cfg, "general", "vm-newuser")))
15390 if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
15392 if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
15394 if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
15396 if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
15398 if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
15400 if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
15402 if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
15404 }
15405 if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
15407 }
15408 /* load configurable audio prompts */
15409 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
15411 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
15413 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
15415 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
15417 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
15419
15420 if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
15421 val = "no";
15423
15424 if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
15425 val = "voicemail.conf";
15426 }
15427 if (!(strcmp(val, "spooldir"))) {
15429 } else {
15431 }
15432
15434 if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
15435 if (sscanf(val, "%30u", &poll_freq) != 1) {
15437 ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
15438 }
15439 }
15440
15441 poll_mailboxes = 0;
15442 if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
15444
15445 memset(fromstring, 0, sizeof(fromstring));
15446 memset(pagerfromstring, 0, sizeof(pagerfromstring));
15447 strcpy(charset, "ISO-8859-1");
15448 if (emailbody) {
15450 emailbody = NULL;
15451 }
15452 if (emailsubject) {
15455 }
15456 if (pagerbody) {
15458 pagerbody = NULL;
15459 }
15460 if (pagersubject) {
15463 }
15464 if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
15466 if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
15468 if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
15470 if ((val = ast_variable_retrieve(cfg, "general", "charset")))
15472 if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
15473 sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
15474 for (x = 0; x < 4; x++) {
15475 memcpy(&adsifdn[x], &tmpadsi[x], 1);
15476 }
15477 }
15478 if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
15479 sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
15480 for (x = 0; x < 4; x++) {
15481 memcpy(&adsisec[x], &tmpadsi[x], 1);
15482 }
15483 }
15484 if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
15485 if (atoi(val)) {
15486 adsiver = atoi(val);
15487 }
15488 }
15489 if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
15491 }
15492 if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
15493 ast_copy_string(locale, val, sizeof(locale));
15494 }
15495 if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
15497 }
15498 if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
15500 }
15501 if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
15503 }
15504 if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
15506 }
15507
15508 tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
15509 if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_high"))) {
15510 if (sscanf(val, "%30ld", &tps_queue_high) != 1 || tps_queue_high <= 0) {
15511 ast_log(AST_LOG_WARNING, "Invalid the taskprocessor high water alert trigger level '%s'\n", val);
15512 tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
15513 }
15514 }
15515 tps_queue_low = -1;
15516 if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_low"))) {
15517 if (sscanf(val, "%30ld", &tps_queue_low) != 1 ||
15518 tps_queue_low < -1 || tps_queue_high < tps_queue_low) {
15519 ast_log(AST_LOG_WARNING, "Invalid the taskprocessor low water clear alert level '%s'\n", val);
15520 tps_queue_low = -1;
15521 }
15522 }
15523 if (ast_taskprocessor_alert_set_levels(mwi_subscription_tps, tps_queue_low, tps_queue_high)) {
15524 ast_log(AST_LOG_WARNING, "Failed to set alert levels for voicemail taskprocessor.\n");
15525 }
15526
15527 /* load mailboxes from users.conf */
15528 if (ucfg) {
15529 for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
15530 if (!strcasecmp(cat, "general")) {
15531 continue;
15532 }
15533 if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
15534 continue;
15535 if ((current = find_or_create(userscontext, cat))) {
15538 ast_copy_string(current->context, userscontext, sizeof(current->context));
15539 if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
15540 current->passwordlocation = OPT_PWLOC_USERSCONF;
15541 }
15542
15543 switch (current->passwordlocation) {
15544 case OPT_PWLOC_SPOOLDIR:
15545 snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
15546 read_password_from_file(secretfn, current->password, sizeof(current->password));
15547 }
15548 }
15549 }
15550 }
15551
15552 /* load mailboxes from voicemail.conf */
15553
15554 /*
15555 * Aliases must be loaded before users or the aliases won't be notified
15556 * if there's existing voicemail in the user mailbox.
15557 */
15558 load_aliases(cfg);
15559
15560 load_zonemessages(cfg);
15561
15562 load_users(cfg);
15563
15565
15570
15571 return 0;
15572 } else {
15574 ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
15575 return 0;
15576 }
15577}
15578
15579static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
15580{
15581 int res = -1;
15582 char dir[PATH_MAX];
15583 snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
15584 ast_debug(2, "About to try retrieving name file %s\n", dir);
15585 RETRIEVE(dir, -1, mailbox, context);
15586 if (ast_fileexists(dir, NULL, NULL)) {
15587 res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
15588 }
15589 DISPOSE(dir, -1);
15590 return res;
15591}
15592
15593/*!
15594 * \internal
15595 * \brief Play a recorded user name for the mailbox to the specified channel.
15596 *
15597 * \param chan Where to play the recorded name file.
15598 * \param mailbox_id The mailbox name.
15599 *
15600 * \retval 0 Name played without interruption
15601 * \retval dtmf ASCII value of the DTMF which interrupted playback.
15602 * \retval -1 Unable to locate mailbox or hangup occurred.
15603 */
15604static int vm_sayname(struct ast_channel *chan, const char *mailbox_id)
15605{
15606 char *context;
15607 char *mailbox;
15608
15609 if (ast_strlen_zero(mailbox_id)
15610 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
15611 return -1;
15612 }
15613 return sayname(chan, mailbox, context);
15614}
15615
15616static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
15617 struct ast_config *pwconf;
15618 struct ast_flags config_flags = { 0 };
15619
15620 pwconf = ast_config_load(secretfn, config_flags);
15621 if (valid_config(pwconf)) {
15622 const char *val = ast_variable_retrieve(pwconf, "general", "password");
15623 if (val) {
15624 ast_copy_string(password, val, passwordlen);
15625 ast_config_destroy(pwconf);
15626 return;
15627 }
15628 ast_config_destroy(pwconf);
15629 }
15630 ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
15631}
15632
15633static int write_password_to_file(const char *secretfn, const char *password) {
15634 struct ast_config *conf;
15635 struct ast_category *cat;
15636 struct ast_variable *var;
15637 int res = -1;
15638
15639 if (!(conf = ast_config_new())) {
15640 ast_log(LOG_ERROR, "Error creating new config structure\n");
15641 return res;
15642 }
15643 if (!(cat = ast_category_new("general", "", 1))) {
15644 ast_log(LOG_ERROR, "Error creating new category structure\n");
15646 return res;
15647 }
15648 if (!(var = ast_variable_new("password", password, ""))) {
15649 ast_log(LOG_ERROR, "Error creating new variable structure\n");
15652 return res;
15653 }
15656 if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
15657 res = 0;
15658 } else {
15659 ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
15660 }
15661
15663 return res;
15664}
15665
15666static int vmsayname_exec(struct ast_channel *chan, const char *data)
15667{
15668 char *context;
15669 char *mailbox;
15670 int res;
15671
15672 if (ast_strlen_zero(data)
15674 ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
15675 return -1;
15676 }
15677
15678 if ((res = sayname(chan, mailbox, context)) < 0) {
15679 ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", mailbox, context);
15680 res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
15681 if (!res) {
15683 }
15684 }
15685
15686 return res;
15687}
15688
15689#ifdef TEST_FRAMEWORK
15690static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
15691{
15692 return 0;
15693}
15694
15695static struct ast_frame *fake_read(struct ast_channel *ast)
15696{
15697 return &ast_null_frame;
15698}
15699
15700AST_TEST_DEFINE(test_voicemail_vmsayname)
15701{
15702 char dir[PATH_MAX];
15703 char dir2[PATH_MAX];
15704 static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
15705 static const char TEST_EXTENSION[] = "1234";
15706
15707 struct ast_channel *test_channel1 = NULL;
15708 int res = -1;
15709 struct ast_format_cap *capabilities;
15710
15711 static const struct ast_channel_tech fake_tech = {
15712 .write = fake_write,
15713 .read = fake_read,
15714 };
15715
15716 switch (cmd) {
15717 case TEST_INIT:
15718 info->name = "vmsayname_exec";
15719 info->category = "/apps/app_voicemail/";
15720 info->summary = "Vmsayname unit test";
15721 info->description =
15722 "This tests passing various parameters to vmsayname";
15723 return AST_TEST_NOT_RUN;
15724 case TEST_EXECUTE:
15725 break;
15726 }
15727
15728 if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL,
15729 NULL, NULL, 0, 0, "TestChannel1"))) {
15730 goto exit_vmsayname_test;
15731 }
15732
15733 /* normally this is done in the channel driver */
15735 if (!capabilities) {
15736 goto exit_vmsayname_test;
15737 }
15740 ao2_ref(capabilities, -1);
15745 ast_channel_tech_set(test_channel1, &fake_tech);
15746
15747 ast_channel_unlock(test_channel1);
15748
15749 ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
15750 snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
15751 if (!(res = vmsayname_exec(test_channel1, dir))) {
15752 snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15753 if (ast_fileexists(dir, NULL, NULL)) {
15754 ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
15755 res = -1;
15756 goto exit_vmsayname_test;
15757 } else {
15758 /* no greeting already exists as expected, let's create one to fully test sayname */
15759 if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
15760 ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
15761 goto exit_vmsayname_test;
15762 }
15763 snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_DATA_DIR);
15764 snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15765 /* we're not going to hear the sound anyway, just use a valid gsm audio file */
15766 if ((res = symlink(dir, dir2))) {
15767 ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
15768 goto exit_vmsayname_test;
15769 }
15770 ast_test_status_update(test, "Test playing created mailbox greeting...\n");
15771 snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
15772 res = vmsayname_exec(test_channel1, dir);
15773
15774 /* TODO: there may be a better way to do this */
15775 unlink(dir2);
15776 snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15777 rmdir(dir2);
15778 snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
15779 rmdir(dir2);
15780 }
15781 }
15782
15783exit_vmsayname_test:
15784
15785 ast_hangup(test_channel1);
15786
15787 return res ? AST_TEST_FAIL : AST_TEST_PASS;
15788}
15789
15790struct test_files {
15791 char dir[256];
15792 char file[256];
15793 char txtfile[256];
15794};
15795
15796AST_TEST_DEFINE(test_voicemail_msgcount)
15797{
15798 int i, j, res = AST_TEST_PASS, syserr;
15799 struct ast_vm_user *vmu;
15800 struct ast_vm_user svm;
15801 struct vm_state vms;
15802#ifdef IMAP_STORAGE
15803 struct ast_channel *chan = NULL;
15804#endif
15805 /* Using ast_alloca instead of just declaring tmp as an array is a workaround for a GCC 10 issue with -Wrestrict */
15806 struct test_files *tmp = ast_alloca(sizeof(struct test_files) * 3);
15807 char syscmd[256];
15808 const char origweasels[] = "tt-weasels";
15809 const char testcontext[] = "test";
15810 const char testmailbox[] = "00000000";
15811 const char testspec[] = "00000000@test";
15812 FILE *txt;
15813 int new, old, urgent;
15814 const char *folders[3] = { "Old", "Urgent", "INBOX" };
15815 const int folder2mbox[3] = { 1, 11, 0 };
15816 const int expected_results[3][12] = {
15817 /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
15818 { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
15819 { 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
15820 { 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 2 },
15821 };
15822
15823 switch (cmd) {
15824 case TEST_INIT:
15825 info->name = "test_voicemail_msgcount";
15826 info->category = "/apps/app_voicemail/";
15827 info->summary = "Test Voicemail status checks";
15828 info->description =
15829 "Verify that message counts are correct when retrieved through the public API";
15830 return AST_TEST_NOT_RUN;
15831 case TEST_EXECUTE:
15832 break;
15833 }
15834
15835 /* Make sure the original path was completely empty */
15836 snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
15837 if ((syserr = ast_safe_system(syscmd))) {
15838 ast_test_status_update(test, "Unable to clear test directory: %s\n",
15839 syserr > 0 ? strerror(syserr) : "unable to fork()");
15840 return AST_TEST_FAIL;
15841 }
15842
15843#ifdef IMAP_STORAGE
15844 if (!(chan = ast_dummy_channel_alloc())) {
15845 ast_test_status_update(test, "Unable to create dummy channel\n");
15846 return AST_TEST_FAIL;
15847 }
15848#endif
15849
15850 memset(&svm, 0, sizeof(svm));
15851 if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
15852 !(vmu = find_or_create(testcontext, testmailbox))) {
15853 ast_test_status_update(test, "Cannot create vmu structure\n");
15854#ifdef IMAP_STORAGE
15855 chan = ast_channel_unref(chan);
15856#endif
15857 return AST_TEST_FAIL;
15858 }
15859
15860 populate_defaults(vmu);
15861 memset(&vms, 0, sizeof(vms));
15862
15863 /* Create temporary voicemail */
15864 for (i = 0; i < 3; i++) {
15865 create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
15866 make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
15867 snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
15868
15869 if (ast_fileexists(origweasels, "gsm", "en") > 0) {
15870 snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
15871 VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
15872 if ((syserr = ast_safe_system(syscmd))) {
15873 ast_test_status_update(test, "Unable to create test voicemail: %s\n",
15874 syserr > 0 ? strerror(syserr) : "unable to fork()");
15875#ifdef IMAP_STORAGE
15876 chan = ast_channel_unref(chan);
15877#endif
15878 free_user(vmu);
15879 return AST_TEST_FAIL;
15880 }
15881 }
15882
15883 if ((txt = fopen(tmp[i].txtfile, "w+"))) {
15884 fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
15885 fclose(txt);
15886 } else {
15887 ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
15888 res = AST_TEST_FAIL;
15889 break;
15890 }
15891 open_mailbox(&vms, vmu, folder2mbox[i]);
15892 STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent", NULL);
15893
15894 /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
15895 for (j = 0; j < 3; j++) {
15896 /* folder[2] is INBOX, __has_voicemail will default back to INBOX */
15897 if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
15898 ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
15899 testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
15900 res = AST_TEST_FAIL;
15901 }
15902 }
15903
15904 new = old = urgent = 0;
15905 if (ast_app_inboxcount(testspec, &new, &old)) {
15906 ast_test_status_update(test, "inboxcount returned failure\n");
15907 res = AST_TEST_FAIL;
15908 } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
15909 ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
15910 testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
15911 res = AST_TEST_FAIL;
15912 }
15913
15914 new = old = urgent = 0;
15915 if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
15916 ast_test_status_update(test, "inboxcount2 returned failure\n");
15917 res = AST_TEST_FAIL;
15918 } else if (old != expected_results[i][6 + 0] ||
15919 urgent != expected_results[i][6 + 1] ||
15920 new != expected_results[i][6 + 2] ) {
15921 ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
15922 testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
15923 res = AST_TEST_FAIL;
15924 }
15925
15926 new = old = urgent = 0;
15927 for (j = 0; j < 3; j++) {
15928 if (ast_app_messagecount(testspec, folders[j]) != expected_results[i][9 + j]) {
15929 ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
15930 testspec, folders[j], ast_app_messagecount(testspec, folders[j]), expected_results[i][9 + j]);
15931 res = AST_TEST_FAIL;
15932 }
15933 }
15934 }
15935
15936 for (i = 0; i < 3; i++) {
15937 /* This is necessary if the voicemails are stored on an ODBC/IMAP
15938 * server, in which case, the rm below will not affect the
15939 * voicemails. */
15940 DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
15941 DISPOSE(tmp[i].dir, 0);
15942 }
15943
15944 if (vms.deleted) {
15945 ast_free(vms.deleted);
15946 }
15947 if (vms.heard) {
15948 ast_free(vms.heard);
15949 }
15950
15951#ifdef IMAP_STORAGE
15952 chan = ast_channel_unref(chan);
15953#endif
15954
15955 /* And remove test directory */
15956 snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
15957 if ((syserr = ast_safe_system(syscmd))) {
15958 ast_test_status_update(test, "Unable to clear test directory: %s\n",
15959 syserr > 0 ? strerror(syserr) : "unable to fork()");
15960 }
15961
15962 free_user(vmu);
15963 force_reload_config(); /* Restore original config */
15964 return res;
15965}
15966
15967AST_TEST_DEFINE(test_voicemail_notify_endl)
15968{
15969 int res = AST_TEST_PASS;
15970 char testcontext[] = "test";
15971 char testmailbox[] = "00000000";
15972 char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
15973 char attach[256], attach2[256];
15974 char buf[256] = ""; /* No line should actually be longer than 80 */
15975 struct ast_channel *chan = NULL;
15976 struct ast_vm_user *vmu, vmus = {
15977 .flags = 0,
15978 };
15979 FILE *file;
15980 struct {
15981 char *name;
15982 enum { INT, FLAGVAL, STATIC, STRPTR } type;
15983 void *location;
15984 union {
15985 int intval;
15986 char *strval;
15987 } u;
15988 } test_items[] = {
15989 { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
15990 { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
15991 { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
15992 { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
15993 { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
15994 { "attach2", STRPTR, attach2, .u.strval = "" },
15995 { "attach", STRPTR, attach, .u.strval = "" },
15996 };
15997 int which;
15998
15999 switch (cmd) {
16000 case TEST_INIT:
16001 info->name = "test_voicemail_notify_endl";
16002 info->category = "/apps/app_voicemail/";
16003 info->summary = "Test Voicemail notification end-of-line";
16004 info->description =
16005 "Verify that notification emails use a consistent end-of-line character";
16006 return AST_TEST_NOT_RUN;
16007 case TEST_EXECUTE:
16008 break;
16009 }
16010
16011 snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_DATA_DIR);
16012 snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_DATA_DIR);
16013
16014 if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
16015 !(vmu = find_or_create(testcontext, testmailbox))) {
16016 ast_test_status_update(test, "Cannot create vmu structure\n");
16017 return AST_TEST_NOT_RUN;
16018 }
16019
16020 if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
16021 ast_test_status_update(test, "Cannot find vmu structure?!!\n");
16022 return AST_TEST_NOT_RUN;
16023 }
16024
16025 populate_defaults(vmu);
16026 vmu->email = ast_strdup("test2@example.net");
16027#ifdef IMAP_STORAGE
16028 /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
16029#endif
16030
16031 file = tmpfile();
16032 for (which = 0; which < ARRAY_LEN(test_items); which++) {
16033 /* Kill previous test, if any */
16034 rewind(file);
16035 if (ftruncate(fileno(file), 0)) {
16036 ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
16037 res = AST_TEST_FAIL;
16038 break;
16039 }
16040
16041 /* Make each change, in order, to the test mailbox */
16042 if (test_items[which].type == INT) {
16043 *((int *) test_items[which].location) = test_items[which].u.intval;
16044 } else if (test_items[which].type == FLAGVAL) {
16045 if (ast_test_flag(vmu, test_items[which].u.intval)) {
16046 ast_clear_flag(vmu, test_items[which].u.intval);
16047 } else {
16048 ast_set_flag(vmu, test_items[which].u.intval);
16049 }
16050 } else if (test_items[which].type == STATIC) {
16051 strcpy(test_items[which].location, test_items[which].u.strval);
16052 } else if (test_items[which].type == STRPTR) {
16053 test_items[which].location = test_items[which].u.strval;
16054 }
16055
16056 make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL, NULL);
16057 rewind(file);
16058 while (fgets(buf, sizeof(buf), file)) {
16059 if (
16060 (strlen(buf) > 1 &&
16061#ifdef IMAP_STORAGE
16062 buf[strlen(buf) - 2] != '\r'
16063#else
16064 buf[strlen(buf) - 2] == '\r'
16065#endif
16066 )
16067 || buf[strlen(buf) - 1] != '\n') {
16068 res = AST_TEST_FAIL;
16069 }
16070 }
16071 }
16072 fclose(file);
16073 free_user(vmu);
16074 force_reload_config(); /* Restore original config */
16075 return res;
16076}
16077
16078AST_TEST_DEFINE(test_voicemail_load_config)
16079{
16080 int res = AST_TEST_PASS;
16081 struct ast_vm_user *vmu;
16082 struct ast_config *cfg;
16083 char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
16084 int fd;
16085 FILE *file;
16086 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
16087
16088 switch (cmd) {
16089 case TEST_INIT:
16090 info->name = "test_voicemail_load_config";
16091 info->category = "/apps/app_voicemail/";
16092 info->summary = "Test loading Voicemail config";
16093 info->description =
16094 "Verify that configuration is loaded consistently. "
16095 "This is to test regressions of ASTERISK-18838 where it was noticed that "
16096 "some options were loaded after the mailboxes were instantiated, causing "
16097 "those options not to be set correctly.";
16098 return AST_TEST_NOT_RUN;
16099 case TEST_EXECUTE:
16100 break;
16101 }
16102
16103 /* build a config file by hand... */
16104 if ((fd = mkstemp(config_filename)) < 0) {
16105 return AST_TEST_FAIL;
16106 }
16107 if (!(file = fdopen(fd, "w"))) {
16108 close(fd);
16109 unlink(config_filename);
16110 return AST_TEST_FAIL;
16111 }
16112 fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
16113 fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
16114 fputs("00000002 => 9999,Mrs. Test\n", file);
16115 fclose(file);
16116
16117 if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
16118 res = AST_TEST_FAIL;
16119 goto cleanup;
16120 }
16121
16122 load_config_from_memory(1, cfg, NULL);
16123 ast_config_destroy(cfg);
16124
16125#define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
16126 ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
16127 u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
16128
16130 AST_LIST_TRAVERSE(&users, vmu, list) {
16131 if (!strcmp(vmu->mailbox, "00000001")) {
16132 if (0); /* trick to get CHECK to work */
16133 CHECK(vmu, callback, "othercontext")
16134 CHECK(vmu, locale, "nl_NL.UTF-8")
16135 CHECK(vmu, zonetag, "central")
16136 } else if (!strcmp(vmu->mailbox, "00000002")) {
16137 if (0); /* trick to get CHECK to work */
16138 CHECK(vmu, callback, "somecontext")
16139 CHECK(vmu, locale, "de_DE.UTF-8")
16140 CHECK(vmu, zonetag, "european")
16141 }
16142 }
16144
16145#undef CHECK
16146
16147 /* Forcibly restore the original config, to reinitialize after test */
16148 force_reload_config(); /* this might say "Failed to load configuration file." */
16149
16150cleanup:
16151 unlink(config_filename);
16152 return res;
16153}
16154
16155AST_TEST_DEFINE(test_voicemail_vm_info)
16156{
16157 struct ast_vm_user *vmu;
16158 struct ast_channel *chan = NULL;
16159 const char testcontext[] = "test";
16160 const char testmailbox[] = "00000000";
16161 const char vminfo_cmd[] = "VM_INFO";
16162 char vminfo_buf[256], vminfo_args[256];
16163 int res = AST_TEST_PASS;
16164 int test_ret = 0;
16165 int test_counter = 0;
16166
16167 struct {
16168 char *vminfo_test_args;
16169 char *vminfo_expected;
16170 int vminfo_ret;
16171 } test_items[] = {
16172 { "", "", -1 }, /* Missing argument */
16173 { "00000000@test,badparam", "", -1 }, /* Wrong argument */
16174 { "00000000@test", "", -1 }, /* Missing argument */
16175 { "00000000@test,exists", "1", 0 },
16176 { "11111111@test,exists", "0", 0 }, /* Invalid mailbox */
16177 { "00000000@test,email", "vm-info-test@example.net", 0 },
16178 { "11111111@test,email", "", 0 }, /* Invalid mailbox */
16179 { "00000000@test,fullname", "Test Framework Mailbox", 0 },
16180 { "00000000@test,pager", "vm-info-pager-test@example.net", 0 },
16181 { "00000000@test,locale", "en_US", 0 },
16182 { "00000000@test,tz", "central", 0 },
16183 { "00000000@test,language", "en", 0 },
16184 { "00000000@test,password", "9876", 0 },
16185 };
16186
16187 switch (cmd) {
16188 case TEST_INIT:
16189 info->name = "test_voicemail_vm_info";
16190 info->category = "/apps/app_voicemail/";
16191 info->summary = "VM_INFO unit test";
16192 info->description =
16193 "This tests passing various parameters to VM_INFO";
16194 return AST_TEST_NOT_RUN;
16195 case TEST_EXECUTE:
16196 break;
16197 }
16198
16199 if (!(chan = ast_dummy_channel_alloc())) {
16200 ast_test_status_update(test, "Unable to create dummy channel\n");
16201 return AST_TEST_FAIL;
16202 }
16203
16204 if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
16205 !(vmu = find_or_create(testcontext, testmailbox))) {
16206 ast_test_status_update(test, "Cannot create vmu structure\n");
16207 chan = ast_channel_unref(chan);
16208 return AST_TEST_FAIL;
16209 }
16210
16211 populate_defaults(vmu);
16212
16213 vmu->email = ast_strdup("vm-info-test@example.net");
16214 if (!vmu->email) {
16215 ast_test_status_update(test, "Cannot create vmu email\n");
16216 chan = ast_channel_unref(chan);
16217 return AST_TEST_FAIL;
16218 }
16219 ast_copy_string(vmu->fullname, "Test Framework Mailbox", sizeof(vmu->fullname));
16220 ast_copy_string(vmu->pager, "vm-info-pager-test@example.net", sizeof(vmu->pager));
16221 ast_copy_string(vmu->language, "en", sizeof(vmu->language));
16222 ast_copy_string(vmu->zonetag, "central", sizeof(vmu->zonetag));
16223 ast_copy_string(vmu->locale, "en_US", sizeof(vmu->zonetag));
16224 ast_copy_string(vmu->password, "9876", sizeof(vmu->password));
16225
16226 for (test_counter = 0; test_counter < ARRAY_LEN(test_items); test_counter++) {
16227 ast_copy_string(vminfo_args, test_items[test_counter].vminfo_test_args, sizeof(vminfo_args));
16228 test_ret = acf_vm_info(chan, vminfo_cmd, vminfo_args, vminfo_buf, sizeof(vminfo_buf));
16229 if (strcmp(vminfo_buf, test_items[test_counter].vminfo_expected)) {
16230 ast_test_status_update(test, "VM_INFO respose was: '%s', but expected: '%s'\n", vminfo_buf, test_items[test_counter].vminfo_expected);
16231 res = AST_TEST_FAIL;
16232 }
16233 if (!(test_ret == test_items[test_counter].vminfo_ret)) {
16234 ast_test_status_update(test, "VM_INFO return code was: '%i', but expected '%i'\n", test_ret, test_items[test_counter].vminfo_ret);
16235 res = AST_TEST_FAIL;
16236 }
16237 }
16238
16239 chan = ast_channel_unref(chan);
16240 free_user(vmu);
16241 return res;
16242}
16243#endif /* defined(TEST_FRAMEWORK) */
16244
16245static const struct ast_vm_functions vm_table = {
16247 .module_name = AST_MODULE,
16248
16249 .has_voicemail = has_voicemail,
16250 .inboxcount = inboxcount,
16251 .inboxcount2 = inboxcount2,
16252 .messagecount = messagecount,
16253 .copy_recording_to_vm = msg_create_from_file,
16254 .index_to_foldername = vm_index_to_foldername,
16255 .mailbox_snapshot_create = vm_mailbox_snapshot_create,
16256 .mailbox_snapshot_destroy = vm_mailbox_snapshot_destroy,
16257 .msg_move = vm_msg_move,
16258 .msg_remove = vm_msg_remove,
16259 .msg_forward = vm_msg_forward,
16260 .msg_play = vm_msg_play,
16261};
16262
16265 .module_name = AST_MODULE,
16266
16267 .sayname = vm_sayname,
16268};
16269
16270static int reload(void)
16271{
16272 return load_config(1);
16273}
16274
16275static int unload_module(void)
16276{
16277 int res;
16278
16285 res |= ast_manager_unregister("VoicemailUsersList");
16286 res |= ast_manager_unregister("VoicemailUserStatus");
16287 res |= ast_manager_unregister("VoicemailRefresh");
16288 res |= ast_manager_unregister("VoicemailBoxSummary");
16289 res |= ast_manager_unregister("VoicemailMove");
16290 res |= ast_manager_unregister("VoicemailRemove");
16291 res |= ast_manager_unregister("VoicemailForward");
16292#ifdef TEST_FRAMEWORK
16293 res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
16294 res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
16295 res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
16296 res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
16297 res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
16298 res |= AST_TEST_UNREGISTER(test_voicemail_vm_info);
16299#endif
16303#ifdef TEST_FRAMEWORK
16304 ast_uninstall_vm_test_functions();
16305#endif
16307
16308 ao2_container_unregister("voicemail_alias_mailbox_mappings");
16310 ao2_container_unregister("voicemail_mailbox_alias_mappings");
16312
16315
16317 ast_unload_realtime("voicemail");
16318 ast_unload_realtime("voicemail_data");
16319
16320#ifdef IMAP_STORAGE
16321 ast_mwi_state_callback_all(imap_close_subscribed_mailbox, NULL);
16322#endif
16323 free_vm_users();
16324 free_vm_zones();
16325 return res;
16326}
16327
16328static void print_mappings(void *v_obj, void *where, ao2_prnt_fn *prnt)
16329{
16330 struct alias_mailbox_mapping *mapping = v_obj;
16331
16332 if (!mapping) {
16333 return;
16334 }
16335 prnt(where, "Alias: %s Mailbox: %s", mapping->alias, mapping->mailbox);
16336}
16337
16338/*!
16339 * \brief Load the module
16340 *
16341 * Module loading including tests for configuration or dependencies.
16342 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
16343 * or AST_MODULE_LOAD_SUCCESS.
16344 *
16345 * If a dependency, allocation or environment variable fails tests, return AST_MODULE_LOAD_FAILURE.
16346 *
16347 * If the module can't load the configuration file, can't register as a provider or
16348 * has another issue not fatal to Asterisk itself, return AST_MODULE_LOAD_DECLINE.
16349 *
16350 * On success return AST_MODULE_LOAD_SUCCESS.
16351 */
16352static int load_module(void)
16353{
16354 int res;
16355 my_umask = umask(0);
16356 umask(my_umask);
16357
16360 if (!inprocess_container) {
16362 }
16363
16365 alias_mailbox_mapping_hash_fn, NULL, alias_mailbox_mapping_cmp_fn);
16367 ast_log(LOG_ERROR, "Unable to create alias_mailbox_mappings container\n");
16370 }
16371 res = ao2_container_register("voicemail_alias_mailbox_mappings", alias_mailbox_mappings, print_mappings);
16372 if (res) {
16373 ast_log(LOG_ERROR, "Unable to register alias_mailbox_mappings container\n");
16377 }
16378
16380 mailbox_alias_mapping_hash_fn, NULL, mailbox_alias_mapping_cmp_fn);
16382 ast_log(LOG_ERROR, "Unable to create mailbox_alias_mappings container\n");
16384 ao2_container_unregister("voicemail_alias_mailbox_mappings");
16387 }
16388 res = ao2_container_register("voicemail_mailbox_alias_mappings", mailbox_alias_mappings, print_mappings);
16389 if (res) {
16390 ast_log(LOG_ERROR, "Unable to register mailbox_alias_mappings container\n");
16392 ao2_container_unregister("voicemail_alias_mailbox_mappings");
16396 }
16397
16398 /* compute the location of the voicemail spool directory */
16399 snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
16400
16401 if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
16402 ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor. MWI will not work\n");
16403 }
16404
16405 if ((res = load_config(0))) {
16406 unload_module();
16408 }
16409
16423#ifdef TEST_FRAMEWORK
16424 res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
16425 res |= AST_TEST_REGISTER(test_voicemail_msgcount);
16426 res |= AST_TEST_REGISTER(test_voicemail_vmuser);
16427 res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
16428 res |= AST_TEST_REGISTER(test_voicemail_load_config);
16429 res |= AST_TEST_REGISTER(test_voicemail_vm_info);
16430#endif
16431
16432 if (res) {
16433 ast_log(LOG_ERROR, "Failure registering applications, functions or tests\n");
16434 unload_module();
16436 }
16437
16438 /* ast_vm_register may return DECLINE if another module registered for vm */
16439 res = ast_vm_register(&vm_table);
16440 if (res) {
16441 ast_log(LOG_ERROR, "Failure registering as a voicemail provider\n");
16442 unload_module();
16444 }
16445
16446 /* ast_vm_greeter_register may return DECLINE if another module registered as a greeter */
16448 if (res) {
16449 ast_log(LOG_ERROR, "Failure registering as a greeter provider\n");
16450 unload_module();
16452 }
16453
16455
16456#ifdef TEST_FRAMEWORK
16457 ast_install_vm_test_functions(vm_test_create_user, vm_test_destroy_user);
16458#endif
16459
16460 ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
16461 ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
16462
16464}
16465
16466static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
16467{
16468 int cmd = 0;
16469 char destination[80] = "";
16470 int retries = 0;
16471
16472 if (!num) {
16473 ast_verb(3, "Destination number will be entered manually\n");
16474 while (retries < 3 && cmd != 't') {
16475 destination[1] = '\0';
16476 destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
16477 if (!cmd)
16478 destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
16479 if (!cmd)
16480 destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
16481 if (!cmd) {
16482 cmd = ast_waitfordigit(chan, 6000);
16483 if (cmd)
16484 destination[0] = cmd;
16485 }
16486 if (!cmd) {
16487 retries++;
16488 } else {
16489
16490 if (cmd < 0)
16491 return 0;
16492 if (cmd == '*') {
16493 ast_verb(3, "User hit '*' to cancel outgoing call\n");
16494 return 0;
16495 }
16496 if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0)
16497 retries++;
16498 else
16499 cmd = 't';
16500 }
16501 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
16502 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
16503 }
16504 if (retries >= 3) {
16505 return 0;
16506 }
16507
16508 } else {
16509 ast_verb(3, "Destination number is CID number '%s'\n", num);
16510 ast_copy_string(destination, num, sizeof(destination));
16511 }
16512
16513 if (!ast_strlen_zero(destination)) {
16514 if (destination[strlen(destination) -1 ] == '*')
16515 return 0;
16516 ast_verb(3, "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, ast_channel_context(chan));
16517 ast_channel_exten_set(chan, destination);
16518 ast_channel_context_set(chan, outgoing_context);
16519 ast_channel_priority_set(chan, 0);
16520 return 9;
16521 }
16522 return 0;
16523}
16524
16525/*!
16526 * \brief The advanced options within a message.
16527 * \param chan
16528 * \param vmu
16529 * \param vms
16530 * \param msg
16531 * \param option
16532 * \param record_gain
16533 *
16534 * Provides handling for the play message envelope, call the person back, or reply to message.
16535 *
16536 * \return zero on success, -1 on error.
16537 */
16538static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain)
16539{
16540 int res = 0;
16541 char filename[PATH_MAX];
16542 struct ast_config *msg_cfg = NULL;
16543 const char *origtime, *context;
16544 char *name, *num;
16545 int retries = 0;
16546 char *cid;
16547 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
16548
16549 vms->starting = 0;
16550
16551 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
16552
16553 /* Retrieve info from VM attribute file */
16554 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
16555 RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
16556 msg_cfg = ast_config_load(filename, config_flags);
16557 DISPOSE(vms->curdir, vms->curmsg);
16558 if (!valid_config(msg_cfg)) {
16559 ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
16560 return 0;
16561 }
16562
16563 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
16564 ast_config_destroy(msg_cfg);
16565 return 0;
16566 }
16567
16568 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
16569
16570 context = ast_variable_retrieve(msg_cfg, "message", "context");
16571 switch (option) {
16572 case 3: /* Play message envelope */
16573 if (!res) {
16574 res = play_message_datetime(chan, vmu, origtime, filename);
16575 }
16576 if (!res) {
16577 res = play_message_callerid(chan, vms, cid, context, 0, 1);
16578 }
16579
16580 res = 't';
16581 break;
16582
16583 case 2: /* Call back */
16584
16585 if (ast_strlen_zero(cid))
16586 break;
16587
16588 ast_callerid_parse(cid, &name, &num);
16589 while ((res > -1) && (res != 't')) {
16590 switch (res) {
16591 case '1':
16592 if (num) {
16593 /* Dial the CID number */
16594 res = dialout(chan, vmu, num, vmu->callback);
16595 if (res) {
16596 ast_config_destroy(msg_cfg);
16597 return 9;
16598 }
16599 } else {
16600 res = '2';
16601 }
16602 break;
16603
16604 case '2':
16605 /* Want to enter a different number, can only do this if there's a dialout context for this user */
16606 if (!ast_strlen_zero(vmu->dialout)) {
16607 res = dialout(chan, vmu, NULL, vmu->dialout);
16608 if (res) {
16609 ast_config_destroy(msg_cfg);
16610 return 9;
16611 }
16612 } else {
16613 ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
16614 res = ast_play_and_wait(chan, "vm-sorry");
16615 }
16616 ast_config_destroy(msg_cfg);
16617 return res;
16618 case '*':
16619 res = 't';
16620 break;
16621 case '3':
16622 case '4':
16623 case '5':
16624 case '6':
16625 case '7':
16626 case '8':
16627 case '9':
16628 case '0':
16629
16630 res = ast_play_and_wait(chan, "vm-sorry");
16631 retries++;
16632 break;
16633 default:
16634 if (num) {
16635 ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
16636 res = ast_play_and_wait(chan, "vm-num-i-have");
16637 if (!res)
16638 res = play_message_callerid(chan, vms, num, vmu->context, 1, 1);
16639 if (!res)
16640 res = ast_play_and_wait(chan, "vm-tocallnum");
16641 /* Only prompt for a caller-specified number if there is a dialout context specified */
16642 if (!ast_strlen_zero(vmu->dialout)) {
16643 if (!res)
16644 res = ast_play_and_wait(chan, "vm-calldiffnum");
16645 }
16646 } else {
16647 res = ast_play_and_wait(chan, "vm-nonumber");
16648 if (!ast_strlen_zero(vmu->dialout)) {
16649 if (!res)
16650 res = ast_play_and_wait(chan, "vm-toenternumber");
16651 }
16652 }
16653 if (!res) {
16654 res = ast_play_and_wait(chan, "vm-star-cancel");
16655 }
16656 if (!res) {
16657 res = ast_waitfordigit(chan, 6000);
16658 }
16659 if (!res) {
16660 retries++;
16661 if (retries > 3) {
16662 res = 't';
16663 }
16664 }
16665 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
16666 isprint(res) ? res : '?', isprint(res) ? res : '?');
16667 break;
16668
16669 }
16670 if (res == 't')
16671 res = 0;
16672 else if (res == '*')
16673 res = -1;
16674 }
16675 break;
16676
16677 case 1: /* Reply */
16678 /* Send reply directly to sender */
16679 if (ast_strlen_zero(cid))
16680 break;
16681
16682 ast_callerid_parse(cid, &name, &num);
16683 if (!num) {
16684 ast_verb(3, "No CID number available, no reply sent\n");
16685 if (!res)
16686 res = ast_play_and_wait(chan, "vm-nonumber");
16687 ast_config_destroy(msg_cfg);
16688 return res;
16689 } else {
16690 struct ast_vm_user vmu2, *vmu3;
16691 memset(&vmu2, 0, sizeof(vmu2));
16692 vmu3 = find_user(&vmu2, vmu->context, num);
16693 if (vmu3) {
16694 struct leave_vm_options leave_options;
16695 char mailbox[AST_MAX_EXTENSION * 2 + 2];
16696 snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
16697
16698 ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
16699
16700 memset(&leave_options, 0, sizeof(leave_options));
16701 leave_options.record_gain = record_gain;
16702 leave_options.beeptone = "beep";
16703 res = leave_voicemail(chan, mailbox, &leave_options);
16704 if (!res)
16705 res = 't';
16706 ast_config_destroy(msg_cfg);
16707 free_user(vmu3);
16708 return res;
16709 } else {
16710 /* Sender has no mailbox, can't reply */
16711 ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
16712 ast_play_and_wait(chan, "vm-nobox");
16713 res = 't';
16714 ast_config_destroy(msg_cfg);
16715 return res;
16716 }
16717 }
16718 res = 0;
16719
16720 break;
16721 }
16722
16723 ast_config_destroy(msg_cfg);
16724
16725#ifndef IMAP_STORAGE
16726 if (!res) {
16727 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
16728 vms->heard[msg] = 1;
16729 res = wait_file(chan, vms, vms->fn);
16730 }
16731#endif
16732 return res;
16733}
16734
16735static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
16736 int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
16737 signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro)
16738{
16739 /* Record message & let caller review or re-record it, or set options if applicable */
16740 int res = 0;
16741 int cmd = 0;
16742 int max_attempts = 3;
16743 int attempts = 0;
16744 int recorded = 0;
16745 int msg_exists = 0;
16746 signed char zero_gain = 0;
16747 char tempfile[PATH_MAX];
16748 char *acceptdtmf = "#";
16749 char *canceldtmf = "";
16750 int canceleddtmf = 0;
16751 SCOPE_ENTER(3, "%s: rf: %s fmt: %s type: %s vmu: %s\n",
16752 ast_channel_name(chan), recordfile, fmt, outsidecaller ? "msg" : "greeting",
16753 vmu->mailbox);
16754 /* Note that urgent and private are for flagging messages as such in the future */
16755
16756 /* barf if no pointer passed to store duration in */
16757 if (duration == NULL) {
16758 SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING, "%s\n", "Error play_record_review called without duration pointer\n");
16759 }
16760
16761 if (!outsidecaller)
16762 snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
16763 else
16764 ast_copy_string(tempfile, recordfile, sizeof(tempfile));
16765
16766 cmd = '3'; /* Want to start by recording */
16767
16768 while ((cmd >= 0) && (cmd != 't')) {
16769 switch (cmd) {
16770 case '1':
16771 if (!msg_exists) {
16772 /* In this case, 1 is to record a message */
16773 cmd = '3';
16774 break;
16775 } else {
16776 /* Otherwise 1 is to save the existing message */
16777 ast_verb(3, "Saving message as is\n");
16778 if (!outsidecaller) {
16779 ast_trace(-1, "Renaming greeting '%s' to '%s'\n", tempfile, recordfile);
16780 ast_filerename(tempfile, recordfile, NULL);
16781 }
16782 if (!forwardintro) {
16783 ast_stream_and_wait(chan, "vm-msgsaved", "");
16784 }
16785 if (!outsidecaller) {
16786 /* Saves to IMAP server only if imapgreeting=yes */
16787 ast_trace(-1, "Saving greeting '%s'\n", recordfile);
16788 SCOPE_CALL(-1, STORE, recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag, msg_id);
16789 SCOPE_CALL(-1, DISPOSE, recordfile, -1);
16790 }
16791 cmd = 't';
16792 SCOPE_EXIT_RTN_VALUE(res, "Message saved to %s\n", recordfile);
16793 }
16794 case '2':
16795 /* Review */
16796 ast_verb(3, "Reviewing the message\n");
16797 ast_trace(-1, "Reviewing '%s'\n", tempfile);
16798 cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
16799 break;
16800 case '3':
16801 msg_exists = 0;
16802 /* Record */
16803 if (recorded == 1)
16804 ast_verb(3, "Re-recording the message\n");
16805 else
16806 ast_verb(3, "Recording the message\n");
16807
16808 if (recorded && outsidecaller) {
16809 if (forwardintro) {
16810 cmd = ast_play_and_wait(chan, "vm-record-prepend");
16811 } else {
16812 cmd = ast_play_and_wait(chan, INTRO);
16813 }
16814 cmd = ast_play_and_wait(chan, "beep");
16815 }
16816 if (cmd == -1) {
16817 /* User has hung up, no options to give */
16818 ast_filedelete(tempfile, NULL);
16819 SCOPE_EXIT_RTN_VALUE(cmd, "User hung up before message could be rerecorded. Deleted '%s'\n", tempfile);
16820 }
16821 recorded = 1;
16822 /* 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 */
16823 if (record_gain)
16825 if (ast_test_flag(vmu, VM_OPERATOR))
16826 canceldtmf = "0";
16827 ast_trace(-1, "Recording '%s'\n", tempfile);
16828 cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, sound_duration, 0, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf, 0, AST_RECORD_IF_EXISTS_OVERWRITE);
16829 if (strchr(canceldtmf, cmd)) {
16830 /* need this flag here to distinguish between pressing '0' during message recording or after */
16831 canceleddtmf = 1;
16832 }
16833 if (record_gain)
16834 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
16835 if (cmd == -1) {
16836 /* User has hung up, no options to give */
16837 if (!outsidecaller) {
16838 /* user was recording a greeting and they hung up, so let's delete the recording. */
16839 ast_filedelete(tempfile, NULL);
16840 }
16841 SCOPE_EXIT_RTN_VALUE(cmd, "User hung up after recording. %s %s\n",
16842 outsidecaller ? "Saved message " : "Deleted greeting \n", tempfile);
16843 }
16844 if (cmd == '0') {
16845 break;
16846 } else if (cmd == '*') {
16847 break;
16848#if 0
16849 } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
16850 /* Message is too short */
16851 ast_verb(3, "Message too short\n");
16852 cmd = ast_play_and_wait(chan, "vm-tooshort");
16853 cmd = ast_filedelete(tempfile, NULL);
16854 break;
16855 } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
16856 /* Message is all silence */
16857 ast_verb(3, "Nothing recorded\n");
16858 cmd = ast_filedelete(tempfile, NULL);
16859 cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
16860 if (!cmd)
16861 cmd = ast_play_and_wait(chan, "vm-speakup");
16862 break;
16863#endif
16864 } else {
16865 /* If all is well, a message exists */
16866 msg_exists = 1;
16867 cmd = 0;
16868 }
16869 break;
16870 case '4':
16871 if (outsidecaller && ast_test_flag(vmu, VM_MARK_URGENT)) { /* only mark vm messages */
16872 /* Mark Urgent */
16873 if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
16874 ast_verb(3, "marking message as Urgent\n");
16875 res = ast_play_and_wait(chan, "vm-marked-urgent");
16876 strcpy(flag, "Urgent");
16877 } else if (flag) {
16878 ast_verb(3, "UNmarking message as Urgent\n");
16879 res = ast_play_and_wait(chan, "vm-marked-nonurgent");
16880 strcpy(flag, "");
16881 } else {
16882 ast_play_and_wait(chan, "vm-sorry");
16883 }
16884 cmd = 0;
16885 } else {
16886 cmd = ast_play_and_wait(chan, "vm-sorry");
16887 }
16888 break;
16889 case '5':
16890 case '6':
16891 case '7':
16892 case '8':
16893 case '9':
16894 case '*':
16895 case '#':
16896 cmd = ast_play_and_wait(chan, "vm-sorry");
16897 break;
16898#if 0
16899/* XXX Commented out for the moment because of the dangers of deleting
16900 a message while recording (can put the message numbers out of sync) */
16901 case '*':
16902 /* Cancel recording, delete message, offer to take another message*/
16903 cmd = ast_play_and_wait(chan, "vm-deleted");
16904 cmd = ast_filedelete(tempfile, NULL);
16905 if (outsidecaller) {
16906 res = vm_exec(chan, NULL);
16907 return res;
16908 }
16909 else
16910 return 1;
16911#endif
16912 case '0':
16913 if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
16914 cmd = ast_play_and_wait(chan, "vm-sorry");
16915 break;
16916 }
16917 if (msg_exists || recorded) {
16918 ast_trace(-1, "Reviewing '%s'\n", tempfile);
16919 cmd = ast_play_and_wait(chan, "vm-saveoper");
16920 if (!cmd)
16921 cmd = ast_waitfordigit(chan, 3000);
16922 if (cmd == '1') {
16923 ast_trace(-1, "Saving '%s' to '%s'\n", tempfile, recordfile);
16924 ast_filerename(tempfile, recordfile, NULL);
16925 ast_play_and_wait(chan, "vm-msgsaved");
16926 cmd = '0';
16927 } else if (cmd == '4') {
16928 if (flag) {
16929 ast_play_and_wait(chan, "vm-marked-urgent");
16930 strcpy(flag, "Urgent");
16931 }
16932 ast_play_and_wait(chan, "vm-msgsaved");
16933 cmd = '0';
16934 } else {
16935 ast_trace(-1, "Deleting '%s'\n", tempfile);
16936 ast_play_and_wait(chan, "vm-deleted");
16937 SCOPE_CALL(-1, DELETE, tempfile, -1, tempfile, vmu);
16938 SCOPE_CALL(-1, DISPOSE, tempfile, -1);
16939 cmd = '0';
16940 }
16941 }
16942 return cmd;
16943 default:
16944 /* If the caller is an outside caller and the review option is enabled or it's forward intro
16945 allow them to review the message, but let the owner of the box review
16946 their OGM's */
16947 if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW) && !forwardintro) {
16948 SCOPE_EXIT_RTN_VALUE(cmd, "Done. Outside caller, review not set, no forwardintro\n");
16949 }
16950 if (msg_exists) {
16951 cmd = ast_play_and_wait(chan, "vm-review");
16952 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_MARK_URGENT)) {
16953 if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
16954 cmd = ast_play_and_wait(chan, "vm-review-urgent");
16955 } else if (flag) {
16956 cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
16957 }
16958 }
16959 } else {
16960 cmd = ast_play_and_wait(chan, "vm-torerecord");
16961 if (!cmd)
16962 cmd = ast_waitfordigit(chan, 600);
16963 }
16964
16965 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
16966 cmd = ast_play_and_wait(chan, "vm-reachoper");
16967 if (!cmd)
16968 cmd = ast_waitfordigit(chan, 600);
16969 }
16970#if 0
16971 if (!cmd)
16972 cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
16973#endif
16974 if (!cmd)
16975 cmd = ast_waitfordigit(chan, 6000);
16976 if (!cmd) {
16977 attempts++;
16978 }
16979 if (attempts > max_attempts) {
16980 cmd = 't';
16981 }
16982 }
16983 }
16984 if (!outsidecaller && (cmd == -1 || cmd == 't')) {
16985 /* Hang up or timeout, so delete the recording. */
16986 ast_trace(-1, "Deleting '%s' on hangup or timeout\n", tempfile);
16987 ast_filedelete(tempfile, NULL);
16988 }
16989
16990 if (cmd != 't' && outsidecaller)
16991 ast_play_and_wait(chan, "vm-goodbye");
16992
16993 SCOPE_EXIT_RTN_VALUE(cmd, "Done\n");
16994}
16995
16997{
16998 struct ast_vm_msg_snapshot *msg_snapshot;
16999
17000 if (!(msg_snapshot = ast_calloc(1, sizeof(*msg_snapshot)))) {
17001 return NULL;
17002 }
17003
17004 if (ast_string_field_init(msg_snapshot, 512)) {
17005 ast_free(msg_snapshot);
17006 return NULL;
17007 }
17008
17009 return msg_snapshot;
17010}
17011
17013{
17014 ast_string_field_free_memory(msg_snapshot);
17015 ast_free(msg_snapshot);
17016
17017 return NULL;
17018}
17019
17020#ifdef TEST_FRAMEWORK
17021
17022static int vm_test_destroy_user(const char *context, const char *mailbox)
17023{
17024 struct ast_vm_user *vmu;
17025
17028 if (!strcmp(context, vmu->context)
17029 && !strcmp(mailbox, vmu->mailbox)) {
17031 ast_free(vmu);
17032 break;
17033 }
17034 }
17037 return 0;
17038}
17039
17040static int vm_test_create_user(const char *context, const char *mailbox)
17041{
17042 struct ast_vm_user *vmu;
17043
17044 if (!(vmu = find_or_create(context, mailbox))) {
17045 return -1;
17046 }
17047 populate_defaults(vmu);
17048 return 0;
17049}
17050
17051#endif
17052
17053/*!
17054 * \brief Create and store off all the msgs in an open mailbox
17055 *
17056 * \note TODO XXX This function should work properly for all
17057 * voicemail storage options, but is far more expensive for
17058 * ODBC at the moment. This is because the RETRIEVE macro
17059 * not only pulls out the message's meta data file from the
17060 * database, but also the actual audio for each message, temporarily
17061 * writing it to the file system. This is an area that needs
17062 * to be made more efficient.
17063 */
17065 struct vm_state *vms,
17066 struct ast_vm_mailbox_snapshot *mailbox_snapshot,
17067 int snapshot_index,
17068 int mailbox_index,
17069 int descending,
17070 enum ast_vm_snapshot_sort_val sort_val)
17071{
17072 struct ast_vm_msg_snapshot *msg_snapshot;
17073 struct ast_vm_msg_snapshot *msg_snapshot_tmp;
17074 struct ast_config *msg_cfg;
17075 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17076 char filename[PATH_MAX];
17077 const char *value;
17078
17079 for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
17080 int inserted = 0;
17081 /* Find the msg */
17082 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
17083 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
17084 RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
17085 msg_cfg = ast_config_load(filename, config_flags);
17086 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17087 DISPOSE(vms->curdir, vms->curmsg);
17088 continue;
17089 }
17090
17091 /* Create the snapshot object */
17092 if (!(msg_snapshot = vm_msg_snapshot_alloc())) {
17093 ast_config_destroy(msg_cfg);
17094 return -1;
17095 }
17096
17097 /* Fill in the snapshot object */
17098 if ((value = ast_variable_retrieve(msg_cfg, "message", "msg_id"))) {
17099 ast_string_field_set(msg_snapshot, msg_id, value);
17100 } else {
17101 /* Message snapshots *really* should have a
17102 * message ID. Add one to the message config
17103 * if it does not already exist
17104 */
17105 char id[MSG_ID_LEN];
17106 if (!(add_message_id(msg_cfg, vms->curdir, vms->curmsg,
17107 filename, id, sizeof(id), vmu, mailbox_index))) {
17108 ast_string_field_set(msg_snapshot, msg_id, id);
17109 } else {
17110 ast_log(LOG_WARNING, "Unable to create a message ID for message %s/%d\n", vms->curdir, vms->curmsg);
17111 }
17112 }
17113 if ((value = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
17114 ast_string_field_set(msg_snapshot, callerid, value);
17115 }
17116 if ((value = ast_variable_retrieve(msg_cfg, "message", "callerchan"))) {
17117 ast_string_field_set(msg_snapshot, callerchan, value);
17118 }
17119 if ((value = ast_variable_retrieve(msg_cfg, "message", "exten"))) {
17120 ast_string_field_set(msg_snapshot, exten, value);
17121 }
17122 if ((value = ast_variable_retrieve(msg_cfg, "message", "origdate"))) {
17123 ast_string_field_set(msg_snapshot, origdate, value);
17124 }
17125 if ((value = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
17126 ast_string_field_set(msg_snapshot, origtime, value);
17127 }
17128 if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17129 ast_string_field_set(msg_snapshot, duration, value);
17130 }
17131 if ((value = ast_variable_retrieve(msg_cfg, "message", "flag"))) {
17132 ast_string_field_set(msg_snapshot, flag, value);
17133 }
17134 msg_snapshot->msg_number = vms->curmsg;
17135 ast_string_field_set(msg_snapshot, folder_name, mailbox_folders[mailbox_index]);
17136
17137 /* store msg snapshot in mailbox snapshot */
17138 switch (sort_val) {
17139 default:
17141 if (descending) {
17142 AST_LIST_INSERT_HEAD(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17143 } else {
17144 AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17145 }
17146 inserted = 1;
17147 break;
17149 AST_LIST_TRAVERSE_SAFE_BEGIN(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot_tmp, msg) {
17150 int val = strcmp(msg_snapshot->origtime, msg_snapshot_tmp->origtime);
17151 if (descending && val >= 0) {
17152 AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
17153 inserted = 1;
17154 break;
17155 } else if (!descending && val <= 0) {
17156 AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
17157 inserted = 1;
17158 break;
17159 }
17160 }
17162 break;
17163 }
17164
17165 if (!inserted) {
17166 AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17167 }
17168
17169 mailbox_snapshot->total_msg_num++;
17170
17171 /* cleanup configs and msg */
17172 ast_config_destroy(msg_cfg);
17173 DISPOSE(vms->curdir, vms->curmsg);
17174 }
17175
17176 return 0;
17177}
17178
17180 const char *context,
17181 const char *folder,
17182 int descending,
17183 enum ast_vm_snapshot_sort_val sort_val,
17184 int combine_INBOX_and_OLD)
17185{
17186 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
17187 struct vm_state vms;
17188 struct ast_vm_user *vmu = NULL, vmus;
17189 int res;
17190 int i;
17191 int this_index_only = -1;
17192 int open = 0;
17193 int inbox_index = get_folder_by_name("INBOX");
17194 int old_index = get_folder_by_name("Old");
17195 int urgent_index = get_folder_by_name("Urgent");
17196
17197 if (ast_strlen_zero(mailbox)) {
17198 ast_log(LOG_WARNING, "Cannot create a mailbox snapshot since no mailbox was specified\n");
17199 return NULL;
17200 }
17201
17202 memset(&vmus, 0, sizeof(vmus));
17203
17204 if (!(ast_strlen_zero(folder))) {
17205 /* find the folder index */
17206 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
17207 if (!strcasecmp(mailbox_folders[i], folder)) {
17208 this_index_only = i;
17209 break;
17210 }
17211 }
17212 if (this_index_only == -1) {
17213 /* Folder was specified and it did not match any folder in our list */
17214 return NULL;
17215 }
17216 }
17217
17218 if (!(vmu = find_user(&vmus, context, mailbox))) {
17219 ast_log(AST_LOG_WARNING, "Failed to create mailbox snapshot for unknown voicemail user %s@%s\n", mailbox, context);
17220 return NULL;
17221 }
17222
17223 if (!(mailbox_snapshot = ast_calloc(1, sizeof(*mailbox_snapshot)))) {
17224 ast_log(AST_LOG_ERROR, "Failed to allocate memory for mailbox snapshot\n");
17225 free_user(vmu);
17226 return NULL;
17227 }
17228
17229 if (!(mailbox_snapshot->snapshots = ast_calloc(ARRAY_LEN(mailbox_folders), sizeof(*mailbox_snapshot->snapshots)))) {
17230 ast_free(mailbox_snapshot);
17231 free_user(vmu);
17232 return NULL;
17233 }
17234
17235 mailbox_snapshot->folders = ARRAY_LEN(mailbox_folders);
17236
17237 for (i = 0; i < mailbox_snapshot->folders; i++) {
17238 int msg_folder_index = i;
17239
17240 /* We want this message in the snapshot if any of the following:
17241 * No folder was specified.
17242 * The specified folder matches the current folder.
17243 * The specified folder is INBOX AND we were asked to combine messages AND the current folder is either Old or Urgent.
17244 */
17245 if (!(this_index_only == -1 || this_index_only == i || (this_index_only == inbox_index && combine_INBOX_and_OLD && (i == old_index || i == urgent_index)))) {
17246 continue;
17247 }
17248
17249 /* Make sure that Old or Urgent messages are marked as being in INBOX. */
17250 if (combine_INBOX_and_OLD && (i == old_index || i == urgent_index)) {
17251 msg_folder_index = inbox_index;
17252 }
17253
17254 memset(&vms, 0, sizeof(vms));
17255 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17256 vms.lastmsg = -1;
17257 open = 0;
17258
17259 /* open the mailbox state */
17260 if ((res = open_mailbox(&vms, vmu, i)) < 0) {
17261 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17262 goto snapshot_cleanup;
17263 }
17264 open = 1;
17265
17266 /* Iterate through each msg, storing off info */
17267 if (vms.lastmsg != -1) {
17268 if ((vm_msg_snapshot_create(vmu, &vms, mailbox_snapshot, msg_folder_index, i, descending, sort_val))) {
17269 ast_log(LOG_WARNING, "Failed to create msg snapshots for %s@%s\n", mailbox, context);
17270 goto snapshot_cleanup;
17271 }
17272 }
17273
17274 /* close mailbox */
17275 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17276 goto snapshot_cleanup;
17277 }
17278 open = 0;
17279 }
17280
17281snapshot_cleanup:
17282 if (vmu && open) {
17283 close_mailbox(&vms, vmu);
17284 }
17285
17286#ifdef IMAP_STORAGE
17287 if (vmu) {
17288 vmstate_delete(&vms);
17289 }
17290#endif
17291
17292 free_user(vmu);
17293 return mailbox_snapshot;
17294}
17295
17297{
17298 int i;
17299 struct ast_vm_msg_snapshot *msg_snapshot;
17300
17301 for (i = 0; i < mailbox_snapshot->folders; i++) {
17302 while ((msg_snapshot = AST_LIST_REMOVE_HEAD(&mailbox_snapshot->snapshots[i], msg))) {
17303 msg_snapshot = vm_msg_snapshot_destroy(msg_snapshot);
17304 }
17305 }
17306 ast_free(mailbox_snapshot->snapshots);
17307 ast_free(mailbox_snapshot);
17308 return NULL;
17309}
17310
17311/*!
17312 * \brief common bounds checking and existence check for Voicemail API functions.
17313 *
17314 * \details
17315 * This is called by vm_msg_move, vm_msg_remove, and vm_msg_forward to
17316 * ensure that data passed in are valid. This ensures that given the
17317 * desired message IDs, they can be found.
17318 *
17319 * \param vms The voicemail state corresponding to an open mailbox
17320 * \param msg_ids An array of message identifiers
17321 * \param num_msgs The number of identifiers in msg_ids
17322 * \param[out] msg_nums The message indexes corresponding to the given
17323 * \param vmu
17324 * message IDs
17325 * \pre vms must have open_mailbox() called on it prior to this function.
17326 *
17327 * \retval -1 Failure
17328 * \retval 0 Success
17329 */
17330static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids [], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu)
17331{
17332 int i;
17333 int res = 0;
17334 for (i = 0; i < num_msgs; ++i) {
17335 const char *msg_id = msg_ids[i];
17336 int found = 0;
17337 for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
17338 const char *other_msg_id;
17339 char filename[PATH_MAX];
17340 struct ast_config *msg_cfg;
17341 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17342
17343 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
17344 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
17345 RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
17346 msg_cfg = ast_config_load(filename, config_flags);
17347 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17348 DISPOSE(vms->curdir, vms->curmsg);
17349 res = -1;
17350 goto done;
17351 }
17352
17353 other_msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
17354
17355 if (!ast_strlen_zero(other_msg_id) && !strcmp(other_msg_id, msg_id)) {
17356 /* Message found. We can get out of this inner loop
17357 * and move on to the next message to find
17358 */
17359 found = 1;
17360 msg_nums[i] = vms->curmsg;
17361 ast_config_destroy(msg_cfg);
17362 DISPOSE(vms->curdir, vms->curmsg);
17363 break;
17364 }
17365 ast_config_destroy(msg_cfg);
17366 DISPOSE(vms->curdir, vms->curmsg);
17367 }
17368 if (!found) {
17369 /* If we can't find one of the message IDs requested, then OH NO! */
17370 res = -1;
17371 goto done;
17372 }
17373 }
17374
17375done:
17376 return res;
17377}
17378
17379static void notify_new_state(struct ast_vm_user *vmu)
17380{
17381 int new = 0, old = 0, urgent = 0;
17382 char ext_context[1024];
17383
17384 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
17385 run_externnotify(vmu->context, vmu->mailbox, NULL);
17386 ast_app_inboxcount2(ext_context, &urgent, &new, &old);
17387 queue_mwi_event(NULL, ext_context, urgent, new, old);
17388}
17389
17390static int vm_msg_forward(const char *from_mailbox,
17391 const char *from_context,
17392 const char *from_folder,
17393 const char *to_mailbox,
17394 const char *to_context,
17395 const char *to_folder,
17396 size_t num_msgs,
17397 const char *msg_ids [],
17398 int delete_old)
17399{
17400 struct vm_state from_vms;
17401 struct ast_vm_user *vmu = NULL, vmus;
17402 struct ast_vm_user *to_vmu = NULL, to_vmus;
17403 struct ast_config *msg_cfg;
17404 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17405 char filename[PATH_MAX];
17406 int from_folder_index;
17407 int open = 0;
17408 int res = 0;
17409 int i;
17410 int *msg_nums;
17411
17412 if (ast_strlen_zero(from_mailbox) || ast_strlen_zero(to_mailbox)) {
17413 ast_log(LOG_WARNING, "Cannot forward message because either the from or to mailbox was not specified\n");
17414 return -1;
17415 }
17416
17417 if (!num_msgs) {
17418 ast_log(LOG_WARNING, "Invalid number of messages specified to forward: %zu\n", num_msgs);
17419 return -1;
17420 }
17421
17422 if (ast_strlen_zero(from_folder) || ast_strlen_zero(to_folder)) {
17423 ast_log(LOG_WARNING, "Cannot forward message because the from_folder or to_folder was not specified\n");
17424 return -1;
17425 }
17426
17427 memset(&vmus, 0, sizeof(vmus));
17428 memset(&to_vmus, 0, sizeof(to_vmus));
17429 memset(&from_vms, 0, sizeof(from_vms));
17430
17431 from_folder_index = get_folder_by_name(from_folder);
17432 if (from_folder_index == -1) {
17433 return -1;
17434 }
17435
17436 if (get_folder_by_name(to_folder) == -1) {
17437 return -1;
17438 }
17439
17440 if (!(vmu = find_user(&vmus, from_context, from_mailbox))) {
17441 ast_log(LOG_WARNING, "Can't find voicemail user to forward from (%s@%s)\n", from_mailbox, from_context);
17442 return -1;
17443 }
17444
17445 if (!(to_vmu = find_user(&to_vmus, to_context, to_mailbox))) {
17446 ast_log(LOG_WARNING, "Can't find voicemail user to forward to (%s@%s)\n", to_mailbox, to_context);
17447 free_user(vmu);
17448 return -1;
17449 }
17450
17451 ast_copy_string(from_vms.username, from_mailbox, sizeof(from_vms.username));
17452 from_vms.lastmsg = -1;
17453 open = 0;
17454
17455 /* open the mailbox state */
17456 if ((res = open_mailbox(&from_vms, vmu, from_folder_index)) < 0) {
17457 ast_log(LOG_WARNING, "Could not open mailbox %s\n", from_mailbox);
17458 res = -1;
17459 goto vm_forward_cleanup;
17460 }
17461
17462 open = 1;
17463
17464 if ((from_vms.lastmsg + 1) < num_msgs) {
17465 ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", from_folder, num_msgs);
17466 res = -1;
17467 goto vm_forward_cleanup;
17468 }
17469
17470 msg_nums = ast_alloca(sizeof(int) * num_msgs);
17471
17472 if ((res = message_range_and_existence_check(&from_vms, msg_ids, num_msgs, msg_nums, vmu) < 0)) {
17473 goto vm_forward_cleanup;
17474 }
17475
17476 /* Now we actually forward the messages */
17477 for (i = 0; i < num_msgs; i++) {
17478 int cur_msg = msg_nums[i];
17479 int duration = 0;
17480 const char *value;
17481
17482 make_file(from_vms.fn, sizeof(from_vms.fn), from_vms.curdir, cur_msg);
17483 snprintf(filename, sizeof(filename), "%s.txt", from_vms.fn);
17484 RETRIEVE(from_vms.curdir, cur_msg, vmu->mailbox, vmu->context);
17485 msg_cfg = ast_config_load(filename, config_flags);
17486 /* XXX This likely will not fail since we previously ensured that the
17487 * message we are looking for exists. However, there still could be some
17488 * circumstance where this fails, so atomicity is not guaranteed.
17489 */
17490 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17491 DISPOSE(from_vms.curdir, cur_msg);
17492 continue;
17493 }
17494 if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17495 duration = atoi(value);
17496 }
17497
17498 copy_message(NULL, vmu, from_folder_index, cur_msg, duration, to_vmu, vmfmts, from_vms.curdir, "", to_folder);
17499
17500 if (delete_old) {
17501 from_vms.deleted[cur_msg] = 1;
17502 }
17503 ast_config_destroy(msg_cfg);
17504 DISPOSE(from_vms.curdir, cur_msg);
17505 }
17506
17507 /* close mailbox */
17508 if ((res = close_mailbox(&from_vms, vmu) == ERROR_LOCK_PATH)) {
17509 res = -1;
17510 goto vm_forward_cleanup;
17511 }
17512 open = 0;
17513
17514vm_forward_cleanup:
17515 if (vmu && open) {
17516 close_mailbox(&from_vms, vmu);
17517 }
17518#ifdef IMAP_STORAGE
17519 if (vmu) {
17520 vmstate_delete(&from_vms);
17521 }
17522#endif
17523
17524 if (!res) {
17525 notify_new_state(to_vmu);
17526 }
17527
17528 free_user(vmu);
17529 free_user(to_vmu);
17530 return res;
17531}
17532
17533static int vm_msg_move(const char *mailbox,
17534 const char *context,
17535 size_t num_msgs,
17536 const char *oldfolder,
17537 const char *old_msg_ids [],
17538 const char *newfolder)
17539{
17540 struct vm_state vms;
17541 struct ast_vm_user *vmu = NULL, vmus;
17542 int old_folder_index;
17543 int new_folder_index;
17544 int open = 0;
17545 int res = 0;
17546 int i;
17547 int *old_msg_nums;
17548
17549 if (ast_strlen_zero(mailbox)) {
17550 ast_log(LOG_WARNING, "Cannot move message because no mailbox was specified\n");
17551 return -1;
17552 }
17553
17554 if (!num_msgs) {
17555 ast_log(LOG_WARNING, "Invalid number of messages specified to move: %zu\n", num_msgs);
17556 return -1;
17557 }
17558
17559 if (ast_strlen_zero(oldfolder) || ast_strlen_zero(newfolder)) {
17560 ast_log(LOG_WARNING, "Cannot move message because either oldfolder or newfolder was not specified\n");
17561 return -1;
17562 }
17563
17564 old_folder_index = get_folder_by_name(oldfolder);
17565 new_folder_index = get_folder_by_name(newfolder);
17566
17567 memset(&vmus, 0, sizeof(vmus));
17568 memset(&vms, 0, sizeof(vms));
17569
17570 if (old_folder_index == -1 || new_folder_index == -1) {
17571 return -1;
17572 }
17573
17574 if (!(vmu = find_user(&vmus, context, mailbox))) {
17575 return -1;
17576 }
17577
17578 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17579 vms.lastmsg = -1;
17580 open = 0;
17581
17582 /* open the mailbox state */
17583 if ((res = open_mailbox(&vms, vmu, old_folder_index)) < 0) {
17584 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17585 res = -1;
17586 goto vm_move_cleanup;
17587 }
17588
17589 open = 1;
17590
17591 if ((vms.lastmsg + 1) < num_msgs) {
17592 ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", oldfolder, num_msgs);
17593 res = -1;
17594 goto vm_move_cleanup;
17595 }
17596
17597 old_msg_nums = ast_alloca(sizeof(int) * num_msgs);
17598
17599 if ((res = message_range_and_existence_check(&vms, old_msg_ids, num_msgs, old_msg_nums, vmu)) < 0) {
17600 goto vm_move_cleanup;
17601 }
17602
17603 /* Now actually move the message */
17604 for (i = 0; i < num_msgs; ++i) {
17605 if (save_to_folder(vmu, &vms, old_msg_nums[i], new_folder_index, NULL, 0)) {
17606 res = -1;
17607 goto vm_move_cleanup;
17608 }
17609 vms.deleted[old_msg_nums[i]] = 1;
17610 }
17611
17612 /* close mailbox */
17613 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17614 res = -1;
17615 goto vm_move_cleanup;
17616 }
17617 open = 0;
17618
17619vm_move_cleanup:
17620 if (vmu && open) {
17621 close_mailbox(&vms, vmu);
17622 }
17623#ifdef IMAP_STORAGE
17624 if (vmu) {
17625 vmstate_delete(&vms);
17626 }
17627#endif
17628
17629 if (!res) {
17630 notify_new_state(vmu);
17631 }
17632
17633 free_user(vmu);
17634 return res;
17635}
17636
17637static int vm_msg_remove(const char *mailbox,
17638 const char *context,
17639 size_t num_msgs,
17640 const char *folder,
17641 const char *msgs[])
17642{
17643 struct vm_state vms;
17644 struct ast_vm_user *vmu = NULL, vmus;
17645 int folder_index;
17646 int open = 0;
17647 int res = 0;
17648 int i;
17649 int *msg_nums;
17650
17651 if (ast_strlen_zero(mailbox)) {
17652 ast_log(LOG_WARNING, "Cannot remove message because no mailbox was specified\n");
17653 return -1;
17654 }
17655
17656 if (!num_msgs) {
17657 ast_log(LOG_WARNING, "Invalid number of messages specified to remove: %zu\n", num_msgs);
17658 return -1;
17659 }
17660
17661 if (ast_strlen_zero(folder)) {
17662 ast_log(LOG_WARNING, "Cannot remove message because no folder was specified\n");
17663 return -1;
17664 }
17665
17666 memset(&vmus, 0, sizeof(vmus));
17667 memset(&vms, 0, sizeof(vms));
17668
17669 folder_index = get_folder_by_name(folder);
17670 if (folder_index == -1) {
17671 ast_log(LOG_WARNING, "Could not remove msgs from unknown folder %s\n", folder);
17672 return -1;
17673 }
17674
17675 if (!(vmu = find_user(&vmus, context, mailbox))) {
17676 ast_log(LOG_WARNING, "Can't find voicemail user to remove msg from (%s@%s)\n", mailbox, context);
17677 return -1;
17678 }
17679
17680 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17681 vms.lastmsg = -1;
17682 open = 0;
17683
17684 /* open the mailbox state */
17685 if ((res = open_mailbox(&vms, vmu, folder_index)) < 0) {
17686 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17687 res = -1;
17688 goto vm_remove_cleanup;
17689 }
17690
17691 open = 1;
17692
17693 if ((vms.lastmsg + 1) < num_msgs) {
17694 ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", folder, num_msgs);
17695 res = -1;
17696 goto vm_remove_cleanup;
17697 }
17698
17699 msg_nums = ast_alloca(sizeof(int) * num_msgs);
17700
17701 if ((res = message_range_and_existence_check(&vms, msgs, num_msgs, msg_nums, vmu)) < 0) {
17702 goto vm_remove_cleanup;
17703 }
17704
17705 for (i = 0; i < num_msgs; i++) {
17706 vms.deleted[msg_nums[i]] = 1;
17707 }
17708
17709 /* close mailbox */
17710 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17711 res = -1;
17712 ast_log(AST_LOG_ERROR, "Failed to close mailbox folder %s while removing msgs\n", folder);
17713 goto vm_remove_cleanup;
17714 }
17715 open = 0;
17716
17717vm_remove_cleanup:
17718 if (vmu && open) {
17719 close_mailbox(&vms, vmu);
17720 }
17721#ifdef IMAP_STORAGE
17722 if (vmu) {
17723 vmstate_delete(&vms);
17724 }
17725#endif
17726
17727 if (!res) {
17728 notify_new_state(vmu);
17729 }
17730
17731 free_user(vmu);
17732 return res;
17733}
17734
17735static int vm_msg_play(struct ast_channel *chan,
17736 const char *mailbox,
17737 const char *context,
17738 const char *folder,
17739 const char *msg_id,
17741{
17742 struct vm_state vms;
17743 struct ast_vm_user *vmu = NULL, vmus;
17744 int res = 0;
17745 int open = 0;
17746 int i;
17747 char filename[PATH_MAX];
17748 struct ast_config *msg_cfg;
17749 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17750 int duration = 0;
17751 const char *value;
17752
17753 if (ast_strlen_zero(mailbox)) {
17754 ast_log(LOG_WARNING, "Cannot play message because no mailbox was specified\n");
17755 return -1;
17756 }
17757
17758 if (ast_strlen_zero(folder)) {
17759 ast_log(LOG_WARNING, "Cannot play message because no folder was specified\n");
17760 return -1;
17761 }
17762
17763 if (ast_strlen_zero(msg_id)) {
17764 ast_log(LOG_WARNING, "Cannot play message because no message number was specified\n");
17765 return -1;
17766 }
17767
17768 memset(&vmus, 0, sizeof(vmus));
17769 memset(&vms, 0, sizeof(vms));
17770
17771 if (ast_strlen_zero(context)) {
17772 context = "default";
17773 }
17774
17775 if (!(vmu = find_user(&vmus, context, mailbox))) {
17776 return -1;
17777 }
17778
17779 i = get_folder_by_name(folder);
17780 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17781 vms.lastmsg = -1;
17782 if ((res = open_mailbox(&vms, vmu, i)) < 0) {
17783 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17784 goto play2_msg_cleanup;
17785 }
17786 open = 1;
17787
17788 if (message_range_and_existence_check(&vms, &msg_id, 1, &vms.curmsg, vmu)) {
17789 res = -1;
17790 goto play2_msg_cleanup;
17791 }
17792
17793 /* Find the msg */
17794 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
17795 snprintf(filename, sizeof(filename), "%s.txt", vms.fn);
17796 RETRIEVE(vms.curdir, vms.curmsg, vmu->mailbox, vmu->context);
17797
17798 msg_cfg = ast_config_load(filename, config_flags);
17799 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17800 DISPOSE(vms.curdir, vms.curmsg);
17801 res = -1;
17802 goto play2_msg_cleanup;
17803 }
17804 if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17805 duration = atoi(value);
17806 }
17807 ast_config_destroy(msg_cfg);
17808
17809#ifdef IMAP_STORAGE
17810 /*IMAP storage stores any prepended message from a forward
17811 * as a separate file from the rest of the message
17812 */
17813 if (!ast_strlen_zero(vms.introfn) && ast_fileexists(vms.introfn, NULL, NULL) > 0) {
17814 wait_file(chan, &vms, vms.introfn);
17815 }
17816#endif
17817 if (cb) {
17818 cb(chan, vms.fn, duration);
17819 } else if ((wait_file(chan, &vms, vms.fn)) < 0) {
17820 ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms.fn);
17821 } else {
17822 res = 0;
17823 }
17824
17825 vms.heard[vms.curmsg] = 1;
17826
17827 /* cleanup configs and msg */
17828 DISPOSE(vms.curdir, vms.curmsg);
17829
17830play2_msg_cleanup:
17831 if (vmu && open) {
17832 close_mailbox(&vms, vmu);
17833 }
17834
17835#ifdef IMAP_STORAGE
17836 if (vmu) {
17837 vmstate_delete(&vms);
17838 }
17839#endif
17840
17841 if (!res) {
17842 notify_new_state(vmu);
17843 }
17844
17845 free_user(vmu);
17846 return res;
17847}
17848
17849/* This is a workaround so that menuselect displays a proper description
17850 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
17851 */
17852
17854 .support_level = AST_MODULE_SUPPORT_CORE,
17855 .load = load_module,
17856 .unload = unload_module,
17857 .reload = reload,
17858 .optional_modules = "res_adsi,res_smdi",
ADSI Support (built upon Caller*ID)
#define ADSI_KEY_APPS
Definition: adsi.h:109
int ast_adsi_display(unsigned char *buf, int page, int line, int just, int wrap, char *col1, char *col2)
Loads a line of info into the display.
Definition: adsi.c:274
int ast_adsi_load_soft_key(unsigned char *buf, int key, const char *llabel, const char *slabel, char *ret, int data)
Creates "load soft key" parameters.
Definition: adsi.c:296
#define ADSI_MSG_DOWNLOAD
Definition: adsi.h:33
int ast_adsi_data_mode(unsigned char *buf)
Puts CPE in data mode.
Definition: adsi.c:219
int ast_adsi_unload_session(struct ast_channel *chan)
Definition: adsi.c:87
#define ADSI_JUST_CENT
Definition: adsi.h:114
#define ADSI_KEY_SKT
Definition: adsi.h:117
#define ADSI_MSG_DISPLAY
Definition: adsi.h:32
int ast_adsi_begin_download(struct ast_channel *chan, char *service, unsigned char *fdn, unsigned char *sec, int version)
Definition: adsi.c:32
#define ADSI_JUST_LEFT
Definition: adsi.h:112
int ast_adsi_set_line(unsigned char *buf, int page, int line)
Sets the current line and page.
Definition: adsi.c:285
#define ADSI_COMM_PAGE
Definition: adsi.h:107
int ast_adsi_input_control(unsigned char *buf, int page, int line, int display, int format, int just)
Set input information.
Definition: adsi.c:318
int ast_adsi_input_format(unsigned char *buf, int num, int dir, int wrap, char *format1, char *format2)
Set input format.
Definition: adsi.c:329
int ast_adsi_load_session(struct ast_channel *chan, unsigned char *app, int ver, int data)
Check if scripts for a given app are already loaded. Version may be -1, if any version is okay,...
Definition: adsi.c:76
int ast_adsi_end_download(struct ast_channel *chan)
Definition: adsi.c:43
int ast_adsi_download_disconnect(unsigned char *buf)
Disconnects (and hopefully saves) a downloaded script.
Definition: adsi.c:208
int ast_adsi_available(struct ast_channel *chan)
Returns non-zero if Channel does or might support ADSI.
Definition: adsi.c:263
int ast_adsi_transmit_message(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype)
Definition: adsi.c:98
#define ADSI_DIR_FROM_LEFT
Definition: adsi.h:120
int ast_adsi_set_keys(unsigned char *buf, unsigned char *keys)
Set which soft keys should be displayed.
Definition: adsi.c:307
int ast_adsi_voice_mode(unsigned char *buf, int when)
Puts CPE in voice mode.
Definition: adsi.c:252
#define AST_MODULE
jack_status_t status
Definition: app_jack.c:149
const char * str
Definition: app_jack.c:150
enum queue_result id
Definition: app_queue.c:1808
ast_mutex_t lock
Definition: app_sla.c:337
struct sla_ringing_trunk * last
Definition: app_sla.c:338
vm_option_flags
vm_box
vm_passwordlocation
vm_option_args
#define VMBOX_STRING_DATA_FORMAT
static char * vm_check_password_shell(char *command, char *buf, size_t len)
static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Vietnamese syntax for 'You have N messages' greeting.
static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move)
static char pagerfromstring[100]
static char vm_password[80]
static char vm_invalid_password[80]
#define VM_SEARCH
#define VM_ODBC_AUDIO_ON_DISK
static char * handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail users in the CLI.
static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Default English syntax for 'You have N messages' greeting.
static char listen_control_reverse_key[12]
#define MAX_VM_CONTEXT_LEN
#define EXISTS(a, b, c, d)
static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
static void adsi_password(struct ast_channel *chan)
static char serveremail[80]
#define VM_SAYCID
static int get_folder(struct ast_channel *chan, int start)
get_folder: Folder menu Plays "press 1 for INBOX messages" etc. Should possibly be internationalized
#define VMSTATE_MAX_MSG_ARRAY
static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
Prompts the user and records a voicemail to a mailbox.
static void adsi_folders(struct ast_channel *chan, int start, char *label)
static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
#define VM_FWDURGAUTO
static int mwi_handle_unsubscribe2(void *data)
#define VM_SAYDURATION
#define VOICEMAIL_DIR_MODE
static int make_file(char *dest, const int len, const char *dir, const int num)
Creates a file system path expression for a folder within the voicemail data folder and the appropria...
static int manager_voicemail_refresh(struct mansession *s, const struct message *m)
static int inprocess_hash_fn(const void *obj, const int flags)
static struct ast_custom_function vm_info_acf
static int pwdchange
static const char * ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
Encode a string according to the MIME rules for encoding strings that are not 7-bit clean or contain ...
static int show_messages_for_mailbox(struct ast_cli_args *a)
static int vm_msg_play(struct ast_channel *chan, const char *mailbox, const char *context, const char *folder, const char *msg_num, ast_vm_msg_play_cb cb)
#define VM_TEMPGREETWARN
static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag, const char *msg_id)
Creates the email file to be sent to indicate a new voicemail exists for a user.
static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
Manager list voicemail users command.
static int skipms
#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY
static int vm_delete(char *file)
Removes the voicemail sound and information file.
static char mailcmd[160]
static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
static int msg_id_incrementor
#define ERROR_LOCK_PATH
static const struct ast_vm_functions vm_table
#define CHUNKSIZE
static void free_zone(struct vm_zone *z)
static int vmminsecs
static void adsi_begin(struct ast_channel *chan, int *useadsi)
static char * handle_voicemail_show_mailbox(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void read_password_from_file(const char *secretfn, char *password, int passwordlen)
static int say_and_wait(struct ast_channel *chan, int num, const char *language)
static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
Loads the options specific to a voicemail user.
static void populate_defaults(struct ast_vm_user *vmu)
Sets default voicemail system options to a voicemail user.
static struct ast_taskprocessor * mwi_subscription_tps
static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Chinese (Taiwan)syntax for 'You have N messages' greeting.
#define VM_OPERATOR
static char vm_newpassword[80]
static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
static int vm_execmain(struct ast_channel *chan, const char *data)
static void free_user(struct ast_vm_user *vmu)
AO2_STRING_FIELD_CMP_FN(alias_mailbox_mapping, alias)
static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
@ OPT_MESSAGE_PRIORITY
@ OPT_PREPEND_MAILBOX
@ OPT_BEEP
@ OPT_BUSY_GREETING
@ OPT_RECORDGAIN
@ OPT_EARLYM_GREETING
@ OPT_SILENT_IF_GREET
@ OPT_UNAVAIL_GREETING
@ OPT_READONLY
@ OPT_SILENT
@ OPT_DTMFEXIT
@ OPT_MESSAGE_Urgent
@ OPT_AUTOPLAY
static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
#define UPDATE_MSG_ID(a, b, c, d, e, f)
static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
#define VM_STRING_HEADER_FORMAT
#define VM_MARK_URGENT
#define MAPPING_BUCKETS
static void generate_msg_id(char *dst)
Sets the destination string to a uniquely identifying msg_id string.
static struct ast_flags globalflags
static char listen_control_forward_key[12]
static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir, signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro)
static int vmauthenticate(struct ast_channel *chan, const char *data)
#define MSG_ID_LEN
static char vm_login[80]
static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
#define INTRO
#define VM_DIRECTFORWARD
static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Portuguese syntax for 'You have N messages' greeting.
static char vm_newuser[80]
#define VMBOX_STRING_HEADER_FORMAT
static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag, const char *msg_id)
static const char * ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
static ast_mutex_t poll_lock
static int check_mime(const char *str)
Check if the string would need encoding within the MIME standard, to avoid confusing certain mail sof...
static const char * vm_index_to_foldername(int id)
static unsigned char adsisec[4]
static int play_message_category(struct ast_channel *chan, const char *category)
static struct ast_vm_user * find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
Finds a voicemail user from the realtime engine.
static const char * substitute_escapes(const char *value)
static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
Performs a change of the voicemail passowrd in the realtime engine.
static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
Resets a user password to a specified password.
static int count_messages(struct ast_vm_user *vmu, char *dir)
Find all .txt files - even if they are not in sequence from 0000.
#define VM_SKIPAFTERCMD
static pthread_t poll_thread
static char zonetag[80]
static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
static int vm_exec(struct ast_channel *chan, const char *data)
static struct ast_vm_msg_snapshot * vm_msg_snapshot_alloc(void)
static char * pagersubject
static int manager_voicemail_forward(struct mansession *s, const struct message *m)
static int maxmsg
static char vmfmts[80]
static int manager_voicemail_move(struct mansession *s, const struct message *m)
#define COPY(a, b, c, d, e, f, g, h)
static void load_zonemessages(struct ast_config *cfg)
static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Top level method to invoke the language variant vm_browse_messages_XX function.
static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
static int vm_playmsgexec(struct ast_channel *chan, const char *data)
static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
static int passwordlocation
static int get_folder_ja(struct ast_channel *chan, int start)
static char vm_mismatch[80]
static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
static const struct ast_app_option vm_app_options[128]
static char * voicemail_app
#define COUNT(a, b)
static char * playmsg_app
static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Spanish syntax for 'You have N messages' greeting.
static void notify_new_state(struct ast_vm_user *vmu)
static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
#define ASTERISK_USERNAME
static int vm_newuser_setup(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
static int valid_config(const struct ast_config *cfg)
Check if configuration file is valid.
#define OPERATOR_EXIT
static char vm_pls_try_again[80]
static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback, int saycidnumber)
#define DISPOSE(a, b)
static int write_password_to_file(const char *secretfn, const char *password)
static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
#define VM_PBXSKIP
static int append_vmbox_info_astman(struct mansession *s, const struct message *m, struct ast_vm_user *vmu, const char *event_name, const char *actionid)
Append vmbox info string into given astman with event_name.
static int msg_create_from_file(struct ast_vm_recording_data *recdata)
static char * complete_voicemail_show_mailbox(struct ast_cli_args *a)
#define VOICEMAIL_CONFIG
static const struct ast_vm_greeter_functions vm_greeter_table
static void rename_file(char *sfn, char *dfn)
Renames a message in a mailbox folder.
static int move_message_from_mailbox(struct ast_cli_args *a)
static struct alias_mailbox_mapping * alias_mailbox_mapping_create(const char *alias, const char *mailbox)
static struct ast_vm_mailbox_snapshot * vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot)
struct ast_mwi_observer mwi_observer
static char ext_pass_check_cmd[128]
static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms)
static int append_vmu_info_astman(struct mansession *s, struct ast_vm_user *vmu, const char *event_name, const char *actionid)
Append vmu info string into given astman with event_name.
static char * pagerbody
static int separate_mailbox(char *mailbox_id, char **mailbox, char **context)
static int vm_msg_snapshot_create(struct ast_vm_user *vmu, struct vm_state *vms, struct ast_vm_mailbox_snapshot *mailbox_snapshot, int snapshot_index, int mailbox_index, int descending, enum ast_vm_snapshot_sort_val sort_val)
Create and store off all the msgs in an open mailbox.
static char dialcontext[AST_MAX_CONTEXT]
static int vmsayname_exec(struct ast_channel *chan, const char *data)
#define DEFAULT_POLL_FREQ
static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
#define VM_ENVELOPE
static int messagecount(const char *mailbox_id, const char *folder)
static int manager_voicemail_remove(struct mansession *s, const struct message *m)
static void stop_poll_thread(void)
AO2_STRING_FIELD_HASH_FN(alias_mailbox_mapping, alias)
#define MAXMSG
static char aliasescontext[MAX_VM_CONTEXT_LEN]
static int vm_sayname(struct ast_channel *chan, const char *mailbox_id)
#define DELETE(a, b, c, d)
static void print_mappings(void *v_obj, void *where, ao2_prnt_fn *prnt)
static struct ast_cli_entry cli_voicemail[]
#define DEFAULT_LISTEN_CONTROL_RESTART_KEY
static char listen_control_stop_key[12]
static char fromstring[100]
static int vm_browse_messages_ja(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Japanese syntax for 'You have N messages' greeting.
#define PWDCHANGE_INTERNAL
static char * handle_voicemail_forward_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
static void mwi_handle_unsubscribe(const char *id, struct ast_mwi_subscriber *sub)
static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
The handler for the change password option.
#define VM_FORCEGREET
#define VM_MESSAGEWRAP
static void * mb_poll_thread(void *data)
static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64]
static void run_externnotify(const char *context, const char *extension, const char *flag)
static int append_mailbox(const char *context, const char *box, const char *data)
static int vm_lock_path(const char *path)
Lock file path only return failure if ast_lock_path returns 'timeout', not if the path does not exist...
static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
#define DEFAULT_LISTEN_CONTROL_STOP_KEY
static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
static char emaildateformat[32]
static char vm_reenterpassword[80]
static void adsi_login(struct ast_channel *chan)
static int manager_get_mailbox_summary(struct mansession *s, const struct message *m)
static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain)
The advanced options within a message.
#define MAX_VM_MAILBOX_LEN
static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
static int check_password(struct ast_vm_user *vmu, char *password)
Check that password meets minimum required length.
static int play_message_by_id_helper(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, const char *msg_id)
static int load_config_force(int reload, int force)
Reload voicemail.conf.
static int saydurationminfo
#define VM_ATTACH
struct ao2_container * inprocess_container
static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg)
static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, const char *category, const char *flag)
static char * sayname_app
static void free_user_final(struct ast_vm_user *vmu)
static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
static void load_users(struct ast_config *cfg)
static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids[], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu)
common bounds checking and existence check for Voicemail API functions.
static int get_folder2(struct ast_channel *chan, char *fn, int start)
plays a prompt and waits for a keypress.
static int vm_msg_forward(const char *from_mailbox, const char *from_context, const char *from_folder, const char *to_mailbox, const char *to_context, const char *to_folder, size_t num_msgs, const char *msg_ids[], int delete_old)
#define ENDL
static void adsi_goodbye(struct ast_channel *chan)
#define HVSU_OUTPUT_FORMAT
static int maxgreet
#define SENDMAIL
#define VM_FORCENAME
static char ext_pass_cmd[128]
static int is_valid_dtmf(const char *key)
Determines if a DTMF key entered is valid.
static char pagerdateformat[32]
static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
The handler for 'record a temporary greeting'.
static double volgain
static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
static int get_folder_by_name(const char *name)
static int adsiver
#define HVSZ_OUTPUT_FORMAT
static int maxlogins
static char * emailsubject
static int inprocess_count(const char *context, const char *mailbox, int delta)
static struct ast_vm_user * find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
Finds a voicemail user from the users file or the realtime engine.
static struct ao2_container * mailbox_alias_mappings
static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
Creates a file system path expression for a folder within the voicemail data folder and the appropria...
static void apply_options(struct ast_vm_user *vmu, const char *options)
Destructively Parse options and apply.
static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
#define VM_MOVEHEARD
static char externnotify[160]
static int adsi_logo(unsigned char *buf)
static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
static unsigned char poll_thread_run
static char listen_control_restart_key[12]
static int add_message_id(struct ast_config *msg_cfg, char *dir, int msg, char *filename, char *id, size_t id_size, struct ast_vm_user *vmu, int folder)
static int play_message_by_id(struct ast_channel *chan, const char *mailbox, const char *context, const char *msg_id)
Finds a message in a specific mailbox by msg_id and plays it to the channel.
static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
basically mkdir -p $dest/$context/$ext/$folder
#define MAX_VM_MBOX_ID_LEN
static char * handle_voicemail_show_aliases(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail zones in the CLI.
@ FRIENDS_FOLDER
@ NEW_FOLDER
@ GREETINGS_FOLDER
@ FAMILY_FOLDER
@ WORK_FOLDER
@ OLD_FOLDER
static char * handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail zones in the CLI.
static void copy_plain_file(char *frompath, char *topath)
Copies a voicemail information (envelope) file.
static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vm_fmts, char *context, signed char record_gain, long *duration, struct vm_state *vms, char *flag)
presents the option to prepend to an existing message when forwarding it.
#define DEFAULT_LISTEN_CONTROL_PAUSE_KEY
#define MAX_NUM_CID_CONTEXTS
static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
static char * complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
@ OPT_PWLOC_USERSCONF
@ OPT_PWLOC_SPOOLDIR
@ OPT_PWLOC_VOICEMAILCONF
static char vm_prepend_timeout[80]
static char userscontext[AST_MAX_EXTENSION]
static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size, struct ast_vm_user *res_vmu, const char *context, const char *prefix, int skipuser, int max_logins, int silent)
@ OPT_ARG_RECORDGAIN
@ OPT_ARG_PLAYFOLDER
@ OPT_ARG_BEEP_TONE
@ OPT_ARG_ARRAY_SIZE
@ OPT_ARG_DTMFEXIT
#define RETRIEVE(a, b, c, d)
static int inprocess_cmp_fn(void *obj, void *arg, int flags)
static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
static char VM_SPOOL_DIR[PATH_MAX]
static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
#define SMDI_MWI_WAIT_TIMEOUT
static char callcontext[AST_MAX_CONTEXT]
static int maxdeletedmsg
#define VM_DELETE
static void mwi_handle_subscribe(const char *id, struct ast_mwi_subscriber *sub)
static int vm_intro_da(struct ast_channel *chan, struct vm_state *vms)
static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
static int show_mailbox_snapshot(struct ast_cli_args *a)
static char * emailbody
static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Italian syntax for 'You have N messages' greeting.
static int vm_msg_remove(const char *mailbox, const char *context, size_t num_msgs, const char *folder, const char *msgs[])
static char listen_control_pause_key[12]
static int copy(char *infile, char *outfile)
Utility function to copy a file.
static int silencethreshold
static void start_poll_thread(void)
#define MAX_MAIL_BODY_CONTENT_SIZE
static char * strip_control_and_high(const char *input, char *buf, size_t buflen)
Strips control and non 7-bit clean characters from input string.
#define RENAME(a, b, c, d, e, f, g, h)
static int load_module(void)
Load the module.
static char * handle_voicemail_move_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
static unsigned int poll_mailboxes
static int poll_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category, const char *flag)
#define MINPASSWORD
static int minpassword
static int forward_message_from_mailbox(struct ast_cli_args *a)
#define STORE(a, b, c, d, e, f, g, h, i, j, k)
static int vm_intro_is(struct ast_channel *chan, struct vm_state *vms)
static char * complete_voicemail_move_message(struct ast_cli_args *a, int maxpos)
static char * handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Reload voicemail configuration from the CLI.
static unsigned int poll_freq
static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
static int last_message_index(char *dir)
Determines the highest message number in use for a given user and mailbox folder.
#define VALID_DTMF
static ast_cond_t poll_cond
static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
static int unload_module(void)
static int load_config(int reload)
static void free_vm_zones(void)
Free the zones structure.
static int reload(void)
static int mwi_handle_subscribe2(void *data)
static int forward_message(struct ast_channel *chan, char *context, struct vm_state *vms, struct ast_vm_user *sender, char *fmt, int is_new_message, signed char record_gain, int urgent)
Sends a voicemail message to a mailbox recipient.
#define VM_SVMAIL
static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
#define PWDCHANGE_EXTERNAL
static struct ast_vm_user * find_or_create(const char *context, const char *box)
static char locale[20]
static unsigned char adsifdn[4]
static void queue_mwi_event(const char *channel_id, const char *box, int urgent, int new, int old)
static int manager_status_voicemail_user(struct mansession *s, const struct message *m)
#define VOICEMAIL_FILE_MODE
static int maxsilence
static char * handle_voicemail_remove_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define LAST_MSG_INDEX(a)
static void load_aliases(struct ast_config *cfg)
static int vmmaxsecs
#define tdesc
static struct ao2_container * alias_mailbox_mappings
static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
static char * addesc
static const struct ast_tm * vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
fill in *tm for current time according to the proper timezone, if any.
static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY
static char * show_users_realtime(int fd, const char *context)
static int remove_message_from_mailbox(struct ast_cli_args *a)
static int has_voicemail(const char *mailbox, const char *folder)
Determines if the given folder has messages.
static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag)
Sends email notification that a user has a new voicemail waiting for them.
#define VM_EMAIL_EXT_RECS
static int my_umask
static struct ast_vm_mailbox_snapshot * vm_mailbox_snapshot_create(const char *mailbox, const char *context, const char *folder, int descending, enum ast_vm_snapshot_sort_val sort_val, int combine_INBOX_and_OLD)
static int manager_match_mailbox(struct ast_mwi_state *mwi_state, void *data)
static int vm_intro_ja(struct ast_channel *chan, struct vm_state *vms)
#define MAXMSGLIMIT
#define ERROR_MAX_MSGS
static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
Check the given mailbox's message count.
static char exitcontext[AST_MAX_CONTEXT]
static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
static int vm_msg_move(const char *mailbox, const char *context, size_t num_msgs, const char *oldfolder, const char *old_msg_ids[], const char *newfolder)
static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
static int get_date(char *s, int len)
Gets the current date and time, as formatted string.
#define VM_REVIEW
static const char *const mailbox_folders[]
#define VM_ALLOCED
static int show_mailbox_details(struct ast_cli_args *a)
static const char * mbox(struct ast_vm_user *vmu, int id)
static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag, const char *dest_folder)
Copies a message from one mailbox to another.
static char * vmauthenticate_app
#define MSGFILE_LEN
static int vm_play_folder_name_ja(struct ast_channel *chan, char *box)
#define force_reload_config()
Forcibly reload voicemail.conf, even if it has not changed. This is necessary after running unit test...
static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
Sets a specific property value.
static char vm_passchanged[80]
static struct ast_vm_msg_snapshot * vm_msg_snapshot_destroy(struct ast_vm_msg_snapshot *msg_snapshot)
static char * voicemailmain_app
#define ALIASES_OUTPUT_FORMAT
static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Greek syntax for 'You have N messages' greeting.
static struct ast_smdi_interface * smdi_iface
static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
static void free_vm_users(void)
Free the users structure.
static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
#define var
Definition: ast_expr2f.c:605
if(!yyg->yy_init)
Definition: ast_expr2f.c:854
static int input(yyscan_t yyscanner)
Definition: ast_expr2f.c:1570
char * strsep(char **str, const char *delims)
char * mkdtemp(char *template_s)
char * strcasestr(const char *, const char *)
Asterisk main include file. File version handling, generic pbx functions.
#define PATH_MAX
Definition: asterisk.h:40
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_realloc(p, len)
A wrapper for realloc()
Definition: astmm.h:226
#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_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ CMP_MATCH
Definition: astobj2.h:1027
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
void ao2_container_unregister(const char *name)
Unregister a container for CLI stats and integrity check.
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
int ao2_container_register(const char *name, struct ao2_container *self, ao2_prnt_obj_fn *prnt_obj)
Register a container for CLI stats and integrity check.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_MULTIPLE
Definition: astobj2.h:1049
@ OBJ_UNLINK
Definition: astobj2.h:1039
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
void() ao2_prnt_fn(void *where, const char *fmt,...)
Print output.
Definition: astobj2.h:1435
char * ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown)
Definition: callerid.c:1273
int ast_callerid_parse(char *instr, char **name, char **location)
Destructively parse inbuf into name and location (or number)
Definition: callerid.c:1162
int ast_callerid_split(const char *src, char *name, int namelen, char *num, int numlen)
Definition: callerid.c:1292
enum cc_state state
Definition: ccss.c:399
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
static int priority
static char language[MAX_LANGUAGE]
Definition: chan_iax2.c:348
static const char type[]
Definition: chan_ooh323.c:109
static int transfer(void *data)
Definition: chan_pjsip.c:2133
charset
Definition: chan_unistim.c:336
General Asterisk PBX channel definitions.
void ast_channel_exten_set(struct ast_channel *chan, const char *value)
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
Definition: channel.c:3203
const char * ast_channel_name(const struct ast_channel *chan)
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2570
#define ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, assignedids, requestor, amaflag,...)
Create a channel structure.
Definition: channel.h:1299
void ast_channel_nativeformats_set(struct ast_channel *chan, struct ast_format_cap *value)
#define ast_channel_lock(chan)
Definition: channel.h:2970
struct ast_party_redirecting * ast_channel_redirecting(struct ast_channel *chan)
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)
@ AST_ADSI_UNAVAILABLE
Definition: channel.h:891
void ast_channel_set_rawreadformat(struct ast_channel *chan, struct ast_format *format)
#define MAX_LANGUAGE
Definition: channel.h:174
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
void ast_channel_set_rawwriteformat(struct ast_channel *chan, struct ast_format *format)
void ast_channel_set_readformat(struct ast_channel *chan, struct ast_format *format)
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:3006
#define AST_MAX_CONTEXT
Definition: channel.h:135
const char * ast_channel_language(const struct ast_channel *chan)
void ast_channel_context_set(struct ast_channel *chan, const char *value)
#define ast_dummy_channel_alloc()
Create a fake channel structure.
Definition: channel.h:1328
int ast_channel_setoption(struct ast_channel *channel, int option, void *data, int datalen, int block)
Sets an option on a channel.
Definition: channel.c:7443
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
void ast_channel_priority_set(struct ast_channel *chan, int value)
int ast_readstring(struct ast_channel *c, char *s, int len, int timeout, int rtimeout, char *enders)
Reads multiple digits.
Definition: channel.c:6575
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2834
void ast_channel_adsicpe_set(struct ast_channel *chan, enum ast_channel_adsicpe value)
int ast_indicate(struct ast_channel *chan, int condition)
Indicates condition of channel.
Definition: channel.c:4294
void ast_channel_tech_set(struct ast_channel *chan, const struct ast_channel_tech *value)
const char * ast_channel_exten(const struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition: channel.h:2971
#define AST_MAX_EXTENSION
Definition: channel.h:134
void ast_channel_set_writeformat(struct ast_channel *chan, struct ast_format *format)
void ast_party_caller_init(struct ast_party_caller *init)
Initialize the given caller structure.
Definition: channel.c:2005
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_DOWN
Definition: channelstate.h:36
@ 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
#define RESULT_SUCCESS
Definition: cli.h:40
#define ESS(x)
Definition: cli.h:59
@ CLI_HANDLER
Definition: cli.h:154
@ 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
static struct progalias aliases[]
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
static char * config_filename
Definition: extconf.c:2120
long int flag
Definition: f2c.h:83
Generic File Format Support. Should be included by clients of the file handling routines....
off_t ast_tellstream(struct ast_filestream *fs)
Tell where we are in a stream.
Definition: file.c:1093
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
Seeks into stream.
Definition: file.c:1083
int ast_filerename(const char *oldname, const char *newname, const char *fmt)
Renames a file.
Definition: file.c:1154
struct ast_filestream * ast_readfile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
Starts reading from a file.
Definition: file.c:1379
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1301
FILE * ast_file_mkftemp(char *template_name, mode_t mode)
same as mkstemp, but return a FILE
Definition: file.c:187
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1886
char * ast_format_str_reduce(char *fmts)
Definition: file.c:1902
int ast_filecopy(const char *oldname, const char *newname, const char *fmt)
Copies a file.
Definition: file.c:1159
int ast_ratestream(struct ast_filestream *fs)
Return the sample rate of the stream's format.
Definition: file.c:1098
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1119
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1137
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1149
#define AST_DIGIT_ANY
Definition: file.h:48
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1848
Media Format Cache API.
struct ast_format * ast_format_gsm
Built-in cached gsm format.
Definition: format_cache.c:96
@ AST_FORMAT_CAP_FLAG_DEFAULT
Definition: format_cap.h:38
#define ast_format_cap_append(cap, format, framing)
Add format capability to capabilities structure.
Definition: format_cap.h:99
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
static const char name[]
Definition: format_mp3.c:68
static int exists(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_logic.c:185
static struct ast_threadstorage buf2
static struct ast_threadstorage buf1
static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
Definition: manager.c:2028
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:1986
void ast_manager_publish_event(const char *type, int class_type, struct ast_json *obj)
Publish an event to AMI.
Definition: manager.c:638
void astman_send_list_complete_start(struct mansession *s, const struct message *m, const char *event_name, int count)
Start the list complete event.
Definition: manager.c:2064
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:2018
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:1647
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:2072
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:1907
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:7697
#define SCOPE_EXIT_RTN(...)
#define SCOPE_EXIT_RTN_VALUE(__return_value,...)
#define SCOPE_EXIT_LOG_RTN_VALUE(__value, __log_level,...)
#define SCOPE_ENTER(level,...)
#define SCOPE_CALL_WITH_INT_RESULT(level, __funcname,...)
#define SCOPE_CALL(level, __funcname,...)
#define SCOPE_EXIT_LOG_RTN(__log_level,...)
#define ast_trace(level,...)
static char prefix[MAX_PREFIX]
Definition: http.c:144
const char * ext
Definition: http.c:150
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence_ms, const char *path, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists)
Record a file based on input from a channel This function will play "auth-thankyou" upon successful r...
Definition: main/app.c:2149
int ast_app_messagecount(const char *mailbox_id, const char *folder)
Get the number of messages in a given mailbox folder.
Definition: main/app.c:645
int ast_control_streamfile(struct ast_channel *chan, const char *file, const char *fwd, const char *rev, const char *stop, const char *pause, const char *restart, int skipms, long *offsetms)
Stream a file with fast forward, pause, reverse, restart.
Definition: main/app.c:1465
@ AST_LOCK_TIMEOUT
void ast_vm_greeter_unregister(const char *module_name)
Unregister the specified voicemail greeter provider.
Definition: main/app.c:511
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
Plays a stream and gets DTMF data from a channel.
Definition: main/app.c:188
ast_vm_snapshot_sort_val
@ AST_VM_SNAPSHOT_SORT_BY_ID
@ AST_VM_SNAPSHOT_SORT_BY_TIME
int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime_sec, char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence_ms)
Record a file based on input frm a channel. Recording is performed in 'prepend' mode which works a li...
Definition: main/app.c:2159
enum AST_LOCK_RESULT ast_lock_path(const char *path)
Lock a filesystem path.
Definition: main/app.c:2614
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
int ast_app_has_voicemail(const char *mailboxes, const char *folder)
Determine if a given mailbox has any voicemail If folder is NULL, defaults to "INBOX"....
Definition: main/app.c:582
#define VM_GREETER_MODULE_VERSION
int ast_safe_fork(int stop_reaper)
Common routine to safely fork without a chance of a signal handler firing badly in the child.
Definition: main/app.c:3207
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define VM_MODULE_VERSION
struct ast_vm_mailbox_snapshot * ast_vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot)
destroy a snapshot
Definition: main/app.c:675
int ast_safe_system(const char *s)
Safely spawn an OS shell command while closing file descriptors.
Definition: extconf.c:829
void ast_vm_unregister(const char *module_name)
Unregister the specified voicemail provider.
Definition: main/app.c:400
#define ast_vm_register(vm_table)
See __ast_vm_register()
int ast_app_inboxcount(const char *mailboxes, int *newmsgs, int *oldmsgs)
Determine number of new/old messages in a mailbox.
Definition: main/app.c:604
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
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
void() ast_vm_msg_play_cb(struct ast_channel *chan, const char *playfile, int duration)
Voicemail playback callback function definition.
@ AST_RECORD_IF_EXISTS_OVERWRITE
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the 'nonstandard' argument separation process for an application.
int ast_app_inboxcount2(const char *mailboxes, int *urgentmsgs, int *newmsgs, int *oldmsgs)
Determine number of urgent/new/old messages in a mailbox.
Definition: main/app.c:619
#define ast_vm_greeter_register(vm_table)
See __ast_vm_greeter_register()
struct ast_vm_mailbox_snapshot * ast_vm_mailbox_snapshot_create(const char *mailbox, const char *context, const char *folder, int descending, enum ast_vm_snapshot_sort_val sort_val, int combine_INBOX_and_OLD)
Create a snapshot of a mailbox which contains information about every msg.
Definition: main/app.c:661
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
int ast_unlock_path(const char *path)
Unlock a path.
Definition: main/app.c:2630
void ast_close_fds_above_n(int n)
Common routine for child processes, to close all fds prior to exec(2)
Definition: main/app.c:3202
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
int ast_update2_realtime(const char *family,...) attribute_sentinel
Update realtime configuration.
Definition: main/config.c:3915
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: extconf.c:3274
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
Definition: main/config.c:3769
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3842
int ast_config_text_file_save(const char *filename, const struct ast_config *cfg, const char *generator)
Save a config text file preserving the pre 13.2 behavior.
Definition: main/config.c:2868
@ CONFIG_FLAG_NOCACHE
@ CONFIG_FLAG_WITHCOMMENTS
@ CONFIG_FLAG_FILEUNCHANGED
void ast_category_append(struct ast_config *config, struct ast_category *category)
Appends a category to a config.
Definition: extconf.c:2833
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: extconf.c:1177
int ast_variable_update(struct ast_category *category, const char *variable, const char *value, const char *match, unsigned int object)
Update variable value within a config.
Definition: main/config.c:1645
struct ast_category * ast_category_new(const char *name, const char *in_file, int lineno)
Create a category.
Definition: extconf.c:2788
#define ast_variable_new(name, value, filename)
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
Definition: main/config.c:3796
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3750
int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Destroy realtime configuration.
Definition: main/config.c:3996
void ast_category_destroy(struct ast_category *cat)
Definition: extconf.c:2845
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:869
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
Definition: main/config.c:3879
int ast_store_realtime(const char *family,...) attribute_sentinel
Create realtime configuration.
Definition: main/config.c:3960
const char * ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
Retrieve a configuration variable within the configuration set.
Definition: main/config.c:859
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3726
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name, const char *filter)
Retrieve a category if it exists.
Definition: main/config.c:1205
#define AST_OPTION_RXGAIN
@ AST_CONTROL_PROGRESS
struct ast_frame ast_null_frame
Definition: main/frame.c:79
Support for logging to various files, console and syslog Configuration in file logger....
#define AST_LOG_WARNING
#define AST_LOG_ERROR
#define DEBUG_ATLEAST(level)
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_DEBUG
#define LOG_ERROR
#define AST_LOG_NOTICE
#define ast_verb(level,...)
#define LOG_NOTICE
#define LOG_WARNING
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
#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_HEAD_NOLOCK_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:346
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:599
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#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
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
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
int ast_strftime_locale(char *buf, size_t len, const char *format, const struct ast_tm *tm, const char *locale)
Definition: localtime.c:2452
Asterisk locking-related definitions:
#define AST_PTHREADT_NULL
Definition: lock.h:70
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:210
#define ast_mutex_init(pmutex)
Definition: lock.h:190
#define ast_mutex_unlock(a)
Definition: lock.h:194
int ast_atomic_fetchadd_int(volatile int *p, int v)
Atomically add v to *p and return the previous value of *p.
Definition: lock.h:761
pthread_cond_t ast_cond_t
Definition: lock.h:182
#define ast_mutex_destroy(a)
Definition: lock.h:192
#define ast_mutex_lock(a)
Definition: lock.h:193
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:524
#define ast_cond_signal(cond)
Definition: lock.h:207
size_t current
Definition: main/cli.c:113
int errno
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define EVENT_FLAG_REPORTING
Definition: manager.h:84
#define EVENT_FLAG_SYSTEM
Definition: manager.h:75
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:192
#define EVENT_FLAG_CALL
Definition: manager.h:76
#define EVENT_FLAG_USER
Definition: manager.h:81
Asterisk module definitions.
@ AST_MODFLAG_DEFAULT
Definition: module.h:329
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#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
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Asterisk MWI API.
#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
void ast_mwi_state_callback_all(on_mwi_state handler, void *data)
For each managed mailbox call the given handler.
Definition: mwi.c:338
void ast_mwi_state_callback_subscribed(on_mwi_state handler, void *data)
For each managed mailbox that has a subscriber call the given handler.
Definition: mwi.c:348
int ast_mwi_add_observer(struct ast_mwi_observer *observer)
Add an observer to receive MWI state related events.
Definition: mwi.c:301
int ast_delete_mwi_state_full(const char *mailbox, const char *context, struct ast_eid *eid)
Delete MWI state cached by stasis with all parameters.
Definition: mwi.c:404
void ast_mwi_remove_observer(struct ast_mwi_observer *observer)
Remove an MWI state observer.
Definition: mwi.c:307
struct ast_mwi_state * ast_mwi_subscriber_data(struct ast_mwi_subscriber *sub)
Retrieves the state data object associated with the MWI subscriber.
Definition: mwi.c:269
def info(msg)
def from_mailbox(key, val, section, pjsip, nmapped)
#define MAXHOSTNAMELEN
Definition: network.h:69
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
const char * ast_config_AST_SPOOL_DIR
Definition: options.c:154
Core PBX routines and definitions.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:4190
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition: pbx_app.c:471
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1559
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:8796
int ast_canmatch_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Looks for a valid matching extension.
Definition: pbx.c:4205
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
struct ast_app * pbx_findapp(const char *app)
Look up an application.
Definition: ael_main.c:165
static char email[80]
Definition: pbx_dundi.c:215
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
static char intro[ADSI_MAX_INTRO][20]
Definition: res_adsi.c:65
static struct stasis_rest_handlers mailboxes
REST handler for /api-docs/mailboxes.json.
static char user[512]
struct stasis_forward * sub
Definition: res_corosync.c:240
ODBC resource manager.
int ast_odbc_prepare(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Prepares a SQL query on a statement.
Definition: res_odbc.c:454
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:804
struct ast_str * ast_odbc_print_errors(SQLSMALLINT handle_type, SQLHANDLE handle, const char *operation)
Shortcut for printing errors to logs after a failed SQL operation.
Definition: res_odbc.c:515
SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT(*exec_cb)(struct odbc_obj *obj, void *data), void *data)
Executes an non prepared statement and returns the resulting statement handle.
Definition: res_odbc.c:360
SQLRETURN ast_odbc_execute_sql(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Execute a unprepared SQL query.
Definition: res_odbc.c:469
SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
Prepares, executes, and returns the resulting statement handle.
Definition: res_odbc.c:398
#define ast_odbc_request_obj(name, check)
Get a ODBC connection object.
Definition: res_odbc.h:120
static int debug
Global debug status.
Definition: res_xmpp.c:570
#define NULL
Definition: resample.c:96
Say numbers and dates (maybe words one day too)
int ast_say_character_str(struct ast_channel *chan, const char *num, const char *ints, const char *lang, enum ast_say_case_sensitivity sensitivity)
function to pronounce character and phonetic strings
Definition: channel.c:8297
int ast_say_number(struct ast_channel *chan, int num, const char *ints, const char *lang, const char *options)
says a number
Definition: channel.c:8261
SAY_EXTERN int(* ast_say_date_with_format)(struct ast_channel *chan, time_t t, const char *ints, const char *lang, const char *format, const char *timezone) SAY_INIT(ast_say_date_with_format)
Definition: say.h:208
int ast_say_counted_noun(struct ast_channel *chan, int num, const char *noun)
@ AST_SAY_CASE_NONE
Definition: say.h:182
int ast_say_counted_adjective(struct ast_channel *chan, int num, const char *adjective, const char *gender)
int ast_say_digit_str(struct ast_channel *chan, const char *num, const char *ints, const char *lang)
says digits of a string
Definition: channel.c:8285
SMDI support for Asterisk.
struct ast_smdi_interface * ast_smdi_interface_find(const char *iface_name)
Find an SMDI interface with the specified name.
Definition: res_smdi.c:569
int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
Set the MWI indicator for a mailbox.
Definition: res_smdi.c:315
struct ast_smdi_mwi_message * ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout, const char *station)
Definition: res_smdi.c:562
int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
Unset the MWI indicator for a mailbox.
Definition: res_smdi.c:320
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
String manipulation functions.
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
int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
Parse a time (integer) string.
Definition: utils.c:2446
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
int ast_build_string(char **buffer, size_t *space, const char *fmt,...)
Build a string in a buffer, designed to be called repeatedly.
Definition: utils.c:2167
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"....
Definition: utils.c:2216
#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
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:909
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
ast_app: A registered application
Definition: pbx_app.c:45
Structure to describe a channel "technology", ie a channel driver See for examples:
Definition: channel.h:648
int(*const write)(struct ast_channel *chan, struct ast_frame *frame)
Write a frame, in standard format (see frame.h)
Definition: channel.h:770
struct ast_format_cap * capabilities
Definition: channel.h:652
Main Channel structure associated with a channel.
descriptor for a cli entry.
Definition: cli.h:171
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
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Definition: mod_format.h:101
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
Data structure associated with a single frame of data.
Abstract JSON element (object, array, string, int, ...).
Structure for mutex and tracking information.
Definition: lock.h:139
MWI state event interface.
Definition: mwi.h:248
void(* on_subscribe)(const char *mailbox, struct ast_mwi_subscriber *sub)
Raised when MWI is being subscribed.
Definition: mwi.h:255
The structure that contains MWI state.
Definition: mwi.h:455
int urgent_msgs
Definition: mwi.h:464
int old_msgs
Definition: mwi.h:460
int new_msgs
Definition: mwi.h:459
const ast_string_field uniqueid
Definition: mwi.h:458
Caller Party information.
Definition: channel.h:420
struct ast_party_id id
Caller party ID.
Definition: channel.h:422
struct ast_party_name name
Subscriber name.
Definition: channel.h:342
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:344
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:281
char * str
Subscriber name (Malloced)
Definition: channel.h:266
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:299
char * str
Subscriber phone number (Malloced)
Definition: channel.h:293
An SMDI message waiting indicator message.
Definition: smdi.h:51
char cause[SMDI_MWI_FAIL_CAUSE_LEN+1]
Definition: smdi.h:54
char fwd_st[SMDI_MAX_STATION_NUM_LEN+1]
Definition: smdi.h:53
Support for dynamic strings.
Definition: strings.h:623
A ast_taskprocessor structure is a singleton by name.
Definition: taskprocessor.c:69
Structure for variables, used for configurations and for channel variables.
struct ast_variable * next
Voicemail function table definition.
unsigned int module_version
The version of this function table.
const char * module_name
The name of the module that provides the voicemail functionality.
Voicemail greeter function table definition.
unsigned int module_version
The version of this function table.
const char * module_name
The name of the module that provides the voicemail greeter functionality.
struct ast_vm_mailbox_snapshot::@185 * snapshots
const ast_string_field origtime
struct ast_vm_msg_snapshot::@184 msg
const ast_string_field msg_id
Structure used for ast_copy_recording_to_vm in order to cleanly supply data needed for making the rec...
const ast_string_field recording_file
const ast_string_field call_callerchan
const ast_string_field context
const ast_string_field folder
const ast_string_field call_callerid
const ast_string_field call_context
const ast_string_field recording_ext
const ast_string_field call_extension
const ast_string_field mailbox
char mailbox[MAX_VM_MBOX_ID_LEN]
char exit[80]
char fromstring[100]
char pager[80]
char * emailbody
int passwordlocation
char * emailsubject
char callback[80]
struct ast_vm_user::@82 list
char password[80]
char uniqueid[80]
char serveremail[80]
char attachfmt[20]
double volgain
char zonetag[80]
char locale[20]
char context[MAX_VM_CONTEXT_LEN]
char language[MAX_LANGUAGE]
unsigned int flags
char dialout[80]
struct ast_vm_user::@84 list
char fullname[80]
All configuration options for http media cache.
structure to hold extensions
char mailbox[0]
char * context
Options for leaving voicemail with the voicemail() application.
Definition: app_minivm.c:681
signed char record_gain
Definition: app_minivm.c:683
unsigned int flags
Definition: app_minivm.c:682
In case you didn't read that giant block of text above the mansession_session struct,...
Definition: manager.c:327
Number structure.
Definition: app_followme.c:157
ODBC container.
Definition: res_odbc.h:46
struct odbc_obj * next
Definition: res_odbc.h:55
SQLHDBC con
Definition: res_odbc.h:47
structure to hold users read from users.conf
list of users found in the config file
Definition: ast_expr2.c:325
int urgentmessages
char username[80]
char intro[PATH_MAX]
char curdir[PATH_MAX]
char context[80]
char curbox[80]
int * deleted
char fn[PATH_MAX]
int dh_arraysize
char vmbox[PATH_MAX]
struct vm_zone::@83 list
char name[80]
char timezone[80]
struct vm_zone::@85 list
char msg_format[512]
int value
Definition: syslog.c:37
An API for managing task processing threads that can be shared across modules.
struct ast_taskprocessor * ast_taskprocessor_get(const char *name, enum ast_tps_options create)
Get a reference to a taskprocessor with the specified name and create the taskprocessor if necessary.
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
int ast_taskprocessor_push(struct ast_taskprocessor *tps, int(*task_exe)(void *datap), void *datap) attribute_warn_unused_result
Push a task into the specified taskprocessor queue and signal the taskprocessor thread.
#define AST_TASKPROCESSOR_HIGH_WATER_LEVEL
Definition: taskprocessor.h:64
int ast_taskprocessor_alert_set_levels(struct ast_taskprocessor *tps, long low_water, long high_water)
Set the high and low alert water marks of the given taskprocessor queue.
Test Framework API.
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:189
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
int done
Definition: test_amihooks.c:48
const char * args
static struct test_options options
static struct test_val a
static struct test_val d
static struct test_val c
#define TEST_EXTENSION
Definition: test_message.c:47
#define TEST_CONTEXT
Definition: test_message.c:46
Definitions to aid in the use of thread local storage.
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
Time-related functions and macros.
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
Definition: time.h:282
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2282
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
char * usage
Definition: utils/frame.c:37
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
int ast_file_is_readable(const char *filename)
Test that a file exists and is readable by the effective user.
Definition: utils.c:3107
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:584
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define ast_clear_flag(p, flag)
Definition: utils.h:77
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