Asterisk - The Open Source Telephony Project GIT-master-0a46be9
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
785};
786
801});
802
803static const char * const mailbox_folders[] = {
804#ifdef IMAP_STORAGE
805 imapfolder,
806#else
807 "INBOX",
808#endif
809 "Old",
810 "Work",
811 "Family",
812 "Friends",
813 "Cust1",
814 "Cust2",
815 "Cust3",
816 "Cust4",
817 "Cust5",
818 "Deleted",
819 "Urgent",
820};
821
822/*!
823 * \brief Reload voicemail.conf
824 * \param reload Whether this is a reload as opposed to module load
825 * \param force Forcefully reload the config, even it has not changed
826 * \retval 0 on success, nonzero on failure
827 */
828static int load_config_force(int reload, int force);
829
830/*! \brief Forcibly reload voicemail.conf, even if it has not changed.
831 * This is necessary after running unit tests. */
832#define force_reload_config() load_config_force(1, 1)
833
834static int load_config(int reload);
835#ifdef TEST_FRAMEWORK
836static int load_config_from_memory(int reload, struct ast_config *cfg);
837#endif
838static int actual_load_config(int reload, struct ast_config *cfg);
839
840/*! \page vmlang Voicemail Language Syntaxes Supported
841
842 \par Syntaxes supported, not really language codes.
843 \arg \b en - English
844 \arg \b de - German
845 \arg \b es - Spanish
846 \arg \b fr - French
847 \arg \b it - Italian
848 \arg \b nl - Dutch
849 \arg \b pt - Portuguese
850 \arg \b pt_BR - Portuguese (Brazil)
851 \arg \b gr - Greek
852 \arg \b no - Norwegian
853 \arg \b se - Swedish
854 \arg \b tw - Chinese (Taiwan)
855 \arg \b ua - Ukrainian
856
857German requires the following additional soundfile:
858\arg \b 1F einE (feminine)
859
860Spanish requires the following additional soundfile:
861\arg \b 1M un (masculine)
862
863Dutch, Portuguese & Spanish require the following additional soundfiles:
864\arg \b vm-INBOXs singular of 'new'
865\arg \b vm-Olds singular of 'old/heard/read'
866
867NB these are plural:
868\arg \b vm-INBOX nieuwe (nl)
869\arg \b vm-Old oude (nl)
870
871Polish uses:
872\arg \b vm-new-a 'new', feminine singular accusative
873\arg \b vm-new-e 'new', feminine plural accusative
874\arg \b vm-new-ych 'new', feminine plural genitive
875\arg \b vm-old-a 'old', feminine singular accusative
876\arg \b vm-old-e 'old', feminine plural accusative
877\arg \b vm-old-ych 'old', feminine plural genitive
878\arg \b digits/1-a 'one', not always same as 'digits/1'
879\arg \b digits/2-ie 'two', not always same as 'digits/2'
880
881Swedish uses:
882\arg \b vm-nytt singular of 'new'
883\arg \b vm-nya plural of 'new'
884\arg \b vm-gammalt singular of 'old'
885\arg \b vm-gamla plural of 'old'
886\arg \b digits/ett 'one', not always same as 'digits/1'
887
888Norwegian uses:
889\arg \b vm-ny singular of 'new'
890\arg \b vm-nye plural of 'new'
891\arg \b vm-gammel singular of 'old'
892\arg \b vm-gamle plural of 'old'
893
894Dutch also uses:
895\arg \b nl-om 'at'?
896
897Spanish also uses:
898\arg \b vm-youhaveno
899
900Italian requires the following additional soundfile:
901
902For vm_intro_it:
903\arg \b vm-nuovo new
904\arg \b vm-nuovi new plural
905\arg \b vm-vecchio old
906\arg \b vm-vecchi old plural
907
908Japanese requires the following additional soundfile:
909\arg \b jp-arimasu there is
910\arg \b jp-arimasen there is not
911\arg \b jp-oshitekudasai please press
912\arg \b jp-ni article ni
913\arg \b jp-ga article ga
914\arg \b jp-wa article wa
915\arg \b jp-wo article wo
916
917Chinese (Taiwan) requires the following additional soundfile:
918\arg \b vm-tong A class-word for call (tong1)
919\arg \b vm-ri A class-word for day (ri4)
920\arg \b vm-you You (ni3)
921\arg \b vm-haveno Have no (mei2 you3)
922\arg \b vm-have Have (you3)
923\arg \b vm-listen To listen (yao4 ting1)
924
925
926\note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
927spelled among others when you have to change folder. For the above reasons, vm-INBOX
928and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
929
930*/
931
932#define MAX_VM_MBOX_ID_LEN (AST_MAX_EXTENSION)
933#define MAX_VM_CONTEXT_LEN (AST_MAX_CONTEXT)
934/* MAX_VM_MAILBOX_LEN allows enough room for the '@' and NULL terminator */
935#define MAX_VM_MAILBOX_LEN (MAX_VM_MBOX_ID_LEN + MAX_VM_CONTEXT_LEN)
936
937/*! Structure for linked list of users
938 * Use ast_vm_user_destroy() to free one of these structures. */
939struct ast_vm_user {
940 char context[MAX_VM_CONTEXT_LEN];/*!< Voicemail context */
941 char mailbox[MAX_VM_MBOX_ID_LEN];/*!< Mailbox id, unique within vm context */
942 char password[80]; /*!< Secret pin code, numbers only */
943 char fullname[80]; /*!< Full name, for directory app */
944 char *email; /*!< E-mail address */
945 char *emailsubject; /*!< E-mail subject */
946 char *emailbody; /*!< E-mail body */
947 char pager[80]; /*!< E-mail address to pager (no attachment) */
948 char serveremail[80]; /*!< From: Mail address */
949 char fromstring[100]; /*!< From: Username */
950 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
951 char zonetag[80]; /*!< Time zone */
952 char locale[20]; /*!< The locale (for presentation of date/time) */
953 char callback[80];
954 char dialout[80];
955 char uniqueid[80]; /*!< Unique integer identifier */
956 char exit[80];
957 char attachfmt[20]; /*!< Attachment format */
958 unsigned int flags; /*!< VM_ flags */
959 int saydurationm;
960 int minsecs; /*!< Minimum number of seconds per message for this mailbox */
961 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
962 int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
963 int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
964 int passwordlocation; /*!< Storage location of the password */
965#ifdef IMAP_STORAGE
966 char imapserver[48]; /*!< IMAP server address */
967 char imapport[8]; /*!< IMAP server port */
968 char imapflags[128]; /*!< IMAP optional flags */
969 char imapuser[80]; /*!< IMAP server login */
970 char imappassword[80]; /*!< IMAP server password if authpassword not defined */
971 char imapfolder[64]; /*!< IMAP voicemail folder */
972 char imapvmshareid[80]; /*!< Shared mailbox ID to use rather than the dialed one */
973 int imapversion; /*!< If configuration changes, use the new values */
974#endif
975 double volgain; /*!< Volume gain for voicemails sent via email */
977};
978
979/*! Voicemail time zones */
980struct vm_zone {
982 char name[80];
983 char timezone[80];
984 char msg_format[512];
985};
986
987#define VMSTATE_MAX_MSG_ARRAY 256
988
989/*! Voicemail mailbox state */
990struct vm_state {
991 char curbox[80];
992 char username[80];
993 char context[80];
994 char curdir[PATH_MAX];
995 char vmbox[PATH_MAX];
996 char fn[PATH_MAX];
997 char intro[PATH_MAX];
998 int *deleted;
999 int *heard;
1000 int dh_arraysize; /* used for deleted / heard allocation */
1001 int curmsg;
1002 int lastmsg;
1003 int newmessages;
1004 int oldmessages;
1005 int urgentmessages;
1006 int starting;
1007 int repeats;
1008#ifdef IMAP_STORAGE
1010 int updated; /*!< decremented on each mail check until 1 -allows delay */
1011 long *msgArray;
1012 unsigned msg_array_max;
1013 MAILSTREAM *mailstream;
1014 int vmArrayIndex;
1015 char imapuser[80]; /*!< IMAP server login */
1016 char imapfolder[64]; /*!< IMAP voicemail folder */
1017 char imapserver[48]; /*!< IMAP server address */
1018 char imapport[8]; /*!< IMAP server port */
1019 char imapflags[128]; /*!< IMAP optional flags */
1020 int imapversion;
1021 int interactive;
1022 char introfn[PATH_MAX]; /*!< Name of prepended file */
1023 unsigned int quota_limit;
1024 unsigned int quota_usage;
1025 struct vm_state *persist_vms;
1026#endif
1027};
1028
1029#ifdef ODBC_STORAGE
1030static char odbc_database[80] = "asterisk";
1031static char odbc_table[80] = "voicemessages";
1032size_t odbc_table_len = sizeof(odbc_table);
1033#define COUNT(a, b) odbc_count_messages(a,b)
1034#define LAST_MSG_INDEX(a) odbc_last_message_index(a)
1035#define RETRIEVE(a,b,c,d) odbc_retrieve_message(a,b)
1036#define DISPOSE(a,b) odbc_remove_files(a,b)
1037#define STORE(a,b,c,d,e,f,g,h,i,j,k) odbc_store_message(a,b,c,d)
1038#define EXISTS(a,b,c,d) (odbc_message_exists(a,b))
1039#define RENAME(a,b,c,d,e,f,g,h) (odbc_rename_message(a,b,c,d,e,f))
1040#define COPY(a,b,c,d,e,f,g,h) (odbc_copy_message(a,b,c,d,e,f))
1041#define DELETE(a,b,c,d) (odbc_delete_message(a,b))
1042#define UPDATE_MSG_ID(a, b, c, d, e, f) (odbc_update_msg_id((a), (b), (c)))
1043#else
1044#ifdef IMAP_STORAGE
1045#define DISPOSE(a,b) (imap_remove_file(a,b))
1046#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))
1047#define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
1048#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
1049#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
1050#define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
1051#define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
1052#define UPDATE_MSG_ID(a, b, c, d, e, f) (vm_imap_update_msg_id((a), (b), (c), (d), (e), (f)))
1053#else
1054#define COUNT(a, b) count_messages(a,b)
1055#define LAST_MSG_INDEX(a) last_message_index(a)
1056#define RETRIEVE(a,b,c,d)
1057#define DISPOSE(a,b)
1058#define STORE(a,b,c,d,e,f,g,h,i,j,k)
1059#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
1060#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
1061#define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
1062#define DELETE(a,b,c,d) (vm_delete(c))
1063#define UPDATE_MSG_ID(a, b, c, d, e, f)
1064#endif
1065#endif
1066
1068
1069static char ext_pass_cmd[128];
1070static char ext_pass_check_cmd[128];
1071
1072static int my_umask;
1073
1074#define PWDCHANGE_INTERNAL (1 << 1)
1075#define PWDCHANGE_EXTERNAL (1 << 2)
1077
1078#ifdef ODBC_STORAGE
1079#define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
1080#else
1081# ifdef IMAP_STORAGE
1082# define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
1083# else
1084# define tdesc "Comedian Mail (Voicemail System)"
1085# endif
1086#endif
1087
1088static char *addesc = "Comedian Mail";
1089
1090/* Leave a message */
1091static char *voicemail_app = "VoiceMail";
1092
1093/* Check mail, control, etc */
1094static char *voicemailmain_app = "VoiceMailMain";
1095
1096static char *vmauthenticate_app = "VMAuthenticate";
1097
1098static char *playmsg_app = "VoiceMailPlayMsg";
1099
1100static char *sayname_app = "VMSayName";
1101
1104static char zonetag[80];
1105static char locale[20];
1106static int maxsilence;
1107static int maxmsg = MAXMSG;
1108static int maxdeletedmsg;
1109static int silencethreshold = 128;
1111static char mailcmd[160] = SENDMAIL; /* Configurable mail cmd */
1112static char externnotify[160];
1114static char vmfmts[80] = "wav";
1115static double volgain;
1116static int vmminsecs;
1117static int vmmaxsecs;
1118static int maxgreet;
1119static int skipms = 3000;
1120static int maxlogins = 3;
1124
1125/*! Poll mailboxes for changes since there is something external to
1126 * app_voicemail that may change them. */
1127static unsigned int poll_mailboxes;
1128
1129/*! By default, poll every 30 seconds */
1130#define DEFAULT_POLL_FREQ 30
1131/*! Polling frequency */
1132static unsigned int poll_freq = DEFAULT_POLL_FREQ;
1133
1135static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
1137static unsigned char poll_thread_run;
1138
1140
1141struct alias_mailbox_mapping {
1142 char *alias;
1143 char *mailbox;
1144 char buf[0];
1145};
1146
1147struct mailbox_alias_mapping {
1148 char *alias;
1149 char *mailbox;
1150 char buf[0];
1151};
1152
1153#define MAPPING_BUCKETS 511
1157
1161
1162/* custom audio control prompts for voicemail playback */
1168
1169/* custom password sounds */
1170static char vm_login[80] = "vm-login";
1171static char vm_newuser[80] = "vm-newuser";
1172static char vm_password[80] = "vm-password";
1173static char vm_newpassword[80] = "vm-newpassword";
1174static char vm_passchanged[80] = "vm-passchanged";
1175static char vm_reenterpassword[80] = "vm-reenterpassword";
1176static char vm_mismatch[80] = "vm-mismatch";
1177static char vm_invalid_password[80] = "vm-invalid-password";
1178static char vm_pls_try_again[80] = "vm-pls-try-again";
1179
1180/*
1181 * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
1182 * 1. create a sound along the lines of "Please try again. When done, press the pound key" which could be spliced
1183 * from existing sound clips. This would require some programming changes in the area of vm_forward options and also
1184 * app.c's __ast_play_and_record function
1185 * 2. create a sound prompt saying "Please try again. When done recording, press any key to stop and send the prepended
1186 * message." At the time of this comment, I think this would require new voice work to be commissioned.
1187 * 3. Something way different like providing instructions before a time out or a post-recording menu. This would require
1188 * more effort than either of the other two.
1189 */
1190static char vm_prepend_timeout[80] = "vm-then-pound";
1191
1192static struct ast_flags globalflags = {0};
1193
1194static int saydurationminfo = 2;
1195
1199
1201
1202
1203static char *emailbody;
1204static char *emailsubject;
1205static char *pagerbody;
1206static char *pagersubject;
1207static char fromstring[100];
1208static char pagerfromstring[100];
1209static char charset[32] = "ISO-8859-1";
1210
1211static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
1212static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
1213static int adsiver = 1;
1214static char emaildateformat[32] = "%A, %B %d, %Y at %r";
1215static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
1216
1217/* Forward declarations - generic */
1218static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
1219static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu);
1220static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
1221static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
1222static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
1223 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
1224 signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro);
1225static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
1226static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
1227static 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);
1228static 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);
1229static void apply_options(struct ast_vm_user *vmu, const char *options);
1230static 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);
1231static int is_valid_dtmf(const char *key);
1232static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
1233static int write_password_to_file(const char *secretfn, const char *password);
1234static const char *substitute_escapes(const char *value);
1235static 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);
1236static void notify_new_state(struct ast_vm_user *vmu);
1237static int append_vmu_info_astman(struct mansession *s, struct ast_vm_user *vmu, const char* event_name, const char* actionid);
1238static int append_vmbox_info_astman(struct mansession *s, const struct message *m, struct ast_vm_user *vmu, const char* event_name, const char* actionid);
1239
1240
1241/*!
1242 * Place a message in the indicated folder
1243 *
1244 * \param vmu Voicemail user
1245 * \param vms Current voicemail state for the user
1246 * \param msg The message number to save
1247 * \param box The folder into which the message should be saved
1248 * \param[out] newmsg The new message number of the saved message
1249 * \param move Tells whether to copy or to move the message
1250 *
1251 * \note the "move" parameter is only honored for IMAP voicemail presently
1252 * \retval 0 Success
1253 * \retval other Failure
1254 */
1255static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move);
1256
1257static 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);
1258static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot);
1259
1260static 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);
1261static 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);
1262static int vm_msg_remove(const char *mailbox, const char *context, size_t num_msgs, const char *folder, const char *msgs[]);
1263static 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);
1264
1265#ifdef TEST_FRAMEWORK
1266static int vm_test_destroy_user(const char *context, const char *mailbox);
1267static int vm_test_create_user(const char *context, const char *mailbox);
1268#endif
1269
1270/*!
1271 * \internal
1272 * \brief Parse the given mailbox_id into mailbox and context.
1273 * \since 12.0.0
1274 *
1275 * \param mailbox_id The mailbox\@context string to separate.
1276 * \param mailbox Where the mailbox part will start.
1277 * \param context Where the context part will start. ("default" if not present)
1278 *
1279 * \retval 0 on success.
1280 * \retval -1 on error.
1281 */
1282static int separate_mailbox(char *mailbox_id, char **mailbox, char **context)
1283{
1284 if (ast_strlen_zero(mailbox_id) || !mailbox || !context) {
1285 return -1;
1286 }
1287 *context = mailbox_id;
1288 *mailbox = strsep(context, "@");
1289 if (ast_strlen_zero(*mailbox)) {
1290 return -1;
1291 }
1292 if (ast_strlen_zero(*context)) {
1293 *context = "default";
1294 }
1295 return 0;
1296}
1297
1299
1300struct inprocess {
1301 int count;
1302 char *context;
1303 char mailbox[0];
1304};
1305
1306static int inprocess_hash_fn(const void *obj, const int flags)
1307{
1308 const struct inprocess *i = obj;
1309 return atoi(i->mailbox);
1310}
1311
1312static int inprocess_cmp_fn(void *obj, void *arg, int flags)
1313{
1314 struct inprocess *i = obj, *j = arg;
1315 if (strcmp(i->mailbox, j->mailbox)) {
1316 return 0;
1317 }
1318 return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
1319}
1320
1321static int inprocess_count(const char *context, const char *mailbox, int delta)
1322{
1323 int context_len = strlen(context) + 1;
1324 int mailbox_len = strlen(mailbox) + 1;
1325 struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + context_len + mailbox_len);
1326 arg->context = arg->mailbox + mailbox_len;
1327 ast_copy_string(arg->mailbox, mailbox, mailbox_len); /* SAFE */
1328 ast_copy_string(arg->context, context, context_len); /* SAFE */
1330 if ((i = ao2_find(inprocess_container, arg, 0))) {
1331 int ret = ast_atomic_fetchadd_int(&i->count, delta);
1333 ao2_ref(i, -1);
1334 return ret;
1335 }
1336 if (delta < 0) {
1337 ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
1338 }
1339 if (!(i = ao2_alloc(sizeof(*i) + context_len + mailbox_len, NULL))) {
1341 return 0;
1342 }
1343 i->context = i->mailbox + mailbox_len;
1344 ast_copy_string(i->mailbox, mailbox, mailbox_len); /* SAFE */
1345 ast_copy_string(i->context, context, context_len); /* SAFE */
1346 i->count = delta;
1349 ao2_ref(i, -1);
1350 return 0;
1351}
1352
1353#if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
1354static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
1355#endif
1356
1357/*!
1358 * \brief Strips control and non 7-bit clean characters from input string.
1359 *
1360 * \note To map control and none 7-bit characters to a 7-bit clean characters
1361 * please use ast_str_encode_mine().
1362 */
1363static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
1364{
1365 char *bufptr = buf;
1366 for (; *input; input++) {
1367 if (*input < 32) {
1368 continue;
1369 }
1370 *bufptr++ = *input;
1371 if (bufptr == buf + buflen - 1) {
1372 break;
1373 }
1374 }
1375 *bufptr = '\0';
1376 return buf;
1377}
1378
1379/*
1380 * These macros are currently only used by the ODBC code but
1381 * should be used anyplace we're allocating a PATH_MAX
1382 * sized buffer for something which will be much smaller
1383 * in practice.
1384 */
1385#ifdef ODBC_STORAGE
1386/*!
1387 * \internal
1388 * \brief Get the length needed for a message path base.
1389 *
1390 * \param dir The absolute path to a directory
1391 *
1392 * \note The directory provided should include all components
1393 * down to the folder level. For example:
1394 * /var/spool/asterisk/voicemail/default/1234/INBOX
1395 *
1396 * \returns Number of bytes needed for the message path base.
1397 */
1398static size_t get_msg_path_len(const char *dir)
1399{
1400 if (ast_strlen_zero(dir)) {
1401 return 0;
1402 }
1403 /* dir + '/' + MSGFILE_LEN + '\0' */
1404 return strlen(dir) + 1 + MSGFILE_LEN + 1;
1405}
1406
1407/*
1408 * The longest sound file extension we currently
1409 * have is "siren14" which is 7 characters long
1410 * but we'll make this 12 to be extra safe.
1411 */
1412#define MAX_SOUND_EXTEN_LEN 12
1413/*!
1414 * \internal
1415 * \brief Get the length needed for a message sound or metadata file path.
1416 *
1417 * \param dir The absolute path to a directory
1418 *
1419 * \note The directory provided should include all components
1420 * down to the folder level. For example:
1421 * /var/spool/asterisk/voicemail/default/1234/INBOX
1422 *
1423 * \returns Number of bytes needed for the message sound or metadata file path.
1424 */
1425static size_t get_msg_path_ext_len(const char *dir)
1426{
1427 if (ast_strlen_zero(dir)) {
1428 return 0;
1429 }
1430 /* dir + '/' + MSGFILE_LEN + extlen(including '.') + '\0' */
1431 return strlen(dir) + 1 + MSGFILE_LEN + MAX_SOUND_EXTEN_LEN + 1;
1432}
1433
1434/*!
1435 * \internal
1436 * \brief Allocate a buffer on the stack containing a message file path base.
1437 *
1438 * \param dir The absolute path to a directory
1439 * \param msgnum The message number or -1 for a prompt file
1440 *
1441 * \note The returned buffer will automatically be freed when it
1442 * goes out of scope.
1443 *
1444 * If passed (/var/spool/asterisk/voicemail/default/1234/INBOX, 0)
1445 * the returned path would be:
1446 * /var/spool/asterisk/voicemail/default/1234/INBOX/msg0000
1447 *
1448 * If passed (/var/spool/asterisk/voicemail/default/1234/unavail, -1)
1449 * the returned path would be:
1450 * /var/spool/asterisk/voicemail/default/1234/unavail
1451 * (no change)
1452 *
1453 * \returns A pointer to the buffer containing the path.
1454 */
1455#define MAKE_FILE_PTRA(dir, msgnum) \
1456({ \
1457 size_t __len = get_msg_path_len(dir); \
1458 char *__var; \
1459 if (msgnum < 0) { \
1460 __var = ast_strdupa(dir); \
1461 } else { \
1462 __var = ast_alloca(__len); \
1463 snprintf(__var, __len, "%s/msg%04d", dir, msgnum); \
1464 } \
1465 __var; \
1466})
1467
1468/*!
1469 * \internal
1470 * \brief Allocate a buffer on the stack for a message sound or metadata file path.
1471 *
1472 * \param dir The absolute path to a directory
1473 * \param msgnum The message number or -1 for a prompt file
1474 *
1475 * \note The returned buffer will automatically be freed when it
1476 * goes out of scope.
1477 *
1478 * If passed (/var/spool/asterisk/voicemail/default/1234/INBOX, 0, "txt")
1479 * the returned path would be:
1480 * /var/spool/asterisk/voicemail/default/1234/INBOX/msg0000.txt
1481 *
1482 * If passed (/var/spool/asterisk/voicemail/default/1234/unavail, -1, "WAV")
1483 * the returned path would be:
1484 * /var/spool/asterisk/voicemail/default/1234/unavail.WAV
1485 *
1486 * The buffer returned has enough space that you can safely re-use it
1487 * for other sound file extensions. For instance:
1488 *
1489 * char *fn = MAKE_FILE_PTRA(dir, msgnum);
1490 * char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
1491 * char *fmt = "g722";
1492 * ...do domething with full_fn then re-use it...
1493 * sprintf(full_fn, "%s.%s", fn, fmt); * Safe since the same dir and msgnum were used *
1494 *
1495 * \returns A pointer to the allocated buffer
1496 */
1497#define MAKE_FILE_EXT_PTRA(dir, msgnum, ext) \
1498({ \
1499 size_t __len = get_msg_path_ext_len(dir); \
1500 char *__var = ast_alloca(__len); \
1501 if (msgnum < 0) { \
1502 snprintf(__var, __len, "%s.%s", dir, ext); \
1503 } else { \
1504 snprintf(__var, __len, "%s/msg%04d.%s", dir, msgnum, ext); \
1505 } \
1506 __var; \
1507})
1508#endif
1509
1510/*!
1511 * \brief Sets default voicemail system options to a voicemail user.
1512 *
1513 * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
1514 * - all the globalflags
1515 * - the saydurationminfo
1516 * - the callcontext
1517 * - the dialcontext
1518 * - the exitcontext
1519 * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
1520 * - volume gain.
1521 * - emailsubject, emailbody set to NULL
1522 */
1523static void populate_defaults(struct ast_vm_user *vmu)
1524{
1527 if (saydurationminfo) {
1529 }
1530 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
1531 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
1532 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
1533 ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
1534 ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
1535 if (vmminsecs) {
1536 vmu->minsecs = vmminsecs;
1537 }
1538 if (vmmaxsecs) {
1539 vmu->maxsecs = vmmaxsecs;
1540 }
1541 if (maxmsg) {
1542 vmu->maxmsg = maxmsg;
1543 }
1544 if (maxdeletedmsg) {
1546 }
1547 vmu->volgain = volgain;
1548 ast_free(vmu->email);
1549 vmu->email = NULL;
1550 ast_free(vmu->emailsubject);
1551 vmu->emailsubject = NULL;
1552 ast_free(vmu->emailbody);
1553 vmu->emailbody = NULL;
1554#ifdef IMAP_STORAGE
1555 ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
1556 ast_copy_string(vmu->imapserver, imapserver, sizeof(vmu->imapserver));
1557 ast_copy_string(vmu->imapport, imapport, sizeof(vmu->imapport));
1558 ast_copy_string(vmu->imapflags, imapflags, sizeof(vmu->imapflags));
1559#endif
1560}
1561
1562/*!
1563 * \brief Sets a specific property value.
1564 * \param vmu The voicemail user object to work with.
1565 * \param var The name of the property to be set.
1566 * \param value The value to be set to the property.
1567 *
1568 * The property name must be one of the understood properties. See the source for details.
1569 */
1570static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
1571{
1572 int x;
1573 if (!strcasecmp(var, "attach")) {
1575 } else if (!strcasecmp(var, "attachfmt")) {
1576 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
1577 } else if (!strcasecmp(var, "attachextrecs")) {
1579 } else if (!strcasecmp(var, "serveremail")) {
1580 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
1581 } else if (!strcasecmp(var, "fromstring")) {
1582 ast_copy_string(vmu->fromstring, value, sizeof(vmu->fromstring));
1583 } else if (!strcasecmp(var, "emailbody")) {
1584 ast_free(vmu->emailbody);
1586 } else if (!strcasecmp(var, "emailsubject")) {
1587 ast_free(vmu->emailsubject);
1589 } else if (!strcasecmp(var, "language")) {
1590 ast_copy_string(vmu->language, value, sizeof(vmu->language));
1591 } else if (!strcasecmp(var, "tz")) {
1592 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
1593 } else if (!strcasecmp(var, "locale")) {
1594 ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
1595#ifdef IMAP_STORAGE
1596 } else if (!strcasecmp(var, "imapuser")) {
1597 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
1598 vmu->imapversion = imapversion;
1599 } else if (!strcasecmp(var, "imapserver")) {
1600 ast_copy_string(vmu->imapserver, value, sizeof(vmu->imapserver));
1601 vmu->imapversion = imapversion;
1602 } else if (!strcasecmp(var, "imapport")) {
1603 ast_copy_string(vmu->imapport, value, sizeof(vmu->imapport));
1604 vmu->imapversion = imapversion;
1605 } else if (!strcasecmp(var, "imapflags")) {
1606 ast_copy_string(vmu->imapflags, value, sizeof(vmu->imapflags));
1607 vmu->imapversion = imapversion;
1608 } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
1609 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
1610 vmu->imapversion = imapversion;
1611 } else if (!strcasecmp(var, "imapfolder")) {
1612 ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
1613 vmu->imapversion = imapversion;
1614 } else if (!strcasecmp(var, "imapvmshareid")) {
1615 ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
1616 vmu->imapversion = imapversion;
1617#endif
1618 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
1620 } else if (!strcasecmp(var, "saycid")){
1622 } else if (!strcasecmp(var, "sendvoicemail")){
1624 } else if (!strcasecmp(var, "review")){
1626 } else if (!strcasecmp(var, "leaveurgent")){
1628 } else if (!strcasecmp(var, "tempgreetwarn")){
1630 } else if (!strcasecmp(var, "messagewrap")){
1632 } else if (!strcasecmp(var, "operator")) {
1634 } else if (!strcasecmp(var, "envelope")){
1636 } else if (!strcasecmp(var, "moveheard")){
1638 } else if (!strcasecmp(var, "sayduration")){
1640 } else if (!strcasecmp(var, "saydurationm")){
1641 if (sscanf(value, "%30d", &x) == 1) {
1642 vmu->saydurationm = x;
1643 } else {
1644 ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
1645 }
1646 } else if (!strcasecmp(var, "forcename")){
1648 } else if (!strcasecmp(var, "forcegreetings")){
1650 } else if (!strcasecmp(var, "callback")) {
1651 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
1652 } else if (!strcasecmp(var, "dialout")) {
1653 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
1654 } else if (!strcasecmp(var, "exitcontext")) {
1655 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
1656 } else if (!strcasecmp(var, "minsecs")) {
1657 if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
1658 vmu->minsecs = x;
1659 } else {
1660 ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
1661 vmu->minsecs = vmminsecs;
1662 }
1663 } else if (!strcasecmp(var, "maxsecs")) {
1664 vmu->maxsecs = atoi(value);
1665 if (vmu->maxsecs <= 0) {
1666 ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
1667 vmu->maxsecs = vmmaxsecs;
1668 } else {
1669 vmu->maxsecs = atoi(value);
1670 }
1671 } else if (!strcasecmp(var, "maxmsg")) {
1672 vmu->maxmsg = atoi(value);
1673 /* Accept maxmsg=0 (Greetings only voicemail) */
1674 if (vmu->maxmsg < 0) {
1675 ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
1676 vmu->maxmsg = MAXMSG;
1677 } else if (vmu->maxmsg > MAXMSGLIMIT) {
1678 ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
1679 vmu->maxmsg = MAXMSGLIMIT;
1680 }
1681 } else if (!strcasecmp(var, "nextaftercmd")) {
1683 } else if (!strcasecmp(var, "backupdeleted")) {
1684 if (sscanf(value, "%30d", &x) == 1)
1685 vmu->maxdeletedmsg = x;
1686 else if (ast_true(value))
1687 vmu->maxdeletedmsg = MAXMSG;
1688 else
1689 vmu->maxdeletedmsg = 0;
1690
1691 if (vmu->maxdeletedmsg < 0) {
1692 ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
1693 vmu->maxdeletedmsg = MAXMSG;
1694 } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
1695 ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
1697 }
1698 } else if (!strcasecmp(var, "volgain")) {
1699 sscanf(value, "%30lf", &vmu->volgain);
1700 } else if (!strcasecmp(var, "passwordlocation")) {
1701 if (!strcasecmp(value, "spooldir")) {
1703 } else {
1705 }
1706 } else if (!strcasecmp(var, "options")) {
1707 apply_options(vmu, value);
1708 }
1709}
1710
1711static char *vm_check_password_shell(char *command, char *buf, size_t len)
1712{
1713 int fds[2], pid = 0;
1714
1715 memset(buf, 0, len);
1716
1717 if (pipe(fds)) {
1718 snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
1719 } else {
1720 /* good to go*/
1721 pid = ast_safe_fork(0);
1722
1723 if (pid < 0) {
1724 /* ok maybe not */
1725 close(fds[0]);
1726 close(fds[1]);
1727 snprintf(buf, len, "FAILURE: Fork failed");
1728 } else if (pid) {
1729 /* parent */
1730 close(fds[1]);
1731 if (read(fds[0], buf, len) < 0) {
1732 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
1733 }
1734 close(fds[0]);
1735 } else {
1736 /* child */
1738 AST_APP_ARG(v)[20];
1739 );
1740 char *mycmd = ast_strdupa(command);
1741
1742 close(fds[0]);
1743 dup2(fds[1], STDOUT_FILENO);
1744 close(fds[1]);
1745 ast_close_fds_above_n(STDOUT_FILENO);
1746
1747 AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
1748
1749 execv(arg.v[0], arg.v);
1750 printf("FAILURE: %s", strerror(errno));
1751 _exit(0);
1752 }
1753 }
1754 return buf;
1755}
1756
1757/*!
1758 * \brief Check that password meets minimum required length
1759 * \param vmu The voicemail user to change the password for.
1760 * \param password The password string to check
1761 *
1762 * \return zero on ok, 1 on not ok.
1763 */
1764static int check_password(struct ast_vm_user *vmu, char *password)
1765{
1766 /* check minimum length */
1767 if (strlen(password) < minpassword)
1768 return 1;
1769 /* check that password does not contain '*' character */
1770 if (!ast_strlen_zero(password) && password[0] == '*')
1771 return 1;
1773 char cmd[255], buf[255];
1774
1775 ast_debug(1, "Verify password policies for %s\n", password);
1776
1777 snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
1778 if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
1779 ast_debug(5, "Result: %s\n", buf);
1780 if (!strncasecmp(buf, "VALID", 5)) {
1781 ast_debug(3, "Passed password check: '%s'\n", buf);
1782 return 0;
1783 } else if (!strncasecmp(buf, "FAILURE", 7)) {
1784 ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
1785 return 0;
1786 } else {
1787 ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
1788 return 1;
1789 }
1790 }
1791 }
1792 return 0;
1793}
1794
1795/*!
1796 * \brief Performs a change of the voicemail password in the realtime engine.
1797 * \param vmu The voicemail user to change the password for.
1798 * \param password The new value to be set to the password for this user.
1799 *
1800 * This only works if there is a realtime engine configured.
1801 * This is called from the (top level) vm_change_password.
1802 *
1803 * \return zero on success, -1 on error.
1804 */
1805static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
1806{
1807 int res = -1;
1808 if (!strcmp(vmu->password, password)) {
1809 /* No change (but an update would return 0 rows updated, so we opt out here) */
1810 return 0;
1811 }
1812
1813 if (strlen(password) > 10) {
1814 ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
1815 }
1816 if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
1817 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
1818 ast_copy_string(vmu->password, password, sizeof(vmu->password));
1819 res = 0;
1820 }
1821 return res;
1822}
1823
1824/*!
1825 * \brief Destructively Parse options and apply.
1826 */
1827static void apply_options(struct ast_vm_user *vmu, const char *options)
1828{
1829 char *stringp;
1830 char *s;
1831 char *var, *value;
1832 stringp = ast_strdupa(options);
1833 while ((s = strsep(&stringp, "|"))) {
1834 value = s;
1835 if ((var = strsep(&value, "=")) && value) {
1836 apply_option(vmu, var, value);
1837 }
1838 }
1839}
1840
1841/*!
1842 * \brief Loads the options specific to a voicemail user.
1843 *
1844 * This is called when a vm_user structure is being set up, such as from load_options.
1845 */
1846static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
1847{
1848 for (; var; var = var->next) {
1849 if (!strcasecmp(var->name, "vmsecret")) {
1850 ast_copy_string(retval->password, var->value, sizeof(retval->password));
1851 } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
1852 if (ast_strlen_zero(retval->password)) {
1853 if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
1854 ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
1855 "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
1856 } else {
1857 ast_copy_string(retval->password, var->value, sizeof(retval->password));
1858 }
1859 }
1860 } else if (!strcasecmp(var->name, "uniqueid")) {
1861 ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
1862 } else if (!strcasecmp(var->name, "pager")) {
1863 ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
1864 } else if (!strcasecmp(var->name, "email")) {
1865 ast_free(retval->email);
1866 retval->email = ast_strdup(var->value);
1867 } else if (!strcasecmp(var->name, "fullname")) {
1868 ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
1869 } else if (!strcasecmp(var->name, "context")) {
1870 ast_copy_string(retval->context, var->value, sizeof(retval->context));
1871 } else if (!strcasecmp(var->name, "emailsubject")) {
1872 ast_free(retval->emailsubject);
1873 retval->emailsubject = ast_strdup(substitute_escapes(var->value));
1874 } else if (!strcasecmp(var->name, "emailbody")) {
1875 ast_free(retval->emailbody);
1876 retval->emailbody = ast_strdup(substitute_escapes(var->value));
1877#ifdef IMAP_STORAGE
1878 } else if (!strcasecmp(var->name, "imapuser")) {
1879 ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
1880 retval->imapversion = imapversion;
1881 } else if (!strcasecmp(var->name, "imapserver")) {
1882 ast_copy_string(retval->imapserver, var->value, sizeof(retval->imapserver));
1883 retval->imapversion = imapversion;
1884 } else if (!strcasecmp(var->name, "imapport")) {
1885 ast_copy_string(retval->imapport, var->value, sizeof(retval->imapport));
1886 retval->imapversion = imapversion;
1887 } else if (!strcasecmp(var->name, "imapflags")) {
1888 ast_copy_string(retval->imapflags, var->value, sizeof(retval->imapflags));
1889 retval->imapversion = imapversion;
1890 } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
1891 ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
1892 retval->imapversion = imapversion;
1893 } else if (!strcasecmp(var->name, "imapfolder")) {
1894 ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
1895 retval->imapversion = imapversion;
1896 } else if (!strcasecmp(var->name, "imapvmshareid")) {
1897 ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
1898 retval->imapversion = imapversion;
1899#endif
1900 } else
1901 apply_option(retval, var->name, var->value);
1902 }
1903}
1904
1905/*!
1906 * \brief Determines if a DTMF key entered is valid.
1907 * \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.
1908 *
1909 * Tests the character entered against the set of valid DTMF characters.
1910 * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1911 */
1912static int is_valid_dtmf(const char *key)
1913{
1914 int i;
1915 char *local_key = ast_strdupa(key);
1916
1917 for (i = 0; i < strlen(key); ++i) {
1918 if (!strchr(VALID_DTMF, *local_key)) {
1919 ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1920 return 0;
1921 }
1922 local_key++;
1923 }
1924 return 1;
1925}
1926
1927/*!
1928 * \brief Finds a voicemail user from the realtime engine.
1929 * \param ivm
1930 * \param context
1931 * \param mailbox
1932 *
1933 * 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.
1934 *
1935 * \return The ast_vm_user structure for the user that was found.
1936 */
1937static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1938{
1939 struct ast_variable *var;
1940 struct ast_vm_user *retval;
1941
1942 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1943 if (ivm) {
1944 memset(retval, 0, sizeof(*retval));
1945 }
1946 populate_defaults(retval);
1947 if (!ivm) {
1948 ast_set_flag(retval, VM_ALLOCED);
1949 }
1950 if (mailbox) {
1951 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1952 }
1954 var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1955 } else {
1956 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1957 }
1958 if (var) {
1959 apply_options_full(retval, var);
1961 } else {
1962 if (!ivm)
1963 ast_free(retval);
1964 retval = NULL;
1965 }
1966 }
1967 return retval;
1968}
1969
1970/*!
1971 * \brief Finds a voicemail user from the users file or the realtime engine.
1972 * \param ivm
1973 * \param context
1974 * \param mailbox
1975 *
1976 * \return The ast_vm_user structure for the user that was found.
1977 */
1978static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1979{
1980 /* This function could be made to generate one from a database, too */
1981 struct ast_vm_user *vmu = NULL, *cur;
1983
1985 context = "default";
1986
1987 AST_LIST_TRAVERSE(&users, cur, list) {
1988#ifdef IMAP_STORAGE
1989 if (cur->imapversion != imapversion) {
1990 continue;
1991 }
1992#endif
1993 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1994 break;
1995 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1996 break;
1997 }
1998 if (cur) {
1999 /* Make a copy, so that on a reload, we have no race */
2000 if ((vmu = (ivm ? ivm : ast_calloc(1, sizeof(*vmu))))) {
2001 ast_free(vmu->email);
2002 ast_free(vmu->emailbody);
2003 ast_free(vmu->emailsubject);
2004 *vmu = *cur;
2005 vmu->email = ast_strdup(cur->email);
2006 vmu->emailbody = ast_strdup(cur->emailbody);
2007 vmu->emailsubject = ast_strdup(cur->emailsubject);
2008 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
2009 AST_LIST_NEXT(vmu, list) = NULL;
2010 }
2011 }
2013 if (!vmu) {
2014 vmu = find_user_realtime(ivm, context, mailbox);
2015 }
2016 if (!vmu && !ast_strlen_zero(aliasescontext)) {
2017 struct alias_mailbox_mapping *mapping;
2018 char *search_string = ast_alloca(MAX_VM_MAILBOX_LEN);
2019
2020 snprintf(search_string, MAX_VM_MAILBOX_LEN, "%s%s%s",
2021 mailbox,
2022 ast_strlen_zero(context) ? "" : "@",
2023 S_OR(context, ""));
2024
2025 mapping = ao2_find(alias_mailbox_mappings, search_string, OBJ_SEARCH_KEY);
2026 if (mapping) {
2027 char *search_mailbox = NULL;
2028 char *search_context = NULL;
2029
2030 separate_mailbox(ast_strdupa(mapping->mailbox), &search_mailbox, &search_context);
2031 ao2_ref(mapping, -1);
2032 vmu = find_user(ivm, search_mailbox, search_context);
2033 }
2034 }
2035
2036 return vmu;
2037}
2038
2039/*!
2040 * \brief Resets a user password to a specified password.
2041 * \param context
2042 * \param mailbox
2043 * \param newpass
2044 *
2045 * This does the actual change password work, called by the vm_change_password() function.
2046 *
2047 * \return zero on success, -1 on error.
2048 */
2049static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
2050{
2051 /* This function could be made to generate one from a database, too */
2052 struct ast_vm_user *cur;
2053 int res = -1;
2055 AST_LIST_TRAVERSE(&users, cur, list) {
2056 if ((!context || !strcasecmp(context, cur->context)) &&
2057 (!strcasecmp(mailbox, cur->mailbox)))
2058 break;
2059 }
2060 if (cur) {
2061 ast_copy_string(cur->password, newpass, sizeof(cur->password));
2062 res = 0;
2063 }
2065 if (!res) {
2066 struct ast_json *json_object;
2067
2068 json_object = ast_json_pack("{s: s, s: s, s: s}",
2069 "Context", S_OR(context, "default"),
2070 "Mailbox", mailbox,
2071 "NewPassword", newpass);
2072 ast_manager_publish_event("VoicemailPasswordChange", EVENT_FLAG_SYSTEM | EVENT_FLAG_USER, json_object);
2073 ast_json_unref(json_object);
2074 }
2075 return res;
2076}
2077
2078/*!
2079 * \brief Check if configuration file is valid
2080 */
2081static inline int valid_config(const struct ast_config *cfg)
2082{
2083 return cfg && cfg != CONFIG_STATUS_FILEINVALID;
2084}
2085
2086/*!
2087 * \brief The handler for the change password option.
2088 * \param vmu The voicemail user to work with.
2089 * \param newpassword The new password (that has been gathered from the appropriate prompting).
2090 * 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.
2091 * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
2092 */
2093static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
2094{
2095 struct ast_config *cfg = NULL;
2096 struct ast_category *cat = NULL;
2097 char *category = NULL;
2098 const char *tmp = NULL;
2099 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
2100 char secretfn[PATH_MAX] = "";
2101 int found = 0;
2102
2103 if (!change_password_realtime(vmu, newpassword))
2104 return;
2105
2106 /* check if we should store the secret in the spool directory next to the messages */
2107 switch (vmu->passwordlocation) {
2108 case OPT_PWLOC_SPOOLDIR:
2109 snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
2110 if (write_password_to_file(secretfn, newpassword) == 0) {
2111 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
2112 ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
2113 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2114 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2115 break;
2116 } else {
2117 ast_log(LOG_WARNING, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
2118 }
2119 /* Fall-through */
2121 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
2122 while ((category = ast_category_browse(cfg, category))) {
2123 if (!strcasecmp(category, vmu->context)) {
2124 char *value = NULL;
2125 char *new = NULL;
2126 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
2127 ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
2128 break;
2129 }
2130 value = strstr(tmp, ",");
2131 if (!value) {
2132 new = ast_malloc(strlen(newpassword) + 1);
2133 sprintf(new, "%s", newpassword);
2134 } else {
2135 new = ast_malloc((strlen(value) + strlen(newpassword) + 1));
2136 sprintf(new, "%s%s", newpassword, value);
2137 }
2138 if (!(cat = ast_category_get(cfg, category, NULL))) {
2139 ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
2140 ast_free(new);
2141 break;
2142 }
2143 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
2144 found = 1;
2145 ast_free(new);
2146 }
2147 }
2148 /* save the results */
2149 if (found) {
2150 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
2151 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2152 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2153 ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "app_voicemail");
2154 ast_config_destroy(cfg);
2155 break;
2156 }
2157
2158 ast_config_destroy(cfg);
2159 }
2160 break;
2161 }
2162}
2163
2164static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
2165{
2166 char buf[255];
2167 snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
2168 ast_debug(1, "External password: %s\n",buf);
2169 if (!ast_safe_system(buf)) {
2170 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
2171 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2172 /* Reset the password in memory, too */
2173 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2174 }
2175}
2176
2177/*!
2178 * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
2179 * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
2180 * \param len The length of the path string that was written out.
2181 * \param context
2182 * \param ext
2183 * \param folder
2184 *
2185 * The path is constructed as
2186 * VM_SPOOL_DIRcontext/ext/folder
2187 *
2188 * \return zero on success, -1 on error.
2189 */
2190static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
2191{
2192 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
2193}
2194
2195/*!
2196 * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
2197 * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
2198 * \param len The length of the path string that was written out.
2199 * \param dir
2200 * \param num
2201 *
2202 * The path is constructed as
2203 * VM_SPOOL_DIRcontext/ext/folder
2204 *
2205 * \return zero on success, -1 on error.
2206 */
2207static int make_file(char *dest, const int len, const char *dir, const int num)
2208{
2209 return snprintf(dest, len, "%s/msg%04d", dir, num);
2210}
2211
2212/*! \brief basically mkdir -p $dest/$context/$ext/$folder
2213 * \param dest String. base directory.
2214 * \param len Length of dest.
2215 * \param context String. Ignored if is null or empty string.
2216 * \param ext String. Ignored if is null or empty string.
2217 * \param folder String. Ignored if is null or empty string.
2218 * \return -1 on failure, 0 on success.
2219 */
2220static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
2221{
2222 mode_t mode = VOICEMAIL_DIR_MODE;
2223 int res;
2224
2225 make_dir(dest, len, context, ext, folder);
2226 if ((res = ast_mkdir(dest, mode))) {
2227 ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
2228 return -1;
2229 }
2230 return 0;
2231}
2232
2233static const char *mbox(struct ast_vm_user *vmu, int id)
2234{
2235#ifdef IMAP_STORAGE
2236 if (vmu && id == 0) {
2237 return vmu->imapfolder;
2238 }
2239#endif
2240 return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
2241}
2242
2243static const char *vm_index_to_foldername(int id)
2244{
2245 return mbox(NULL, id);
2246}
2247
2248
2249static int get_folder_by_name(const char *name)
2250{
2251 size_t i;
2252
2253 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
2254 if (strcasecmp(name, mailbox_folders[i]) == 0) {
2255 return i;
2256 }
2257 }
2258
2259 return -1;
2260}
2261
2262static void free_user(struct ast_vm_user *vmu)
2263{
2264 if (!vmu) {
2265 return;
2266 }
2267
2268 ast_free(vmu->email);
2269 vmu->email = NULL;
2270 ast_free(vmu->emailbody);
2271 vmu->emailbody = NULL;
2272 ast_free(vmu->emailsubject);
2273 vmu->emailsubject = NULL;
2274
2275 if (ast_test_flag(vmu, VM_ALLOCED)) {
2276 ast_free(vmu);
2277 }
2278}
2279
2280static void free_user_final(struct ast_vm_user *vmu)
2281{
2282 if (!vmu) {
2283 return;
2284 }
2285
2286 if (!ast_strlen_zero(vmu->mailbox)) {
2288 }
2289
2290 free_user(vmu);
2291}
2292
2293static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
2294
2295 int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
2296
2297 /* remove old allocation */
2298 if (vms->deleted) {
2299 ast_free(vms->deleted);
2300 vms->deleted = NULL;
2301 }
2302 if (vms->heard) {
2303 ast_free(vms->heard);
2304 vms->heard = NULL;
2305 }
2306 vms->dh_arraysize = 0;
2307
2308 if (arraysize > 0) {
2309 if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
2310 return -1;
2311 }
2312 if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
2313 ast_free(vms->deleted);
2314 vms->deleted = NULL;
2315 return -1;
2316 }
2317 vms->dh_arraysize = arraysize;
2318 }
2319
2320 return 0;
2321}
2322
2323/* All IMAP-specific functions should go in this block. This
2324 * keeps them from being spread out all over the code */
2325#ifdef IMAP_STORAGE
2326static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
2327{
2328 char arg[10];
2329 struct vm_state *vms;
2330 unsigned long messageNum;
2331
2332 /* If greetings aren't stored in IMAP, just delete the file */
2333 if (msgnum < 0 && !imapgreetings) {
2335 return;
2336 }
2337
2338 if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2339 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);
2340 return;
2341 }
2342
2343 if (msgnum < 0) {
2344 imap_delete_old_greeting(file, vms);
2345 return;
2346 }
2347
2348 /* find real message number based on msgnum */
2349 /* this may be an index into vms->msgArray based on the msgnum. */
2350 messageNum = vms->msgArray[msgnum];
2351 if (messageNum == 0) {
2352 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
2353 return;
2354 }
2355 ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
2356 /* delete message */
2357 snprintf (arg, sizeof(arg), "%lu", messageNum);
2358 ast_mutex_lock(&vms->lock);
2359 mail_setflag (vms->mailstream, arg, "\\DELETED");
2360 mail_expunge(vms->mailstream);
2361 ast_mutex_unlock(&vms->lock);
2362}
2363
2364static 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)
2365{
2366 struct ast_channel *chan;
2367 char *cid;
2368 char *cid_name;
2369 char *cid_num;
2370 struct vm_state *vms;
2371 const char *duration_str;
2372 int duration = 0;
2373
2374 /*
2375 * First, get things initially set up. If any of this fails, then
2376 * back out before doing anything substantial
2377 */
2378 vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0);
2379 if (!vms) {
2380 return;
2381 }
2382
2383 if (open_mailbox(vms, vmu, folder)) {
2384 return;
2385 }
2386
2387 chan = ast_dummy_channel_alloc();
2388 if (!chan) {
2389 close_mailbox(vms, vmu);
2390 return;
2391 }
2392
2393 /*
2394 * We need to make sure the new message we save has the same
2395 * callerid, flag, and duration as the original message
2396 */
2397 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
2398
2399 if (!ast_strlen_zero(cid)) {
2400 ast_callerid_parse(cid, &cid_name, &cid_num);
2402 if (!ast_strlen_zero(cid_name)) {
2403 ast_channel_caller(chan)->id.name.valid = 1;
2404 ast_channel_caller(chan)->id.name.str = ast_strdup(cid_name);
2405 }
2406 if (!ast_strlen_zero(cid_num)) {
2407 ast_channel_caller(chan)->id.number.valid = 1;
2408 ast_channel_caller(chan)->id.number.str = ast_strdup(cid_num);
2409 }
2410 }
2411
2412 duration_str = ast_variable_retrieve(msg_cfg, "message", "duration");
2413
2414 if (!ast_strlen_zero(duration_str)) {
2415 sscanf(duration_str, "%30d", &duration);
2416 }
2417
2418 /*
2419 * IMAP messages cannot be altered once delivered. So we have to delete the
2420 * current message and then re-add it with the updated message ID.
2421 *
2422 * Furthermore, there currently is no atomic way to create a new message and to
2423 * store it in an arbitrary folder. So we have to save it to the INBOX and then
2424 * move to the appropriate folder.
2425 */
2426 if (!imap_store_file(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, vmfmts,
2427 duration, vms, ast_variable_retrieve(msg_cfg, "message", "flag"), msg_id)) {
2428 if (folder != NEW_FOLDER) {
2429 save_to_folder(vmu, vms, msgnum, folder, NULL, 1);
2430 }
2431 vm_imap_delete(dir, msgnum, vmu);
2432 }
2433 close_mailbox(vms, vmu);
2434 ast_channel_unref(chan);
2435}
2436
2437static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
2438{
2439 struct vm_state *vms_p;
2440 char *file, *filename;
2441 char dest[PATH_MAX];
2442 int i;
2443 BODY *body;
2444 int ret = 0;
2445 int curr_mbox;
2446
2447 /* This function is only used for retrieval of IMAP greetings
2448 * regular messages are not retrieved this way, nor are greetings
2449 * if they are stored locally*/
2450 if (msgnum > -1 || !imapgreetings) {
2451 return 0;
2452 } else {
2453 file = strrchr(ast_strdupa(dir), '/');
2454 if (file)
2455 *file++ = '\0';
2456 else {
2457 ast_debug(1, "Failed to procure file name from directory passed.\n");
2458 return -1;
2459 }
2460 }
2461
2462 /* check if someone is accessing this box right now... */
2463 if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) &&
2464 !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2465 /* Unlike when retrieving a message, it is reasonable not to be able to find a
2466 * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
2467 * that's all we need to do.
2468 */
2469 if (!(vms_p = create_vm_state_from_user(vmu))) {
2470 ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
2471 return -1;
2472 }
2473 }
2474
2475 /* Greetings will never have a prepended message */
2476 *vms_p->introfn = '\0';
2477
2478 ast_mutex_lock(&vms_p->lock);
2479
2480 /* get the current mailbox so that we can point the mailstream back to it later */
2481 curr_mbox = get_folder_by_name(vms_p->curbox);
2482
2483 if (init_mailstream(vms_p, GREETINGS_FOLDER) || !vms_p->mailstream) {
2484 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2485 ast_mutex_unlock(&vms_p->lock);
2486 return -1;
2487 }
2488
2489 /*XXX Yuck, this could probably be done a lot better */
2490 for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
2491 mail_fetchstructure(vms_p->mailstream, i + 1, &body);
2492 /* We have the body, now we extract the file name of the first attachment. */
2493 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2494 char *attachment = body->nested.part->next->body.parameter->value;
2495 char copy[strlen(attachment) + 1];
2496
2497 strcpy(copy, attachment); /* safe */
2498 attachment = copy;
2499
2500 filename = strsep(&attachment, ".");
2501 if (!strcmp(filename, file)) {
2502 ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
2503 vms_p->msgArray[vms_p->curmsg] = i + 1;
2504 create_dirpath(dest, sizeof(dest), vmu->context, vms_p->username, "");
2505 save_body(body, vms_p, "2", attachment, 0);
2506 ret = 0;
2507 break;
2508 }
2509 } else {
2510 ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
2511 ret = -1;
2512 break;
2513 }
2514 }
2515
2516 if (curr_mbox != -1) {
2517 /* restore previous mbox stream */
2518 if (init_mailstream(vms_p, curr_mbox) || !vms_p->mailstream) {
2519 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2520 ret = -1;
2521 }
2522 }
2523 ast_mutex_unlock(&vms_p->lock);
2524 return ret;
2525}
2526
2527static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
2528{
2529 BODY *body;
2530 char *header_content;
2531 char *attachedfilefmt;
2532 char buf[80];
2533 struct vm_state *vms;
2534 char text_file[PATH_MAX];
2535 FILE *text_file_ptr;
2536 int res = 0;
2537 struct ast_vm_user *vmu;
2538 int curr_mbox;
2539
2540 if (!(vmu = find_user(NULL, context, mailbox))) {
2541 ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
2542 return -1;
2543 }
2544
2545 if (msgnum < 0) {
2546 if (imapgreetings) {
2547 res = imap_retrieve_greeting(dir, msgnum, vmu);
2548 goto exit;
2549 } else {
2550 res = 0;
2551 goto exit;
2552 }
2553 }
2554
2555 /* Before anything can happen, we need a vm_state so that we can
2556 * actually access the imap server through the vms->mailstream
2557 */
2558 if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2559 /* This should not happen. If it does, then I guess we'd
2560 * need to create the vm_state, extract which mailbox to
2561 * open, and then set up the msgArray so that the correct
2562 * IMAP message could be accessed. If I have seen correctly
2563 * though, the vms should be obtainable from the vmstates list
2564 * and should have its msgArray properly set up.
2565 */
2566 ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
2567 res = -1;
2568 goto exit;
2569 }
2570
2571 /* Ensure we have the correct mailbox open and have a valid mailstream for it */
2572 curr_mbox = get_folder_by_name(vms->curbox);
2573 if (curr_mbox < 0) {
2574 ast_debug(3, "Mailbox folder curbox not set, defaulting to Inbox\n");
2575 curr_mbox = 0;
2576 }
2577 init_mailstream(vms, curr_mbox);
2578 if (!vms->mailstream) {
2579 ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmu->mailbox);
2580 res = -1;
2581 goto exit;
2582 }
2583
2584 make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
2585 snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
2586
2587 /* Don't try to retrieve a message from IMAP if it already is on the file system */
2588 if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
2589 res = 0;
2590 goto exit;
2591 }
2592
2593 ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
2594 if (vms->msgArray[msgnum] == 0) {
2595 ast_log(LOG_WARNING, "Trying to access unknown message\n");
2596 res = -1;
2597 goto exit;
2598 }
2599
2600 /* This will only work for new messages... */
2601 ast_mutex_lock(&vms->lock);
2602 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
2603 ast_mutex_unlock(&vms->lock);
2604 /* empty string means no valid header */
2605 if (ast_strlen_zero(header_content)) {
2606 ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
2607 res = -1;
2608 goto exit;
2609 }
2610
2611 ast_mutex_lock(&vms->lock);
2612 mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
2613 ast_mutex_unlock(&vms->lock);
2614
2615 /* We have the body, now we extract the file name of the first attachment. */
2616 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2617 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
2618 } else {
2619 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
2620 res = -1;
2621 goto exit;
2622 }
2623
2624 /* Find the format of the attached file */
2625
2626 strsep(&attachedfilefmt, ".");
2627 if (!attachedfilefmt) {
2628 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
2629 res = -1;
2630 goto exit;
2631 }
2632
2633 save_body(body, vms, "2", attachedfilefmt, 0);
2634 if (save_body(body, vms, "3", attachedfilefmt, 1)) {
2635 *vms->introfn = '\0';
2636 }
2637
2638 /* Get info from headers!! */
2639 snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
2640
2641 if (!(text_file_ptr = fopen(text_file, "w"))) {
2642 ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
2643 goto exit;
2644 }
2645
2646 fprintf(text_file_ptr, "%s\n", "[message]");
2647
2648 if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))) {
2649 fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
2650 }
2651 if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))) {
2652 fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
2653 }
2654 if (get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))) {
2655 fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
2656 }
2657 if (get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))) {
2658 fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
2659 }
2660 if (get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))) {
2661 fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
2662 }
2663 if (get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))) {
2664 fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
2665 }
2666 if (get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf))) {
2667 fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
2668 }
2669 if (get_header_by_tag(header_content, "X-Asterisk-VM-Message-ID:", buf, sizeof(buf))) {
2670 fprintf(text_file_ptr, "msg_id=%s\n", S_OR(buf, ""));
2671 }
2672 fclose(text_file_ptr);
2673
2674exit:
2675 free_user(vmu);
2676 return res;
2677}
2678
2679static int folder_int(const char *folder)
2680{
2681 /*assume a NULL folder means INBOX*/
2682 if (!folder) {
2683 return 0;
2684 }
2685 if (!strcasecmp(folder, imapfolder)) {
2686 return 0;
2687 } else if (!strcasecmp(folder, "Old")) {
2688 return 1;
2689 } else if (!strcasecmp(folder, "Work")) {
2690 return 2;
2691 } else if (!strcasecmp(folder, "Family")) {
2692 return 3;
2693 } else if (!strcasecmp(folder, "Friends")) {
2694 return 4;
2695 } else if (!strcasecmp(folder, "Cust1")) {
2696 return 5;
2697 } else if (!strcasecmp(folder, "Cust2")) {
2698 return 6;
2699 } else if (!strcasecmp(folder, "Cust3")) {
2700 return 7;
2701 } else if (!strcasecmp(folder, "Cust4")) {
2702 return 8;
2703 } else if (!strcasecmp(folder, "Cust5")) {
2704 return 9;
2705 } else if (!strcasecmp(folder, "Urgent")) {
2706 return 11;
2707 } else { /*assume they meant INBOX if folder is not found otherwise*/
2708 return 0;
2709 }
2710}
2711
2712static int __messagecount(const char *context, const char *mailbox, const char *folder)
2713{
2714 SEARCHPGM *pgm;
2715 SEARCHHEADER *hdr;
2716
2717 struct ast_vm_user *vmu, vmus;
2718 struct vm_state *vms_p;
2719 int ret = 0;
2720 int fold = folder_int(folder);
2721 int urgent = 0;
2722
2723 /* If URGENT, then look at INBOX */
2724 if (fold == 11) {
2725 fold = NEW_FOLDER;
2726 urgent = 1;
2727 }
2728
2730 return 0;
2731
2732 /* We have to get the user before we can open the stream! */
2733 memset(&vmus, 0, sizeof(vmus));
2734 vmu = find_user(&vmus, context, mailbox);
2735 if (!vmu) {
2736 ast_log(AST_LOG_WARNING, "Couldn't find mailbox %s in context %s\n", mailbox, context);
2737 free_user(vmu);
2738 return -1;
2739 } else {
2740 /* No IMAP account available */
2741 if (vmu->imapuser[0] == '\0') {
2742 ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2743 free_user(vmu);
2744 return -1;
2745 }
2746 }
2747
2748 /* No IMAP account available */
2749 if (vmu->imapuser[0] == '\0') {
2750 ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2751 free_user(vmu);
2752 return -1;
2753 }
2754
2755 /* check if someone is accessing this box right now... */
2756 vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
2757 if (!vms_p) {
2758 vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
2759 }
2760 if (vms_p) {
2761 ast_debug(3, "Returning before search - user is logged in\n");
2762 if (fold == 0) { /* INBOX */
2763 free_user(vmu);
2764 return urgent ? vms_p->urgentmessages : vms_p->newmessages;
2765 }
2766 if (fold == 1) { /* Old messages */
2767 free_user(vmu);
2768 return vms_p->oldmessages;
2769 }
2770 }
2771
2772 /* add one if not there... */
2773 vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
2774 if (!vms_p) {
2775 vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
2776 }
2777
2778 if (!vms_p) {
2779 vms_p = create_vm_state_from_user(vmu);
2780 }
2781 ret = init_mailstream(vms_p, fold);
2782 if (!vms_p->mailstream) {
2783 ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
2784 free_user(vmu);
2785 return -1;
2786 }
2787 if (ret == 0) {
2788 ast_mutex_lock(&vms_p->lock);
2789 pgm = mail_newsearchpgm ();
2790 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
2791 hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
2792 pgm->header = hdr;
2793 if (fold != OLD_FOLDER) {
2794 pgm->unseen = 1;
2795 pgm->seen = 0;
2796 }
2797 /* In the special case where fold is 1 (old messages) we have to do things a bit
2798 * differently. Old messages are stored in the INBOX but are marked as "seen"
2799 */
2800 else {
2801 pgm->unseen = 0;
2802 pgm->seen = 1;
2803 }
2804 /* look for urgent messages */
2805 if (fold == NEW_FOLDER) {
2806 if (urgent) {
2807 pgm->flagged = 1;
2808 pgm->unflagged = 0;
2809 } else {
2810 pgm->flagged = 0;
2811 pgm->unflagged = 1;
2812 }
2813 }
2814 pgm->undeleted = 1;
2815 pgm->deleted = 0;
2816
2817 vms_p->vmArrayIndex = 0;
2818 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2819 if (fold == 0 && urgent == 0)
2820 vms_p->newmessages = vms_p->vmArrayIndex;
2821 if (fold == 1)
2822 vms_p->oldmessages = vms_p->vmArrayIndex;
2823 if (fold == 0 && urgent == 1)
2824 vms_p->urgentmessages = vms_p->vmArrayIndex;
2825 /*Freeing the searchpgm also frees the searchhdr*/
2826 mail_free_searchpgm(&pgm);
2827 ast_mutex_unlock(&vms_p->lock);
2828 free_user(vmu);
2829 vms_p->updated = 0;
2830 return vms_p->vmArrayIndex;
2831 } else {
2832 ast_mutex_lock(&vms_p->lock);
2833 mail_ping(vms_p->mailstream);
2834 ast_mutex_unlock(&vms_p->lock);
2835 }
2836 free_user(vmu);
2837 return 0;
2838}
2839
2840static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
2841{
2842 /* Check if mailbox is full */
2843 check_quota(vms, vmu->imapfolder);
2844 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
2845 ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
2846 if (chan) {
2847 ast_play_and_wait(chan, "vm-mailboxfull");
2848 }
2849 return -1;
2850 }
2851
2852 /* Check if we have exceeded maxmsg */
2853 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));
2854 if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
2855 ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
2856 if (chan) {
2857 ast_play_and_wait(chan, "vm-mailboxfull");
2858 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2859 }
2860 return -1;
2861 }
2862
2863 return 0;
2864}
2865
2866/*!
2867 * \brief Gets the number of messages that exist in a mailbox folder.
2868 * \param mailbox_id
2869 * \param folder
2870 *
2871 * This method is used when IMAP backend is used.
2872 * \return The number of messages in this mailbox folder (zero or more).
2873 */
2874static int messagecount(const char *mailbox_id, const char *folder)
2875{
2876 char *context;
2877 char *mailbox;
2878 int count;
2879
2880 if (ast_strlen_zero(mailbox_id)
2881 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
2882 return 0;
2883 }
2884
2885 if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
2886 count = __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
2887 } else {
2888 count = __messagecount(context, mailbox, folder);
2889 }
2890 return count < 0 ? 0 : count;
2891}
2892
2893static 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)
2894{
2895 char *myserveremail = serveremail;
2896 char fn[PATH_MAX];
2897 char introfn[PATH_MAX];
2898 char mailbox[256];
2899 char *stringp;
2900 FILE *p = NULL;
2901 char tmp[80] = "/tmp/astmail-XXXXXX";
2902 long len;
2903 void *buf;
2904 int tempcopy = 0;
2905 STRING str;
2906 int ret; /* for better error checking */
2907 char *imap_flags = NIL;
2908 int msgcount;
2909 int box = NEW_FOLDER;
2910
2911 snprintf(mailbox, sizeof(mailbox), "%s@%s", vmu->mailbox, vmu->context);
2912 msgcount = messagecount(mailbox, "INBOX") + messagecount(mailbox, "Old");
2913
2914 /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
2915 if (msgnum < 0) {
2916 if(!imapgreetings) {
2917 return 0;
2918 } else {
2919 box = GREETINGS_FOLDER;
2920 }
2921 }
2922
2923 if (imap_check_limits(chan, vms, vmu, msgcount)) {
2924 return -1;
2925 }
2926
2927 /* Set urgent flag for IMAP message */
2928 if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
2929 ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
2930 imap_flags = "\\FLAGGED";
2931 }
2932
2933 /* Attach only the first format */
2934 fmt = ast_strdupa(fmt);
2935 stringp = fmt;
2936 strsep(&stringp, "|");
2937
2938 if (!ast_strlen_zero(vmu->serveremail))
2939 myserveremail = vmu->serveremail;
2940
2941 if (msgnum > -1)
2942 make_file(fn, sizeof(fn), dir, msgnum);
2943 else
2944 ast_copy_string (fn, dir, sizeof(fn));
2945
2946 snprintf(introfn, sizeof(introfn), "%sintro", fn);
2947 if (ast_fileexists(introfn, NULL, NULL) <= 0) {
2948 *introfn = '\0';
2949 }
2950
2951 if (ast_strlen_zero(vmu->email)) {
2952 /* We need the vmu->email to be set when we call make_email_file, but
2953 * if we keep it set, a duplicate e-mail will be created. So at the end
2954 * of this function, we will revert back to an empty string if tempcopy
2955 * is 1.
2956 */
2957 vmu->email = ast_strdup(vmu->imapuser);
2958 tempcopy = 1;
2959 }
2960
2961 if (!strcmp(fmt, "wav49"))
2962 fmt = "WAV";
2963 ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2964
2965 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2966 command hangs. */
2967 if (!(p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask))) {
2968 ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2969 if (tempcopy) {
2970 ast_free(vmu->email);
2971 vmu->email = NULL;
2972 }
2973 return -1;
2974 }
2975
2976 if (msgnum < 0 && imapgreetings) {
2977 if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
2978 ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
2979 return -1;
2980 }
2981 imap_delete_old_greeting(fn, vms);
2982 }
2983
2984 make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
2985 chan ? S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL) : NULL,
2986 chan ? S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL) : NULL,
2987 fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
2988 /* read mail file to memory */
2989 len = ftell(p);
2990 rewind(p);
2991 if (!(buf = ast_malloc(len + 1))) {
2992 ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
2993 fclose(p);
2994 if (tempcopy)
2995 *(vmu->email) = '\0';
2996 return -1;
2997 }
2998 if (fread(buf, 1, len, p) != len) {
2999 if (ferror(p)) {
3000 ast_log(LOG_ERROR, "Error while reading mail file: %s\n", tmp);
3001 return -1;
3002 }
3003 }
3004 ((char *) buf)[len] = '\0';
3005 INIT(&str, mail_string, buf, len);
3006 ret = init_mailstream(vms, box);
3007 if (ret == 0) {
3008 imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
3009 ast_mutex_lock(&vms->lock);
3010 if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
3011 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
3012 ast_mutex_unlock(&vms->lock);
3013 fclose(p);
3014 unlink(tmp);
3015 ast_free(buf);
3016 } else {
3017 ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
3018 fclose(p);
3019 unlink(tmp);
3020 ast_free(buf);
3021 return -1;
3022 }
3023 ast_debug(3, "%s stored\n", fn);
3024
3025 if (tempcopy)
3026 *(vmu->email) = '\0';
3027 inprocess_count(vmu->mailbox, vmu->context, -1);
3028 return 0;
3029
3030}
3031
3032/*!
3033 * \brief Gets the number of messages that exist in the inbox folder.
3034 * \param mailbox_context
3035 * \param newmsgs The variable that is updated with the count of new messages within this inbox.
3036 * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
3037 * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
3038 *
3039 * This method is used when IMAP backend is used.
3040 * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
3041 *
3042 * \return zero on success, -1 on error.
3043 */
3044
3045static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
3046{
3047 char tmp[PATH_MAX] = "";
3048 char *mailboxnc;
3049 char *context;
3050 char *mb;
3051 char *cur;
3052 int count = 0;
3053 if (newmsgs)
3054 *newmsgs = 0;
3055 if (oldmsgs)
3056 *oldmsgs = 0;
3057 if (urgentmsgs)
3058 *urgentmsgs = 0;
3059
3060 ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
3061 /* If no mailbox, return immediately */
3062 if (ast_strlen_zero(mailbox_context))
3063 return 0;
3064
3065 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
3066 context = strchr(tmp, '@');
3067 if (strchr(mailbox_context, ',')) {
3068 int tmpnew, tmpold, tmpurgent;
3069 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
3070 mb = tmp;
3071 while ((cur = strsep(&mb, ", "))) {
3072 if (!ast_strlen_zero(cur)) {
3073 if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
3074 return -1;
3075 else {
3076 if (newmsgs)
3077 *newmsgs += tmpnew;
3078 if (oldmsgs)
3079 *oldmsgs += tmpold;
3080 if (urgentmsgs)
3081 *urgentmsgs += tmpurgent;
3082 }
3083 }
3084 }
3085 return 0;
3086 }
3087 if (context) {
3088 *context = '\0';
3089 mailboxnc = tmp;
3090 context++;
3091 } else {
3092 context = "default";
3093 mailboxnc = (char *) mailbox_context;
3094 }
3095
3096 if (newmsgs) {
3097 struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
3098 if (!vmu) {
3099 ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
3100 return -1;
3101 }
3102 if ((count = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
3103 free_user(vmu);
3104 return -1;
3105 }
3106 *newmsgs = count;
3107 free_user(vmu);
3108 }
3109 if (oldmsgs) {
3110 if ((count = __messagecount(context, mailboxnc, "Old")) < 0) {
3111 return -1;
3112 }
3113 *oldmsgs = count;
3114 }
3115 if (urgentmsgs) {
3116 if ((count = __messagecount(context, mailboxnc, "Urgent")) < 0) {
3117 return -1;
3118 }
3119 *urgentmsgs = count;
3120 }
3121 return 0;
3122}
3123
3124/*!
3125 * \brief Determines if the given folder has messages.
3126 * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
3127 * \param folder the folder to look in
3128 *
3129 * This function is used when the mailbox is stored in an IMAP back end.
3130 * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
3131 * \return 1 if the folder has one or more messages. zero otherwise.
3132 */
3133
3134static int has_voicemail(const char *mailbox, const char *folder)
3135{
3136 char tmp[256], *tmp2, *box, *context;
3137 ast_copy_string(tmp, mailbox, sizeof(tmp));
3138 tmp2 = tmp;
3139 if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
3140 while ((box = strsep(&tmp2, ",&"))) {
3141 if (!ast_strlen_zero(box)) {
3142 if (has_voicemail(box, folder)) {
3143 return 1;
3144 }
3145 }
3146 }
3147 }
3148 if ((context = strchr(tmp, '@'))) {
3149 *context++ = '\0';
3150 } else {
3151 context = "default";
3152 }
3153 return __messagecount(context, tmp, folder) > 0 ? 1 : 0;
3154}
3155
3156/*!
3157 * \brief Copies a message from one mailbox to another.
3158 * \param chan
3159 * \param vmu
3160 * \param imbox
3161 * \param msgnum
3162 * \param duration
3163 * \param recip
3164 * \param fmt
3165 * \param dir
3166 * \param flag, dest_folder
3167 *
3168 * This works with IMAP storage based mailboxes.
3169 *
3170 * \return zero on success, -1 on error.
3171 */
3172static 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)
3173{
3174 struct vm_state *sendvms = NULL;
3175 char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
3176 if (msgnum >= recip->maxmsg) {
3177 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
3178 return -1;
3179 }
3180 if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
3181 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
3182 return -1;
3183 }
3184 if (!get_vm_state_by_imapuser(recip->imapuser, 0)) {
3185 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
3186 return -1;
3187 }
3188 snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
3189 ast_mutex_lock(&sendvms->lock);
3190 if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
3191 ast_mutex_unlock(&sendvms->lock);
3192 return 0;
3193 }
3194 ast_mutex_unlock(&sendvms->lock);
3195 ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
3196 return -1;
3197}
3198
3199static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
3200{
3201 char tmp[256], *t = tmp;
3202 size_t left = sizeof(tmp);
3203
3204 if (box == OLD_FOLDER) {
3205 ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
3206 } else {
3207 ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
3208 }
3209
3210 if (box == NEW_FOLDER) {
3211 ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
3212 } else {
3213 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
3214 }
3215
3216 /* Build up server information */
3217 ast_build_string(&t, &left, "{%s:%s/imap", S_OR(vms->imapserver, imapserver), S_OR(vms->imapport, imapport));
3218
3219 /* Add authentication user if present */
3220 if (!ast_strlen_zero(authuser))
3221 ast_build_string(&t, &left, "/authuser=%s", authuser);
3222
3223 /* Add flags if present */
3224 if (!ast_strlen_zero(imapflags) || !(ast_strlen_zero(vms->imapflags))) {
3225 ast_build_string(&t, &left, "/%s", S_OR(vms->imapflags, imapflags));
3226 }
3227
3228 /* End with username */
3229#if 1
3230 ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
3231#else
3232 ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
3233#endif
3234 if (box == NEW_FOLDER || box == OLD_FOLDER)
3235 snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
3236 else if (box == GREETINGS_FOLDER)
3237 snprintf(spec, len, "%s%s", tmp, greetingfolder);
3238 else { /* Other folders such as Friends, Family, etc... */
3239 if (!ast_strlen_zero(imapparentfolder)) {
3240 /* imapparentfolder would typically be set to INBOX */
3241 snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
3242 } else {
3243 snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
3244 }
3245 }
3246}
3247
3248static int init_mailstream(struct vm_state *vms, int box)
3249{
3250 MAILSTREAM *stream = NIL;
3251 long debug;
3252 char tmp[256];
3253
3254 if (!vms) {
3255 ast_log(LOG_ERROR, "vm_state is NULL!\n");
3256 return -1;
3257 }
3258 ast_debug(3, "vm_state user is:%s\n", vms->imapuser);
3259 if (vms->mailstream == NIL || !vms->mailstream) {
3260 ast_debug(1, "mailstream not set.\n");
3261 } else {
3262 stream = vms->mailstream;
3263 }
3264 /* debug = T; user wants protocol telemetry? */
3265 debug = NIL; /* NO protocol telemetry? */
3266
3267 if (delimiter == '\0') { /* did not probe the server yet */
3268 char *cp;
3269#ifdef USE_SYSTEM_IMAP
3270#include <imap/linkage.c>
3271#elif defined(USE_SYSTEM_CCLIENT)
3272#include <c-client/linkage.c>
3273#else
3274#include "linkage.c"
3275#endif
3276 /* Connect to INBOX first to get folders delimiter */
3277 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
3278 ast_mutex_lock(&vms->lock);
3279 ast_mutex_lock(&mail_open_lock);
3280 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
3281 ast_mutex_unlock(&mail_open_lock);
3282 ast_mutex_unlock(&vms->lock);
3283 if (stream == NIL) {
3284 ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
3285 return -1;
3286 }
3287 get_mailbox_delimiter(vms, stream);
3288 /* update delimiter in imapfolder */
3289 for (cp = vms->imapfolder; *cp; cp++)
3290 if (*cp == '/')
3291 *cp = delimiter;
3292 }
3293 /* Now connect to the target folder */
3294 imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
3295 ast_debug(3, "Before mail_open, server: %s, box:%d\n", tmp, box);
3296 ast_mutex_lock(&vms->lock);
3297 ast_mutex_lock(&mail_open_lock);
3298 vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
3299 /* Create the folder if it doesn't exist */
3300 if (vms->mailstream && !mail_status(vms->mailstream, tmp, SA_UIDNEXT)) {
3301 mail_create(vms->mailstream, tmp);
3302 }
3303 ast_mutex_unlock(&mail_open_lock);
3304 ast_mutex_unlock(&vms->lock);
3305 if (vms->mailstream == NIL) {
3306 return -1;
3307 } else {
3308 return 0;
3309 }
3310}
3311
3312static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
3313{
3314 SEARCHPGM *pgm;
3315 SEARCHHEADER *hdr;
3316 int urgent = 0;
3317
3318 /* If Urgent, then look at INBOX */
3319 if (box == 11) {
3320 box = NEW_FOLDER;
3321 urgent = 1;
3322 }
3323
3324 ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
3325 ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
3326 ast_copy_string(vms->imapserver, vmu->imapserver, sizeof(vms->imapserver));
3327 ast_copy_string(vms->imapport, vmu->imapport, sizeof(vms->imapport));
3328 ast_copy_string(vms->imapflags, vmu->imapflags, sizeof(vms->imapflags));
3329 vms->imapversion = vmu->imapversion;
3330 ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
3331
3332 if (init_mailstream(vms, box) || !vms->mailstream) {
3333 ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
3334 return -1;
3335 }
3336
3337 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
3338
3339 /* Check Quota */
3340 if (box == 0) {
3341 ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
3342 check_quota(vms, (char *) mbox(vmu, box));
3343 }
3344
3345 ast_mutex_lock(&vms->lock);
3346 pgm = mail_newsearchpgm();
3347
3348 /* Check IMAP folder for Asterisk messages only... */
3349 hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
3350 hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
3351 pgm->header = hdr;
3352 pgm->deleted = 0;
3353 pgm->undeleted = 1;
3354
3355 /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
3356 if (box == NEW_FOLDER && urgent == 1) {
3357 pgm->unseen = 1;
3358 pgm->seen = 0;
3359 pgm->flagged = 1;
3360 pgm->unflagged = 0;
3361 } else if (box == NEW_FOLDER && urgent == 0) {
3362 pgm->unseen = 1;
3363 pgm->seen = 0;
3364 pgm->flagged = 0;
3365 pgm->unflagged = 1;
3366 } else if (box == OLD_FOLDER) {
3367 pgm->seen = 1;
3368 pgm->unseen = 0;
3369 }
3370
3371 ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
3372
3373 vms->vmArrayIndex = 0;
3374 mail_search_full (vms->mailstream, NULL, pgm, NIL);
3375 vms->lastmsg = vms->vmArrayIndex - 1;
3376 mail_free_searchpgm(&pgm);
3377 /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
3378 * ensure to allocate enough space to account for all of them. Warn if old messages
3379 * have not been checked first as that is required.
3380 */
3381 if (box == 0 && !vms->dh_arraysize) {
3382 ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
3383 }
3384 if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
3385 ast_mutex_unlock(&vms->lock);
3386 return -1;
3387 }
3388
3389 ast_mutex_unlock(&vms->lock);
3390 return 0;
3391}
3392
3393static void write_file(char *filename, char *buffer, unsigned long len)
3394{
3395 FILE *output;
3396
3397 if (!filename || !buffer) {
3398 return;
3399 }
3400
3401 if (!(output = fopen(filename, "w"))) {
3402 ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", filename, strerror(errno));
3403 return;
3404 }
3405
3406 if (fwrite(buffer, len, 1, output) != 1) {
3407 if (ferror(output)) {
3408 ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
3409 }
3410 }
3411 fclose (output);
3412}
3413
3414static void update_messages_by_imapuser(const char *user, unsigned long number)
3415{
3416 struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
3417
3418 if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
3419 return;
3420 }
3421
3422 ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
3423
3424 /* Ensure we have room for the next message. */
3425 if (vms->vmArrayIndex >= vms->msg_array_max) {
3426 long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
3427 if (!new_mem) {
3428 return;
3429 }
3430 vms->msgArray = new_mem;
3431 vms->msg_array_max *= 2;
3432 }
3433
3434 vms->msgArray[vms->vmArrayIndex++] = number;
3435}
3436
3437void mm_searched(MAILSTREAM *stream, unsigned long number)
3438{
3439 char *mailbox = stream->mailbox, buf[1024] = "", *user;
3440
3441 if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
3442 return;
3443
3444 update_messages_by_imapuser(user, number);
3445}
3446
3447static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
3448{
3449 struct ast_variable *var;
3450 struct ast_vm_user *vmu;
3451
3452 vmu = ast_calloc(1, sizeof *vmu);
3453 if (!vmu)
3454 return NULL;
3455
3456 populate_defaults(vmu);
3458
3459 var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
3460 if (var) {
3461 apply_options_full(vmu, var);
3463 return vmu;
3464 } else {
3465 ast_free(vmu);
3466 return NULL;
3467 }
3468}
3469
3470/* Interfaces to C-client */
3471
3472void mm_exists(MAILSTREAM * stream, unsigned long number)
3473{
3474 /* mail_ping will callback here if new mail! */
3475 ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
3476 if (number == 0) return;
3477 set_update(stream);
3478}
3479
3480
3481void mm_expunged(MAILSTREAM * stream, unsigned long number)
3482{
3483 /* mail_ping will callback here if expunged mail! */
3484 ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
3485 if (number == 0) return;
3486 set_update(stream);
3487}
3488
3489
3490void mm_flags(MAILSTREAM * stream, unsigned long number)
3491{
3492 /* mail_ping will callback here if read mail! */
3493 ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
3494 if (number == 0) return;
3495 set_update(stream);
3496}
3497
3498
3499void mm_notify(MAILSTREAM * stream, char *string, long errflg)
3500{
3501 ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
3502 mm_log (string, errflg);
3503}
3504
3505
3506void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
3507{
3508 if (delimiter == '\0') {
3509 delimiter = delim;
3510 }
3511
3512 ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
3513 if (attributes & LATT_NOINFERIORS)
3514 ast_debug(5, "no inferiors\n");
3515 if (attributes & LATT_NOSELECT)
3516 ast_debug(5, "no select\n");
3517 if (attributes & LATT_MARKED)
3518 ast_debug(5, "marked\n");
3519 if (attributes & LATT_UNMARKED)
3520 ast_debug(5, "unmarked\n");
3521}
3522
3523
3524void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
3525{
3526 ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
3527 if (attributes & LATT_NOINFERIORS)
3528 ast_debug(5, "no inferiors\n");
3529 if (attributes & LATT_NOSELECT)
3530 ast_debug(5, "no select\n");
3531 if (attributes & LATT_MARKED)
3532 ast_debug(5, "marked\n");
3533 if (attributes & LATT_UNMARKED)
3534 ast_debug(5, "unmarked\n");
3535}
3536
3537
3538void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
3539{
3540 struct ast_str *str;
3541
3542 if (!DEBUG_ATLEAST(5) || !(str = ast_str_create(256))) {
3543 return;
3544 }
3545
3546 ast_str_append(&str, 0, " Mailbox %s", mailbox);
3547 if (status->flags & SA_MESSAGES) {
3548 ast_str_append(&str, 0, ", %lu messages", status->messages);
3549 }
3550 if (status->flags & SA_RECENT) {
3551 ast_str_append(&str, 0, ", %lu recent", status->recent);
3552 }
3553 if (status->flags & SA_UNSEEN) {
3554 ast_str_append(&str, 0, ", %lu unseen", status->unseen);
3555 }
3556 if (status->flags & SA_UIDVALIDITY) {
3557 ast_str_append(&str, 0, ", %lu UID validity", status->uidvalidity);
3558 }
3559 if (status->flags & SA_UIDNEXT) {
3560 ast_str_append(&str, 0, ", %lu next UID", status->uidnext);
3561 }
3563
3564 ast_free(str);
3565}
3566
3567
3568void mm_log(char *string, long errflg)
3569{
3570 switch ((short) errflg) {
3571 case NIL:
3572 ast_debug(1, "IMAP Info: %s\n", string);
3573 break;
3574 case PARSE:
3575 case WARN:
3576 ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
3577 break;
3578 case ERROR:
3579 ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
3580 break;
3581 }
3582}
3583
3584
3585void mm_dlog(char *string)
3586{
3587 ast_log(AST_LOG_NOTICE, "%s\n", string);
3588}
3589
3590
3591void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
3592{
3593 struct ast_vm_user *vmu;
3594
3595 ast_debug(4, "Entering callback mm_login\n");
3596
3597 ast_copy_string(user, mb->user, MAILTMPLEN);
3598
3599 /* We should only do this when necessary */
3600 if (!ast_strlen_zero(authpassword)) {
3601 ast_copy_string(pwd, authpassword, MAILTMPLEN);
3602 } else {
3603 AST_LIST_TRAVERSE(&users, vmu, list) {
3604 if (!strcasecmp(mb->user, vmu->imapuser)) {
3605 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
3606 break;
3607 }
3608 }
3609 if (!vmu) {
3610 if ((vmu = find_user_realtime_imapuser(mb->user))) {
3611 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
3612 free_user(vmu);
3613 }
3614 }
3615 }
3616}
3617
3618
3619void mm_critical(MAILSTREAM * stream)
3620{
3621}
3622
3623
3624void mm_nocritical(MAILSTREAM * stream)
3625{
3626}
3627
3628
3629long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
3630{
3631 kill (getpid (), SIGSTOP);
3632 return NIL;
3633}
3634
3635
3636void mm_fatal(char *string)
3637{
3638 ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
3639}
3640
3641/* C-client callback to handle quota */
3642static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
3643{
3644 struct vm_state *vms;
3645 char *mailbox = stream->mailbox, *user;
3646 char buf[1024] = "";
3647 unsigned long usage = 0, limit = 0;
3648
3649 while (pquota) {
3650 usage = pquota->usage;
3651 limit = pquota->limit;
3652 pquota = pquota->next;
3653 }
3654
3655 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)))) {
3656 ast_log(AST_LOG_ERROR, "No state found.\n");
3657 return;
3658 }
3659
3660 ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
3661
3662 vms->quota_usage = usage;
3663 vms->quota_limit = limit;
3664}
3665
3666static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
3667{
3668 char *start, *eol_pnt;
3669 int taglen;
3670
3672 return NULL;
3673
3674 taglen = strlen(tag) + 1;
3675 if (taglen < 1)
3676 return NULL;
3677
3678 if (!(start = strcasestr(header, tag)))
3679 return NULL;
3680
3681 /* Since we can be called multiple times we should clear our buffer */
3682 memset(buf, 0, len);
3683
3684 ast_copy_string(buf, start+taglen, len);
3685 if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
3686 *eol_pnt = '\0';
3687 return buf;
3688}
3689
3690static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
3691{
3692 char *start, *eol_pnt, *quote;
3693
3695 return NULL;
3696
3697 if (!(start = strstr(mailbox, "/user=")))
3698 return NULL;
3699
3700 ast_copy_string(buf, start+6, len);
3701
3702 if (!(quote = strchr(buf, '"'))) {
3703 if ((eol_pnt = strchr(buf, '/')) || (eol_pnt = strchr(buf, '}'))) {
3704 *eol_pnt = '\0';
3705 }
3706 return buf;
3707 } else {
3708 if ((eol_pnt = strchr(quote + 1, '"'))) {
3709 *eol_pnt = '\0';
3710 }
3711 return quote + 1;
3712 }
3713}
3714
3715static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
3716{
3717 struct vm_state *vms_p;
3718
3719 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3720 if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
3721 return vms_p;
3722 }
3723 ast_debug(5, "Adding new vmstate for %s\n", vmu->imapuser);
3724 /* XXX: Is this correctly freed always? */
3725 if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
3726 return NULL;
3727 ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
3728 ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
3729 ast_copy_string(vms_p->imapserver, vmu->imapserver, sizeof(vms_p->imapserver));
3730 ast_copy_string(vms_p->imapport, vmu->imapport, sizeof(vms_p->imapport));
3731 ast_copy_string(vms_p->imapflags, vmu->imapflags, sizeof(vms_p->imapflags));
3732 ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
3733 ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
3734 vms_p->mailstream = NIL; /* save for access from interactive entry point */
3735 vms_p->imapversion = vmu->imapversion;
3736 ast_debug(5, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
3737 vms_p->updated = 1;
3738 /* set mailbox to INBOX! */
3739 ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
3740 init_vm_state(vms_p);
3741 vmstate_insert(vms_p);
3742 return vms_p;
3743}
3744
3745static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
3746{
3747 struct vmstate *vlist = NULL;
3748
3749 if (interactive) {
3750 struct vm_state *vms;
3751 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3752 if ((vms = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms->imapuser, user)) {
3753 return vms;
3754 }
3755 }
3756
3757 AST_LIST_LOCK(&vmstates);
3758 AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3759 if (!vlist->vms) {
3760 ast_debug(3, "error: vms is NULL for %s\n", user);
3761 continue;
3762 }
3763 if (vlist->vms->imapversion != imapversion) {
3764 continue;
3765 }
3766
3767 if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
3768 AST_LIST_UNLOCK(&vmstates);
3769 return vlist->vms;
3770 }
3771 }
3772 AST_LIST_UNLOCK(&vmstates);
3773
3774 ast_debug(3, "%s not found in vmstates\n", user);
3775
3776 return NULL;
3777}
3778
3779static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
3780{
3781
3782 struct vmstate *vlist = NULL;
3783 const char *local_context = S_OR(context, "default");
3784
3785 if (interactive) {
3786 struct vm_state *vms;
3787 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3788 if ((vms = pthread_getspecific(ts_vmstate.key)) &&
3789 !strcmp(vms->username,mailbox) && !strcmp(vms->context, local_context)) {
3790 return vms;
3791 }
3792 }
3793
3794 AST_LIST_LOCK(&vmstates);
3795 AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3796 if (!vlist->vms) {
3797 ast_debug(3, "error: vms is NULL for %s\n", mailbox);
3798 continue;
3799 }
3800 if (vlist->vms->imapversion != imapversion) {
3801 continue;
3802 }
3803
3804 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);
3805
3806 if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
3807 ast_debug(3, "Found it!\n");
3808 AST_LIST_UNLOCK(&vmstates);
3809 return vlist->vms;
3810 }
3811 }
3812 AST_LIST_UNLOCK(&vmstates);
3813
3814 ast_debug(3, "%s not found in vmstates\n", mailbox);
3815
3816 return NULL;
3817}
3818
3819static void vmstate_insert(struct vm_state *vms)
3820{
3821 struct vmstate *v;
3822 struct vm_state *altvms;
3823
3824 /* If interactive, it probably already exists, and we should
3825 use the one we already have since it is more up to date.
3826 We can compare the username to find the duplicate */
3827 if (vms->interactive == 1) {
3828 altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
3829 if (altvms) {
3830 ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3831 vms->newmessages = altvms->newmessages;
3832 vms->oldmessages = altvms->oldmessages;
3833 vms->vmArrayIndex = altvms->vmArrayIndex;
3834 /* XXX: no msgArray copying? */
3835 vms->lastmsg = altvms->lastmsg;
3836 vms->curmsg = altvms->curmsg;
3837 /* get a pointer to the persistent store */
3838 vms->persist_vms = altvms;
3839 /* Reuse the mailstream? */
3840#ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
3841 vms->mailstream = altvms->mailstream;
3842#else
3843 vms->mailstream = NIL;
3844#endif
3845 }
3846 return;
3847 }
3848
3849 if (!(v = ast_calloc(1, sizeof(*v))))
3850 return;
3851
3852 v->vms = vms;
3853
3854 ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3855
3856 AST_LIST_LOCK(&vmstates);
3857 AST_LIST_INSERT_TAIL(&vmstates, v, list);
3858 AST_LIST_UNLOCK(&vmstates);
3859}
3860
3861static void vmstate_delete(struct vm_state *vms)
3862{
3863 struct vmstate *vc = NULL;
3864 struct vm_state *altvms = NULL;
3865
3866 /* If interactive, we should copy pertinent info
3867 back to the persistent state (to make update immediate) */
3868 if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
3869 ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3870 altvms->newmessages = vms->newmessages;
3871 altvms->oldmessages = vms->oldmessages;
3872 altvms->updated = 1;
3873 vms->mailstream = mail_close(vms->mailstream);
3874
3875 /* Interactive states are not stored within the persistent list */
3876 return;
3877 }
3878
3879 ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3880
3881 AST_LIST_LOCK(&vmstates);
3882 AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
3883 if (vc->vms == vms) {
3885 break;
3886 }
3887 }
3889 AST_LIST_UNLOCK(&vmstates);
3890
3891 if (vc) {
3892 ast_mutex_destroy(&vc->vms->lock);
3893 ast_free(vc->vms->msgArray);
3894 vc->vms->msgArray = NULL;
3895 vc->vms->msg_array_max = 0;
3896 /* XXX: is no one supposed to free vms itself? */
3897 ast_free(vc);
3898 } else {
3899 ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3900 }
3901}
3902
3903static void set_update(MAILSTREAM * stream)
3904{
3905 struct vm_state *vms;
3906 char *mailbox = stream->mailbox, *user;
3907 char buf[1024] = "";
3908
3909 if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
3910 if (user && DEBUG_ATLEAST(3))
3911 ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
3912 return;
3913 }
3914
3915 ast_debug(3, "User %s mailbox set for update.\n", user);
3916
3917 vms->updated = 1; /* Set updated flag since mailbox changed */
3918}
3919
3920static void init_vm_state(struct vm_state *vms)
3921{
3922 vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
3923 vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
3924 if (!vms->msgArray) {
3925 /* Out of mem? This can't be good. */
3926 vms->msg_array_max = 0;
3927 }
3928 vms->vmArrayIndex = 0;
3929 ast_mutex_init(&vms->lock);
3930}
3931
3932static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro)
3933{
3934 char *body_content;
3935 char *body_decoded;
3936 char *fn = is_intro ? vms->introfn : vms->fn;
3937 unsigned long len = 0;
3938 unsigned long newlen = 0;
3939 char filename[256];
3940
3941 if (!body || body == NIL)
3942 return -1;
3943
3944 ast_mutex_lock(&vms->lock);
3945 body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
3946 ast_mutex_unlock(&vms->lock);
3949 "Msgno %ld, section %s. The body's content size %ld is huge (max %ld). User:%s, mailbox %s\n",
3950 vms->msgArray[vms->curmsg], section, len, MAX_MAIL_BODY_CONTENT_SIZE, vms->imapuser, vms->username);
3951 return -1;
3952 }
3953 if (body_content != NIL && len) {
3954 snprintf(filename, sizeof(filename), "%s.%s", fn, format);
3955 /* ast_debug(1, body_content); */
3956 body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
3957 /* If the body of the file is empty, return an error */
3958 if (!newlen || !body_decoded) {
3959 return -1;
3960 }
3961 write_file(filename, (char *) body_decoded, newlen);
3962 } else {
3963 ast_debug(5, "Body of message is NULL.\n");
3964 return -1;
3965 }
3966 return 0;
3967}
3968
3969/*!
3970 * \brief Get delimiter via mm_list callback
3971 * \param vms The voicemail state object
3972 * \param stream
3973 *
3974 * Determines the delimiter character that is used by the underlying IMAP based mail store.
3975 */
3976/* MUTEX should already be held */
3977static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream) {
3978 char tmp[50];
3979 snprintf(tmp, sizeof(tmp), "{%s}", S_OR(vms->imapserver, imapserver));
3980 mail_list(stream, tmp, "*");
3981}
3982
3983/*!
3984 * \brief Check Quota for user
3985 * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
3986 * \param mailbox the mailbox to check the quota for.
3987 *
3988 * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
3989 */
3990static void check_quota(struct vm_state *vms, char *mailbox) {
3991 ast_mutex_lock(&vms->lock);
3992 mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
3993 ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
3994 if (vms && vms->mailstream != NULL) {
3995 imap_getquotaroot(vms->mailstream, mailbox);
3996 } else {
3997 ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
3998 }
3999 ast_mutex_unlock(&vms->lock);
4000}
4001
4002#endif /* IMAP_STORAGE */
4003
4004/*! \brief Lock file path
4005 * only return failure if ast_lock_path returns 'timeout',
4006 * not if the path does not exist or any other reason
4007 */
4008static int vm_lock_path(const char *path)
4009{
4010 switch (ast_lock_path(path)) {
4011 case AST_LOCK_TIMEOUT:
4012 return -1;
4013 default:
4014 return 0;
4015 }
4016}
4017
4018#define MSG_ID_LEN 256
4019
4020/* Used to attach a unique identifier to an msg_id */
4022
4023/*!
4024 * \brief Sets the destination string to a uniquely identifying msg_id string
4025 * \param dst pointer to a character buffer that should contain MSG_ID_LEN characters.
4026 */
4027static void generate_msg_id(char *dst);
4028
4029#ifdef ODBC_STORAGE
4030
4031/*!
4032 * \internal
4033 * \brief Create a buffer containing the SQL statement with the table name inserted.
4034 *
4035 * \param __sql_fmt The SQL statement with a single '%s' where the table name should be inserted.
4036 *
4037 * \note The buffer is allocated on the stack and should not be freed.
4038 *
4039 * \return A pointer to a buffer containing the SQL statement with the table name inserted.
4040 */
4041#define MAKE_SQL_PTRA(__sql_fmt) \
4042({ \
4043 /* The NULL terminator is included in odbc_table_len. */ \
4044 char *__sql = ast_alloca(strlen(__sql_fmt) + odbc_table_len); \
4045 sprintf(__sql, __sql_fmt, odbc_table); /* Safe */ \
4046 __sql; \
4047})
4048
4049/*!
4050 * \internal
4051 * \brief Create a buffer containing the SQL statement with the table name inserted twice.
4052 *
4053 * \param __sql_fmt The SQL statement with two '%s' where the table name should be inserted.
4054 *
4055 * \note The buffer is allocated on the stack and should not be freed.
4056 *
4057 * \return A pointer to a buffer containing the SQL statement with the table name inserted.
4058 */
4059#define MAKE_SQL_PTRA2(__sql_fmt) \
4060({ \
4061 /* The NULL terminator is included in odbc_table_len. */ \
4062 char *__sql = ast_alloca(strlen(__sql_fmt) + (odbc_table_len * 2)); \
4063 sprintf(__sql, __sql_fmt, odbc_table, odbc_table); /* Safe */ \
4064 __sql; \
4065})
4066
4067struct generic_prepare_struct {
4068 char *sql;
4069 int argc;
4070 char **argv;
4071};
4072
4073static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
4074{
4075 struct generic_prepare_struct *gps = data;
4076 int res, i;
4077 SQLHSTMT stmt;
4078
4079 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4080 if (!SQL_SUCCEEDED(res)) {
4081 ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
4082 return NULL;
4083 }
4084 res = ast_odbc_prepare(obj, stmt, gps->sql);
4085 if (!SQL_SUCCEEDED(res)) {
4086 ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
4087 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4088 return NULL;
4089 }
4090 for (i = 0; i < gps->argc; i++)
4091 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
4092
4093 return stmt;
4094}
4095
4096static void odbc_update_msg_id(char *dir, int msg_num, char *msg_id)
4097{
4098 SQLHSTMT stmt;
4099 char *sql = MAKE_SQL_PTRA("UPDATE %s SET msg_id=? WHERE dir=? AND msgnum=?");
4100 struct odbc_obj *obj;
4101 char msg_num_str[20];
4102 char *argv[] = { msg_id, dir, msg_num_str };
4103 struct generic_prepare_struct gps = { .sql = sql, .argc = 3, .argv = argv };
4104 SCOPE_ENTER(3, "dir: %s msg_num: %d msg_id: %s\n", dir, msg_num, msg_id);
4105
4106 obj = ast_odbc_request_obj(odbc_database, 0);
4107 if (!obj) {
4108 SCOPE_EXIT_LOG_RTN(LOG_WARNING, "Unable to update message ID for message %d in %s\n", msg_num, dir);
4109 }
4110
4111 snprintf(msg_num_str, sizeof(msg_num_str), "%d", msg_num);
4113 if (!stmt) {
4114 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4115 } else {
4116 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4117 }
4120}
4121
4122#define AUDIO_ON_DISK_MAGIC "AUDMAGIC"
4123#define AUDIO_ON_DISK_MAGIC_LEN 8
4124
4125static void odbc_update_set_audmagic(char *dir, int msg_num)
4126{
4127 SQLHSTMT stmt;
4128 char *sql = MAKE_SQL_PTRA("UPDATE %s SET recording=? WHERE dir=? AND msgnum=?");
4129 struct odbc_obj *obj;
4130 SQLLEN datalen = AUDIO_ON_DISK_MAGIC_LEN;
4131 SQLLEN indlen = datalen;
4132 int res;
4133 char msg_num_str[20];
4134 SCOPE_ENTER(3, "dir: %s msg_num: %d\n", dir, msg_num);
4135
4136 obj = ast_odbc_request_obj(odbc_database, 0);
4137 if (!obj) {
4138 SCOPE_EXIT_LOG_RTN(LOG_WARNING, "Unable to request obj for message %d in %s\n", msg_num, dir);
4139 }
4140
4141 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4142 if (!SQL_SUCCEEDED(res)) {
4144 SCOPE_EXIT_LOG_RTN(LOG_WARNING, "Unable to allocate stmt for message %d in %s\n", msg_num, dir);
4145 }
4146
4147 snprintf(msg_num_str, sizeof(msg_num_str), "%d", msg_num);
4148
4149 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
4150 datalen, 0, (void *) AUDIO_ON_DISK_MAGIC,
4151 datalen, &indlen);
4152
4153 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
4154 strlen(dir), 0, (void *) dir, 0, NULL);
4155
4156 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
4157 strlen(msg_num_str), 0, (void *) msg_num_str, 0, NULL);
4158
4159 res = ast_odbc_execute_sql(obj, stmt, sql);
4160 if (!SQL_SUCCEEDED(res)) {
4161 ast_log(LOG_WARNING, "Unable to execute stmt for message %d in %s\n", msg_num, dir);
4162 ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute");
4163 }
4164 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4165 stmt = NULL;
4166
4168 SCOPE_EXIT_RTN("Done\n");
4169}
4170
4171static int odbc_store_message(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum);
4172
4173/*!
4174 * \brief Retrieves a file from an ODBC data store.
4175 * \param dir the path to the file to be retrieved.
4176 * \param msgnum the message number, such as within a mailbox folder.
4177 *
4178 * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
4179 * 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.
4180 *
4181 * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
4182 * The output is the message information file with the name msgnum and the extension .txt
4183 * and the message file with the extension of its format, in the directory with base file name of the msgnum.
4184 *
4185 * \return 0 on success, -1 on error.
4186 */
4187static int odbc_retrieve_message(char *dir, int msgnum)
4188{
4189 int x = 0;
4190 int res;
4191 int fd = -1;
4192 size_t fdlen = 0;
4193 void *fdm = MAP_FAILED;
4194 SQLSMALLINT colcount = 0;
4195 SQLHSTMT stmt;
4196 char *sql = MAKE_SQL_PTRA("SELECT * FROM %s WHERE dir=? AND msgnum=?");
4197 char fmt[80] = "";
4198 char *c;
4199 char coltitle[256];
4200 SQLSMALLINT collen;
4201 SQLSMALLINT datatype;
4202 SQLSMALLINT decimaldigits;
4203 SQLSMALLINT nullable;
4204 SQLULEN colsize;
4205 SQLLEN colsize2;
4206 FILE *f = NULL;
4207 char rowdata[80];
4208 char *fn = MAKE_FILE_PTRA(dir, msgnum);
4209 char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
4210 char msgnums[80];
4211 char *mailboxuser = NULL;
4212 char *mailboxcontext = NULL;
4213 char msg_id[MSG_ID_LEN] = "";
4214 char *argv[] = { dir, msgnums };
4215 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4216 struct odbc_obj *obj;
4217 int storage_conversion_to_disk = 0;
4218 int storage_conversion_to_odbc = 0;
4219 SCOPE_ENTER(3, "dir: %s msgnum: %d msgtype: %s\n", dir, msgnum, msgnum < 0 ? "Greeting" : "Message");
4220
4221 obj = ast_odbc_request_obj(odbc_database, 0);
4222 if (!obj) {
4223 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4224 }
4225
4226 ast_copy_string(fmt, vmfmts, sizeof(fmt));
4227 c = strchr(fmt, '|');
4228 if (c)
4229 *c = '\0';
4230 if (!strcasecmp(fmt, "wav49"))
4231 strcpy(fmt, "WAV");
4232
4233 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4234
4235 ast_trace(-1, "Opening '%s' for writing\n", full_fn);
4236 if (!(f = fopen(full_fn, "w+"))) {
4237 ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
4238 goto bail;
4239 }
4240
4241 sprintf(full_fn, "%s.%s", fn, fmt); /* Safe. We're just replacing the file exten. */
4242
4244 if (!stmt) {
4245 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4246 goto bail;
4247 }
4248
4249 res = SQLFetch(stmt);
4250 if (!SQL_SUCCEEDED(res)) {
4251 if (res != SQL_NO_DATA) {
4252 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4253 }
4254 goto bail_with_handle;
4255 }
4256
4257 res = SQLNumResultCols(stmt, &colcount);
4258 if (!SQL_SUCCEEDED(res)) {
4259 ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
4260 goto bail_with_handle;
4261 }
4262
4263 fprintf(f, "[message]\n");
4264 for (x = 0; x < colcount; x++) {
4265 rowdata[0] = '\0';
4266 colsize = 0;
4267 collen = sizeof(coltitle);
4268 res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen,
4269 &datatype, &colsize, &decimaldigits, &nullable);
4270 if (!SQL_SUCCEEDED(res)) {
4271 ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
4272 goto bail_with_handle;
4273 }
4274
4275 if (!strcasecmp(coltitle, "recording")) {
4276 off_t offset;
4277 char tmp[1] = "";
4278
4279 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
4280 fdlen = colsize2;
4281 ast_trace(-1, "Audio size: %ld\n", colsize2);
4282 if (colsize2 == AUDIO_ON_DISK_MAGIC_LEN) {
4283 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, AUDIO_ON_DISK_MAGIC_LEN, NULL);
4284 if (memcmp(rowdata, AUDIO_ON_DISK_MAGIC, AUDIO_ON_DISK_MAGIC_LEN) != 0) {
4285 ast_log(AST_LOG_WARNING, "Invalid audio magic number '0x%02X%02X%02X%02X%02X%02X%02X%02X' for '%s'\n",
4286 rowdata[0], rowdata[1], rowdata[2], rowdata[3], rowdata[4], rowdata[5], rowdata[6],
4287 rowdata[7], full_fn);
4288 goto bail_with_handle;
4289 }
4290 ast_trace(-1, "Audio is stored on disk. No need to write '%s'\n", full_fn);
4292 storage_conversion_to_odbc = 1;
4293 }
4294
4295 continue;
4296 }
4297
4298 ast_trace(-1, "Opening '%s' for writing\n", full_fn);
4299 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
4300 if (fd < 0) {
4301 ast_log(AST_LOG_WARNING, "Failed to open '%s' for writing: %s\n", full_fn, strerror(errno));
4302 goto bail_with_handle;
4303 }
4305 storage_conversion_to_disk = 1;
4306 }
4307
4308 lseek(fd, fdlen - 1, SEEK_SET);
4309 if (write(fd, tmp, 1) != 1) {
4310 close(fd);
4311 fd = -1;
4312 continue;
4313 }
4314 /* Read out in small chunks */
4315 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
4316 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
4317 ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
4318 goto bail_with_handle;
4319 }
4320 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
4321 munmap(fdm, CHUNKSIZE);
4322 if (!SQL_SUCCEEDED(res)) {
4323 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4324 unlink(full_fn);
4325 goto bail_with_handle;
4326 }
4327 }
4328 if (truncate(full_fn, fdlen) < 0) {
4329 ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
4330 }
4331 ast_trace(-1, "Wrote %d bytes to '%s'\n", (int)fdlen, full_fn);
4332 } else {
4333 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4334 if (res == SQL_NULL_DATA && !strcasecmp(coltitle, "msg_id")) {
4335 /*
4336 * Generate msg_id if there wasn't one, but don't store it until we're
4337 * done with this connection.
4338 */
4339 generate_msg_id(msg_id);
4340 ast_trace(-1, "msg_id was NULL. Generating new one: %s\n", msg_id);
4341 snprintf(rowdata, sizeof(rowdata), "%s", msg_id);
4342 } else if (!strcasecmp(coltitle, "mailboxuser")) {
4343 mailboxuser = ast_strdupa(rowdata);
4344 } else if (!strcasecmp(coltitle, "mailboxcontext")) {
4345 mailboxcontext = ast_strdupa(rowdata);
4346 } else if (res == SQL_NULL_DATA && !strcasecmp(coltitle, "category")) {
4347 /* Ignore null column value for category */
4348 ast_trace(-1, "Ignoring null category column in ODBC voicemail retrieve_file.\n");
4349 continue;
4350 } else if (!SQL_SUCCEEDED(res)) {
4351 ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
4352 goto bail_with_handle;
4353 }
4354 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir")) {
4355 fprintf(f, "%s=%s\n", coltitle, rowdata);
4356 }
4357 }
4358 }
4359
4360bail_with_handle:
4361 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4362
4363bail:
4364 if (f)
4365 fclose(f);
4366 if (fd > -1)
4367 close(fd);
4368
4370
4371 /* If res_odbc is configured to only allow a single database connection, we
4372 will deadlock if we try to do this before releasing the connection we
4373 were just using. */
4374 if (!ast_strlen_zero(msg_id)) {
4375 odbc_update_msg_id(dir, msgnum, msg_id);
4376 }
4377
4378 if (SQL_SUCCEEDED(res)) {
4379 if (storage_conversion_to_disk) {
4380 /*
4381 * If we're currently storing audio on disk but there was pre-existing
4382 * audio in the database, we need to update the database to set the
4383 * 'recording' column to AUDIO_ON_DISK_MAGIC.
4384 */
4385 SCOPE_CALL(-1, odbc_update_set_audmagic, dir, msgnum);
4386 }
4387 if (storage_conversion_to_odbc) {
4388 /*
4389 * If we're currently storing audio in the database but there was
4390 * pre-existing audio on disk, we need to add the audio back
4391 * into the database overwriting the AUDIO_ON_DISK_MAGIC
4392 * magic number.
4393 */
4394 SCOPE_CALL(-1, odbc_store_message, dir, mailboxuser, mailboxcontext, msgnum);
4395 }
4396 }
4397
4398 SCOPE_EXIT_RTN_VALUE(x - 1, "Done. msg_id: %s RC: %d\n", msg_id, x - 1);
4399}
4400
4401/*!
4402 * \brief Determines the highest message number in use for a given user and mailbox folder.
4403 * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
4404 *
4405 * This method is used when mailboxes are stored in an ODBC back end.
4406 * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
4407 *
4408 * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
4409 */
4410static int odbc_last_message_index(char *dir)
4411{
4412 int x = -1;
4413 int res;
4414 SQLHSTMT stmt;
4415 char *sql = MAKE_SQL_PTRA("SELECT msgnum FROM %s WHERE dir=? order by msgnum desc");
4416 char rowdata[20];
4417 char *argv[] = { dir };
4418 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
4419 struct odbc_obj *obj;
4420 SCOPE_ENTER(3, "dir: %s\n", dir);
4421
4422 obj = ast_odbc_request_obj(odbc_database, 0);
4423 if (!obj) {
4424 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4425 }
4426
4428 if (!stmt) {
4429 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4430 goto bail;
4431 }
4432
4433 res = SQLFetch(stmt);
4434 if (!SQL_SUCCEEDED(res)) {
4435 if (res == SQL_NO_DATA) {
4436 ast_trace(-1, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
4437 } else {
4438 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4439 }
4440 goto bail_with_handle;
4441 }
4442
4443 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4444 if (!SQL_SUCCEEDED(res)) {
4445 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4446 goto bail_with_handle;
4447 }
4448
4449 if (sscanf(rowdata, "%30d", &x) != 1) {
4450 ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
4451 }
4452
4453bail_with_handle:
4454 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4455
4456bail:
4458
4459 SCOPE_EXIT_RTN_VALUE(x, "Done. Last message index: %d\n", x);
4460}
4461
4462/*!
4463 * \brief Determines if the specified message exists.
4464 * \param dir the folder the mailbox folder to look for messages.
4465 * \param msgnum the message index to query for.
4466 *
4467 * This method is used when mailboxes are stored in an ODBC back end.
4468 *
4469 * \return greater than zero if the message exists, zero when the message does not exist or on error.
4470 */
4471static int odbc_message_exists(char *dir, int msgnum)
4472{
4473 int x = 0;
4474 int res;
4475 SQLHSTMT stmt;
4476 char *sql = MAKE_SQL_PTRA("SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?");
4477 char rowdata[20];
4478 char msgnums[20];
4479 char *argv[] = { dir, msgnums };
4480 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4481 struct odbc_obj *obj;
4482 SCOPE_ENTER(3, "dir: %s msgnum: %d\n", dir, msgnum);
4483
4484 obj = ast_odbc_request_obj(odbc_database, 0);
4485 if (!obj) {
4486 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4487 }
4488
4489 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4491 if (!stmt) {
4492 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4493 goto bail;
4494 }
4495
4496 res = SQLFetch(stmt);
4497 if (!SQL_SUCCEEDED(res)) {
4498 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4499 goto bail_with_handle;
4500 }
4501
4502 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4503 if (!SQL_SUCCEEDED(res)) {
4504 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4505 goto bail_with_handle;
4506 }
4507
4508 if (sscanf(rowdata, "%30d", &x) != 1) {
4509 ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
4510 }
4511
4512bail_with_handle:
4513 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4514
4515bail:
4517 SCOPE_EXIT_RTN_VALUE(x, "Done. Msg %s\n", x ? "exists" : "does not exist");
4518}
4519
4520/*!
4521 * \brief returns the number of messages found.
4522 * \param vmu
4523 * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
4524 *
4525 * This method is used when mailboxes are stored in an ODBC back end.
4526 *
4527 * \return The count of messages being zero or more, less than zero on error.
4528 */
4529static int odbc_count_messages(struct ast_vm_user *vmu, char *dir)
4530{
4531 int x = -1;
4532 int res;
4533 SQLHSTMT stmt;
4534 char *sql = MAKE_SQL_PTRA("SELECT COUNT(*) FROM %s WHERE dir=?");
4535 char rowdata[20];
4536 char *argv[] = { dir };
4537 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
4538 struct odbc_obj *obj;
4539 SCOPE_ENTER(3, "dir: %s\n", dir);
4540
4541 obj = ast_odbc_request_obj(odbc_database, 0);
4542 if (!obj) {
4543 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4544 }
4545
4547 if (!stmt) {
4548 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4549 goto bail;
4550 }
4551
4552 res = SQLFetch(stmt);
4553 if (!SQL_SUCCEEDED(res)) {
4554 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4555 goto bail_with_handle;
4556 }
4557
4558 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4559 if (!SQL_SUCCEEDED(res)) {
4560 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4561 goto bail_with_handle;
4562 }
4563
4564 if (sscanf(rowdata, "%30d", &x) != 1) {
4565 ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
4566 }
4567
4568bail_with_handle:
4569 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4570
4571bail:
4573 SCOPE_EXIT_RTN_VALUE(x, "Done. Count %d\n", x);
4574}
4575
4576/*!
4577 * \brief Deletes a message from the mailbox folder.
4578 * \param sdir The mailbox folder to work in.
4579 * \param smsg The message index to be deleted.
4580 *
4581 * This method is used when mailboxes are stored in an ODBC back end.
4582 * The specified message is directly deleted from the database 'voicemessages' table.
4583 */
4584#define DELETE_SQL_FMT "DELETE FROM %s WHERE dir=? AND msgnum=?"
4585static void odbc_delete_message(const char *sdir, int smsg)
4586{
4587 SQLHSTMT stmt;
4588 char *sql = MAKE_SQL_PTRA(DELETE_SQL_FMT);
4589 char msgnums[20];
4590 char *argv[] = { NULL, msgnums };
4591 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4592 struct odbc_obj *obj;
4593 SCOPE_ENTER(3, "sdir: %s smsg: %d\n", sdir, smsg);
4594
4595 obj = ast_odbc_request_obj(odbc_database, 0);
4596 if (!obj) {
4597 SCOPE_EXIT_LOG_RTN(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4598 }
4599
4600 argv[0] = ast_strdupa(sdir);
4601
4602 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4604 if (!stmt) {
4605 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4606 } else {
4607 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4608 }
4610
4612 char *src_fn = MAKE_FILE_PTRA(sdir, smsg);
4613 ast_trace(-1, "Audio stored on disk. Deleting '%s'\n", src_fn);
4614 ast_filedelete(src_fn, NULL);
4615 }
4616
4617 SCOPE_EXIT_RTN("Done\n");
4618}
4619
4620/*!
4621 * \brief Copies a voicemail from one mailbox to another.
4622 * \param sdir the folder for which to look for the message to be copied.
4623 * \param smsg the index of the message to be copied.
4624 * \param ddir the destination folder to copy the message into.
4625 * \param dmsg the index to be used for the copied message.
4626 * \param dmailboxuser The user who owns the mailbox that contains the destination folder.
4627 * \param dmailboxcontext The context for the destination user.
4628 *
4629 * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
4630 */
4631#define COPY_SQL_FMT "INSERT INTO %s (dir, msgnum, msg_id, context, callerid, origtime, " \
4632 "duration, recording, flag, mailboxuser, mailboxcontext) " \
4633 "SELECT ?,?,msg_id,context,callerid,origtime,duration,recording,flag,?,? " \
4634 "FROM %s WHERE dir=? AND msgnum=?"
4635static void odbc_copy_message(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
4636{
4637 SQLHSTMT stmt;
4638 char *sql = MAKE_SQL_PTRA2(COPY_SQL_FMT);
4639 char msgnums[20];
4640 char msgnumd[20];
4641 struct odbc_obj *obj;
4642 char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
4643 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
4644 SCOPE_ENTER(3, "sdir: %s smsg: %d duser: %s dcontext: %s ddir: %s dmsg: %d\n",
4645 sdir, smsg, dmailboxuser, dmailboxcontext, ddir, dmsg);
4646
4647 SCOPE_CALL(-1, odbc_delete_message, ddir, dmsg);
4648
4649 obj = ast_odbc_request_obj(odbc_database, 0);
4650 if (!obj) {
4651 SCOPE_EXIT_LOG_RTN(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4652 }
4653
4654 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4655 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
4656
4658 if (!stmt)
4659 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
4660 else
4661 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4663
4665 char *src_fn = MAKE_FILE_PTRA(sdir, smsg);
4666 char *dst_fn = MAKE_FILE_PTRA(ddir, dmsg);
4667
4668 ast_trace(-1, "Audio stored on disk. Copying '%s' to '%s'\n", src_fn, dst_fn);
4669 ast_filecopy(src_fn, dst_fn, NULL);
4670 }
4671
4672 SCOPE_EXIT_RTN("Done\n");
4673}
4674#undef COPY_SQL_FMT
4675
4676struct insert_data {
4677 const char *dir;
4678 const char *msgnums;
4679 void *data;
4680 SQLLEN datalen;
4681 SQLLEN indlen;
4682 const char *context;
4683 const char *callerid;
4684 const char *origtime;
4685 const char *duration;
4686 const char *mailboxuser;
4687 const char *mailboxcontext;
4688 const char *category;
4689 const char *flag;
4690 const char *msg_id;
4691};
4692
4693#define STORE_SQL_FMT_CAT "INSERT INTO %s (dir, msgnum, recording, context, callerid, " \
4694 "origtime, duration, mailboxuser, mailboxcontext, flag, msg_id, category) " \
4695 "VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"
4696#define STORE_SQL_FMT "INSERT INTO %s (dir, msgnum, recording, context, callerid, "\
4697 "origtime, duration, mailboxuser, mailboxcontext, flag, msg_id) "\
4698 "VALUES (?,?,?,?,?,?,?,?,?,?,?)"
4699
4700static SQLHSTMT odbc_insert_data_cb(struct odbc_obj *obj, void *vdata)
4701{
4702 struct insert_data *data = vdata;
4703 char *insert_sql;
4704 char *delete_sql;
4705 int res;
4706 SQLHSTMT stmt;
4707 SCOPE_ENTER(3, "dir: %s msgnums: %s msg_id: %s\n", data->dir, data->msgnums,
4708 data->msg_id);
4709
4710 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4711 if (!SQL_SUCCEEDED(res)) {
4712 SCOPE_EXIT_LOG_RTN_VALUE(NULL, AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
4713 }
4714
4715 /* Delete any existing row with the same dir and msgnum */
4716 delete_sql = MAKE_SQL_PTRA(DELETE_SQL_FMT);
4717 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
4718 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
4719 res = ast_odbc_execute_sql(obj, stmt, delete_sql);
4720 if (!SQL_SUCCEEDED(res)) {
4721 ast_trace(-1, "There wasn't an existing row. Good.\n");
4722 } else {
4723 ast_trace(-1, "There WAS an existing row. This is OK if we're replacing a message.\n");
4724 }
4725 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4726 stmt = NULL;
4727
4728 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4729 if (!SQL_SUCCEEDED(res)) {
4730 SCOPE_EXIT_LOG_RTN_VALUE(NULL, AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
4731 }
4732
4733 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
4734 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
4735 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
4736 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
4737 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
4738 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
4739 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
4740 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
4741 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
4742 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
4743 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msg_id), 0, (void *) data->msg_id, 0, NULL);
4744 if (!ast_strlen_zero(data->category)) {
4745 insert_sql = MAKE_SQL_PTRA(STORE_SQL_FMT_CAT);
4746 SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
4747 } else {
4748 insert_sql = MAKE_SQL_PTRA(STORE_SQL_FMT);
4749 }
4750 res = ast_odbc_execute_sql(obj, stmt, insert_sql);
4751 if (!SQL_SUCCEEDED(res)) {
4752 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n", insert_sql);
4753 ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute");
4754 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4755 stmt = NULL;
4756 }
4757
4758 SCOPE_EXIT_RTN_VALUE(stmt, "%s\n", stmt ? "Success" : "Failed");
4759}
4760
4761/*!
4762 * \brief Stores a voicemail into the database.
4763 * \param dir the folder the mailbox folder to store the message.
4764 * \param mailboxuser the user owning the mailbox folder.
4765 * \param mailboxcontext
4766 * \param msgnum the message index for the message to be stored.
4767 *
4768 * This method is used when mailboxes are stored in an ODBC back end.
4769 * The message sound file and information file is looked up on the file system.
4770 * A SQL query is invoked to store the message into the (MySQL) database.
4771 *
4772 * \return the zero on success -1 on error.
4773 */
4774static int odbc_store_message(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
4775{
4776 int res = 0;
4777 int fd = -1;
4778 void *fdm = MAP_FAILED;
4779 off_t fdlen = -1;
4780 SQLHSTMT stmt;
4781 char msgnums[20];
4782 char *fn = MAKE_FILE_PTRA(dir, msgnum);
4783 char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
4784 char fmt[80]="";
4785 char *c;
4786 struct ast_config *cfg = NULL;
4787 struct odbc_obj *obj;
4788 struct insert_data idata = { .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
4789 .context = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "", .msg_id = "" };
4790 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
4791 SCOPE_ENTER(3, "dir: %s user: %s context: %s msgnum: %d msgtype: %s\n",
4792 dir, mailboxuser, mailboxcontext, msgnum, msgnum < 0 ? "Greeting" : "Message");
4793
4794 obj = ast_odbc_request_obj(odbc_database, 0);
4795 if (!obj) {
4796 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4797 }
4798
4799 do {
4800 ast_copy_string(fmt, vmfmts, sizeof(fmt));
4801 c = strchr(fmt, '|');
4802 if (c)
4803 *c = '\0';
4804 if (!strcasecmp(fmt, "wav49"))
4805 strcpy(fmt, "WAV");
4806
4807 ast_trace(-1, "Formats: %s Using format: '%s'\n", vmfmts, fmt);
4808 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4809
4810 ast_trace(-1, "Base path: '%s'\n", fn);
4811 ast_trace(-1, "Opening '%s'\n", full_fn);
4812 cfg = ast_config_load(full_fn, config_flags);
4813 if (!valid_config(cfg)) {
4814 if (msgnum < 0) {
4815 ast_trace(-1, "No information file found for '%s'. This is a greeting so this is OK.\n", full_fn);
4816 } else {
4817 ast_log(AST_LOG_WARNING, "Failed to open '%s'\n", full_fn);
4818 res = -1;
4819 break;
4820 }
4821 }
4822
4823 sprintf(full_fn, "%s.%s", fn, fmt); /* Safe */
4825 ast_trace(-1, "Audio stored on disk. No need to open '%s'\n", full_fn);
4826 } else {
4827 ast_trace(-1, "Opening '%s'\n", full_fn);
4828 fd = open(full_fn, O_RDWR);
4829 if (fd < 0) {
4830 ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
4831 res = -1;
4832 break;
4833 }
4834 }
4835
4836 if (valid_config(cfg)) {
4837 ast_trace(-1, "Using information file '%s'\n", fn);
4838 if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
4839 idata.context = "";
4840 }
4841 if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
4842 idata.callerid = "";
4843 }
4844 if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
4845 idata.origtime = "";
4846 }
4847 if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
4848 idata.duration = "";
4849 }
4850 if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
4851 idata.category = "";
4852 }
4853 if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
4854 idata.flag = "";
4855 }
4856 if (!(idata.msg_id = ast_variable_retrieve(cfg, "message", "msg_id"))) {
4857 idata.msg_id = "";
4858 }
4859 }
4860
4861 if (fd < 0) {
4862 ast_trace(-1, "Audio stored on disk. Not reading sound file '%s' but setting magic number.\n", full_fn);
4863 idata.data = AUDIO_ON_DISK_MAGIC;
4864 idata.datalen = idata.indlen = AUDIO_ON_DISK_MAGIC_LEN;
4865 } else {
4866 ast_trace(-1, "Reading sound file '%s'\n", full_fn);
4867 fdlen = lseek(fd, 0, SEEK_END);
4868 if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
4869 ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
4870 res = -1;
4871 break;
4872 }
4873 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
4874 if (fdm == MAP_FAILED) {
4875 ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
4876 res = -1;
4877 break;
4878 }
4879 idata.data = fdm;
4880 idata.datalen = idata.indlen = fdlen;
4881 }
4882
4883 if (ast_strlen_zero(idata.origtime)) {
4884 idata.origtime = "0";
4885 }
4886
4887 if (ast_strlen_zero(idata.duration)) {
4888 idata.duration = "0";
4889 }
4890
4891 if ((stmt = ast_odbc_direct_execute(obj, odbc_insert_data_cb, &idata))) {
4892 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4893 } else {
4894 res = -1;
4895 }
4896 } while (0);
4897
4899
4900 if (valid_config(cfg))
4901 ast_config_destroy(cfg);
4902 if (fdm != MAP_FAILED)
4903 munmap(fdm, fdlen);
4904 if (fd > -1)
4905 close(fd);
4906 SCOPE_EXIT_RTN_VALUE(res, "%s\n", res ? "Failed" : "Success");
4907}
4908#undef STORE_SQL_FMT
4909#undef STORE_SQL_FMT_CAT
4910
4911/*!
4912 * \brief Renames a message in a mailbox folder.
4913 * \param sdir The folder of the message to be renamed.
4914 * \param smsg The index of the message to be renamed.
4915 * \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.
4916 * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
4917 * \param ddir The destination folder for the message to be renamed into
4918 * \param dmsg The destination message for the message to be renamed.
4919 *
4920 * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
4921 * The is usually used to resequence the messages in the mailbox, such as to delete message index 0, it would be called successively to slide all the other messages down one index.
4922 * 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.
4923 */
4924static void odbc_rename_message(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
4925{
4926 SQLHSTMT stmt;
4927 char *sql = MAKE_SQL_PTRA("UPDATE %s SET dir=?, msgnum=? WHERE mailboxuser=? AND mailboxcontext=? AND dir=? AND msgnum=?");
4928 char msgnums[20];
4929 char msgnumd[20];
4930 struct odbc_obj *obj;
4931 char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
4932 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
4933 SCOPE_ENTER(3, "sdir: %s smsg: %d user: %s context: %s ddir: %s dmsg: %d\n", sdir, smsg,
4934 mailboxuser, mailboxcontext, ddir, dmsg);
4935
4936 SCOPE_CALL(-1, odbc_delete_message, ddir, dmsg);
4937
4938 obj = ast_odbc_request_obj(odbc_database, 0);
4939 if (!obj) {
4940 SCOPE_EXIT_LOG_RTN(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4941 }
4942
4943 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4944 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
4945
4947 if (!stmt)
4948 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4949 else
4950 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4952
4954 char *src_fn = MAKE_FILE_PTRA(sdir, smsg);
4955 char *dst_fn = MAKE_FILE_PTRA(ddir, dmsg);
4956
4957 ast_trace(-1, "Recordings stored on disk. Renaming '%s' to '%s'\n", src_fn, dst_fn);
4958 ast_filerename(src_fn, dst_fn, NULL);
4959 }
4960
4961 SCOPE_EXIT_RTN("Done.\n");
4962}
4963
4964/*!
4965 * \brief Removes a voicemail message file.
4966 * \param dir the path to the message file.
4967 * \param msgnum the unique number for the message within the mailbox.
4968 *
4969 * Removes the message content file and the information file.
4970 * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
4971 * Typical use is to clean up after a RETRIEVE operation.
4972 * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
4973 * \return zero on success, -1 on error.
4974 */
4975static int odbc_remove_files(char *dir, int msgnum)
4976{
4977 char *fn = MAKE_FILE_PTRA(dir, msgnum);
4978 char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
4979 SCOPE_ENTER(3, "dir: %s msgnum: %d\n", dir, msgnum);
4980
4982 ast_trace(-1, "Audio stored on disk. Keeping '%s' sound files\n", fn);
4983 } else {
4984 ast_trace(-1, "Audio stored in ODBC. Removing '%s' sound files\n", fn);
4985 ast_filedelete(fn, NULL);
4986 }
4987
4988 /* Always remove the information file */
4989 ast_trace(-1, "Removing '%s' information file\n", full_fn);
4990 unlink(full_fn);
4991 SCOPE_EXIT_RTN_VALUE(0, "Done.\n");
4992}
4993#else
4994#ifndef IMAP_STORAGE
4995/*!
4996 * \brief Find all .txt files - even if they are not in sequence from 0000.
4997 * \param vmu
4998 * \param dir
4999 *
5000 * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
5001 *
5002 * \return the count of messages, zero or more.
5003 */
5004static int count_messages(struct ast_vm_user *vmu, char *dir)
5005{
5006
5007 int vmcount = 0;
5008 DIR *vmdir = NULL;
5009 struct dirent *vment = NULL;
5010
5011 if (vm_lock_path(dir))
5012 return ERROR_LOCK_PATH;
5013
5014 if ((vmdir = opendir(dir))) {
5015 while ((vment = readdir(vmdir))) {
5016 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
5017 vmcount++;
5018 }
5019 }
5020 closedir(vmdir);
5021 }
5022 ast_unlock_path(dir);
5023
5024 return vmcount;
5025}
5026
5027/*!
5028 * \brief Renames a message in a mailbox folder.
5029 * \param sfn The path to the mailbox information and data file to be renamed.
5030 * \param dfn The path for where the message data and information files will be renamed to.
5031 *
5032 * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
5033 */
5034static void rename_file(char *sfn, char *dfn)
5035{
5036 char stxt[PATH_MAX];
5037 char dtxt[PATH_MAX];
5038 ast_filerename(sfn, dfn, NULL);
5039 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
5040 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
5041 if (ast_check_realtime("voicemail_data")) {
5042 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
5043 }
5044 rename(stxt, dtxt);
5045}
5046
5047/*!
5048 * \brief Determines the highest message number in use for a given user and mailbox folder.
5049 * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
5050 *
5051 * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
5052 * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
5053 *
5054 * \note Should always be called with a lock already set on dir.
5055 * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
5056 */
5057static int last_message_index(char *dir)
5058{
5059 int x;
5060 unsigned char map[MAXMSGLIMIT] = "";
5061 DIR *msgdir;
5062 struct dirent *msgdirent;
5063 int msgdirint;
5064 char extension[4];
5065 int stopcount = 0;
5066
5067 /* Reading the entire directory into a file map scales better than
5068 * doing a stat repeatedly on a predicted sequence. I suspect this
5069 * is partially due to stat(2) internally doing a readdir(2) itself to
5070 * find each file. */
5071 if (!(msgdir = opendir(dir))) {
5072 return -1;
5073 }
5074
5075 while ((msgdirent = readdir(msgdir))) {
5076 if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
5077 map[msgdirint] = 1;
5078 stopcount++;
5079 ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
5080 }
5081 }
5082 closedir(msgdir);
5083
5084 for (x = 0; x < MAXMSGLIMIT && stopcount; x++) {
5085 stopcount -= map[x];
5086 }
5087
5088 return x - 1;
5089}
5090
5091#endif /* #ifndef IMAP_STORAGE */
5092#endif /* #else of #ifdef ODBC_STORAGE */
5093#ifndef IMAP_STORAGE
5094/*!
5095 * \brief Utility function to copy a file.
5096 * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
5097 * \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.
5098 *
5099 * 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.
5100 * The copy operation copies up to 4096 bytes at once.
5101 *
5102 * \return zero on success, -1 on error.
5103 */
5104static int copy(char *infile, char *outfile)
5105{
5106 int ifd;
5107 int ofd;
5108 int res = -1;
5109 int len;
5110 char buf[4096];
5111
5112#ifdef HARDLINK_WHEN_POSSIBLE
5113 /* Hard link if possible; saves disk space & is faster */
5114 if (!link(infile, outfile)) {
5115 return 0;
5116 }
5117#endif
5118
5119 if ((ifd = open(infile, O_RDONLY)) < 0) {
5120 ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
5121 return -1;
5122 }
5123
5124 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
5125 ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
5126 close(ifd);
5127 return -1;
5128 }
5129
5130 for (;;) {
5131 int wrlen;
5132
5133 len = read(ifd, buf, sizeof(buf));
5134 if (!len) {
5135 res = 0;
5136 break;
5137 }
5138
5139 if (len < 0) {
5140 ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
5141 break;
5142 }
5143
5144 wrlen = write(ofd, buf, len);
5145 if (errno == ENOMEM || errno == ENOSPC || wrlen != len) {
5146 ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, wrlen, len, strerror(errno));
5147 break;
5148 }
5149 }
5150
5151 close(ifd);
5152 close(ofd);
5153 if (res) {
5154 unlink(outfile);
5155 }
5156
5157 return res;
5158}
5159
5160/*!
5161 * \brief Copies a voicemail information (envelope) file.
5162 * \param frompath
5163 * \param topath
5164 *
5165 * Every voicemail has the data (.wav) file, and the information file.
5166 * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
5167 * This is used by the COPY macro when not using IMAP storage.
5168 */
5169static void copy_plain_file(char *frompath, char *topath)
5170{
5171 char frompath2[PATH_MAX], topath2[PATH_MAX];
5172 struct ast_variable *tmp, *var = NULL;
5173 const char *origmailbox = "", *context = "", *exten = "";
5174 const char *priority = "", *callerchan = "", *callerid = "", *origdate = "";
5175 const char *origtime = "", *category = "", *duration = "";
5176
5177 ast_filecopy(frompath, topath, NULL);
5178 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
5179 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
5180
5181 if (ast_check_realtime("voicemail_data")) {
5182 var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
5183 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
5184 for (tmp = var; tmp; tmp = tmp->next) {
5185 if (!strcasecmp(tmp->name, "origmailbox")) {
5186 origmailbox = tmp->value;
5187 } else if (!strcasecmp(tmp->name, "context")) {
5188 context = tmp->value;
5189 } else if (!strcasecmp(tmp->name, "exten")) {
5190 exten = tmp->value;
5191 } else if (!strcasecmp(tmp->name, "priority")) {
5192 priority = tmp->value;
5193 } else if (!strcasecmp(tmp->name, "callerchan")) {
5194 callerchan = tmp->value;
5195 } else if (!strcasecmp(tmp->name, "callerid")) {
5196 callerid = tmp->value;
5197 } else if (!strcasecmp(tmp->name, "origdate")) {
5198 origdate = tmp->value;
5199 } else if (!strcasecmp(tmp->name, "origtime")) {
5200 origtime = tmp->value;
5201 } else if (!strcasecmp(tmp->name, "category")) {
5202 category = tmp->value;
5203 } else if (!strcasecmp(tmp->name, "duration")) {
5204 duration = tmp->value;
5205 }
5206 }
5207 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);
5208 }
5209 copy(frompath2, topath2);
5211}
5212#endif
5213
5214/*!
5215 * \brief Removes the voicemail sound and information file.
5216 * \param file The path to the sound file. This will be the folder and message index, without the extension.
5217 *
5218 * This is used by the DELETE macro when voicemails are stored on the file system.
5219 *
5220 * \return zero on success, -1 on error.
5221 */
5222static int vm_delete(char *file)
5223{
5224 char *txt;
5225 int txtsize = 0;
5226 int res = 0;
5227 SCOPE_ENTER(3, "file: %s\n", file);
5228
5229 txtsize = (strlen(file) + 5)*sizeof(char);
5230 txt = ast_alloca(txtsize);
5231 /* Sprintf here would safe because we alloca'd exactly the right length,
5232 * but trying to eliminate all sprintf's anyhow
5233 */
5234 if (ast_check_realtime("voicemail_data")) {
5235 ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
5236 }
5237 snprintf(txt, txtsize, "%s.txt", file);
5238 ast_trace(-1, "unlinking '%s'\n", txt);
5239 unlink(txt);
5240 ast_trace(-1, "deleting sound files '%s'\n", file);
5241 res = ast_filedelete(file, NULL);
5242 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d\n", res);
5243}
5244
5245static 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)
5246{
5247 char callerid[256];
5248 char num[12];
5249 char fromdir[256], fromfile[256];
5250 struct ast_config *msg_cfg;
5251 const char *origcallerid, *origtime;
5252 char origcidname[80], origcidnum[80], origdate[80];
5253 int inttime;
5254 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
5255
5256 /* Prepare variables for substitution in email body and subject */
5257 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
5258 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
5259 snprintf(num, sizeof(num), "%d", msgnum);
5260 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
5261 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
5262 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
5263 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
5264 ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
5265 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
5266 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
5267 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
5268 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
5269 pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
5270
5271 /* Retrieve info from VM attribute file */
5272 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
5273 make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
5274 if (strlen(fromfile) < sizeof(fromfile) - 5) {
5275 strcat(fromfile, ".txt");
5276 }
5277 if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
5278 ast_debug(1, "Config load for message text file '%s' failed\n", fromfile);
5279 return;
5280 }
5281
5282 if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
5283 pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
5284 ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
5285 pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
5286 pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
5287 }
5288
5289 if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
5290 struct timeval tv = { inttime, };
5291 struct ast_tm tm;
5292 ast_localtime(&tv, &tm, NULL);
5293 ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5294 pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
5295 }
5296 ast_config_destroy(msg_cfg);
5297}
5298
5299/*!
5300 * \brief Wraps a character sequence in double quotes, escaping occurrences of quotes within the string.
5301 * \param from The string to work with.
5302 * \param buf The buffer into which to write the modified quoted string.
5303 * \param maxlen Always zero, but see \see ast_str
5304 *
5305 * \return The destination string with quotes wrapped on it (the to field).
5306 */
5307static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
5308{
5309 const char *ptr;
5310
5311 /* We're only ever passing 0 to maxlen, so short output isn't possible */
5312 ast_str_set(buf, maxlen, "\"");
5313 for (ptr = from; *ptr; ptr++) {
5314 if (*ptr == '"' || *ptr == '\\') {
5315 ast_str_append(buf, maxlen, "\\%c", *ptr);
5316 } else {
5317 ast_str_append(buf, maxlen, "%c", *ptr);
5318 }
5319 }
5320 ast_str_append(buf, maxlen, "\"");
5321
5322 return ast_str_buffer(*buf);
5323}
5324
5325/*! \brief
5326 * fill in *tm for current time according to the proper timezone, if any.
5327 * \return tm so it can be used as a function argument.
5328 */
5329static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
5330{
5331 const struct vm_zone *z = NULL;
5332 struct timeval t = ast_tvnow();
5333
5334 /* Does this user have a timezone specified? */
5335 if (!ast_strlen_zero(vmu->zonetag)) {
5336 /* Find the zone in the list */
5338 AST_LIST_TRAVERSE(&zones, z, list) {
5339 if (!strcmp(z->name, vmu->zonetag))
5340 break;
5341 }
5343 }
5344 ast_localtime(&t, tm, z ? z->timezone : NULL);
5345 return tm;
5346}
5347
5348/*!\brief Check if the string would need encoding within the MIME standard, to
5349 * avoid confusing certain mail software that expects messages to be 7-bit
5350 * clean.
5351 */
5352static int check_mime(const char *str)
5353{
5354 for (; *str; str++) {
5355 if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
5356 return 1;
5357 }
5358 }
5359 return 0;
5360}
5361
5362/*!\brief Encode a string according to the MIME rules for encoding strings
5363 * that are not 7-bit clean or contain control characters.
5364 *
5365 * Additionally, if the encoded string would exceed the MIME limit of 76
5366 * characters per line, then the encoding will be broken up into multiple
5367 * sections, separated by a space character, in order to facilitate
5368 * breaking up the associated header across multiple lines.
5369 *
5370 * \param end An expandable buffer for holding the result
5371 * \param maxlen Always zero, but see \see ast_str
5372 * \param start A string to be encoded
5373 * \param preamble The length of the first line already used for this string,
5374 * to ensure that each line maintains a maximum length of 76 chars.
5375 * \param postamble the length of any additional characters appended to the
5376 * line, used to ensure proper field wrapping.
5377 * \retval The encoded string.
5378 */
5379static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
5380{
5381 struct ast_str *tmp = ast_str_alloca(80);
5382 int first_section = 1;
5383
5385 ast_str_set(&tmp, -1, "=?%s?Q?", charset);
5386 for (; *start; start++) {
5387 int need_encoding = 0;
5388 if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
5389 need_encoding = 1;
5390 }
5391 if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
5392 (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
5393 (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
5394 (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
5395 /* Start new line */
5396 ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
5397 ast_str_set(&tmp, -1, "=?%s?Q?", charset);
5398 first_section = 0;
5399 }
5400 if (need_encoding && *start == ' ') {
5401 ast_str_append(&tmp, -1, "_");
5402 } else if (need_encoding) {
5403 ast_str_append(&tmp, -1, "=%hhX", *start);
5404 } else {
5405 ast_str_append(&tmp, -1, "%c", *start);
5406 }
5407 }
5408 ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
5409 return ast_str_buffer(*end);
5410}
5411
5412/*!
5413 * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
5414 * \param p The output file to generate the email contents into.
5415 * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
5416 * \param vmu The voicemail user who is sending the voicemail.
5417 * \param msgnum The message index in the mailbox folder.
5418 * \param context
5419 * \param mailbox The voicemail box to read the voicemail to be notified in this email.
5420 * \param fromfolder
5421 * \param cidnum The caller ID number.
5422 * \param cidname The caller ID name.
5423 * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
5424 * \param attach2
5425 * \param format The message sound file format. i.e. .wav
5426 * \param duration The time of the message content, in seconds.
5427 * \param attach_user_voicemail if 1, the sound file is attached to the email.
5428 * \param chan
5429 * \param category
5430 * \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.
5431 * \param flag, msg_id
5432 *
5433 * 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.
5434 */
5435static void make_email_file(FILE *p,
5436 char *srcemail,
5437 struct ast_vm_user *vmu,
5438 int msgnum,
5439 char *context,
5440 char *mailbox,
5441 const char *fromfolder,
5442 char *cidnum,
5443 char *cidname,
5444 char *attach,
5445 char *attach2,
5446 char *format,
5447 int duration,
5448 int attach_user_voicemail,
5449 struct ast_channel *chan,
5450 const char *category,
5451 int imap,
5452 const char *flag,
5453 const char *msg_id)
5454{
5455 char date[256];
5456 char host[MAXHOSTNAMELEN] = "";
5457 char who[256];
5458 char bound[256];
5459 char dur[256];
5460 struct ast_tm tm;
5461 char enc_cidnum[256] = "", enc_cidname[256] = "";
5462 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
5463 char *greeting_attachment;
5464 char filename[256];
5465 int first_line;
5466 char *emailsbuf;
5467 char *email;
5468
5469 if (!str1 || !str2) {
5470 ast_free(str1);
5471 ast_free(str2);
5472 return;
5473 }
5474
5475 if (cidnum) {
5476 strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
5477 }
5478 if (cidname) {
5479 strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
5480 }
5481 gethostname(host, sizeof(host) - 1);
5482
5483 if (strchr(srcemail, '@')) {
5484 ast_copy_string(who, srcemail, sizeof(who));
5485 } else {
5486 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
5487 }
5488
5489 greeting_attachment = strrchr(ast_strdupa(attach), '/');
5490 if (greeting_attachment) {
5491 *greeting_attachment++ = '\0';
5492 }
5493
5494 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
5495 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
5496 fprintf(p, "Date: %s" ENDL, date);
5497
5498 /* Set date format for voicemail mail */
5499 ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5500
5502 struct ast_channel *ast;
5503 char *e_fromstring = !ast_strlen_zero(vmu->fromstring) ? vmu->fromstring : fromstring;
5504 if ((ast = ast_dummy_channel_alloc())) {
5505 char *ptr;
5506 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
5507 ast_str_substitute_variables(&str1, 0, ast, e_fromstring);
5508
5509 if (check_mime(ast_str_buffer(str1))) {
5510 first_line = 1;
5511 ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
5512 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5513 *ptr = '\0';
5514 fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
5515 first_line = 0;
5516 /* Substring is smaller, so this will never grow */
5517 ast_str_set(&str2, 0, "%s", ptr + 1);
5518 }
5519 fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
5520 } else {
5521 fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
5522 }
5523 ast = ast_channel_unref(ast);
5524 } else {
5525 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5526 }
5527 } else {
5528 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
5529 }
5530
5531 emailsbuf = ast_strdupa(vmu->email);
5532 fprintf(p, "To:");
5533 first_line = 1;
5534 while ((email = strsep(&emailsbuf, "|"))) {
5535 char *next = emailsbuf;
5536 if (check_mime(vmu->fullname)) {
5537 char *ptr;
5538 ast_str_encode_mime(&str2, 0, vmu->fullname, first_line ? strlen("To: ") : 0, strlen(email) + 3 + (next ? strlen(",") : 0));
5539 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5540 *ptr = '\0';
5541 fprintf(p, " %s" ENDL, ast_str_buffer(str2));
5542 /* Substring is smaller, so this will never grow */
5543 ast_str_set(&str2, 0, "%s", ptr + 1);
5544 }
5545 fprintf(p, " %s <%s>%s" ENDL, ast_str_buffer(str2), email, next ? "," : "");
5546 } else {
5547 fprintf(p, " %s <%s>%s" ENDL, ast_str_quote(&str2, 0, vmu->fullname), email, next ? "," : "");
5548 }
5549 first_line = 0;
5550 }
5551
5552 if (msgnum <= -1) {
5553 fprintf(p, "Subject: New greeting '%s' on %s." ENDL, greeting_attachment, date);
5555 char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
5556 struct ast_channel *ast;
5557 if ((ast = ast_dummy_channel_alloc())) {
5558 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5559 ast_str_substitute_variables(&str1, 0, ast, e_subj);
5560 if (check_mime(ast_str_buffer(str1))) {
5561 char *ptr;
5562 first_line = 1;
5563 ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
5564 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5565 *ptr = '\0';
5566 fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5567 first_line = 0;
5568 /* Substring is smaller, so this will never grow */
5569 ast_str_set(&str2, 0, "%s", ptr + 1);
5570 }
5571 fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5572 } else {
5573 fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
5574 }
5575 ast = ast_channel_unref(ast);
5576 } else {
5577 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5578 }
5579 } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
5580 if (ast_strlen_zero(flag)) {
5581 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
5582 } else {
5583 fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
5584 }
5585 } else {
5586 if (ast_strlen_zero(flag)) {
5587 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
5588 } else {
5589 fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
5590 }
5591 }
5592
5593 fprintf(p, "Message-ID: <Asterisk-%d-%u-%s-%d@%s>" ENDL, msgnum + 1,
5594 (unsigned int) ast_random(), mailbox, (int) getpid(), host);
5595 if (imap) {
5596 /* additional information needed for IMAP searching */
5597 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
5598 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
5599 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
5600 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
5601#ifdef IMAP_STORAGE
5602 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
5603#else
5604 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
5605#endif
5606 /* flag added for Urgent */
5607 fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, S_OR(flag, ""));
5608 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan ? ast_channel_priority(chan) : 0);
5609 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
5610 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
5611 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
5612 if (!ast_strlen_zero(category)) {
5613 fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
5614 } else {
5615 fprintf(p, "X-Asterisk-VM-Category: " ENDL);
5616 }
5617 fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
5618 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
5619 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
5620 fprintf(p, "X-Asterisk-VM-Message-ID: %s" ENDL, msg_id);
5621 }
5622 if (!ast_strlen_zero(cidnum)) {
5623 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
5624 }
5625 if (!ast_strlen_zero(cidname)) {
5626 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
5627 }
5628 fprintf(p, "MIME-Version: 1.0" ENDL);
5629 if (attach_user_voicemail) {
5630 /* Something unique. */
5631 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%u", msgnum + 1, mailbox,
5632 (int) getpid(), (unsigned int) ast_random());
5633
5634 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
5635 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
5636 fprintf(p, "--%s" ENDL, bound);
5637 }
5638 fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
5639 if (msgnum <= -1) {
5640 fprintf(p, "This message is to let you know that your greeting '%s' was changed on %s." ENDL
5641 "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL,
5642 greeting_attachment, date);
5643 } else if (emailbody || vmu->emailbody) {
5644 char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
5645 struct ast_channel *ast;
5646 if ((ast = ast_dummy_channel_alloc())) {
5647 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5648 ast_str_substitute_variables(&str1, 0, ast, e_body);
5649#ifdef IMAP_STORAGE
5650 {
5651 /* Convert body to native line terminators for IMAP backend */
5652 char *line = ast_str_buffer(str1), *next;
5653 do {
5654 /* Terminate line before outputting it to the file */
5655 if ((next = strchr(line, '\n'))) {
5656 *next++ = '\0';
5657 }
5658 fprintf(p, "%s" ENDL, line);
5659 line = next;
5660 } while (!ast_strlen_zero(line));
5661 }
5662#else
5663 fprintf(p, "%s" ENDL, ast_str_buffer(str1));
5664#endif
5665 ast = ast_channel_unref(ast);
5666 } else {
5667 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5668 }
5669 } else {
5670 if (strcmp(vmu->mailbox, mailbox)) {
5671 /* Forwarded type */
5672 struct ast_config *msg_cfg;
5673 const char *v;
5674 int inttime;
5675 char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
5676 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
5677 /* Retrieve info from VM attribute file */
5678 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
5679 make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
5680 if (strlen(fromfile) < sizeof(fromfile) - 5) {
5681 strcat(fromfile, ".txt");
5682 }
5683 if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
5684 if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
5685 ast_copy_string(origcallerid, v, sizeof(origcallerid));
5686 }
5687
5688 /* You might be tempted to do origdate, except that a) it's in the wrong
5689 * format, and b) it's missing for IMAP recordings. */
5690 if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
5691 struct timeval tv = { inttime, };
5692 struct ast_tm tm;
5693 ast_localtime(&tv, &tm, NULL);
5694 ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5695 }
5696 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
5697 " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
5698 "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
5699 " chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
5700 msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
5701 date, origcallerid, origdate);
5702 ast_config_destroy(msg_cfg);
5703 } else {
5704 goto plain_message;
5705 }
5706 } else {
5707plain_message:
5708 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
5709 "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
5710 "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
5711 ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
5712 (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
5713 }
5714 }
5715
5716 if (imap || attach_user_voicemail) {
5717 if (!ast_strlen_zero(attach2)) {
5718 snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
5719 ast_debug(5, "creating second attachment filename %s\n", filename);
5720 add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
5721 snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
5722 ast_debug(5, "creating attachment filename %s\n", filename);
5723 add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
5724 } else {
5725 snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
5726 ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
5727 add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
5728 }
5729 }
5730 ast_free(str1);
5731 ast_free(str2);
5732}
5733
5734static 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)
5735{
5736 char fname[PATH_MAX] = "";
5737 char sox_gain_tmpdir[PATH_MAX];
5738 char *file_to_delete = NULL, *dir_to_delete = NULL;
5739 int res;
5740 char altfname[PATH_MAX] = "";
5741 int altused = 0;
5742 char altformat[80] = "";
5743 char *c = NULL;
5744
5745 /* Eww. We want formats to tell us their own MIME type */
5746 char *mime_type = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
5747
5748 /* Users of multiple file formats need special attention. */
5749 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
5750 if (!ast_file_is_readable(fname)) {
5751 ast_copy_string(altformat, vmfmts, sizeof(altformat));
5752 c = strchr(altformat, '|');
5753 if (c) {
5754 *c = '\0';
5755 }
5756 ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s - trying first/alternate format %s\n", fname, strerror(errno), altformat);
5757 snprintf(altfname, sizeof(altfname), "%s.%s", attach, altformat);
5758 if (!ast_file_is_readable(altfname)) {
5759 ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s - alternate format %s failure\n", altfname, strerror(errno), altformat);
5760 } else {
5761 altused = 1;
5762 }
5763 }
5764
5765 /* This 'while' loop will only execute once. We use it so that we can 'break' */
5766 while (vmu->volgain < -.001 || vmu->volgain > .001 || altused) {
5767 char tmpdir[PATH_MAX];
5768
5769 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
5770
5771 res = snprintf(sox_gain_tmpdir, sizeof(sox_gain_tmpdir), "%s/vm-gain-XXXXXX", tmpdir);
5772 if (res >= sizeof(sox_gain_tmpdir)) {
5773 ast_log(LOG_ERROR, "Failed to create temporary directory path %s: Out of buffer space\n", tmpdir);
5774 break;
5775 }
5776
5777 if (mkdtemp(sox_gain_tmpdir)) {
5778 int soxstatus = 0;
5779 char sox_gain_cmd[PATH_MAX];
5780
5781 ast_debug(3, "sox_gain_tmpdir: %s\n", sox_gain_tmpdir);
5782
5783 /* Save for later */
5784 dir_to_delete = sox_gain_tmpdir;
5785
5786 res = snprintf(fname, sizeof(fname), "%s/output.%s", sox_gain_tmpdir, format);
5787 if (res >= sizeof(fname)) {
5788 ast_log(LOG_ERROR, "Failed to create filename buffer for %s/output.%s: Too long\n", sox_gain_tmpdir, format);
5789 break;
5790 }
5791
5792 if (!altused) {
5793 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s",
5794 vmu->volgain, attach, format, fname);
5795 } else {
5796 if (!strcasecmp(format, "wav")) {
5797 if (vmu->volgain < -.001 || vmu->volgain > .001) {
5798 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s -e signed-integer -b 16 %s",
5799 vmu->volgain, attach, altformat, fname);
5800 } else {
5801 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox %s.%s -e signed-integer -b 16 %s",
5802 attach, altformat, fname);
5803 }
5804 } else {
5805 if (vmu->volgain < -.001 || vmu->volgain > .001) {
5806 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s",
5807 vmu->volgain, attach, altformat, fname);
5808 } else {
5809 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox %s.%s %s",
5810 attach, altformat, fname);
5811 }
5812 }
5813 }
5814
5815 if (res >= sizeof(sox_gain_cmd)) {
5816 ast_log(LOG_ERROR, "Failed to generate sox command, out of buffer space\n");
5817 break;
5818 }
5819
5820 soxstatus = ast_safe_system(sox_gain_cmd);
5821 if (!soxstatus) {
5822 /* Save for later */
5823 file_to_delete = fname;
5824 ast_debug(3, "VOLGAIN: Stored at: %s - Level: %.4f - Mailbox: %s\n", fname, vmu->volgain, mailbox);
5825 } else {
5826 ast_log(LOG_WARNING, "Sox failed to re-encode %s: %s (have you installed support for all sox file formats?)\n",
5827 fname,
5828 soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
5829 ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
5830 }
5831 }
5832
5833 break;
5834 }
5835
5836 if (!file_to_delete) {
5837 res = snprintf(fname, sizeof(fname), "%s.%s", attach, format);
5838 if (res >= sizeof(fname)) {
5839 ast_log(LOG_ERROR, "Failed to create filename buffer for %s.%s: Too long\n", attach, format);
5840 return -1;
5841 }
5842 }
5843
5844 fprintf(p, "--%s" ENDL, bound);
5845 if (msgnum > -1)
5846 fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, mime_type, format, filename);
5847 else
5848 fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, mime_type, format, greeting_attachment, format);
5849 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
5850 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
5851 if (msgnum > -1)
5852 fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
5853 else
5854 fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
5856 if (last)
5857 fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
5858
5859 if (file_to_delete) {
5860 unlink(file_to_delete);
5861 }
5862
5863 if (dir_to_delete) {
5864 rmdir(dir_to_delete);
5865 }
5866
5867 return 0;
5868}
5869
5870static int sendmail(char *srcemail,
5871 struct ast_vm_user *vmu,
5872 int msgnum,
5873 char *context,
5874 char *mailbox,
5875 const char *fromfolder,
5876 char *cidnum,
5877 char *cidname,
5878 char *attach,
5879 char *attach2,
5880 char *format,
5881 int duration,
5882 int attach_user_voicemail,
5883 struct ast_channel *chan,
5884 const char *category,
5885 const char *flag,
5886 const char *msg_id)
5887{
5888 FILE *p = NULL;
5889 char tmp[80] = "/tmp/astmail-XXXXXX";
5890 char tmp2[256];
5891 char *stringp;
5892
5893 if (vmu && ast_strlen_zero(vmu->email)) {
5894 ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
5895 return(0);
5896 }
5897
5898 /* Mail only the first format */
5899 format = ast_strdupa(format);
5900 stringp = format;
5901 strsep(&stringp, "|");
5902
5903 if (!strcmp(format, "wav49"))
5904 format = "WAV";
5905 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));
5906 /* Make a temporary file instead of piping directly to sendmail, in case the mail
5907 command hangs */
5908 if ((p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask)) == NULL) {
5909 ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
5910 return -1;
5911 } else {
5912 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);
5913 fclose(p);
5914 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
5915 ast_safe_system(tmp2);
5916 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
5917 }
5918 return 0;
5919}
5920
5921static 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)
5922{
5923 char enc_cidnum[256], enc_cidname[256];
5924 char date[256];
5925 char host[MAXHOSTNAMELEN] = "";
5926 char who[256];
5927 char dur[PATH_MAX];
5928 char tmp[80] = "/tmp/astmail-XXXXXX";
5929 char tmp2[PATH_MAX];
5930 struct ast_tm tm;
5931 FILE *p;
5932 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
5933
5934 if (!str1 || !str2) {
5935 ast_free(str1);
5936 ast_free(str2);
5937 return -1;
5938 }
5939
5940 if (cidnum) {
5941 strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
5942 }
5943 if (cidname) {
5944 strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
5945 }
5946
5947 if ((p = ast_file_mkftemp(tmp, VOICEMAIL_FILE_MODE & ~my_umask)) == NULL) {
5948 ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
5949 ast_free(str1);
5950 ast_free(str2);
5951 return -1;
5952 }
5953 gethostname(host, sizeof(host)-1);
5954 if (strchr(srcemail, '@')) {
5955 ast_copy_string(who, srcemail, sizeof(who));
5956 } else {
5957 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
5958 }
5959 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
5960 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
5961 fprintf(p, "Date: %s\n", date);
5962
5963 /* Reformat for custom pager format */
5964 ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
5965
5967 struct ast_channel *ast;
5968 if ((ast = ast_dummy_channel_alloc())) {
5969 char *ptr;
5970 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
5972
5973 if (check_mime(ast_str_buffer(str1))) {
5974 int first_line = 1;
5975 ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
5976 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5977 *ptr = '\0';
5978 fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
5979 first_line = 0;
5980 /* Substring is smaller, so this will never grow */
5981 ast_str_set(&str2, 0, "%s", ptr + 1);
5982 }
5983 fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
5984 } else {
5985 fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
5986 }
5987 ast = ast_channel_unref(ast);
5988 } else {
5989 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5990 }
5991 } else {
5992 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
5993 }
5994
5995 if (check_mime(vmu->fullname)) {
5996 int first_line = 1;
5997 char *ptr;
5998 ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
5999 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
6000 *ptr = '\0';
6001 fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
6002 first_line = 0;
6003 /* Substring is smaller, so this will never grow */
6004 ast_str_set(&str2, 0, "%s", ptr + 1);
6005 }
6006 fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
6007 } else {
6008 fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
6009 }
6010
6012 struct ast_channel *ast;
6013 if ((ast = ast_dummy_channel_alloc())) {
6014 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
6016 if (check_mime(ast_str_buffer(str1))) {
6017 int first_line = 1;
6018 char *ptr;
6019 ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
6020 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
6021 *ptr = '\0';
6022 fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
6023 first_line = 0;
6024 /* Substring is smaller, so this will never grow */
6025 ast_str_set(&str2, 0, "%s", ptr + 1);
6026 }
6027 fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
6028 } else {
6029 fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
6030 }
6031 ast = ast_channel_unref(ast);
6032 } else {
6033 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
6034 }
6035 } else {
6036 if (ast_strlen_zero(flag)) {
6037 fprintf(p, "Subject: New VM" ENDL);
6038 } else {
6039 fprintf(p, "Subject: New %s VM" ENDL, flag);
6040 }
6041 }
6042
6043 /* End of headers */
6044 fputs(ENDL, p);
6045
6046 if (pagerbody) {
6047 struct ast_channel *ast;
6048 if ((ast = ast_dummy_channel_alloc())) {
6049 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
6051 fprintf(p, "%s" ENDL, ast_str_buffer(str1));
6052 ast = ast_channel_unref(ast);
6053 } else {
6054 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
6055 }
6056 } else {
6057 fprintf(p, "New %s long %s msg in box %s\n"
6058 "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
6059 }
6060
6061 fclose(p);
6062 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
6063 ast_safe_system(tmp2);
6064 ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
6065 ast_free(str1);
6066 ast_free(str2);
6067 return 0;
6068}
6069
6070/*!
6071 * \brief Gets the current date and time, as formatted string.
6072 * \param s The buffer to hold the output formatted date.
6073 * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
6074 *
6075 * The date format string used is "%a %b %e %r UTC %Y".
6076 *
6077 * \return zero on success, -1 on error.
6078 */
6079static int get_date(char *s, int len)
6080{
6081 struct ast_tm tm;
6082 struct timeval t = ast_tvnow();
6083
6084 ast_localtime(&t, &tm, "UTC");
6085
6086 return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
6087}
6088
6089static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
6090{
6091 int res;
6092 char fn[PATH_MAX];
6093 char dest[PATH_MAX];
6094
6095 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
6096
6097 if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
6098 ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
6099 return -1;
6100 }
6101
6102 RETRIEVE(fn, -1, ext, context);
6103 if (ast_fileexists(fn, NULL, NULL) > 0) {
6104 res = ast_stream_and_wait(chan, fn, ecodes);
6105 if (res) {
6106 DISPOSE(fn, -1);
6107 return res;
6108 }
6109 } else {
6110 /* Dispose just in case */
6111 DISPOSE(fn, -1);
6112 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
6113 if (res)
6114 return res;
6115 res = ast_say_digit_str(chan, ext, ecodes, ast_channel_language(chan));
6116 if (res)
6117 return res;
6118 }
6119 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
6120 return res;
6121}
6122
6123static void free_zone(struct vm_zone *z)
6124{
6125 ast_free(z);
6126}
6127
6128#ifdef ODBC_STORAGE
6129#define COUNT_MSGS_SQL_FMT "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'"
6130static int count_messages_in_folder(struct odbc_obj *odbc, const char *context, const char *mailbox, const char *folder, int *messages)
6131{
6132 int res;
6133 char sql[sizeof(COUNT_MSGS_SQL_FMT) + odbc_table_len + strlen(VM_SPOOL_DIR)
6134 + strlen(context) + strlen(mailbox) + strlen(folder)];
6135 char rowdata[20];
6136 SQLHSTMT stmt = NULL;
6137 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
6138 SCOPE_ENTER(3, "context: %s mb: %s folder: %s", context, mailbox, folder);
6139
6140 if (!messages) {
6141 SCOPE_EXIT_RTN_VALUE(0, "No messages pointer\n");
6142 }
6143
6144 snprintf(sql, sizeof(sql), COUNT_MSGS_SQL_FMT, odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
6145 if (!(stmt = ast_odbc_prepare_and_execute(odbc, generic_prepare, &gps))) {
6146 SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
6147 }
6148 res = SQLFetch(stmt);
6149 if (!SQL_SUCCEEDED(res)) {
6150 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6151 SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
6152 }
6153 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
6154 if (!SQL_SUCCEEDED(res)) {
6155 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6156 SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
6157 }
6158
6159 *messages = atoi(rowdata);
6160 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6161
6162 SCOPE_EXIT_RTN_VALUE(0, "messages: %d\n", *messages);
6163}
6164#undef COUNT_MSGS_SQL_FMT
6165
6166static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
6167{
6168 char tmp[PATH_MAX] = "";
6169 struct odbc_obj *obj;
6170 char *context;
6171 SCOPE_ENTER(3, "mb: %s", mailbox);
6172
6173 if (newmsgs)
6174 *newmsgs = 0;
6175 if (oldmsgs)
6176 *oldmsgs = 0;
6177 if (urgentmsgs)
6178 *urgentmsgs = 0;
6179
6180 /* If no mailbox, return immediately */
6181 if (ast_strlen_zero(mailbox)) {
6182 SCOPE_EXIT_RTN_VALUE(0, "No mailbox\n");
6183 }
6184
6185 ast_copy_string(tmp, mailbox, sizeof(tmp));
6186
6187 if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
6188 int u, n, o;
6189 char *next, *remaining = tmp;
6190 while ((next = strsep(&remaining, " ,"))) {
6191 if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
6192 SCOPE_EXIT_RTN_VALUE(-1, "Failed to obtain message count for mailbox %s\n", next);
6193 }
6194 if (urgentmsgs) {
6195 *urgentmsgs += u;
6196 }
6197 if (newmsgs) {
6198 *newmsgs += n;
6199 }
6200 if (oldmsgs) {
6201 *oldmsgs += o;
6202 }
6203 }
6204 SCOPE_EXIT_RTN_VALUE(0, "Urgent: %d New: %d Old: %d\n", urgentmsgs ? *urgentmsgs : 0, newmsgs ? *newmsgs : 0, oldmsgs ? *oldmsgs : 0);
6205 }
6206
6207 context = strchr(tmp, '@');
6208 if (context) {
6209 *context = '\0';
6210 context++;
6211 } else
6212 context = "default";
6213
6214 obj = ast_odbc_request_obj(odbc_database, 0);
6215 if (!obj) {
6216 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
6217 }
6218
6219 if (count_messages_in_folder(obj, context, tmp, "INBOX", newmsgs)
6220 || count_messages_in_folder(obj, context, tmp, "Old", oldmsgs)
6221 || count_messages_in_folder(obj, context, tmp, "Urgent", urgentmsgs)) {
6222 ast_log(AST_LOG_WARNING, "Failed to obtain message count for mailbox %s@%s\n",
6223 tmp, context);
6224 }
6225
6227 SCOPE_EXIT_RTN_VALUE(0, "Urgent: %d New: %d Old: %d\n", urgentmsgs ? *urgentmsgs : 0, newmsgs ? *newmsgs : 0, oldmsgs ? *oldmsgs : 0);
6228}
6229
6230/*!
6231 * \brief Gets the number of messages that exist in a mailbox folder.
6232 * \param mailbox_id
6233 * \param folder
6234 *
6235 * This method is used when ODBC backend is used.
6236 * \return The number of messages in this mailbox folder (zero or more).
6237 */
6238#define MSGCOUNT_SQL_FMT_INBOX "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/INBOX' OR dir = '%s%s/%s/Urgent'"
6239#define MSGCOUNT_SQL_FMT "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'"
6240static int messagecount(const char *mailbox_id, const char *folder)
6241{
6242 struct odbc_obj *obj = NULL;
6243 char *context;
6244 char *mailbox;
6245 int nummsgs = 0;
6246 int res;
6247 SQLHSTMT stmt = NULL;
6248 char rowdata[20];
6249 struct generic_prepare_struct gps = { .argc = 0 };
6250 SCOPE_ENTER(3, "mb: %s folder: %s", mailbox_id, folder);
6251
6252 /* If no mailbox, return immediately */
6253 if (ast_strlen_zero(mailbox_id)
6254 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
6255 SCOPE_EXIT_RTN_VALUE(0, "Couldn't parse mailbox\n");
6256 }
6257
6258 if (ast_strlen_zero(folder)) {
6259 folder = "INBOX";
6260 }
6261
6262 obj = ast_odbc_request_obj(odbc_database, 0);
6263 if (!obj) {
6264 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
6265 }
6266
6267 if (!strcmp(folder, "INBOX")) {
6268 res = ast_asprintf(&gps.sql, MSGCOUNT_SQL_FMT_INBOX, odbc_table, VM_SPOOL_DIR, context, mailbox, VM_SPOOL_DIR, context, mailbox);
6269 } else {
6270 res = ast_asprintf(&gps.sql, MSGCOUNT_SQL_FMT, odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
6271 }
6272 if (res <= 0) {
6273 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "Failed to allocate memory for SQL statement for '%s'!\n", odbc_database);
6274 }
6275 ast_trace(-1, "SQL: %s\n", gps.sql);
6276
6278 if (!stmt) {
6279 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", gps.sql);
6280 goto bail;
6281 }
6282 res = SQLFetch(stmt);
6283 if (!SQL_SUCCEEDED(res)) {
6284 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", gps.sql);
6285 goto bail_with_handle;
6286 }
6287 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
6288 if (!SQL_SUCCEEDED(res)) {
6289 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", gps.sql);
6290 goto bail_with_handle;
6291 }
6292 nummsgs = atoi(rowdata);
6293
6294bail_with_handle:
6295 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6296
6297bail:
6299 ast_free(gps.sql);
6300 SCOPE_EXIT_RTN_VALUE(nummsgs, "Messages: %d\n", nummsgs);
6301}
6302#undef MSGCOUNT_SQL_FMT
6303#undef MSGCOUNT_SQL_FMT_INBOX
6304
6305/*!
6306 * \brief Determines if the given folder has messages.
6307 * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
6308 *
6309 * This function is used when the mailbox is stored in an ODBC back end.
6310 * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
6311 * \return 1 if the folder has one or more messages. zero otherwise.
6312 */
6313static int has_voicemail(const char *mailboxes, const char *folder)
6314{
6315 char *parse;
6316 char *mailbox;
6317
6318 parse = ast_strdupa(mailboxes);
6319 while ((mailbox = strsep(&parse, ",&"))) {
6320 if (messagecount(mailbox, folder)) {
6321 return 1;
6322 }
6323 }
6324 return 0;
6325}
6326#endif
6327#ifndef IMAP_STORAGE
6328/*!
6329 * \brief Copies a message from one mailbox to another.
6330 * \param chan
6331 * \param vmu
6332 * \param imbox
6333 * \param msgnum
6334 * \param duration
6335 * \param recip
6336 * \param fmt
6337 * \param dir
6338 * \param flag, dest_folder
6339 *
6340 * This is only used by file storage based mailboxes.
6341 *
6342 * \return zero on success, -1 on error.
6343 */
6344static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum,
6345 long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag,
6346 const char *dest_folder)
6347{
6348 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
6349 const char *frombox = mbox(vmu, imbox);
6350 const char *userfolder;
6351 int recipmsgnum;
6352 int res = 0;
6353 SCOPE_ENTER(3, "mb: %s imb: %d msgnum: %d recip: %s dir: %s dest_folder: %s",
6354 vmu->mailbox, imbox, msgnum, recip->mailbox, dir, dest_folder);
6355
6356 ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
6357
6358 if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
6359 userfolder = "Urgent";
6360 } else if (!ast_strlen_zero(dest_folder)) {
6361 userfolder = dest_folder;
6362 } else {
6363 userfolder = "INBOX";
6364 }
6365
6366 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
6367 ast_trace(-1, "todir: %s\n", todir);
6368
6369 if (!dir)
6370 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
6371 else
6372 ast_copy_string(fromdir, dir, sizeof(fromdir));
6373
6374 ast_trace(-1, "fromdir: %s\n", fromdir);
6375
6376 make_file(frompath, sizeof(frompath), fromdir, msgnum);
6377 ast_trace(-1, "frompath: %s\n", frompath);
6378
6379 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
6380 ast_trace(-1, "todir: %s\n", todir);
6381
6382 if (vm_lock_path(todir)) {
6384 }
6385
6386 recipmsgnum = LAST_MSG_INDEX(todir) + 1;
6387 ast_trace(-1, "recip msgnum: %d\n", recipmsgnum);
6388
6389 if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
6390 int exists = 0;
6391
6392 make_file(topath, sizeof(topath), todir, recipmsgnum);
6393 ast_trace(-1, "topath: %s\n", topath);
6394
6395 exists = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, fromdir, msgnum, frompath, chan ? ast_channel_language(chan) : "");
6396 if (exists) {
6397 SCOPE_CALL(-1, COPY, fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
6398 } else {
6399 SCOPE_CALL(-1, copy_plain_file,frompath, topath);
6400 SCOPE_CALL(-1, STORE, todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL, NULL);
6401 SCOPE_CALL(-1, vm_delete, topath);
6402
6403 }
6404 } else {
6405 ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
6406 res = -1;
6407 }
6408 ast_unlock_path(todir);
6409 if (chan) {
6410 struct ast_party_caller *caller = ast_channel_caller(chan);
6411 notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
6412 S_COR(caller->id.number.valid, caller->id.number.str, NULL),
6413 S_COR(caller->id.name.valid, caller->id.name.str, NULL),
6414 flag);
6415 }
6416
6417 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d\n", res);
6418}
6419#endif
6420#if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
6421
6422static int messagecount(const char *mailbox_id, const char *folder)
6423{
6424 char *context;
6425 char *mailbox;
6426
6427 if (ast_strlen_zero(mailbox_id)
6428 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
6429 return 0;
6430 }
6431
6432 return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
6433}
6434
6435static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
6436{
6437 DIR *dir;
6438 struct dirent *de;
6439 char fn[256];
6440 int ret = 0;
6441 struct alias_mailbox_mapping *mapping;
6442 char *c;
6443 char *m;
6444
6445 /* If no mailbox, return immediately */
6447 return 0;
6448
6449 if (ast_strlen_zero(folder))
6450 folder = "INBOX";
6452 context = "default";
6453
6454 c = (char *)context;
6455 m = (char *)mailbox;
6456
6458 char tmp[MAX_VM_MAILBOX_LEN];
6459
6460 snprintf(tmp, MAX_VM_MAILBOX_LEN, "%s@%s", mailbox, context);
6462 if (mapping) {
6463 separate_mailbox(ast_strdupa(mapping->mailbox), &m, &c);
6464 ao2_ref(mapping, -1);
6465 }
6466 }
6467
6468 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, c, m, folder);
6469
6470 if (!(dir = opendir(fn)))
6471 return 0;
6472
6473 while ((de = readdir(dir))) {
6474 if (!strncasecmp(de->d_name, "msg", 3)) {
6475 if (shortcircuit) {
6476 ret = 1;
6477 break;
6478 } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
6479 ret++;
6480 }
6481 }
6482 }
6483
6484 closedir(dir);
6485
6486 return ret;
6487}
6488
6489/*!
6490 * \brief Determines if the given folder has messages.
6491 * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
6492 * \param folder the folder to look in
6493 *
6494 * This function is used when the mailbox is stored in a filesystem back end.
6495 * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
6496 * \return 1 if the folder has one or more messages. zero otherwise.
6497 */
6498static int has_voicemail(const char *mailbox, const char *folder)
6499{
6500 char tmp[256], *tmp2 = tmp, *box, *context;
6501 ast_copy_string(tmp, mailbox, sizeof(tmp));
6502 if (ast_strlen_zero(folder)) {
6503 folder = "INBOX";
6504 }
6505 while ((box = strsep(&tmp2, ",&"))) {
6506 if ((context = strchr(box, '@')))
6507 *context++ = '\0';
6508 else
6509 context = "default";
6510 if (__has_voicemail(context, box, folder, 1))
6511 return 1;
6512 /* If we are checking INBOX, we should check Urgent as well */
6513 if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
6514 return 1;
6515 }
6516 }
6517 return 0;
6518}
6519
6520/*!
6521 * \brief Check the given mailbox's message count.
6522 * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
6523 * \param urgentmsgs urgent message count.
6524 * \param newmsgs new message count.
6525 * \param oldmsgs old message count pointer
6526 * \return -1 if error occurred, 0 otherwise.
6527 */
6528static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
6529{
6530 char tmp[256];
6531 char *context;
6532
6533 /* If no mailbox, return immediately */
6534 if (ast_strlen_zero(mailbox)) {
6535 return 0;
6536 }
6537
6538 if (newmsgs) {
6539 *newmsgs = 0;
6540 }
6541 if (oldmsgs) {
6542 *oldmsgs = 0;
6543 }
6544 if (urgentmsgs) {
6545 *urgentmsgs = 0;
6546 }
6547
6548 if (strchr(mailbox, ',')) {
6549 int tmpnew, tmpold, tmpurgent;
6550 char *mb, *cur;
6551
6552 ast_copy_string(tmp, mailbox, sizeof(tmp));
6553 mb = tmp;
6554 while ((cur = strsep(&mb, ", "))) {
6555 if (!ast_strlen_zero(cur)) {
6556 if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL)) {
6557 return -1;
6558 } else {
6559 if (newmsgs) {
6560 *newmsgs += tmpnew;
6561 }
6562 if (oldmsgs) {
6563 *oldmsgs += tmpold;
6564 }
6565 if (urgentmsgs) {
6566 *urgentmsgs += tmpurgent;
6567 }
6568 }
6569 }
6570 }
6571 return 0;
6572 }
6573
6574 ast_copy_string(tmp, mailbox, sizeof(tmp));
6575
6576 if ((context = strchr(tmp, '@'))) {
6577 *context++ = '\0';
6578 } else {
6579 context = "default";
6580 }
6581
6582 if (newmsgs) {
6583 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
6584 }
6585 if (oldmsgs) {
6586 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
6587 }
6588 if (urgentmsgs) {
6589 *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
6590 }
6591
6592 return 0;
6593}
6594
6595#endif
6596
6597/* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
6598static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
6599{
6600 int urgentmsgs = 0;
6601 int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
6602 if (newmsgs) {
6603 *newmsgs += urgentmsgs;
6604 }
6605 return res;
6606}
6607
6608static void run_externnotify(const char *context, const char *extension, const char *flag)
6609{
6610 char arguments[255];
6611 char ext_context[256] = "";
6612 int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
6613 struct ast_smdi_mwi_message *mwi_msg;
6614
6616 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
6617 else
6618 ast_copy_string(ext_context, extension, sizeof(ext_context));
6619
6620 if (smdi_iface) {
6621 if (ast_app_has_voicemail(ext_context, NULL))
6623 else
6625
6627 ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
6628 if (!strncmp(mwi_msg->cause, "INV", 3))
6629 ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
6630 else if (!strncmp(mwi_msg->cause, "BLK", 3))
6631 ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
6632 ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
6633 ao2_ref(mwi_msg, -1);
6634 } else {
6635 ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
6636 }
6637 }
6638
6640 if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
6641 ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
6642 } else {
6643 snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
6644 externnotify, S_OR(context, "\"\""),
6645 extension, newvoicemails,
6646 oldvoicemails, urgentvoicemails);
6647 ast_debug(1, "Executing %s\n", arguments);
6648 ast_safe_system(arguments);
6649 }
6650 }
6651}
6652
6653/*!
6654 * \brief Variables used for saving a voicemail.
6655 *
6656 * This includes the record gain, mode flags, and the exit context of the channel that was used for leaving the voicemail.
6657 */
6658struct leave_vm_options {
6659 unsigned int flags;
6660 signed char record_gain;
6661 char *exitcontext;
6662 char *beeptone;
6663};
6664
6665static void generate_msg_id(char *dst)
6666{
6667 /* msg id is time of msg_id generation plus an incrementing value
6668 * called each time a new msg_id is generated. This should achieve uniqueness,
6669 * but only in single system solutions.
6670 */
6671 unsigned int unique_counter = ast_atomic_fetchadd_int(&msg_id_incrementor, +1);
6672 snprintf(dst, MSG_ID_LEN, "%ld-%08x", (long) time(NULL), unique_counter);
6673}
6674
6675/*!
6676 * \internal
6677 * \brief Creates a voicemail based on a specified file to a mailbox.
6678 * \param recdata A vm_recording_data containing filename and voicemail txt info.
6679 * \retval -1 failure
6680 * \retval 0 success
6681 *
6682 * This is installed to the app.h voicemail functions and accommodates all voicemail
6683 * storage methods. It should probably be broken out along with leave_voicemail at
6684 * some point in the future.
6685 *
6686 * This function currently only works for a single recipient and only uses the format
6687 * specified in recording_ext.
6688 */
6690{
6691 /* voicemail recipient structure */
6692 struct ast_vm_user *recipient; /* points to svm once it's been created */
6693 struct ast_vm_user svm; /* struct storing the voicemail recipient */
6694
6695 /* File paths */
6696 char tmpdir[PATH_MAX]; /* directory temp files are stored in */
6697 char tmptxtfile[PATH_MAX]; /* tmp file for voicemail txt file */
6698 char desttxtfile[PATH_MAX]; /* final destination for txt file */
6699 char tmpaudiofile[PATH_MAX]; /* tmp file where audio is stored */
6700 char dir[PATH_MAX]; /* destination for tmp files on completion */
6701 char destination[PATH_MAX]; /* destination with msgXXXX. Basically <dir>/msgXXXX */
6702
6703 /* stuff that only seems to be needed for IMAP */
6704 #ifdef IMAP_STORAGE
6705 struct vm_state *vms = NULL;
6706 char ext_context[256] = "";
6707 int newmsgs = 0;
6708 int oldmsgs = 0;
6709 #endif
6710
6711 /* miscellaneous operational variables */
6712 int res = 0; /* Used to store error codes from functions */
6713 int txtdes /* File descriptor for the text file used to write the voicemail info */;
6714 FILE *txt; /* FILE pointer to text file used to write the voicemail info */
6715 char date[256]; /* string used to hold date of the voicemail (only used for ODBC) */
6716 int msgnum; /* the 4 digit number designated to the voicemail */
6717 int duration = 0; /* Length of the audio being recorded in seconds */
6718 struct ast_filestream *recording_fs; /*used to read the recording to get duration data */
6719
6720 /* We aren't currently doing anything with category, since it comes from a channel variable and
6721 * this function doesn't use channels, but this function could add that as an argument later. */
6722 const char *category = NULL; /* pointless for now */
6723 char msg_id[MSG_ID_LEN];
6724
6725 /* Start by checking to see if the file actually exists... */
6726 if (!(ast_fileexists(recdata->recording_file, recdata->recording_ext, NULL))) {
6727 ast_log(LOG_ERROR, "File: %s not found.\n", recdata->recording_file);
6728 return -1;
6729 }
6730
6731 memset(&svm, 0, sizeof(svm));
6732 if (!(recipient = find_user(&svm, recdata->context, recdata->mailbox))) {
6733 ast_log(LOG_ERROR, "No entry in voicemail config file for '%s@%s'\n", recdata->mailbox, recdata->context);
6734 return -1;
6735 }
6736
6737 /* determine duration in seconds */
6738 if ((recording_fs = ast_readfile(recdata->recording_file, recdata->recording_ext, NULL, 0, 0, VOICEMAIL_DIR_MODE))) {
6739 if (!ast_seekstream(recording_fs, 0, SEEK_END)) {
6740 long framelength = ast_tellstream(recording_fs);
6741 int sample_rate = ast_ratestream(recording_fs);
6742 if (sample_rate) {
6743 duration = (int) (framelength / sample_rate);
6744 } else {
6745 ast_log(LOG_ERROR,"Unable to determine sample rate of recording %s\n", recdata->recording_file);
6746 }
6747 }
6748 ast_closestream(recording_fs);
6749 }
6750
6751 /* If the duration was below the minimum duration for the user, let's just drop the whole thing now */
6752 if (duration < recipient->minsecs) {
6753 ast_log(LOG_NOTICE, "Copying recording to voicemail %s@%s skipped because duration was shorter than "
6754 "minmessage of recipient\n", recdata->mailbox, recdata->context);
6755 return -1;
6756 }
6757
6758 /* Note that this number must be dropped back to a net sum of zero before returning from this function */
6759
6760 if ((res = create_dirpath(tmpdir, sizeof(tmpdir), recipient->context, recdata->mailbox, "tmp"))) {
6761 ast_log(LOG_ERROR, "Failed to make directory.\n");
6762 }
6763
6764 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
6765 txtdes = mkstemp(tmptxtfile);
6766 if (txtdes < 0) {
6767 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
6768 /* Something screwed up. Abort. */
6769 ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
6770 free_user(recipient);
6771 return -1;
6772 }
6773
6774 /* Store information */
6775 txt = fdopen(txtdes, "w+");
6776 if (txt) {
6777 generate_msg_id(msg_id);
6778 get_date(date, sizeof(date));
6779 fprintf(txt,
6780 ";\n"
6781 "; Message Information file\n"
6782 ";\n"
6783 "[message]\n"
6784 "origmailbox=%s\n"
6785 "context=%s\n"
6786 "exten=%s\n"
6787 "rdnis=Unknown\n"
6788 "priority=%d\n"
6789 "callerchan=%s\n"
6790 "callerid=%s\n"
6791 "origdate=%s\n"
6792 "origtime=%ld\n"
6793 "category=%s\n"
6794 "msg_id=%s\n"
6795 "flag=\n" /* flags not supported in copy from file yet */
6796 "duration=%d\n", /* Don't have any reliable way to get duration of file. */
6797
6798 recdata->mailbox,
6799 S_OR(recdata->call_context, ""),
6800 S_OR(recdata->call_extension, ""),
6801 recdata->call_priority,
6802 S_OR(recdata->call_callerchan, "Unknown"),
6803 S_OR(recdata->call_callerid, "Unknown"),
6804 date, (long) time(NULL),
6805 S_OR(category, ""),
6806 msg_id,
6807 duration);
6808
6809 /* Since we are recording from a file, we shouldn't need to do anything else with
6810 * this txt file */
6811 fclose(txt);
6812
6813 } else {
6814 ast_log(LOG_WARNING, "Error opening text file for output\n");
6815 if (ast_check_realtime("voicemail_data")) {
6816 ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6817 }
6818 free_user(recipient);
6819 return -1;
6820 }
6821
6822 /* At this point, the actual creation of a voicemail message should be finished.
6823 * Now we just need to copy the files being recorded into the receiving folder. */
6824
6825 create_dirpath(dir, sizeof(dir), recipient->context, recipient->mailbox, recdata->folder);
6826
6827#ifdef IMAP_STORAGE
6828 /* make recipient info into an inboxcount friendly string */
6829 snprintf(ext_context, sizeof(ext_context), "%s@%s", recipient->mailbox, recipient->context);
6830
6831 /* Is ext a mailbox? */
6832 /* must open stream for this user to get info! */
6833 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
6834 if (res < 0) {
6835 ast_log(LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
6836 free_user(recipient);
6837 unlink(tmptxtfile);
6838 return -1;
6839 }
6840 if (!(vms = get_vm_state_by_mailbox(recipient->mailbox, recipient->context, 0))) {
6841 /* It is possible under certain circumstances that inboxcount did not
6842 * create a vm_state when it was needed. This is a catchall which will
6843 * rarely be used.
6844 */
6845 if (!(vms = create_vm_state_from_user(recipient))) {
6846 ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
6847 free_user(recipient);
6848 unlink(tmptxtfile);
6849 return -1;
6850 }
6851 }
6852 vms->newmessages++;
6853
6854 /* here is a big difference! We add one to it later */
6855 msgnum = newmsgs + oldmsgs;
6856 ast_debug(3, "Messagecount set to %d\n", msgnum);
6857 snprintf(destination, sizeof(destination), "%simap/msg%s%04d", VM_SPOOL_DIR, recipient->mailbox, msgnum);
6858
6859 /* Check to see if we have enough room in the mailbox. If not, spit out an error and end
6860 * Note that imap_check_limits raises inprocess_count if successful */
6861 if ((res = imap_check_limits(NULL, vms, recipient, msgnum))) {
6862 ast_log(LOG_NOTICE, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
6863 inprocess_count(recipient->mailbox, recipient->context, -1);
6864 free_user(recipient);
6865 unlink(tmptxtfile);
6866 return -1;
6867 }
6868
6869#else
6870
6871 /* Check to see if the mailbox is full for ODBC/File storage */
6872 ast_debug(3, "mailbox = %d : inprocess = %d\n", COUNT(recipient, dir),
6873 inprocess_count(recipient->mailbox, recipient->context, 0));
6874 if (COUNT(recipient, dir) > recipient->maxmsg - inprocess_count(recipient->mailbox, recipient->context, +1)) {
6875 ast_log(AST_LOG_WARNING, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
6876 inprocess_count(recipient->mailbox, recipient->context, -1);
6877 free_user(recipient);
6878 unlink(tmptxtfile);
6879 return -1;
6880 }
6881
6882 msgnum = LAST_MSG_INDEX(dir) + 1;
6883#endif
6884
6885 /* Lock the directory receiving the voicemail since we want it to still exist when we attempt to copy the voicemail.
6886 * We need to unlock it before we return. */
6887 if (vm_lock_path(dir)) {
6888 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
6889 /* Delete files */
6890 ast_filedelete(tmptxtfile, NULL);
6891 unlink(tmptxtfile);
6892 free_user(recipient);
6893 return -1;
6894 }
6895
6896 make_file(destination, sizeof(destination), dir, msgnum);
6897
6898 make_file(tmpaudiofile, sizeof(tmpaudiofile), tmpdir, msgnum);
6899
6900 if (ast_filecopy(recdata->recording_file, tmpaudiofile, recdata->recording_ext)) {
6901 ast_log(LOG_ERROR, "Audio file failed to copy to tmp dir. Probably low disk space.\n");
6902
6903 inprocess_count(recipient->mailbox, recipient->context, -1);
6904 ast_unlock_path(dir);
6905 free_user(recipient);
6906 unlink(tmptxtfile);
6907 return -1;
6908 }
6909
6910 /* Alright, try to copy to the destination folder now. */
6911 if (ast_filerename(tmpaudiofile, destination, recdata->recording_ext)) {
6912 ast_log(LOG_ERROR, "Audio file failed to move to destination directory. Permissions/Overlap?\n");
6913 inprocess_count(recipient->mailbox, recipient->context, -1);
6914 ast_unlock_path(dir);
6915 free_user(recipient);
6916 unlink(tmptxtfile);
6917 return -1;
6918 }
6919
6920 snprintf(desttxtfile, sizeof(desttxtfile), "%s.txt", destination);
6921 rename(tmptxtfile, desttxtfile);
6922
6923 if (chmod(desttxtfile, VOICEMAIL_FILE_MODE) < 0) {
6924 ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", desttxtfile, strerror(errno));
6925 }
6926
6927
6928 ast_unlock_path(dir);
6929 inprocess_count(recipient->mailbox, recipient->context, -1);
6930
6931 /* If we copied something, we should store it either to ODBC or IMAP if we are using those. The STORE macro allows us
6932 * 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
6933 * time to create the voicemail database entry. */
6934 if (ast_fileexists(destination, NULL, NULL) > 0) {
6935 struct ast_channel *chan = NULL;
6936 char fmt[80];
6937 char clid[80];
6938 char cidnum[80], cidname[80];
6939 int send_email;
6940
6941 if (ast_check_realtime("voicemail_data")) {
6942 get_date(date, sizeof(date));
6943 ast_store_realtime("voicemail_data",
6944 "origmailbox", recdata->mailbox,
6945 "context", S_OR(recdata->context, ""),
6946 "exten", S_OR(recdata->call_extension, ""),
6947 "priority", recdata->call_priority,
6948 "callerchan", S_OR(recdata->call_callerchan, "Unknown"),
6949 "callerid", S_OR(recdata->call_callerid, "Unknown"),
6950 "origdate", date,
6951 "origtime", time(NULL),
6952 "category", S_OR(category, ""),
6953 "filename", tmptxtfile,
6954 "duration", duration,
6955 SENTINEL);
6956 }
6957
6958 STORE(dir, recipient->mailbox, recipient->context, msgnum, NULL, recipient, fmt, 0, vms, "", msg_id);
6959
6960 send_email = ast_test_flag(recipient, VM_EMAIL_EXT_RECS);
6961
6962 if (send_email) {
6963 /* Send an email if possible, fall back to just notifications if not. */
6964 ast_copy_string(fmt, recdata->recording_ext, sizeof(fmt));
6965 ast_copy_string(clid, recdata->call_callerid, sizeof(clid));
6966 ast_callerid_split(clid, cidname, sizeof(cidname), cidnum, sizeof(cidnum));
6967
6968 /* recdata->call_callerchan itself no longer exists, so we can't use the real channel. Use a dummy one. */
6969 chan = ast_dummy_channel_alloc();
6970 }
6971 if (chan) {
6972 notify_new_message(chan, recipient, NULL, msgnum, duration, fmt, cidnum, cidname, "");
6973 ast_channel_unref(chan);
6974 } else {
6975 if (send_email) { /* We tried and failed. */
6976 ast_log(LOG_WARNING, "Failed to allocate dummy channel, email will not be sent\n");
6977 }
6978 notify_new_state(recipient);
6979 }
6980 }
6981
6982 free_user(recipient);
6983 unlink(tmptxtfile);
6984 return 0;
6985}
6986
6987/*!
6988 * \brief Prompts the user and records a voicemail to a mailbox.
6989 * \param chan
6990 * \param ext
6991 * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
6992 *
6993 *
6994 *
6995 * \return zero on success, -1 on error.
6996 */
6997static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
6998{
6999#ifdef IMAP_STORAGE
7000 int newmsgs, oldmsgs;
7001#endif
7002 char txtfile[PATH_MAX];
7003 char tmptxtfile[PATH_MAX];
7004 struct vm_state *vms = NULL;
7005 char callerid[256];
7006 FILE *txt;
7007 char date[256];
7008 int txtdes;
7009 int res = 0;
7010 int msgnum;
7011 int duration = 0;
7012 int sound_duration = 0;
7013 int ouseexten = 0;
7014 int greeting_only = 0;
7015 char tmpdur[16];
7016 char priority[16];
7017 char origtime[16];
7018 char dir[PATH_MAX];
7019 char tmpdir[PATH_MAX];
7020 char fn[PATH_MAX];
7021 char prefile[PATH_MAX] = "";
7022 char tempfile[PATH_MAX] = "";
7023 char ext_context[256] = "";
7024 char fmt[80];
7025 char *context;
7026 char ecodes[17] = "#";
7027 struct ast_str *tmp = ast_str_create(16);
7028 char *tmpptr;
7029 struct ast_vm_user *vmu;
7030 struct ast_vm_user svm;
7031 const char *category = NULL;
7032 const char *code;
7033 const char *alldtmf = "0123456789ABCD*#";
7034 char flag[80];
7035 SCOPE_ENTER(3, "%s: %s\n", ast_channel_name(chan), ext);
7036
7037 if (!tmp) {
7038 SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for tmp\n");
7039 }
7040
7041 ast_str_set(&tmp, 0, "%s", ext);
7042 ext = ast_str_buffer(tmp);
7043 if ((context = strchr(ext, '@'))) {
7044 *context++ = '\0';
7045 tmpptr = strchr(context, '&');
7046 } else {
7047 tmpptr = strchr(ext, '&');
7048 }
7049
7050 if (tmpptr)
7051 *tmpptr++ = '\0';
7052
7053 ast_channel_lock(chan);
7054 if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
7055 category = ast_strdupa(category);
7056 }
7057 ast_channel_unlock(chan);
7058
7060 ast_copy_string(flag, "Urgent", sizeof(flag));
7062 ast_copy_string(flag, "PRIORITY", sizeof(flag));
7063 } else {
7064 flag[0] = '\0';
7065 }
7066
7067 ast_debug(3, "Before find_user\n");
7068 memset(&svm, 0, sizeof(svm));
7069 if (!(vmu = find_user(&svm, context, ext))) {
7070 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7071 ast_free(tmp);
7073 "%s: Exten: %s: No entry in voicemail config file for '%s'\n", ast_channel_name(chan), ext, ext);
7074 }
7075
7076 /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
7077 if (vmu->maxmsg == 0) {
7078 greeting_only = 1;
7080 }
7081
7082 /* Setup pre-file if appropriate */
7083 if (strcmp(vmu->context, "default"))
7084 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
7085 else
7086 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
7087
7088 /* Set the path to the prefile. Will be one of
7089 VM_SPOOL_DIRcontext/ext/busy
7090 VM_SPOOL_DIRcontext/ext/unavail
7091 Depending on the flag set in options.
7092 */
7094 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
7096 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
7097 }
7098 ast_trace(-1, "prefile: %s\n", prefile);
7099 /* Set the path to the tmpfile as
7100 VM_SPOOL_DIR/context/ext/temp
7101 and attempt to create the folder structure.
7102 */
7103 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
7104 ast_trace(-1, "tempfile: %s\n", tempfile);
7105 if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
7106 free_user(vmu);
7107 ast_free(tmp);
7109 "%s: Exten: %s: Failed to make directory (%s)\n", ast_channel_name(chan), ext, tempfile);
7110 }
7111 SCOPE_CALL(-1, RETRIEVE, tempfile, -1, vmu->mailbox, vmu->context);
7112 if (ast_fileexists(tempfile, NULL, NULL) > 0) {
7113 ast_copy_string(prefile, tempfile, sizeof(prefile));
7114 ast_trace(-1, "new prefile: %s\n", prefile);
7115 }
7116
7117 SCOPE_CALL(-1, DISPOSE, tempfile, -1);
7118
7119 /* It's easier just to try to make it than to check for its existence */
7120#ifndef IMAP_STORAGE
7121 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
7122#else
7123 snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
7124 if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
7125 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
7126 }
7127#endif
7128
7129 /* Check current context for special extensions */
7130 if (ast_test_flag(vmu, VM_OPERATOR)) {
7131 if (!ast_strlen_zero(vmu->exit)) {
7132 if (ast_exists_extension(chan, vmu->exit, "o", 1,
7133 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7134 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
7135 ouseexten = 1;
7136 }
7137 } else if (ast_exists_extension(chan, ast_channel_context(chan), "o", 1,
7138 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7139 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
7140 ouseexten = 1;
7141 }
7142 }
7143
7144 if (!ast_strlen_zero(vmu->exit)) {
7145 if (ast_exists_extension(chan, vmu->exit, "a", 1,
7146 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7147 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
7148 }
7149 } else if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
7150 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7151 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
7152 }
7153
7155 for (code = alldtmf; *code; code++) {
7156 char e[2] = "";
7157 e[0] = *code;
7158 if (strchr(ecodes, e[0]) == NULL
7159 && ast_canmatch_extension(chan,
7160 (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : ast_channel_context(chan)),
7161 e, 1, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7162 strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
7163 }
7164 }
7165 }
7166
7167 /* Play the beginning intro if desired */
7168 if (!ast_strlen_zero(prefile)) {
7169#if defined(ODBC_STORAGE)
7170 int success = SCOPE_CALL_WITH_INT_RESULT(-1, RETRIEVE, prefile, -1, ext, context);
7171#elif defined(IMAP_STORAGE)
7172 SCOPE_CALL(-1, RETRIEVE, prefile, -1, ext, context);
7173#endif
7174
7175 if (ast_fileexists(prefile, NULL, NULL) > 0) {
7176 if (ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1) {
7177 /* We know we have a greeting at this point, so squelch the instructions
7178 * if that is what is being asked of us */
7181 }
7182 res = ast_waitstream(chan, ecodes);
7183 }
7184#ifdef ODBC_STORAGE
7185 if (success == -1) {
7186 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
7187 ast_trace(-1, "Greeting '%s' not retrieved from database, but found in file storage. Inserting into database\n", prefile);
7188 SCOPE_CALL(-1, odbc_store_message, prefile, vmu->mailbox, vmu->context, -1);
7189 }
7190#endif
7191 } else {
7192 ast_trace(-1, "%s doesn't exist, doing what we can\n", prefile);
7193 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
7194 }
7195 SCOPE_CALL(-1, DISPOSE, prefile, -1);
7196 if (res < 0) {
7197 free_user(vmu);
7198 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7199 ast_free(tmp);
7200 SCOPE_EXIT_RTN_VALUE(-1, "Hang up during prefile playback\n");
7201 }
7202 }
7203 if (res == '#') {
7204 /* On a '#' we skip the instructions */
7206 res = 0;
7207 }
7208 if (!res && !ast_test_flag(options, OPT_SILENT)) {
7209 res = ast_stream_and_wait(chan, INTRO, ecodes);
7210 if (res == '#') {
7212 res = 0;
7213 }
7214 }
7215 if (res > 0)
7216 ast_stopstream(chan);
7217 /* Check for a '*' here in case the caller wants to escape from voicemail to something
7218 other than the operator -- an automated attendant or mailbox login for example */
7219 if (res == '*') {
7220 ast_channel_exten_set(chan, "a");
7221 if (!ast_strlen_zero(vmu->exit)) {
7222 ast_channel_context_set(chan, vmu->exit);
7223 }
7224 ast_channel_priority_set(chan, 0);
7225 free_user(vmu);
7226 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
7227 ast_free(tmp);
7228 SCOPE_EXIT_RTN_VALUE(0, "User escaped with '*'\n");
7229 }
7230
7231 /* Check for a '0' here */
7232 if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
7233 transfer:
7234 if (ouseexten) {
7235 ast_channel_exten_set(chan, "o");
7236 if (!ast_strlen_zero(vmu->exit)) {
7237 ast_channel_context_set(chan, vmu->exit);
7238 }
7239 ast_play_and_wait(chan, "transfer");
7240 ast_channel_priority_set(chan, 0);
7241 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
7242 }
7243 free_user(vmu);
7244 ast_free(tmp);
7245 SCOPE_EXIT_RTN_VALUE(OPERATOR_EXIT, "User escaped with '0'\n");
7246 }
7247
7248 /* Allow all other digits to exit Voicemail and return to the dialplan */
7249 if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
7250 if (!ast_strlen_zero(options->exitcontext)) {
7251 ast_channel_context_set(chan, options->exitcontext);
7252 }
7253 free_user(vmu);
7254 ast_free(tmp);
7255 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
7256 SCOPE_EXIT_RTN_VALUE(res, "User escaped back to dialplan '%c'\n", res);
7257 }
7258
7259 if (greeting_only) {
7260 ast_debug(3, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
7261 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
7262 res = 0;
7263 goto leave_vm_out;
7264 }
7265
7266 if (res < 0) {
7267 free_user(vmu);
7268 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7269 ast_free(tmp);
7270 SCOPE_EXIT_RTN_VALUE(-1, "Other error '%d'\n", res);
7271 }
7272 /* The meat of recording the message... All the announcements and beeps have been played*/
7273 if (ast_channel_state(chan) != AST_STATE_UP) {
7274 ast_answer(chan);
7275 }
7276 ast_copy_string(fmt, vmfmts, sizeof(fmt));
7277 if (!ast_strlen_zero(fmt)) {
7278 char msg_id[MSG_ID_LEN] = "";
7279 msgnum = 0;
7280
7281#ifdef IMAP_STORAGE
7282 /* Is ext a mailbox? */
7283 /* must open stream for this user to get info! */
7284 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
7285 if (res < 0) {
7286 ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
7287 free_user(vmu);
7288 ast_free(tmp);
7289 return -1;
7290 }
7291 if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
7292 /* It is possible under certain circumstances that inboxcount did not
7293 * create a vm_state when it was needed. This is a catchall which will
7294 * rarely be used.
7295 */
7296 if (!(vms = create_vm_state_from_user(vmu))) {
7297 ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
7298 free_user(vmu);
7299 ast_free(tmp);
7300 return -1;
7301 }
7302 }
7303 vms->newmessages++;
7304
7305 /* here is a big difference! We add one to it later */
7306 msgnum = newmsgs + oldmsgs;
7307 ast_debug(3, "Messagecount set to %d\n", msgnum);
7308 snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
7309 /* set variable for compatibility */
7310 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
7311
7312 if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
7313 goto leave_vm_out;
7314 }
7315#else
7316 if (COUNT(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
7317 res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
7318 if (!res)
7319 res = ast_waitstream(chan, "");
7320 ast_log(AST_LOG_WARNING, "No more messages possible\n");
7321 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7322 inprocess_count(vmu->mailbox, vmu->context, -1);
7323 goto leave_vm_out;
7324 }
7325
7326#endif
7327 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
7328 ast_trace(-1, "Tempfile: %s\n", tmptxtfile);
7329 txtdes = mkstemp(tmptxtfile);
7330 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
7331 if (txtdes < 0) {
7332 res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
7333 if (!res)
7334 res = ast_waitstream(chan, "");
7335 ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
7336 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7337 inprocess_count(vmu->mailbox, vmu->context, -1);
7338 goto leave_vm_out;
7339 }
7340
7341 /* Now play the beep once we have the message number for our next message. */
7342 if (res >= 0) {
7343 /* Unless we're *really* silent, try to send the beep */
7344 /* Play default or custom beep, unless no beep desired */
7345 if (!ast_strlen_zero(options->beeptone)) {
7346 res = ast_stream_and_wait(chan, options->beeptone, "");
7347 }
7348 }
7349
7350 /* Store information in real-time storage */
7351 if (ast_check_realtime("voicemail_data")) {
7352 snprintf(priority, sizeof(priority), "%d", ast_channel_priority(chan));
7353 snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
7354 get_date(date, sizeof(date));
7355 ast_callerid_merge(callerid, sizeof(callerid),
7356 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7357 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7358 "Unknown");
7359 ast_store_realtime("voicemail_data",
7360 "origmailbox", ext,
7361 "context", ast_channel_context(chan),
7362 "exten", ast_channel_exten(chan),
7363 "priority", priority,
7364 "callerchan", ast_channel_name(chan),
7365 "callerid", callerid,
7366 "origdate", date,
7367 "origtime", origtime,
7368 "category", S_OR(category, ""),
7369 "filename", tmptxtfile,
7370 SENTINEL);
7371 }
7372
7373 /* Store information */
7374 txt = fdopen(txtdes, "w+");
7375 if (txt) {
7376 generate_msg_id(msg_id);
7377 get_date(date, sizeof(date));
7378 ast_callerid_merge(callerid, sizeof(callerid),
7379 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7380 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7381 "Unknown");
7382 fprintf(txt,
7383 ";\n"
7384 "; Message Information file\n"
7385 ";\n"
7386 "[message]\n"
7387 "origmailbox=%s\n"
7388 "context=%s\n"
7389 "exten=%s\n"
7390 "rdnis=%s\n"
7391 "priority=%d\n"
7392 "callerchan=%s\n"
7393 "callerid=%s\n"
7394 "origdate=%s\n"
7395 "origtime=%ld\n"
7396 "category=%s\n"
7397 "msg_id=%s\n",
7398 ext,
7399 ast_channel_context(chan),
7400 ast_channel_exten(chan),
7401 S_COR(ast_channel_redirecting(chan)->from.number.valid,
7402 ast_channel_redirecting(chan)->from.number.str, "unknown"),
7404 ast_channel_name(chan),
7405 callerid,
7406 date, (long) time(NULL),
7407 category ? category : "",
7408 msg_id);
7409 ast_trace(-1, "Saving txt file mbox: %s msg_id: %s\n", ext, msg_id);
7410 } else {
7411 ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
7412 inprocess_count(vmu->mailbox, vmu->context, -1);
7413 if (ast_check_realtime("voicemail_data")) {
7414 ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7415 }
7416 res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
7417 goto leave_vm_out;
7418 }
7419
7420 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);
7421
7422 /* At this point, either we were instructed to make the message Urgent
7423 by arguments to VoiceMail or during the review process by the person
7424 leaving the message. So we update the directory where we want this
7425 message to go. */
7426 if (!strcmp(flag, "Urgent")) {
7427 create_dirpath(dir, sizeof(dir), vmu->context, ext, "Urgent");
7428 }
7429
7430 if (txt) {
7431 fprintf(txt, "flag=%s\n", flag);
7432 if (sound_duration < vmu->minsecs) {
7433 fclose(txt);
7434 ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
7435 ast_filedelete(tmptxtfile, NULL);
7436 unlink(tmptxtfile);
7437 if (ast_check_realtime("voicemail_data")) {
7438 ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7439 }
7440 inprocess_count(vmu->mailbox, vmu->context, -1);
7441 } else {
7442 fprintf(txt, "duration=%d\n", duration);
7443 fclose(txt);
7444 if (vm_lock_path(dir)) {
7445 ast_log(AST_LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
7446 /* Delete files */
7447 ast_filedelete(tmptxtfile, NULL);
7448 unlink(tmptxtfile);
7449 inprocess_count(vmu->mailbox, vmu->context, -1);
7450 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
7451 ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
7452 unlink(tmptxtfile);
7453 ast_unlock_path(dir);
7454 inprocess_count(vmu->mailbox, vmu->context, -1);
7455 if (ast_check_realtime("voicemail_data")) {
7456 ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7457 }
7458 } else {
7459#ifndef IMAP_STORAGE
7460 msgnum = LAST_MSG_INDEX(dir) + 1;
7461#endif
7462 make_file(fn, sizeof(fn), dir, msgnum);
7463
7464 /* assign a variable with the name of the voicemail file */
7465#ifndef IMAP_STORAGE
7466 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
7467#else
7468 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
7469#endif
7470
7471 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
7472
7473 ast_trace(-1, "Renaming recordings '%s' -> fn '%s'\n", tmptxtfile, fn);
7474 /* ast_filerename renames the recordings but not the txt file */
7475 ast_filerename(tmptxtfile, fn, NULL);
7476
7477 ast_trace(-1, "Renaming txt file '%s' -> fn '%s'\n", tmptxtfile, txtfile);
7478 rename(tmptxtfile, txtfile);
7479 inprocess_count(vmu->mailbox, vmu->context, -1);
7480
7481 /* Properly set permissions on voicemail text descriptor file.
7482 Unfortunately mkstemp() makes this file 0600 on most unix systems. */
7483 if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
7484 ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
7485
7486 ast_unlock_path(dir);
7487 if (ast_check_realtime("voicemail_data")) {
7488 snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
7489 ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
7490 }
7491 /* We must store the file first, before copying the message, because
7492 * ODBC storage does the entire copy with SQL.
7493 */
7494 if (ast_fileexists(fn, NULL, NULL) > 0) {
7495 SCOPE_CALL(-1, STORE, dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag, msg_id);
7496 }
7497
7498 /* Are there to be more recipients of this message? */
7499 while (tmpptr) {
7500 struct ast_vm_user recipu, *recip;
7501 char *exten, *cntx;
7502
7503 exten = strsep(&tmpptr, "&");
7504 cntx = strchr(exten, '@');
7505 if (cntx) {
7506 *cntx = '\0';
7507 cntx++;
7508 }
7509 memset(&recipu, 0, sizeof(recipu));
7510 if ((recip = find_user(&recipu, cntx, exten))) {
7511 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag, NULL);
7512 free_user(recip);
7513 }
7514 }
7515
7516 /* Notification needs to happen after the copy, though. */
7517 if (ast_fileexists(fn, NULL, NULL)) {
7518#ifdef IMAP_STORAGE
7519 notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
7520 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7521 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7522 flag);
7523#else
7524 notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
7525 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7526 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7527 flag);
7528#endif
7529 }
7530
7531 /* Disposal needs to happen after the optional move and copy */
7532 if (ast_fileexists(fn, NULL, NULL)) {
7533 SCOPE_CALL(-1, DISPOSE, dir, msgnum);
7534 }
7535 }
7536 }
7537 } else {
7538 inprocess_count(vmu->mailbox, vmu->context, -1);
7539 }
7540 if (res == '0') {
7541 goto transfer;
7542 } else if (res > 0 && res != 't')
7543 res = 0;
7544
7545 if (sound_duration < vmu->minsecs)
7546 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
7547 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7548 else
7549 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
7550 } else
7551 ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
7552leave_vm_out:
7553 free_user(vmu);
7554
7555#ifdef IMAP_STORAGE
7556 /* expunge message - use UID Expunge if supported on IMAP server*/
7557 ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
7558 if (expungeonhangup == 1 && vms->mailstream != NULL) {
7559 ast_mutex_lock(&vms->lock);
7560#ifdef HAVE_IMAP_TK2006
7561 if (LEVELUIDPLUS (vms->mailstream)) {
7562 mail_expunge_full(vms->mailstream, NIL, EX_UID);
7563 } else
7564#endif
7565 mail_expunge(vms->mailstream);
7566 ast_mutex_unlock(&vms->lock);
7567 }
7568#endif
7569
7570 ast_free(tmp);
7571 SCOPE_EXIT_RTN_VALUE(res, "Done: '%d'\n", res);
7572}
7573
7574#if !defined(IMAP_STORAGE)
7575static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
7576{
7577 /* we know the actual number of messages, so stop process when number is hit */
7578
7579 int x, dest;
7580 char sfn[PATH_MAX];
7581 char dfn[PATH_MAX];
7582
7583 if (vm_lock_path(dir)) {
7584 return ERROR_LOCK_PATH;
7585 }
7586
7587 for (x = 0, dest = 0; dest != stopcount && x < MAXMSGLIMIT; x++) {
7588 make_file(sfn, sizeof(sfn), dir, x);
7589 if (EXISTS(dir, x, sfn, NULL)) {
7590
7591 if (x != dest) {
7592 make_file(dfn, sizeof(dfn), dir, dest);
7593 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
7594 }
7595
7596 dest++;
7597 }
7598 }
7599 ast_unlock_path(dir);
7600
7601 return dest;
7602}
7603#endif
7604
7605static int say_and_wait(struct ast_channel *chan, int num, const char *language)
7606{
7607 int d;
7608 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
7609 return d;
7610}
7611
7612static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move)
7613{
7614#ifdef IMAP_STORAGE
7615 /* we must use mbox(x) folder names, and copy the message there */
7616 /* simple. huh? */
7617 char sequence[10];
7618 char mailbox[256];
7619 int res;
7620 int curr_mbox;
7621
7622 /* get the real IMAP message number for this message */
7623 snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
7624
7625 ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
7626 ast_mutex_lock(&vms->lock);
7627 /* if save to Old folder, put in INBOX as read */
7628 if (box == OLD_FOLDER) {
7629 mail_setflag(vms->mailstream, sequence, "\\Seen");
7630 } else if (box == NEW_FOLDER) {
7631 mail_clearflag(vms->mailstream, sequence, "\\Seen");
7632 }
7633 if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
7634 ast_mutex_unlock(&vms->lock);
7635 return 0;
7636 }
7637
7638 /* get the current mailbox so that we can point the mailstream back to it later */
7639 curr_mbox = get_folder_by_name(vms->curbox);
7640
7641 /* Create the folder if it doesn't exist */
7642 imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
7643 if (vms->mailstream && !mail_status(vms->mailstream, mailbox, SA_UIDNEXT)) {
7644 if (mail_create(vms->mailstream, mailbox) != NIL) {
7645 ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
7646 }
7647 }
7648
7649 /* restore previous mbox stream */
7650 if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
7651 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
7652 res = -1;
7653 } else {
7654 if (move) {
7655 res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
7656 } else {
7657 res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
7658 }
7659 }
7660 ast_mutex_unlock(&vms->lock);
7661 return res;
7662#else
7663 char *dir = vms->curdir;
7664 char *username = vms->username;
7665 char *context = vmu->context;
7666 char sfn[PATH_MAX];
7667 char dfn[PATH_MAX];
7668 char ddir[PATH_MAX];
7669 const char *dbox = mbox(vmu, box);
7670 int x, i;
7671 SCOPE_ENTER(3, "dir: %s msg: %d box: %d dbox: %s move? %d \n", dir, msg, box, dbox, move);
7672
7673 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
7674 ast_trace(-1, "ddir: %s\n", ddir);
7675
7676 if (vm_lock_path(ddir)) {
7677 SCOPE_EXIT_RTN_VALUE(ERROR_LOCK_PATH, "Failed to lock path %s\n", ddir);
7678 }
7679
7680 x = LAST_MSG_INDEX(ddir) + 1;
7681
7682 if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
7683 ast_trace(-1, "Deleting message %d\n", msg);
7684 x--;
7685 for (i = 1; i <= x; i++) {
7686 /* Push files down a "slot". The oldest file (msg0000) will be deleted. */
7687 make_file(sfn, sizeof(sfn), ddir, i);
7688 make_file(dfn, sizeof(dfn), ddir, i - 1);
7689 if (EXISTS(ddir, i, sfn, NULL)) {
7690 SCOPE_CALL(-1, RENAME, ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
7691 } else
7692 break;
7693 }
7694 } else {
7695 if (x >= vmu->maxmsg) {
7696 ast_unlock_path(ddir);
7697 SCOPE_EXIT_RTN_VALUE(ERROR_MAX_MSGS, "Max messages reached\n");
7698 }
7699 }
7700 make_file(sfn, sizeof(sfn), dir, msg);
7701 make_file(dfn, sizeof(dfn), ddir, x);
7702 if (strcmp(sfn, dfn)) {
7703 ast_trace(-1, "Copying message '%s' to '%s'\n", sfn, dfn);
7704 SCOPE_CALL(-1, COPY, dir, msg, ddir, x, username, context, sfn, dfn);
7705 }
7706 ast_unlock_path(ddir);
7707
7708 if (newmsg) {
7709 *newmsg = x;
7710 }
7711 SCOPE_EXIT_RTN_VALUE(0, "Done\n");
7712#endif
7713}
7714
7715static int adsi_logo(unsigned char *buf)
7716{
7717 int bytes = 0;
7718 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
7719 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
7720 return bytes;
7721}
7722
7723static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
7724{
7725 unsigned char buf[256];
7726 int bytes = 0;
7727 int x;
7728 char num[5];
7729
7730 *useadsi = 0;
7731 bytes += ast_adsi_data_mode(buf + bytes);
7733
7734 bytes = 0;
7735 bytes += adsi_logo(buf);
7736 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
7737#ifdef DISPLAY
7738 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
7739#endif
7740 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7741 bytes += ast_adsi_data_mode(buf + bytes);
7743
7745 bytes = 0;
7746 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
7747 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
7748 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7749 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7751 return 0;
7752 }
7753
7754#ifdef DISPLAY
7755 /* Add a dot */
7756 bytes = 0;
7757 bytes += ast_adsi_logo(buf);
7758 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
7759 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
7760 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7762#endif
7763 bytes = 0;
7764 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
7765 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
7766 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advanced", "3", 1);
7767 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
7768 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
7769 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
7771
7772#ifdef DISPLAY
7773 /* Add another dot */
7774 bytes = 0;
7775 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
7776 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7777
7778 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7780#endif
7781
7782 bytes = 0;
7783 /* These buttons we load but don't use yet */
7784 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
7785 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
7786 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
7787 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
7788 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
7789 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
7791
7792#ifdef DISPLAY
7793 /* Add another dot */
7794 bytes = 0;
7795 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
7796 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7798#endif
7799
7800 bytes = 0;
7801 for (x = 0; x < 5; x++) {
7802 snprintf(num, sizeof(num), "%d", x);
7803 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
7804 }
7805 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
7807
7808#ifdef DISPLAY
7809 /* Add another dot */
7810 bytes = 0;
7811 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
7812 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7814#endif
7815
7816 if (ast_adsi_end_download(chan)) {
7817 bytes = 0;
7818 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
7819 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
7820 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7821 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7823 return 0;
7824 }
7825 bytes = 0;
7826 bytes += ast_adsi_download_disconnect(buf + bytes);
7827 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7829
7830 ast_debug(1, "Done downloading scripts...\n");
7831
7832#ifdef DISPLAY
7833 /* Add last dot */
7834 bytes = 0;
7835 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
7836 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7837#endif
7838 ast_debug(1, "Restarting session...\n");
7839
7840 bytes = 0;
7841 /* Load the session now */
7842 if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
7843 *useadsi = 1;
7844 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
7845 } else
7846 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
7847
7849 return 0;
7850}
7851
7852static void adsi_begin(struct ast_channel *chan, int *useadsi)
7853{
7854 int x;
7855 if (!ast_adsi_available(chan))
7856 return;
7857 x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
7858 if (x < 0) {
7859 *useadsi = 0;
7861 return;
7862 }
7863 if (!x) {
7864 if (adsi_load_vmail(chan, useadsi)) {
7865 ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
7866 return;
7867 }
7868 } else
7869 *useadsi = 1;
7870}
7871
7872static void adsi_login(struct ast_channel *chan)
7873{
7874 unsigned char buf[256];
7875 int bytes = 0;
7876 unsigned char keys[8];
7877 int x;
7878 if (!ast_adsi_available(chan))
7879 return;
7880
7881 for (x = 0; x < 8; x++)
7882 keys[x] = 0;
7883 /* Set one key for next */
7884 keys[3] = ADSI_KEY_APPS + 3;
7885
7886 bytes += adsi_logo(buf + bytes);
7887 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
7888 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
7889 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7890 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
7891 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
7892 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
7893 bytes += ast_adsi_set_keys(buf + bytes, keys);
7894 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7896}
7897
7898static void adsi_password(struct ast_channel *chan)
7899{
7900 unsigned char buf[256];
7901 int bytes = 0;
7902 unsigned char keys[8];
7903 int x;
7904 if (!ast_adsi_available(chan))
7905 return;
7906
7907 for (x = 0; x < 8; x++)
7908 keys[x] = 0;
7909 /* Set one key for next */
7910 keys[3] = ADSI_KEY_APPS + 3;
7911
7912 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7913 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
7914 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
7915 bytes += ast_adsi_set_keys(buf + bytes, keys);
7916 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7918}
7919
7920static void adsi_folders(struct ast_channel *chan, int start, char *label)
7921{
7922 unsigned char buf[256];
7923 int bytes = 0;
7924 unsigned char keys[8];
7925 int x, y;
7926
7927 if (!ast_adsi_available(chan))
7928 return;
7929
7930 for (x = 0; x < 5; x++) {
7931 y = ADSI_KEY_APPS + 12 + start + x;
7932 if (y > ADSI_KEY_APPS + 12 + 4)
7933 y = 0;
7934 keys[x] = ADSI_KEY_SKT | y;
7935 }
7936 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
7937 keys[6] = 0;
7938 keys[7] = 0;
7939
7940 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
7941 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
7942 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7943 bytes += ast_adsi_set_keys(buf + bytes, keys);
7944 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7945
7947}
7948
7949static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
7950{
7951 int bytes = 0;
7952 unsigned char buf[256];
7953 char buf1[256], buf2[256];
7954 char fn2[PATH_MAX];
7955
7956 char cid[256] = "";
7957 char *val;
7958 char *name, *num;
7959 char datetime[21] = "";
7960 FILE *f;
7961
7962 unsigned char keys[8];
7963
7964 int x;
7965
7966 if (!ast_adsi_available(chan))
7967 return;
7968
7969 /* Retrieve important info */
7970 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
7971 f = fopen(fn2, "r");
7972 if (f) {
7973 while (!feof(f)) {
7974 if (!fgets((char *) buf, sizeof(buf), f)) {
7975 continue;
7976 }
7977 if (!feof(f)) {
7978 char *stringp = NULL;
7979 stringp = (char *) buf;
7980 strsep(&stringp, "=");
7981 val = strsep(&stringp, "=");
7982 if (!ast_strlen_zero(val)) {
7983 if (!strcmp((char *) buf, "callerid"))
7984 ast_copy_string(cid, val, sizeof(cid));
7985 if (!strcmp((char *) buf, "origdate"))
7986 ast_copy_string(datetime, val, sizeof(datetime));
7987 }
7988 }
7989 }
7990 fclose(f);
7991 }
7992 /* New meaning for keys */
7993 for (x = 0; x < 5; x++)
7994 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
7995 keys[6] = 0x0;
7996 keys[7] = 0x0;
7997
7998 if (!vms->curmsg) {
7999 /* No prev key, provide "Folder" instead */
8000 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8001 }
8002 if (vms->curmsg >= vms->lastmsg) {
8003 /* If last message ... */
8004 if (vms->curmsg) {
8005 /* but not only message, provide "Folder" instead */
8006 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8007 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8008
8009 } else {
8010 /* Otherwise if only message, leave blank */
8011 keys[3] = 1;
8012 }
8013 }
8014
8015 if (!ast_strlen_zero(cid)) {
8016 ast_callerid_parse(cid, &name, &num);
8017 if (!name)
8018 name = num;
8019 } else {
8020 name = "Unknown Caller";
8021 }
8022
8023 /* If deleted, show "undeleted" */
8024#ifdef IMAP_STORAGE
8025 ast_mutex_lock(&vms->lock);
8026#endif
8027 if (vms->deleted[vms->curmsg]) {
8028 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
8029 }
8030#ifdef IMAP_STORAGE
8031 ast_mutex_unlock(&vms->lock);
8032#endif
8033
8034 /* Except "Exit" */
8035 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
8036 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
8037 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
8038 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
8039
8040 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8041 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8042 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
8043 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
8044 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8045 bytes += ast_adsi_set_keys(buf + bytes, keys);
8046 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8047
8049}
8050
8051static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
8052{
8053 int bytes = 0;
8054 unsigned char buf[256];
8055 unsigned char keys[8];
8056
8057 int x;
8058
8059 if (!ast_adsi_available(chan))
8060 return;
8061
8062 /* New meaning for keys */
8063 for (x = 0; x < 5; x++)
8064 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
8065
8066 keys[6] = 0x0;
8067 keys[7] = 0x0;
8068
8069 if (!vms->curmsg) {
8070 /* No prev key, provide "Folder" instead */
8071 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8072 }
8073 if (vms->curmsg >= vms->lastmsg) {
8074 /* If last message ... */
8075 if (vms->curmsg) {
8076 /* but not only message, provide "Folder" instead */
8077 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8078 } else {
8079 /* Otherwise if only message, leave blank */
8080 keys[3] = 1;
8081 }
8082 }
8083
8084 /* If deleted, show "undeleted" */
8085#ifdef IMAP_STORAGE
8086 ast_mutex_lock(&vms->lock);
8087#endif
8088 if (vms->deleted[vms->curmsg]) {
8089 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
8090 }
8091#ifdef IMAP_STORAGE
8092 ast_mutex_unlock(&vms->lock);
8093#endif
8094
8095 /* Except "Exit" */
8096 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
8097 bytes += ast_adsi_set_keys(buf + bytes, keys);
8098 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8099
8101}
8102
8103static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
8104{
8105 unsigned char buf[256] = "";
8106 char buf1[256] = "", buf2[256] = "";
8107 int bytes = 0;
8108 unsigned char keys[8];
8109 int x;
8110
8111 char *newm = (vms->newmessages == 1) ? "message" : "messages";
8112 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
8113 if (!ast_adsi_available(chan))
8114 return;
8115 if (vms->newmessages) {
8116 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
8117 if (vms->oldmessages) {
8118 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
8119 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
8120 } else {
8121 snprintf(buf2, sizeof(buf2), "%s.", newm);
8122 }
8123 } else if (vms->oldmessages) {
8124 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
8125 snprintf(buf2, sizeof(buf2), "%s.", oldm);
8126 } else {
8127 strcpy(buf1, "You have no messages.");
8128 buf2[0] = ' ';
8129 buf2[1] = '\0';
8130 }
8131 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8132 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8133 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8134
8135 for (x = 0; x < 6; x++)
8136 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
8137 keys[6] = 0;
8138 keys[7] = 0;
8139
8140 /* Don't let them listen if there are none */
8141 if (vms->lastmsg < 0)
8142 keys[0] = 1;
8143 bytes += ast_adsi_set_keys(buf + bytes, keys);
8144
8145 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8146
8148}
8149
8150static void adsi_status2(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 *mess = (vms->lastmsg == 0) ? "message" : "messages";
8159
8160 if (!ast_adsi_available(chan))
8161 return;
8162
8163 /* Original command keys */
8164 for (x = 0; x < 6; x++)
8165 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
8166
8167 keys[6] = 0;
8168 keys[7] = 0;
8169
8170 if ((vms->lastmsg + 1) < 1)
8171 keys[0] = 0;
8172
8173 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
8174 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
8175
8176 if (vms->lastmsg + 1)
8177 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
8178 else
8179 strcpy(buf2, "no messages.");
8180 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8181 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8182 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
8183 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8184 bytes += ast_adsi_set_keys(buf + bytes, keys);
8185
8186 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8187
8189
8190}
8191
8192/*
8193static void adsi_clear(struct ast_channel *chan)
8194{
8195 char buf[256];
8196 int bytes=0;
8197 if (!ast_adsi_available(chan))
8198 return;
8199 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8200 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8201
8202 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
8203}
8204*/
8205
8206static void adsi_goodbye(struct ast_channel *chan)
8207{
8208 unsigned char buf[256];
8209 int bytes = 0;
8210
8211 if (!ast_adsi_available(chan))
8212 return;
8213 bytes += adsi_logo(buf + bytes);
8214 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
8215 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
8216 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8217 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8218
8220}
8221
8222/*!\brief get_folder: Folder menu
8223 * Plays "press 1 for INBOX messages" etc.
8224 * Should possibly be internationalized
8225 */
8226static int get_folder(struct ast_channel *chan, int start)
8227{
8228 int x;
8229 int d;
8230 char fn[PATH_MAX];
8231 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
8232 if (d)
8233 return d;
8234 for (x = start; x < 5; x++) { /* For all folders */
8235 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), NULL)))
8236 return d;
8237 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
8238 if (d)
8239 return d;
8240 snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
8241
8242 /* The inbox folder can have its name changed under certain conditions
8243 * so this checks if the sound file exists for the inbox folder name and
8244 * if it doesn't, plays the default name instead. */
8245 if (x == 0) {
8246 if (ast_fileexists(fn, NULL, NULL)) {
8247 d = vm_play_folder_name(chan, fn);
8248 } else {
8249 ast_verb(4, "Failed to find file %s; falling back to INBOX\n", fn);
8250 d = vm_play_folder_name(chan, "vm-INBOX");
8251 }
8252 } else {
8253 ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
8254 d = vm_play_folder_name(chan, fn);
8255 }
8256
8257 if (d)
8258 return d;
8259 d = ast_waitfordigit(chan, 500);
8260 if (d)
8261 return d;
8262 }
8263
8264 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
8265 if (d)
8266 return d;
8267 d = ast_waitfordigit(chan, 4000);
8268 return d;
8269}
8270
8271/* Japanese Syntax */
8272static int get_folder_ja(struct ast_channel *chan, int start)
8273{
8274 int x;
8275 int d;
8276 char fn[256];
8277 for (x = start; x < 5; x++) { /* For all folders */
8278 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL))) {
8279 return d;
8280 }
8281 snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
8282 d = vm_play_folder_name(chan, fn);
8283 if (d) {
8284 return d;
8285 }
8286 d = ast_waitfordigit(chan, 500);
8287 if (d) {
8288 return d;
8289 }
8290 }
8291 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
8292 if (d) {
8293 return d;
8294 }
8295 d = ast_waitfordigit(chan, 4000);
8296 return d;
8297}
8298
8299/*!
8300 * \brief plays a prompt and waits for a keypress.
8301 * \param chan
8302 * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
8303 * \param start Does not appear to be used at this time.
8304 *
8305 * This is used by the main menu option to move a message to a folder or to save a message into a folder.
8306 * After playing the message identified by the fn parameter value, it calls get_folder(), which plays the
8307 * prompting for the number inputs that correspond to the available folders.
8308 *
8309 * \return zero on success, or -1 on error.
8310 */
8311static int get_folder2(struct ast_channel *chan, char *fn, int start)
8312{
8313 int res = 0;
8314 int loops = 0;
8315
8316 res = ast_play_and_wait(chan, fn); /* Folder name */
8317 while (((res < '0') || (res > '9')) &&
8318 (res != '#') && (res >= 0) &&
8319 loops < 4) {
8320 /* res = get_folder(chan, 0); */
8321 if (!strcasecmp(ast_channel_language(chan), "ja")) { /* Japanese syntax */
8322 res = get_folder_ja(chan, 0);
8323 } else { /* Default syntax */
8324 res = get_folder(chan, 0);
8325 }
8326 loops++;
8327 }
8328 if (loops == 4) { /* give up */
8329 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
8330 return '#';
8331 }
8332 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8333 isprint(res) ? res : '?', isprint(res) ? res : '?');
8334 return res;
8335}
8336
8337/*!
8338 * \brief presents the option to prepend to an existing message when forwarding it.
8339 * \param chan
8340 * \param vmu
8341 * \param curdir
8342 * \param curmsg
8343 * \param vm_fmts
8344 * \param context
8345 * \param record_gain
8346 * \param duration
8347 * \param vms
8348 * \param flag
8349 *
8350 * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
8351 *
8352 * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
8353 * \return zero on success, -1 on error.
8354 */
8355static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir,
8356 int curmsg, char *vm_fmts, char *context, signed char record_gain, long *duration,
8357 struct vm_state *vms, char *flag)
8358{
8359 int cmd = 0;
8360 int retries = 0, prepend_duration = 0, already_recorded = 0;
8361 char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
8362 char textfile[PATH_MAX];
8363 struct ast_config *msg_cfg;
8364 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8365#ifndef IMAP_STORAGE
8366 signed char zero_gain = 0;
8367#else
8368 const char *msg_id = NULL;
8369#endif
8370 const char *duration_str;
8371 SCOPE_ENTER(3, "mbox: %s msgnum: %d curdir: %s", vmu->mailbox, curmsg, curdir);
8372
8373 /* Must always populate duration correctly */
8374 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
8375 ast_trace(-1, "msgfile: %s\n", msgfile);
8376 strcpy(textfile, msgfile);
8377 strcpy(backup, msgfile);
8378 strcpy(backup_textfile, msgfile);
8379 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
8380 strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
8381 strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
8382
8383 if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
8384 *duration = atoi(duration_str);
8385 } else {
8386 *duration = 0;
8387 }
8388
8389 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
8390 if (cmd)
8391 retries = 0;
8392 switch (cmd) {
8393 case '1':
8394
8395#ifdef IMAP_STORAGE
8396 /* Record new intro file */
8397 if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
8398 msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
8399 }
8400 make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
8401 strncat(vms->introfn, "intro", sizeof(vms->introfn));
8402 ast_play_and_wait(chan, "vm-record-prepend");
8403 ast_play_and_wait(chan, "beep");
8404 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);
8405 if (cmd == -1) {
8406 break;
8407 }
8408 cmd = 't';
8409#else
8410
8411 /* prepend a message to the current message, update the metadata and return */
8412 ast_trace(-1, "Prepending to message %d\n", curmsg);
8413
8414 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
8415 ast_trace(-1, "msgfile: %s\n", msgfile);
8416
8417 strcpy(textfile, msgfile);
8418 strncat(textfile, ".txt", sizeof(textfile) - 1);
8419 *duration = 0;
8420
8421 /* if we can't read the message metadata, stop now */
8422 if (!valid_config(msg_cfg)) {
8423 cmd = 0;
8424 break;
8425 }
8426
8427 /* Back up the original file, so we can retry the prepend and restore it after forward. */
8428#ifndef IMAP_STORAGE
8429 if (already_recorded) {
8430 ast_trace(-1, "Restoring '%s' to '%s'\n", backup, msgfile);
8431 ast_filecopy(backup, msgfile, NULL);
8432 copy(backup_textfile, textfile);
8433 }
8434 else {
8435 ast_trace(-1, "Backing up '%s' to '%s'\n", backup, msgfile);
8436 ast_filecopy(msgfile, backup, NULL);
8437 copy(textfile, backup_textfile);
8438 }
8439#endif
8440 already_recorded = 1;
8441
8442 if (record_gain)
8443 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
8444
8445 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, ast_play_and_prepend, chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
8446
8447 if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
8448 ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
8450 ast_filerename(backup, msgfile, NULL);
8451 }
8452
8453 if (record_gain)
8454 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
8455
8456
8457 if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
8458 *duration = atoi(duration_str);
8459
8460 if (prepend_duration) {
8461 struct ast_category *msg_cat;
8462 /* need enough space for a maximum-length message duration */
8463 char duration_buf[12];
8464
8465 *duration += prepend_duration;
8466 ast_trace(-1, "Prepending duration: %d total duration: %ld\n", prepend_duration, *duration);
8467 msg_cat = ast_category_get(msg_cfg, "message", NULL);
8468 snprintf(duration_buf, sizeof(duration_buf), "%ld", *duration);
8469 if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
8470 ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
8471 }
8472 }
8473
8474#endif
8475 break;
8476 case '2':
8477 /* NULL out introfile so we know there is no intro! */
8478#ifdef IMAP_STORAGE
8479 *vms->introfn = '\0';
8480#endif
8481 cmd = 't';
8482 break;
8483 case '*':
8484 cmd = '*';
8485 break;
8486 default:
8487 /* If time_out and return to menu, reset already_recorded */
8488 already_recorded = 0;
8489
8490 cmd = ast_play_and_wait(chan, "vm-forwardoptions");
8491 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
8492 if (!cmd) {
8493 cmd = ast_play_and_wait(chan, "vm-starmain");
8494 /* "press star to return to the main menu" */
8495 }
8496 if (!cmd) {
8497 cmd = ast_waitfordigit(chan, 6000);
8498 }
8499 if (!cmd) {
8500 retries++;
8501 }
8502 if (retries > 3) {
8503 cmd = '*'; /* Let's cancel this beast */
8504 }
8505 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8506 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
8507 }
8508 }
8509
8510 if (valid_config(msg_cfg))
8511 ast_config_destroy(msg_cfg);
8512 if (prepend_duration)
8513 *duration = prepend_duration;
8514
8515 if (already_recorded && cmd == -1) {
8516 /* restore original message if prepention cancelled */
8517 ast_trace(-1, "Restoring '%s' to '%s'\n", backup, msgfile);
8518 ast_filerename(backup, msgfile, NULL);
8519 rename(backup_textfile, textfile);
8520 }
8521
8522 if (cmd == 't' || cmd == 'S') { /* XXX entering this block with a value of 'S' is probably no longer possible. */
8523 cmd = 0;
8524 }
8525 SCOPE_EXIT_RTN_VALUE(cmd, "Done. CMD: %d %c\n", cmd, cmd >= 32 && cmd < 127 ? cmd : '?');
8526}
8527
8528static void queue_mwi_event(const char *channel_id, const char *box, int urgent, int new, int old)
8529{
8530 char *mailbox;
8531 char *context;
8532
8534 return;
8535 }
8536
8537 ast_debug(3, "Queueing event for mailbox %s New: %d Old: %d\n", box, new + urgent, old);
8538 ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
8539
8541 struct ao2_iterator *aliases;
8542 struct mailbox_alias_mapping *mapping;
8543
8545 while ((mapping = ao2_iterator_next(aliases))) {
8546 char alias[strlen(mapping->alias) + 1];
8547 strcpy(alias, mapping->alias); /* safe */
8548 mailbox = NULL;
8549 context = NULL;
8550 ast_debug(3, "Found alias mapping: %s -> %s\n", mapping->alias, box);
8552 ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
8553 ao2_ref(mapping, -1);
8554 }
8556 }
8557}
8558
8559/*!
8560 * \brief Sends email notification that a user has a new voicemail waiting for them.
8561 * \param chan
8562 * \param vmu
8563 * \param vms
8564 * \param msgnum
8565 * \param duration
8566 * \param fmt
8567 * \param cidnum The Caller ID phone number value.
8568 * \param cidname The Caller ID name value.
8569 * \param flag
8570 *
8571 * \return zero on success, -1 on error.
8572 */
8573static 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)
8574{
8575 char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
8576 int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
8577 const char *category;
8578 char *myserveremail = serveremail;
8579
8580 ast_channel_lock(chan);
8581 if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
8582 category = ast_strdupa(category);
8583 }
8584 ast_channel_unlock(chan);
8585
8586#ifndef IMAP_STORAGE
8587 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
8588#else
8589 snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
8590#endif
8591 make_file(fn, sizeof(fn), todir, msgnum);
8592 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
8593
8594 if (!ast_strlen_zero(vmu->attachfmt)) {
8595 if (strstr(fmt, vmu->attachfmt))
8596 fmt = vmu->attachfmt;
8597 else
8598 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);
8599 }
8600
8601 /* Attach only the first format */
8602 fmt = ast_strdupa(fmt);
8603 stringp = fmt;
8604 strsep(&stringp, "|");
8605
8606 if (!ast_strlen_zero(vmu->serveremail))
8607 myserveremail = vmu->serveremail;
8608
8609 if (!ast_strlen_zero(vmu->email)) {
8610 int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
8611 char *msg_id = NULL;
8612#ifdef IMAP_STORAGE
8613 struct ast_config *msg_cfg;
8614 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8615 char filename[PATH_MAX];
8616
8617 snprintf(filename, sizeof(filename), "%s.txt", fn);
8618 msg_cfg = ast_config_load(filename, config_flags);
8619 if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
8620 msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
8621 ast_config_destroy(msg_cfg);
8622 }
8623#endif
8624
8625 if (attach_user_voicemail)
8626 RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
8627
8628 /* XXX possible imap issue, should category be NULL XXX */
8629 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);
8630
8631 if (attach_user_voicemail)
8632 DISPOSE(todir, msgnum);
8633 }
8634
8635 if (!ast_strlen_zero(vmu->pager)) {
8636 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
8637 }
8638
8639 if (ast_test_flag(vmu, VM_DELETE))
8640 DELETE(todir, msgnum, fn, vmu);
8641
8642 /* Leave voicemail for someone */
8643 if (ast_app_has_voicemail(ext_context, NULL))
8644 ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
8645
8646 queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgentmsgs, newmsgs, oldmsgs);
8647 run_externnotify(vmu->context, vmu->mailbox, flag);
8648
8649#ifdef IMAP_STORAGE
8650 vm_delete(fn); /* Delete the file, but not the IMAP message */
8651 if (ast_test_flag(vmu, VM_DELETE)) { /* Delete the IMAP message if delete = yes */
8652 vm_imap_delete(NULL, vms->curmsg, vmu);
8653 vms->newmessages--; /* Fix new message count */
8654 }
8655#endif
8656
8657 return 0;
8658}
8659
8660/*!
8661 * \brief Sends a voicemail message to a mailbox recipient.
8662 * \param chan
8663 * \param context
8664 * \param vms
8665 * \param sender
8666 * \param fmt
8667 * \param is_new_message Used to indicate the mode for which this method was invoked.
8668 * Will be 0 when called to forward an existing message (option 8)
8669 * Will be 1 when called to leave a message (option 3->5)
8670 * \param record_gain
8671 * \param urgent
8672 *
8673 * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
8674 *
8675 * When in the leave message mode (is_new_message == 1):
8676 * - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
8677 * - attempt to determine the context and mailbox, and then invoke leave_message() function to record and store the message.
8678 *
8679 * When in the forward message mode (is_new_message == 0):
8680 * - retrieves the current message to be forwarded
8681 * - copies the original message to a temporary file, so updates to the envelope can be done.
8682 * - determines the target mailbox and folders
8683 * - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
8684 *
8685 * \return zero on success, -1 on error.
8686 */
8687static 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)
8688{
8689#ifdef IMAP_STORAGE
8690 int todircount = 0;
8691 struct vm_state *dstvms;
8692#endif
8693 char username[70]="";
8694 char fn[PATH_MAX]; /* for playback of name greeting */
8695 char ecodes[16] = "#";
8696 int res = 0, cmd = 0;
8697 struct ast_vm_user *receiver = NULL, *vmtmp;
8699 char *stringp;
8700 const char *s;
8701 const char mailbox_context[256];
8702 int saved_messages = 0;
8703 int valid_extensions = 0;
8704 char *dir;
8705 int curmsg;
8706 char urgent_str[7] = "";
8707 int prompt_played = 0;
8708#ifndef IMAP_STORAGE
8709 char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
8710#endif
8711 SCOPE_ENTER(3, "%s: user: %s dir: %s msg: %d\n", ast_channel_name(chan),
8712 vms->username, vms->curdir, vms->curmsg);
8713
8715 ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
8716 }
8717
8718 if (vms == NULL) {
8719 SCOPE_EXIT_RTN_VALUE(-1, "vms is NULL\n");
8720 }
8721 dir = vms->curdir;
8722 curmsg = vms->curmsg;
8723
8724 ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
8725 while (!res && !valid_extensions) {
8726 int use_directory = 0;
8728 int done = 0;
8729 int retries = 0;
8730 cmd = 0;
8731 while ((cmd >= 0) && !done ){
8732 if (cmd)
8733 retries = 0;
8734 switch (cmd) {
8735 case '1':
8736 use_directory = 0;
8737 done = 1;
8738 break;
8739 case '2':
8740 use_directory = 1;
8741 done = 1;
8742 break;
8743 case '*':
8744 cmd = 't';
8745 done = 1;
8746 break;
8747 default:
8748 /* Press 1 to enter an extension press 2 to use the directory */
8749 cmd = ast_play_and_wait(chan, "vm-forward");
8750 if (!cmd) {
8751 cmd = ast_waitfordigit(chan, 3000);
8752 }
8753 if (!cmd) {
8754 retries++;
8755 }
8756 if (retries > 3) {
8757 cmd = 't';
8758 done = 1;
8759 }
8760 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8761 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
8762 }
8763 }
8764 if (cmd < 0 || cmd == 't')
8765 break;
8766 }
8767
8768 if (use_directory) {
8769 /* use app_directory */
8770
8771 struct ast_app* directory_app;
8772
8773 directory_app = pbx_findapp("Directory");
8774 if (directory_app) {
8775 char vmcontext[256];
8776 char old_context[strlen(ast_channel_context(chan)) + 1];
8777 char old_exten[strlen(ast_channel_exten(chan)) + 1];
8778 int old_priority;
8779 /* make backup copies */
8780 strcpy(old_context, ast_channel_context(chan)); /* safe */
8781 strcpy(old_exten, ast_channel_exten(chan)); /* safe */
8782 old_priority = ast_channel_priority(chan);
8783
8784 /* call the Directory, changes the channel */
8785 snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
8786 res = pbx_exec(chan, directory_app, vmcontext);
8787
8788 ast_copy_string(username, ast_channel_exten(chan), sizeof(username));
8789
8790 /* restore the old context, exten, and priority */
8791 ast_channel_context_set(chan, old_context);
8792 ast_channel_exten_set(chan, old_exten);
8793 ast_channel_priority_set(chan, old_priority);
8794 } else {
8795 ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
8797 }
8798 } else {
8799 /* Ask for an extension */
8800 res = ast_streamfile(chan, "vm-extension", ast_channel_language(chan)); /* "extension" */
8801 prompt_played++;
8802 if (res || prompt_played > 4)
8803 break;
8804 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#")) < 0)
8805 break;
8806 }
8807
8808 /* start all over if no username */
8809 if (ast_strlen_zero(username))
8810 continue;
8811 stringp = username;
8812 s = strsep(&stringp, "*");
8813 /* start optimistic */
8814 valid_extensions = 1;
8815 while (s) {
8816 snprintf((char*)mailbox_context, sizeof(mailbox_context), "%s@%s", s, context ? context : "default");
8817 if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
8818 int oldmsgs;
8819 int newmsgs;
8820 int capacity;
8821
8822 if (inboxcount(mailbox_context, &newmsgs, &oldmsgs)) {
8823 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", mailbox_context);
8824 /* Shouldn't happen, but allow trying another extension if it does */
8825 res = ast_play_and_wait(chan, "pbx-invalid");
8826 valid_extensions = 0;
8827 break;
8828 }
8829#ifdef IMAP_STORAGE
8830 if (!(dstvms = get_vm_state_by_mailbox(s, context, 0))) {
8831 if (!(dstvms = create_vm_state_from_user(receiver))) {
8832 ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
8833 /* Shouldn't happen, but allow trying another extension if it does */
8834 res = ast_play_and_wait(chan, "pbx-invalid");
8835 valid_extensions = 0;
8836 break;
8837 }
8838 }
8839 check_quota(dstvms, imapfolder);
8840 if (dstvms->quota_limit && dstvms->quota_usage >= dstvms->quota_limit) {
8841 ast_log(LOG_NOTICE, "Mailbox '%s' is exceeded quota %u >= %u\n", mailbox_context, dstvms->quota_usage, dstvms->quota_limit);
8842 res = ast_play_and_wait(chan, "vm-mailboxfull");
8843 valid_extensions = 0;
8844 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8845 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8846 free_user(vmtmp);
8847 }
8848 break;
8849 }
8850#endif
8851 capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
8852 if ((newmsgs + oldmsgs) >= capacity) {
8853 ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", mailbox_context, capacity);
8854 res = ast_play_and_wait(chan, "vm-mailboxfull");
8855 valid_extensions = 0;
8856 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8857 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8858 free_user(vmtmp);
8859 }
8860 inprocess_count(receiver->mailbox, receiver->context, -1);
8861 break;
8862 }
8863 AST_LIST_INSERT_HEAD(&extensions, receiver, list);
8864 } else {
8865 /* XXX Optimization for the future. When we encounter a single bad extension,
8866 * bailing out on all of the extensions may not be the way to go. We should
8867 * probably just bail on that single extension, then allow the user to enter
8868 * several more. XXX
8869 */
8870 while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8871 free_user(receiver);
8872 }
8873 ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", mailbox_context);
8874 /* "I am sorry, that's not a valid extension. Please try again." */
8875 res = ast_play_and_wait(chan, "pbx-invalid");
8876 valid_extensions = 0;
8877 break;
8878 }
8879
8880 /* play name if available, else play extension number */
8881 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
8882 SCOPE_CALL(-1, RETRIEVE, fn, -1, s, receiver->context);
8883 if (ast_fileexists(fn, NULL, NULL) > 0) {
8884 res = ast_stream_and_wait(chan, fn, ecodes);
8885 if (res) {
8886 SCOPE_CALL(-1, DISPOSE, fn, -1);
8887 return res;
8888 }
8889 } else {
8890 res = ast_say_digit_str(chan, s, ecodes, ast_channel_language(chan));
8891 }
8892 SCOPE_CALL(-1, DISPOSE, fn, -1);
8893
8894 s = strsep(&stringp, "*");
8895 }
8896 /* break from the loop of reading the extensions */
8897 if (valid_extensions)
8898 break;
8899 }
8900 /* check if we're clear to proceed */
8901 if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
8902 return res;
8903 if (is_new_message == 1) {
8904 struct leave_vm_options leave_options;
8905 char mailbox[AST_MAX_EXTENSION * 2 + 2];
8906 snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
8907
8908 /* Send VoiceMail */
8909 memset(&leave_options, 0, sizeof(leave_options));
8910 leave_options.record_gain = record_gain;
8911 leave_options.beeptone = "beep";
8912 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, leave_voicemail, chan, mailbox, &leave_options);
8913 } else {
8914 /* Forward VoiceMail */
8915 long duration = 0;
8916 struct vm_state vmstmp;
8917 int copy_msg_result = 0;
8918#ifdef IMAP_STORAGE
8919 char filename[PATH_MAX];
8920 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8921 const char *msg_id = NULL;
8922 struct ast_config *msg_cfg;
8923#endif
8924 memcpy(&vmstmp, vms, sizeof(vmstmp));
8925
8926 SCOPE_CALL(-1, RETRIEVE, dir, curmsg, sender->mailbox, sender->context);
8927#ifdef IMAP_STORAGE
8928 make_file(filename, sizeof(filename), dir, curmsg);
8929 strncat(filename, ".txt", sizeof(filename) - strlen(filename) - 1);
8930 msg_cfg = ast_config_load(filename, config_flags);
8931 if (msg_cfg && msg_cfg == CONFIG_STATUS_FILEINVALID) {
8932 msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
8933 ast_config_destroy(msg_cfg);
8934 }
8935#endif
8936
8937 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);
8938 if (!cmd) {
8939 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
8940#ifdef IMAP_STORAGE
8941 int attach_user_voicemail;
8942 char *myserveremail = serveremail;
8943
8944 /* get destination mailbox */
8945 dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
8946 if (!dstvms) {
8947 dstvms = create_vm_state_from_user(vmtmp);
8948 }
8949 if (dstvms) {
8950 init_mailstream(dstvms, 0);
8951 if (!dstvms->mailstream) {
8952 ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
8953 } else {
8954 copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
8955 run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str);
8956 }
8957 } else {
8958 ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
8959 }
8960 if (!ast_strlen_zero(vmtmp->serveremail))
8961 myserveremail = vmtmp->serveremail;
8962 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
8963 /* NULL category for IMAP storage */
8964 sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
8965 dstvms->curbox,
8966 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
8967 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
8968 vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
8969 NULL, urgent_str, msg_id);
8970#else
8971 copy_msg_result = SCOPE_CALL_WITH_INT_RESULT(-1, copy_message, chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str, NULL);
8972#endif
8973 saved_messages++;
8975 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8976 free_user(vmtmp);
8977 if (res)
8978 break;
8979 }
8981 if (saved_messages > 0 && !copy_msg_result) {
8982 /* give confirmation that the message was saved */
8983 /* commented out since we can't forward batches yet
8984 if (saved_messages == 1)
8985 res = ast_play_and_wait(chan, "vm-message");
8986 else
8987 res = ast_play_and_wait(chan, "vm-messages");
8988 if (!res)
8989 res = ast_play_and_wait(chan, "vm-saved"); */
8990 res = ast_play_and_wait(chan, "vm-msgforwarded");
8991 }
8992#ifndef IMAP_STORAGE
8993 else {
8994 /* with IMAP, mailbox full warning played by imap_check_limits */
8995 res = ast_play_and_wait(chan, "vm-mailboxfull");
8996 }
8997 /* Restore original message without prepended message if backup exists */
8998 make_file(msgfile, sizeof(msgfile), dir, curmsg);
8999 strcpy(textfile, msgfile);
9000 strcpy(backup, msgfile);
9001 strcpy(backup_textfile, msgfile);
9002 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
9003 strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
9004 strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
9005 if (ast_fileexists(backup, NULL, NULL) > 0) {
9006 ast_filerename(backup, msgfile, NULL);
9007 rename(backup_textfile, textfile);
9008 }
9009#endif
9010 }
9011 SCOPE_CALL(-1, DISPOSE, dir, curmsg);
9012#ifndef IMAP_STORAGE
9013 if (cmd) { /* assuming hangup, cleanup backup file */
9014 make_file(msgfile, sizeof(msgfile), dir, curmsg);
9015 strcpy(textfile, msgfile);
9016 strcpy(backup_textfile, msgfile);
9017 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
9018 strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
9019 rename(backup_textfile, textfile);
9020 }
9021#endif
9022 }
9023
9024 /* If anything failed above, we still have this list to free */
9025 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
9026 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
9027 free_user(vmtmp);
9028 }
9029 SCOPE_EXIT_RTN_VALUE(res ? res : cmd, "Done. res: %d cmd: %d\n", res, cmd);
9030}
9031
9032static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
9033{
9034 int res;
9035 if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
9036 ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file);
9037 return res;
9038}
9039
9040static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
9041{
9042 ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
9044}
9045
9046static int play_message_category(struct ast_channel *chan, const char *category)
9047{
9048 int res = 0;
9049
9050 if (!ast_strlen_zero(category))
9051 res = ast_play_and_wait(chan, category);
9052
9053 if (res) {
9054 ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
9055 res = 0;
9056 }
9057
9058 return res;
9059}
9060
9061static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
9062{
9063 int res = 0;
9064 struct vm_zone *the_zone = NULL;
9065 time_t t;
9066
9067 if (ast_get_time_t(origtime, &t, 0, NULL)) {
9068 ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
9069 return 0;
9070 }
9071
9072 /* Does this user have a timezone specified? */
9073 if (!ast_strlen_zero(vmu->zonetag)) {
9074 /* Find the zone in the list */
9075 struct vm_zone *z;
9078 if (!strcmp(z->name, vmu->zonetag)) {
9079 the_zone = z;
9080 break;
9081 }
9082 }
9084 }
9085
9086/* No internal variable parsing for now, so we'll comment it out for the time being */
9087#if 0
9088 /* Set the DIFF_* variables */
9089 ast_localtime(&t, &time_now, NULL);
9090 tv_now = ast_tvnow();
9091 ast_localtime(&tv_now, &time_then, NULL);
9092
9093 /* Day difference */
9094 if (time_now.tm_year == time_then.tm_year)
9095 snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
9096 else
9097 snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
9098 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
9099
9100 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
9101#endif
9102 if (the_zone) {
9103 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), the_zone->msg_format, the_zone->timezone);
9104 } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
9105 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9106 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
9107 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q H 'digits/kai' M ", NULL);
9108 } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */
9109 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9110 } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
9111 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);
9112 } else if (!strcasecmp(ast_channel_language(chan),"ja")) { /* Japanese syntax */
9113 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "PHM q 'jp-ni' 'vm-received'", NULL);
9114 } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
9115 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/nl-om' HM", NULL);
9116 } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
9117 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9118 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
9119 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q HM", NULL);
9120 } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* Brazilian PORTUGUESE syntax */
9121 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);
9122 } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
9123 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' dB 'digits/at' k 'and' M", NULL);
9124 } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
9125 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "qR 'vm-received'", NULL);
9126 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
9127 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);
9128 } else {
9129 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' IMp", NULL);
9130 }
9131#if 0
9132 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
9133#endif
9134 return res;
9135}
9136
9137
9138
9139static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback, int saycidnumber)
9140{
9141 int res = 0;
9142 int i;
9143 char *callerid, *name;
9144 char prefile[PATH_MAX] = "";
9145
9146 /* If voicemail cid is not enabled, or we didn't get cid or context from
9147 * the attribute file, leave now.
9148 *
9149 * TODO Still need to change this so that if this function is called by the
9150 * message envelope (and someone is explicitly requesting to hear the CID),
9151 * it does not check to see if CID is enabled in the config file.
9152 */
9153 if ((cid == NULL)||(context == NULL))
9154 return res;
9155
9156 /* Strip off caller ID number from name */
9157 ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
9158 ast_callerid_parse(cid, &name, &callerid);
9159 if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
9160 /* Check for internal contexts and only */
9161 /* say extension when the call didn't come from an internal context in the list */
9162 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
9163 ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
9164 if ((strcmp(cidinternalcontexts[i], context) == 0))
9165 break;
9166 }
9167 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
9168 if (!res) {
9169 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
9170 if (!ast_strlen_zero(prefile)) {
9171 /* See if we can find a recorded name for this callerid
9172 * and if found, use that instead of saying number. */
9173 if (ast_fileexists(prefile, NULL, NULL) > 0) {
9174 ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
9175 if (!callback)
9176 res = wait_file2(chan, vms, "vm-from");
9177 res = ast_stream_and_wait(chan, prefile, "");
9178 } else {
9179 ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
9180 /* Say "from extension" as one saying to sound smoother */
9181 if (!callback)
9182 res = wait_file2(chan, vms, "vm-from-extension");
9183 res = ast_say_digit_str(chan, callerid, "", ast_channel_language(chan));
9184 }
9185 }
9186 }
9187 } else if (!res) {
9188 ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
9189 /* If there is a recording for this numeric callerid then play that */
9190 if (!callback) {
9191 /* See if we can find a recorded name for this person instead of their extension number */
9192 snprintf(prefile, sizeof(prefile), "%s/recordings/callerids/%s", ast_config_AST_SPOOL_DIR, callerid);
9193 if (!saycidnumber && ast_fileexists(prefile, NULL, NULL) > 0) {
9194 ast_verb(3, "Playing recorded name for CID number '%s' - '%s'\n", callerid,prefile);
9195 wait_file2(chan, vms, "vm-from");
9196 res = ast_stream_and_wait(chan, prefile, "");
9197 ast_verb(3, "Played recorded name result '%d'\n", res);
9198 } else {
9199 /* Since this is all nicely figured out, why not say "from phone number" in this case" */
9200 wait_file2(chan, vms, "vm-from-phonenumber");
9201 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
9202 }
9203 } else {
9204 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
9205 }
9206 }
9207 } else {
9208 /* Number unknown */
9209 ast_debug(1, "VM-CID: From an unknown number\n");
9210 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
9211 res = wait_file2(chan, vms, "vm-unknown-caller");
9212 }
9213 return res;
9214}
9215
9216static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
9217{
9218 int res = 0;
9219 int durationm;
9220 int durations;
9221 /* Verify that we have a duration for the message */
9222 if (duration == NULL)
9223 return res;
9224
9225 /* Convert from seconds to minutes */
9226 durations = atoi(duration);
9227 durationm = (durations / 60);
9228
9229 ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
9230
9231 if ((!res) && (durationm >= minduration)) {
9232 res = wait_file2(chan, vms, "vm-duration");
9233
9234 /* POLISH syntax */
9235 if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9236 div_t num = div(durationm, 10);
9237
9238 if (durationm == 1) {
9239 res = ast_play_and_wait(chan, "digits/1z");
9240 res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
9241 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
9242 if (num.rem == 2) {
9243 if (!num.quot) {
9244 res = ast_play_and_wait(chan, "digits/2-ie");
9245 } else {
9246 res = say_and_wait(chan, durationm - 2 , ast_channel_language(chan));
9247 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
9248 }
9249 } else {
9250 res = say_and_wait(chan, durationm, ast_channel_language(chan));
9251 }
9252 res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
9253 } else {
9254 res = say_and_wait(chan, durationm, ast_channel_language(chan));
9255 res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
9256 }
9257 /* DEFAULT syntax */
9258 } else {
9259 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
9260 res = wait_file2(chan, vms, "vm-minutes");
9261 }
9262 }
9263 return res;
9264}
9265
9266static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
9267{
9268 int res = 0;
9269 char filename[PATH_MAX], *cid;
9270 const char *origtime, *context, *category, *duration, *flag;
9271 struct ast_config *msg_cfg;
9272 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
9273 SCOPE_ENTER(3, "%s: user: %s dir: %s msg: %d\n", ast_channel_name(chan),
9274 vms->username, vms->curdir, vms->curmsg);
9275
9276 vms->starting = 0;
9277 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
9278 adsi_message(chan, vms);
9279 if (!vms->curmsg) {
9280 res = wait_file2(chan, vms, "vm-first"); /* "First" */
9281 } else if (vms->curmsg == vms->lastmsg) {
9282 res = wait_file2(chan, vms, "vm-last"); /* "last" */
9283 }
9284
9285 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
9286 SCOPE_CALL(-1, RETRIEVE, vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
9287 msg_cfg = ast_config_load(filename, config_flags);
9288 if (!valid_config(msg_cfg)) {
9289 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
9290 return 0;
9291 }
9292 flag = ast_variable_retrieve(msg_cfg, "message", "flag");
9293
9294 /* Play the word urgent if we are listening to urgent messages */
9295 if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
9296 res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
9297 }
9298
9299 if (!res) {
9300 /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
9301 /* POLISH syntax */
9302 if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9303 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9304 int ten, one;
9305 char nextmsg[256];
9306 ten = (vms->curmsg + 1) / 10;
9307 one = (vms->curmsg + 1) % 10;
9308
9309 if (vms->curmsg < 20) {
9310 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
9311 res = wait_file2(chan, vms, nextmsg);
9312 } else {
9313 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
9314 res = wait_file2(chan, vms, nextmsg);
9315 if (one > 0) {
9316 if (!res) {
9317 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
9318 res = wait_file2(chan, vms, nextmsg);
9319 }
9320 }
9321 }
9322 }
9323 if (!res)
9324 res = wait_file2(chan, vms, "vm-message");
9325 /* HEBREW syntax */
9326 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
9327 if (!vms->curmsg) {
9328 res = wait_file2(chan, vms, "vm-message");
9329 res = wait_file2(chan, vms, "vm-first");
9330 } else if (vms->curmsg == vms->lastmsg) {
9331 res = wait_file2(chan, vms, "vm-message");
9332 res = wait_file2(chan, vms, "vm-last");
9333 } else {
9334 res = wait_file2(chan, vms, "vm-message");
9335 res = wait_file2(chan, vms, "vm-number");
9336 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9337 }
9338 /* ICELANDIC syntax */
9339 } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) {
9340 res = wait_file2(chan, vms, "vm-message");
9341 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9342 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "n");
9343 }
9344 /* VIETNAMESE syntax */
9345 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
9346 if (!vms->curmsg) {
9347 res = wait_file2(chan, vms, "vm-message");
9348 res = wait_file2(chan, vms, "vm-first");
9349 } else if (vms->curmsg == vms->lastmsg) {
9350 res = wait_file2(chan, vms, "vm-message");
9351 res = wait_file2(chan, vms, "vm-last");
9352 } else {
9353 res = wait_file2(chan, vms, "vm-message");
9354 res = wait_file2(chan, vms, "vm-number");
9355 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9356 }
9357 } else {
9358 if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
9359 res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
9360 } else { /* DEFAULT syntax */
9361 res = wait_file2(chan, vms, "vm-message");
9362 }
9363 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9364 if (!res) {
9365 ast_test_suite_event_notify("PLAYBACK", "Message: message number");
9366 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
9367 }
9368 }
9369 }
9370 }
9371
9372 if (!valid_config(msg_cfg)) {
9373 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
9374 }
9375
9376 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
9377 SCOPE_CALL(-1, DISPOSE, vms->curdir, vms->curmsg);
9378 ast_config_destroy(msg_cfg);
9379 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "No origtime?!\n");
9380 }
9381
9382 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
9383 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
9384 category = ast_variable_retrieve(msg_cfg, "message", "category");
9385
9386 context = ast_variable_retrieve(msg_cfg, "message", "context");
9387 if (!res) {
9388 res = play_message_category(chan, category);
9389 }
9390 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
9391 res = play_message_datetime(chan, vmu, origtime, filename);
9392 }
9393 if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
9394 res = play_message_callerid(chan, vms, cid, context, 0, 0);
9395 }
9396 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
9397 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
9398 }
9399 /* Allow pressing '1' to skip envelope / callerid */
9400 if (res == '1') {
9401 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
9402 res = 0;
9403 }
9404 ast_config_destroy(msg_cfg);
9405
9406 if (!res) {
9407 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
9408#ifdef IMAP_STORAGE
9409 ast_mutex_lock(&vms->lock);
9410#endif
9411 vms->heard[vms->curmsg] = 1;
9412#ifdef IMAP_STORAGE
9413 ast_mutex_unlock(&vms->lock);
9414 /*IMAP storage stores any prepended message from a forward
9415 * as a separate file from the rest of the message
9416 */
9417 if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
9418 wait_file(chan, vms, vms->introfn);
9419 }
9420#endif
9421 if ((res = wait_file(chan, vms, vms->fn)) < 0) {
9422 ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
9423 res = 0;
9424 }
9425 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
9426 isprint(res) ? res : '?', isprint(res) ? res : '?');
9427 }
9428 SCOPE_CALL(-1, DISPOSE, vms->curdir, vms->curmsg);
9429 SCOPE_EXIT_RTN_VALUE(res, "Done: RC: %d\n", res);
9430}
9431
9432#ifdef IMAP_STORAGE
9433static int imap_remove_file(char *dir, int msgnum)
9434{
9435 char fn[PATH_MAX];
9436 char full_fn[PATH_MAX];
9437 char intro[PATH_MAX] = {0,};
9438
9439 if (msgnum > -1) {
9440 make_file(fn, sizeof(fn), dir, msgnum);
9441 snprintf(intro, sizeof(intro), "%sintro", fn);
9442 } else
9443 ast_copy_string(fn, dir, sizeof(fn));
9444
9445 if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
9446 ast_filedelete(fn, NULL);
9447 if (!ast_strlen_zero(intro)) {
9449 }
9450 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
9451 unlink(full_fn);
9452 }
9453 return 0;
9454}
9455
9456
9457
9458static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
9459{
9460 char *file, *filename;
9461 char arg[11];
9462 int i;
9463 BODY* body;
9464 int curr_mbox;
9465
9466 file = strrchr(ast_strdupa(dir), '/');
9467 if (file) {
9468 *file++ = '\0';
9469 } else {
9470 ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
9471 return -1;
9472 }
9473
9474 ast_mutex_lock(&vms->lock);
9475
9476 /* get the current mailbox so that we can point the mailstream back to it later */
9477 curr_mbox = get_folder_by_name(vms->curbox);
9478
9479 if (init_mailstream(vms, GREETINGS_FOLDER) || !vms->mailstream) {
9480 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
9481 ast_mutex_unlock(&vms->lock);
9482 return -1;
9483 }
9484
9485 for (i = 0; i < vms->mailstream->nmsgs; i++) {
9486 mail_fetchstructure(vms->mailstream, i + 1, &body);
9487 /* We have the body, now we extract the file name of the first attachment. */
9488 if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
9489 char *attachment = body->nested.part->next->body.parameter->value;
9490 char copy[strlen(attachment) + 1];
9491
9492 strcpy(copy, attachment); /* safe */
9493 attachment = copy;
9494
9495 filename = strsep(&attachment, ".");
9496 if (!strcmp(filename, file)) {
9497 snprintf(arg, sizeof(arg), "%d", i + 1);
9498 mail_setflag(vms->mailstream, arg, "\\DELETED");
9499 }
9500 } else {
9501 ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
9502 ast_mutex_unlock(&vms->lock);
9503 return -1;
9504 }
9505 }
9506 mail_expunge(vms->mailstream);
9507
9508 if (curr_mbox != -1) {
9509 /* restore previous mbox stream */
9510 if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
9511 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
9512 }
9513 }
9514
9515 ast_mutex_unlock(&vms->lock);
9516 return 0;
9517}
9518
9519#elif !defined(IMAP_STORAGE)
9520static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
9521{
9522 int count_msg, last_msg;
9523 SCOPE_ENTER(3, "user: %s dir: %s msg: %d box %d\n",
9524 vms->username, vms->curdir, vms->curmsg, box);
9525
9526 ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
9527
9528 /* Rename the member vmbox HERE so that we don't try to return before
9529 * we know what's going on.
9530 */
9531 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
9532
9533 /* Faster to make the directory than to check if it exists. */
9534 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
9535
9536 /* traverses directory using readdir (or select query for ODBC) */
9537 count_msg = COUNT(vmu, vms->curdir);
9538 if (count_msg < 0) {
9539 SCOPE_EXIT_RTN_VALUE(count_msg, "msgs: %d\n", count_msg);
9540 } else {
9541 vms->lastmsg = count_msg - 1;
9542 }
9543
9544 if (vm_allocate_dh(vms, vmu, count_msg)) {
9545 SCOPE_EXIT_RTN_VALUE(-1, "failed to allocate dh\n");
9546 }
9547
9548 /*
9549 The following test is needed in case sequencing gets messed up.
9550 There appears to be more than one way to mess up sequence, so
9551 we will not try to find all of the root causes--just fix it when
9552 detected.
9553 */
9554
9555 if (vm_lock_path(vms->curdir)) {
9556 SCOPE_EXIT_LOG_RTN_VALUE(ERROR_LOCK_PATH, AST_LOG_ERROR, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
9557 }
9558
9559 /* for local storage, checks directory for messages up to MAXMSGLIMIT */
9560 last_msg = LAST_MSG_INDEX(vms->curdir);
9561 ast_unlock_path(vms->curdir);
9562
9563 if (last_msg < -1) {
9564 SCOPE_EXIT_RTN_VALUE(last_msg, "last msg: %d\n", last_msg);
9565 } else if (vms->lastmsg != last_msg) {
9566 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);
9567 resequence_mailbox(vmu, vms->curdir, count_msg);
9568 }
9569
9570 SCOPE_EXIT_RTN_VALUE(0, "Done\n");
9571}
9572#endif
9573
9574static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
9575{
9576 int x = 0;
9577 int last_msg_idx = 0;
9578
9579#ifndef IMAP_STORAGE
9580 int res = 0, nummsg;
9581 char fn2[PATH_MAX];
9582#endif
9583 SCOPE_ENTER(3, "user: %s dir: %s msg: %d\n",
9584 vms->username, vms->curdir, vms->curmsg);
9585
9586 if (vms->lastmsg <= -1) {
9587 ast_trace(-1, "No messages in mailbox\n");
9588 goto done;
9589 }
9590
9591 vms->curmsg = -1;
9592#ifndef IMAP_STORAGE
9593 /* Get the deleted messages fixed */
9594 if (vm_lock_path(vms->curdir)) {
9595 SCOPE_EXIT_RTN_VALUE(ERROR_LOCK_PATH, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
9596 }
9597
9598 /* update count as message may have arrived while we've got mailbox open */
9599 last_msg_idx = LAST_MSG_INDEX(vms->curdir);
9600 if (last_msg_idx != vms->lastmsg) {
9601 ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
9602 }
9603
9604 /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
9605 for (x = 0; x < last_msg_idx + 1; x++) {
9606 if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
9607 /* Save this message. It's not in INBOX or hasn't been heard */
9608 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9609 if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
9610 break;
9611 }
9612 vms->curmsg++;
9613 make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
9614 if (strcmp(vms->fn, fn2)) {
9615 SCOPE_CALL(-1, RENAME, vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
9616 }
9617 } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
9618 /* Move to old folder before deleting */
9619 res = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, vms, x, 1, NULL, 0);
9620 if (res == ERROR_LOCK_PATH || res == ERROR_MAX_MSGS) {
9621 /* If save failed do not delete the message */
9622 ast_log(AST_LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
9623 vms->deleted[x] = 0;
9624 vms->heard[x] = 0;
9625 --x;
9626 }
9627 } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
9628 /* Move to deleted folder */
9629 res = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, vms, x, 10, NULL, 0);
9630 if (res == ERROR_LOCK_PATH) {
9631 ast_trace(-1, "Unable to lock path. Not moving message to deleted folder.\n");
9632 /* If save failed do not delete the message */
9633 vms->deleted[x] = 0;
9634 vms->heard[x] = 0;
9635 --x;
9636 }
9637 } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
9638 /* If realtime storage enabled - we should explicitly delete this message,
9639 cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
9640 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9641 res = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, vms->curdir, x, vms->fn, NULL);
9642 if (res) {
9643 SCOPE_CALL(-1, DELETE, vms->curdir, x, vms->fn, vmu);
9644 }
9645 }
9646 }
9647
9648 /* Delete ALL remaining messages */
9649 nummsg = x - 1;
9650 for (x = vms->curmsg + 1; x <= nummsg; x++) {
9651 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9652 res = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, vms->curdir, x, vms->fn, NULL);
9653 if (res) {
9654 SCOPE_CALL(-1, DELETE, vms->curdir, x, vms->fn, vmu);
9655 }
9656 }
9657 ast_unlock_path(vms->curdir);
9658#else /* defined(IMAP_STORAGE) */
9659 ast_mutex_lock(&vms->lock);
9660 if (vms->deleted) {
9661 /* Since we now expunge after each delete, deleting in reverse order
9662 * ensures that no reordering occurs between each step. */
9663 last_msg_idx = vms->dh_arraysize;
9664 for (x = last_msg_idx - 1; x >= 0; x--) {
9665 if (vms->deleted[x]) {
9666 ast_debug(3, "IMAP delete of %d\n", x);
9667 DELETE(vms->curdir, x, vms->fn, vmu);
9668 }
9669 }
9670 }
9671#endif
9672
9673done:
9674 if (vms->deleted) {
9675 ast_free(vms->deleted);
9676 vms->deleted = NULL;
9677 }
9678 if (vms->heard) {
9679 ast_free(vms->heard);
9680 vms->heard = NULL;
9681 }
9682 vms->dh_arraysize = 0;
9683#ifdef IMAP_STORAGE
9684 ast_mutex_unlock(&vms->lock);
9685#endif
9686
9687 SCOPE_EXIT_RTN_VALUE(0, "Done\n");
9688}
9689
9690/* In Greek even though we CAN use a syntax like "friends messages"
9691 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
9692 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
9693 * syntax for the above three categories which is more elegant.
9694 */
9695
9696static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
9697{
9698 int cmd;
9699 char *buf;
9700
9701 buf = ast_alloca(strlen(box) + 2);
9702 strcpy(buf, box);
9703 strcat(buf, "s");
9704
9705 if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
9706 cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
9707 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
9708 } else {
9709 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
9710 return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
9711 }
9712}
9713
9714static int vm_play_folder_name_ja(struct ast_channel *chan, char *box)
9715{
9716 int cmd;
9717
9718 if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
9719 cmd = ast_play_and_wait(chan, box);
9720 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9721 } else {
9722 cmd = ast_play_and_wait(chan, box);
9723 return cmd;
9724 }
9725}
9726
9727static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
9728{
9729 int cmd;
9730
9731 if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
9732 if (!strcasecmp(box, "vm-INBOX"))
9733 cmd = ast_play_and_wait(chan, "vm-new-e");
9734 else
9735 cmd = ast_play_and_wait(chan, "vm-old-e");
9736 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9737 } else {
9738 cmd = ast_play_and_wait(chan, "vm-messages");
9739 return cmd ? cmd : ast_play_and_wait(chan, box);
9740 }
9741}
9742
9743static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
9744{
9745 int cmd;
9746
9747 if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
9748 cmd = ast_play_and_wait(chan, "vm-messages");
9749 return cmd ? cmd : ast_play_and_wait(chan, box);
9750 } else {
9751 cmd = ast_play_and_wait(chan, box);
9752 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9753 }
9754}
9755
9756static int vm_play_folder_name(struct ast_channel *chan, char *box)
9757{
9758 int cmd;
9759
9760 if ( !strncasecmp(ast_channel_language(chan), "it", 2) ||
9761 !strncasecmp(ast_channel_language(chan), "es", 2) ||
9762 !strncasecmp(ast_channel_language(chan), "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
9763 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
9764 return cmd ? cmd : ast_play_and_wait(chan, box);
9765 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {
9766 return vm_play_folder_name_gr(chan, box);
9767 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* Hebrew syntax */
9768 return ast_play_and_wait(chan, box);
9769 } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
9770 return vm_play_folder_name_ja(chan, box);
9771 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9772 return vm_play_folder_name_pl(chan, box);
9773 } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian syntax */
9774 return vm_play_folder_name_ua(chan, box);
9775 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
9776 return ast_play_and_wait(chan, box);
9777 } else { /* Default English */
9778 cmd = ast_play_and_wait(chan, box);
9779 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
9780 }
9781}
9782
9783/* GREEK SYNTAX
9784 In greek the plural for old/new is
9785 different so we need the following files
9786 We also need vm-denExeteMynhmata because
9787 this syntax is different.
9788
9789 -> vm-Olds.wav : "Palia"
9790 -> vm-INBOXs.wav : "Nea"
9791 -> vm-denExeteMynhmata : "den exete mynhmata"
9792*/
9793
9794
9795static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
9796{
9797 int res = 0;
9798
9799 if (vms->newmessages) {
9800 res = ast_play_and_wait(chan, "vm-youhave");
9801 if (!res)
9803 if (!res) {
9804 if (vms->newmessages == 1) {
9805 res = ast_play_and_wait(chan, "vm-INBOX");
9806 if (!res)
9807 res = ast_play_and_wait(chan, "vm-message");
9808 } else {
9809 res = ast_play_and_wait(chan, "vm-INBOXs");
9810 if (!res)
9811 res = ast_play_and_wait(chan, "vm-messages");
9812 }
9813 }
9814 } else if (vms->oldmessages){
9815 res = ast_play_and_wait(chan, "vm-youhave");
9816 if (!res)
9818 if (vms->oldmessages == 1){
9819 res = ast_play_and_wait(chan, "vm-Old");
9820 if (!res)
9821 res = ast_play_and_wait(chan, "vm-message");
9822 } else {
9823 res = ast_play_and_wait(chan, "vm-Olds");
9824 if (!res)
9825 res = ast_play_and_wait(chan, "vm-messages");
9826 }
9827 } else if (!vms->oldmessages && !vms->newmessages)
9828 res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
9829 return res;
9830}
9831
9832/* Version of vm_intro() designed to work for many languages.
9833 *
9834 * It is hoped that this function can prevent the proliferation of
9835 * language-specific vm_intro() functions and in time replace the language-
9836 * specific functions which already exist. An examination of the language-
9837 * specific functions revealed that they all corrected the same deficiencies
9838 * in vm_intro_en() (which was the default function). Namely:
9839 *
9840 * 1) The vm-Old and vm-INBOX sound files were overloaded. The English
9841 * wording of the voicemail greeting hides this problem. For example,
9842 * vm-INBOX contains only the word "new". This means that both of these
9843 * sequences produce valid utterances:
9844 * * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
9845 * * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
9846 * However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
9847 * in many languages) the first utterance becomes "you have 1 the new message".
9848 * 2) The function contains hardcoded rules for pluralizing the word "message".
9849 * These rules are correct for English, but not for many other languages.
9850 * 3) No attempt is made to pluralize the adjectives ("old" and "new") as
9851 * required in many languages.
9852 * 4) The gender of the word for "message" is not specified. This is a problem
9853 * because in many languages the gender of the number in phrases such
9854 * as "you have one new message" must match the gender of the word
9855 * meaning "message".
9856 *
9857 * Fixing these problems for each new language has meant duplication of effort.
9858 * This new function solves the problems in the following general ways:
9859 * 1) Add new sound files vm-new and vm-old. These can be linked to vm-INBOX
9860 * and vm-Old respectively for those languages where it makes sense.
9861 * 2) Call ast_say_counted_noun() to put the proper gender and number prefix
9862 * on vm-message.
9863 * 3) Call ast_say_counted_adjective() to put the proper gender and number
9864 * prefix on vm-new and vm-old (none for English).
9865 * 4) Pass the gender of the language's word for "message" as an argument to
9866 * this function which is can in turn pass on to the functions which
9867 * say numbers and put endings on nouns and adjectives.
9868 *
9869 * All languages require these messages:
9870 * vm-youhave "You have..."
9871 * vm-and "and"
9872 * vm-no "no" (in the sense of "none", as in "you have no messages")
9873 *
9874 * To use it for English, you will need these additional sound files:
9875 * vm-new "new"
9876 * vm-message "message", singular
9877 * vm-messages "messages", plural
9878 *
9879 * If you use it for Russian and other slavic languages, you will need these additional sound files:
9880 *
9881 * vm-newn "novoye" (singular, neuter)
9882 * vm-newx "novikh" (counting plural form, genative plural)
9883 * vm-message "sobsheniye" (singular form)
9884 * vm-messagex1 "sobsheniya" (first counting plural form, genative singular)
9885 * vm-messagex2 "sobsheniy" (second counting plural form, genative plural)
9886 * digits/1n "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
9887 * digits/2n "dva" (neuter singular)
9888 */
9889static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
9890{
9891 int res;
9892 int lastnum = 0;
9893
9894 res = ast_play_and_wait(chan, "vm-youhave");
9895
9896 if (!res && vms->newmessages) {
9897 lastnum = vms->newmessages;
9898
9899 if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
9900 res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
9901 }
9902
9903 if (!res && vms->oldmessages) {
9904 res = ast_play_and_wait(chan, "vm-and");
9905 }
9906 }
9907
9908 if (!res && vms->oldmessages) {
9909 lastnum = vms->oldmessages;
9910
9911 if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
9912 res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
9913 }
9914 }
9915
9916 if (!res) {
9917 if (lastnum == 0) {
9918 res = ast_play_and_wait(chan, "vm-no");
9919 }
9920 if (!res) {
9921 res = ast_say_counted_noun(chan, lastnum, "vm-message");
9922 }
9923 }
9924
9925 return res;
9926}
9927
9928/* Default Hebrew syntax */
9929static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
9930{
9931 int res = 0;
9932
9933 /* Introduce messages they have */
9934 if (!res) {
9935 if ((vms->newmessages) || (vms->oldmessages)) {
9936 res = ast_play_and_wait(chan, "vm-youhave");
9937 }
9938 /*
9939 * The word "shtei" refers to the number 2 in hebrew when performing a count
9940 * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
9941 * an element, this is one of them.
9942 */
9943 if (vms->newmessages) {
9944 if (!res) {
9945 if (vms->newmessages == 1) {
9946 res = ast_play_and_wait(chan, "vm-INBOX1");
9947 } else {
9948 if (vms->newmessages == 2) {
9949 res = ast_play_and_wait(chan, "vm-shtei");
9950 } else {
9951 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9952 }
9953 res = ast_play_and_wait(chan, "vm-INBOX");
9954 }
9955 }
9956 if (vms->oldmessages && !res) {
9957 res = ast_play_and_wait(chan, "vm-and");
9958 if (vms->oldmessages == 1) {
9959 res = ast_play_and_wait(chan, "vm-Old1");
9960 } else {
9961 if (vms->oldmessages == 2) {
9962 res = ast_play_and_wait(chan, "vm-shtei");
9963 } else {
9964 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9965 }
9966 res = ast_play_and_wait(chan, "vm-Old");
9967 }
9968 }
9969 }
9970 if (!res && vms->oldmessages && !vms->newmessages) {
9971 if (!res) {
9972 if (vms->oldmessages == 1) {
9973 res = ast_play_and_wait(chan, "vm-Old1");
9974 } else {
9975 if (vms->oldmessages == 2) {
9976 res = ast_play_and_wait(chan, "vm-shtei");
9977 } else {
9978 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9979 }
9980 res = ast_play_and_wait(chan, "vm-Old");
9981 }
9982 }
9983 }
9984 if (!res) {
9985 if (!vms->oldmessages && !vms->newmessages) {
9986 if (!res) {
9987 res = ast_play_and_wait(chan, "vm-nomessages");
9988 }
9989 }
9990 }
9991 }
9992 return res;
9993}
9994
9995/* Japanese syntax */
9996static int vm_intro_ja(struct ast_channel *chan,struct vm_state *vms)
9997{
9998 /* Introduce messages they have */
9999 int res;
10000 if (vms->newmessages) {
10001 res = ast_play_and_wait(chan, "vm-INBOX");
10002 if (!res)
10003 res = ast_play_and_wait(chan, "vm-message");
10004 if (!res)
10005 res = ast_play_and_wait(chan, "jp-ga");
10006 if (!res)
10007 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10008 if (vms->oldmessages && !res)
10009 res = ast_play_and_wait(chan, "silence/1");
10010
10011 }
10012 if (vms->oldmessages) {
10013 res = ast_play_and_wait(chan, "vm-Old");
10014 if (!res)
10015 res = ast_play_and_wait(chan, "vm-message");
10016 if (!res)
10017 res = ast_play_and_wait(chan, "jp-ga");
10018 if (!res)
10019 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10020 }
10021 if (!vms->oldmessages && !vms->newmessages) {
10022 res = ast_play_and_wait(chan, "vm-messages");
10023 if (!res)
10024 res = ast_play_and_wait(chan, "jp-wa");
10025 if (!res)
10026 res = ast_play_and_wait(chan, "jp-arimasen");
10027 }
10028 else {
10029 res = ast_play_and_wait(chan, "jp-arimasu");
10030 }
10031 return res;
10032} /* Japanese */
10033
10034/* Default English syntax */
10035static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
10036{
10037 int res;
10038
10039 /* Introduce messages they have */
10040 res = ast_play_and_wait(chan, "vm-youhave");
10041 if (!res) {
10042 if (vms->urgentmessages) {
10043 res = say_and_wait(chan, vms->urgentmessages, ast_channel_language(chan));
10044 if (!res)
10045 res = ast_play_and_wait(chan, "vm-Urgent");
10046 if ((vms->oldmessages || vms->newmessages) && !res) {
10047 res = ast_play_and_wait(chan, "vm-and");
10048 } else if (!res) {
10049 if (vms->urgentmessages == 1)
10050 res = ast_play_and_wait(chan, "vm-message");
10051 else
10052 res = ast_play_and_wait(chan, "vm-messages");
10053 }
10054 }
10055 if (vms->newmessages) {
10056 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10057 if (!res)
10058 res = ast_play_and_wait(chan, "vm-INBOX");
10059 if (vms->oldmessages && !res)
10060 res = ast_play_and_wait(chan, "vm-and");
10061 else if (!res) {
10062 if (vms->newmessages == 1)
10063 res = ast_play_and_wait(chan, "vm-message");
10064 else
10065 res = ast_play_and_wait(chan, "vm-messages");
10066 }
10067
10068 }
10069 if (!res && vms->oldmessages) {
10070 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10071 if (!res)
10072 res = ast_play_and_wait(chan, "vm-Old");
10073 if (!res) {
10074 if (vms->oldmessages == 1)
10075 res = ast_play_and_wait(chan, "vm-message");
10076 else
10077 res = ast_play_and_wait(chan, "vm-messages");
10078 }
10079 }
10080 if (!res) {
10081 if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
10082 res = ast_play_and_wait(chan, "vm-no");
10083 if (!res)
10084 res = ast_play_and_wait(chan, "vm-messages");
10085 }
10086 }
10087 }
10088 return res;
10089}
10090
10091/* ICELANDIC syntax */
10092static int vm_intro_is(struct ast_channel *chan, struct vm_state *vms)
10093{
10094 int res;
10095
10096 /* Introduce messages they have */
10097 res = ast_play_and_wait(chan, "vm-youhave");
10098 if (!res) {
10099 if (vms->urgentmessages) {
10100 /* Digits 1-4 are spoken in neutral and plural when talking about messages,
10101 however, feminine is used for 1 as it is the same as the neutral for plural,
10102 and singular neutral is the same after 1. */
10103 if (vms->urgentmessages < 5) {
10104 char recname[16];
10105 if (vms->urgentmessages == 1)
10106 snprintf(recname, sizeof(recname), "digits/1kvk");
10107 else
10108 snprintf(recname, sizeof(recname), "digits/%dhk", vms->urgentmessages);
10109 res = ast_play_and_wait(chan, recname);
10110 } else if (!res)
10111 res = ast_play_and_wait(chan, "vm-Urgent");
10112 if ((vms->oldmessages || vms->newmessages) && !res) {
10113 res = ast_play_and_wait(chan, "vm-and");
10114 } else if (!res)
10115 res = ast_play_and_wait(chan, "vm-messages");
10116 }
10117 if (vms->newmessages) {
10118 if (vms->newmessages < 5) {
10119 char recname[16];
10120 if (vms->newmessages == 1)
10121 snprintf(recname, sizeof(recname), "digits/1kvk");
10122 else
10123 snprintf(recname, sizeof(recname), "digits/%dhk", vms->newmessages);
10124 res = ast_play_and_wait(chan, recname);
10125 } else
10126 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10127 if (!res)
10128 res = ast_play_and_wait(chan, "vm-INBOX");
10129 if (vms->oldmessages && !res)
10130 res = ast_play_and_wait(chan, "vm-and");
10131 else if (!res)
10132 res = ast_play_and_wait(chan, "vm-messages");
10133 }
10134 if (!res && vms->oldmessages) {
10135 if (vms->oldmessages < 5) {
10136 char recname[16];
10137 if (vms->oldmessages == 1)
10138 snprintf(recname, sizeof(recname), "digits/1kvk");
10139 else
10140 snprintf(recname, sizeof(recname), "digits/%dhk", vms->oldmessages);
10141 res = ast_play_and_wait(chan, recname);
10142 } else
10143 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10144 if (!res)
10145 res = ast_play_and_wait(chan, "vm-Old");
10146 if (!res)
10147 res = ast_play_and_wait(chan, "vm-messages");
10148 }
10149 if (!res) {
10150 if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
10151 res = ast_play_and_wait(chan, "vm-no");
10152 if (!res)
10153 res = ast_play_and_wait(chan, "vm-messages");
10154 }
10155 }
10156 }
10157 return res;
10158}
10159
10160/* ITALIAN syntax */
10161static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
10162{
10163 /* Introduce messages they have */
10164 int res;
10165 if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
10166 res = ast_play_and_wait(chan, "vm-no") ||
10167 ast_play_and_wait(chan, "vm-message");
10168 else
10169 res = ast_play_and_wait(chan, "vm-youhave");
10170 if (!res && vms->newmessages) {
10171 res = (vms->newmessages == 1) ?
10172 ast_play_and_wait(chan, "digits/un") ||
10173 ast_play_and_wait(chan, "vm-nuovo") ||
10174 ast_play_and_wait(chan, "vm-message") :
10175 /* 2 or more new messages */
10176 say_and_wait(chan, vms->newmessages, ast_channel_language(chan)) ||
10177 ast_play_and_wait(chan, "vm-nuovi") ||
10178 ast_play_and_wait(chan, "vm-messages");
10179 if (!res && vms->oldmessages)
10180 res = ast_play_and_wait(chan, "vm-and");
10181 }
10182 if (!res && vms->oldmessages) {
10183 res = (vms->oldmessages == 1) ?
10184 ast_play_and_wait(chan, "digits/un") ||
10185 ast_play_and_wait(chan, "vm-vecchio") ||
10186 ast_play_and_wait(chan, "vm-message") :
10187 /* 2 or more old messages */
10188 say_and_wait(chan, vms->oldmessages, ast_channel_language(chan)) ||
10189 ast_play_and_wait(chan, "vm-vecchi") ||
10190 ast_play_and_wait(chan, "vm-messages");
10191 }
10192 return res;
10193}
10194
10195/* POLISH syntax */
10196static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
10197{
10198 /* Introduce messages they have */
10199 int res;
10200 div_t num;
10201
10202 if (!vms->oldmessages && !vms->newmessages) {
10203 res = ast_play_and_wait(chan, "vm-no");
10204 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10205 return res;
10206 } else {
10207 res = ast_play_and_wait(chan, "vm-youhave");
10208 }
10209
10210 if (vms->newmessages) {
10211 num = div(vms->newmessages, 10);
10212 if (vms->newmessages == 1) {
10213 res = ast_play_and_wait(chan, "digits/1-a");
10214 res = res ? res : ast_play_and_wait(chan, "vm-new-a");
10215 res = res ? res : ast_play_and_wait(chan, "vm-message");
10216 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
10217 if (num.rem == 2) {
10218 if (!num.quot) {
10219 res = ast_play_and_wait(chan, "digits/2-ie");
10220 } else {
10221 res = say_and_wait(chan, vms->newmessages - 2 , ast_channel_language(chan));
10222 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
10223 }
10224 } else {
10225 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10226 }
10227 res = res ? res : ast_play_and_wait(chan, "vm-new-e");
10228 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10229 } else {
10230 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10231 res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
10232 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10233 }
10234 if (!res && vms->oldmessages)
10235 res = ast_play_and_wait(chan, "vm-and");
10236 }
10237 if (!res && vms->oldmessages) {
10238 num = div(vms->oldmessages, 10);
10239 if (vms->oldmessages == 1) {
10240 res = ast_play_and_wait(chan, "digits/1-a");
10241 res = res ? res : ast_play_and_wait(chan, "vm-old-a");
10242 res = res ? res : ast_play_and_wait(chan, "vm-message");
10243 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
10244 if (num.rem == 2) {
10245 if (!num.quot) {
10246 res = ast_play_and_wait(chan, "digits/2-ie");
10247 } else {
10248 res = say_and_wait(chan, vms->oldmessages - 2 , ast_channel_language(chan));
10249 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
10250 }
10251 } else {
10252 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10253 }
10254 res = res ? res : ast_play_and_wait(chan, "vm-old-e");
10255 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10256 } else {
10257 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10258 res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
10259 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10260 }
10261 }
10262
10263 return res;
10264}
10265
10266/* SWEDISH syntax */
10267static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
10268{
10269 /* Introduce messages they have */
10270 int res;
10271
10272 res = ast_play_and_wait(chan, "vm-youhave");
10273 if (res)
10274 return res;
10275
10276 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10277 res = ast_play_and_wait(chan, "vm-no");
10278 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10279 return res;
10280 }
10281
10282 if (vms->newmessages) {
10283 if (vms->newmessages == 1) {
10284 res = ast_play_and_wait(chan, "digits/ett");
10285 res = res ? res : ast_play_and_wait(chan, "vm-nytt");
10286 res = res ? res : ast_play_and_wait(chan, "vm-message");
10287 } else {
10288 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10289 res = res ? res : ast_play_and_wait(chan, "vm-nya");
10290 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10291 }
10292 if (!res && vms->oldmessages)
10293 res = ast_play_and_wait(chan, "vm-and");
10294 }
10295 if (!res && vms->oldmessages) {
10296 if (vms->oldmessages == 1) {
10297 res = ast_play_and_wait(chan, "digits/ett");
10298 res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
10299 res = res ? res : ast_play_and_wait(chan, "vm-message");
10300 } else {
10301 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10302 res = res ? res : ast_play_and_wait(chan, "vm-gamla");
10303 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10304 }
10305 }
10306
10307 return res;
10308}
10309
10310/* NORWEGIAN syntax */
10311static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
10312{
10313 /* Introduce messages they have */
10314 int res;
10315
10316 res = ast_play_and_wait(chan, "vm-youhave");
10317 if (res)
10318 return res;
10319
10320 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10321 res = ast_play_and_wait(chan, "vm-no");
10322 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10323 return res;
10324 }
10325
10326 if (vms->newmessages) {
10327 if (vms->newmessages == 1) {
10328 res = ast_play_and_wait(chan, "digits/1");
10329 res = res ? res : ast_play_and_wait(chan, "vm-ny");
10330 res = res ? res : ast_play_and_wait(chan, "vm-message");
10331 } else {
10332 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10333 res = res ? res : ast_play_and_wait(chan, "vm-nye");
10334 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10335 }
10336 if (!res && vms->oldmessages)
10337 res = ast_play_and_wait(chan, "vm-and");
10338 }
10339 if (!res && vms->oldmessages) {
10340 if (vms->oldmessages == 1) {
10341 res = ast_play_and_wait(chan, "digits/1");
10342 res = res ? res : ast_play_and_wait(chan, "vm-gamel");
10343 res = res ? res : ast_play_and_wait(chan, "vm-message");
10344 } else {
10345 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10346 res = res ? res : ast_play_and_wait(chan, "vm-gamle");
10347 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10348 }
10349 }
10350
10351 return res;
10352}
10353
10354/* Danish syntax */
10355static int vm_intro_da(struct ast_channel *chan, struct vm_state *vms)
10356{
10357 /* Introduce messages they have */
10358 int res;
10359
10360 res = ast_play_and_wait(chan, "vm-youhave");
10361 if (res)
10362 return res;
10363
10364 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10365 res = ast_play_and_wait(chan, "vm-no");
10366 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10367 return res;
10368 }
10369
10370 if (vms->newmessages) {
10371 if ((vms->newmessages == 1)) {
10372 res = ast_play_and_wait(chan, "digits/1");
10373 res = res ? res : ast_play_and_wait(chan, "vm-INBOX");
10374 res = res ? res : ast_play_and_wait(chan, "vm-message");
10375 } else {
10376 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10377 res = res ? res : ast_play_and_wait(chan, "vm-INBOXs");
10378 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10379 }
10380 if (!res && vms->oldmessages)
10381 res = ast_play_and_wait(chan, "vm-and");
10382 }
10383 if (!res && vms->oldmessages) {
10384 if (vms->oldmessages == 1) {
10385 res = ast_play_and_wait(chan, "digits/1");
10386 res = res ? res : ast_play_and_wait(chan, "vm-Old");
10387 res = res ? res : ast_play_and_wait(chan, "vm-message");
10388 } else {
10389 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10390 res = res ? res : ast_play_and_wait(chan, "vm-Olds");
10391 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10392 }
10393 }
10394
10395 return res;
10396}
10397
10398
10399/* GERMAN syntax */
10400static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
10401{
10402 /* Introduce messages they have */
10403 int res;
10404 res = ast_play_and_wait(chan, "vm-youhave");
10405 if (!res) {
10406 if (vms->newmessages) {
10407 if (vms->newmessages == 1)
10408 res = ast_play_and_wait(chan, "digits/1F");
10409 else
10410 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10411 if (!res)
10412 res = ast_play_and_wait(chan, "vm-INBOX");
10413 if (vms->oldmessages && !res)
10414 res = ast_play_and_wait(chan, "vm-and");
10415 else if (!res) {
10416 if (vms->newmessages == 1)
10417 res = ast_play_and_wait(chan, "vm-message");
10418 else
10419 res = ast_play_and_wait(chan, "vm-messages");
10420 }
10421
10422 }
10423 if (!res && vms->oldmessages) {
10424 if (vms->oldmessages == 1)
10425 res = ast_play_and_wait(chan, "digits/1F");
10426 else
10427 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10428 if (!res)
10429 res = ast_play_and_wait(chan, "vm-Old");
10430 if (!res) {
10431 if (vms->oldmessages == 1)
10432 res = ast_play_and_wait(chan, "vm-message");
10433 else
10434 res = ast_play_and_wait(chan, "vm-messages");
10435 }
10436 }
10437 if (!res) {
10438 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10439 res = ast_play_and_wait(chan, "vm-no");
10440 if (!res)
10441 res = ast_play_and_wait(chan, "vm-messages");
10442 }
10443 }
10444 }
10445 return res;
10446}
10447
10448/* SPANISH syntax */
10449static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
10450{
10451 /* Introduce messages they have */
10452 int res;
10453 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10454 res = ast_play_and_wait(chan, "vm-youhaveno");
10455 if (!res)
10456 res = ast_play_and_wait(chan, "vm-messages");
10457 } else {
10458 res = ast_play_and_wait(chan, "vm-youhave");
10459 }
10460 if (!res) {
10461 if (vms->newmessages) {
10462 if (!res) {
10463 if (vms->newmessages == 1) {
10464 res = ast_play_and_wait(chan, "digits/1M");
10465 if (!res)
10466 res = ast_play_and_wait(chan, "vm-message");
10467 if (!res)
10468 res = ast_play_and_wait(chan, "vm-INBOXs");
10469 } else {
10470 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10471 if (!res)
10472 res = ast_play_and_wait(chan, "vm-messages");
10473 if (!res)
10474 res = ast_play_and_wait(chan, "vm-INBOX");
10475 }
10476 }
10477 if (vms->oldmessages && !res)
10478 res = ast_play_and_wait(chan, "vm-and");
10479 }
10480 if (vms->oldmessages) {
10481 if (!res) {
10482 if (vms->oldmessages == 1) {
10483 res = ast_play_and_wait(chan, "digits/1M");
10484 if (!res)
10485 res = ast_play_and_wait(chan, "vm-message");
10486 if (!res)
10487 res = ast_play_and_wait(chan, "vm-Olds");
10488 } else {
10489 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10490 if (!res)
10491 res = ast_play_and_wait(chan, "vm-messages");
10492 if (!res)
10493 res = ast_play_and_wait(chan, "vm-Old");
10494 }
10495 }
10496 }
10497 }
10498return res;
10499}
10500
10501/* BRAZILIAN PORTUGUESE syntax */
10502static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
10503 /* Introduce messages they have */
10504 int res;
10505 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10506 res = ast_play_and_wait(chan, "vm-nomessages");
10507 return res;
10508 } else {
10509 res = ast_play_and_wait(chan, "vm-youhave");
10510 }
10511 if (vms->newmessages) {
10512 if (!res)
10513 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10514 if (vms->newmessages == 1) {
10515 if (!res)
10516 res = ast_play_and_wait(chan, "vm-message");
10517 if (!res)
10518 res = ast_play_and_wait(chan, "vm-INBOXs");
10519 } else {
10520 if (!res)
10521 res = ast_play_and_wait(chan, "vm-messages");
10522 if (!res)
10523 res = ast_play_and_wait(chan, "vm-INBOX");
10524 }
10525 if (vms->oldmessages && !res)
10526 res = ast_play_and_wait(chan, "vm-and");
10527 }
10528 if (vms->oldmessages) {
10529 if (!res)
10530 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10531 if (vms->oldmessages == 1) {
10532 if (!res)
10533 res = ast_play_and_wait(chan, "vm-message");
10534 if (!res)
10535 res = ast_play_and_wait(chan, "vm-Olds");
10536 } else {
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 return res;
10544}
10545
10546/* FRENCH syntax */
10547static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
10548{
10549 /* Introduce messages they have */
10550 int res;
10551 res = ast_play_and_wait(chan, "vm-youhave");
10552 if (!res) {
10553 if (vms->newmessages) {
10554 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10555 if (!res)
10556 res = ast_play_and_wait(chan, "vm-INBOX");
10557 if (vms->oldmessages && !res)
10558 res = ast_play_and_wait(chan, "vm-and");
10559 else if (!res) {
10560 if (vms->newmessages == 1)
10561 res = ast_play_and_wait(chan, "vm-message");
10562 else
10563 res = ast_play_and_wait(chan, "vm-messages");
10564 }
10565
10566 }
10567 if (!res && vms->oldmessages) {
10568 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10569 if (!res)
10570 res = ast_play_and_wait(chan, "vm-Old");
10571 if (!res) {
10572 if (vms->oldmessages == 1)
10573 res = ast_play_and_wait(chan, "vm-message");
10574 else
10575 res = ast_play_and_wait(chan, "vm-messages");
10576 }
10577 }
10578 if (!res) {
10579 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10580 res = ast_play_and_wait(chan, "vm-no");
10581 if (!res)
10582 res = ast_play_and_wait(chan, "vm-messages");
10583 }
10584 }
10585 }
10586 return res;
10587}
10588
10589/* DUTCH syntax */
10590static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
10591{
10592 /* Introduce messages they have */
10593 int res;
10594 res = ast_play_and_wait(chan, "vm-youhave");
10595 if (!res) {
10596 if (vms->newmessages) {
10597 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10598 if (!res) {
10599 if (vms->newmessages == 1)
10600 res = ast_play_and_wait(chan, "vm-INBOXs");
10601 else
10602 res = ast_play_and_wait(chan, "vm-INBOX");
10603 }
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 if (vms->oldmessages == 1)
10618 res = ast_play_and_wait(chan, "vm-Olds");
10619 else
10620 res = ast_play_and_wait(chan, "vm-Old");
10621 }
10622 if (!res) {
10623 if (vms->oldmessages == 1)
10624 res = ast_play_and_wait(chan, "vm-message");
10625 else
10626 res = ast_play_and_wait(chan, "vm-messages");
10627 }
10628 }
10629 if (!res) {
10630 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10631 res = ast_play_and_wait(chan, "vm-no");
10632 if (!res)
10633 res = ast_play_and_wait(chan, "vm-messages");
10634 }
10635 }
10636 }
10637 return res;
10638}
10639
10640/* PORTUGUESE syntax */
10641static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
10642{
10643 /* Introduce messages they have */
10644 int res;
10645 res = ast_play_and_wait(chan, "vm-youhave");
10646 if (!res) {
10647 if (vms->newmessages) {
10648 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10649 if (!res) {
10650 if (vms->newmessages == 1) {
10651 res = ast_play_and_wait(chan, "vm-message");
10652 if (!res)
10653 res = ast_play_and_wait(chan, "vm-INBOXs");
10654 } else {
10655 res = ast_play_and_wait(chan, "vm-messages");
10656 if (!res)
10657 res = ast_play_and_wait(chan, "vm-INBOX");
10658 }
10659 }
10660 if (vms->oldmessages && !res)
10661 res = ast_play_and_wait(chan, "vm-and");
10662 }
10663 if (!res && vms->oldmessages) {
10664 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10665 if (!res) {
10666 if (vms->oldmessages == 1) {
10667 res = ast_play_and_wait(chan, "vm-message");
10668 if (!res)
10669 res = ast_play_and_wait(chan, "vm-Olds");
10670 } else {
10671 res = ast_play_and_wait(chan, "vm-messages");
10672 if (!res)
10673 res = ast_play_and_wait(chan, "vm-Old");
10674 }
10675 }
10676 }
10677 if (!res) {
10678 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10679 res = ast_play_and_wait(chan, "vm-no");
10680 if (!res)
10681 res = ast_play_and_wait(chan, "vm-messages");
10682 }
10683 }
10684 }
10685 return res;
10686}
10687
10688
10689/* CZECH syntax */
10690/* in czech there must be declension of word new and message
10691 * czech : english : czech : english
10692 * --------------------------------------------------------
10693 * vm-youhave : you have
10694 * vm-novou : one new : vm-zpravu : message
10695 * vm-nove : 2-4 new : vm-zpravy : messages
10696 * vm-novych : 5-infinite new : vm-zprav : messages
10697 * vm-starou : one old
10698 * vm-stare : 2-4 old
10699 * vm-starych : 5-infinite old
10700 * jednu : one - falling 4.
10701 * vm-no : no ( no messages )
10702 */
10703
10704static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
10705{
10706 int res;
10707 res = ast_play_and_wait(chan, "vm-youhave");
10708 if (!res) {
10709 if (vms->newmessages) {
10710 if (vms->newmessages == 1) {
10711 res = ast_play_and_wait(chan, "digits/jednu");
10712 } else {
10713 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10714 }
10715 if (!res) {
10716 if (vms->newmessages == 1)
10717 res = ast_play_and_wait(chan, "vm-novou");
10718 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
10719 res = ast_play_and_wait(chan, "vm-nove");
10720 if (vms->newmessages > 4)
10721 res = ast_play_and_wait(chan, "vm-novych");
10722 }
10723 if (vms->oldmessages && !res)
10724 res = ast_play_and_wait(chan, "vm-and");
10725 else if (!res) {
10726 if (vms->newmessages == 1)
10727 res = ast_play_and_wait(chan, "vm-zpravu");
10728 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
10729 res = ast_play_and_wait(chan, "vm-zpravy");
10730 if (vms->newmessages > 4)
10731 res = ast_play_and_wait(chan, "vm-zprav");
10732 }
10733 }
10734 if (!res && vms->oldmessages) {
10735 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10736 if (!res) {
10737 if (vms->oldmessages == 1)
10738 res = ast_play_and_wait(chan, "vm-starou");
10739 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
10740 res = ast_play_and_wait(chan, "vm-stare");
10741 if (vms->oldmessages > 4)
10742 res = ast_play_and_wait(chan, "vm-starych");
10743 }
10744 if (!res) {
10745 if (vms->oldmessages == 1)
10746 res = ast_play_and_wait(chan, "vm-zpravu");
10747 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
10748 res = ast_play_and_wait(chan, "vm-zpravy");
10749 if (vms->oldmessages > 4)
10750 res = ast_play_and_wait(chan, "vm-zprav");
10751 }
10752 }
10753 if (!res) {
10754 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10755 res = ast_play_and_wait(chan, "vm-no");
10756 if (!res)
10757 res = ast_play_and_wait(chan, "vm-zpravy");
10758 }
10759 }
10760 }
10761 return res;
10762}
10763
10764/* CHINESE (Taiwan) syntax */
10765static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
10766{
10767 int res;
10768 /* Introduce messages they have */
10769 res = ast_play_and_wait(chan, "vm-you");
10770
10771 if (!res && vms->newmessages) {
10772 res = ast_play_and_wait(chan, "vm-have");
10773 if (!res)
10774 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10775 if (!res)
10776 res = ast_play_and_wait(chan, "vm-tong");
10777 if (!res)
10778 res = ast_play_and_wait(chan, "vm-INBOX");
10779 if (vms->oldmessages && !res)
10780 res = ast_play_and_wait(chan, "vm-and");
10781 else if (!res)
10782 res = ast_play_and_wait(chan, "vm-messages");
10783 }
10784 if (!res && vms->oldmessages) {
10785 res = ast_play_and_wait(chan, "vm-have");
10786 if (!res)
10787 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10788 if (!res)
10789 res = ast_play_and_wait(chan, "vm-tong");
10790 if (!res)
10791 res = ast_play_and_wait(chan, "vm-Old");
10792 if (!res)
10793 res = ast_play_and_wait(chan, "vm-messages");
10794 }
10795 if (!res && !vms->oldmessages && !vms->newmessages) {
10796 res = ast_play_and_wait(chan, "vm-haveno");
10797 if (!res)
10798 res = ast_play_and_wait(chan, "vm-messages");
10799 }
10800 return res;
10801}
10802
10803/* Vietnamese syntax */
10804static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
10805{
10806 int res;
10807
10808 /* Introduce messages they have */
10809 res = ast_play_and_wait(chan, "vm-youhave");
10810 if (!res) {
10811 if (vms->newmessages) {
10812 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10813 if (!res)
10814 res = ast_play_and_wait(chan, "vm-INBOX");
10815 if (vms->oldmessages && !res)
10816 res = ast_play_and_wait(chan, "vm-and");
10817 }
10818 if (!res && vms->oldmessages) {
10819 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10820 if (!res)
10821 res = ast_play_and_wait(chan, "vm-Old");
10822 }
10823 if (!res) {
10824 if (!vms->oldmessages && !vms->newmessages) {
10825 res = ast_play_and_wait(chan, "vm-no");
10826 if (!res)
10827 res = ast_play_and_wait(chan, "vm-message");
10828 }
10829 }
10830 }
10831 return res;
10832}
10833
10834static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
10835{
10836 char prefile[256];
10837
10838 /* Notify the user that the temp greeting is set and give them the option to remove it */
10839 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
10840 if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10841 RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
10842 if (ast_fileexists(prefile, NULL, NULL) > 0) {
10843 ast_play_and_wait(chan, "vm-tempgreetactive");
10844 }
10845 DISPOSE(prefile, -1);
10846 }
10847
10848 /* Play voicemail intro - syntax is different for different languages */
10849 if (0) {
10850 return 0;
10851 } else if (!strncasecmp(ast_channel_language(chan), "cs", 2)) { /* CZECH syntax */
10852 return vm_intro_cs(chan, vms);
10853 } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
10854 return vm_intro_de(chan, vms);
10855 } else if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH syntax */
10856 return vm_intro_es(chan, vms);
10857 } else if (!strncasecmp(ast_channel_language(chan), "fr", 2)) { /* FRENCH syntax */
10858 return vm_intro_fr(chan, vms);
10859 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
10860 return vm_intro_gr(chan, vms);
10861 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW syntax */
10862 return vm_intro_he(chan, vms);
10863 } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */
10864 return vm_intro_is(chan, vms);
10865 } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
10866 return vm_intro_it(chan, vms);
10867 } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE syntax */
10868 return vm_intro_ja(chan, vms);
10869 } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
10870 return vm_intro_nl(chan, vms);
10871 } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
10872 return vm_intro_no(chan, vms);
10873 } else if (!strncasecmp(ast_channel_language(chan), "da", 2)) { /* DANISH syntax */
10874 return vm_intro_da(chan, vms);
10875 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
10876 return vm_intro_pl(chan, vms);
10877 } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* BRAZILIAN PORTUGUESE syntax */
10878 return vm_intro_pt_BR(chan, vms);
10879 } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE syntax */
10880 return vm_intro_pt(chan, vms);
10881 } else if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* RUSSIAN syntax */
10882 return vm_intro_multilang(chan, vms, "n");
10883 } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
10884 return vm_intro_se(chan, vms);
10885 } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* UKRAINIAN syntax */
10886 return vm_intro_multilang(chan, vms, "n");
10887 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
10888 return vm_intro_vi(chan, vms);
10889 } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
10890 return vm_intro_zh(chan, vms);
10891 } else { /* Default to ENGLISH */
10892 return vm_intro_en(chan, vms);
10893 }
10894}
10895
10896static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
10897{
10898 int res = 0;
10899 /* Play instructions and wait for new command */
10900 while (!res) {
10901 if (vms->starting) {
10902 if (vms->lastmsg > -1) {
10903 if (skipadvanced)
10904 res = ast_play_and_wait(chan, "vm-onefor-full");
10905 else
10906 res = ast_play_and_wait(chan, "vm-onefor");
10907 if (!res)
10908 res = vm_play_folder_name(chan, vms->vmbox);
10909 }
10910 if (!res) {
10911 if (skipadvanced)
10912 res = ast_play_and_wait(chan, "vm-opts-full");
10913 else
10914 res = ast_play_and_wait(chan, "vm-opts");
10915 }
10916 } else {
10917 /* Added for additional help */
10918 if (skipadvanced) {
10919 res = ast_play_and_wait(chan, "vm-onefor-full");
10920 if (!res)
10921 res = vm_play_folder_name(chan, vms->vmbox);
10922 res = ast_play_and_wait(chan, "vm-opts-full");
10923 }
10924 /* Logic:
10925 * If the current message is not the first OR
10926 * if we're listening to the first new message and there are
10927 * also urgent messages, then prompt for navigation to the
10928 * previous message
10929 */
10930 if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
10931 res = ast_play_and_wait(chan, "vm-prev");
10932 }
10933 if (!res && !skipadvanced)
10934 res = ast_play_and_wait(chan, "vm-advopts");
10935 if (!res)
10936 res = ast_play_and_wait(chan, "vm-repeat");
10937 /* Logic:
10938 * If we're not listening to the last message OR
10939 * we're listening to the last urgent message and there are
10940 * also new non-urgent messages, then prompt for navigation
10941 * to the next message
10942 */
10943 if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
10944 (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
10945 res = ast_play_and_wait(chan, "vm-next");
10946 }
10947 if (!res) {
10948 int curmsg_deleted;
10949#ifdef IMAP_STORAGE
10950 ast_mutex_lock(&vms->lock);
10951#endif
10952 curmsg_deleted = vms->deleted[vms->curmsg];
10953#ifdef IMAP_STORAGE
10954 ast_mutex_unlock(&vms->lock);
10955#endif
10956 if (!nodelete) {
10957 if (!curmsg_deleted) {
10958 res = ast_play_and_wait(chan, "vm-delete");
10959 } else {
10960 res = ast_play_and_wait(chan, "vm-undelete");
10961 }
10962 }
10963 if (!res) {
10964 res = ast_play_and_wait(chan, "vm-toforward");
10965 }
10966 if (!res) {
10967 res = ast_play_and_wait(chan, "vm-savemessage");
10968 }
10969 }
10970 }
10971 if (!res) {
10972 res = ast_play_and_wait(chan, "vm-helpexit");
10973 }
10974 if (!res)
10975 res = ast_waitfordigit(chan, 6000);
10976 if (!res) {
10977 vms->repeats++;
10978 if (vms->repeats > 2) {
10979 res = 't';
10980 }
10981 }
10982 }
10983 return res;
10984}
10985
10986static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
10987{
10988 int res = 0;
10989 /* Play instructions and wait for new command */
10990 while (!res) {
10991 if (vms->starting) {
10992 if (vms->lastmsg > -1) {
10993 res = vm_play_folder_name(chan, vms->vmbox);
10994 if (!res)
10995 res = ast_play_and_wait(chan, "jp-wa");
10996 if (!res)
10997 res = ast_play_and_wait(chan, "digits/1");
10998 if (!res)
10999 res = ast_play_and_wait(chan, "jp-wo");
11000 if (!res)
11001 res = ast_play_and_wait(chan, "silence/1");
11002 }
11003 if (!res)
11004 res = ast_play_and_wait(chan, "vm-opts");
11005 } else {
11006 /* Added for additional help */
11007 if (skipadvanced) {
11008 res = vm_play_folder_name(chan, vms->vmbox);
11009 if (!res)
11010 res = ast_play_and_wait(chan, "jp-wa");
11011 if (!res)
11012 res = ast_play_and_wait(chan, "digits/1");
11013 if (!res)
11014 res = ast_play_and_wait(chan, "jp-wo");
11015 if (!res)
11016 res = ast_play_and_wait(chan, "silence/1");
11017 res = ast_play_and_wait(chan, "vm-opts-full");
11018 }
11019 /* Logic:
11020 * If the current message is not the first OR
11021 * if we're listening to the first new message and there are
11022 * also urgent messages, then prompt for navigation to the
11023 * previous message
11024 */
11025 if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
11026 res = ast_play_and_wait(chan, "vm-prev");
11027 }
11028 if (!res && !skipadvanced)
11029 res = ast_play_and_wait(chan, "vm-advopts");
11030 if (!res)
11031 res = ast_play_and_wait(chan, "vm-repeat");
11032 /* Logic:
11033 * If we're not listening to the last message OR
11034 * we're listening to the last urgent message and there are
11035 * also new non-urgent messages, then prompt for navigation
11036 * to the next message
11037 */
11038 if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
11039 (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
11040 res = ast_play_and_wait(chan, "vm-next");
11041 }
11042 if (!res) {
11043 int curmsg_deleted;
11044#ifdef IMAP_STORAGE
11045 ast_mutex_lock(&vms->lock);
11046#endif
11047 curmsg_deleted = vms->deleted[vms->curmsg];
11048#ifdef IMAP_STORAGE
11049 ast_mutex_unlock(&vms->lock);
11050#endif
11051 if (!curmsg_deleted) {
11052 res = ast_play_and_wait(chan, "vm-delete");
11053 } else {
11054 res = ast_play_and_wait(chan, "vm-undelete");
11055 }
11056 if (!res) {
11057 res = ast_play_and_wait(chan, "vm-toforward");
11058 }
11059 if (!res) {
11060 res = ast_play_and_wait(chan, "vm-savemessage");
11061 }
11062 }
11063 }
11064
11065 if (!res) {
11066 res = ast_play_and_wait(chan, "vm-helpexit");
11067 }
11068 if (!res)
11069 res = ast_waitfordigit(chan, 6000);
11070 if (!res) {
11071 vms->repeats++;
11072 if (vms->repeats > 2) {
11073 res = 't';
11074 }
11075 }
11076
11077 }
11078
11079 return res;
11080}
11081
11082static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
11083{
11084 int res = 0;
11085 /* Play instructions and wait for new command */
11086 while (!res) {
11087 if (vms->lastmsg > -1) {
11088 res = ast_play_and_wait(chan, "vm-listen");
11089 if (!res)
11090 res = vm_play_folder_name(chan, vms->vmbox);
11091 if (!res)
11092 res = ast_play_and_wait(chan, "press");
11093 if (!res)
11094 res = ast_play_and_wait(chan, "digits/1");
11095 }
11096 if (!res)
11097 res = ast_play_and_wait(chan, "vm-opts");
11098 if (!res) {
11099 vms->starting = 0;
11100 return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11101 }
11102 }
11103 return res;
11104}
11105
11106static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
11107{
11108 if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
11109 return vm_instructions_ja(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11110 } else if (vms->starting && !strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
11111 return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11112 } else { /* Default to ENGLISH */
11113 return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11114 }
11115}
11116
11117static int vm_newuser_setup(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11118{
11119 int cmd = 0;
11120 int duration = 0;
11121 int tries = 0;
11122 char newpassword[80] = "";
11123 char newpassword2[80] = "";
11124 char prefile[PATH_MAX] = "";
11125 unsigned char buf[256];
11126 int bytes = 0;
11127
11128 ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
11129 if (ast_adsi_available(chan)) {
11130 bytes += adsi_logo(buf + bytes);
11131 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
11132 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11133 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11134 bytes += ast_adsi_voice_mode(buf + bytes, 0);
11136 }
11137
11138 /* If forcename is set, have the user record their name */
11139 if (ast_test_flag(vmu, VM_FORCENAME)) {
11140 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
11141 if (ast_fileexists(prefile, NULL, NULL) < 1) {
11142 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11143 if (cmd < 0 || cmd == 't' || cmd == '#')
11144 return cmd;
11145 }
11146 }
11147
11148 /* If forcegreetings is set, have the user record their greetings */
11149 if (ast_test_flag(vmu, VM_FORCEGREET)) {
11150 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
11151 if (ast_fileexists(prefile, NULL, NULL) < 1) {
11152 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11153 if (cmd < 0 || cmd == 't' || cmd == '#')
11154 return cmd;
11155 }
11156
11157 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
11158 if (ast_fileexists(prefile, NULL, NULL) < 1) {
11159 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11160 if (cmd < 0 || cmd == 't' || cmd == '#')
11161 return cmd;
11162 }
11163 }
11164
11165 /*
11166 * Change the password last since new users will be able to skip over any steps this one comes before
11167 * by hanging up and calling back to voicemail main since the password is used to verify new user status.
11168 */
11169 for (;;) {
11170 newpassword[1] = '\0';
11171 newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
11172 if (cmd == '#')
11173 newpassword[0] = '\0';
11174 if (cmd < 0 || cmd == 't' || cmd == '#')
11175 return cmd;
11176 cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
11177 if (cmd < 0 || cmd == 't' || cmd == '#')
11178 return cmd;
11179 cmd = check_password(vmu, newpassword); /* perform password validation */
11180 if (cmd != 0) {
11181 ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
11183 } else {
11184 newpassword2[1] = '\0';
11185 newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
11186 if (cmd == '#')
11187 newpassword2[0] = '\0';
11188 if (cmd < 0 || cmd == 't' || cmd == '#')
11189 return cmd;
11190 cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
11191 if (cmd < 0 || cmd == 't' || cmd == '#')
11192 return cmd;
11193 if (!strcmp(newpassword, newpassword2))
11194 break;
11195 ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
11196 cmd = ast_play_and_wait(chan, vm_mismatch);
11197 }
11198 if (++tries == 3)
11199 return -1;
11200 if (cmd != 0) {
11202 }
11203 }
11205 vm_change_password(vmu, newpassword);
11207 vm_change_password_shell(vmu, newpassword);
11208
11209 ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
11210 cmd = ast_play_and_wait(chan, vm_passchanged);
11211
11212 return cmd;
11213}
11214
11215static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11216{
11217 int cmd = 0;
11218 int retries = 0;
11219 int duration = 0;
11220 char newpassword[80] = "";
11221 char newpassword2[80] = "";
11222 char prefile[PATH_MAX] = "";
11223 unsigned char buf[256];
11224 int bytes = 0;
11225 SCOPE_ENTER(3, "%s: %s entering mailbox options", ast_channel_name(chan), vms->username);
11226
11227 ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
11228 if (ast_adsi_available(chan)) {
11229 bytes += adsi_logo(buf + bytes);
11230 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
11231 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11232 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11233 bytes += ast_adsi_voice_mode(buf + bytes, 0);
11235 }
11236 while ((cmd >= 0) && (cmd != 't')) {
11237 if (cmd)
11238 retries = 0;
11239 switch (cmd) {
11240 case '1': /* Record your unavailable message */
11241 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
11242 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);
11243 break;
11244 case '2': /* Record your busy message */
11245 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
11246 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);
11247 break;
11248 case '3': /* Record greeting */
11249 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
11250 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);
11251 break;
11252 case '4': /* manage the temporary greeting */
11253 cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
11254 break;
11255 case '5': /* change password */
11256 if (vmu->password[0] == '-') {
11257 cmd = ast_play_and_wait(chan, "vm-no");
11258 break;
11259 }
11260 newpassword[1] = '\0';
11261 newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
11262 if (cmd == '#')
11263 newpassword[0] = '\0';
11264 else {
11265 if (cmd < 0)
11266 break;
11267 if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
11268 break;
11269 }
11270 }
11271 cmd = check_password(vmu, newpassword); /* perform password validation */
11272 if (cmd != 0) {
11273 ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
11275 if (!cmd) {
11277 }
11278 break;
11279 }
11280 newpassword2[1] = '\0';
11281 newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
11282 if (cmd == '#')
11283 newpassword2[0] = '\0';
11284 else {
11285 if (cmd < 0)
11286 break;
11287
11288 if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
11289 break;
11290 }
11291 }
11292 if (strcmp(newpassword, newpassword2)) {
11293 ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
11294 cmd = ast_play_and_wait(chan, vm_mismatch);
11295 if (!cmd) {
11297 }
11298 break;
11299 }
11300
11302 vm_change_password(vmu, newpassword);
11303 }
11305 vm_change_password_shell(vmu, newpassword);
11306 }
11307
11308 ast_debug(1, "User %s set password to %s of length %d\n",
11309 vms->username, newpassword, (int) strlen(newpassword));
11310 cmd = ast_play_and_wait(chan, vm_passchanged);
11311 break;
11312 case '*':
11313 cmd = 't';
11314 break;
11315 default:
11316 cmd = 0;
11317 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
11318 SCOPE_CALL(-1, RETRIEVE, prefile, -1, vmu->mailbox, vmu->context);
11319 if (ast_fileexists(prefile, NULL, NULL)) {
11320 cmd = ast_play_and_wait(chan, "vm-tmpexists");
11321 }
11322 SCOPE_CALL(-1, DISPOSE, prefile, -1);
11323 if (!cmd) {
11324 cmd = ast_play_and_wait(chan, "vm-options");
11325 }
11326 if (!cmd) {
11327 cmd = ast_waitfordigit(chan, 6000);
11328 }
11329 if (!cmd) {
11330 retries++;
11331 }
11332 if (retries > 3) {
11333 cmd = 't';
11334 }
11335 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
11336 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
11337 }
11338 }
11339 if (cmd == 't')
11340 cmd = 0;
11341 SCOPE_EXIT_RTN_VALUE(cmd, "Done\n");
11342}
11343
11344/*!
11345 * \brief The handler for 'record a temporary greeting'.
11346 * \param chan
11347 * \param vmu
11348 * \param vms
11349 * \param fmtc
11350 * \param record_gain
11351 *
11352 * This is option 4 from the mailbox options menu.
11353 * This function manages the following promptings:
11354 * 1: play / record / review the temporary greeting. : invokes play_record_review().
11355 * 2: remove (delete) the temporary greeting.
11356 * *: return to the main menu.
11357 *
11358 * \return zero on success, -1 on error.
11359 */
11360static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11361{
11362 int cmd = 0;
11363 int retries = 0;
11364 int duration = 0;
11365 char prefile[PATH_MAX] = "";
11366 unsigned char buf[256];
11367 int bytes = 0;
11368
11369 if (ast_adsi_available(chan)) {
11370 bytes += adsi_logo(buf + bytes);
11371 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
11372 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11373 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11374 bytes += ast_adsi_voice_mode(buf + bytes, 0);
11376 }
11377
11378 ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
11379 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
11380 while ((cmd >= 0) && (cmd != 't')) {
11381 if (cmd)
11382 retries = 0;
11383 RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
11384 if (ast_fileexists(prefile, NULL, NULL) <= 0) {
11385 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11386 if (cmd == -1) {
11387 break;
11388 }
11389 cmd = 't';
11390 } else {
11391 switch (cmd) {
11392 case '1':
11393 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11394 break;
11395 case '2':
11396 DELETE(prefile, -1, prefile, vmu);
11397 ast_play_and_wait(chan, "vm-tempremoved");
11398 cmd = 't';
11399 break;
11400 case '*':
11401 cmd = 't';
11402 break;
11403 default:
11404 cmd = ast_play_and_wait(chan,
11405 ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
11406 "vm-tempgreeting2" : "vm-tempgreeting");
11407 if (!cmd) {
11408 cmd = ast_waitfordigit(chan, 6000);
11409 }
11410 if (!cmd) {
11411 retries++;
11412 }
11413 if (retries > 3) {
11414 cmd = 't';
11415 }
11416 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
11417 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
11418 }
11419 }
11420 DISPOSE(prefile, -1);
11421 }
11422 if (cmd == 't')
11423 cmd = 0;
11424 return cmd;
11425}
11426
11427/*!
11428 * \brief Greek syntax for 'You have N messages' greeting.
11429 * \param chan
11430 * \param vms
11431 * \param vmu
11432 *
11433 * \return zero on success, -1 on error.
11434 */
11435static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11436{
11437 int cmd = 0;
11438
11439 if (vms->lastmsg > -1) {
11440 cmd = play_message(chan, vmu, vms);
11441 } else {
11442 cmd = ast_play_and_wait(chan, "vm-youhaveno");
11443 if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
11444 if (!cmd) {
11445 snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
11446 cmd = ast_play_and_wait(chan, vms->fn);
11447 }
11448 if (!cmd)
11449 cmd = ast_play_and_wait(chan, "vm-messages");
11450 } else {
11451 if (!cmd)
11452 cmd = ast_play_and_wait(chan, "vm-messages");
11453 if (!cmd) {
11454 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11455 cmd = ast_play_and_wait(chan, vms->fn);
11456 }
11457 }
11458 }
11459 return cmd;
11460}
11461
11462/* Hebrew Syntax */
11463static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11464{
11465 int cmd = 0;
11466
11467 if (vms->lastmsg > -1) {
11468 cmd = play_message(chan, vmu, vms);
11469 } else {
11470 if (!strcasecmp(vms->fn, "INBOX")) {
11471 cmd = ast_play_and_wait(chan, "vm-nonewmessages");
11472 } else {
11473 cmd = ast_play_and_wait(chan, "vm-nomessages");
11474 }
11475 }
11476 return cmd;
11477}
11478
11479/*!
11480 * \brief Default English syntax for 'You have N messages' greeting.
11481 * \param chan
11482 * \param vms
11483 * \param vmu
11484 *
11485 * \return zero on success, -1 on error.
11486 */
11487static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11488{
11489 int cmd = 0;
11490
11491 if (vms->lastmsg > -1) {
11492 cmd = play_message(chan, vmu, vms);
11493 } else {
11494 cmd = ast_play_and_wait(chan, "vm-youhave");
11495 if (!cmd)
11496 cmd = ast_play_and_wait(chan, "vm-no");
11497 if (!cmd) {
11498 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", 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 }
11504 return cmd;
11505}
11506
11507/*!
11508 *\brief Italian syntax for 'You have N messages' greeting.
11509 * \param chan
11510 * \param vms
11511 * \param vmu
11512 *
11513 * \return zero on success, -1 on error.
11514 */
11515static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11516{
11517 int cmd;
11518
11519 if (vms->lastmsg > -1) {
11520 cmd = play_message(chan, vmu, vms);
11521 } else {
11522 cmd = ast_play_and_wait(chan, "vm-no");
11523 if (!cmd)
11524 cmd = ast_play_and_wait(chan, "vm-message");
11525 if (!cmd) {
11526 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11527 cmd = ast_play_and_wait(chan, vms->fn);
11528 }
11529 }
11530 return cmd;
11531}
11532
11533/*!
11534 * \brief Japanese syntax for 'You have N messages' greeting.
11535 * \param chan
11536 * \param vms
11537 * \param vmu
11538 *
11539 * \return zero on success, -1 on error.
11540 */
11541static int vm_browse_messages_ja(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11542{
11543 int cmd = 0;
11544
11545 if (vms->lastmsg > -1) {
11546 cmd = play_message(chan, vmu, vms);
11547 } else {
11548 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11549 cmd = ast_play_and_wait(chan, vms->fn);
11550 if (!cmd)
11551 cmd = ast_play_and_wait(chan, "vm-messages");
11552 if (!cmd)
11553 cmd = ast_play_and_wait(chan, "jp-wa");
11554 if (!cmd)
11555 cmd = ast_play_and_wait(chan, "jp-arimasen");
11556 }
11557 return cmd;
11558}
11559
11560/*!
11561 * \brief Spanish 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_es(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-youhaveno");
11576 if (!cmd)
11577 cmd = ast_play_and_wait(chan, "vm-messages");
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 Portuguese 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_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11595{
11596 int cmd;
11597
11598 if (vms->lastmsg > -1) {
11599 cmd = play_message(chan, vmu, vms);
11600 } else {
11601 cmd = ast_play_and_wait(chan, "vm-no");
11602 if (!cmd) {
11603 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11604 cmd = ast_play_and_wait(chan, vms->fn);
11605 }
11606 if (!cmd)
11607 cmd = ast_play_and_wait(chan, "vm-messages");
11608 }
11609 return cmd;
11610}
11611
11612/*!
11613 * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
11614 * \param chan
11615 * \param vms
11616 * \param vmu
11617 *
11618 * \return zero on success, -1 on error.
11619 */
11620static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11621{
11622 int cmd;
11623
11624 if (vms->lastmsg > -1) {
11625 cmd = play_message(chan, vmu, vms);
11626 } else {
11627 cmd = ast_play_and_wait(chan, "vm-you");
11628 if (!cmd)
11629 cmd = ast_play_and_wait(chan, "vm-haveno");
11630 if (!cmd)
11631 cmd = ast_play_and_wait(chan, "vm-messages");
11632 if (!cmd) {
11633 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11634 cmd = ast_play_and_wait(chan, vms->fn);
11635 }
11636 }
11637 return cmd;
11638}
11639
11640/*!
11641 * \brief Vietnamese syntax for 'You have N messages' greeting.
11642 * \param chan
11643 * \param vms
11644 * \param vmu
11645 *
11646 * \return zero on success, -1 on error.
11647 */
11648static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11649{
11650 int cmd = 0;
11651
11652 if (vms->lastmsg > -1) {
11653 cmd = play_message(chan, vmu, vms);
11654 } else {
11655 cmd = ast_play_and_wait(chan, "vm-no");
11656 if (!cmd) {
11657 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11658 cmd = ast_play_and_wait(chan, vms->fn);
11659 }
11660 }
11661 return cmd;
11662}
11663
11664/*!
11665 * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
11666 * \param chan The channel for the current user. We read the language property from this.
11667 * \param vms passed into the language-specific vm_browse_messages function.
11668 * \param vmu passed into the language-specific vm_browse_messages function.
11669 *
11670 * The method to be invoked is determined by the value of language code property in the user's channel.
11671 * The default (when unable to match) is to use english.
11672 *
11673 * \return zero on success, -1 on error.
11674 */
11675static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11676{
11677 if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH */
11678 return vm_browse_messages_es(chan, vms, vmu);
11679 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK */
11680 return vm_browse_messages_gr(chan, vms, vmu);
11681 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW */
11682 return vm_browse_messages_he(chan, vms, vmu);
11683 } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN */
11684 return vm_browse_messages_it(chan, vms, vmu);
11685 } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE */
11686 return vm_browse_messages_ja(chan, vms, vmu);
11687 } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE */
11688 return vm_browse_messages_pt(chan, vms, vmu);
11689 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE */
11690 return vm_browse_messages_vi(chan, vms, vmu);
11691 } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) */
11692 return vm_browse_messages_zh(chan, vms, vmu);
11693 } else { /* Default to English syntax */
11694 return vm_browse_messages_en(chan, vms, vmu);
11695 }
11696}
11697
11698static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
11699 struct ast_vm_user *res_vmu, const char *context, const char *prefix,
11700 int skipuser, int max_logins, int silent)
11701{
11702 int useadsi = 0, valid = 0, logretries = 0;
11703 char password[AST_MAX_EXTENSION], *passptr = NULL;
11704 struct ast_vm_user vmus, *vmu = NULL;
11705
11706 /* If ADSI is supported, setup login screen */
11707 adsi_begin(chan, &useadsi);
11708 if (!skipuser && useadsi)
11709 adsi_login(chan);
11710 if (!silent && !skipuser && ast_streamfile(chan, vm_login, ast_channel_language(chan))) {
11711 ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
11712 return -1;
11713 }
11714
11715 /* Authenticate them and get their mailbox/password */
11716
11717 while (!valid && (logretries < max_logins)) {
11718 /* Prompt for, and read in the username */
11719 if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
11720 ast_log(AST_LOG_WARNING, "Couldn't read username\n");
11721 return -1;
11722 }
11723 if (ast_strlen_zero(mailbox)) {
11724 if (ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str) {
11725 ast_copy_string(mailbox, ast_channel_caller(chan)->id.number.str, mailbox_size);
11726 } else {
11727 ast_verb(3, "Username not entered\n");
11728 return -1;
11729 }
11730 } else if (mailbox[0] == '*') {
11731 /* user entered '*' */
11732 ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
11733 if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
11734 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
11735 return -1;
11736 }
11737 ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
11738 mailbox[0] = '\0';
11739 }
11740
11741 if (useadsi)
11742 adsi_password(chan);
11743
11744 if (!ast_strlen_zero(prefix)) {
11745 char fullusername[80];
11746
11747 ast_copy_string(fullusername, prefix, sizeof(fullusername));
11748 strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
11749 ast_copy_string(mailbox, fullusername, mailbox_size);
11750 }
11751
11752 ast_debug(1, "Before find user for mailbox %s\n", mailbox);
11753 memset(&vmus, 0, sizeof(vmus));
11754 vmu = find_user(&vmus, context, mailbox);
11755 if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
11756 /* saved password is blank, so don't bother asking */
11757 password[0] = '\0';
11758 } else {
11760 if (!ast_check_hangup(chan)) {
11761 ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
11762 }
11763 free_user(vmu);
11764 return -1;
11765 }
11766 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
11767 ast_log(AST_LOG_NOTICE, "Unable to read password\n");
11768 free_user(vmu);
11769 return -1;
11770 } else if (password[0] == '*') {
11771 /* user entered '*' */
11772 ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
11773 if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
11774 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
11775 mailbox[0] = '*';
11776 free_user(vmu);
11777 return -1;
11778 }
11779 ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
11780 mailbox[0] = '\0';
11781 /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
11782 free_user(vmu);
11783 vmu = NULL;
11784 }
11785 }
11786
11787 if (vmu) {
11788 passptr = vmu->password;
11789 if (passptr[0] == '-') passptr++;
11790 }
11791 if (vmu && !strcmp(passptr, password))
11792 valid++;
11793 else {
11794 ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
11795 if (!ast_strlen_zero(prefix))
11796 mailbox[0] = '\0';
11797 }
11798 logretries++;
11799 if (!valid) {
11800 if (skipuser || logretries >= max_logins) {
11801 if (ast_streamfile(chan, "vm-incorrect", ast_channel_language(chan))) {
11802 ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
11803 free_user(vmu);
11804 return -1;
11805 }
11806 if (ast_waitstream(chan, "")) { /* Channel is hung up */
11807 free_user(vmu);
11808 return -1;
11809 }
11810 } else {
11811 if (useadsi)
11812 adsi_login(chan);
11813 if (ast_streamfile(chan, "vm-incorrect-mailbox", ast_channel_language(chan))) {
11814 ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
11815 free_user(vmu);
11816 return -1;
11817 }
11818 }
11819 }
11820 }
11821 if (!valid && (logretries >= max_logins)) {
11822 ast_stopstream(chan);
11823 ast_play_and_wait(chan, "vm-goodbye");
11824 free_user(vmu);
11825 return -1;
11826 }
11827 if (vmu && !skipuser) {
11828 *res_vmu = *vmu;
11829 }
11830 return 0;
11831}
11832
11834 struct ast_vm_user *vmu,
11835 struct vm_state *vms,
11836 const char *msg_id)
11837{
11838 if (message_range_and_existence_check(vms, &msg_id, 1, &vms->curmsg, vmu)) {
11839 return -1;
11840 }
11841 /* Found the msg, so play it back */
11842
11843 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
11844
11845#ifdef IMAP_STORAGE
11846 /*IMAP storage stores any prepended message from a forward
11847 * as a separate file from the rest of the message
11848 */
11849 if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
11850 wait_file(chan, vms, vms->introfn);
11851 }
11852#endif
11853 RETRIEVE(vms->curdir,vms->curmsg,vmu->mailbox, vmu->context);
11854
11855 if ((wait_file(chan, vms, vms->fn)) < 0) {
11856 ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
11857 } else {
11858#ifdef IMAP_STORAGE
11859 ast_mutex_lock(&vms->lock);
11860#endif
11861 vms->heard[vms->curmsg] = 1;
11862#ifdef IMAP_STORAGE
11863 ast_mutex_unlock(&vms->lock);
11864#endif
11865 }
11866 DISPOSE(vms->curdir, vms->curmsg);
11867 return 0;
11868}
11869
11870/*!
11871 * \brief Finds a message in a specific mailbox by msg_id and plays it to the channel
11872 *
11873 * \retval 0 Success
11874 * \retval -1 Failure
11875 */
11876static int play_message_by_id(struct ast_channel *chan, const char *mailbox, const char *context, const char *msg_id)
11877{
11878 struct vm_state vms;
11879 struct ast_vm_user *vmu = NULL, vmus;
11880 int res = 0;
11881 int open = 0;
11882 int played = 0;
11883 int i;
11884
11885 memset(&vmus, 0, sizeof(vmus));
11886 memset(&vms, 0, sizeof(vms));
11887
11888 if (!(vmu = find_user(&vmus, context, mailbox))) {
11889 goto play_msg_cleanup;
11890 }
11891
11892 /* Iterate through every folder, find the msg, and play it */
11893 for (i = 0; i < ARRAY_LEN(mailbox_folders) && !played; i++) {
11894 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
11895 vms.lastmsg = -1;
11896
11897 /* open the mailbox state */
11898 if ((res = open_mailbox(&vms, vmu, i)) < 0) {
11899 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
11900 res = -1;
11901 goto play_msg_cleanup;
11902 }
11903 open = 1;
11904
11905 /* play msg if it exists in this mailbox */
11906 if ((vms.lastmsg != -1) && !(play_message_by_id_helper(chan, vmu, &vms, msg_id))) {
11907 played = 1;
11908 }
11909
11910 /* close mailbox */
11911 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
11912 res = -1;
11913 goto play_msg_cleanup;
11914 }
11915 open = 0;
11916 }
11917
11918play_msg_cleanup:
11919 if (!played) {
11920 res = -1;
11921 }
11922
11923 if (vmu && open) {
11924 close_mailbox(&vms, vmu);
11925 }
11926
11927#ifdef IMAP_STORAGE
11928 if (vmu) {
11929 vmstate_delete(&vms);
11930 }
11931#endif
11932
11933 free_user(vmu);
11934
11935 return res;
11936}
11937
11938static int vm_playmsgexec(struct ast_channel *chan, const char *data)
11939{
11940 char *parse;
11941 char *mailbox = NULL;
11942 char *context = NULL;
11943 int res;
11944
11947 AST_APP_ARG(msg_id);
11948 );
11949
11950 if (ast_channel_state(chan) != AST_STATE_UP) {
11951 ast_debug(1, "Before ast_answer\n");
11952 ast_answer(chan);
11953 }
11954
11955 if (ast_strlen_zero(data)) {
11956 return -1;
11957 }
11958
11959 parse = ast_strdupa(data);
11961
11962 if (ast_strlen_zero(args.mailbox) || ast_strlen_zero(args.msg_id)) {
11963 return -1;
11964 }
11965
11966 if ((context = strchr(args.mailbox, '@'))) {
11967 *context++ = '\0';
11968 }
11969 mailbox = args.mailbox;
11970
11971 res = play_message_by_id(chan, mailbox, context, args.msg_id);
11972 pbx_builtin_setvar_helper(chan, "VOICEMAIL_PLAYBACKSTATUS", res ? "FAILED" : "SUCCESS");
11973
11974 return 0;
11975}
11976
11978{
11979#define VMBOX_STRING_HEADER_FORMAT "%-32.32s %-32.32s %-16.16s %-16.16s %-16.16s %-16.16s\n"
11980#define VMBOX_STRING_DATA_FORMAT "%-32.32s %-32.32s %-16.16s %-16.16s %-16.16s %-16.16s\n"
11981
11982 const char *mailbox = a->argv[3];
11983 const char *context = a->argv[4];
11984 struct vm_state vms;
11985 struct ast_vm_user *vmu = NULL, vmus;
11986 memset(&vmus, 0, sizeof(vmus));
11987 memset(&vms, 0, sizeof(vms));
11988
11989 if (!(vmu = find_user(&vmus, context, mailbox))) {
11990 ast_cli(a->fd, "Can't find voicemail user %s@%s\n", mailbox, context);
11991 return -1;
11992 }
11993
11994 ast_cli(a->fd, VMBOX_STRING_HEADER_FORMAT, "Full Name", "Email", "Pager", "Language", "Locale", "Time Zone");
11995 ast_cli(a->fd, VMBOX_STRING_DATA_FORMAT, vmu->fullname, vmu->email, vmu->pager, vmu->language, vmu->locale, vmu->zonetag);
11996
11997 return 0;
11998}
11999
12001{
12002#define VM_STRING_HEADER_FORMAT "%-8.8s %-32.32s %-32.32s %-9.9s %-6.6s %-30.30s\n"
12003 const char *mailbox = a->argv[3];
12004 const char *context = a->argv[4];
12005 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
12006 struct ast_vm_msg_snapshot *msg;
12007 int i;
12008
12009 /* Take a snapshot of the mailbox and walk through each folder's contents */
12011 if (!mailbox_snapshot) {
12012 ast_cli(a->fd, "Can't create snapshot for voicemail user %s@%s\n", mailbox, context);
12013 return -1;
12014 }
12015
12016 ast_cli(a->fd, VM_STRING_HEADER_FORMAT, "Folder", "Caller ID", "Date", "Duration", "Flag", "ID");
12017
12018 for (i = 0; i < mailbox_snapshot->folders; i++) {
12019 AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
12020 ast_cli(a->fd, VM_STRING_HEADER_FORMAT, msg->folder_name, msg->callerid, msg->origdate, msg->duration,
12021 msg->flag, msg->msg_id);
12022 }
12023 }
12024
12025 ast_cli(a->fd, "%d Message%s Total\n", mailbox_snapshot->total_msg_num, ESS(mailbox_snapshot->total_msg_num));
12026 /* done, destroy. */
12027 mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
12028
12029 return 0;
12030}
12031
12033{
12034 if (show_mailbox_details(a)){
12035 return -1;
12036 }
12037 ast_cli(a->fd, "\n");
12038 return show_mailbox_snapshot(a);
12039}
12040
12042{
12043 const char *from_mailbox = a->argv[2];
12044 const char *from_context = a->argv[3];
12045 const char *from_folder = a->argv[4];
12046 const char *id[] = { a->argv[5] };
12047 const char *to_mailbox = a->argv[6];
12048 const char *to_context = a->argv[7];
12049 const char *to_folder = a->argv[8];
12050 int ret = vm_msg_forward(from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder, 1, id, 0);
12051 if (ret) {
12052 ast_cli(a->fd, "Error forwarding message %s from mailbox %s@%s %s to mailbox %s@%s %s\n",
12053 id[0], from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder);
12054 } else {
12055 ast_cli(a->fd, "Forwarded message %s from mailbox %s@%s %s to mailbox %s@%s %s\n",
12056 id[0], from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder);
12057 }
12058 return ret;
12059}
12060
12062{
12063 const char *mailbox = a->argv[2];
12064 const char *context = a->argv[3];
12065 const char *from_folder = a->argv[4];
12066 const char *id[] = { a->argv[5] };
12067 const char *to_folder = a->argv[6];
12068 int ret = vm_msg_move(mailbox, context, 1, from_folder, id, to_folder);
12069 if (ret) {
12070 ast_cli(a->fd, "Error moving message %s from mailbox %s@%s %s to %s\n",
12071 id[0], mailbox, context, from_folder, to_folder);
12072 } else {
12073 ast_cli(a->fd, "Moved message %s from mailbox %s@%s %s to %s\n",
12074 id[0], mailbox, context, from_folder, to_folder);
12075 }
12076 return ret;
12077}
12078
12080{
12081 const char *mailbox = a->argv[2];
12082 const char *context = a->argv[3];
12083 const char *folder = a->argv[4];
12084 const char *id[] = { a->argv[5] };
12085 int ret = vm_msg_remove(mailbox, context, 1, folder, id);
12086 if (ret) {
12087 ast_cli(a->fd, "Error removing message %s from mailbox %s@%s %s\n",
12088 id[0], mailbox, context, folder);
12089 } else {
12090 ast_cli(a->fd, "Removed message %s from mailbox %s@%s %s\n",
12091 id[0], mailbox, context, folder);
12092 }
12093 return ret;
12094}
12095
12097{
12098 const char *word = a->word;
12099 int pos = a->pos;
12100 int state = a->n;
12101 int which = 0;
12102 int wordlen;
12103 struct ast_vm_user *vmu;
12104 const char *context = "", *mailbox = "";
12105 char *ret = NULL;
12106
12107 /* 0 - voicemail; 1 - show; 2 - mailbox; 3 - <mailbox>; 4 - <context> */
12108 if (pos == 3) {
12109 wordlen = strlen(word);
12111 AST_LIST_TRAVERSE(&users, vmu, list) {
12112 if (!strncasecmp(word, vmu->mailbox , wordlen)) {
12113 if (mailbox && strcmp(mailbox, vmu->mailbox) && ++which > state) {
12114 ret = ast_strdup(vmu->mailbox);
12116 return ret;
12117 }
12118 mailbox = vmu->mailbox;
12119 }
12120 }
12122 } else if (pos == 4) {
12123 /* Only display contexts that match the user in pos 3 */
12124 const char *box = a->argv[3];
12125 wordlen = strlen(word);
12127 AST_LIST_TRAVERSE(&users, vmu, list) {
12128 if (!strncasecmp(word, vmu->context, wordlen) && !strcasecmp(box, vmu->mailbox)) {
12129 if (context && strcmp(context, vmu->context) && ++which > state) {
12130 ret = ast_strdup(vmu->context);
12132 return ret;
12133 }
12134 context = vmu->context;
12135 }
12136 }
12138 }
12139
12140 return ret;
12141}
12142
12143static char *handle_voicemail_show_mailbox(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12144{
12145 switch (cmd) {
12146 case CLI_INIT:
12147 e->command = "voicemail show mailbox";
12148 e->usage =
12149 "Usage: voicemail show mailbox <mailbox> <context>\n"
12150 " Show contents of mailbox <mailbox>@<context>\n";
12151 return NULL;
12152 case CLI_GENERATE:
12154 case CLI_HANDLER:
12155 break;
12156 }
12157
12158 if (a->argc != 5) {
12159 return CLI_SHOWUSAGE;
12160 }
12161
12163 return CLI_FAILURE;
12164 }
12165
12166 return CLI_SUCCESS;
12167}
12168
12169/* Handles filling in data for one of the following three formats (based on maxpos = 5|6|8):
12170
12171 maxpos = 5
12172 0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12173 maxpos = 6
12174 0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12175 6 - <to_folder>;
12176 maxpos = 8
12177 0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12178 6 - <to_mailbox>; 7 - <to_context>; 8 - <to_folder>;
12179
12180 Passing in the maximum expected position 'maxpos' helps us fill in the missing entries in one function
12181 instead of three by taking advantage of the overlap in the command sequence between forward, move and
12182 remove as each of these use nearly the same syntax up until their maximum number of arguments.
12183 The value of pos = 6 changes to be either <messageid> or <folder> based on maxpos being 6 or 8.
12184*/
12185
12186static char *complete_voicemail_move_message(struct ast_cli_args *a, int maxpos)
12187{
12188 const char *word = a->word;
12189 int pos = a->pos;
12190 int state = a->n;
12191 int which = 0;
12192 int wordlen;
12193 struct ast_vm_user *vmu;
12194 const char *context = "", *mailbox = "", *folder = "", *id = "";
12195 char *ret = NULL;
12196
12197 if (pos > maxpos) {
12198 /* If the passed in pos is above the max, return NULL to avoid 'over-filling' the cli */
12199 return NULL;
12200 }
12201
12202 /* if we are in pos 2 or pos 6 in 'forward' mode */
12203 if (pos == 2 || (pos == 6 && maxpos == 8)) {
12204 /* find users */
12205 wordlen = strlen(word);
12207 AST_LIST_TRAVERSE(&users, vmu, list) {
12208 if (!strncasecmp(word, vmu->mailbox , wordlen)) {
12209 if (mailbox && strcmp(mailbox, vmu->mailbox) && ++which > state) {
12210 ret = ast_strdup(vmu->mailbox);
12212 return ret;
12213 }
12214 mailbox = vmu->mailbox;
12215 }
12216 }
12218 } else if (pos == 3 || pos == 7) {
12219 /* find contexts that match the user */
12220 mailbox = (pos == 3) ? a->argv[2] : a->argv[6];
12221 wordlen = strlen(word);
12223 AST_LIST_TRAVERSE(&users, vmu, list) {
12224 if (!strncasecmp(word, vmu->context, wordlen) && !strcasecmp(mailbox, vmu->mailbox)) {
12225 if (context && strcmp(context, vmu->context) && ++which > state) {
12226 ret = ast_strdup(vmu->context);
12228 return ret;
12229 }
12230 context = vmu->context;
12231 }
12232 }
12234 } else if (pos == 4 || pos == 8 || (pos == 6 && maxpos == 6) ) {
12235 int i;
12236 /* Walk through the standard folders */
12237 wordlen = strlen(word);
12238 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
12239 if (folder && !strncasecmp(word, mailbox_folders[i], wordlen) && ++which > state) {
12240 return ast_strdup(mailbox_folders[i]);
12241 }
12242 folder = mailbox_folders[i];
12243 }
12244 } else if (pos == 5) {
12245 /* find messages in the folder */
12246 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
12247 struct ast_vm_msg_snapshot *msg;
12248 mailbox = a->argv[2];
12249 context = a->argv[3];
12250 folder = a->argv[4];
12251 wordlen = strlen(word);
12252
12253 /* Take a snapshot of the mailbox and snag the individual info */
12254 if ((mailbox_snapshot = ast_vm_mailbox_snapshot_create(mailbox, context, folder, 0, AST_VM_SNAPSHOT_SORT_BY_ID, 0))) {
12255 int i;
12256 /* we are only requesting the one folder, but we still need to know it's index */
12257 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
12258 if (!strcasecmp(mailbox_folders[i], folder)) {
12259 break;
12260 }
12261 }
12262 AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
12263 if (id && !strncasecmp(word, msg->msg_id, wordlen) && ++which > state) {
12264 ret = ast_strdup(msg->msg_id);
12265 break;
12266 }
12267 id = msg->msg_id;
12268 }
12269 /* done, destroy. */
12270 mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
12271 }
12272 }
12273
12274 return ret;
12275}
12276
12277static char *handle_voicemail_forward_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12278{
12279 switch (cmd) {
12280 case CLI_INIT:
12281 e->command = "voicemail forward";
12282 e->usage =
12283 "Usage: voicemail forward <from_mailbox> <from_context> <from_folder> <messageid> <to_mailbox> <to_context> <to_folder>\n"
12284 " Forward message <messageid> in mailbox <mailbox>@<context> <from_folder>\n"
12285 " to mailbox <mailbox>@<context> <to_folder>\n";
12286 return NULL;
12287 case CLI_GENERATE:
12289 case CLI_HANDLER:
12290 break;
12291 }
12292
12293 if (a->argc != 9) {
12294 return CLI_SHOWUSAGE;
12295 }
12296
12298 return CLI_FAILURE;
12299 }
12300
12301 return CLI_SUCCESS;
12302}
12303
12304static char *handle_voicemail_move_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12305{
12306 switch (cmd) {
12307 case CLI_INIT:
12308 e->command = "voicemail move";
12309 e->usage =
12310 "Usage: voicemail move <mailbox> <context> <from_folder> <messageid> <to_folder>\n"
12311 " Move message <messageid> in mailbox <mailbox>&<context> from <from_folder> to <to_folder>\n";
12312 return NULL;
12313 case CLI_GENERATE:
12315 case CLI_HANDLER:
12316 break;
12317 }
12318
12319 if (a->argc != 7) {
12320 return CLI_SHOWUSAGE;
12321 }
12322
12324 return CLI_FAILURE;
12325 }
12326
12327 return CLI_SUCCESS;
12328}
12329
12330static char *handle_voicemail_remove_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 remove";
12335 e->usage =
12336 "Usage: voicemail remove <mailbox> <context> <from_folder> <messageid>\n"
12337 " Remove message <messageid> from <from_folder> in mailbox <mailbox>@<context>\n";
12338 return NULL;
12339 case CLI_GENERATE:
12341 case CLI_HANDLER:
12342 break;
12343 }
12344
12345 if (a->argc != 6) {
12346 return CLI_SHOWUSAGE;
12347 }
12348
12350 return CLI_FAILURE;
12351 }
12352
12353 return CLI_SUCCESS;
12354}
12355
12356static int vm_execmain(struct ast_channel *chan, const char *data)
12357{
12358 /* XXX This is, admittedly, some pretty horrendous code. For some
12359 reason it just seemed a lot easier to do with GOTO's. I feel
12360 like I'm back in my GWBASIC days. XXX */
12361 int res = -1;
12362 int cmd = 0;
12363 int valid = 0;
12364 char prefixstr[80] ="";
12365 char ext_context[256]="";
12366 int box;
12367 int useadsi = 0;
12368 int skipuser = 0;
12369 struct vm_state vms = {{0}};
12370 struct ast_vm_user *vmu = NULL, vmus = {{0}};
12371 char *context = NULL;
12372 int silentexit = 0;
12373 struct ast_flags flags = { 0 };
12374 signed char record_gain = 0;
12375 int play_auto = 0;
12376 int play_folder = 0;
12377 int in_urgent = 0;
12378 int nodelete = 0;
12379#ifdef IMAP_STORAGE
12380 int deleted = 0;
12381#endif
12382 SCOPE_ENTER(3, "%s:\n", ast_channel_name(chan));
12383
12384 /* Add the vm_state to the active list and keep it active */
12385 vms.lastmsg = -1;
12386
12387 ast_test_suite_event_notify("START", "Message: vm_execmain started");
12388 if (ast_channel_state(chan) != AST_STATE_UP) {
12389 ast_debug(1, "Before ast_answer\n");
12390 ast_answer(chan);
12391 }
12392
12393 if (!ast_strlen_zero(data)) {
12394 char *opts[OPT_ARG_ARRAY_SIZE];
12395 char *parse;
12397 AST_APP_ARG(argv0);
12398 AST_APP_ARG(argv1);
12399 );
12400
12401 parse = ast_strdupa(data);
12402
12404
12405 if (args.argc == 2) {
12406 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
12407 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid option string '%s'\n", args.argv1);
12408 }
12410 int gain;
12411 if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
12412 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
12413 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
12414 } else {
12415 record_gain = (signed char) gain;
12416 }
12417 } else {
12418 ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
12419 }
12420 }
12422 play_auto = 1;
12423 if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
12424 /* See if it is a folder name first */
12425 if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
12426 if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
12427 play_folder = -1;
12428 }
12429 } else {
12430 play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
12431 }
12432 } else {
12433 ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
12434 }
12435 if (play_folder > 9 || play_folder < 0) {
12437 "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
12438 opts[OPT_ARG_PLAYFOLDER]);
12439 play_folder = 0;
12440 }
12441 }
12443 nodelete = 1;
12444 }
12445 } else {
12446 /* old style options parsing */
12447 while (*(args.argv0)) {
12448 if (*(args.argv0) == 's')
12450 else if (*(args.argv0) == 'p')
12452 else
12453 break;
12454 (args.argv0)++;
12455 }
12456
12457 }
12458
12459 valid = ast_test_flag(&flags, OPT_SILENT);
12460
12461 if ((context = strchr(args.argv0, '@')))
12462 *context++ = '\0';
12463
12465 ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
12466 else
12467 ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
12468
12469 if (!ast_strlen_zero(vms.username)) {
12470 if ((vmu = find_user(&vmus, context ,vms.username))) {
12471 skipuser++;
12472 } else {
12473 ast_log(LOG_WARNING, "Mailbox '%s%s%s' doesn't exist\n", vms.username, context ? "@": "", context ? context : "");
12474 valid = 0;
12475 }
12476 } else {
12477 valid = 0;
12478 }
12479 }
12480
12481 if (!valid)
12482 res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
12483
12484 ast_trace(-1, "vm_authenticate user: %s\n", vms.username);
12485
12486 if (vms.username[0] == '*') {
12487 ast_trace(-1, "user pressed * in context '%s'\n", ast_channel_context(chan));
12488
12489 /* user entered '*' */
12490 if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
12491 ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
12492 res = 0; /* prevent hangup */
12493 goto out;
12494 }
12495 }
12496
12497 if (!res) {
12498 valid = 1;
12499 if (!skipuser)
12500 vmu = &vmus;
12501 } else {
12502 res = 0;
12503 }
12504
12505 /* If ADSI is supported, setup login screen */
12506 adsi_begin(chan, &useadsi);
12507
12508 if (!valid) {
12509 ast_trace(-1, "Invalid user\n");
12510 goto out;
12511 }
12512 ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
12513
12514#ifdef IMAP_STORAGE
12515 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
12516 pthread_setspecific(ts_vmstate.key, &vms);
12517
12518 vms.interactive = 1;
12519 vms.updated = 1;
12520 if (vmu)
12521 ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
12522 vmstate_insert(&vms);
12523 init_vm_state(&vms);
12524#endif
12525
12526 /* Set language from config to override channel language */
12527 if (!ast_strlen_zero(vmu->language)) {
12528 ast_channel_lock(chan);
12529 ast_channel_language_set(chan, vmu->language);
12530 ast_channel_unlock(chan);
12531 }
12532
12533 /* Retrieve urgent, old and new message counts */
12534 ast_trace(-1, "Before open_mailbox\n");
12535 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
12536 if (res < 0) {
12537 ast_trace(-1, "open mailbox: %d\n", res);
12538 goto out;
12539 }
12540 vms.oldmessages = vms.lastmsg + 1;
12541 ast_trace(-1, "Number of old messages: %d\n", vms.oldmessages);
12542 /* check INBOX */
12543 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12544 if (res < 0) {
12545 ast_trace(-1, "open mailbox: %d\n", res);
12546 goto out;
12547 }
12548 vms.newmessages = vms.lastmsg + 1;
12549 ast_trace(-1, "Number of new messages: %d\n", vms.newmessages);
12550 /* Start in Urgent */
12551 in_urgent = 1;
12552 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11); /*11 is the Urgent folder */
12553 if (res < 0) {
12554 ast_trace(-1, "open mailbox: %d\n", res);
12555 goto out;
12556 }
12557 vms.urgentmessages = vms.lastmsg + 1;
12558 ast_trace(-1, "Number of urgent messages: %d\n", vms.urgentmessages);
12559
12560 /* Select proper mailbox FIRST!! */
12561 if (play_auto) {
12562 ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
12563 if (vms.urgentmessages) {
12564 in_urgent = 1;
12565 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11);
12566 } else {
12567 in_urgent = 0;
12568 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, play_folder);
12569 }
12570 if (res < 0) {
12571 ast_trace(-1, "open mailbox: %d\n", res);
12572 goto out;
12573 }
12574
12575 /* If there are no new messages, inform the user and hangup */
12576 if (vms.lastmsg == -1) {
12577 in_urgent = 0;
12578 cmd = vm_browse_messages(chan, &vms, vmu);
12579 res = 0;
12580 goto out;
12581 }
12582 } else {
12583 if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
12584 /* If we only have old messages start here */
12585 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
12586 in_urgent = 0;
12587 play_folder = 1;
12588 if (res < 0)
12589 goto out;
12590 } else if (!vms.urgentmessages && vms.newmessages) {
12591 /* If we have new messages but none are urgent */
12592 in_urgent = 0;
12593 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12594 if (res < 0)
12595 goto out;
12596 }
12597 }
12598
12599 if (useadsi)
12600 adsi_status(chan, &vms);
12601 res = 0;
12602
12603 /* Check to see if this is a new user */
12604 if (!strcasecmp(vmu->mailbox, vmu->password) &&
12606 if (ast_play_and_wait(chan, vm_newuser) == -1)
12607 ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
12608 cmd = vm_newuser_setup(chan, vmu, &vms, vmfmts, record_gain);
12609 if ((cmd == 't') || (cmd == '#')) {
12610 /* Timeout */
12611 ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
12612 res = 0;
12613 ast_trace(-1, "Timeout\n");
12614 goto out;
12615 } else if (cmd < 0) {
12616 /* Hangup */
12617 ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
12618 res = -1;
12619 ast_trace(-1, "Hangup\n");
12620 goto out;
12621 }
12622 }
12623#ifdef IMAP_STORAGE
12624 ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
12625 if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
12626 ast_debug(1, "*** QUOTA EXCEEDED!!\n");
12627 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
12628 }
12629 ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
12630 if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
12631 ast_log(AST_LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
12632 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
12633 }
12634#endif
12635
12636 ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
12637 if (play_auto) {
12638 cmd = '1';
12639 } else {
12640 cmd = vm_intro(chan, vmu, &vms);
12641 }
12642 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12643 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12644
12645 vms.repeats = 0;
12646 vms.starting = 1;
12647 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
12648 /* Run main menu */
12649 ast_trace(-1, "Main menu: %d %c\n", cmd, (cmd >= 32 && cmd <= 126 ? cmd : ' '));
12650 switch (cmd) {
12651 case '1': /* First message */
12652 vms.curmsg = 0;
12653 /* Fall through */
12654 case '5': /* Play current message */
12655 ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
12656 cmd = vm_browse_messages(chan, &vms, vmu);
12657 break;
12658 case '2': /* Change folders */
12659 ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
12660 if (useadsi)
12661 adsi_folders(chan, 0, "Change to folder...");
12662
12663 cmd = get_folder2(chan, "vm-changeto", 0);
12664 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12665 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12666 if (cmd == '#') {
12667 cmd = 0;
12668 } else if (cmd > 0) {
12669 cmd = cmd - '0';
12670 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12671 if (res == ERROR_LOCK_PATH) {
12672 ast_trace(-1, "close mailbox: %d\n", res);
12673 goto out;
12674 }
12675 /* If folder is not urgent, set in_urgent to zero! */
12676 if (cmd != 11) in_urgent = 0;
12677 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, cmd);
12678 if (res < 0) {
12679 ast_trace(-1, "open mailbox: %d\n", res);
12680 goto out;
12681 }
12682 play_folder = cmd;
12683 cmd = 0;
12684 }
12685 if (useadsi)
12686 adsi_status2(chan, &vms);
12687
12688 if (!cmd) {
12689 cmd = vm_play_folder_name(chan, vms.vmbox);
12690 }
12691
12692 vms.starting = 1;
12693 vms.curmsg = 0;
12694 break;
12695 case '3': /* Advanced options */
12696 ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
12697 cmd = 0;
12698 vms.repeats = 0;
12699 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
12700 switch (cmd) {
12701 case '1': /* Reply */
12702 if (vms.lastmsg > -1 && !vms.starting) {
12703 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
12704 if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
12705 res = cmd;
12706 ast_trace(-1, "advanced options: %d\n", cmd);
12707 goto out;
12708 }
12709 } else {
12710 cmd = ast_play_and_wait(chan, "vm-sorry");
12711 }
12712 cmd = 't';
12713 break;
12714 case '2': /* Callback */
12715 if (!vms.starting)
12716 ast_verb(3, "Callback Requested\n");
12717 if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
12718 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
12719 ast_trace(-1, "advanced options: %d\n", cmd);
12720 if (cmd == 9) {
12721 silentexit = 1;
12722 goto out;
12723 } else if (cmd == ERROR_LOCK_PATH) {
12724 res = cmd;
12725 goto out;
12726 }
12727 } else {
12728 cmd = ast_play_and_wait(chan, "vm-sorry");
12729 }
12730 cmd = 't';
12731 break;
12732 case '3': /* Envelope */
12733 if (vms.lastmsg > -1 && !vms.starting) {
12734 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
12735 if (cmd == ERROR_LOCK_PATH) {
12736 res = cmd;
12737 ast_trace(-1, "advanced options: %d\n", cmd);
12738 goto out;
12739 }
12740 } else {
12741 cmd = ast_play_and_wait(chan, "vm-sorry");
12742 }
12743 cmd = 't';
12744 break;
12745 case '4': /* Dialout */
12746 if (!ast_strlen_zero(vmu->dialout)) {
12747 cmd = dialout(chan, vmu, NULL, vmu->dialout);
12748 if (cmd == 9) {
12749 silentexit = 1;
12750 ast_trace(-1, "dialout: %d\n", cmd);
12751 goto out;
12752 }
12753 } else {
12754 cmd = ast_play_and_wait(chan, "vm-sorry");
12755 }
12756 cmd = 't';
12757 break;
12758
12759 case '5': /* Leave VoiceMail */
12760 if (ast_test_flag(vmu, VM_SVMAIL)) {
12761 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, forward_message, chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
12762 if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
12763 res = cmd;
12764 ast_trace(-1, "forward message: %d\n", cmd);
12765 goto out;
12766 }
12767 } else {
12768 cmd = ast_play_and_wait(chan, "vm-sorry");
12769 }
12770 cmd = 't';
12771 break;
12772
12773 case '*': /* Return to main menu */
12774 cmd = 't';
12775 break;
12776
12777 default:
12778 cmd = 0;
12779 if (!vms.starting) {
12780 cmd = ast_play_and_wait(chan, "vm-toreply");
12781 }
12782 if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
12783 cmd = ast_play_and_wait(chan, "vm-tocallback");
12784 }
12785 if (!cmd && !vms.starting) {
12786 cmd = ast_play_and_wait(chan, "vm-tohearenv");
12787 }
12788 if (!ast_strlen_zero(vmu->dialout) && !cmd) {
12789 cmd = ast_play_and_wait(chan, "vm-tomakecall");
12790 }
12791 if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
12792 cmd = ast_play_and_wait(chan, "vm-leavemsg");
12793 }
12794 if (!cmd) {
12795 cmd = ast_play_and_wait(chan, "vm-starmain");
12796 }
12797 if (!cmd) {
12798 cmd = ast_waitfordigit(chan, 6000);
12799 }
12800 if (!cmd) {
12801 vms.repeats++;
12802 }
12803 if (vms.repeats > 3) {
12804 cmd = 't';
12805 }
12806 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12807 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12808 }
12809 }
12810 if (cmd == 't') {
12811 cmd = 0;
12812 vms.repeats = 0;
12813 }
12814 break;
12815 case '4': /* Go to the previous message */
12816 ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
12817 if (vms.curmsg > 0) {
12818 vms.curmsg--;
12819 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12820 } else {
12821 /* Check if we were listening to new
12822 messages. If so, go to Urgent messages
12823 instead of saying "no more messages"
12824 */
12825 if (in_urgent == 0 && vms.urgentmessages > 0) {
12826 /* Check for Urgent messages */
12827 in_urgent = 1;
12828 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12829 if (res == ERROR_LOCK_PATH) {
12830 ast_trace(-1, "close mailbox: %d\n", res);
12831 goto out;
12832 }
12833 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11); /* Open Urgent folder */
12834 if (res < 0) {
12835 ast_trace(-1, "open mailbox: %d\n", res);
12836 goto out;
12837 }
12838 ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
12839 vms.curmsg = vms.lastmsg;
12840 if (vms.lastmsg < 0) {
12841 cmd = ast_play_and_wait(chan, "vm-nomore");
12842 }
12843 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12844 vms.curmsg = vms.lastmsg;
12845 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12846 } else {
12847 cmd = ast_play_and_wait(chan, "vm-nomore");
12848 }
12849 }
12850 break;
12851 case '6': /* Go to the next message */
12852 ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
12853 if (vms.curmsg < vms.lastmsg) {
12854 vms.curmsg++;
12855 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12856 } else {
12857 if (in_urgent && vms.newmessages > 0) {
12858 /* Check if we were listening to urgent
12859 * messages. If so, go to regular new messages
12860 * instead of saying "no more messages"
12861 */
12862 in_urgent = 0;
12863 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12864 if (res == ERROR_LOCK_PATH) {
12865 ast_trace(-1, "close mailbox: %d\n", res);
12866 goto out;
12867 }
12868 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12869 if (res < 0) {
12870 ast_trace(-1, "open mailbox: %d\n", res);
12871 goto out;
12872 }
12873 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12874 vms.curmsg = -1;
12875 if (vms.lastmsg < 0) {
12876 cmd = ast_play_and_wait(chan, "vm-nomore");
12877 }
12878 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12879 vms.curmsg = 0;
12880 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12881 } else {
12882 cmd = ast_play_and_wait(chan, "vm-nomore");
12883 }
12884 }
12885 break;
12886 case '7': /* Delete the current message */
12887 if (!nodelete && vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
12888 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
12889 if (useadsi)
12890 adsi_delete(chan, &vms);
12891 if (vms.deleted[vms.curmsg]) {
12892 if (play_folder == 0) {
12893 if (in_urgent) {
12894 vms.urgentmessages--;
12895 } else {
12896 vms.newmessages--;
12897 }
12898 }
12899 else if (play_folder == 1)
12900 vms.oldmessages--;
12901 cmd = ast_play_and_wait(chan, "vm-deleted");
12902 } else {
12903 if (play_folder == 0) {
12904 if (in_urgent) {
12905 vms.urgentmessages++;
12906 } else {
12907 vms.newmessages++;
12908 }
12909 }
12910 else if (play_folder == 1)
12911 vms.oldmessages++;
12912 cmd = ast_play_and_wait(chan, "vm-undeleted");
12913 }
12914 if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
12915 if (vms.curmsg < vms.lastmsg) {
12916 vms.curmsg++;
12917 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12918 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12919 vms.curmsg = 0;
12920 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12921 } else {
12922 /* Check if we were listening to urgent
12923 messages. If so, go to regular new messages
12924 instead of saying "no more messages"
12925 */
12926 if (in_urgent == 1) {
12927 /* Check for new messages */
12928 in_urgent = 0;
12929 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12930 if (res == ERROR_LOCK_PATH) {
12931 ast_trace(-1, "close mailbox: %d\n", res);
12932 goto out;
12933 }
12934 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12935 if (res < 0) {
12936 ast_trace(-1, "open mailbox: %d\n", res);
12937 goto out;
12938 }
12939 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12940 vms.curmsg = -1;
12941 if (vms.lastmsg < 0) {
12942 cmd = ast_play_and_wait(chan, "vm-nomore");
12943 }
12944 } else {
12945 cmd = ast_play_and_wait(chan, "vm-nomore");
12946 }
12947 }
12948 }
12949 } else /* Delete not valid if we haven't selected a message */
12950 cmd = 0;
12951#ifdef IMAP_STORAGE
12952 deleted = 1;
12953#endif
12954 break;
12955
12956 case '8': /* Forward the current message */
12957 if (vms.lastmsg > -1) {
12958 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, forward_message, chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
12959 if (cmd == ERROR_LOCK_PATH) {
12960 res = cmd;
12961 ast_trace(-1, "forward message: %d\n", res);
12962 goto out;
12963 }
12964 } else {
12965 /* Check if we were listening to urgent
12966 messages. If so, go to regular new messages
12967 instead of saying "no more messages"
12968 */
12969 if (in_urgent == 1 && vms.newmessages > 0) {
12970 /* Check for new messages */
12971 in_urgent = 0;
12972 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12973 if (res == ERROR_LOCK_PATH) {
12974 ast_trace(-1, "close mailbox: %d\n", res);
12975 goto out;
12976 }
12977 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12978 if (res < 0) {
12979 ast_trace(-1, "open mailbox: %d\n", res);
12980 goto out;
12981 }
12982 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12983 vms.curmsg = -1;
12984 if (vms.lastmsg < 0) {
12985 cmd = ast_play_and_wait(chan, "vm-nomore");
12986 }
12987 } else {
12988 cmd = ast_play_and_wait(chan, "vm-nomore");
12989 }
12990 }
12991 break;
12992 case '9': /* Save message to folder */
12993 ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
12994 if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
12995 /* No message selected */
12996 cmd = 0;
12997 break;
12998 }
12999 if (useadsi)
13000 adsi_folders(chan, 1, "Save to folder...");
13001 cmd = get_folder2(chan, "vm-savefolder", 1);
13002 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
13003 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
13004 box = 0; /* Shut up compiler */
13005 if (cmd == '#') {
13006 cmd = 0;
13007 break;
13008 } else if (cmd > 0) {
13009 box = cmd = cmd - '0';
13010 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, &vms, vms.curmsg, cmd, NULL, 0);
13011 if (cmd == ERROR_LOCK_PATH) {
13012 res = cmd;
13013 ast_trace(-1, "save to folder: %d\n", res);
13014 goto out;
13015#ifndef IMAP_STORAGE
13016 } else if (!cmd) {
13017 vms.deleted[vms.curmsg] = 1;
13018#endif
13019 } else {
13020 vms.deleted[vms.curmsg] = 0;
13021 vms.heard[vms.curmsg] = 0;
13022 }
13023 }
13024 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
13025 if (useadsi)
13026 adsi_message(chan, &vms);
13027 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
13028 if (!cmd) {
13029 cmd = ast_play_and_wait(chan, "vm-message");
13030 if (!cmd)
13031 cmd = say_and_wait(chan, vms.curmsg + 1, ast_channel_language(chan));
13032 if (!cmd)
13033 cmd = ast_play_and_wait(chan, "vm-savedto");
13034 if (!cmd)
13035 cmd = vm_play_folder_name(chan, vms.fn);
13036 } else {
13037 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
13038 }
13040 if (vms.curmsg < vms.lastmsg) {
13041 vms.curmsg++;
13042 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
13043 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
13044 vms.curmsg = 0;
13045 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
13046 } else {
13047 /* Check if we were listening to urgent
13048 messages. If so, go to regular new messages
13049 instead of saying "no more messages"
13050 */
13051 if (in_urgent == 1 && vms.newmessages > 0) {
13052 /* Check for new messages */
13053 in_urgent = 0;
13054 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
13055 if (res == ERROR_LOCK_PATH) {
13056 ast_trace(-1, "close mailbox: %d\n", res);
13057 goto out;
13058 }
13059 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
13060 if (res < 0) {
13061 ast_trace(-1, "open mailbox: %d\n", res);
13062 goto out;
13063 }
13064 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
13065 vms.curmsg = -1;
13066 if (vms.lastmsg < 0) {
13067 cmd = ast_play_and_wait(chan, "vm-nomore");
13068 }
13069 } else {
13070 cmd = ast_play_and_wait(chan, "vm-nomore");
13071 }
13072 }
13073 }
13074 break;
13075 case '*': /* Help */
13076 if (!vms.starting) {
13077 if (!strncasecmp(ast_channel_language(chan), "ja", 2)) {
13078 cmd = vm_play_folder_name(chan, vms.vmbox);
13079 if (!cmd)
13080 cmd = ast_play_and_wait(chan, "jp-wa");
13081 if (!cmd)
13082 cmd = ast_play_and_wait(chan, "digits/1");
13083 if (!cmd)
13084 cmd = ast_play_and_wait(chan, "jp-wo");
13085 if (!cmd)
13086 cmd = ast_play_and_wait(chan, "silence/1");
13087 if (!cmd)
13088 cmd = ast_play_and_wait(chan, "vm-opts");
13089 if (!cmd)
13090 cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent, nodelete);
13091 break;
13092 }
13093 cmd = ast_play_and_wait(chan, "vm-onefor");
13094 if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
13095 cmd = ast_play_and_wait(chan, "vm-for");
13096 }
13097 if (!cmd)
13098 cmd = vm_play_folder_name(chan, vms.vmbox);
13099 if (!cmd)
13100 cmd = ast_play_and_wait(chan, "vm-opts");
13101 if (!cmd)
13102 cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent, nodelete);
13103 } else
13104 cmd = 0;
13105 break;
13106 case '0': /* Mailbox options */
13107 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, vm_options,chan, vmu, &vms, vmfmts, record_gain);
13108 if (useadsi)
13109 adsi_status(chan, &vms);
13110 /* Reopen play_folder */
13111 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, play_folder);
13112 if (res < 0) {
13113 ast_trace(-1, "open mailbox: %d\n", res);
13114 goto out;
13115 }
13116 vms.starting = 1;
13117 break;
13118 default: /* Nothing */
13119 ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
13120 cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent, nodelete);
13121 break;
13122 }
13123 }
13124 if ((cmd == 't') || (cmd == '#')) {
13125 /* Timeout */
13126 res = 0;
13127 } else {
13128 /* Hangup */
13129 res = -1;
13130 }
13131
13132out:
13133 if (res > -1) {
13134 ast_stopstream(chan);
13135 adsi_goodbye(chan);
13136 if (valid && res != OPERATOR_EXIT) {
13137 if (silentexit)
13138 res = ast_play_and_wait(chan, "vm-dialout");
13139 else
13140 res = ast_play_and_wait(chan, "vm-goodbye");
13141 }
13142 if ((valid && res > 0) || res == OPERATOR_EXIT) {
13143 res = 0;
13144 }
13145 if (useadsi)
13147 }
13148 if (vmu) {
13149 SCOPE_CALL(-1, close_mailbox, &vms, vmu);
13150 }
13151 if (valid) {
13152 int new = 0, old = 0, urgent = 0;
13153 snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
13154 /* Urgent flag not passwd to externnotify here */
13155 run_externnotify(vmu->context, vmu->mailbox, NULL);
13156 ast_app_inboxcount2(ext_context, &urgent, &new, &old);
13157 queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgent, new, old);
13158 }
13159#ifdef IMAP_STORAGE
13160 /* expunge message - use UID Expunge if supported on IMAP server*/
13161 ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
13162 if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
13163 ast_mutex_lock(&vms.lock);
13164#ifdef HAVE_IMAP_TK2006
13165 if (LEVELUIDPLUS (vms.mailstream)) {
13166 mail_expunge_full(vms.mailstream, NIL, EX_UID);
13167 } else
13168#endif
13169 mail_expunge(vms.mailstream);
13170 ast_mutex_unlock(&vms.lock);
13171 }
13172 /* before we delete the state, we should copy pertinent info
13173 * back to the persistent model */
13174 if (vmu) {
13175 vmstate_delete(&vms);
13176 }
13177#endif
13178 if (vmu)
13179 free_user(vmu);
13180
13181#ifdef IMAP_STORAGE
13182 pthread_setspecific(ts_vmstate.key, NULL);
13183#endif
13184 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d\n", res);
13185}
13186
13187static int vm_exec(struct ast_channel *chan, const char *data)
13188{
13189 int res = 0;
13190 char *tmp;
13191 struct leave_vm_options leave_options;
13192 struct ast_flags flags = { 0 };
13193 char *opts[OPT_ARG_ARRAY_SIZE];
13195 AST_APP_ARG(argv0);
13196 AST_APP_ARG(argv1);
13197 );
13198 SCOPE_ENTER(3, "%s\n", ast_channel_name(chan));
13199
13200 memset(&leave_options, 0, sizeof(leave_options));
13201
13202 if (!ast_strlen_zero(data)) {
13203 tmp = ast_strdupa(data);
13205 if (args.argc == 2) {
13206 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
13207 SCOPE_EXIT_RTN_VALUE(-1, "parse options failed for '%s'\n", args.argv1);
13208 }
13211 int gain;
13212
13213 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
13214 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
13215 } else {
13216 leave_options.record_gain = (signed char) gain;
13217 }
13218 }
13221 leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
13222 }
13223 }
13224 if (ast_test_flag(&flags, OPT_BEEP)) { /* Use custom beep (or none at all) */
13225 leave_options.beeptone = opts[OPT_ARG_BEEP_TONE];
13226 } else { /* Use default beep */
13227 leave_options.beeptone = "beep";
13228 }
13229 } else {
13230 char temp[256];
13231 res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
13232 if (res < 0) {
13233 SCOPE_EXIT_RTN_VALUE(res, "getdata failed. RC: %d", res);
13234 }
13235 if (ast_strlen_zero(temp)) {
13237 }
13238 args.argv0 = ast_strdupa(temp);
13239 }
13240
13241 if (ast_channel_state(chan) != AST_STATE_UP) {
13244 } else {
13245 ast_answer(chan);
13246 }
13247 }
13248
13249 res = SCOPE_CALL_WITH_INT_RESULT(-1, leave_voicemail, chan, args.argv0, &leave_options);
13250 if (res == 't') {
13251 ast_play_and_wait(chan, "vm-goodbye");
13252 res = 0;
13253 }
13254
13255 if (res == OPERATOR_EXIT) {
13256 res = 0;
13257 }
13258
13259 if (res == ERROR_LOCK_PATH) {
13260 ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
13261 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
13262 res = 0;
13263 }
13264
13265 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d", res);
13266}
13267
13268static 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)
13269{
13270 struct ast_variable *var;
13271 struct ast_category *cat;
13272 generate_msg_id(id);
13273
13274 var = ast_variable_new("msg_id", id, "");
13275 if (!var) {
13276 return -1;
13277 }
13278
13279 cat = ast_category_get(msg_cfg, "message", NULL);
13280 if (!cat) {
13281 ast_log(LOG_ERROR, "Voicemail data file %s/%d.txt has no [message] category?\n", dir, msg);
13283 return -1;
13284 }
13285
13287
13288 if (ast_config_text_file_save(filename, msg_cfg, "app_voicemail")) {
13289 ast_log(LOG_WARNING, "Unable to update %s to have a message ID\n", filename);
13290 return -1;
13291 }
13292
13293 UPDATE_MSG_ID(dir, msg, id, vmu, msg_cfg, folder);
13294 return 0;
13295}
13296
13297static struct ast_vm_user *find_or_create(const char *context, const char *box)
13298{
13299 struct ast_vm_user *vmu;
13300
13301 if (!ast_strlen_zero(box) && box[0] == '*') {
13302 ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character. The '*' character,"
13303 "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
13304 "\n\tpredefined extension 'a'. A mailbox or password beginning with '*' is not valid"
13305 "\n\tand will be ignored.\n", box, context);
13306 return NULL;
13307 }
13308
13309 AST_LIST_TRAVERSE(&users, vmu, list) {
13310 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
13311 if (strcasecmp(vmu->context, context)) {
13312 ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
13313 \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
13314 \n\tconfiguration creates an ambiguity that you likely do not want. Please\
13315 \n\tamend your voicemail.conf file to avoid this situation.\n", box);
13316 }
13317 ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
13318 return NULL;
13319 }
13320 if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
13321 ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
13322 return NULL;
13323 }
13324 }
13325
13326 if (!(vmu = ast_calloc(1, sizeof(*vmu))))
13327 return NULL;
13328
13329 ast_copy_string(vmu->context, context, sizeof(vmu->context));
13330 ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
13331
13333
13334 return vmu;
13335}
13336
13337static int append_mailbox(const char *context, const char *box, const char *data)
13338{
13339 /* Assumes lock is already held */
13340 char *tmp;
13341 char *stringp;
13342 char *s;
13343 struct ast_vm_user *vmu;
13344 char mailbox_full[MAX_VM_MAILBOX_LEN];
13345 int new = 0, old = 0, urgent = 0;
13346 char secretfn[PATH_MAX] = "";
13347
13348 tmp = ast_strdupa(data);
13349
13350 if (!(vmu = find_or_create(context, box)))
13351 return -1;
13352
13353 populate_defaults(vmu);
13354
13355 stringp = tmp;
13356 if ((s = strsep(&stringp, ","))) {
13357 if (!ast_strlen_zero(s) && s[0] == '*') {
13358 ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
13359 "\n\tmust be reset in voicemail.conf.\n", box);
13360 }
13361 /* assign password regardless of validity to prevent NULL password from being assigned */
13362 ast_copy_string(vmu->password, s, sizeof(vmu->password));
13363 }
13364 if (stringp && (s = strsep(&stringp, ","))) {
13365 ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
13366 }
13367 if (stringp && (s = strsep(&stringp, ","))) {
13368 vmu->email = ast_strdup(s);
13369 }
13370 if (stringp && (s = strsep(&stringp, ","))) {
13371 ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
13372 }
13373 if (stringp) {
13374 apply_options(vmu, stringp);
13375 }
13376
13377 switch (vmu->passwordlocation) {
13378 case OPT_PWLOC_SPOOLDIR:
13379 snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
13380 read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
13381 }
13382
13383 snprintf(mailbox_full, MAX_VM_MAILBOX_LEN, "%s%s%s",
13384 box,
13385 ast_strlen_zero(context) ? "" : "@",
13386 context);
13387
13388 inboxcount2(mailbox_full, &urgent, &new, &old);
13389#ifdef IMAP_STORAGE
13390 imap_logout(mailbox_full);
13391#endif
13392 queue_mwi_event(NULL, mailbox_full, urgent, new, old);
13393
13394 return 0;
13395}
13396
13397#ifdef TEST_FRAMEWORK
13398AST_TEST_DEFINE(test_voicemail_vmuser)
13399{
13400 int res = 0;
13401 struct ast_vm_user *vmu;
13402 /* language parameter seems to only be used for display in manager action */
13403 static const char options_string[] = "attach=yes|attachfmt=wav49|"
13404 "serveremail=someguy@digium.com|fromstring=Voicemail System|tz=central|delete=yes|saycid=yes|"
13405 "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|leaveurgent=yes|"
13406 "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
13407 "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
13408 "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
13409 "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
13410 "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
13411 "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
13412#ifdef IMAP_STORAGE
13413 static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
13414 "imapfolder=INBOX|imapvmshareid=6000|imapserver=imapserver|imapport=1234|imapflags=flagged";
13415#endif
13416
13417 switch (cmd) {
13418 case TEST_INIT:
13419 info->name = "vmuser";
13420 info->category = "/apps/app_voicemail/";
13421 info->summary = "Vmuser unit test";
13422 info->description =
13423 "This tests passing all supported parameters to apply_options, the voicemail user config parser";
13424 return AST_TEST_NOT_RUN;
13425 case TEST_EXECUTE:
13426 break;
13427 }
13428
13429 if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
13430 return AST_TEST_NOT_RUN;
13431 }
13432 populate_defaults(vmu);
13434
13435 apply_options(vmu, options_string);
13436
13437 if (!ast_test_flag(vmu, VM_ATTACH)) {
13438 ast_test_status_update(test, "Parse failure for attach option\n");
13439 res = 1;
13440 }
13441 if (strcasecmp(vmu->attachfmt, "wav49")) {
13442 ast_test_status_update(test, "Parse failure for attachfmt option\n");
13443 res = 1;
13444 }
13445 if (strcasecmp(vmu->fromstring, "Voicemail System")) {
13446 ast_test_status_update(test, "Parse failure for fromstring option\n");
13447 res = 1;
13448 }
13449 if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
13450 ast_test_status_update(test, "Parse failure for serveremail option\n");
13451 res = 1;
13452 }
13453 if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
13454 ast_test_status_update(test, "Parse failure for emailsubject option\n");
13455 res = 1;
13456 }
13457 if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
13458 ast_test_status_update(test, "Parse failure for emailbody option\n");
13459 res = 1;
13460 }
13461 if (strcasecmp(vmu->zonetag, "central")) {
13462 ast_test_status_update(test, "Parse failure for tz option\n");
13463 res = 1;
13464 }
13465 if (!ast_test_flag(vmu, VM_DELETE)) {
13466 ast_test_status_update(test, "Parse failure for delete option\n");
13467 res = 1;
13468 }
13469 if (!ast_test_flag(vmu, VM_SAYCID)) {
13470 ast_test_status_update(test, "Parse failure for saycid option\n");
13471 res = 1;
13472 }
13473 if (!ast_test_flag(vmu, VM_SVMAIL)) {
13474 ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
13475 res = 1;
13476 }
13477 if (!ast_test_flag(vmu, VM_REVIEW)) {
13478 ast_test_status_update(test, "Parse failure for review option\n");
13479 res = 1;
13480 }
13481 if (!ast_test_flag(vmu, VM_MARK_URGENT)) {
13482 ast_test_status_update(test, "Parse failure for leaveurgent option\n");
13483 res = 1;
13484 }
13485 if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
13486 ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
13487 res = 1;
13488 }
13489 if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
13490 ast_test_status_update(test, "Parse failure for messagewrap option\n");
13491 res = 1;
13492 }
13493 if (!ast_test_flag(vmu, VM_OPERATOR)) {
13494 ast_test_status_update(test, "Parse failure for operator option\n");
13495 res = 1;
13496 }
13497 if (!ast_test_flag(vmu, VM_ENVELOPE)) {
13498 ast_test_status_update(test, "Parse failure for envelope option\n");
13499 res = 1;
13500 }
13501 if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
13502 ast_test_status_update(test, "Parse failure for moveheard option\n");
13503 res = 1;
13504 }
13505 if (!ast_test_flag(vmu, VM_SAYDURATION)) {
13506 ast_test_status_update(test, "Parse failure for sayduration option\n");
13507 res = 1;
13508 }
13509 if (vmu->saydurationm != 5) {
13510 ast_test_status_update(test, "Parse failure for saydurationm option\n");
13511 res = 1;
13512 }
13513 if (!ast_test_flag(vmu, VM_FORCENAME)) {
13514 ast_test_status_update(test, "Parse failure for forcename option\n");
13515 res = 1;
13516 }
13517 if (!ast_test_flag(vmu, VM_FORCEGREET)) {
13518 ast_test_status_update(test, "Parse failure for forcegreetings option\n");
13519 res = 1;
13520 }
13521 if (strcasecmp(vmu->callback, "somecontext")) {
13522 ast_test_status_update(test, "Parse failure for callbacks option\n");
13523 res = 1;
13524 }
13525 if (strcasecmp(vmu->dialout, "somecontext2")) {
13526 ast_test_status_update(test, "Parse failure for dialout option\n");
13527 res = 1;
13528 }
13529 if (strcasecmp(vmu->exit, "somecontext3")) {
13530 ast_test_status_update(test, "Parse failure for exitcontext option\n");
13531 res = 1;
13532 }
13533 if (vmu->minsecs != 10) {
13534 ast_test_status_update(test, "Parse failure for minsecs option\n");
13535 res = 1;
13536 }
13537 if (vmu->maxsecs != 100) {
13538 ast_test_status_update(test, "Parse failure for maxsecs option\n");
13539 res = 1;
13540 }
13541 if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
13542 ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
13543 res = 1;
13544 }
13545 if (vmu->maxdeletedmsg != 50) {
13546 ast_test_status_update(test, "Parse failure for backupdeleted option\n");
13547 res = 1;
13548 }
13549 if (vmu->volgain != 1.3) {
13550 ast_test_status_update(test, "Parse failure for volgain option\n");
13551 res = 1;
13552 }
13554 ast_test_status_update(test, "Parse failure for passwordlocation option\n");
13555 res = 1;
13556 }
13557#ifdef IMAP_STORAGE
13558 apply_options(vmu, option_string2);
13559
13560 if (strcasecmp(vmu->imapuser, "imapuser")) {
13561 ast_test_status_update(test, "Parse failure for imapuser option\n");
13562 res = 1;
13563 }
13564 if (strcasecmp(vmu->imappassword, "imappasswd")) {
13565 ast_test_status_update(test, "Parse failure for imappasswd option\n");
13566 res = 1;
13567 }
13568 if (strcasecmp(vmu->imapfolder, "INBOX")) {
13569 ast_test_status_update(test, "Parse failure for imapfolder option\n");
13570 res = 1;
13571 }
13572 if (strcasecmp(vmu->imapvmshareid, "6000")) {
13573 ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
13574 res = 1;
13575 }
13576 if (strcasecmp(vmu->imapserver, "imapserver")) {
13577 ast_test_status_update(test, "Parse failure for imapserver option\n");
13578 res = 1;
13579 }
13580 if (strcasecmp(vmu->imapport, "1234")) {
13581 ast_test_status_update(test, "Parse failure for imapport option\n");
13582 res = 1;
13583 }
13584 if (strcasecmp(vmu->imapflags, "flagged")) {
13585 ast_test_status_update(test, "Parse failure for imapflags option\n");
13586 res = 1;
13587 }
13588#endif
13589
13590 free_user(vmu);
13591 force_reload_config(); /* Restore original config */
13592 return res ? AST_TEST_FAIL : AST_TEST_PASS;
13593}
13594#endif
13595
13596static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
13597{
13598 struct ast_vm_user svm;
13599 struct ast_vm_user *vmu = NULL;
13600 char *parse;
13601 char *mailbox;
13602 char *context;
13603 int res = 0;
13604
13606 AST_APP_ARG(mailbox_context);
13607 AST_APP_ARG(attribute);
13608 AST_APP_ARG(folder);
13609 );
13610
13611 buf[0] = '\0';
13612
13613 if (ast_strlen_zero(args)) {
13614 ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
13615 return -1;
13616 }
13617
13618 parse = ast_strdupa(args);
13619 AST_STANDARD_APP_ARGS(arg, parse);
13620
13621 if (ast_strlen_zero(arg.mailbox_context)
13622 || ast_strlen_zero(arg.attribute)
13623 || separate_mailbox(ast_strdupa(arg.mailbox_context), &mailbox, &context)) {
13624 ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
13625 return -1;
13626 }
13627
13628 memset(&svm, 0, sizeof(svm));
13629 vmu = find_user(&svm, context, mailbox);
13630
13631 if (!strncasecmp(arg.attribute, "exists", 5)) {
13632 ast_copy_string(buf, vmu ? "1" : "0", len);
13633 free_user(vmu);
13634 return 0;
13635 }
13636
13637 if (vmu) {
13638 if (!strncasecmp(arg.attribute, "password", 8)) {
13640 } else if (!strncasecmp(arg.attribute, "fullname", 8)) {
13642 } else if (!strncasecmp(arg.attribute, "email", 5)) {
13643 ast_copy_string(buf, vmu->email, len);
13644 } else if (!strncasecmp(arg.attribute, "pager", 5)) {
13645 ast_copy_string(buf, vmu->pager, len);
13646 } else if (!strncasecmp(arg.attribute, "language", 8)) {
13648 } else if (!strncasecmp(arg.attribute, "locale", 6)) {
13649 ast_copy_string(buf, vmu->locale, len);
13650 } else if (!strncasecmp(arg.attribute, "tz", 2)) {
13652 } else if (!strncasecmp(arg.attribute, "count", 5)) {
13653 char *mailbox_id;
13654
13655 mailbox_id = ast_alloca(strlen(mailbox) + strlen(context) + 2);
13656 sprintf(mailbox_id, "%s@%s", mailbox, context);/* Safe */
13657
13658 /* If mbxfolder is empty messagecount will default to INBOX */
13659 res = messagecount(mailbox_id, arg.folder);
13660 if (res < 0) {
13661 ast_log(LOG_ERROR, "Unable to retrieve message count for mailbox %s\n", arg.mailbox_context);
13662 free_user(vmu);
13663 return -1;
13664 }
13665 snprintf(buf, len, "%d", res);
13666 } else {
13667 ast_log(LOG_ERROR, "Unknown attribute '%s' for VM_INFO\n", arg.attribute);
13668 free_user(vmu);
13669 return -1;
13670 }
13671 free_user(vmu);
13672 }
13673
13674 return 0;
13675}
13676
13678 .name = "VM_INFO",
13679 .read = acf_vm_info,
13680};
13681
13682static int vmauthenticate(struct ast_channel *chan, const char *data)
13683{
13684 char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
13685 struct ast_vm_user vmus = {{0}};
13686 char *options = NULL;
13687 int silent = 0, skipuser = 0;
13688 int res = -1;
13689
13690 if (data) {
13691 s = ast_strdupa(data);
13692 user = strsep(&s, ",");
13693 options = strsep(&s, ",");
13694 if (user) {
13695 s = user;
13696 user = strsep(&s, "@");
13697 context = strsep(&s, "");
13698 if (!ast_strlen_zero(user))
13699 skipuser++;
13701 }
13702 }
13703
13704 if (options) {
13705 silent = (strchr(options, 's')) != NULL;
13706 }
13707
13708 if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
13709 pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
13710 pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
13711 ast_play_and_wait(chan, "auth-thankyou");
13712 res = 0;
13713 } else if (mailbox[0] == '*') {
13714 /* user entered '*' */
13715 if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
13716 res = 0; /* prevent hangup */
13717 }
13718 }
13719
13720 return res;
13721}
13722
13723static char *show_users_realtime(int fd, const char *context)
13724{
13725 struct ast_config *cfg;
13726 const char *cat = NULL;
13727
13728 if (!(cfg = ast_load_realtime_multientry("voicemail",
13729 "context", context, SENTINEL))) {
13730 return CLI_FAILURE;
13731 }
13732
13733 ast_cli(fd,
13734 "\n"
13735 "=============================================================\n"
13736 "=== Configured Voicemail Users ==============================\n"
13737 "=============================================================\n"
13738 "===\n");
13739
13740 while ((cat = ast_category_browse(cfg, cat))) {
13741 struct ast_variable *var = NULL;
13742 ast_cli(fd,
13743 "=== Mailbox ...\n"
13744 "===\n");
13745 for (var = ast_variable_browse(cfg, cat); var; var = var->next)
13746 ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
13747 ast_cli(fd,
13748 "===\n"
13749 "=== ---------------------------------------------------------\n"
13750 "===\n");
13751 }
13752
13753 ast_cli(fd,
13754 "=============================================================\n"
13755 "\n");
13756
13757 ast_config_destroy(cfg);
13758
13759 return CLI_SUCCESS;
13760}
13761
13762static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
13763{
13764 int which = 0;
13765 int wordlen;
13766 struct ast_vm_user *vmu;
13767 const char *context = "";
13768 char *ret;
13769
13770 /* 0 - voicemail; 1 - show; 2 - users; 3 - for; 4 - <context> */
13771 if (pos > 4)
13772 return NULL;
13773 wordlen = strlen(word);
13775 AST_LIST_TRAVERSE(&users, vmu, list) {
13776 if (!strncasecmp(word, vmu->context, wordlen)) {
13777 if (context && strcmp(context, vmu->context) && ++which > state) {
13778 ret = ast_strdup(vmu->context);
13780 return ret;
13781 }
13782 /* ignore repeated contexts ? */
13783 context = vmu->context;
13784 }
13785 }
13787 return NULL;
13788}
13789
13790/*! \brief Show a list of voicemail users in the CLI */
13791static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13792{
13793 struct ast_vm_user *vmu;
13794#define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
13795 const char *context = NULL;
13796 int users_counter = 0;
13797
13798 switch (cmd) {
13799 case CLI_INIT:
13800 e->command = "voicemail show users [for]";
13801 e->usage =
13802 "Usage: voicemail show users [for <context>]\n"
13803 " Lists all mailboxes currently set up\n";
13804 return NULL;
13805 case CLI_GENERATE:
13806 return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
13807 }
13808
13809 if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
13810 return CLI_SHOWUSAGE;
13811 if (a->argc == 5) {
13812 if (strcmp(a->argv[3],"for"))
13813 return CLI_SHOWUSAGE;
13814 context = a->argv[4];
13815 }
13816
13817 if (ast_check_realtime("voicemail")) {
13818 if (!context) {
13819 ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
13820 return CLI_SHOWUSAGE;
13821 }
13822 return show_users_realtime(a->fd, context);
13823 }
13824
13826 if (AST_LIST_EMPTY(&users)) {
13827 ast_cli(a->fd, "There are no voicemail users currently defined\n");
13829 return CLI_FAILURE;
13830 }
13831 if (!context) {
13832 ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
13833 } else {
13834 int count = 0;
13835 AST_LIST_TRAVERSE(&users, vmu, list) {
13836 if (!strcmp(context, vmu->context)) {
13837 count++;
13838 break;
13839 }
13840 }
13841 if (count) {
13842 ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
13843 } else {
13844 ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
13846 return CLI_FAILURE;
13847 }
13848 }
13849 AST_LIST_TRAVERSE(&users, vmu, list) {
13850 int newmsgs = 0, oldmsgs = 0;
13851 char count[12], tmp[256] = "";
13852
13853 if (!context || !strcmp(context, vmu->context)) {
13854 snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
13855 inboxcount(tmp, &newmsgs, &oldmsgs);
13856 snprintf(count, sizeof(count), "%d", newmsgs);
13857 ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
13858 users_counter++;
13859 }
13860 }
13862 ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
13863 return CLI_SUCCESS;
13864}
13865
13866/*! \brief Show a list of voicemail zones in the CLI */
13867static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13868{
13869 struct vm_zone *zone;
13870#define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
13871 char *res = CLI_SUCCESS;
13872
13873 switch (cmd) {
13874 case CLI_INIT:
13875 e->command = "voicemail show zones";
13876 e->usage =
13877 "Usage: voicemail show zones\n"
13878 " Lists zone message formats\n";
13879 return NULL;
13880 case CLI_GENERATE:
13881 return NULL;
13882 }
13883
13884 if (a->argc != 3)
13885 return CLI_SHOWUSAGE;
13886
13888 if (!AST_LIST_EMPTY(&zones)) {
13889 ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
13890 AST_LIST_TRAVERSE(&zones, zone, list) {
13891 ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
13892 }
13893 } else {
13894 ast_cli(a->fd, "There are no voicemail zones currently defined\n");
13895 res = CLI_FAILURE;
13896 }
13898
13899 return res;
13900}
13901
13902/*! \brief Show a list of voicemail zones in the CLI */
13903static char *handle_voicemail_show_aliases(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13904{
13905 struct ao2_iterator aliases;
13906 struct alias_mailbox_mapping *mapping;
13907#define ALIASES_OUTPUT_FORMAT "%-32s %-32s\n"
13908 char *res = CLI_SUCCESS;
13909
13910 switch (cmd) {
13911 case CLI_INIT:
13912 e->command = "voicemail show aliases";
13913 e->usage =
13914 "Usage: voicemail show aliases\n"
13915 " Lists mailbox aliases\n";
13916 return NULL;
13917 case CLI_GENERATE:
13918 return NULL;
13919 }
13920
13921 if (a->argc != 3)
13922 return CLI_SHOWUSAGE;
13923
13925 ast_cli(a->fd, "Aliases are not enabled\n");
13926 return res;
13927 }
13928
13929 ast_cli(a->fd, "Aliases context: %s\n", aliasescontext);
13930 ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, "Alias", "Mailbox");
13931
13933 while ((mapping = ao2_iterator_next(&aliases))) {
13934 ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, mapping->alias, mapping->mailbox);
13935 ao2_ref(mapping, -1);
13936 }
13938
13939 return res;
13940}
13941
13942/*! \brief Reload voicemail configuration from the CLI */
13943static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13944{
13945 switch (cmd) {
13946 case CLI_INIT:
13947 e->command = "voicemail reload";
13948 e->usage =
13949 "Usage: voicemail reload\n"
13950 " Reload voicemail configuration\n";
13951 return NULL;
13952 case CLI_GENERATE:
13953 return NULL;
13954 }
13955
13956 if (a->argc != 2)
13957 return CLI_SHOWUSAGE;
13958
13959 ast_cli(a->fd, "Reloading voicemail configuration...\n");
13960 load_config(1);
13961
13962 return CLI_SUCCESS;
13963}
13964
13965static struct ast_cli_entry cli_voicemail[] = {
13966 AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
13967 AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
13968 AST_CLI_DEFINE(handle_voicemail_show_aliases, "List mailbox aliases"),
13969 AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
13970 AST_CLI_DEFINE(handle_voicemail_show_mailbox, "Display a mailbox's content details"),
13971 AST_CLI_DEFINE(handle_voicemail_forward_message, "Forward message to another folder"),
13972 AST_CLI_DEFINE(handle_voicemail_move_message, "Move message to another folder"),
13974};
13975
13976static int poll_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
13977{
13978 int new = 0, old = 0, urgent = 0;
13979
13980 if (!mwi_state) {
13981 /* This should only occur due to allocation failure of a default mwi state object */
13982 return 0;
13983 }
13984
13985 inboxcount2(mwi_state->uniqueid, &urgent, &new, &old);
13986
13987#ifdef IMAP_STORAGE
13988 if (imap_poll_logout) {
13989 imap_logout(mwi_state->uniqueid);
13990 }
13991#endif
13992
13993 if (urgent != mwi_state->urgent_msgs || new != mwi_state->new_msgs || old != mwi_state->old_msgs) {
13994 queue_mwi_event(NULL, mwi_state->uniqueid, urgent, new, old);
13995 run_externnotify(NULL, mwi_state->uniqueid, NULL);
13996 }
13997
13998 return 0;
13999}
14000
14001static void *mb_poll_thread(void *data)
14002{
14003 while (poll_thread_run) {
14004 struct timespec ts = { 0, };
14005 struct timeval wait;
14006
14008
14009 if (!poll_thread_run) {
14010 break;
14011 }
14012
14014 ts.tv_sec = wait.tv_sec;
14015 ts.tv_nsec = wait.tv_usec * 1000;
14016
14020 }
14021
14022 return NULL;
14023}
14024
14025#ifdef IMAP_STORAGE
14026static void imap_logout(const char *mailbox_id)
14027{
14028 char *context;
14029 char *mailbox;
14030 struct ast_vm_user vmus;
14031 RAII_VAR(struct ast_vm_user *, vmu, NULL, free_user);
14032 struct vm_state *vms = NULL;
14033
14034 if (ast_strlen_zero(mailbox_id)
14035 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
14036 return;
14037 }
14038
14039 memset(&vmus, 0, sizeof(vmus));
14040
14041 if (!(vmu = find_user(&vmus, context, mailbox)) || vmu->imapuser[0] == '\0') {
14042 return;
14043 }
14044
14045 vms = get_vm_state_by_imapuser(vmu->imapuser, 0);
14046 if (!vms) {
14047 vms = get_vm_state_by_mailbox(mailbox, context, 0);
14048 }
14049 if (!vms) {
14050 return;
14051 }
14052
14053 ast_mutex_lock(&vms->lock);
14054 vms->mailstream = mail_close(vms->mailstream);
14055 ast_mutex_unlock(&vms->lock);
14056
14057 vmstate_delete(vms);
14058}
14059
14060static int imap_close_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
14061{
14062 if (mwi_state && !ast_strlen_zero(mwi_state->uniqueid)) {
14063 imap_logout(mwi_state->uniqueid);
14064 }
14065
14066 return 0;
14067}
14068
14069#endif
14070
14071static int mwi_handle_unsubscribe2(void *data)
14072{
14073 struct ast_mwi_state *mwi_state = data;
14074
14075 /*
14076 * Go ahead and clear the implicit MWI publisher here to avoid a leak. If a backing
14077 * configuration is available it'll re-initialize (reset the cached state) on its
14078 * next publish.
14079 */
14081
14082#ifdef IMAP_STORAGE
14083 imap_close_subscribed_mailbox(mwi_state, NULL);
14084#endif
14085
14086 ao2_ref(mwi_state, -1);
14087 return 0;
14088}
14089
14090static void mwi_handle_unsubscribe(const char *id, struct ast_mwi_subscriber *sub)
14091{
14092 void *data = ast_mwi_subscriber_data(sub);
14093
14094 /* Don't bump data's reference. We'll just use the one returned above */
14096 /* A reference was returned for data when retrieving, so remove it on error */
14097 ao2_ref(data, -1);
14098 }
14099}
14100
14101static int mwi_handle_subscribe2(void *data)
14102{
14104 ao2_ref(data, -1);
14105 return 0;
14106}
14107
14108static void mwi_handle_subscribe(const char *id, struct ast_mwi_subscriber *sub)
14109{
14110 void *data = ast_mwi_subscriber_data(sub);
14111
14112 /* Don't bump data's reference. We'll just use the one returned above */
14114 /* A reference was returned for data when retrieving, so remove it on error */
14115 ao2_ref(data, -1);
14116 }
14117}
14118
14121 .on_unsubscribe = mwi_handle_unsubscribe,
14122};
14123
14124static void start_poll_thread(void)
14125{
14126 int errcode;
14128
14129 poll_thread_run = 1;
14130
14131 if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
14132 ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
14133 }
14134}
14135
14136static void stop_poll_thread(void)
14137{
14138 poll_thread_run = 0;
14139
14143
14144 pthread_join(poll_thread, NULL);
14146
14148}
14149
14150/*!
14151 * \brief Append vmu info string into given astman with event_name.
14152 * \return 0 failed. 1 otherwise.
14153*/
14155 struct mansession *s,
14156 struct ast_vm_user *vmu,
14157 const char* event_name,
14158 const char* actionid
14159 )
14160{
14161 int new;
14162 int old;
14163 char *mailbox;
14164 int ret;
14165
14166 if((s == NULL) || (vmu == NULL) || (event_name == NULL) || (actionid == NULL)) {
14167 ast_log(LOG_ERROR, "Wrong input parameter.");
14168 return 0;
14169 }
14170
14171 /* create mailbox string */
14172 if (!ast_strlen_zero(vmu->context)) {
14173 ret = ast_asprintf(&mailbox, "%s@%s", vmu->mailbox, vmu->context);
14174 } else {
14175 ret = ast_asprintf(&mailbox, "%s", vmu->mailbox);
14176 }
14177 if (ret == -1) {
14178 ast_log(LOG_ERROR, "Could not create mailbox string. err[%s]\n", strerror(errno));
14179 return 0;
14180 }
14181
14182 /* get mailbox count */
14183 ret = inboxcount(mailbox, &new, &old);
14185 if (ret == -1) {
14186 ast_log(LOG_ERROR, "Could not get mailbox count. user[%s], context[%s]\n",
14187 vmu->mailbox ?: "", vmu->context ?: "");
14188 return 0;
14189 }
14190
14191 astman_append(s,
14192 "Event: %s\r\n"
14193 "%s"
14194 "VMContext: %s\r\n"
14195 "VoiceMailbox: %s\r\n"
14196 "Fullname: %s\r\n"
14197 "Email: %s\r\n"
14198 "Pager: %s\r\n"
14199 "ServerEmail: %s\r\n"
14200 "FromString: %s\r\n"
14201 "MailCommand: %s\r\n"
14202 "Language: %s\r\n"
14203 "TimeZone: %s\r\n"
14204 "Callback: %s\r\n"
14205 "Dialout: %s\r\n"
14206 "UniqueID: %s\r\n"
14207 "ExitContext: %s\r\n"
14208 "SayDurationMinimum: %d\r\n"
14209 "SayEnvelope: %s\r\n"
14210 "SayCID: %s\r\n"
14211 "AttachMessage: %s\r\n"
14212 "AttachmentFormat: %s\r\n"
14213 "DeleteMessage: %s\r\n"
14214 "VolumeGain: %.2f\r\n"
14215 "CanReview: %s\r\n"
14216 "CanMarkUrgent: %s\r\n"
14217 "CallOperator: %s\r\n"
14218 "MaxMessageCount: %d\r\n"
14219 "MaxMessageLength: %d\r\n"
14220 "NewMessageCount: %d\r\n"
14221 "OldMessageCount: %d\r\n"
14222#ifdef IMAP_STORAGE
14223 "IMAPUser: %s\r\n"
14224 "IMAPServer: %s\r\n"
14225 "IMAPPort: %s\r\n"
14226 "IMAPFlags: %s\r\n"
14227#endif
14228 "\r\n",
14229
14230 event_name,
14231 actionid,
14232 vmu->context,
14233 vmu->mailbox,
14234 vmu->fullname,
14235 vmu->email,
14236 vmu->pager,
14239 mailcmd,
14240 vmu->language,
14241 vmu->zonetag,
14242 vmu->callback,
14243 vmu->dialout,
14244 vmu->uniqueid,
14245 vmu->exit,
14246 vmu->saydurationm,
14247 ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
14248 ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
14249 ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
14250 vmu->attachfmt,
14251 ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
14252 vmu->volgain,
14253 ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
14254 ast_test_flag(vmu, VM_MARK_URGENT) ? "Yes" : "No",
14255 ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
14256 vmu->maxmsg,
14257 vmu->maxsecs,
14258 new,
14259 old
14260#ifdef IMAP_STORAGE
14261 ,
14262 vmu->imapuser,
14263 vmu->imapserver,
14264 vmu->imapport,
14265 vmu->imapflags
14266#endif
14267 );
14268
14269 return 1;
14270
14271}
14272
14273
14274/*!
14275 * \brief Append vmbox info string into given astman with event_name.
14276 * \return 0 if unable to append details, 1 otherwise.
14277*/
14279 struct mansession *s,
14280 const struct message *m,
14281 struct ast_vm_user *vmu,
14282 const char* event_name,
14283 const char* actionid)
14284{
14285 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
14286 struct ast_vm_msg_snapshot *msg;
14287 int nummessages = 0;
14288 int i;
14289
14290 /* Take a snapshot of the mailbox */
14291 mailbox_snapshot = ast_vm_mailbox_snapshot_create(vmu->mailbox, vmu->context, NULL, 0, AST_VM_SNAPSHOT_SORT_BY_ID, 0);
14292 if (!mailbox_snapshot) {
14293 ast_log(LOG_ERROR, "Could not append voicemail box info for box %s@%s.",
14294 vmu->mailbox, vmu->context);
14295 return 0;
14296 }
14297
14298 astman_send_listack(s, m, "Voicemail box detail will follow", "start");
14299 /* walk through each folder's contents and append info for each message */
14300 for (i = 0; i < mailbox_snapshot->folders; i++) {
14301 AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
14302 astman_append(s,
14303 "Event: %s\r\n"
14304 "%s"
14305 "Folder: %s\r\n"
14306 "CallerID: %s\r\n"
14307 "Date: %s\r\n"
14308 "Duration: %s\r\n"
14309 "Flag: %s\r\n"
14310 "ID: %s\r\n"
14311 "\r\n",
14312 event_name,
14313 actionid,
14314 msg->folder_name,
14315 msg->callerid,
14316 msg->origdate,
14317 msg->duration,
14318 msg->flag,
14319 msg->msg_id
14320 );
14321 nummessages++;
14322 }
14323 }
14324
14325 /* done, destroy. */
14326 mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
14327 astman_send_list_complete_start(s, m, "VoicemailBoxDetailComplete", nummessages);
14329
14330 return 1;
14331}
14332
14333static int manager_match_mailbox(struct ast_mwi_state *mwi_state, void *data)
14334{
14335 const char *context = astman_get_header(data, "Context");
14336 const char *mailbox = astman_get_header(data, "Mailbox");
14337 const char *at;
14338
14339 if (!ast_strlen_zero(mwi_state->uniqueid)) {
14340 if (
14341 /* First case: everything matches */
14343 /* Second case: match the mailbox only */
14345 (at = strchr(mwi_state->uniqueid, '@')) &&
14346 strncmp(mailbox, mwi_state->uniqueid, at - mwi_state->uniqueid) == 0) ||
14347 /* Third case: match the context only */
14349 (at = strchr(mwi_state->uniqueid, '@')) &&
14350 strcmp(context, at + 1) == 0) ||
14351 /* Final case: match an exact specified mailbox */
14353 (at = strchr(mwi_state->uniqueid, '@')) &&
14354 strncmp(mailbox, mwi_state->uniqueid, at - mwi_state->uniqueid) == 0 &&
14355 strcmp(context, at + 1) == 0)
14356 ) {
14357 poll_subscribed_mailbox(mwi_state, NULL);
14358 }
14359 }
14360
14361 return 0;
14362}
14363
14364static int manager_voicemail_refresh(struct mansession *s, const struct message *m)
14365{
14367 astman_send_ack(s, m, "Refresh sent");
14368 return RESULT_SUCCESS;
14369}
14370
14371static int manager_status_voicemail_user(struct mansession *s, const struct message *m)
14372{
14373 struct ast_vm_user *vmu = NULL;
14374 const char *id = astman_get_header(m, "ActionID");
14375 char actionid[128];
14376 struct ast_vm_user svm;
14377 int ret;
14378
14379 const char *context = astman_get_header(m, "Context");
14380 const char *mailbox = astman_get_header(m, "Mailbox");
14381
14383 astman_send_error(s, m, "Need 'Context' and 'Mailbox' parameters.");
14384 return RESULT_SUCCESS;
14385 }
14386
14387 actionid[0] = '\0';
14388 if (!ast_strlen_zero(id)) {
14389 snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14390 }
14391
14392 /* find user */
14393 memset(&svm, 0, sizeof(svm));
14394 vmu = find_user(&svm, context, mailbox);
14395 if (!vmu) {
14396 /* could not find it */
14397 astman_send_ack(s, m, "There is no voicemail user of the given info.");
14398 return RESULT_SUCCESS;
14399 }
14400
14401 astman_send_listack(s, m, "Voicemail user detail will follow", "start");
14402
14403 /* append vmu info event */
14404 ret = append_vmu_info_astman(s, vmu, "VoicemailUserDetail", actionid);
14405 free_user(vmu);
14406 if(ret == 0) {
14407 ast_log(LOG_ERROR, "Could not append voicemail user info.");
14408 }
14409
14410 astman_send_list_complete_start(s, m, "VoicemailUserDetailComplete", 1);
14412
14413 return RESULT_SUCCESS;
14414}
14415
14416/*! \brief Manager list voicemail users command */
14417static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
14418{
14419 struct ast_vm_user *vmu = NULL;
14420 const char *id = astman_get_header(m, "ActionID");
14421 char actionid[128];
14422 int num_users = 0;
14423 int ret;
14424
14425 actionid[0] = '\0';
14426 if (!ast_strlen_zero(id)) {
14427 snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14428 }
14429
14431
14432 if (AST_LIST_EMPTY(&users)) {
14433 astman_send_ack(s, m, "There are no voicemail users currently defined.");
14435 return RESULT_SUCCESS;
14436 }
14437
14438 astman_send_listack(s, m, "Voicemail user list will follow", "start");
14439
14440 AST_LIST_TRAVERSE(&users, vmu, list) {
14441 /* append vmu info event */
14442 ret = append_vmu_info_astman(s, vmu, "VoicemailUserEntry", actionid);
14443 if(ret == 0) {
14444 ast_log(LOG_ERROR, "Could not append voicemail user info.");
14445 continue;
14446 }
14447 ++num_users;
14448 }
14449
14450 astman_send_list_complete_start(s, m, "VoicemailUserEntryComplete", num_users);
14452
14454
14455 return RESULT_SUCCESS;
14456}
14457
14458static int manager_get_mailbox_summary(struct mansession *s, const struct message *m)
14459{
14460 struct ast_vm_user *vmu = NULL;
14461 const char *id = astman_get_header(m, "ActionID");
14462 char actionid[128];
14463 struct ast_vm_user svm;
14464
14465 const char *context = astman_get_header(m, "Context");
14466 const char *mailbox = astman_get_header(m, "Mailbox");
14467
14469 astman_send_error(s, m, "Need 'Context' and 'Mailbox' parameters.");
14470 return 0;
14471 }
14472
14473 actionid[0] = '\0';
14474 if (!ast_strlen_zero(id)) {
14475 snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14476 }
14477
14478 /* find user */
14479 memset(&svm, 0, sizeof(svm));
14480 vmu = find_user(&svm, context, mailbox);
14481 if (!vmu) {
14482 /* could not find it */
14483 astman_send_ack(s, m, "There is no voicemail user matching the given user.");
14484 return 0;
14485 }
14486
14487 /* Append the mailbox details */
14488 if (!append_vmbox_info_astman(s, m, vmu, "VoicemailBoxDetail", actionid)) {
14489 astman_send_error(s, m, "Unable to get mailbox info for the given user.");
14490 }
14491
14492 free_user(vmu);
14493 return 0;
14494}
14495
14496static int manager_voicemail_move(struct mansession *s, const struct message *m)
14497{
14498 const char *mailbox = astman_get_header(m, "Mailbox");
14499 const char *context = astman_get_header(m, "Context");
14500 const char *from_folder = astman_get_header(m, "Folder");
14501 const char *id[] = { astman_get_header(m, "ID") };
14502 const char *to_folder = astman_get_header(m, "ToFolder");
14503
14504 if (ast_strlen_zero(mailbox)) {
14505 astman_send_error(s, m, "Mailbox not specified, required");
14506 return 0;
14507 }
14508 if (ast_strlen_zero(context)) {
14509 astman_send_error(s, m, "Context not specified, required");
14510 return 0;
14511 }
14512 if (ast_strlen_zero(from_folder)) {
14513 astman_send_error(s, m, "Folder not specified, required");
14514 return 0;
14515 }
14516 if (ast_strlen_zero(id[0])) {
14517 astman_send_error(s, m, "ID not specified, required");
14518 return 0;
14519 }
14520 if (ast_strlen_zero(to_folder)) {
14521 astman_send_error(s, m, "ToFolder not specified, required");
14522 return 0;
14523 }
14524
14525 if (vm_msg_move(mailbox, context, 1, from_folder, id, to_folder)) {
14526 astman_send_ack(s, m, "Message move failed\n");
14527 } else {
14528 astman_send_ack(s, m, "Message move successful\n");
14529 }
14530
14531 return 0;
14532}
14533
14534static int manager_voicemail_remove(struct mansession *s, const struct message *m)
14535{
14536 const char *mailbox = astman_get_header(m, "Mailbox");
14537 const char *context = astman_get_header(m, "Context");
14538 const char *folder = astman_get_header(m, "Folder");
14539 const char *id[] = { astman_get_header(m, "ID") };
14540
14541 if (ast_strlen_zero(mailbox)) {
14542 astman_send_error(s, m, "Mailbox not specified, required");
14543 return 0;
14544 }
14545 if (ast_strlen_zero(context)) {
14546 astman_send_error(s, m, "Context not specified, required");
14547 return 0;
14548 }
14549 if (ast_strlen_zero(folder)) {
14550 astman_send_error(s, m, "Folder not specified, required");
14551 return 0;
14552 }
14553 if (ast_strlen_zero(id[0])) {
14554 astman_send_error(s, m, "ID not specified, required");
14555 return 0;
14556 }
14557
14558 if (vm_msg_remove(mailbox, context, 1, folder, id)) {
14559 astman_send_ack(s, m, "Message remove failed\n");
14560 } else {
14561 astman_send_ack(s, m, "Message remove successful\n");
14562 }
14563
14564 return 0;
14565}
14566
14567static int manager_voicemail_forward(struct mansession *s, const struct message *m)
14568{
14569 const char *from_mailbox = astman_get_header(m, "Mailbox");
14570 const char *from_context = astman_get_header(m, "Context");
14571 const char *from_folder = astman_get_header(m, "Folder");
14572 const char *id[] = { astman_get_header(m, "ID") };
14573 const char *to_mailbox = astman_get_header(m, "ToMailbox");
14574 const char *to_context = astman_get_header(m, "ToContext");
14575 const char *to_folder = astman_get_header(m, "ToFolder");
14576
14578 astman_send_error(s, m, "Mailbox not specified, required");
14579 return 0;
14580 }
14581 if (ast_strlen_zero(from_context)) {
14582 astman_send_error(s, m, "Context not specified, required");
14583 return 0;
14584 }
14585 if (ast_strlen_zero(from_folder)) {
14586 astman_send_error(s, m, "Folder not specified, required");
14587 return 0;
14588 }
14589 if (ast_strlen_zero(id[0])) {
14590 astman_send_error(s, m, "ID not specified, required");
14591 return 0;
14592 }
14593 if (ast_strlen_zero(to_mailbox)) {
14594 astman_send_error(s, m, "ToMailbox not specified, required");
14595 return 0;
14596 }
14597 if (ast_strlen_zero(to_context)) {
14598 astman_send_error(s, m, "ToContext not specified, required");
14599 return 0;
14600 }
14601 if (ast_strlen_zero(to_folder)) {
14602 astman_send_error(s, m, "ToFolder not specified, required");
14603 return 0;
14604 }
14605
14606 if (vm_msg_forward(from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder, 1, id, 0)) {
14607 astman_send_ack(s, m, "Message forward failed\n");
14608 } else {
14609 astman_send_ack(s, m, "Message forward successful\n");
14610 }
14611
14612 return 0;
14613}
14614
14615/*! \brief Free the users structure. */
14616static void free_vm_users(void)
14617{
14618 struct ast_vm_user *current;
14620 while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
14623 }
14625}
14626
14627/*! \brief Free the zones structure. */
14628static void free_vm_zones(void)
14629{
14630 struct vm_zone *zcur;
14632 while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
14633 free_zone(zcur);
14635}
14636
14637static const char *substitute_escapes(const char *value)
14638{
14639 char *current;
14640
14641 /* Add 16 for fudge factor */
14642 struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
14643
14645
14646 /* Substitute strings \r, \n, and \t into the appropriate characters */
14647 for (current = (char *) value; *current; current++) {
14648 if (*current == '\\') {
14649 current++;
14650 if (!*current) {
14651 ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
14652 break;
14653 }
14654 switch (*current) {
14655 case '\\':
14656 ast_str_append(&str, 0, "\\");
14657 break;
14658 case 'r':
14659 ast_str_append(&str, 0, "\r");
14660 break;
14661 case 'n':
14662#ifdef IMAP_STORAGE
14663 if (!str->used || str->str[str->used - 1] != '\r') {
14664 ast_str_append(&str, 0, "\r");
14665 }
14666#endif
14667 ast_str_append(&str, 0, "\n");
14668 break;
14669 case 't':
14670 ast_str_append(&str, 0, "\t");
14671 break;
14672 default:
14673 ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
14674 break;
14675 }
14676 } else {
14677 ast_str_append(&str, 0, "%c", *current);
14678 }
14679 }
14680
14681 return ast_str_buffer(str);
14682}
14683
14684static int load_config_force(int reload, int force)
14685{
14686 struct ast_config *cfg;
14687 struct ast_flags config_flags = { reload && !force ? CONFIG_FLAG_FILEUNCHANGED : 0 };
14688 int res;
14689
14690 ast_unload_realtime("voicemail");
14691 ast_unload_realtime("voicemail_data");
14692
14693 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
14695 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
14696 ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
14697 return 0;
14698 }
14699 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
14700 ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
14701 return 0;
14702 } else {
14704 }
14705
14706 res = actual_load_config(reload, cfg);
14707
14708 ast_config_destroy(cfg);
14709
14710 return res;
14711}
14712
14713static int load_config(int reload)
14714{
14715 return load_config_force(reload, 0);
14716}
14717
14718#ifdef TEST_FRAMEWORK
14719static int load_config_from_memory(int reload, struct ast_config *cfg)
14720{
14721 ast_unload_realtime("voicemail");
14722 ast_unload_realtime("voicemail_data");
14723 return actual_load_config(reload, cfg);
14724}
14725#endif
14726
14727static struct alias_mailbox_mapping *alias_mailbox_mapping_create(const char *alias, const char *mailbox)
14728{
14729 struct alias_mailbox_mapping *mapping;
14730 size_t from_len = strlen(alias) + 1;
14731 size_t to_len = strlen(mailbox) + 1;
14732
14733 mapping = ao2_alloc(sizeof(*mapping) + from_len + to_len, NULL);
14734 if (!mapping) {
14735 return NULL;
14736 }
14737 mapping->alias = mapping->buf;
14738 mapping->mailbox = mapping->buf + from_len;
14739 ast_copy_string(mapping->alias, alias, from_len); /* Safe */
14740 ast_copy_string(mapping->mailbox, mailbox, to_len); /* Safe */
14741
14742 return mapping;
14743}
14744
14745static void load_aliases(struct ast_config *cfg)
14746{
14747 struct ast_variable *var;
14748
14750 return;
14751 }
14753 while (var) {
14754 struct alias_mailbox_mapping *mapping = alias_mailbox_mapping_create(var->name, var->value);
14755 if (mapping) {
14758 ao2_ref(mapping, -1);
14759 }
14760 var = var->next;
14761 }
14762}
14763
14764static void load_zonemessages(struct ast_config *cfg)
14765{
14766 struct ast_variable *var;
14767
14768 var = ast_variable_browse(cfg, "zonemessages");
14769 while (var) {
14770 if (var->value) {
14771 struct vm_zone *z;
14772 char *msg_format, *tzone;
14773 char storage[strlen(var->value) + 1];
14774
14775 z = ast_malloc(sizeof(*z));
14776 if (!z) {
14777 return;
14778 }
14779
14780 strcpy(storage, var->value); /* safe */
14781 msg_format = storage;
14782 tzone = strsep(&msg_format, "|,");
14783 if (msg_format) {
14784 ast_copy_string(z->name, var->name, sizeof(z->name));
14785 ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
14790 } else {
14791 ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
14792 ast_free(z);
14793 }
14794 }
14795 var = var->next;
14796 }
14797}
14798
14799static void load_users(struct ast_config *cfg)
14800{
14801 struct ast_variable *var;
14802 char *cat = NULL;
14803
14804 while ((cat = ast_category_browse(cfg, cat))) {
14805 if (strcasecmp(cat, "general") == 0
14806 || strcasecmp(cat, aliasescontext) == 0
14807 || strcasecmp(cat, "zonemessages") == 0) {
14808 continue;
14809 }
14810
14811 var = ast_variable_browse(cfg, cat);
14812 while (var) {
14813 append_mailbox(cat, var->name, var->value);
14814 var = var->next;
14815 }
14816 }
14817}
14818
14819static int actual_load_config(int reload, struct ast_config *cfg)
14820{
14821 const char *val;
14822 char *q, *stringp, *tmp;
14823 int x;
14824 unsigned int tmpadsi[4];
14825 long tps_queue_low;
14826 long tps_queue_high;
14827
14828#ifdef IMAP_STORAGE
14829 ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
14830#endif
14831 /* set audio control prompts */
14838
14839#ifdef IMAP_STORAGE
14840 ast_mwi_state_callback_all(imap_close_subscribed_mailbox, NULL);
14841#endif
14842
14843 /* Free all the users structure */
14844 free_vm_users();
14845
14846 /* Free all the zones structure */
14847 free_vm_zones();
14848
14849 /* Remove all aliases */
14852
14854
14855 memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
14856 memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
14857
14858 if (cfg) {
14859 /* General settings */
14860 aliasescontext[0] = '\0';
14861 val = ast_variable_retrieve(cfg, "general", "aliasescontext");
14863
14864 /* Attach voice message to mail message ? */
14865 if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
14866 val = "yes";
14868
14869 if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
14870 val = "no";
14872
14873 volgain = 0.0;
14874 if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
14875 sscanf(val, "%30lf", &volgain);
14876
14877#ifdef ODBC_STORAGE
14878 strcpy(odbc_database, "asterisk");
14879 if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
14880 ast_copy_string(odbc_database, val, sizeof(odbc_database));
14881 }
14882
14883 strcpy(odbc_table, "voicemessages");
14884 if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
14885 ast_copy_string(odbc_table, val, sizeof(odbc_table));
14886 }
14887 odbc_table_len = strlen(odbc_table);
14888
14890 if (!(val = ast_variable_retrieve(cfg, "general", "odbc_audio_on_disk")))
14891 val = "no";
14893
14894#endif
14895 /* Mail command */
14896 strcpy(mailcmd, SENDMAIL);
14897 if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
14898 ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
14899
14900 maxsilence = 0;
14901 if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
14902 maxsilence = atoi(val);
14903 if (maxsilence > 0)
14904 maxsilence *= 1000;
14905 }
14906
14907 if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
14908 maxmsg = MAXMSG;
14909 } else {
14910 maxmsg = atoi(val);
14911 if (maxmsg < 0) {
14912 ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
14913 maxmsg = MAXMSG;
14914 } else if (maxmsg > MAXMSGLIMIT) {
14915 ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
14917 }
14918 }
14919
14920 if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
14921 maxdeletedmsg = 0;
14922 } else {
14923 if (sscanf(val, "%30d", &x) == 1)
14924 maxdeletedmsg = x;
14925 else if (ast_true(val))
14927 else
14928 maxdeletedmsg = 0;
14929
14930 if (maxdeletedmsg < 0) {
14931 ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
14933 } else if (maxdeletedmsg > MAXMSGLIMIT) {
14934 ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
14936 }
14937 }
14938
14939 /* Load date format config for voicemail mail */
14940 if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
14942 }
14943
14944 /* Load date format config for voicemail pager mail */
14945 if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
14947 }
14948
14949 /* External password changing command */
14950 if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
14953 } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
14956 }
14957
14958 /* External password validation command */
14959 if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
14961 ast_debug(1, "found externpasscheck: %s\n", ext_pass_check_cmd);
14962 }
14963
14964#ifdef IMAP_STORAGE
14965 /* IMAP server address */
14966 if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
14967 ast_copy_string(imapserver, val, sizeof(imapserver));
14968 } else {
14969 ast_copy_string(imapserver, "localhost", sizeof(imapserver));
14970 }
14971 /* IMAP server port */
14972 if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
14973 ast_copy_string(imapport, val, sizeof(imapport));
14974 } else {
14975 ast_copy_string(imapport, "143", sizeof(imapport));
14976 }
14977 /* IMAP server flags */
14978 if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
14979 ast_copy_string(imapflags, val, sizeof(imapflags));
14980 }
14981 /* IMAP server master username */
14982 if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
14983 ast_copy_string(authuser, val, sizeof(authuser));
14984 }
14985 /* IMAP server master password */
14986 if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
14987 ast_copy_string(authpassword, val, sizeof(authpassword));
14988 }
14989 /* Expunge on exit */
14990 if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
14991 if (ast_false(val))
14992 expungeonhangup = 0;
14993 else
14994 expungeonhangup = 1;
14995 } else {
14996 expungeonhangup = 1;
14997 }
14998 /* IMAP voicemail folder */
14999 if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
15000 ast_copy_string(imapfolder, val, sizeof(imapfolder));
15001 } else {
15002 ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
15003 }
15004 if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
15005 ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
15006 }
15007 if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
15008 imapgreetings = ast_true(val);
15009 } else {
15010 imapgreetings = 0;
15011 }
15012 if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
15013 ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
15014 } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
15015 /* Also support greetingsfolder as documented in voicemail.conf.sample */
15016 ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
15017 } else {
15018 ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
15019 }
15020 if ((val = ast_variable_retrieve(cfg, "general", "imap_poll_logout"))) {
15021 imap_poll_logout = ast_true(val);
15022 } else {
15023 imap_poll_logout = 0;
15024 }
15025
15026 /* There is some very unorthodox casting done here. This is due
15027 * to the way c-client handles the argument passed in. It expects a
15028 * void pointer and casts the pointer directly to a long without
15029 * first dereferencing it. */
15030 if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
15031 mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
15032 } else {
15033 mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
15034 }
15035
15036 if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
15037 mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
15038 } else {
15039 mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
15040 }
15041
15042 if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
15043 mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
15044 } else {
15045 mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
15046 }
15047
15048 if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
15049 mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
15050 } else {
15051 mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
15052 }
15053
15054 /* Increment configuration version */
15055 imapversion++;
15056#endif
15057 /* External voicemail notify application */
15058 if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
15060 ast_debug(1, "found externnotify: %s\n", externnotify);
15061 } else {
15062 externnotify[0] = '\0';
15063 }
15064
15065 /* SMDI voicemail notification */
15066 if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
15067 ast_debug(1, "Enabled SMDI voicemail notification\n");
15068 if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
15070 } else {
15071 ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
15072 smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
15073 }
15074 if (!smdi_iface) {
15075 ast_log(AST_LOG_ERROR, "No valid SMDI interface specified, disabling SMDI voicemail notification\n");
15076 }
15077 }
15078
15079 /* Silence treshold */
15081 if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
15082 silencethreshold = atoi(val);
15083
15084 if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
15087
15088 vmmaxsecs = 0;
15089 if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
15090 if (sscanf(val, "%30d", &x) == 1) {
15091 vmmaxsecs = x;
15092 } else {
15093 ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
15094 }
15095 }
15096
15097 vmminsecs = 0;
15098 if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
15099 if (sscanf(val, "%30d", &x) == 1) {
15100 vmminsecs = x;
15101 if (maxsilence / 1000 >= vmminsecs) {
15102 ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
15103 }
15104 } else {
15105 ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
15106 }
15107 }
15108
15109 val = ast_variable_retrieve(cfg, "general", "format");
15110 if (!val) {
15111 val = "wav";
15112 } else {
15113 tmp = ast_strdupa(val);
15115 if (!val) {
15116 ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
15117 val = "wav";
15118 }
15119 }
15120 ast_copy_string(vmfmts, val, sizeof(vmfmts));
15121
15122 skipms = 3000;
15123 if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
15124 if (sscanf(val, "%30d", &x) == 1) {
15125 maxgreet = x;
15126 } else {
15127 ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
15128 }
15129 }
15130
15131 if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
15132 if (sscanf(val, "%30d", &x) == 1) {
15133 skipms = x;
15134 } else {
15135 ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
15136 }
15137 }
15138
15139 maxlogins = 3;
15140 if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
15141 if (sscanf(val, "%30d", &x) == 1) {
15142 maxlogins = x;
15143 } else {
15144 ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
15145 }
15146 }
15147
15149 if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
15150 if (sscanf(val, "%30d", &x) == 1) {
15151 minpassword = x;
15152 } else {
15153 ast_log(AST_LOG_WARNING, "Invalid minimum password length. Default to %d\n", minpassword);
15154 }
15155 }
15156
15157 /* Force new user to record name ? */
15158 if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
15159 val = "no";
15161
15162 /* Force new user to record greetings ? */
15163 if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
15164 val = "no";
15166
15167 if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
15168 ast_debug(1, "VM_CID Internal context string: %s\n", val);
15169 stringp = ast_strdupa(val);
15170 for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
15171 if (!ast_strlen_zero(stringp)) {
15172 q = strsep(&stringp, ",");
15173 while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
15174 q++;
15176 ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
15177 } else {
15178 cidinternalcontexts[x][0] = '\0';
15179 }
15180 }
15181 }
15182 if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
15183 ast_debug(1, "VM Review Option disabled globally\n");
15184 val = "no";
15185 }
15187
15188 if (!(val = ast_variable_retrieve(cfg, "general", "leaveurgent"))){
15189 val = "yes";
15190 } else if (ast_false(val)) {
15191 ast_debug(1, "VM leave urgent messages disabled globally\n");
15192 val = "no";
15193 }
15195
15196 /* Temporary greeting reminder */
15197 if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
15198 ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
15199 val = "no";
15200 } else {
15201 ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
15202 }
15204 if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
15205 ast_debug(1, "VM next message wrap disabled globally\n");
15206 val = "no";
15207 }
15209
15210 if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
15211 ast_debug(1, "VM Operator break disabled globally\n");
15212 val = "no";
15213 }
15215
15216 if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
15217 ast_debug(1, "VM CID Info before msg disabled globally\n");
15218 val = "no";
15219 }
15221
15222 if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
15223 ast_debug(1, "Send Voicemail msg disabled globally\n");
15224 val = "no";
15225 }
15227
15228 if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
15229 ast_debug(1, "ENVELOPE before msg enabled globally\n");
15230 val = "yes";
15231 }
15233
15234 if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
15235 ast_debug(1, "Move Heard enabled globally\n");
15236 val = "yes";
15237 }
15239
15240 if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
15241 ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
15242 val = "no";
15243 }
15245
15246 if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
15247 ast_debug(1, "Duration info before msg enabled globally\n");
15248 val = "yes";
15249 }
15251
15252 saydurationminfo = 2;
15253 if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
15254 if (sscanf(val, "%30d", &x) == 1) {
15255 saydurationminfo = x;
15256 } else {
15257 ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
15258 }
15259 }
15260
15261 if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
15262 ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
15263 val = "no";
15264 }
15266
15267 if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
15269 ast_debug(1, "found dialout context: %s\n", dialcontext);
15270 } else {
15271 dialcontext[0] = '\0';
15272 }
15273
15274 if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
15276 ast_debug(1, "found callback context: %s\n", callcontext);
15277 } else {
15278 callcontext[0] = '\0';
15279 }
15280
15281 if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
15283 ast_debug(1, "found operator context: %s\n", exitcontext);
15284 } else {
15285 exitcontext[0] = '\0';
15286 }
15287
15288 /* load password sounds configuration */
15289 if ((val = ast_variable_retrieve(cfg, "general", "vm-login")))
15291 if ((val = ast_variable_retrieve(cfg, "general", "vm-newuser")))
15293 if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
15295 if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
15297 if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
15299 if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
15301 if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
15303 if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
15305 if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
15307 }
15308 if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
15310 }
15311 /* load configurable audio prompts */
15312 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
15314 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
15316 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
15318 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
15320 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
15322
15323 if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
15324 val = "no";
15326
15327 if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
15328 val = "voicemail.conf";
15329 }
15330 if (!(strcmp(val, "spooldir"))) {
15332 } else {
15334 }
15335
15337 if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
15338 if (sscanf(val, "%30u", &poll_freq) != 1) {
15340 ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
15341 }
15342 }
15343
15344 poll_mailboxes = 0;
15345 if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
15347
15348 memset(fromstring, 0, sizeof(fromstring));
15349 memset(pagerfromstring, 0, sizeof(pagerfromstring));
15350 strcpy(charset, "ISO-8859-1");
15351 if (emailbody) {
15353 emailbody = NULL;
15354 }
15355 if (emailsubject) {
15358 }
15359 if (pagerbody) {
15361 pagerbody = NULL;
15362 }
15363 if (pagersubject) {
15366 }
15367 if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
15369 if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
15371 if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
15373 if ((val = ast_variable_retrieve(cfg, "general", "charset")))
15375 if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
15376 sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
15377 for (x = 0; x < 4; x++) {
15378 memcpy(&adsifdn[x], &tmpadsi[x], 1);
15379 }
15380 }
15381 if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
15382 sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
15383 for (x = 0; x < 4; x++) {
15384 memcpy(&adsisec[x], &tmpadsi[x], 1);
15385 }
15386 }
15387 if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
15388 if (atoi(val)) {
15389 adsiver = atoi(val);
15390 }
15391 }
15392 if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
15394 }
15395 if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
15396 ast_copy_string(locale, val, sizeof(locale));
15397 }
15398 if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
15400 }
15401 if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
15403 }
15404 if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
15406 }
15407 if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
15409 }
15410
15411 tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
15412 if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_high"))) {
15413 if (sscanf(val, "%30ld", &tps_queue_high) != 1 || tps_queue_high <= 0) {
15414 ast_log(AST_LOG_WARNING, "Invalid the taskprocessor high water alert trigger level '%s'\n", val);
15415 tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
15416 }
15417 }
15418 tps_queue_low = -1;
15419 if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_low"))) {
15420 if (sscanf(val, "%30ld", &tps_queue_low) != 1 ||
15421 tps_queue_low < -1 || tps_queue_high < tps_queue_low) {
15422 ast_log(AST_LOG_WARNING, "Invalid the taskprocessor low water clear alert level '%s'\n", val);
15423 tps_queue_low = -1;
15424 }
15425 }
15426 if (ast_taskprocessor_alert_set_levels(mwi_subscription_tps, tps_queue_low, tps_queue_high)) {
15427 ast_log(AST_LOG_WARNING, "Failed to set alert levels for voicemail taskprocessor.\n");
15428 }
15429
15430 /* load mailboxes from voicemail.conf */
15431
15432 /*
15433 * Aliases must be loaded before users or the aliases won't be notified
15434 * if there's existing voicemail in the user mailbox.
15435 */
15436 load_aliases(cfg);
15437
15438 load_zonemessages(cfg);
15439
15440 load_users(cfg);
15441
15443
15448
15449 return 0;
15450 } else {
15452 ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
15453 return 0;
15454 }
15455}
15456
15457static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
15458{
15459 int res = -1;
15460 char dir[PATH_MAX];
15461 snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
15462 ast_debug(2, "About to try retrieving name file %s\n", dir);
15463 RETRIEVE(dir, -1, mailbox, context);
15464 if (ast_fileexists(dir, NULL, NULL)) {
15465 res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
15466 }
15467 DISPOSE(dir, -1);
15468 return res;
15469}
15470
15471/*!
15472 * \internal
15473 * \brief Play a recorded user name for the mailbox to the specified channel.
15474 *
15475 * \param chan Where to play the recorded name file.
15476 * \param mailbox_id The mailbox name.
15477 *
15478 * \retval 0 Name played without interruption
15479 * \retval dtmf ASCII value of the DTMF which interrupted playback.
15480 * \retval -1 Unable to locate mailbox or hangup occurred.
15481 */
15482static int vm_sayname(struct ast_channel *chan, const char *mailbox_id)
15483{
15484 char *context;
15485 char *mailbox;
15486
15487 if (ast_strlen_zero(mailbox_id)
15488 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
15489 return -1;
15490 }
15491 return sayname(chan, mailbox, context);
15492}
15493
15494static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
15495 struct ast_config *pwconf;
15496 struct ast_flags config_flags = { 0 };
15497
15498 pwconf = ast_config_load(secretfn, config_flags);
15499 if (valid_config(pwconf)) {
15500 const char *val = ast_variable_retrieve(pwconf, "general", "password");
15501 if (val) {
15502 ast_copy_string(password, val, passwordlen);
15503 ast_config_destroy(pwconf);
15504 return;
15505 }
15506 ast_config_destroy(pwconf);
15507 }
15508 ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
15509}
15510
15511static int write_password_to_file(const char *secretfn, const char *password) {
15512 struct ast_config *conf;
15513 struct ast_category *cat;
15514 struct ast_variable *var;
15515 int res = -1;
15516
15517 if (!(conf = ast_config_new())) {
15518 ast_log(LOG_ERROR, "Error creating new config structure\n");
15519 return res;
15520 }
15521 if (!(cat = ast_category_new("general", "", 1))) {
15522 ast_log(LOG_ERROR, "Error creating new category structure\n");
15524 return res;
15525 }
15526 if (!(var = ast_variable_new("password", password, ""))) {
15527 ast_log(LOG_ERROR, "Error creating new variable structure\n");
15530 return res;
15531 }
15534 if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
15535 res = 0;
15536 } else {
15537 ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
15538 }
15539
15541 return res;
15542}
15543
15544static int vmsayname_exec(struct ast_channel *chan, const char *data)
15545{
15546 char *context;
15547 char *mailbox;
15548 int res;
15549
15550 if (ast_strlen_zero(data)
15552 ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
15553 return -1;
15554 }
15555
15556 if ((res = sayname(chan, mailbox, context)) < 0) {
15557 ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", mailbox, context);
15558 res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
15559 if (!res) {
15561 }
15562 }
15563
15564 return res;
15565}
15566
15567#ifdef TEST_FRAMEWORK
15568static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
15569{
15570 return 0;
15571}
15572
15573static struct ast_frame *fake_read(struct ast_channel *ast)
15574{
15575 return &ast_null_frame;
15576}
15577
15578AST_TEST_DEFINE(test_voicemail_vmsayname)
15579{
15580 char dir[PATH_MAX];
15581 char dir2[PATH_MAX];
15582 static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
15583 static const char TEST_EXTENSION[] = "1234";
15584
15585 struct ast_channel *test_channel1 = NULL;
15586 int res = -1;
15587 struct ast_format_cap *capabilities;
15588
15589 static const struct ast_channel_tech fake_tech = {
15590 .write = fake_write,
15591 .read = fake_read,
15592 };
15593
15594 switch (cmd) {
15595 case TEST_INIT:
15596 info->name = "vmsayname_exec";
15597 info->category = "/apps/app_voicemail/";
15598 info->summary = "Vmsayname unit test";
15599 info->description =
15600 "This tests passing various parameters to vmsayname";
15601 return AST_TEST_NOT_RUN;
15602 case TEST_EXECUTE:
15603 break;
15604 }
15605
15606 if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL,
15607 NULL, NULL, 0, 0, "TestChannel1"))) {
15608 goto exit_vmsayname_test;
15609 }
15610
15611 /* normally this is done in the channel driver */
15613 if (!capabilities) {
15614 goto exit_vmsayname_test;
15615 }
15618 ao2_ref(capabilities, -1);
15623 ast_channel_tech_set(test_channel1, &fake_tech);
15624
15625 ast_channel_unlock(test_channel1);
15626
15627 ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
15628 snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
15629 if (!(res = vmsayname_exec(test_channel1, dir))) {
15630 snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15631 if (ast_fileexists(dir, NULL, NULL)) {
15632 ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
15633 res = -1;
15634 goto exit_vmsayname_test;
15635 } else {
15636 /* no greeting already exists as expected, let's create one to fully test sayname */
15637 if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
15638 ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
15639 goto exit_vmsayname_test;
15640 }
15641 snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_DATA_DIR);
15642 snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15643 /* we're not going to hear the sound anyway, just use a valid gsm audio file */
15644 if ((res = symlink(dir, dir2))) {
15645 ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
15646 goto exit_vmsayname_test;
15647 }
15648 ast_test_status_update(test, "Test playing created mailbox greeting...\n");
15649 snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
15650 res = vmsayname_exec(test_channel1, dir);
15651
15652 /* TODO: there may be a better way to do this */
15653 unlink(dir2);
15654 snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15655 rmdir(dir2);
15656 snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
15657 rmdir(dir2);
15658 }
15659 }
15660
15661exit_vmsayname_test:
15662
15663 ast_hangup(test_channel1);
15664
15665 return res ? AST_TEST_FAIL : AST_TEST_PASS;
15666}
15667
15668struct test_files {
15669 char dir[256];
15670 char file[256];
15671 char txtfile[256];
15672};
15673
15674AST_TEST_DEFINE(test_voicemail_msgcount)
15675{
15676 int i, j, res = AST_TEST_PASS, syserr;
15677 struct ast_vm_user *vmu;
15678 struct ast_vm_user svm;
15679 struct vm_state vms;
15680#ifdef IMAP_STORAGE
15681 struct ast_channel *chan = NULL;
15682#endif
15683 /* Using ast_alloca instead of just declaring tmp as an array is a workaround for a GCC 10 issue with -Wrestrict */
15684 struct test_files *tmp = ast_alloca(sizeof(struct test_files) * 3);
15685 char syscmd[256];
15686 const char origweasels[] = "tt-weasels";
15687 const char testcontext[] = "test";
15688 const char testmailbox[] = "00000000";
15689 const char testspec[] = "00000000@test";
15690 FILE *txt;
15691 int new, old, urgent;
15692 const char *folders[3] = { "Old", "Urgent", "INBOX" };
15693 const int folder2mbox[3] = { 1, 11, 0 };
15694 const int expected_results[3][12] = {
15695 /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
15696 { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
15697 { 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
15698 { 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 2 },
15699 };
15700
15701 switch (cmd) {
15702 case TEST_INIT:
15703 info->name = "test_voicemail_msgcount";
15704 info->category = "/apps/app_voicemail/";
15705 info->summary = "Test Voicemail status checks";
15706 info->description =
15707 "Verify that message counts are correct when retrieved through the public API";
15708 return AST_TEST_NOT_RUN;
15709 case TEST_EXECUTE:
15710 break;
15711 }
15712
15713 /* Make sure the original path was completely empty */
15714 snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
15715 if ((syserr = ast_safe_system(syscmd))) {
15716 ast_test_status_update(test, "Unable to clear test directory: %s\n",
15717 syserr > 0 ? strerror(syserr) : "unable to fork()");
15718 return AST_TEST_FAIL;
15719 }
15720
15721#ifdef IMAP_STORAGE
15722 if (!(chan = ast_dummy_channel_alloc())) {
15723 ast_test_status_update(test, "Unable to create dummy channel\n");
15724 return AST_TEST_FAIL;
15725 }
15726#endif
15727
15728 memset(&svm, 0, sizeof(svm));
15729 if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
15730 !(vmu = find_or_create(testcontext, testmailbox))) {
15731 ast_test_status_update(test, "Cannot create vmu structure\n");
15732#ifdef IMAP_STORAGE
15733 chan = ast_channel_unref(chan);
15734#endif
15735 return AST_TEST_FAIL;
15736 }
15737
15738 populate_defaults(vmu);
15739 memset(&vms, 0, sizeof(vms));
15740
15741 /* Create temporary voicemail */
15742 for (i = 0; i < 3; i++) {
15743 create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
15744 make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
15745 snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
15746
15747 if (ast_fileexists(origweasels, "gsm", "en") > 0) {
15748 snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
15749 VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
15750 if ((syserr = ast_safe_system(syscmd))) {
15751 ast_test_status_update(test, "Unable to create test voicemail: %s\n",
15752 syserr > 0 ? strerror(syserr) : "unable to fork()");
15753#ifdef IMAP_STORAGE
15754 chan = ast_channel_unref(chan);
15755#endif
15756 free_user(vmu);
15757 return AST_TEST_FAIL;
15758 }
15759 }
15760
15761 if ((txt = fopen(tmp[i].txtfile, "w+"))) {
15762 fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
15763 fclose(txt);
15764 } else {
15765 ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
15766 res = AST_TEST_FAIL;
15767 break;
15768 }
15769 open_mailbox(&vms, vmu, folder2mbox[i]);
15770 STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent", NULL);
15771
15772 /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
15773 for (j = 0; j < 3; j++) {
15774 /* folder[2] is INBOX, __has_voicemail will default back to INBOX */
15775 if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
15776 ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
15777 testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
15778 res = AST_TEST_FAIL;
15779 }
15780 }
15781
15782 new = old = urgent = 0;
15783 if (ast_app_inboxcount(testspec, &new, &old)) {
15784 ast_test_status_update(test, "inboxcount returned failure\n");
15785 res = AST_TEST_FAIL;
15786 } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
15787 ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
15788 testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
15789 res = AST_TEST_FAIL;
15790 }
15791
15792 new = old = urgent = 0;
15793 if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
15794 ast_test_status_update(test, "inboxcount2 returned failure\n");
15795 res = AST_TEST_FAIL;
15796 } else if (old != expected_results[i][6 + 0] ||
15797 urgent != expected_results[i][6 + 1] ||
15798 new != expected_results[i][6 + 2] ) {
15799 ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
15800 testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
15801 res = AST_TEST_FAIL;
15802 }
15803
15804 new = old = urgent = 0;
15805 for (j = 0; j < 3; j++) {
15806 if (ast_app_messagecount(testspec, folders[j]) != expected_results[i][9 + j]) {
15807 ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
15808 testspec, folders[j], ast_app_messagecount(testspec, folders[j]), expected_results[i][9 + j]);
15809 res = AST_TEST_FAIL;
15810 }
15811 }
15812 }
15813
15814 for (i = 0; i < 3; i++) {
15815 /* This is necessary if the voicemails are stored on an ODBC/IMAP
15816 * server, in which case, the rm below will not affect the
15817 * voicemails. */
15818 DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
15819 DISPOSE(tmp[i].dir, 0);
15820 }
15821
15822 if (vms.deleted) {
15823 ast_free(vms.deleted);
15824 }
15825 if (vms.heard) {
15826 ast_free(vms.heard);
15827 }
15828
15829#ifdef IMAP_STORAGE
15830 chan = ast_channel_unref(chan);
15831#endif
15832
15833 /* And remove test directory */
15834 snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
15835 if ((syserr = ast_safe_system(syscmd))) {
15836 ast_test_status_update(test, "Unable to clear test directory: %s\n",
15837 syserr > 0 ? strerror(syserr) : "unable to fork()");
15838 }
15839
15840 free_user(vmu);
15841 force_reload_config(); /* Restore original config */
15842 return res;
15843}
15844
15845AST_TEST_DEFINE(test_voicemail_notify_endl)
15846{
15847 int res = AST_TEST_PASS;
15848 char testcontext[] = "test";
15849 char testmailbox[] = "00000000";
15850 char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
15851 char attach[256], attach2[256];
15852 char buf[256] = ""; /* No line should actually be longer than 80 */
15853 struct ast_channel *chan = NULL;
15854 struct ast_vm_user *vmu, vmus = {
15855 .flags = 0,
15856 };
15857 FILE *file;
15858 struct {
15859 char *name;
15860 enum { INT, FLAGVAL, STATIC, STRPTR } type;
15861 void *location;
15862 union {
15863 int intval;
15864 char *strval;
15865 } u;
15866 } test_items[] = {
15867 { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
15868 { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
15869 { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
15870 { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
15871 { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
15872 { "attach2", STRPTR, attach2, .u.strval = "" },
15873 { "attach", STRPTR, attach, .u.strval = "" },
15874 };
15875 int which;
15876
15877 switch (cmd) {
15878 case TEST_INIT:
15879 info->name = "test_voicemail_notify_endl";
15880 info->category = "/apps/app_voicemail/";
15881 info->summary = "Test Voicemail notification end-of-line";
15882 info->description =
15883 "Verify that notification emails use a consistent end-of-line character";
15884 return AST_TEST_NOT_RUN;
15885 case TEST_EXECUTE:
15886 break;
15887 }
15888
15889 snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_DATA_DIR);
15890 snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_DATA_DIR);
15891
15892 if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
15893 !(vmu = find_or_create(testcontext, testmailbox))) {
15894 ast_test_status_update(test, "Cannot create vmu structure\n");
15895 return AST_TEST_NOT_RUN;
15896 }
15897
15898 if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
15899 ast_test_status_update(test, "Cannot find vmu structure?!!\n");
15900 return AST_TEST_NOT_RUN;
15901 }
15902
15903 populate_defaults(vmu);
15904 vmu->email = ast_strdup("test2@example.net");
15905#ifdef IMAP_STORAGE
15906 /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
15907#endif
15908
15909 file = tmpfile();
15910 for (which = 0; which < ARRAY_LEN(test_items); which++) {
15911 /* Kill previous test, if any */
15912 rewind(file);
15913 if (ftruncate(fileno(file), 0)) {
15914 ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
15915 res = AST_TEST_FAIL;
15916 break;
15917 }
15918
15919 /* Make each change, in order, to the test mailbox */
15920 if (test_items[which].type == INT) {
15921 *((int *) test_items[which].location) = test_items[which].u.intval;
15922 } else if (test_items[which].type == FLAGVAL) {
15923 if (ast_test_flag(vmu, test_items[which].u.intval)) {
15924 ast_clear_flag(vmu, test_items[which].u.intval);
15925 } else {
15926 ast_set_flag(vmu, test_items[which].u.intval);
15927 }
15928 } else if (test_items[which].type == STATIC) {
15929 strcpy(test_items[which].location, test_items[which].u.strval);
15930 } else if (test_items[which].type == STRPTR) {
15931 test_items[which].location = test_items[which].u.strval;
15932 }
15933
15934 make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL, NULL);
15935 rewind(file);
15936 while (fgets(buf, sizeof(buf), file)) {
15937 if (
15938 (strlen(buf) > 1 &&
15939#ifdef IMAP_STORAGE
15940 buf[strlen(buf) - 2] != '\r'
15941#else
15942 buf[strlen(buf) - 2] == '\r'
15943#endif
15944 )
15945 || buf[strlen(buf) - 1] != '\n') {
15946 res = AST_TEST_FAIL;
15947 }
15948 }
15949 }
15950 fclose(file);
15951 free_user(vmu);
15952 force_reload_config(); /* Restore original config */
15953 return res;
15954}
15955
15956AST_TEST_DEFINE(test_voicemail_load_config)
15957{
15958 int res = AST_TEST_PASS;
15959 struct ast_vm_user *vmu;
15960 struct ast_config *cfg;
15961 char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
15962 int fd;
15963 FILE *file;
15964 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
15965
15966 switch (cmd) {
15967 case TEST_INIT:
15968 info->name = "test_voicemail_load_config";
15969 info->category = "/apps/app_voicemail/";
15970 info->summary = "Test loading Voicemail config";
15971 info->description =
15972 "Verify that configuration is loaded consistently. "
15973 "This is to test regressions of ASTERISK-18838 where it was noticed that "
15974 "some options were loaded after the mailboxes were instantiated, causing "
15975 "those options not to be set correctly.";
15976 return AST_TEST_NOT_RUN;
15977 case TEST_EXECUTE:
15978 break;
15979 }
15980
15981 /* build a config file by hand... */
15982 if ((fd = mkstemp(config_filename)) < 0) {
15983 return AST_TEST_FAIL;
15984 }
15985 if (!(file = fdopen(fd, "w"))) {
15986 close(fd);
15987 unlink(config_filename);
15988 return AST_TEST_FAIL;
15989 }
15990 fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
15991 fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
15992 fputs("00000002 => 9999,Mrs. Test\n", file);
15993 fclose(file);
15994
15995 if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
15996 res = AST_TEST_FAIL;
15997 goto cleanup;
15998 }
15999
16000 load_config_from_memory(1, cfg);
16001 ast_config_destroy(cfg);
16002
16003#define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
16004 ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
16005 u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
16006
16008 AST_LIST_TRAVERSE(&users, vmu, list) {
16009 if (!strcmp(vmu->mailbox, "00000001")) {
16010 if (0); /* trick to get CHECK to work */
16011 CHECK(vmu, callback, "othercontext")
16012 CHECK(vmu, locale, "nl_NL.UTF-8")
16013 CHECK(vmu, zonetag, "central")
16014 } else if (!strcmp(vmu->mailbox, "00000002")) {
16015 if (0); /* trick to get CHECK to work */
16016 CHECK(vmu, callback, "somecontext")
16017 CHECK(vmu, locale, "de_DE.UTF-8")
16018 CHECK(vmu, zonetag, "european")
16019 }
16020 }
16022
16023#undef CHECK
16024
16025 /* Forcibly restore the original config, to reinitialize after test */
16026 force_reload_config(); /* this might say "Failed to load configuration file." */
16027
16028cleanup:
16029 unlink(config_filename);
16030 return res;
16031}
16032
16033AST_TEST_DEFINE(test_voicemail_vm_info)
16034{
16035 struct ast_vm_user *vmu;
16036 struct ast_channel *chan = NULL;
16037 const char testcontext[] = "test";
16038 const char testmailbox[] = "00000000";
16039 const char vminfo_cmd[] = "VM_INFO";
16040 char vminfo_buf[256], vminfo_args[256];
16041 int res = AST_TEST_PASS;
16042 int test_ret = 0;
16043 int test_counter = 0;
16044
16045 struct {
16046 char *vminfo_test_args;
16047 char *vminfo_expected;
16048 int vminfo_ret;
16049 } test_items[] = {
16050 { "", "", -1 }, /* Missing argument */
16051 { "00000000@test,badparam", "", -1 }, /* Wrong argument */
16052 { "00000000@test", "", -1 }, /* Missing argument */
16053 { "00000000@test,exists", "1", 0 },
16054 { "11111111@test,exists", "0", 0 }, /* Invalid mailbox */
16055 { "00000000@test,email", "vm-info-test@example.net", 0 },
16056 { "11111111@test,email", "", 0 }, /* Invalid mailbox */
16057 { "00000000@test,fullname", "Test Framework Mailbox", 0 },
16058 { "00000000@test,pager", "vm-info-pager-test@example.net", 0 },
16059 { "00000000@test,locale", "en_US", 0 },
16060 { "00000000@test,tz", "central", 0 },
16061 { "00000000@test,language", "en", 0 },
16062 { "00000000@test,password", "9876", 0 },
16063 };
16064
16065 switch (cmd) {
16066 case TEST_INIT:
16067 info->name = "test_voicemail_vm_info";
16068 info->category = "/apps/app_voicemail/";
16069 info->summary = "VM_INFO unit test";
16070 info->description =
16071 "This tests passing various parameters to VM_INFO";
16072 return AST_TEST_NOT_RUN;
16073 case TEST_EXECUTE:
16074 break;
16075 }
16076
16077 if (!(chan = ast_dummy_channel_alloc())) {
16078 ast_test_status_update(test, "Unable to create dummy channel\n");
16079 return AST_TEST_FAIL;
16080 }
16081
16082 if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
16083 !(vmu = find_or_create(testcontext, testmailbox))) {
16084 ast_test_status_update(test, "Cannot create vmu structure\n");
16085 chan = ast_channel_unref(chan);
16086 return AST_TEST_FAIL;
16087 }
16088
16089 populate_defaults(vmu);
16090
16091 vmu->email = ast_strdup("vm-info-test@example.net");
16092 if (!vmu->email) {
16093 ast_test_status_update(test, "Cannot create vmu email\n");
16094 chan = ast_channel_unref(chan);
16095 return AST_TEST_FAIL;
16096 }
16097 ast_copy_string(vmu->fullname, "Test Framework Mailbox", sizeof(vmu->fullname));
16098 ast_copy_string(vmu->pager, "vm-info-pager-test@example.net", sizeof(vmu->pager));
16099 ast_copy_string(vmu->language, "en", sizeof(vmu->language));
16100 ast_copy_string(vmu->zonetag, "central", sizeof(vmu->zonetag));
16101 ast_copy_string(vmu->locale, "en_US", sizeof(vmu->zonetag));
16102 ast_copy_string(vmu->password, "9876", sizeof(vmu->password));
16103
16104 for (test_counter = 0; test_counter < ARRAY_LEN(test_items); test_counter++) {
16105 ast_copy_string(vminfo_args, test_items[test_counter].vminfo_test_args, sizeof(vminfo_args));
16106 test_ret = acf_vm_info(chan, vminfo_cmd, vminfo_args, vminfo_buf, sizeof(vminfo_buf));
16107 if (strcmp(vminfo_buf, test_items[test_counter].vminfo_expected)) {
16108 ast_test_status_update(test, "VM_INFO response was: '%s', but expected: '%s'\n", vminfo_buf, test_items[test_counter].vminfo_expected);
16109 res = AST_TEST_FAIL;
16110 }
16111 if (!(test_ret == test_items[test_counter].vminfo_ret)) {
16112 ast_test_status_update(test, "VM_INFO return code was: '%i', but expected '%i'\n", test_ret, test_items[test_counter].vminfo_ret);
16113 res = AST_TEST_FAIL;
16114 }
16115 }
16116
16117 chan = ast_channel_unref(chan);
16118 free_user(vmu);
16119 return res;
16120}
16121#endif /* defined(TEST_FRAMEWORK) */
16122
16123static const struct ast_vm_functions vm_table = {
16125 .module_name = AST_MODULE,
16126
16127 .has_voicemail = has_voicemail,
16128 .inboxcount = inboxcount,
16129 .inboxcount2 = inboxcount2,
16130 .messagecount = messagecount,
16131 .copy_recording_to_vm = msg_create_from_file,
16132 .index_to_foldername = vm_index_to_foldername,
16133 .mailbox_snapshot_create = vm_mailbox_snapshot_create,
16134 .mailbox_snapshot_destroy = vm_mailbox_snapshot_destroy,
16135 .msg_move = vm_msg_move,
16136 .msg_remove = vm_msg_remove,
16137 .msg_forward = vm_msg_forward,
16138 .msg_play = vm_msg_play,
16139};
16140
16143 .module_name = AST_MODULE,
16144
16145 .sayname = vm_sayname,
16146};
16147
16148static int reload(void)
16149{
16150 return load_config(1);
16151}
16152
16153static int unload_module(void)
16154{
16155 int res;
16156
16163 res |= ast_manager_unregister("VoicemailUsersList");
16164 res |= ast_manager_unregister("VoicemailUserStatus");
16165 res |= ast_manager_unregister("VoicemailRefresh");
16166 res |= ast_manager_unregister("VoicemailBoxSummary");
16167 res |= ast_manager_unregister("VoicemailMove");
16168 res |= ast_manager_unregister("VoicemailRemove");
16169 res |= ast_manager_unregister("VoicemailForward");
16170#ifdef TEST_FRAMEWORK
16171 res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
16172 res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
16173 res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
16174 res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
16175 res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
16176 res |= AST_TEST_UNREGISTER(test_voicemail_vm_info);
16177#endif
16181#ifdef TEST_FRAMEWORK
16182 ast_uninstall_vm_test_functions();
16183#endif
16185
16186 ao2_container_unregister("voicemail_alias_mailbox_mappings");
16188 ao2_container_unregister("voicemail_mailbox_alias_mappings");
16190
16193
16195 ast_unload_realtime("voicemail");
16196 ast_unload_realtime("voicemail_data");
16197
16198#ifdef IMAP_STORAGE
16199 ast_mwi_state_callback_all(imap_close_subscribed_mailbox, NULL);
16200#endif
16201 free_vm_users();
16202 free_vm_zones();
16203 return res;
16204}
16205
16206static void print_mappings(void *v_obj, void *where, ao2_prnt_fn *prnt)
16207{
16208 struct alias_mailbox_mapping *mapping = v_obj;
16209
16210 if (!mapping) {
16211 return;
16212 }
16213 prnt(where, "Alias: %s Mailbox: %s", mapping->alias, mapping->mailbox);
16214}
16215
16216/*!
16217 * \brief Load the module
16218 *
16219 * Module loading including tests for configuration or dependencies.
16220 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
16221 * or AST_MODULE_LOAD_SUCCESS.
16222 *
16223 * If a dependency, allocation or environment variable fails tests, return AST_MODULE_LOAD_FAILURE.
16224 *
16225 * If the module can't load the configuration file, can't register as a provider or
16226 * has another issue not fatal to Asterisk itself, return AST_MODULE_LOAD_DECLINE.
16227 *
16228 * On success return AST_MODULE_LOAD_SUCCESS.
16229 */
16230static int load_module(void)
16231{
16232 int res;
16233 my_umask = umask(0);
16234 umask(my_umask);
16235
16238 if (!inprocess_container) {
16240 }
16241
16243 alias_mailbox_mapping_hash_fn, NULL, alias_mailbox_mapping_cmp_fn);
16245 ast_log(LOG_ERROR, "Unable to create alias_mailbox_mappings container\n");
16248 }
16249 res = ao2_container_register("voicemail_alias_mailbox_mappings", alias_mailbox_mappings, print_mappings);
16250 if (res) {
16251 ast_log(LOG_ERROR, "Unable to register alias_mailbox_mappings container\n");
16255 }
16256
16258 mailbox_alias_mapping_hash_fn, NULL, mailbox_alias_mapping_cmp_fn);
16260 ast_log(LOG_ERROR, "Unable to create mailbox_alias_mappings container\n");
16262 ao2_container_unregister("voicemail_alias_mailbox_mappings");
16265 }
16266 res = ao2_container_register("voicemail_mailbox_alias_mappings", mailbox_alias_mappings, print_mappings);
16267 if (res) {
16268 ast_log(LOG_ERROR, "Unable to register mailbox_alias_mappings container\n");
16270 ao2_container_unregister("voicemail_alias_mailbox_mappings");
16274 }
16275
16276 /* compute the location of the voicemail spool directory */
16277 snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
16278
16279 if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
16280 ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor. MWI will not work\n");
16281 }
16282
16283 if ((res = load_config(0))) {
16284 unload_module();
16286 }
16287
16301#ifdef TEST_FRAMEWORK
16302 res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
16303 res |= AST_TEST_REGISTER(test_voicemail_msgcount);
16304 res |= AST_TEST_REGISTER(test_voicemail_vmuser);
16305 res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
16306 res |= AST_TEST_REGISTER(test_voicemail_load_config);
16307 res |= AST_TEST_REGISTER(test_voicemail_vm_info);
16308#endif
16309
16310 if (res) {
16311 ast_log(LOG_ERROR, "Failure registering applications, functions or tests\n");
16312 unload_module();
16314 }
16315
16316 /* ast_vm_register may return DECLINE if another module registered for vm */
16317 res = ast_vm_register(&vm_table);
16318 if (res) {
16319 ast_log(LOG_ERROR, "Failure registering as a voicemail provider\n");
16320 unload_module();
16322 }
16323
16324 /* ast_vm_greeter_register may return DECLINE if another module registered as a greeter */
16326 if (res) {
16327 ast_log(LOG_ERROR, "Failure registering as a greeter provider\n");
16328 unload_module();
16330 }
16331
16333
16334#ifdef TEST_FRAMEWORK
16335 ast_install_vm_test_functions(vm_test_create_user, vm_test_destroy_user);
16336#endif
16337
16338 ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
16339 ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
16340
16342}
16343
16344static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
16345{
16346 int cmd = 0;
16347 char destination[80] = "";
16348 int retries = 0;
16349
16350 if (!num) {
16351 ast_verb(3, "Destination number will be entered manually\n");
16352 while (retries < 3 && cmd != 't') {
16353 destination[1] = '\0';
16354 destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
16355 if (!cmd)
16356 destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
16357 if (!cmd)
16358 destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
16359 if (!cmd) {
16360 cmd = ast_waitfordigit(chan, 6000);
16361 if (cmd)
16362 destination[0] = cmd;
16363 }
16364 if (!cmd) {
16365 retries++;
16366 } else {
16367
16368 if (cmd < 0)
16369 return 0;
16370 if (cmd == '*') {
16371 ast_verb(3, "User hit '*' to cancel outgoing call\n");
16372 return 0;
16373 }
16374 if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0)
16375 retries++;
16376 else
16377 cmd = 't';
16378 }
16379 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
16380 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
16381 }
16382 if (retries >= 3) {
16383 return 0;
16384 }
16385
16386 } else {
16387 ast_verb(3, "Destination number is CID number '%s'\n", num);
16388 ast_copy_string(destination, num, sizeof(destination));
16389 }
16390
16391 if (!ast_strlen_zero(destination)) {
16392 if (destination[strlen(destination) -1 ] == '*')
16393 return 0;
16394 ast_verb(3, "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, ast_channel_context(chan));
16395 ast_channel_exten_set(chan, destination);
16396 ast_channel_context_set(chan, outgoing_context);
16397 ast_channel_priority_set(chan, 0);
16398 return 9;
16399 }
16400 return 0;
16401}
16402
16403/*!
16404 * \brief The advanced options within a message.
16405 * \param chan
16406 * \param vmu
16407 * \param vms
16408 * \param msg
16409 * \param option
16410 * \param record_gain
16411 *
16412 * Provides handling for the play message envelope, call the person back, or reply to message.
16413 *
16414 * \return zero on success, -1 on error.
16415 */
16416static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain)
16417{
16418 int res = 0;
16419 char filename[PATH_MAX];
16420 struct ast_config *msg_cfg = NULL;
16421 const char *origtime, *context;
16422 char *name, *num;
16423 int retries = 0;
16424 char *cid;
16425 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
16426
16427 vms->starting = 0;
16428
16429 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
16430
16431 /* Retrieve info from VM attribute file */
16432 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
16433 RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
16434 msg_cfg = ast_config_load(filename, config_flags);
16435 DISPOSE(vms->curdir, vms->curmsg);
16436 if (!valid_config(msg_cfg)) {
16437 ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
16438 return 0;
16439 }
16440
16441 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
16442 ast_config_destroy(msg_cfg);
16443 return 0;
16444 }
16445
16446 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
16447
16448 context = ast_variable_retrieve(msg_cfg, "message", "context");
16449 switch (option) {
16450 case 3: /* Play message envelope */
16451 if (!res) {
16452 res = play_message_datetime(chan, vmu, origtime, filename);
16453 }
16454 if (!res) {
16455 res = play_message_callerid(chan, vms, cid, context, 0, 1);
16456 }
16457
16458 res = 't';
16459 break;
16460
16461 case 2: /* Call back */
16462
16463 if (ast_strlen_zero(cid))
16464 break;
16465
16466 ast_callerid_parse(cid, &name, &num);
16467 while ((res > -1) && (res != 't')) {
16468 switch (res) {
16469 case '1':
16470 if (num) {
16471 /* Dial the CID number */
16472 res = dialout(chan, vmu, num, vmu->callback);
16473 if (res) {
16474 ast_config_destroy(msg_cfg);
16475 return 9;
16476 }
16477 } else {
16478 res = '2';
16479 }
16480 break;
16481
16482 case '2':
16483 /* Want to enter a different number, can only do this if there's a dialout context for this user */
16484 if (!ast_strlen_zero(vmu->dialout)) {
16485 res = dialout(chan, vmu, NULL, vmu->dialout);
16486 if (res) {
16487 ast_config_destroy(msg_cfg);
16488 return 9;
16489 }
16490 } else {
16491 ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
16492 res = ast_play_and_wait(chan, "vm-sorry");
16493 }
16494 ast_config_destroy(msg_cfg);
16495 return res;
16496 case '*':
16497 res = 't';
16498 break;
16499 case '3':
16500 case '4':
16501 case '5':
16502 case '6':
16503 case '7':
16504 case '8':
16505 case '9':
16506 case '0':
16507
16508 res = ast_play_and_wait(chan, "vm-sorry");
16509 retries++;
16510 break;
16511 default:
16512 if (num) {
16513 ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
16514 res = ast_play_and_wait(chan, "vm-num-i-have");
16515 if (!res)
16516 res = play_message_callerid(chan, vms, num, vmu->context, 1, 1);
16517 if (!res)
16518 res = ast_play_and_wait(chan, "vm-tocallnum");
16519 /* Only prompt for a caller-specified number if there is a dialout context specified */
16520 if (!ast_strlen_zero(vmu->dialout)) {
16521 if (!res)
16522 res = ast_play_and_wait(chan, "vm-calldiffnum");
16523 }
16524 } else {
16525 res = ast_play_and_wait(chan, "vm-nonumber");
16526 if (!ast_strlen_zero(vmu->dialout)) {
16527 if (!res)
16528 res = ast_play_and_wait(chan, "vm-toenternumber");
16529 }
16530 }
16531 if (!res) {
16532 res = ast_play_and_wait(chan, "vm-star-cancel");
16533 }
16534 if (!res) {
16535 res = ast_waitfordigit(chan, 6000);
16536 }
16537 if (!res) {
16538 retries++;
16539 if (retries > 3) {
16540 res = 't';
16541 }
16542 }
16543 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
16544 isprint(res) ? res : '?', isprint(res) ? res : '?');
16545 break;
16546
16547 }
16548 if (res == 't')
16549 res = 0;
16550 else if (res == '*')
16551 res = -1;
16552 }
16553 break;
16554
16555 case 1: /* Reply */
16556 /* Send reply directly to sender */
16557 if (ast_strlen_zero(cid))
16558 break;
16559
16560 ast_callerid_parse(cid, &name, &num);
16561 if (!num) {
16562 ast_verb(3, "No CID number available, no reply sent\n");
16563 if (!res)
16564 res = ast_play_and_wait(chan, "vm-nonumber");
16565 ast_config_destroy(msg_cfg);
16566 return res;
16567 } else {
16568 struct ast_vm_user vmu2, *vmu3;
16569 memset(&vmu2, 0, sizeof(vmu2));
16570 vmu3 = find_user(&vmu2, vmu->context, num);
16571 if (vmu3) {
16572 struct leave_vm_options leave_options;
16573 char mailbox[AST_MAX_EXTENSION * 2 + 2];
16574 snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
16575
16576 ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
16577
16578 memset(&leave_options, 0, sizeof(leave_options));
16579 leave_options.record_gain = record_gain;
16580 leave_options.beeptone = "beep";
16581 res = leave_voicemail(chan, mailbox, &leave_options);
16582 if (!res)
16583 res = 't';
16584 ast_config_destroy(msg_cfg);
16585 free_user(vmu3);
16586 return res;
16587 } else {
16588 /* Sender has no mailbox, can't reply */
16589 ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
16590 ast_play_and_wait(chan, "vm-nobox");
16591 res = 't';
16592 ast_config_destroy(msg_cfg);
16593 return res;
16594 }
16595 }
16596 res = 0;
16597
16598 break;
16599 }
16600
16601 ast_config_destroy(msg_cfg);
16602
16603#ifndef IMAP_STORAGE
16604 if (!res) {
16605 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
16606 vms->heard[msg] = 1;
16607 res = wait_file(chan, vms, vms->fn);
16608 }
16609#endif
16610 return res;
16611}
16612
16613static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
16614 int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
16615 signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro)
16616{
16617 /* Record message & let caller review or re-record it, or set options if applicable */
16618 int res = 0;
16619 int cmd = 0;
16620 int max_attempts = 3;
16621 int attempts = 0;
16622 int recorded = 0;
16623 int msg_exists = 0;
16624 signed char zero_gain = 0;
16625 char tempfile[PATH_MAX];
16626 char *acceptdtmf = "#";
16627 char *canceldtmf = "";
16628 int canceleddtmf = 0;
16629 SCOPE_ENTER(3, "%s: rf: %s fmt: %s type: %s vmu: %s\n",
16630 ast_channel_name(chan), recordfile, fmt, outsidecaller ? "msg" : "greeting",
16631 vmu->mailbox);
16632 /* Note that urgent and private are for flagging messages as such in the future */
16633
16634 /* barf if no pointer passed to store duration in */
16635 if (duration == NULL) {
16636 SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING, "%s\n", "Error play_record_review called without duration pointer\n");
16637 }
16638
16639 if (!outsidecaller)
16640 snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
16641 else
16642 ast_copy_string(tempfile, recordfile, sizeof(tempfile));
16643
16644 cmd = '3'; /* Want to start by recording */
16645
16646 while ((cmd >= 0) && (cmd != 't')) {
16647 switch (cmd) {
16648 case '1':
16649 if (!msg_exists) {
16650 /* In this case, 1 is to record a message */
16651 cmd = '3';
16652 break;
16653 } else {
16654 /* Otherwise 1 is to save the existing message */
16655 ast_verb(3, "Saving message as is\n");
16656 if (!outsidecaller) {
16657 ast_trace(-1, "Renaming greeting '%s' to '%s'\n", tempfile, recordfile);
16658 ast_filerename(tempfile, recordfile, NULL);
16659 }
16660 if (!forwardintro) {
16661 ast_stream_and_wait(chan, "vm-msgsaved", "");
16662 }
16663 if (!outsidecaller) {
16664 /* Saves to IMAP server only if imapgreeting=yes */
16665 ast_trace(-1, "Saving greeting '%s'\n", recordfile);
16666 SCOPE_CALL(-1, STORE, recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag, msg_id);
16667 SCOPE_CALL(-1, DISPOSE, recordfile, -1);
16668 }
16669 cmd = 't';
16670 SCOPE_EXIT_RTN_VALUE(res, "Message saved to %s\n", recordfile);
16671 }
16672 case '2':
16673 /* Review */
16674 ast_verb(3, "Reviewing the message\n");
16675 ast_trace(-1, "Reviewing '%s'\n", tempfile);
16676 cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
16677 break;
16678 case '3':
16679 msg_exists = 0;
16680 /* Record */
16681 if (recorded == 1)
16682 ast_verb(3, "Re-recording the message\n");
16683 else
16684 ast_verb(3, "Recording the message\n");
16685
16686 if (recorded && outsidecaller) {
16687 if (forwardintro) {
16688 cmd = ast_play_and_wait(chan, "vm-record-prepend");
16689 } else {
16690 cmd = ast_play_and_wait(chan, INTRO);
16691 }
16692 cmd = ast_play_and_wait(chan, "beep");
16693 }
16694 if (cmd == -1) {
16695 /* User has hung up, no options to give */
16696 ast_filedelete(tempfile, NULL);
16697 SCOPE_EXIT_RTN_VALUE(cmd, "User hung up before message could be rerecorded. Deleted '%s'\n", tempfile);
16698 }
16699 recorded = 1;
16700 /* 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 */
16701 if (record_gain)
16703 if (ast_test_flag(vmu, VM_OPERATOR))
16704 canceldtmf = "0";
16705 ast_trace(-1, "Recording '%s'\n", tempfile);
16706 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);
16707 if (strchr(canceldtmf, cmd)) {
16708 /* need this flag here to distinguish between pressing '0' during message recording or after */
16709 canceleddtmf = 1;
16710 }
16711 if (record_gain)
16712 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
16713 if (cmd == -1) {
16714 /* User has hung up, no options to give */
16715 if (!outsidecaller) {
16716 /* user was recording a greeting and they hung up, so let's delete the recording. */
16717 ast_filedelete(tempfile, NULL);
16718 }
16719 SCOPE_EXIT_RTN_VALUE(cmd, "User hung up after recording. %s %s\n",
16720 outsidecaller ? "Saved message " : "Deleted greeting \n", tempfile);
16721 }
16722 if (cmd == '0') {
16723 break;
16724 } else if (cmd == '*') {
16725 break;
16726#if 0
16727 } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
16728 /* Message is too short */
16729 ast_verb(3, "Message too short\n");
16730 cmd = ast_play_and_wait(chan, "vm-tooshort");
16731 cmd = ast_filedelete(tempfile, NULL);
16732 break;
16733 } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
16734 /* Message is all silence */
16735 ast_verb(3, "Nothing recorded\n");
16736 cmd = ast_filedelete(tempfile, NULL);
16737 cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
16738 if (!cmd)
16739 cmd = ast_play_and_wait(chan, "vm-speakup");
16740 break;
16741#endif
16742 } else {
16743 /* If all is well, a message exists */
16744 msg_exists = 1;
16745 cmd = 0;
16746 }
16747 break;
16748 case '4':
16749 if (outsidecaller && ast_test_flag(vmu, VM_MARK_URGENT)) { /* only mark vm messages */
16750 /* Mark Urgent */
16751 if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
16752 ast_verb(3, "marking message as Urgent\n");
16753 res = ast_play_and_wait(chan, "vm-marked-urgent");
16754 strcpy(flag, "Urgent");
16755 } else if (flag) {
16756 ast_verb(3, "UNmarking message as Urgent\n");
16757 res = ast_play_and_wait(chan, "vm-marked-nonurgent");
16758 strcpy(flag, "");
16759 } else {
16760 ast_play_and_wait(chan, "vm-sorry");
16761 }
16762 cmd = 0;
16763 } else {
16764 cmd = ast_play_and_wait(chan, "vm-sorry");
16765 }
16766 break;
16767 case '5':
16768 case '6':
16769 case '7':
16770 case '8':
16771 case '9':
16772 case '*':
16773 case '#':
16774 cmd = ast_play_and_wait(chan, "vm-sorry");
16775 break;
16776#if 0
16777/* XXX Commented out for the moment because of the dangers of deleting
16778 a message while recording (can put the message numbers out of sync) */
16779 case '*':
16780 /* Cancel recording, delete message, offer to take another message*/
16781 cmd = ast_play_and_wait(chan, "vm-deleted");
16782 cmd = ast_filedelete(tempfile, NULL);
16783 if (outsidecaller) {
16784 res = vm_exec(chan, NULL);
16785 return res;
16786 }
16787 else
16788 return 1;
16789#endif
16790 case '0':
16791 if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
16792 cmd = ast_play_and_wait(chan, "vm-sorry");
16793 break;
16794 }
16795 if (msg_exists || recorded) {
16796 ast_trace(-1, "Reviewing '%s'\n", tempfile);
16797 cmd = ast_play_and_wait(chan, "vm-saveoper");
16798 if (!cmd)
16799 cmd = ast_waitfordigit(chan, 3000);
16800 if (cmd == '1') {
16801 ast_trace(-1, "Saving '%s' to '%s'\n", tempfile, recordfile);
16802 ast_filerename(tempfile, recordfile, NULL);
16803 ast_play_and_wait(chan, "vm-msgsaved");
16804 cmd = '0';
16805 } else if (cmd == '4') {
16806 if (flag) {
16807 ast_play_and_wait(chan, "vm-marked-urgent");
16808 strcpy(flag, "Urgent");
16809 }
16810 ast_play_and_wait(chan, "vm-msgsaved");
16811 cmd = '0';
16812 } else {
16813 ast_trace(-1, "Deleting '%s'\n", tempfile);
16814 ast_play_and_wait(chan, "vm-deleted");
16815 SCOPE_CALL(-1, DELETE, tempfile, -1, tempfile, vmu);
16816 SCOPE_CALL(-1, DISPOSE, tempfile, -1);
16817 cmd = '0';
16818 }
16819 }
16820 return cmd;
16821 default:
16822 /* If the caller is an outside caller and the review option is enabled or it's forward intro
16823 allow them to review the message, but let the owner of the box review
16824 their OGM's */
16825 if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW) && !forwardintro) {
16826 SCOPE_EXIT_RTN_VALUE(cmd, "Done. Outside caller, review not set, no forwardintro\n");
16827 }
16828 if (msg_exists) {
16829 cmd = ast_play_and_wait(chan, "vm-review");
16830 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_MARK_URGENT)) {
16831 if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
16832 cmd = ast_play_and_wait(chan, "vm-review-urgent");
16833 } else if (flag) {
16834 cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
16835 }
16836 }
16837 } else {
16838 cmd = ast_play_and_wait(chan, "vm-torerecord");
16839 if (!cmd)
16840 cmd = ast_waitfordigit(chan, 600);
16841 }
16842
16843 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
16844 cmd = ast_play_and_wait(chan, "vm-reachoper");
16845 if (!cmd)
16846 cmd = ast_waitfordigit(chan, 600);
16847 }
16848#if 0
16849 if (!cmd)
16850 cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
16851#endif
16852 if (!cmd)
16853 cmd = ast_waitfordigit(chan, 6000);
16854 if (!cmd) {
16855 attempts++;
16856 }
16857 if (attempts > max_attempts) {
16858 cmd = 't';
16859 }
16860 }
16861 }
16862 if (!outsidecaller && (cmd == -1 || cmd == 't')) {
16863 /* Hang up or timeout, so delete the recording. */
16864 ast_trace(-1, "Deleting '%s' on hangup or timeout\n", tempfile);
16865 ast_filedelete(tempfile, NULL);
16866 }
16867
16868 if (cmd != 't' && outsidecaller)
16869 ast_play_and_wait(chan, "vm-goodbye");
16870
16871 SCOPE_EXIT_RTN_VALUE(cmd, "Done\n");
16872}
16873
16875{
16876 struct ast_vm_msg_snapshot *msg_snapshot;
16877
16878 if (!(msg_snapshot = ast_calloc(1, sizeof(*msg_snapshot)))) {
16879 return NULL;
16880 }
16881
16882 if (ast_string_field_init(msg_snapshot, 512)) {
16883 ast_free(msg_snapshot);
16884 return NULL;
16885 }
16886
16887 return msg_snapshot;
16888}
16889
16891{
16892 ast_string_field_free_memory(msg_snapshot);
16893 ast_free(msg_snapshot);
16894
16895 return NULL;
16896}
16897
16898#ifdef TEST_FRAMEWORK
16899
16900static int vm_test_destroy_user(const char *context, const char *mailbox)
16901{
16902 struct ast_vm_user *vmu;
16903
16906 if (!strcmp(context, vmu->context)
16907 && !strcmp(mailbox, vmu->mailbox)) {
16909 ast_free(vmu);
16910 break;
16911 }
16912 }
16915 return 0;
16916}
16917
16918static int vm_test_create_user(const char *context, const char *mailbox)
16919{
16920 struct ast_vm_user *vmu;
16921
16922 if (!(vmu = find_or_create(context, mailbox))) {
16923 return -1;
16924 }
16925 populate_defaults(vmu);
16926 return 0;
16927}
16928
16929#endif
16930
16931/*!
16932 * \brief Create and store off all the msgs in an open mailbox
16933 *
16934 * \note TODO XXX This function should work properly for all
16935 * voicemail storage options, but is far more expensive for
16936 * ODBC at the moment. This is because the RETRIEVE macro
16937 * not only pulls out the message's meta data file from the
16938 * database, but also the actual audio for each message, temporarily
16939 * writing it to the file system. This is an area that needs
16940 * to be made more efficient.
16941 */
16943 struct vm_state *vms,
16944 struct ast_vm_mailbox_snapshot *mailbox_snapshot,
16945 int snapshot_index,
16946 int mailbox_index,
16947 int descending,
16948 enum ast_vm_snapshot_sort_val sort_val)
16949{
16950 struct ast_vm_msg_snapshot *msg_snapshot;
16951 struct ast_vm_msg_snapshot *msg_snapshot_tmp;
16952 struct ast_config *msg_cfg;
16953 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
16954 char filename[PATH_MAX];
16955 const char *value;
16956
16957 for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
16958 int inserted = 0;
16959 /* Find the msg */
16960 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
16961 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
16962 RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
16963 msg_cfg = ast_config_load(filename, config_flags);
16964 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
16965 DISPOSE(vms->curdir, vms->curmsg);
16966 continue;
16967 }
16968
16969 /* Create the snapshot object */
16970 if (!(msg_snapshot = vm_msg_snapshot_alloc())) {
16971 ast_config_destroy(msg_cfg);
16972 return -1;
16973 }
16974
16975 /* Fill in the snapshot object */
16976 if ((value = ast_variable_retrieve(msg_cfg, "message", "msg_id"))) {
16977 ast_string_field_set(msg_snapshot, msg_id, value);
16978 } else {
16979 /* Message snapshots *really* should have a
16980 * message ID. Add one to the message config
16981 * if it does not already exist
16982 */
16983 char id[MSG_ID_LEN];
16984 if (!(add_message_id(msg_cfg, vms->curdir, vms->curmsg,
16985 filename, id, sizeof(id), vmu, mailbox_index))) {
16986 ast_string_field_set(msg_snapshot, msg_id, id);
16987 } else {
16988 ast_log(LOG_WARNING, "Unable to create a message ID for message %s/%d\n", vms->curdir, vms->curmsg);
16989 }
16990 }
16991 if ((value = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
16992 ast_string_field_set(msg_snapshot, callerid, value);
16993 }
16994 if ((value = ast_variable_retrieve(msg_cfg, "message", "callerchan"))) {
16995 ast_string_field_set(msg_snapshot, callerchan, value);
16996 }
16997 if ((value = ast_variable_retrieve(msg_cfg, "message", "exten"))) {
16998 ast_string_field_set(msg_snapshot, exten, value);
16999 }
17000 if ((value = ast_variable_retrieve(msg_cfg, "message", "origdate"))) {
17001 ast_string_field_set(msg_snapshot, origdate, value);
17002 }
17003 if ((value = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
17004 ast_string_field_set(msg_snapshot, origtime, value);
17005 }
17006 if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17007 ast_string_field_set(msg_snapshot, duration, value);
17008 }
17009 if ((value = ast_variable_retrieve(msg_cfg, "message", "flag"))) {
17010 ast_string_field_set(msg_snapshot, flag, value);
17011 }
17012 msg_snapshot->msg_number = vms->curmsg;
17013 ast_string_field_set(msg_snapshot, folder_name, mailbox_folders[mailbox_index]);
17014
17015 /* store msg snapshot in mailbox snapshot */
17016 switch (sort_val) {
17017 default:
17019 if (descending) {
17020 AST_LIST_INSERT_HEAD(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17021 } else {
17022 AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17023 }
17024 inserted = 1;
17025 break;
17027 AST_LIST_TRAVERSE_SAFE_BEGIN(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot_tmp, msg) {
17028 int val = strcmp(msg_snapshot->origtime, msg_snapshot_tmp->origtime);
17029 if (descending && val >= 0) {
17030 AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
17031 inserted = 1;
17032 break;
17033 } else if (!descending && val <= 0) {
17034 AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
17035 inserted = 1;
17036 break;
17037 }
17038 }
17040 break;
17041 }
17042
17043 if (!inserted) {
17044 AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17045 }
17046
17047 mailbox_snapshot->total_msg_num++;
17048
17049 /* cleanup configs and msg */
17050 ast_config_destroy(msg_cfg);
17051 DISPOSE(vms->curdir, vms->curmsg);
17052 }
17053
17054 return 0;
17055}
17056
17058 const char *context,
17059 const char *folder,
17060 int descending,
17061 enum ast_vm_snapshot_sort_val sort_val,
17062 int combine_INBOX_and_OLD)
17063{
17064 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
17065 struct vm_state vms;
17066 struct ast_vm_user *vmu = NULL, vmus;
17067 int res;
17068 int i;
17069 int this_index_only = -1;
17070 int open = 0;
17071 int inbox_index = get_folder_by_name("INBOX");
17072 int old_index = get_folder_by_name("Old");
17073 int urgent_index = get_folder_by_name("Urgent");
17074
17075 if (ast_strlen_zero(mailbox)) {
17076 ast_log(LOG_WARNING, "Cannot create a mailbox snapshot since no mailbox was specified\n");
17077 return NULL;
17078 }
17079
17080 memset(&vmus, 0, sizeof(vmus));
17081
17082 if (!(ast_strlen_zero(folder))) {
17083 /* find the folder index */
17084 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
17085 if (!strcasecmp(mailbox_folders[i], folder)) {
17086 this_index_only = i;
17087 break;
17088 }
17089 }
17090 if (this_index_only == -1) {
17091 /* Folder was specified and it did not match any folder in our list */
17092 return NULL;
17093 }
17094 }
17095
17096 if (!(vmu = find_user(&vmus, context, mailbox))) {
17097 ast_log(AST_LOG_WARNING, "Failed to create mailbox snapshot for unknown voicemail user %s@%s\n", mailbox, context);
17098 return NULL;
17099 }
17100
17101 if (!(mailbox_snapshot = ast_calloc(1, sizeof(*mailbox_snapshot)))) {
17102 ast_log(AST_LOG_ERROR, "Failed to allocate memory for mailbox snapshot\n");
17103 free_user(vmu);
17104 return NULL;
17105 }
17106
17107 if (!(mailbox_snapshot->snapshots = ast_calloc(ARRAY_LEN(mailbox_folders), sizeof(*mailbox_snapshot->snapshots)))) {
17108 ast_free(mailbox_snapshot);
17109 free_user(vmu);
17110 return NULL;
17111 }
17112
17113 mailbox_snapshot->folders = ARRAY_LEN(mailbox_folders);
17114
17115 for (i = 0; i < mailbox_snapshot->folders; i++) {
17116 int msg_folder_index = i;
17117
17118 /* We want this message in the snapshot if any of the following:
17119 * No folder was specified.
17120 * The specified folder matches the current folder.
17121 * The specified folder is INBOX AND we were asked to combine messages AND the current folder is either Old or Urgent.
17122 */
17123 if (!(this_index_only == -1 || this_index_only == i || (this_index_only == inbox_index && combine_INBOX_and_OLD && (i == old_index || i == urgent_index)))) {
17124 continue;
17125 }
17126
17127 /* Make sure that Old or Urgent messages are marked as being in INBOX. */
17128 if (combine_INBOX_and_OLD && (i == old_index || i == urgent_index)) {
17129 msg_folder_index = inbox_index;
17130 }
17131
17132 memset(&vms, 0, sizeof(vms));
17133 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17134 vms.lastmsg = -1;
17135 open = 0;
17136
17137 /* open the mailbox state */
17138 if ((res = open_mailbox(&vms, vmu, i)) < 0) {
17139 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17140 goto snapshot_cleanup;
17141 }
17142 open = 1;
17143
17144 /* Iterate through each msg, storing off info */
17145 if (vms.lastmsg != -1) {
17146 if ((vm_msg_snapshot_create(vmu, &vms, mailbox_snapshot, msg_folder_index, i, descending, sort_val))) {
17147 ast_log(LOG_WARNING, "Failed to create msg snapshots for %s@%s\n", mailbox, context);
17148 goto snapshot_cleanup;
17149 }
17150 }
17151
17152 /* close mailbox */
17153 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17154 goto snapshot_cleanup;
17155 }
17156 open = 0;
17157 }
17158
17159snapshot_cleanup:
17160 if (vmu && open) {
17161 close_mailbox(&vms, vmu);
17162 }
17163
17164#ifdef IMAP_STORAGE
17165 if (vmu) {
17166 vmstate_delete(&vms);
17167 }
17168#endif
17169
17170 free_user(vmu);
17171 return mailbox_snapshot;
17172}
17173
17175{
17176 int i;
17177 struct ast_vm_msg_snapshot *msg_snapshot;
17178
17179 for (i = 0; i < mailbox_snapshot->folders; i++) {
17180 while ((msg_snapshot = AST_LIST_REMOVE_HEAD(&mailbox_snapshot->snapshots[i], msg))) {
17181 msg_snapshot = vm_msg_snapshot_destroy(msg_snapshot);
17182 }
17183 }
17184 ast_free(mailbox_snapshot->snapshots);
17185 ast_free(mailbox_snapshot);
17186 return NULL;
17187}
17188
17189/*!
17190 * \brief common bounds checking and existence check for Voicemail API functions.
17191 *
17192 * \details
17193 * This is called by vm_msg_move, vm_msg_remove, and vm_msg_forward to
17194 * ensure that data passed in are valid. This ensures that given the
17195 * desired message IDs, they can be found.
17196 *
17197 * \param vms The voicemail state corresponding to an open mailbox
17198 * \param msg_ids An array of message identifiers
17199 * \param num_msgs The number of identifiers in msg_ids
17200 * \param[out] msg_nums The message indexes corresponding to the given
17201 * \param vmu
17202 * message IDs
17203 * \pre vms must have open_mailbox() called on it prior to this function.
17204 *
17205 * \retval -1 Failure
17206 * \retval 0 Success
17207 */
17208static 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)
17209{
17210 int i;
17211 int res = 0;
17212 for (i = 0; i < num_msgs; ++i) {
17213 const char *msg_id = msg_ids[i];
17214 int found = 0;
17215 for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
17216 const char *other_msg_id;
17217 char filename[PATH_MAX];
17218 struct ast_config *msg_cfg;
17219 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17220
17221 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
17222 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
17223 RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
17224 msg_cfg = ast_config_load(filename, config_flags);
17225 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17226 DISPOSE(vms->curdir, vms->curmsg);
17227 res = -1;
17228 goto done;
17229 }
17230
17231 other_msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
17232
17233 if (!ast_strlen_zero(other_msg_id) && !strcmp(other_msg_id, msg_id)) {
17234 /* Message found. We can get out of this inner loop
17235 * and move on to the next message to find
17236 */
17237 found = 1;
17238 msg_nums[i] = vms->curmsg;
17239 ast_config_destroy(msg_cfg);
17240 DISPOSE(vms->curdir, vms->curmsg);
17241 break;
17242 }
17243 ast_config_destroy(msg_cfg);
17244 DISPOSE(vms->curdir, vms->curmsg);
17245 }
17246 if (!found) {
17247 /* If we can't find one of the message IDs requested, then OH NO! */
17248 res = -1;
17249 goto done;
17250 }
17251 }
17252
17253done:
17254 return res;
17255}
17256
17257static void notify_new_state(struct ast_vm_user *vmu)
17258{
17259 int new = 0, old = 0, urgent = 0;
17260 char ext_context[1024];
17261
17262 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
17263 run_externnotify(vmu->context, vmu->mailbox, NULL);
17264 ast_app_inboxcount2(ext_context, &urgent, &new, &old);
17265 queue_mwi_event(NULL, ext_context, urgent, new, old);
17266}
17267
17268static int vm_msg_forward(const char *from_mailbox,
17269 const char *from_context,
17270 const char *from_folder,
17271 const char *to_mailbox,
17272 const char *to_context,
17273 const char *to_folder,
17274 size_t num_msgs,
17275 const char *msg_ids [],
17276 int delete_old)
17277{
17278 struct vm_state from_vms;
17279 struct ast_vm_user *vmu = NULL, vmus;
17280 struct ast_vm_user *to_vmu = NULL, to_vmus;
17281 struct ast_config *msg_cfg;
17282 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17283 char filename[PATH_MAX];
17284 int from_folder_index;
17285 int open = 0;
17286 int res = 0;
17287 int i;
17288 int *msg_nums;
17289
17290 if (ast_strlen_zero(from_mailbox) || ast_strlen_zero(to_mailbox)) {
17291 ast_log(LOG_WARNING, "Cannot forward message because either the from or to mailbox was not specified\n");
17292 return -1;
17293 }
17294
17295 if (!num_msgs) {
17296 ast_log(LOG_WARNING, "Invalid number of messages specified to forward: %zu\n", num_msgs);
17297 return -1;
17298 }
17299
17300 if (ast_strlen_zero(from_folder) || ast_strlen_zero(to_folder)) {
17301 ast_log(LOG_WARNING, "Cannot forward message because the from_folder or to_folder was not specified\n");
17302 return -1;
17303 }
17304
17305 memset(&vmus, 0, sizeof(vmus));
17306 memset(&to_vmus, 0, sizeof(to_vmus));
17307 memset(&from_vms, 0, sizeof(from_vms));
17308
17309 from_folder_index = get_folder_by_name(from_folder);
17310 if (from_folder_index == -1) {
17311 return -1;
17312 }
17313
17314 if (get_folder_by_name(to_folder) == -1) {
17315 return -1;
17316 }
17317
17318 if (!(vmu = find_user(&vmus, from_context, from_mailbox))) {
17319 ast_log(LOG_WARNING, "Can't find voicemail user to forward from (%s@%s)\n", from_mailbox, from_context);
17320 return -1;
17321 }
17322
17323 if (!(to_vmu = find_user(&to_vmus, to_context, to_mailbox))) {
17324 ast_log(LOG_WARNING, "Can't find voicemail user to forward to (%s@%s)\n", to_mailbox, to_context);
17325 free_user(vmu);
17326 return -1;
17327 }
17328
17329 ast_copy_string(from_vms.username, from_mailbox, sizeof(from_vms.username));
17330 from_vms.lastmsg = -1;
17331 open = 0;
17332
17333 /* open the mailbox state */
17334 if ((res = open_mailbox(&from_vms, vmu, from_folder_index)) < 0) {
17335 ast_log(LOG_WARNING, "Could not open mailbox %s\n", from_mailbox);
17336 res = -1;
17337 goto vm_forward_cleanup;
17338 }
17339
17340 open = 1;
17341
17342 if ((from_vms.lastmsg + 1) < num_msgs) {
17343 ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", from_folder, num_msgs);
17344 res = -1;
17345 goto vm_forward_cleanup;
17346 }
17347
17348 msg_nums = ast_alloca(sizeof(int) * num_msgs);
17349
17350 if ((res = message_range_and_existence_check(&from_vms, msg_ids, num_msgs, msg_nums, vmu) < 0)) {
17351 goto vm_forward_cleanup;
17352 }
17353
17354 /* Now we actually forward the messages */
17355 for (i = 0; i < num_msgs; i++) {
17356 int cur_msg = msg_nums[i];
17357 int duration = 0;
17358 const char *value;
17359
17360 make_file(from_vms.fn, sizeof(from_vms.fn), from_vms.curdir, cur_msg);
17361 snprintf(filename, sizeof(filename), "%s.txt", from_vms.fn);
17362 RETRIEVE(from_vms.curdir, cur_msg, vmu->mailbox, vmu->context);
17363 msg_cfg = ast_config_load(filename, config_flags);
17364 /* XXX This likely will not fail since we previously ensured that the
17365 * message we are looking for exists. However, there still could be some
17366 * circumstance where this fails, so atomicity is not guaranteed.
17367 */
17368 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17369 DISPOSE(from_vms.curdir, cur_msg);
17370 continue;
17371 }
17372 if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17373 duration = atoi(value);
17374 }
17375
17376 copy_message(NULL, vmu, from_folder_index, cur_msg, duration, to_vmu, vmfmts, from_vms.curdir, "", to_folder);
17377
17378 if (delete_old) {
17379 from_vms.deleted[cur_msg] = 1;
17380 }
17381 ast_config_destroy(msg_cfg);
17382 DISPOSE(from_vms.curdir, cur_msg);
17383 }
17384
17385 /* close mailbox */
17386 if ((res = close_mailbox(&from_vms, vmu) == ERROR_LOCK_PATH)) {
17387 res = -1;
17388 goto vm_forward_cleanup;
17389 }
17390 open = 0;
17391
17392vm_forward_cleanup:
17393 if (vmu && open) {
17394 close_mailbox(&from_vms, vmu);
17395 }
17396#ifdef IMAP_STORAGE
17397 if (vmu) {
17398 vmstate_delete(&from_vms);
17399 }
17400#endif
17401
17402 if (!res) {
17403 notify_new_state(to_vmu);
17404 }
17405
17406 free_user(vmu);
17407 free_user(to_vmu);
17408 return res;
17409}
17410
17411static int vm_msg_move(const char *mailbox,
17412 const char *context,
17413 size_t num_msgs,
17414 const char *oldfolder,
17415 const char *old_msg_ids [],
17416 const char *newfolder)
17417{
17418 struct vm_state vms;
17419 struct ast_vm_user *vmu = NULL, vmus;
17420 int old_folder_index;
17421 int new_folder_index;
17422 int open = 0;
17423 int res = 0;
17424 int i;
17425 int *old_msg_nums;
17426
17427 if (ast_strlen_zero(mailbox)) {
17428 ast_log(LOG_WARNING, "Cannot move message because no mailbox was specified\n");
17429 return -1;
17430 }
17431
17432 if (!num_msgs) {
17433 ast_log(LOG_WARNING, "Invalid number of messages specified to move: %zu\n", num_msgs);
17434 return -1;
17435 }
17436
17437 if (ast_strlen_zero(oldfolder) || ast_strlen_zero(newfolder)) {
17438 ast_log(LOG_WARNING, "Cannot move message because either oldfolder or newfolder was not specified\n");
17439 return -1;
17440 }
17441
17442 old_folder_index = get_folder_by_name(oldfolder);
17443 new_folder_index = get_folder_by_name(newfolder);
17444
17445 memset(&vmus, 0, sizeof(vmus));
17446 memset(&vms, 0, sizeof(vms));
17447
17448 if (old_folder_index == -1 || new_folder_index == -1) {
17449 return -1;
17450 }
17451
17452 if (!(vmu = find_user(&vmus, context, mailbox))) {
17453 return -1;
17454 }
17455
17456 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17457 vms.lastmsg = -1;
17458 open = 0;
17459
17460 /* open the mailbox state */
17461 if ((res = open_mailbox(&vms, vmu, old_folder_index)) < 0) {
17462 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17463 res = -1;
17464 goto vm_move_cleanup;
17465 }
17466
17467 open = 1;
17468
17469 if ((vms.lastmsg + 1) < num_msgs) {
17470 ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", oldfolder, num_msgs);
17471 res = -1;
17472 goto vm_move_cleanup;
17473 }
17474
17475 old_msg_nums = ast_alloca(sizeof(int) * num_msgs);
17476
17477 if ((res = message_range_and_existence_check(&vms, old_msg_ids, num_msgs, old_msg_nums, vmu)) < 0) {
17478 goto vm_move_cleanup;
17479 }
17480
17481 /* Now actually move the message */
17482 for (i = 0; i < num_msgs; ++i) {
17483 if (save_to_folder(vmu, &vms, old_msg_nums[i], new_folder_index, NULL, 0)) {
17484 res = -1;
17485 goto vm_move_cleanup;
17486 }
17487 vms.deleted[old_msg_nums[i]] = 1;
17488 }
17489
17490 /* close mailbox */
17491 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17492 res = -1;
17493 goto vm_move_cleanup;
17494 }
17495 open = 0;
17496
17497vm_move_cleanup:
17498 if (vmu && open) {
17499 close_mailbox(&vms, vmu);
17500 }
17501#ifdef IMAP_STORAGE
17502 if (vmu) {
17503 vmstate_delete(&vms);
17504 }
17505#endif
17506
17507 if (!res) {
17508 notify_new_state(vmu);
17509 }
17510
17511 free_user(vmu);
17512 return res;
17513}
17514
17515static int vm_msg_remove(const char *mailbox,
17516 const char *context,
17517 size_t num_msgs,
17518 const char *folder,
17519 const char *msgs[])
17520{
17521 struct vm_state vms;
17522 struct ast_vm_user *vmu = NULL, vmus;
17523 int folder_index;
17524 int open = 0;
17525 int res = 0;
17526 int i;
17527 int *msg_nums;
17528
17529 if (ast_strlen_zero(mailbox)) {
17530 ast_log(LOG_WARNING, "Cannot remove message because no mailbox was specified\n");
17531 return -1;
17532 }
17533
17534 if (!num_msgs) {
17535 ast_log(LOG_WARNING, "Invalid number of messages specified to remove: %zu\n", num_msgs);
17536 return -1;
17537 }
17538
17539 if (ast_strlen_zero(folder)) {
17540 ast_log(LOG_WARNING, "Cannot remove message because no folder was specified\n");
17541 return -1;
17542 }
17543
17544 memset(&vmus, 0, sizeof(vmus));
17545 memset(&vms, 0, sizeof(vms));
17546
17547 folder_index = get_folder_by_name(folder);
17548 if (folder_index == -1) {
17549 ast_log(LOG_WARNING, "Could not remove msgs from unknown folder %s\n", folder);
17550 return -1;
17551 }
17552
17553 if (!(vmu = find_user(&vmus, context, mailbox))) {
17554 ast_log(LOG_WARNING, "Can't find voicemail user to remove msg from (%s@%s)\n", mailbox, context);
17555 return -1;
17556 }
17557
17558 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17559 vms.lastmsg = -1;
17560 open = 0;
17561
17562 /* open the mailbox state */
17563 if ((res = open_mailbox(&vms, vmu, folder_index)) < 0) {
17564 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17565 res = -1;
17566 goto vm_remove_cleanup;
17567 }
17568
17569 open = 1;
17570
17571 if ((vms.lastmsg + 1) < num_msgs) {
17572 ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", folder, num_msgs);
17573 res = -1;
17574 goto vm_remove_cleanup;
17575 }
17576
17577 msg_nums = ast_alloca(sizeof(int) * num_msgs);
17578
17579 if ((res = message_range_and_existence_check(&vms, msgs, num_msgs, msg_nums, vmu)) < 0) {
17580 goto vm_remove_cleanup;
17581 }
17582
17583 for (i = 0; i < num_msgs; i++) {
17584 vms.deleted[msg_nums[i]] = 1;
17585 }
17586
17587 /* close mailbox */
17588 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17589 res = -1;
17590 ast_log(AST_LOG_ERROR, "Failed to close mailbox folder %s while removing msgs\n", folder);
17591 goto vm_remove_cleanup;
17592 }
17593 open = 0;
17594
17595vm_remove_cleanup:
17596 if (vmu && open) {
17597 close_mailbox(&vms, vmu);
17598 }
17599#ifdef IMAP_STORAGE
17600 if (vmu) {
17601 vmstate_delete(&vms);
17602 }
17603#endif
17604
17605 if (!res) {
17606 notify_new_state(vmu);
17607 }
17608
17609 free_user(vmu);
17610 return res;
17611}
17612
17613static int vm_msg_play(struct ast_channel *chan,
17614 const char *mailbox,
17615 const char *context,
17616 const char *folder,
17617 const char *msg_id,
17619{
17620 struct vm_state vms;
17621 struct ast_vm_user *vmu = NULL, vmus;
17622 int res = 0;
17623 int open = 0;
17624 int i;
17625 char filename[PATH_MAX];
17626 struct ast_config *msg_cfg;
17627 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17628 int duration = 0;
17629 const char *value;
17630
17631 if (ast_strlen_zero(mailbox)) {
17632 ast_log(LOG_WARNING, "Cannot play message because no mailbox was specified\n");
17633 return -1;
17634 }
17635
17636 if (ast_strlen_zero(folder)) {
17637 ast_log(LOG_WARNING, "Cannot play message because no folder was specified\n");
17638 return -1;
17639 }
17640
17641 if (ast_strlen_zero(msg_id)) {
17642 ast_log(LOG_WARNING, "Cannot play message because no message number was specified\n");
17643 return -1;
17644 }
17645
17646 memset(&vmus, 0, sizeof(vmus));
17647 memset(&vms, 0, sizeof(vms));
17648
17649 if (ast_strlen_zero(context)) {
17650 context = "default";
17651 }
17652
17653 if (!(vmu = find_user(&vmus, context, mailbox))) {
17654 return -1;
17655 }
17656
17657 i = get_folder_by_name(folder);
17658 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17659 vms.lastmsg = -1;
17660 if ((res = open_mailbox(&vms, vmu, i)) < 0) {
17661 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17662 goto play2_msg_cleanup;
17663 }
17664 open = 1;
17665
17666 if (message_range_and_existence_check(&vms, &msg_id, 1, &vms.curmsg, vmu)) {
17667 res = -1;
17668 goto play2_msg_cleanup;
17669 }
17670
17671 /* Find the msg */
17672 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
17673 snprintf(filename, sizeof(filename), "%s.txt", vms.fn);
17674 RETRIEVE(vms.curdir, vms.curmsg, vmu->mailbox, vmu->context);
17675
17676 msg_cfg = ast_config_load(filename, config_flags);
17677 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17678 DISPOSE(vms.curdir, vms.curmsg);
17679 res = -1;
17680 goto play2_msg_cleanup;
17681 }
17682 if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17683 duration = atoi(value);
17684 }
17685 ast_config_destroy(msg_cfg);
17686
17687#ifdef IMAP_STORAGE
17688 /*IMAP storage stores any prepended message from a forward
17689 * as a separate file from the rest of the message
17690 */
17691 if (!ast_strlen_zero(vms.introfn) && ast_fileexists(vms.introfn, NULL, NULL) > 0) {
17692 wait_file(chan, &vms, vms.introfn);
17693 }
17694#endif
17695 if (cb) {
17696 cb(chan, vms.fn, duration);
17697 } else if ((wait_file(chan, &vms, vms.fn)) < 0) {
17698 ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms.fn);
17699 } else {
17700 res = 0;
17701 }
17702
17703 vms.heard[vms.curmsg] = 1;
17704
17705 /* cleanup configs and msg */
17706 DISPOSE(vms.curdir, vms.curmsg);
17707
17708play2_msg_cleanup:
17709 if (vmu && open) {
17710 close_mailbox(&vms, vmu);
17711 }
17712
17713#ifdef IMAP_STORAGE
17714 if (vmu) {
17715 vmstate_delete(&vms);
17716 }
17717#endif
17718
17719 if (!res) {
17720 notify_new_state(vmu);
17721 }
17722
17723 free_user(vmu);
17724 return res;
17725}
17726
17727/* This is a workaround so that menuselect displays a proper description
17728 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
17729 */
17730
17732 .support_level = AST_MODULE_SUPPORT_CORE,
17733 .load = load_module,
17734 .unload = unload_module,
17735 .reload = reload,
17736 .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:1764
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 actual_load_config(int reload, struct ast_config *cfg)
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 occurrences 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 password 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 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_SPOOLDIR
@ OPT_PWLOC_VOICEMAILCONF
static char vm_prepend_timeout[80]
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:2141
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:3145
const char * ast_channel_name(const struct ast_channel *chan)
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2511
#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:2972
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:3008
#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:7404
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:6526
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2776
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:4243
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:2973
#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:1946
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_DOWN
Definition: channelstate.h:36
@ AST_STATE_UP
Definition: channelstate.h:42
size_t current
static struct ast_channel * callback(struct ast_channelstorage_instance *driver, ao2_callback_data_fn *cb_fn, void *arg, void *data, int ao2_flags)
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:2011
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:1104
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:223
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
Seeks into stream.
Definition: file.c:1094
int ast_filerename(const char *oldname, const char *newname, const char *fmt)
Renames a file.
Definition: file.c:1165
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:1405
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1312
FILE * ast_file_mkftemp(char *template_name, mode_t mode)
same as mkstemp, but return a FILE
Definition: file.c:188
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:1912
char * ast_format_str_reduce(char *fmts)
Definition: file.c:1928
int ast_filecopy(const char *oldname, const char *newname, const char *fmt)
Copies a file.
Definition: file.c:1170
int ast_ratestream(struct ast_filestream *fs)
Return the sample rate of the stream's format.
Definition: file.c:1109
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1130
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1148
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1160
#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:1874
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:2024
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:1982
void ast_manager_publish_event(const char *type, int class_type, struct ast_json *obj)
Publish an event to AMI.
Definition: manager.c:634
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:2060
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:2014
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:1643
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:2068
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:1903
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:7695
#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:3927
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:3781
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3854
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
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:3808
#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:3762
int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Destroy realtime configuration.
Definition: main/config.c:4008
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:3891
int ast_store_realtime(const char *family,...) attribute_sentinel
Create realtime configuration.
Definition: main/config.c:3972
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3738
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
@ CONFIG_FLAG_NOCACHE
@ CONFIG_FLAG_WITHCOMMENTS
@ CONFIG_FLAG_FILEUNCHANGED
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:73
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:213
#define ast_mutex_init(pmutex)
Definition: lock.h:193
#define ast_mutex_unlock(a)
Definition: lock.h:197
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:764
pthread_cond_t ast_cond_t
Definition: lock.h:185
#define ast_mutex_destroy(a)
Definition: lock.h:195
#define ast_mutex_lock(a)
Definition: lock.h:196
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:527
#define ast_cond_signal(cond)
Definition: lock.h:210
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:159
const char * ast_config_AST_SPOOL_DIR
Definition: options.c:155
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:4196
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:1562
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:8806
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:4211
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:459
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:828
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:520
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:365
SQLRETURN ast_odbc_execute_sql(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Execute a unprepared SQL query.
Definition: res_odbc.c:474
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:403
#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:8285
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:8249
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:8273
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:2235
#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:2482
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:2203
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:2252
#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:217
unsigned int flags
Definition: utils.h:218
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:142
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::@188 * snapshots
const ast_string_field origtime
struct ast_vm_msg_snapshot::@187 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:323
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 phoneprov_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 * heard
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:978
int ast_file_is_readable(const char *filename)
Test that a file exists and is readable by the effective user.
Definition: utils.c:3143
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:621
#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:2348
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2515
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ARRAY_LEN(a)
Definition: utils.h:703
#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:214