Asterisk - The Open Source Telephony Project GIT-master-7988d11
Loading...
Searching...
No Matches
app_voicemail.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
756
758 OPT_SILENT = (1 << 0),
761 OPT_RECORDGAIN = (1 << 3),
763 OPT_AUTOPLAY = (1 << 6),
764 OPT_DTMFEXIT = (1 << 7),
768 OPT_BEEP = (1 << 11),
770 OPT_READONLY = (1 << 13),
771};
772
778 /* This *must* be the last value in this enum! */
780};
781
786
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. */
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 */
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];
999 int *heard;
1000 int dh_arraysize; /* used for deleted / heard allocation */
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
1142 char *alias;
1143 char *mailbox;
1144 char buf[0];
1145};
1146
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
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 const 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) {
2334 ast_filedelete(file, NULL);
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
2729 if (ast_strlen_zero(mailbox))
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
3694 if (ast_strlen_zero(mailbox))
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";
6451 if (ast_strlen_zero(context))
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
6615 if (!ast_strlen_zero(context))
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;
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)),
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 if (ast_check_realtime("voicemail_data")) {
7487 snprintf(tmpdur, sizeof(tmpdur), "%d", duration);
7488 ast_update_realtime("voicemail_data", "filename", tmptxtfile, "filename", fn, "duration", tmpdur, SENTINEL);
7489 }
7490 /* We must store the file first, before copying the message, because
7491 * ODBC storage does the entire copy with SQL.
7492 * Hold the path lock until after STORE so that concurrent callers
7493 * cannot read the same LAST_MSG_INDEX before the INSERT is committed.
7494 */
7495 if (ast_fileexists(fn, NULL, NULL) > 0) {
7496#ifdef ODBC_STORAGE
7497 int store_failed;
7498 store_failed = odbc_store_message(dir, vmu->mailbox, vmu->context, msgnum);
7499 ast_unlock_path(dir);
7500 if (store_failed) {
7501 ast_log(LOG_ERROR, "Failed to store voicemail for %s/%s msgnum %d, skipping notification\n", vmu->context, vmu->mailbox, msgnum);
7502 if (ast_check_realtime("voicemail_data")) {
7503 ast_destroy_realtime("voicemail_data", "filename", fn, SENTINEL);
7504 }
7505 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7506 goto leave_vm_out;
7507 }
7508#else
7509 ast_unlock_path(dir);
7510 SCOPE_CALL(-1, STORE, dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag, msg_id);
7511#endif
7512 } else {
7513 ast_unlock_path(dir);
7514 }
7515
7516 /* Are there to be more recipients of this message? */
7517 while (tmpptr) {
7518 struct ast_vm_user recipu, *recip;
7519 char *exten, *cntx;
7520
7521 exten = strsep(&tmpptr, "&");
7522 cntx = strchr(exten, '@');
7523 if (cntx) {
7524 *cntx = '\0';
7525 cntx++;
7526 }
7527 memset(&recipu, 0, sizeof(recipu));
7528 if ((recip = find_user(&recipu, cntx, exten))) {
7529 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag, NULL);
7530 free_user(recip);
7531 }
7532 }
7533
7534 /* Notification needs to happen after the copy, though. */
7535 if (ast_fileexists(fn, NULL, NULL)) {
7536#ifdef IMAP_STORAGE
7537 notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
7538 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7539 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7540 flag);
7541#else
7542 notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
7543 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7544 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7545 flag);
7546#endif
7547 }
7548
7549 /* Disposal needs to happen after the optional move and copy */
7550 if (ast_fileexists(fn, NULL, NULL)) {
7551 SCOPE_CALL(-1, DISPOSE, dir, msgnum);
7552 }
7553 }
7554 }
7555 } else {
7556 inprocess_count(vmu->mailbox, vmu->context, -1);
7557 }
7558 if (res == '0') {
7559 goto transfer;
7560 } else if (res > 0 && res != 't')
7561 res = 0;
7562
7563 if (sound_duration < vmu->minsecs)
7564 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
7565 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7566 else
7567 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
7568 } else
7569 ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
7570leave_vm_out:
7571 free_user(vmu);
7572
7573#ifdef IMAP_STORAGE
7574 /* expunge message - use UID Expunge if supported on IMAP server*/
7575 ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
7576 if (expungeonhangup == 1 && vms->mailstream != NULL) {
7577 ast_mutex_lock(&vms->lock);
7578#ifdef HAVE_IMAP_TK2006
7579 if (LEVELUIDPLUS (vms->mailstream)) {
7580 mail_expunge_full(vms->mailstream, NIL, EX_UID);
7581 } else
7582#endif
7583 mail_expunge(vms->mailstream);
7584 ast_mutex_unlock(&vms->lock);
7585 }
7586#endif
7587
7588 ast_free(tmp);
7589 SCOPE_EXIT_RTN_VALUE(res, "Done: '%d'\n", res);
7590}
7591
7592#if !defined(IMAP_STORAGE)
7593static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
7594{
7595 /* we know the actual number of messages, so stop process when number is hit */
7596
7597 int x, dest;
7598 char sfn[PATH_MAX];
7599 char dfn[PATH_MAX];
7600
7601 if (vm_lock_path(dir)) {
7602 return ERROR_LOCK_PATH;
7603 }
7604
7605 for (x = 0, dest = 0; dest != stopcount && x < MAXMSGLIMIT; x++) {
7606 make_file(sfn, sizeof(sfn), dir, x);
7607 if (EXISTS(dir, x, sfn, NULL)) {
7608
7609 if (x != dest) {
7610 make_file(dfn, sizeof(dfn), dir, dest);
7611 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
7612 }
7613
7614 dest++;
7615 }
7616 }
7617 ast_unlock_path(dir);
7618
7619 return dest;
7620}
7621#endif
7622
7623static int say_and_wait(struct ast_channel *chan, int num, const char *language)
7624{
7625 int d;
7626 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
7627 return d;
7628}
7629
7630static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move)
7631{
7632#ifdef IMAP_STORAGE
7633 /* we must use mbox(x) folder names, and copy the message there */
7634 /* simple. huh? */
7635 char sequence[10];
7636 char mailbox[256];
7637 int res;
7638 int curr_mbox;
7639
7640 /* get the real IMAP message number for this message */
7641 snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
7642
7643 ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
7644 ast_mutex_lock(&vms->lock);
7645 /* if save to Old folder, put in INBOX as read */
7646 if (box == OLD_FOLDER) {
7647 mail_setflag(vms->mailstream, sequence, "\\Seen");
7648 } else if (box == NEW_FOLDER) {
7649 mail_clearflag(vms->mailstream, sequence, "\\Seen");
7650 }
7651 if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
7652 ast_mutex_unlock(&vms->lock);
7653 return 0;
7654 }
7655
7656 /* get the current mailbox so that we can point the mailstream back to it later */
7657 curr_mbox = get_folder_by_name(vms->curbox);
7658
7659 /* Create the folder if it doesn't exist */
7660 imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
7661 if (vms->mailstream && !mail_status(vms->mailstream, mailbox, SA_UIDNEXT)) {
7662 if (mail_create(vms->mailstream, mailbox) != NIL) {
7663 ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
7664 }
7665 }
7666
7667 /* restore previous mbox stream */
7668 if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
7669 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
7670 res = -1;
7671 } else {
7672 if (move) {
7673 res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
7674 } else {
7675 res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
7676 }
7677 }
7678 ast_mutex_unlock(&vms->lock);
7679 return res;
7680#else
7681 char *dir = vms->curdir;
7682 char *username = vms->username;
7683 char *context = vmu->context;
7684 char sfn[PATH_MAX];
7685 char dfn[PATH_MAX];
7686 char ddir[PATH_MAX];
7687 const char *dbox = mbox(vmu, box);
7688 int x, i;
7689 SCOPE_ENTER(3, "dir: %s msg: %d box: %d dbox: %s move? %d \n", dir, msg, box, dbox, move);
7690
7691 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
7692 ast_trace(-1, "ddir: %s\n", ddir);
7693
7694 if (vm_lock_path(ddir)) {
7695 SCOPE_EXIT_RTN_VALUE(ERROR_LOCK_PATH, "Failed to lock path %s\n", ddir);
7696 }
7697
7698 x = LAST_MSG_INDEX(ddir) + 1;
7699
7700 if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
7701 ast_trace(-1, "Deleting message %d\n", msg);
7702 x--;
7703 for (i = 1; i <= x; i++) {
7704 /* Push files down a "slot". The oldest file (msg0000) will be deleted. */
7705 make_file(sfn, sizeof(sfn), ddir, i);
7706 make_file(dfn, sizeof(dfn), ddir, i - 1);
7707 if (EXISTS(ddir, i, sfn, NULL)) {
7708 SCOPE_CALL(-1, RENAME, ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
7709 } else
7710 break;
7711 }
7712 } else {
7713 if (x >= vmu->maxmsg) {
7714 ast_unlock_path(ddir);
7715 SCOPE_EXIT_RTN_VALUE(ERROR_MAX_MSGS, "Max messages reached\n");
7716 }
7717 }
7718 make_file(sfn, sizeof(sfn), dir, msg);
7719 make_file(dfn, sizeof(dfn), ddir, x);
7720 if (strcmp(sfn, dfn)) {
7721 ast_trace(-1, "Copying message '%s' to '%s'\n", sfn, dfn);
7722 SCOPE_CALL(-1, COPY, dir, msg, ddir, x, username, context, sfn, dfn);
7723 }
7724 ast_unlock_path(ddir);
7725
7726 if (newmsg) {
7727 *newmsg = x;
7728 }
7729 SCOPE_EXIT_RTN_VALUE(0, "Done\n");
7730#endif
7731}
7732
7733static int adsi_logo(unsigned char *buf)
7734{
7735 int bytes = 0;
7736 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
7737 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
7738 return bytes;
7739}
7740
7741static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
7742{
7743 unsigned char buf[256];
7744 int bytes = 0;
7745 int x;
7746 char num[5];
7747
7748 *useadsi = 0;
7749 bytes += ast_adsi_data_mode(buf + bytes);
7751
7752 bytes = 0;
7753 bytes += adsi_logo(buf);
7754 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
7755#ifdef DISPLAY
7756 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
7757#endif
7758 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7759 bytes += ast_adsi_data_mode(buf + bytes);
7761
7763 bytes = 0;
7764 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
7765 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
7766 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7767 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7769 return 0;
7770 }
7771
7772#ifdef DISPLAY
7773 /* Add a dot */
7774 bytes = 0;
7775 bytes += ast_adsi_logo(buf);
7776 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
7777 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
7778 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7780#endif
7781 bytes = 0;
7782 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
7783 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
7784 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advanced", "3", 1);
7785 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
7786 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
7787 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
7789
7790#ifdef DISPLAY
7791 /* Add another dot */
7792 bytes = 0;
7793 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
7794 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7795
7796 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7798#endif
7799
7800 bytes = 0;
7801 /* These buttons we load but don't use yet */
7802 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
7803 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
7804 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
7805 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
7806 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
7807 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
7809
7810#ifdef DISPLAY
7811 /* Add another dot */
7812 bytes = 0;
7813 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
7814 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7816#endif
7817
7818 bytes = 0;
7819 for (x = 0; x < 5; x++) {
7820 snprintf(num, sizeof(num), "%d", x);
7821 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
7822 }
7823 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
7825
7826#ifdef DISPLAY
7827 /* Add another dot */
7828 bytes = 0;
7829 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
7830 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7832#endif
7833
7834 if (ast_adsi_end_download(chan)) {
7835 bytes = 0;
7836 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
7837 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
7838 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7839 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7841 return 0;
7842 }
7843 bytes = 0;
7844 bytes += ast_adsi_download_disconnect(buf + bytes);
7845 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7847
7848 ast_debug(1, "Done downloading scripts...\n");
7849
7850#ifdef DISPLAY
7851 /* Add last dot */
7852 bytes = 0;
7853 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
7854 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7855#endif
7856 ast_debug(1, "Restarting session...\n");
7857
7858 bytes = 0;
7859 /* Load the session now */
7860 if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
7861 *useadsi = 1;
7862 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
7863 } else
7864 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
7865
7867 return 0;
7868}
7869
7870static void adsi_begin(struct ast_channel *chan, int *useadsi)
7871{
7872 int x;
7873 if (!ast_adsi_available(chan))
7874 return;
7875 x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
7876 if (x < 0) {
7877 *useadsi = 0;
7879 return;
7880 }
7881 if (!x) {
7882 if (adsi_load_vmail(chan, useadsi)) {
7883 ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
7884 return;
7885 }
7886 } else
7887 *useadsi = 1;
7888}
7889
7890static void adsi_login(struct ast_channel *chan)
7891{
7892 unsigned char buf[256];
7893 int bytes = 0;
7894 unsigned char keys[8];
7895 int x;
7896 if (!ast_adsi_available(chan))
7897 return;
7898
7899 for (x = 0; x < 8; x++)
7900 keys[x] = 0;
7901 /* Set one key for next */
7902 keys[3] = ADSI_KEY_APPS + 3;
7903
7904 bytes += adsi_logo(buf + bytes);
7905 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
7906 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
7907 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7908 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
7909 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
7910 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
7911 bytes += ast_adsi_set_keys(buf + bytes, keys);
7912 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7914}
7915
7916static void adsi_password(struct ast_channel *chan)
7917{
7918 unsigned char buf[256];
7919 int bytes = 0;
7920 unsigned char keys[8];
7921 int x;
7922 if (!ast_adsi_available(chan))
7923 return;
7924
7925 for (x = 0; x < 8; x++)
7926 keys[x] = 0;
7927 /* Set one key for next */
7928 keys[3] = ADSI_KEY_APPS + 3;
7929
7930 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7931 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
7932 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
7933 bytes += ast_adsi_set_keys(buf + bytes, keys);
7934 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7936}
7937
7938static void adsi_folders(struct ast_channel *chan, int start, char *label)
7939{
7940 unsigned char buf[256];
7941 int bytes = 0;
7942 unsigned char keys[8];
7943 int x, y;
7944
7945 if (!ast_adsi_available(chan))
7946 return;
7947
7948 for (x = 0; x < 5; x++) {
7949 y = ADSI_KEY_APPS + 12 + start + x;
7950 if (y > ADSI_KEY_APPS + 12 + 4)
7951 y = 0;
7952 keys[x] = ADSI_KEY_SKT | y;
7953 }
7954 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
7955 keys[6] = 0;
7956 keys[7] = 0;
7957
7958 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
7959 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
7960 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7961 bytes += ast_adsi_set_keys(buf + bytes, keys);
7962 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7963
7965}
7966
7967static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
7968{
7969 int bytes = 0;
7970 unsigned char buf[256];
7971 char buf1[256], buf2[256];
7972 char fn2[PATH_MAX];
7973
7974 char cid[256] = "";
7975 char *val;
7976 char *name, *num;
7977 char datetime[21] = "";
7978 FILE *f;
7979
7980 unsigned char keys[8];
7981
7982 int x;
7983
7984 if (!ast_adsi_available(chan))
7985 return;
7986
7987 /* Retrieve important info */
7988 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
7989 f = fopen(fn2, "r");
7990 if (f) {
7991 while (!feof(f)) {
7992 if (!fgets((char *) buf, sizeof(buf), f)) {
7993 continue;
7994 }
7995 if (!feof(f)) {
7996 char *stringp = NULL;
7997 stringp = (char *) buf;
7998 strsep(&stringp, "=");
7999 val = strsep(&stringp, "=");
8000 if (!ast_strlen_zero(val)) {
8001 if (!strcmp((char *) buf, "callerid"))
8002 ast_copy_string(cid, val, sizeof(cid));
8003 if (!strcmp((char *) buf, "origdate"))
8004 ast_copy_string(datetime, val, sizeof(datetime));
8005 }
8006 }
8007 }
8008 fclose(f);
8009 }
8010 /* New meaning for keys */
8011 for (x = 0; x < 5; x++)
8012 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
8013 keys[6] = 0x0;
8014 keys[7] = 0x0;
8015
8016 if (!vms->curmsg) {
8017 /* No prev key, provide "Folder" instead */
8018 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8019 }
8020 if (vms->curmsg >= vms->lastmsg) {
8021 /* If last message ... */
8022 if (vms->curmsg) {
8023 /* but not only message, provide "Folder" instead */
8024 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8025 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8026
8027 } else {
8028 /* Otherwise if only message, leave blank */
8029 keys[3] = 1;
8030 }
8031 }
8032
8033 if (!ast_strlen_zero(cid)) {
8034 ast_callerid_parse(cid, &name, &num);
8035 if (!name)
8036 name = num;
8037 } else {
8038 name = "Unknown Caller";
8039 }
8040
8041 /* If deleted, show "undeleted" */
8042#ifdef IMAP_STORAGE
8043 ast_mutex_lock(&vms->lock);
8044#endif
8045 if (vms->deleted[vms->curmsg]) {
8046 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
8047 }
8048#ifdef IMAP_STORAGE
8049 ast_mutex_unlock(&vms->lock);
8050#endif
8051
8052 /* Except "Exit" */
8053 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
8054 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
8055 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
8056 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
8057
8058 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8059 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8060 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
8061 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
8062 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8063 bytes += ast_adsi_set_keys(buf + bytes, keys);
8064 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8065
8067}
8068
8069static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
8070{
8071 int bytes = 0;
8072 unsigned char buf[256];
8073 unsigned char keys[8];
8074
8075 int x;
8076
8077 if (!ast_adsi_available(chan))
8078 return;
8079
8080 /* New meaning for keys */
8081 for (x = 0; x < 5; x++)
8082 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
8083
8084 keys[6] = 0x0;
8085 keys[7] = 0x0;
8086
8087 if (!vms->curmsg) {
8088 /* No prev key, provide "Folder" instead */
8089 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8090 }
8091 if (vms->curmsg >= vms->lastmsg) {
8092 /* If last message ... */
8093 if (vms->curmsg) {
8094 /* but not only message, provide "Folder" instead */
8095 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8096 } else {
8097 /* Otherwise if only message, leave blank */
8098 keys[3] = 1;
8099 }
8100 }
8101
8102 /* If deleted, show "undeleted" */
8103#ifdef IMAP_STORAGE
8104 ast_mutex_lock(&vms->lock);
8105#endif
8106 if (vms->deleted[vms->curmsg]) {
8107 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
8108 }
8109#ifdef IMAP_STORAGE
8110 ast_mutex_unlock(&vms->lock);
8111#endif
8112
8113 /* Except "Exit" */
8114 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
8115 bytes += ast_adsi_set_keys(buf + bytes, keys);
8116 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8117
8119}
8120
8121static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
8122{
8123 unsigned char buf[256] = "";
8124 char buf1[256] = "", buf2[256] = "";
8125 int bytes = 0;
8126 unsigned char keys[8];
8127 int x;
8128
8129 char *newm = (vms->newmessages == 1) ? "message" : "messages";
8130 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
8131 if (!ast_adsi_available(chan))
8132 return;
8133 if (vms->newmessages) {
8134 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
8135 if (vms->oldmessages) {
8136 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
8137 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
8138 } else {
8139 snprintf(buf2, sizeof(buf2), "%s.", newm);
8140 }
8141 } else if (vms->oldmessages) {
8142 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
8143 snprintf(buf2, sizeof(buf2), "%s.", oldm);
8144 } else {
8145 strcpy(buf1, "You have no messages.");
8146 buf2[0] = ' ';
8147 buf2[1] = '\0';
8148 }
8149 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8150 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8151 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8152
8153 for (x = 0; x < 6; x++)
8154 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
8155 keys[6] = 0;
8156 keys[7] = 0;
8157
8158 /* Don't let them listen if there are none */
8159 if (vms->lastmsg < 0)
8160 keys[0] = 1;
8161 bytes += ast_adsi_set_keys(buf + bytes, keys);
8162
8163 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8164
8166}
8167
8168static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
8169{
8170 unsigned char buf[256] = "";
8171 char buf1[256] = "", buf2[256] = "";
8172 int bytes = 0;
8173 unsigned char keys[8];
8174 int x;
8175
8176 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
8177
8178 if (!ast_adsi_available(chan))
8179 return;
8180
8181 /* Original command keys */
8182 for (x = 0; x < 6; x++)
8183 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
8184
8185 keys[6] = 0;
8186 keys[7] = 0;
8187
8188 if ((vms->lastmsg + 1) < 1)
8189 keys[0] = 0;
8190
8191 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
8192 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
8193
8194 if (vms->lastmsg + 1)
8195 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
8196 else
8197 strcpy(buf2, "no messages.");
8198 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8199 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8200 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
8201 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8202 bytes += ast_adsi_set_keys(buf + bytes, keys);
8203
8204 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8205
8207
8208}
8209
8210/*
8211static void adsi_clear(struct ast_channel *chan)
8212{
8213 char buf[256];
8214 int bytes=0;
8215 if (!ast_adsi_available(chan))
8216 return;
8217 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8218 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8219
8220 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
8221}
8222*/
8223
8224static void adsi_goodbye(struct ast_channel *chan)
8225{
8226 unsigned char buf[256];
8227 int bytes = 0;
8228
8229 if (!ast_adsi_available(chan))
8230 return;
8231 bytes += adsi_logo(buf + bytes);
8232 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
8233 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
8234 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8235 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8236
8238}
8239
8240/*!\brief get_folder: Folder menu
8241 * Plays "press 1 for INBOX messages" etc.
8242 * Should possibly be internationalized
8243 */
8244static int get_folder(struct ast_channel *chan, int start)
8245{
8246 int x;
8247 int d;
8248 char fn[PATH_MAX];
8249 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
8250 if (d)
8251 return d;
8252 for (x = start; x < 5; x++) { /* For all folders */
8253 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), NULL)))
8254 return d;
8255 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
8256 if (d)
8257 return d;
8258 snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
8259
8260 /* The inbox folder can have its name changed under certain conditions
8261 * so this checks if the sound file exists for the inbox folder name and
8262 * if it doesn't, plays the default name instead. */
8263 if (x == 0) {
8264 if (ast_fileexists(fn, NULL, NULL)) {
8265 d = vm_play_folder_name(chan, fn);
8266 } else {
8267 ast_verb(4, "Failed to find file %s; falling back to INBOX\n", fn);
8268 d = vm_play_folder_name(chan, "vm-INBOX");
8269 }
8270 } else {
8271 ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
8272 d = vm_play_folder_name(chan, fn);
8273 }
8274
8275 if (d)
8276 return d;
8277 d = ast_waitfordigit(chan, 500);
8278 if (d)
8279 return d;
8280 }
8281
8282 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
8283 if (d)
8284 return d;
8285 d = ast_waitfordigit(chan, 4000);
8286 return d;
8287}
8288
8289/* Japanese Syntax */
8290static int get_folder_ja(struct ast_channel *chan, int start)
8291{
8292 int x;
8293 int d;
8294 char fn[256];
8295 for (x = start; x < 5; x++) { /* For all folders */
8296 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL))) {
8297 return d;
8298 }
8299 snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
8300 d = vm_play_folder_name(chan, fn);
8301 if (d) {
8302 return d;
8303 }
8304 d = ast_waitfordigit(chan, 500);
8305 if (d) {
8306 return d;
8307 }
8308 }
8309 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
8310 if (d) {
8311 return d;
8312 }
8313 d = ast_waitfordigit(chan, 4000);
8314 return d;
8315}
8316
8317/*!
8318 * \brief plays a prompt and waits for a keypress.
8319 * \param chan
8320 * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
8321 * \param start Does not appear to be used at this time.
8322 *
8323 * This is used by the main menu option to move a message to a folder or to save a message into a folder.
8324 * After playing the message identified by the fn parameter value, it calls get_folder(), which plays the
8325 * prompting for the number inputs that correspond to the available folders.
8326 *
8327 * \return zero on success, or -1 on error.
8328 */
8329static int get_folder2(struct ast_channel *chan, char *fn, int start)
8330{
8331 int res = 0;
8332 int loops = 0;
8333
8334 res = ast_play_and_wait(chan, fn); /* Folder name */
8335 while (((res < '0') || (res > '9')) &&
8336 (res != '#') && (res >= 0) &&
8337 loops < 4) {
8338 /* res = get_folder(chan, 0); */
8339 if (!strcasecmp(ast_channel_language(chan), "ja")) { /* Japanese syntax */
8340 res = get_folder_ja(chan, 0);
8341 } else { /* Default syntax */
8342 res = get_folder(chan, 0);
8343 }
8344 loops++;
8345 }
8346 if (loops == 4) { /* give up */
8347 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
8348 return '#';
8349 }
8350 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8351 isprint(res) ? res : '?', isprint(res) ? res : '?');
8352 return res;
8353}
8354
8355/*!
8356 * \brief presents the option to prepend to an existing message when forwarding it.
8357 * \param chan
8358 * \param vmu
8359 * \param curdir
8360 * \param curmsg
8361 * \param vm_fmts
8362 * \param context
8363 * \param record_gain
8364 * \param duration
8365 * \param vms
8366 * \param flag
8367 *
8368 * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
8369 *
8370 * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
8371 * \return zero on success, -1 on error.
8372 */
8373static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir,
8374 int curmsg, char *vm_fmts, char *context, signed char record_gain, long *duration,
8375 struct vm_state *vms, char *flag)
8376{
8377 int cmd = 0;
8378 int retries = 0, prepend_duration = 0, already_recorded = 0;
8379 char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
8380 char textfile[PATH_MAX];
8381 struct ast_config *msg_cfg;
8382 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8383#ifndef IMAP_STORAGE
8384 signed char zero_gain = 0;
8385#else
8386 const char *msg_id = NULL;
8387#endif
8388 const char *duration_str;
8389 SCOPE_ENTER(3, "mbox: %s msgnum: %d curdir: %s", vmu->mailbox, curmsg, curdir);
8390
8391 /* Must always populate duration correctly */
8392 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
8393 ast_trace(-1, "msgfile: %s\n", msgfile);
8394 strcpy(textfile, msgfile);
8395 strcpy(backup, msgfile);
8396 strcpy(backup_textfile, msgfile);
8397 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
8398 strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
8399 strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
8400
8401 if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
8402 *duration = atoi(duration_str);
8403 } else {
8404 *duration = 0;
8405 }
8406
8407 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
8408 if (cmd)
8409 retries = 0;
8410 switch (cmd) {
8411 case '1':
8412
8413#ifdef IMAP_STORAGE
8414 /* Record new intro file */
8415 if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
8416 msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
8417 }
8418 make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
8419 strncat(vms->introfn, "intro", sizeof(vms->introfn));
8420 ast_play_and_wait(chan, "vm-record-prepend");
8421 ast_play_and_wait(chan, "beep");
8422 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);
8423 if (cmd == -1) {
8424 break;
8425 }
8426 cmd = 't';
8427#else
8428
8429 /* prepend a message to the current message, update the metadata and return */
8430 ast_trace(-1, "Prepending to message %d\n", curmsg);
8431
8432 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
8433 ast_trace(-1, "msgfile: %s\n", msgfile);
8434
8435 strcpy(textfile, msgfile);
8436 strncat(textfile, ".txt", sizeof(textfile) - 1);
8437 *duration = 0;
8438
8439 /* if we can't read the message metadata, stop now */
8440 if (!valid_config(msg_cfg)) {
8441 cmd = 0;
8442 break;
8443 }
8444
8445 /* Back up the original file, so we can retry the prepend and restore it after forward. */
8446#ifndef IMAP_STORAGE
8447 if (already_recorded) {
8448 ast_trace(-1, "Restoring '%s' to '%s'\n", backup, msgfile);
8449 ast_filecopy(backup, msgfile, NULL);
8450 copy(backup_textfile, textfile);
8451 }
8452 else {
8453 ast_trace(-1, "Backing up '%s' to '%s'\n", backup, msgfile);
8454 ast_filecopy(msgfile, backup, NULL);
8455 copy(textfile, backup_textfile);
8456 }
8457#endif
8458 already_recorded = 1;
8459
8460 if (record_gain)
8461 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
8462
8463 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, ast_play_and_prepend, chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
8464
8465 if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
8466 ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
8468 ast_filerename(backup, msgfile, NULL);
8469 }
8470
8471 if (record_gain)
8472 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
8473
8474
8475 if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
8476 *duration = atoi(duration_str);
8477
8478 if (prepend_duration) {
8479 struct ast_category *msg_cat;
8480 /* need enough space for a maximum-length message duration */
8481 char duration_buf[12];
8482
8483 *duration += prepend_duration;
8484 ast_trace(-1, "Prepending duration: %d total duration: %ld\n", prepend_duration, *duration);
8485 msg_cat = ast_category_get(msg_cfg, "message", NULL);
8486 snprintf(duration_buf, sizeof(duration_buf), "%ld", *duration);
8487 if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
8488 ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
8489 }
8490 }
8491
8492#endif
8493 break;
8494 case '2':
8495 /* NULL out introfile so we know there is no intro! */
8496#ifdef IMAP_STORAGE
8497 *vms->introfn = '\0';
8498#endif
8499 cmd = 't';
8500 break;
8501 case '*':
8502 cmd = '*';
8503 break;
8504 default:
8505 /* If time_out and return to menu, reset already_recorded */
8506 already_recorded = 0;
8507
8508 cmd = ast_play_and_wait(chan, "vm-forwardoptions");
8509 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
8510 if (!cmd) {
8511 cmd = ast_play_and_wait(chan, "vm-starmain");
8512 /* "press star to return to the main menu" */
8513 }
8514 if (!cmd) {
8515 cmd = ast_waitfordigit(chan, 6000);
8516 }
8517 if (!cmd) {
8518 retries++;
8519 }
8520 if (retries > 3) {
8521 cmd = '*'; /* Let's cancel this beast */
8522 }
8523 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8524 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
8525 }
8526 }
8527
8528 if (valid_config(msg_cfg))
8529 ast_config_destroy(msg_cfg);
8530 if (prepend_duration)
8531 *duration = prepend_duration;
8532
8533 if (already_recorded && cmd == -1) {
8534 /* restore original message if prepention cancelled */
8535 ast_trace(-1, "Restoring '%s' to '%s'\n", backup, msgfile);
8536 ast_filerename(backup, msgfile, NULL);
8537 rename(backup_textfile, textfile);
8538 }
8539
8540 if (cmd == 't' || cmd == 'S') { /* XXX entering this block with a value of 'S' is probably no longer possible. */
8541 cmd = 0;
8542 }
8543 SCOPE_EXIT_RTN_VALUE(cmd, "Done. CMD: %d %c\n", cmd, cmd >= 32 && cmd < 127 ? cmd : '?');
8544}
8545
8546static void queue_mwi_event(const char *channel_id, const char *box, int urgent, int new, int old)
8547{
8548 char *mailbox;
8549 char *context;
8550
8551 if (separate_mailbox(ast_strdupa(box), &mailbox, &context)) {
8552 return;
8553 }
8554
8555 ast_debug(3, "Queueing event for mailbox %s New: %d Old: %d\n", box, new + urgent, old);
8556 ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
8557
8559 struct ao2_iterator *aliases;
8560 struct mailbox_alias_mapping *mapping;
8561
8563 while ((mapping = ao2_iterator_next(aliases))) {
8564 char alias[strlen(mapping->alias) + 1];
8565 strcpy(alias, mapping->alias); /* safe */
8566 mailbox = NULL;
8567 context = NULL;
8568 ast_debug(3, "Found alias mapping: %s -> %s\n", mapping->alias, box);
8569 separate_mailbox(alias, &mailbox, &context);
8570 ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
8571 ao2_ref(mapping, -1);
8572 }
8574 }
8575}
8576
8577/*!
8578 * \brief Sends email notification that a user has a new voicemail waiting for them.
8579 * \param chan
8580 * \param vmu
8581 * \param vms
8582 * \param msgnum
8583 * \param duration
8584 * \param fmt
8585 * \param cidnum The Caller ID phone number value.
8586 * \param cidname The Caller ID name value.
8587 * \param flag
8588 *
8589 * \return zero on success, -1 on error.
8590 */
8591static 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)
8592{
8593 char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
8594 int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
8595 const char *category;
8596 char *myserveremail = serveremail;
8597
8598 ast_channel_lock(chan);
8599 if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
8600 category = ast_strdupa(category);
8601 }
8602 ast_channel_unlock(chan);
8603
8604#ifndef IMAP_STORAGE
8605 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
8606#else
8607 snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
8608#endif
8609 make_file(fn, sizeof(fn), todir, msgnum);
8610 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
8611
8612 if (!ast_strlen_zero(vmu->attachfmt)) {
8613 if (strstr(fmt, vmu->attachfmt))
8614 fmt = vmu->attachfmt;
8615 else
8616 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);
8617 }
8618
8619 /* Attach only the first format */
8620 fmt = ast_strdupa(fmt);
8621 stringp = fmt;
8622 strsep(&stringp, "|");
8623
8624 if (!ast_strlen_zero(vmu->serveremail))
8625 myserveremail = vmu->serveremail;
8626
8627 if (!ast_strlen_zero(vmu->email)) {
8628 int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
8629 char *msg_id = NULL;
8630#ifdef IMAP_STORAGE
8631 struct ast_config *msg_cfg;
8632 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8633 char filename[PATH_MAX];
8634
8635 snprintf(filename, sizeof(filename), "%s.txt", fn);
8636 msg_cfg = ast_config_load(filename, config_flags);
8637 if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
8638 msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
8639 ast_config_destroy(msg_cfg);
8640 }
8641#endif
8642
8643 if (attach_user_voicemail)
8644 RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
8645
8646 /* XXX possible imap issue, should category be NULL XXX */
8647 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);
8648
8649 if (attach_user_voicemail)
8650 DISPOSE(todir, msgnum);
8651 }
8652
8653 if (!ast_strlen_zero(vmu->pager)) {
8654 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
8655 }
8656
8657 if (ast_test_flag(vmu, VM_DELETE))
8658 DELETE(todir, msgnum, fn, vmu);
8659
8660 /* Leave voicemail for someone */
8661 if (ast_app_has_voicemail(ext_context, NULL))
8662 ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
8663
8664 queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgentmsgs, newmsgs, oldmsgs);
8665 run_externnotify(vmu->context, vmu->mailbox, flag);
8666
8667#ifdef IMAP_STORAGE
8668 vm_delete(fn); /* Delete the file, but not the IMAP message */
8669 if (ast_test_flag(vmu, VM_DELETE)) { /* Delete the IMAP message if delete = yes */
8670 vm_imap_delete(NULL, vms->curmsg, vmu);
8671 vms->newmessages--; /* Fix new message count */
8672 }
8673#endif
8674
8675 return 0;
8676}
8677
8678/*!
8679 * \brief Sends a voicemail message to a mailbox recipient.
8680 * \param chan
8681 * \param context
8682 * \param vms
8683 * \param sender
8684 * \param fmt
8685 * \param is_new_message Used to indicate the mode for which this method was invoked.
8686 * Will be 0 when called to forward an existing message (option 8)
8687 * Will be 1 when called to leave a message (option 3->5)
8688 * \param record_gain
8689 * \param urgent
8690 *
8691 * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
8692 *
8693 * When in the leave message mode (is_new_message == 1):
8694 * - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
8695 * - attempt to determine the context and mailbox, and then invoke leave_message() function to record and store the message.
8696 *
8697 * When in the forward message mode (is_new_message == 0):
8698 * - retrieves the current message to be forwarded
8699 * - copies the original message to a temporary file, so updates to the envelope can be done.
8700 * - determines the target mailbox and folders
8701 * - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
8702 *
8703 * \return zero on success, -1 on error.
8704 */
8705static 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)
8706{
8707#ifdef IMAP_STORAGE
8708 int todircount = 0;
8709 struct vm_state *dstvms;
8710#endif
8711 char username[70]="";
8712 char fn[PATH_MAX]; /* for playback of name greeting */
8713 char ecodes[16] = "#";
8714 int res = 0, cmd = 0;
8715 struct ast_vm_user *receiver = NULL, *vmtmp;
8717 char *stringp;
8718 const char *s;
8719 const char mailbox_context[256];
8720 int saved_messages = 0;
8721 int valid_extensions = 0;
8722 char *dir;
8723 int curmsg;
8724 char urgent_str[7] = "";
8725 int prompt_played = 0;
8726#ifndef IMAP_STORAGE
8727 char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
8728#endif
8729 SCOPE_ENTER(3, "%s: user: %s dir: %s msg: %d\n", ast_channel_name(chan),
8730 vms->username, vms->curdir, vms->curmsg);
8731
8733 ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
8734 }
8735
8736 if (vms == NULL) {
8737 SCOPE_EXIT_RTN_VALUE(-1, "vms is NULL\n");
8738 }
8739 dir = vms->curdir;
8740 curmsg = vms->curmsg;
8741
8742 ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
8743 while (!res && !valid_extensions) {
8744 int use_directory = 0;
8746 int done = 0;
8747 int retries = 0;
8748 cmd = 0;
8749 while ((cmd >= 0) && !done ){
8750 if (cmd)
8751 retries = 0;
8752 switch (cmd) {
8753 case '1':
8754 use_directory = 0;
8755 done = 1;
8756 break;
8757 case '2':
8758 use_directory = 1;
8759 done = 1;
8760 break;
8761 case '*':
8762 cmd = 't';
8763 done = 1;
8764 break;
8765 default:
8766 /* Press 1 to enter an extension press 2 to use the directory */
8767 cmd = ast_play_and_wait(chan, "vm-forward");
8768 if (!cmd) {
8769 cmd = ast_waitfordigit(chan, 3000);
8770 }
8771 if (!cmd) {
8772 retries++;
8773 }
8774 if (retries > 3) {
8775 cmd = 't';
8776 done = 1;
8777 }
8778 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8779 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
8780 }
8781 }
8782 if (cmd < 0 || cmd == 't')
8783 break;
8784 }
8785
8786 if (use_directory) {
8787 /* use app_directory */
8788
8789 struct ast_app* directory_app;
8790
8791 directory_app = pbx_findapp("Directory");
8792 if (directory_app) {
8793 char vmcontext[256];
8794 char old_context[strlen(ast_channel_context(chan)) + 1];
8795 char old_exten[strlen(ast_channel_exten(chan)) + 1];
8796 int old_priority;
8797 /* make backup copies */
8798 strcpy(old_context, ast_channel_context(chan)); /* safe */
8799 strcpy(old_exten, ast_channel_exten(chan)); /* safe */
8800 old_priority = ast_channel_priority(chan);
8801
8802 /* call the Directory, changes the channel */
8803 snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
8804 res = pbx_exec(chan, directory_app, vmcontext);
8805
8806 ast_copy_string(username, ast_channel_exten(chan), sizeof(username));
8807
8808 /* restore the old context, exten, and priority */
8809 ast_channel_context_set(chan, old_context);
8810 ast_channel_exten_set(chan, old_exten);
8811 ast_channel_priority_set(chan, old_priority);
8812 } else {
8813 ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
8815 }
8816 } else {
8817 /* Ask for an extension */
8818 res = ast_streamfile(chan, "vm-extension", ast_channel_language(chan)); /* "extension" */
8819 prompt_played++;
8820 if (res || prompt_played > 4)
8821 break;
8822 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#")) < 0)
8823 break;
8824 }
8825
8826 /* start all over if no username */
8827 if (ast_strlen_zero(username))
8828 continue;
8829 stringp = username;
8830 s = strsep(&stringp, "*");
8831 /* start optimistic */
8832 valid_extensions = 1;
8833 while (s) {
8834 snprintf((char*)mailbox_context, sizeof(mailbox_context), "%s@%s", s, context ? context : "default");
8835 if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
8836 int oldmsgs;
8837 int newmsgs;
8838 int capacity;
8839
8840 if (inboxcount(mailbox_context, &newmsgs, &oldmsgs)) {
8841 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", mailbox_context);
8842 /* Shouldn't happen, but allow trying another extension if it does */
8843 res = ast_play_and_wait(chan, "pbx-invalid");
8844 valid_extensions = 0;
8845 break;
8846 }
8847#ifdef IMAP_STORAGE
8848 if (!(dstvms = get_vm_state_by_mailbox(s, context, 0))) {
8849 if (!(dstvms = create_vm_state_from_user(receiver))) {
8850 ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
8851 /* Shouldn't happen, but allow trying another extension if it does */
8852 res = ast_play_and_wait(chan, "pbx-invalid");
8853 valid_extensions = 0;
8854 break;
8855 }
8856 }
8857 check_quota(dstvms, imapfolder);
8858 if (dstvms->quota_limit && dstvms->quota_usage >= dstvms->quota_limit) {
8859 ast_log(LOG_NOTICE, "Mailbox '%s' is exceeded quota %u >= %u\n", mailbox_context, dstvms->quota_usage, dstvms->quota_limit);
8860 res = ast_play_and_wait(chan, "vm-mailboxfull");
8861 valid_extensions = 0;
8862 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8863 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8864 free_user(vmtmp);
8865 }
8866 break;
8867 }
8868#endif
8869 capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
8870 if ((newmsgs + oldmsgs) >= capacity) {
8871 ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", mailbox_context, capacity);
8872 res = ast_play_and_wait(chan, "vm-mailboxfull");
8873 valid_extensions = 0;
8874 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8875 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8876 free_user(vmtmp);
8877 }
8878 inprocess_count(receiver->mailbox, receiver->context, -1);
8879 break;
8880 }
8881 AST_LIST_INSERT_HEAD(&extensions, receiver, list);
8882 } else {
8883 /* XXX Optimization for the future. When we encounter a single bad extension,
8884 * bailing out on all of the extensions may not be the way to go. We should
8885 * probably just bail on that single extension, then allow the user to enter
8886 * several more. XXX
8887 */
8888 while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8889 free_user(receiver);
8890 }
8891 ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", mailbox_context);
8892 /* "I am sorry, that's not a valid extension. Please try again." */
8893 res = ast_play_and_wait(chan, "pbx-invalid");
8894 valid_extensions = 0;
8895 break;
8896 }
8897
8898 /* play name if available, else play extension number */
8899 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
8900 SCOPE_CALL(-1, RETRIEVE, fn, -1, s, receiver->context);
8901 if (ast_fileexists(fn, NULL, NULL) > 0) {
8902 res = ast_stream_and_wait(chan, fn, ecodes);
8903 if (res) {
8904 SCOPE_CALL(-1, DISPOSE, fn, -1);
8905 return res;
8906 }
8907 } else {
8908 res = ast_say_digit_str(chan, s, ecodes, ast_channel_language(chan));
8909 }
8910 SCOPE_CALL(-1, DISPOSE, fn, -1);
8911
8912 s = strsep(&stringp, "*");
8913 }
8914 /* break from the loop of reading the extensions */
8915 if (valid_extensions)
8916 break;
8917 }
8918 /* check if we're clear to proceed */
8919 if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
8920 return res;
8921 if (is_new_message == 1) {
8922 struct leave_vm_options leave_options;
8923 char mailbox[AST_MAX_EXTENSION * 2 + 2];
8924 snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
8925
8926 /* Send VoiceMail */
8927 memset(&leave_options, 0, sizeof(leave_options));
8928 leave_options.record_gain = record_gain;
8929 leave_options.beeptone = "beep";
8930 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, leave_voicemail, chan, mailbox, &leave_options);
8931 } else {
8932 /* Forward VoiceMail */
8933 long duration = 0;
8934 struct vm_state vmstmp;
8935 int copy_msg_result = 0;
8936#ifdef IMAP_STORAGE
8937 char filename[PATH_MAX];
8938 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8939 const char *msg_id = NULL;
8940 struct ast_config *msg_cfg;
8941#endif
8942 memcpy(&vmstmp, vms, sizeof(vmstmp));
8943
8944 SCOPE_CALL(-1, RETRIEVE, dir, curmsg, sender->mailbox, sender->context);
8945#ifdef IMAP_STORAGE
8946 make_file(filename, sizeof(filename), dir, curmsg);
8947 strncat(filename, ".txt", sizeof(filename) - strlen(filename) - 1);
8948 msg_cfg = ast_config_load(filename, config_flags);
8949 if (msg_cfg && msg_cfg == CONFIG_STATUS_FILEINVALID) {
8950 msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
8951 ast_config_destroy(msg_cfg);
8952 }
8953#endif
8954
8955 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);
8956 if (!cmd) {
8957 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
8958#ifdef IMAP_STORAGE
8959 int attach_user_voicemail;
8960 char *myserveremail = serveremail;
8961
8962 /* get destination mailbox */
8963 dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
8964 if (!dstvms) {
8965 dstvms = create_vm_state_from_user(vmtmp);
8966 }
8967 if (dstvms) {
8968 init_mailstream(dstvms, 0);
8969 if (!dstvms->mailstream) {
8970 ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
8971 } else {
8972 copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
8973 run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str);
8974 }
8975 } else {
8976 ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
8977 }
8978 if (!ast_strlen_zero(vmtmp->serveremail))
8979 myserveremail = vmtmp->serveremail;
8980 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
8981 /* NULL category for IMAP storage */
8982 sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
8983 dstvms->curbox,
8984 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
8985 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
8986 vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
8987 NULL, urgent_str, msg_id);
8988#else
8989 copy_msg_result = SCOPE_CALL_WITH_INT_RESULT(-1, copy_message, chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str, NULL);
8990#endif
8991 saved_messages++;
8993 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8994 free_user(vmtmp);
8995 if (res)
8996 break;
8997 }
8999 if (saved_messages > 0 && !copy_msg_result) {
9000 /* give confirmation that the message was saved */
9001 /* commented out since we can't forward batches yet
9002 if (saved_messages == 1)
9003 res = ast_play_and_wait(chan, "vm-message");
9004 else
9005 res = ast_play_and_wait(chan, "vm-messages");
9006 if (!res)
9007 res = ast_play_and_wait(chan, "vm-saved"); */
9008 res = ast_play_and_wait(chan, "vm-msgforwarded");
9009 }
9010#ifndef IMAP_STORAGE
9011 else {
9012 /* with IMAP, mailbox full warning played by imap_check_limits */
9013 res = ast_play_and_wait(chan, "vm-mailboxfull");
9014 }
9015 /* Restore original message without prepended message if backup exists */
9016 make_file(msgfile, sizeof(msgfile), dir, curmsg);
9017 strcpy(textfile, msgfile);
9018 strcpy(backup, msgfile);
9019 strcpy(backup_textfile, msgfile);
9020 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
9021 strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
9022 strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
9023 if (ast_fileexists(backup, NULL, NULL) > 0) {
9024 ast_filerename(backup, msgfile, NULL);
9025 rename(backup_textfile, textfile);
9026 }
9027#endif
9028 }
9029 SCOPE_CALL(-1, DISPOSE, dir, curmsg);
9030#ifndef IMAP_STORAGE
9031 if (cmd) { /* assuming hangup, cleanup backup file */
9032 make_file(msgfile, sizeof(msgfile), dir, curmsg);
9033 strcpy(textfile, msgfile);
9034 strcpy(backup_textfile, msgfile);
9035 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
9036 strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
9037 rename(backup_textfile, textfile);
9038 }
9039#endif
9040 }
9041
9042 /* If anything failed above, we still have this list to free */
9043 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
9044 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
9045 free_user(vmtmp);
9046 }
9047 SCOPE_EXIT_RTN_VALUE(res ? res : cmd, "Done. res: %d cmd: %d\n", res, cmd);
9048}
9049
9050static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
9051{
9052 int res;
9053 if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
9054 ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file);
9055 return res;
9056}
9057
9058static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
9059{
9060 ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
9062}
9063
9064static int play_message_category(struct ast_channel *chan, const char *category)
9065{
9066 int res = 0;
9067
9068 if (!ast_strlen_zero(category))
9069 res = ast_play_and_wait(chan, category);
9070
9071 if (res) {
9072 ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
9073 res = 0;
9074 }
9075
9076 return res;
9077}
9078
9079static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
9080{
9081 int res = 0;
9082 struct vm_zone *the_zone = NULL;
9083 time_t t;
9084
9085 if (ast_get_time_t(origtime, &t, 0, NULL)) {
9086 ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
9087 return 0;
9088 }
9089
9090 /* Does this user have a timezone specified? */
9091 if (!ast_strlen_zero(vmu->zonetag)) {
9092 /* Find the zone in the list */
9093 struct vm_zone *z;
9096 if (!strcmp(z->name, vmu->zonetag)) {
9097 the_zone = z;
9098 break;
9099 }
9100 }
9102 }
9103
9104/* No internal variable parsing for now, so we'll comment it out for the time being */
9105#if 0
9106 /* Set the DIFF_* variables */
9107 ast_localtime(&t, &time_now, NULL);
9108 tv_now = ast_tvnow();
9109 ast_localtime(&tv_now, &time_then, NULL);
9110
9111 /* Day difference */
9112 if (time_now.tm_year == time_then.tm_year)
9113 snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
9114 else
9115 snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
9116 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
9117
9118 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
9119#endif
9120 if (the_zone) {
9121 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), the_zone->msg_format, the_zone->timezone);
9122 } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
9123 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9124 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
9125 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q H 'digits/kai' M ", NULL);
9126 } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */
9127 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9128 } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
9129 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);
9130 } else if (!strcasecmp(ast_channel_language(chan),"ja")) { /* Japanese syntax */
9131 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "PHM q 'jp-ni' 'vm-received'", NULL);
9132 } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
9133 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/nl-om' HM", NULL);
9134 } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
9135 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9136 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
9137 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q HM", NULL);
9138 } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* Brazilian PORTUGUESE syntax */
9139 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);
9140 } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
9141 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' dB 'digits/at' k 'and' M", NULL);
9142 } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
9143 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "qR 'vm-received'", NULL);
9144 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
9145 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);
9146 } else {
9147 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' IMp", NULL);
9148 }
9149#if 0
9150 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
9151#endif
9152 return res;
9153}
9154
9155
9156
9157static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback, int saycidnumber)
9158{
9159 int res = 0;
9160 int i;
9161 char *callerid, *name;
9162 char prefile[PATH_MAX] = "";
9163
9164 /* If voicemail cid is not enabled, or we didn't get cid or context from
9165 * the attribute file, leave now.
9166 *
9167 * TODO Still need to change this so that if this function is called by the
9168 * message envelope (and someone is explicitly requesting to hear the CID),
9169 * it does not check to see if CID is enabled in the config file.
9170 */
9171 if ((cid == NULL)||(context == NULL))
9172 return res;
9173
9174 /* Strip off caller ID number from name */
9175 ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
9176 ast_callerid_parse(cid, &name, &callerid);
9177 if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
9178 /* Check for internal contexts and only */
9179 /* say extension when the call didn't come from an internal context in the list */
9180 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
9181 ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
9182 if ((strcmp(cidinternalcontexts[i], context) == 0))
9183 break;
9184 }
9185 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
9186 if (!res) {
9187 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
9188 if (!ast_strlen_zero(prefile)) {
9189 /* See if we can find a recorded name for this callerid
9190 * and if found, use that instead of saying number. */
9191 if (ast_fileexists(prefile, NULL, NULL) > 0) {
9192 ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
9193 if (!callback)
9194 res = wait_file2(chan, vms, "vm-from");
9195 res = ast_stream_and_wait(chan, prefile, "");
9196 } else {
9197 ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
9198 /* Say "from extension" as one saying to sound smoother */
9199 if (!callback)
9200 res = wait_file2(chan, vms, "vm-from-extension");
9201 res = ast_say_digit_str(chan, callerid, "", ast_channel_language(chan));
9202 }
9203 }
9204 }
9205 } else if (!res) {
9206 ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
9207 /* If there is a recording for this numeric callerid then play that */
9208 if (!callback) {
9209 /* See if we can find a recorded name for this person instead of their extension number */
9210 snprintf(prefile, sizeof(prefile), "%s/recordings/callerids/%s", ast_config_AST_SPOOL_DIR, callerid);
9211 if (!saycidnumber && ast_fileexists(prefile, NULL, NULL) > 0) {
9212 ast_verb(3, "Playing recorded name for CID number '%s' - '%s'\n", callerid,prefile);
9213 wait_file2(chan, vms, "vm-from");
9214 res = ast_stream_and_wait(chan, prefile, "");
9215 ast_verb(3, "Played recorded name result '%d'\n", res);
9216 } else {
9217 /* Since this is all nicely figured out, why not say "from phone number" in this case" */
9218 wait_file2(chan, vms, "vm-from-phonenumber");
9219 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
9220 }
9221 } else {
9222 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
9223 }
9224 }
9225 } else {
9226 /* Number unknown */
9227 ast_debug(1, "VM-CID: From an unknown number\n");
9228 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
9229 res = wait_file2(chan, vms, "vm-unknown-caller");
9230 }
9231 return res;
9232}
9233
9234static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
9235{
9236 int res = 0;
9237 int durationm;
9238 int durations;
9239 /* Verify that we have a duration for the message */
9240 if (duration == NULL)
9241 return res;
9242
9243 /* Convert from seconds to minutes */
9244 durations = atoi(duration);
9245 durationm = (durations / 60);
9246
9247 ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
9248
9249 if ((!res) && (durationm >= minduration)) {
9250 res = wait_file2(chan, vms, "vm-duration");
9251
9252 /* POLISH syntax */
9253 if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9254 div_t num = div(durationm, 10);
9255
9256 if (durationm == 1) {
9257 res = ast_play_and_wait(chan, "digits/1z");
9258 res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
9259 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
9260 if (num.rem == 2) {
9261 if (!num.quot) {
9262 res = ast_play_and_wait(chan, "digits/2-ie");
9263 } else {
9264 res = say_and_wait(chan, durationm - 2 , ast_channel_language(chan));
9265 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
9266 }
9267 } else {
9268 res = say_and_wait(chan, durationm, ast_channel_language(chan));
9269 }
9270 res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
9271 } else {
9272 res = say_and_wait(chan, durationm, ast_channel_language(chan));
9273 res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
9274 }
9275 /* DEFAULT syntax */
9276 } else {
9277 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
9278 res = wait_file2(chan, vms, "vm-minutes");
9279 }
9280 }
9281 return res;
9282}
9283
9284static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
9285{
9286 int res = 0;
9287 char filename[PATH_MAX], *cid;
9288 const char *origtime, *context, *category, *duration, *flag;
9289 struct ast_config *msg_cfg;
9290 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
9291 SCOPE_ENTER(3, "%s: user: %s dir: %s msg: %d\n", ast_channel_name(chan),
9292 vms->username, vms->curdir, vms->curmsg);
9293
9294 vms->starting = 0;
9295 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
9296 adsi_message(chan, vms);
9297 if (!vms->curmsg) {
9298 res = wait_file2(chan, vms, "vm-first"); /* "First" */
9299 } else if (vms->curmsg == vms->lastmsg) {
9300 res = wait_file2(chan, vms, "vm-last"); /* "last" */
9301 }
9302
9303 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
9304 SCOPE_CALL(-1, RETRIEVE, vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
9305 msg_cfg = ast_config_load(filename, config_flags);
9306 if (!valid_config(msg_cfg)) {
9307 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
9308 return 0;
9309 }
9310 flag = ast_variable_retrieve(msg_cfg, "message", "flag");
9311
9312 /* Play the word urgent if we are listening to urgent messages */
9313 if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
9314 res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
9315 }
9316
9317 if (!res) {
9318 /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
9319 /* POLISH syntax */
9320 if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9321 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9322 int ten, one;
9323 char nextmsg[256];
9324 ten = (vms->curmsg + 1) / 10;
9325 one = (vms->curmsg + 1) % 10;
9326
9327 if (vms->curmsg < 20) {
9328 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
9329 res = wait_file2(chan, vms, nextmsg);
9330 } else {
9331 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
9332 res = wait_file2(chan, vms, nextmsg);
9333 if (one > 0) {
9334 if (!res) {
9335 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
9336 res = wait_file2(chan, vms, nextmsg);
9337 }
9338 }
9339 }
9340 }
9341 if (!res)
9342 res = wait_file2(chan, vms, "vm-message");
9343 /* HEBREW syntax */
9344 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
9345 if (!vms->curmsg) {
9346 res = wait_file2(chan, vms, "vm-message");
9347 res = wait_file2(chan, vms, "vm-first");
9348 } else if (vms->curmsg == vms->lastmsg) {
9349 res = wait_file2(chan, vms, "vm-message");
9350 res = wait_file2(chan, vms, "vm-last");
9351 } else {
9352 res = wait_file2(chan, vms, "vm-message");
9353 res = wait_file2(chan, vms, "vm-number");
9354 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9355 }
9356 /* ICELANDIC syntax */
9357 } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) {
9358 res = wait_file2(chan, vms, "vm-message");
9359 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9360 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "n");
9361 }
9362 /* VIETNAMESE syntax */
9363 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
9364 if (!vms->curmsg) {
9365 res = wait_file2(chan, vms, "vm-message");
9366 res = wait_file2(chan, vms, "vm-first");
9367 } else if (vms->curmsg == vms->lastmsg) {
9368 res = wait_file2(chan, vms, "vm-message");
9369 res = wait_file2(chan, vms, "vm-last");
9370 } else {
9371 res = wait_file2(chan, vms, "vm-message");
9372 res = wait_file2(chan, vms, "vm-number");
9373 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9374 }
9375 } else {
9376 if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
9377 res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
9378 } else { /* DEFAULT syntax */
9379 res = wait_file2(chan, vms, "vm-message");
9380 }
9381 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9382 if (!res) {
9383 ast_test_suite_event_notify("PLAYBACK", "Message: message number");
9384 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
9385 }
9386 }
9387 }
9388 }
9389
9390 if (!valid_config(msg_cfg)) {
9391 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
9392 }
9393
9394 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
9395 SCOPE_CALL(-1, DISPOSE, vms->curdir, vms->curmsg);
9396 ast_config_destroy(msg_cfg);
9397 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "No origtime?!\n");
9398 }
9399
9400 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
9401 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
9402 category = ast_variable_retrieve(msg_cfg, "message", "category");
9403
9404 context = ast_variable_retrieve(msg_cfg, "message", "context");
9405 if (!res) {
9406 res = play_message_category(chan, category);
9407 }
9408 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
9409 res = play_message_datetime(chan, vmu, origtime, filename);
9410 }
9411 if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
9412 res = play_message_callerid(chan, vms, cid, context, 0, 0);
9413 }
9414 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
9415 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
9416 }
9417 /* Allow pressing '1' to skip envelope / callerid */
9418 if (res == '1') {
9419 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
9420 res = 0;
9421 }
9422 ast_config_destroy(msg_cfg);
9423
9424 if (!res) {
9425 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
9426#ifdef IMAP_STORAGE
9427 ast_mutex_lock(&vms->lock);
9428#endif
9429 vms->heard[vms->curmsg] = 1;
9430#ifdef IMAP_STORAGE
9431 ast_mutex_unlock(&vms->lock);
9432 /*IMAP storage stores any prepended message from a forward
9433 * as a separate file from the rest of the message
9434 */
9435 if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
9436 wait_file(chan, vms, vms->introfn);
9437 }
9438#endif
9439 if ((res = wait_file(chan, vms, vms->fn)) < 0) {
9440 ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
9441 res = 0;
9442 }
9443 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
9444 isprint(res) ? res : '?', isprint(res) ? res : '?');
9445 }
9446 SCOPE_CALL(-1, DISPOSE, vms->curdir, vms->curmsg);
9447 SCOPE_EXIT_RTN_VALUE(res, "Done: RC: %d\n", res);
9448}
9449
9450#ifdef IMAP_STORAGE
9451static int imap_remove_file(char *dir, int msgnum)
9452{
9453 char fn[PATH_MAX];
9454 char full_fn[PATH_MAX];
9455 char intro[PATH_MAX] = {0,};
9456
9457 if (msgnum > -1) {
9458 make_file(fn, sizeof(fn), dir, msgnum);
9459 snprintf(intro, sizeof(intro), "%sintro", fn);
9460 } else
9461 ast_copy_string(fn, dir, sizeof(fn));
9462
9463 if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
9464 ast_filedelete(fn, NULL);
9465 if (!ast_strlen_zero(intro)) {
9467 }
9468 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
9469 unlink(full_fn);
9470 }
9471 return 0;
9472}
9473
9474
9475
9476static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
9477{
9478 char *file, *filename;
9479 char arg[11];
9480 int i;
9481 BODY* body;
9482 int curr_mbox;
9483
9484 file = strrchr(ast_strdupa(dir), '/');
9485 if (file) {
9486 *file++ = '\0';
9487 } else {
9488 ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
9489 return -1;
9490 }
9491
9492 ast_mutex_lock(&vms->lock);
9493
9494 /* get the current mailbox so that we can point the mailstream back to it later */
9495 curr_mbox = get_folder_by_name(vms->curbox);
9496
9497 if (init_mailstream(vms, GREETINGS_FOLDER) || !vms->mailstream) {
9498 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
9499 ast_mutex_unlock(&vms->lock);
9500 return -1;
9501 }
9502
9503 for (i = 0; i < vms->mailstream->nmsgs; i++) {
9504 mail_fetchstructure(vms->mailstream, i + 1, &body);
9505 /* We have the body, now we extract the file name of the first attachment. */
9506 if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
9507 char *attachment = body->nested.part->next->body.parameter->value;
9508 char copy[strlen(attachment) + 1];
9509
9510 strcpy(copy, attachment); /* safe */
9511 attachment = copy;
9512
9513 filename = strsep(&attachment, ".");
9514 if (!strcmp(filename, file)) {
9515 snprintf(arg, sizeof(arg), "%d", i + 1);
9516 mail_setflag(vms->mailstream, arg, "\\DELETED");
9517 }
9518 } else {
9519 ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
9520 ast_mutex_unlock(&vms->lock);
9521 return -1;
9522 }
9523 }
9524 mail_expunge(vms->mailstream);
9525
9526 if (curr_mbox != -1) {
9527 /* restore previous mbox stream */
9528 if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
9529 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
9530 }
9531 }
9532
9533 ast_mutex_unlock(&vms->lock);
9534 return 0;
9535}
9536
9537#elif !defined(IMAP_STORAGE)
9538static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
9539{
9540 int count_msg, last_msg;
9541 SCOPE_ENTER(3, "user: %s dir: %s msg: %d box %d\n",
9542 vms->username, vms->curdir, vms->curmsg, box);
9543
9544 ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
9545
9546 /* Rename the member vmbox HERE so that we don't try to return before
9547 * we know what's going on.
9548 */
9549 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
9550
9551 /* Faster to make the directory than to check if it exists. */
9552 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
9553
9554 /* traverses directory using readdir (or select query for ODBC) */
9555 count_msg = COUNT(vmu, vms->curdir);
9556 if (count_msg < 0) {
9557 SCOPE_EXIT_RTN_VALUE(count_msg, "msgs: %d\n", count_msg);
9558 } else {
9559 vms->lastmsg = count_msg - 1;
9560 }
9561
9562 if (vm_allocate_dh(vms, vmu, count_msg)) {
9563 SCOPE_EXIT_RTN_VALUE(-1, "failed to allocate dh\n");
9564 }
9565
9566 /*
9567 The following test is needed in case sequencing gets messed up.
9568 There appears to be more than one way to mess up sequence, so
9569 we will not try to find all of the root causes--just fix it when
9570 detected.
9571 */
9572
9573 if (vm_lock_path(vms->curdir)) {
9574 SCOPE_EXIT_LOG_RTN_VALUE(ERROR_LOCK_PATH, AST_LOG_ERROR, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
9575 }
9576
9577 /* for local storage, checks directory for messages up to MAXMSGLIMIT */
9578 last_msg = LAST_MSG_INDEX(vms->curdir);
9579 ast_unlock_path(vms->curdir);
9580
9581 if (last_msg < -1) {
9582 SCOPE_EXIT_RTN_VALUE(last_msg, "last msg: %d\n", last_msg);
9583 } else if (vms->lastmsg != last_msg) {
9584 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);
9585 resequence_mailbox(vmu, vms->curdir, count_msg);
9586 }
9587
9588 SCOPE_EXIT_RTN_VALUE(0, "Done\n");
9589}
9590#endif
9591
9592static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
9593{
9594 int x = 0;
9595 int last_msg_idx = 0;
9596
9597#ifndef IMAP_STORAGE
9598 int res = 0, nummsg;
9599 char fn2[PATH_MAX];
9600#endif
9601 SCOPE_ENTER(3, "user: %s dir: %s msg: %d\n",
9602 vms->username, vms->curdir, vms->curmsg);
9603
9604 if (vms->lastmsg <= -1) {
9605 ast_trace(-1, "No messages in mailbox\n");
9606 goto done;
9607 }
9608
9609 vms->curmsg = -1;
9610#ifndef IMAP_STORAGE
9611 /* Get the deleted messages fixed */
9612 if (vm_lock_path(vms->curdir)) {
9613 SCOPE_EXIT_RTN_VALUE(ERROR_LOCK_PATH, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
9614 }
9615
9616 /* update count as message may have arrived while we've got mailbox open */
9617 last_msg_idx = LAST_MSG_INDEX(vms->curdir);
9618 if (last_msg_idx != vms->lastmsg) {
9619 ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
9620 }
9621
9622 /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
9623 for (x = 0; x < last_msg_idx + 1; x++) {
9624 if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
9625 /* Save this message. It's not in INBOX or hasn't been heard */
9626 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9627 if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
9628 break;
9629 }
9630 vms->curmsg++;
9631 make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
9632 if (strcmp(vms->fn, fn2)) {
9633 SCOPE_CALL(-1, RENAME, vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
9634 }
9635 } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
9636 /* Move to old folder before deleting */
9637 res = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, vms, x, 1, NULL, 0);
9638 if (res == ERROR_LOCK_PATH || res == ERROR_MAX_MSGS) {
9639 /* If save failed do not delete the message */
9640 ast_log(AST_LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
9641 vms->deleted[x] = 0;
9642 vms->heard[x] = 0;
9643 --x;
9644 }
9645 } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
9646 /* Move to deleted folder */
9647 res = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, vms, x, 10, NULL, 0);
9648 if (res == ERROR_LOCK_PATH) {
9649 ast_trace(-1, "Unable to lock path. Not moving message to deleted folder.\n");
9650 /* If save failed do not delete the message */
9651 vms->deleted[x] = 0;
9652 vms->heard[x] = 0;
9653 --x;
9654 }
9655 } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
9656 /* If realtime storage enabled - we should explicitly delete this message,
9657 cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
9658 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9659 res = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, vms->curdir, x, vms->fn, NULL);
9660 if (res) {
9661 SCOPE_CALL(-1, DELETE, vms->curdir, x, vms->fn, vmu);
9662 }
9663 }
9664 }
9665
9666 /* Delete ALL remaining messages */
9667 nummsg = x - 1;
9668 for (x = vms->curmsg + 1; x <= nummsg; x++) {
9669 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9670 res = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, vms->curdir, x, vms->fn, NULL);
9671 if (res) {
9672 SCOPE_CALL(-1, DELETE, vms->curdir, x, vms->fn, vmu);
9673 }
9674 }
9675 ast_unlock_path(vms->curdir);
9676#else /* defined(IMAP_STORAGE) */
9677 ast_mutex_lock(&vms->lock);
9678 if (vms->deleted) {
9679 /* Since we now expunge after each delete, deleting in reverse order
9680 * ensures that no reordering occurs between each step. */
9681 last_msg_idx = vms->dh_arraysize;
9682 for (x = last_msg_idx - 1; x >= 0; x--) {
9683 if (vms->deleted[x]) {
9684 ast_debug(3, "IMAP delete of %d\n", x);
9685 DELETE(vms->curdir, x, vms->fn, vmu);
9686 }
9687 }
9688 }
9689#endif
9690
9691done:
9692 if (vms->deleted) {
9693 ast_free(vms->deleted);
9694 vms->deleted = NULL;
9695 }
9696 if (vms->heard) {
9697 ast_free(vms->heard);
9698 vms->heard = NULL;
9699 }
9700 vms->dh_arraysize = 0;
9701#ifdef IMAP_STORAGE
9702 ast_mutex_unlock(&vms->lock);
9703#endif
9704
9705 SCOPE_EXIT_RTN_VALUE(0, "Done\n");
9706}
9707
9708/* In Greek even though we CAN use a syntax like "friends messages"
9709 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
9710 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
9711 * syntax for the above three categories which is more elegant.
9712 */
9713
9714static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
9715{
9716 int cmd;
9717 char *buf;
9718
9719 buf = ast_alloca(strlen(box) + 2);
9720 strcpy(buf, box);
9721 strcat(buf, "s");
9722
9723 if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
9724 cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
9725 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
9726 } else {
9727 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
9728 return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
9729 }
9730}
9731
9732static int vm_play_folder_name_ja(struct ast_channel *chan, char *box)
9733{
9734 int cmd;
9735
9736 if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
9737 cmd = ast_play_and_wait(chan, box);
9738 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9739 } else {
9740 cmd = ast_play_and_wait(chan, box);
9741 return cmd;
9742 }
9743}
9744
9745static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
9746{
9747 int cmd;
9748
9749 if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
9750 if (!strcasecmp(box, "vm-INBOX"))
9751 cmd = ast_play_and_wait(chan, "vm-new-e");
9752 else
9753 cmd = ast_play_and_wait(chan, "vm-old-e");
9754 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9755 } else {
9756 cmd = ast_play_and_wait(chan, "vm-messages");
9757 return cmd ? cmd : ast_play_and_wait(chan, box);
9758 }
9759}
9760
9761static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
9762{
9763 int cmd;
9764
9765 if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
9766 cmd = ast_play_and_wait(chan, "vm-messages");
9767 return cmd ? cmd : ast_play_and_wait(chan, box);
9768 } else {
9769 cmd = ast_play_and_wait(chan, box);
9770 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9771 }
9772}
9773
9774static int vm_play_folder_name(struct ast_channel *chan, char *box)
9775{
9776 int cmd;
9777
9778 if ( !strncasecmp(ast_channel_language(chan), "it", 2) ||
9779 !strncasecmp(ast_channel_language(chan), "es", 2) ||
9780 !strncasecmp(ast_channel_language(chan), "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
9781 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
9782 return cmd ? cmd : ast_play_and_wait(chan, box);
9783 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {
9784 return vm_play_folder_name_gr(chan, box);
9785 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* Hebrew syntax */
9786 return ast_play_and_wait(chan, box);
9787 } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
9788 return vm_play_folder_name_ja(chan, box);
9789 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9790 return vm_play_folder_name_pl(chan, box);
9791 } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian syntax */
9792 return vm_play_folder_name_ua(chan, box);
9793 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
9794 return ast_play_and_wait(chan, box);
9795 } else { /* Default English */
9796 cmd = ast_play_and_wait(chan, box);
9797 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
9798 }
9799}
9800
9801/* GREEK SYNTAX
9802 In greek the plural for old/new is
9803 different so we need the following files
9804 We also need vm-denExeteMynhmata because
9805 this syntax is different.
9806
9807 -> vm-Olds.wav : "Palia"
9808 -> vm-INBOXs.wav : "Nea"
9809 -> vm-denExeteMynhmata : "den exete mynhmata"
9810*/
9811
9812
9813static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
9814{
9815 int res = 0;
9816
9817 if (vms->newmessages) {
9818 res = ast_play_and_wait(chan, "vm-youhave");
9819 if (!res)
9821 if (!res) {
9822 if (vms->newmessages == 1) {
9823 res = ast_play_and_wait(chan, "vm-INBOX");
9824 if (!res)
9825 res = ast_play_and_wait(chan, "vm-message");
9826 } else {
9827 res = ast_play_and_wait(chan, "vm-INBOXs");
9828 if (!res)
9829 res = ast_play_and_wait(chan, "vm-messages");
9830 }
9831 }
9832 } else if (vms->oldmessages){
9833 res = ast_play_and_wait(chan, "vm-youhave");
9834 if (!res)
9836 if (vms->oldmessages == 1){
9837 res = ast_play_and_wait(chan, "vm-Old");
9838 if (!res)
9839 res = ast_play_and_wait(chan, "vm-message");
9840 } else {
9841 res = ast_play_and_wait(chan, "vm-Olds");
9842 if (!res)
9843 res = ast_play_and_wait(chan, "vm-messages");
9844 }
9845 } else if (!vms->oldmessages && !vms->newmessages)
9846 res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
9847 return res;
9848}
9849
9850/* Version of vm_intro() designed to work for many languages.
9851 *
9852 * It is hoped that this function can prevent the proliferation of
9853 * language-specific vm_intro() functions and in time replace the language-
9854 * specific functions which already exist. An examination of the language-
9855 * specific functions revealed that they all corrected the same deficiencies
9856 * in vm_intro_en() (which was the default function). Namely:
9857 *
9858 * 1) The vm-Old and vm-INBOX sound files were overloaded. The English
9859 * wording of the voicemail greeting hides this problem. For example,
9860 * vm-INBOX contains only the word "new". This means that both of these
9861 * sequences produce valid utterances:
9862 * * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
9863 * * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
9864 * However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
9865 * in many languages) the first utterance becomes "you have 1 the new message".
9866 * 2) The function contains hardcoded rules for pluralizing the word "message".
9867 * These rules are correct for English, but not for many other languages.
9868 * 3) No attempt is made to pluralize the adjectives ("old" and "new") as
9869 * required in many languages.
9870 * 4) The gender of the word for "message" is not specified. This is a problem
9871 * because in many languages the gender of the number in phrases such
9872 * as "you have one new message" must match the gender of the word
9873 * meaning "message".
9874 *
9875 * Fixing these problems for each new language has meant duplication of effort.
9876 * This new function solves the problems in the following general ways:
9877 * 1) Add new sound files vm-new and vm-old. These can be linked to vm-INBOX
9878 * and vm-Old respectively for those languages where it makes sense.
9879 * 2) Call ast_say_counted_noun() to put the proper gender and number prefix
9880 * on vm-message.
9881 * 3) Call ast_say_counted_adjective() to put the proper gender and number
9882 * prefix on vm-new and vm-old (none for English).
9883 * 4) Pass the gender of the language's word for "message" as an argument to
9884 * this function which is can in turn pass on to the functions which
9885 * say numbers and put endings on nouns and adjectives.
9886 *
9887 * All languages require these messages:
9888 * vm-youhave "You have..."
9889 * vm-and "and"
9890 * vm-no "no" (in the sense of "none", as in "you have no messages")
9891 *
9892 * To use it for English, you will need these additional sound files:
9893 * vm-new "new"
9894 * vm-message "message", singular
9895 * vm-messages "messages", plural
9896 *
9897 * If you use it for Russian and other slavic languages, you will need these additional sound files:
9898 *
9899 * vm-newn "novoye" (singular, neuter)
9900 * vm-newx "novikh" (counting plural form, genative plural)
9901 * vm-message "sobsheniye" (singular form)
9902 * vm-messagex1 "sobsheniya" (first counting plural form, genative singular)
9903 * vm-messagex2 "sobsheniy" (second counting plural form, genative plural)
9904 * digits/1n "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
9905 * digits/2n "dva" (neuter singular)
9906 */
9907static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
9908{
9909 int res;
9910 int lastnum = 0;
9911
9912 res = ast_play_and_wait(chan, "vm-youhave");
9913
9914 if (!res && vms->newmessages) {
9915 lastnum = vms->newmessages;
9916
9917 if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
9918 res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
9919 }
9920
9921 if (!res && vms->oldmessages) {
9922 res = ast_play_and_wait(chan, "vm-and");
9923 }
9924 }
9925
9926 if (!res && vms->oldmessages) {
9927 lastnum = vms->oldmessages;
9928
9929 if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
9930 res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
9931 }
9932 }
9933
9934 if (!res) {
9935 if (lastnum == 0) {
9936 res = ast_play_and_wait(chan, "vm-no");
9937 }
9938 if (!res) {
9939 res = ast_say_counted_noun(chan, lastnum, "vm-message");
9940 }
9941 }
9942
9943 return res;
9944}
9945
9946/* Default Hebrew syntax */
9947static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
9948{
9949 int res = 0;
9950
9951 /* Introduce messages they have */
9952 if (!res) {
9953 if ((vms->newmessages) || (vms->oldmessages)) {
9954 res = ast_play_and_wait(chan, "vm-youhave");
9955 }
9956 /*
9957 * The word "shtei" refers to the number 2 in hebrew when performing a count
9958 * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
9959 * an element, this is one of them.
9960 */
9961 if (vms->newmessages) {
9962 if (!res) {
9963 if (vms->newmessages == 1) {
9964 res = ast_play_and_wait(chan, "vm-INBOX1");
9965 } else {
9966 if (vms->newmessages == 2) {
9967 res = ast_play_and_wait(chan, "vm-shtei");
9968 } else {
9969 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9970 }
9971 res = ast_play_and_wait(chan, "vm-INBOX");
9972 }
9973 }
9974 if (vms->oldmessages && !res) {
9975 res = ast_play_and_wait(chan, "vm-and");
9976 if (vms->oldmessages == 1) {
9977 res = ast_play_and_wait(chan, "vm-Old1");
9978 } else {
9979 if (vms->oldmessages == 2) {
9980 res = ast_play_and_wait(chan, "vm-shtei");
9981 } else {
9982 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9983 }
9984 res = ast_play_and_wait(chan, "vm-Old");
9985 }
9986 }
9987 }
9988 if (!res && vms->oldmessages && !vms->newmessages) {
9989 if (!res) {
9990 if (vms->oldmessages == 1) {
9991 res = ast_play_and_wait(chan, "vm-Old1");
9992 } else {
9993 if (vms->oldmessages == 2) {
9994 res = ast_play_and_wait(chan, "vm-shtei");
9995 } else {
9996 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9997 }
9998 res = ast_play_and_wait(chan, "vm-Old");
9999 }
10000 }
10001 }
10002 if (!res) {
10003 if (!vms->oldmessages && !vms->newmessages) {
10004 if (!res) {
10005 res = ast_play_and_wait(chan, "vm-nomessages");
10006 }
10007 }
10008 }
10009 }
10010 return res;
10011}
10012
10013/* Japanese syntax */
10014static int vm_intro_ja(struct ast_channel *chan,struct vm_state *vms)
10015{
10016 /* Introduce messages they have */
10017 int res;
10018 if (vms->newmessages) {
10019 res = ast_play_and_wait(chan, "vm-INBOX");
10020 if (!res)
10021 res = ast_play_and_wait(chan, "vm-message");
10022 if (!res)
10023 res = ast_play_and_wait(chan, "jp-ga");
10024 if (!res)
10025 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10026 if (vms->oldmessages && !res)
10027 res = ast_play_and_wait(chan, "silence/1");
10028
10029 }
10030 if (vms->oldmessages) {
10031 res = ast_play_and_wait(chan, "vm-Old");
10032 if (!res)
10033 res = ast_play_and_wait(chan, "vm-message");
10034 if (!res)
10035 res = ast_play_and_wait(chan, "jp-ga");
10036 if (!res)
10037 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10038 }
10039 if (!vms->oldmessages && !vms->newmessages) {
10040 res = ast_play_and_wait(chan, "vm-messages");
10041 if (!res)
10042 res = ast_play_and_wait(chan, "jp-wa");
10043 if (!res)
10044 res = ast_play_and_wait(chan, "jp-arimasen");
10045 }
10046 else {
10047 res = ast_play_and_wait(chan, "jp-arimasu");
10048 }
10049 return res;
10050} /* Japanese */
10051
10052/* Default English syntax */
10053static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
10054{
10055 int res;
10056
10057 /* Introduce messages they have */
10058 res = ast_play_and_wait(chan, "vm-youhave");
10059 if (!res) {
10060 if (vms->urgentmessages) {
10061 res = say_and_wait(chan, vms->urgentmessages, ast_channel_language(chan));
10062 if (!res)
10063 res = ast_play_and_wait(chan, "vm-Urgent");
10064 if ((vms->oldmessages || vms->newmessages) && !res) {
10065 res = ast_play_and_wait(chan, "vm-and");
10066 } else if (!res) {
10067 if (vms->urgentmessages == 1)
10068 res = ast_play_and_wait(chan, "vm-message");
10069 else
10070 res = ast_play_and_wait(chan, "vm-messages");
10071 }
10072 }
10073 if (vms->newmessages) {
10074 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10075 if (!res)
10076 res = ast_play_and_wait(chan, "vm-INBOX");
10077 if (vms->oldmessages && !res)
10078 res = ast_play_and_wait(chan, "vm-and");
10079 else if (!res) {
10080 if (vms->newmessages == 1)
10081 res = ast_play_and_wait(chan, "vm-message");
10082 else
10083 res = ast_play_and_wait(chan, "vm-messages");
10084 }
10085
10086 }
10087 if (!res && vms->oldmessages) {
10088 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10089 if (!res)
10090 res = ast_play_and_wait(chan, "vm-Old");
10091 if (!res) {
10092 if (vms->oldmessages == 1)
10093 res = ast_play_and_wait(chan, "vm-message");
10094 else
10095 res = ast_play_and_wait(chan, "vm-messages");
10096 }
10097 }
10098 if (!res) {
10099 if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
10100 res = ast_play_and_wait(chan, "vm-no");
10101 if (!res)
10102 res = ast_play_and_wait(chan, "vm-messages");
10103 }
10104 }
10105 }
10106 return res;
10107}
10108
10109/* ICELANDIC syntax */
10110static int vm_intro_is(struct ast_channel *chan, struct vm_state *vms)
10111{
10112 int res;
10113
10114 /* Introduce messages they have */
10115 res = ast_play_and_wait(chan, "vm-youhave");
10116 if (!res) {
10117 if (vms->urgentmessages) {
10118 /* Digits 1-4 are spoken in neutral and plural when talking about messages,
10119 however, feminine is used for 1 as it is the same as the neutral for plural,
10120 and singular neutral is the same after 1. */
10121 if (vms->urgentmessages < 5) {
10122 char recname[16];
10123 if (vms->urgentmessages == 1)
10124 snprintf(recname, sizeof(recname), "digits/1kvk");
10125 else
10126 snprintf(recname, sizeof(recname), "digits/%dhk", vms->urgentmessages);
10127 res = ast_play_and_wait(chan, recname);
10128 } else if (!res)
10129 res = ast_play_and_wait(chan, "vm-Urgent");
10130 if ((vms->oldmessages || vms->newmessages) && !res) {
10131 res = ast_play_and_wait(chan, "vm-and");
10132 } else if (!res)
10133 res = ast_play_and_wait(chan, "vm-messages");
10134 }
10135 if (vms->newmessages) {
10136 if (vms->newmessages < 5) {
10137 char recname[16];
10138 if (vms->newmessages == 1)
10139 snprintf(recname, sizeof(recname), "digits/1kvk");
10140 else
10141 snprintf(recname, sizeof(recname), "digits/%dhk", vms->newmessages);
10142 res = ast_play_and_wait(chan, recname);
10143 } else
10144 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10145 if (!res)
10146 res = ast_play_and_wait(chan, "vm-INBOX");
10147 if (vms->oldmessages && !res)
10148 res = ast_play_and_wait(chan, "vm-and");
10149 else if (!res)
10150 res = ast_play_and_wait(chan, "vm-messages");
10151 }
10152 if (!res && vms->oldmessages) {
10153 if (vms->oldmessages < 5) {
10154 char recname[16];
10155 if (vms->oldmessages == 1)
10156 snprintf(recname, sizeof(recname), "digits/1kvk");
10157 else
10158 snprintf(recname, sizeof(recname), "digits/%dhk", vms->oldmessages);
10159 res = ast_play_and_wait(chan, recname);
10160 } else
10161 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10162 if (!res)
10163 res = ast_play_and_wait(chan, "vm-Old");
10164 if (!res)
10165 res = ast_play_and_wait(chan, "vm-messages");
10166 }
10167 if (!res) {
10168 if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
10169 res = ast_play_and_wait(chan, "vm-no");
10170 if (!res)
10171 res = ast_play_and_wait(chan, "vm-messages");
10172 }
10173 }
10174 }
10175 return res;
10176}
10177
10178/* ITALIAN syntax */
10179static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
10180{
10181 /* Introduce messages they have */
10182 int res;
10183 if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
10184 res = ast_play_and_wait(chan, "vm-no") ||
10185 ast_play_and_wait(chan, "vm-message");
10186 else
10187 res = ast_play_and_wait(chan, "vm-youhave");
10188 if (!res && vms->newmessages) {
10189 res = (vms->newmessages == 1) ?
10190 ast_play_and_wait(chan, "digits/un") ||
10191 ast_play_and_wait(chan, "vm-nuovo") ||
10192 ast_play_and_wait(chan, "vm-message") :
10193 /* 2 or more new messages */
10194 say_and_wait(chan, vms->newmessages, ast_channel_language(chan)) ||
10195 ast_play_and_wait(chan, "vm-nuovi") ||
10196 ast_play_and_wait(chan, "vm-messages");
10197 if (!res && vms->oldmessages)
10198 res = ast_play_and_wait(chan, "vm-and");
10199 }
10200 if (!res && vms->oldmessages) {
10201 res = (vms->oldmessages == 1) ?
10202 ast_play_and_wait(chan, "digits/un") ||
10203 ast_play_and_wait(chan, "vm-vecchio") ||
10204 ast_play_and_wait(chan, "vm-message") :
10205 /* 2 or more old messages */
10206 say_and_wait(chan, vms->oldmessages, ast_channel_language(chan)) ||
10207 ast_play_and_wait(chan, "vm-vecchi") ||
10208 ast_play_and_wait(chan, "vm-messages");
10209 }
10210 return res;
10211}
10212
10213/* POLISH syntax */
10214static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
10215{
10216 /* Introduce messages they have */
10217 int res;
10218 div_t num;
10219
10220 if (!vms->oldmessages && !vms->newmessages) {
10221 res = ast_play_and_wait(chan, "vm-no");
10222 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10223 return res;
10224 } else {
10225 res = ast_play_and_wait(chan, "vm-youhave");
10226 }
10227
10228 if (vms->newmessages) {
10229 num = div(vms->newmessages, 10);
10230 if (vms->newmessages == 1) {
10231 res = ast_play_and_wait(chan, "digits/1-a");
10232 res = res ? res : ast_play_and_wait(chan, "vm-new-a");
10233 res = res ? res : ast_play_and_wait(chan, "vm-message");
10234 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
10235 if (num.rem == 2) {
10236 if (!num.quot) {
10237 res = ast_play_and_wait(chan, "digits/2-ie");
10238 } else {
10239 res = say_and_wait(chan, vms->newmessages - 2 , ast_channel_language(chan));
10240 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
10241 }
10242 } else {
10243 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10244 }
10245 res = res ? res : ast_play_and_wait(chan, "vm-new-e");
10246 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10247 } else {
10248 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10249 res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
10250 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10251 }
10252 if (!res && vms->oldmessages)
10253 res = ast_play_and_wait(chan, "vm-and");
10254 }
10255 if (!res && vms->oldmessages) {
10256 num = div(vms->oldmessages, 10);
10257 if (vms->oldmessages == 1) {
10258 res = ast_play_and_wait(chan, "digits/1-a");
10259 res = res ? res : ast_play_and_wait(chan, "vm-old-a");
10260 res = res ? res : ast_play_and_wait(chan, "vm-message");
10261 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
10262 if (num.rem == 2) {
10263 if (!num.quot) {
10264 res = ast_play_and_wait(chan, "digits/2-ie");
10265 } else {
10266 res = say_and_wait(chan, vms->oldmessages - 2 , ast_channel_language(chan));
10267 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
10268 }
10269 } else {
10270 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10271 }
10272 res = res ? res : ast_play_and_wait(chan, "vm-old-e");
10273 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10274 } else {
10275 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10276 res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
10277 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10278 }
10279 }
10280
10281 return res;
10282}
10283
10284/* SWEDISH syntax */
10285static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
10286{
10287 /* Introduce messages they have */
10288 int res;
10289
10290 res = ast_play_and_wait(chan, "vm-youhave");
10291 if (res)
10292 return res;
10293
10294 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10295 res = ast_play_and_wait(chan, "vm-no");
10296 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10297 return res;
10298 }
10299
10300 if (vms->newmessages) {
10301 if (vms->newmessages == 1) {
10302 res = ast_play_and_wait(chan, "digits/ett");
10303 res = res ? res : ast_play_and_wait(chan, "vm-nytt");
10304 res = res ? res : ast_play_and_wait(chan, "vm-message");
10305 } else {
10306 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10307 res = res ? res : ast_play_and_wait(chan, "vm-nya");
10308 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10309 }
10310 if (!res && vms->oldmessages)
10311 res = ast_play_and_wait(chan, "vm-and");
10312 }
10313 if (!res && vms->oldmessages) {
10314 if (vms->oldmessages == 1) {
10315 res = ast_play_and_wait(chan, "digits/ett");
10316 res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
10317 res = res ? res : ast_play_and_wait(chan, "vm-message");
10318 } else {
10319 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10320 res = res ? res : ast_play_and_wait(chan, "vm-gamla");
10321 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10322 }
10323 }
10324
10325 return res;
10326}
10327
10328/* NORWEGIAN syntax */
10329static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
10330{
10331 /* Introduce messages they have */
10332 int res;
10333
10334 res = ast_play_and_wait(chan, "vm-youhave");
10335 if (res)
10336 return res;
10337
10338 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10339 res = ast_play_and_wait(chan, "vm-no");
10340 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10341 return res;
10342 }
10343
10344 if (vms->newmessages) {
10345 if (vms->newmessages == 1) {
10346 res = ast_play_and_wait(chan, "digits/1");
10347 res = res ? res : ast_play_and_wait(chan, "vm-ny");
10348 res = res ? res : ast_play_and_wait(chan, "vm-message");
10349 } else {
10350 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10351 res = res ? res : ast_play_and_wait(chan, "vm-nye");
10352 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10353 }
10354 if (!res && vms->oldmessages)
10355 res = ast_play_and_wait(chan, "vm-and");
10356 }
10357 if (!res && vms->oldmessages) {
10358 if (vms->oldmessages == 1) {
10359 res = ast_play_and_wait(chan, "digits/1");
10360 res = res ? res : ast_play_and_wait(chan, "vm-gamel");
10361 res = res ? res : ast_play_and_wait(chan, "vm-message");
10362 } else {
10363 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10364 res = res ? res : ast_play_and_wait(chan, "vm-gamle");
10365 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10366 }
10367 }
10368
10369 return res;
10370}
10371
10372/* Danish syntax */
10373static int vm_intro_da(struct ast_channel *chan, struct vm_state *vms)
10374{
10375 /* Introduce messages they have */
10376 int res;
10377
10378 res = ast_play_and_wait(chan, "vm-youhave");
10379 if (res)
10380 return res;
10381
10382 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10383 res = ast_play_and_wait(chan, "vm-no");
10384 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10385 return res;
10386 }
10387
10388 if (vms->newmessages) {
10389 if ((vms->newmessages == 1)) {
10390 res = ast_play_and_wait(chan, "digits/1");
10391 res = res ? res : ast_play_and_wait(chan, "vm-INBOX");
10392 res = res ? res : ast_play_and_wait(chan, "vm-message");
10393 } else {
10394 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10395 res = res ? res : ast_play_and_wait(chan, "vm-INBOXs");
10396 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10397 }
10398 if (!res && vms->oldmessages)
10399 res = ast_play_and_wait(chan, "vm-and");
10400 }
10401 if (!res && vms->oldmessages) {
10402 if (vms->oldmessages == 1) {
10403 res = ast_play_and_wait(chan, "digits/1");
10404 res = res ? res : ast_play_and_wait(chan, "vm-Old");
10405 res = res ? res : ast_play_and_wait(chan, "vm-message");
10406 } else {
10407 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10408 res = res ? res : ast_play_and_wait(chan, "vm-Olds");
10409 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10410 }
10411 }
10412
10413 return res;
10414}
10415
10416
10417/* GERMAN syntax */
10418static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
10419{
10420 /* Introduce messages they have */
10421 int res;
10422 res = ast_play_and_wait(chan, "vm-youhave");
10423 if (!res) {
10424 if (vms->newmessages) {
10425 if (vms->newmessages == 1)
10426 res = ast_play_and_wait(chan, "digits/1F");
10427 else
10428 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10429 if (!res)
10430 res = ast_play_and_wait(chan, "vm-INBOX");
10431 if (vms->oldmessages && !res)
10432 res = ast_play_and_wait(chan, "vm-and");
10433 else if (!res) {
10434 if (vms->newmessages == 1)
10435 res = ast_play_and_wait(chan, "vm-message");
10436 else
10437 res = ast_play_and_wait(chan, "vm-messages");
10438 }
10439
10440 }
10441 if (!res && vms->oldmessages) {
10442 if (vms->oldmessages == 1)
10443 res = ast_play_and_wait(chan, "digits/1F");
10444 else
10445 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10446 if (!res)
10447 res = ast_play_and_wait(chan, "vm-Old");
10448 if (!res) {
10449 if (vms->oldmessages == 1)
10450 res = ast_play_and_wait(chan, "vm-message");
10451 else
10452 res = ast_play_and_wait(chan, "vm-messages");
10453 }
10454 }
10455 if (!res) {
10456 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10457 res = ast_play_and_wait(chan, "vm-no");
10458 if (!res)
10459 res = ast_play_and_wait(chan, "vm-messages");
10460 }
10461 }
10462 }
10463 return res;
10464}
10465
10466/* SPANISH syntax */
10467static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
10468{
10469 /* Introduce messages they have */
10470 int res;
10471 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10472 res = ast_play_and_wait(chan, "vm-youhaveno");
10473 if (!res)
10474 res = ast_play_and_wait(chan, "vm-messages");
10475 } else {
10476 res = ast_play_and_wait(chan, "vm-youhave");
10477 }
10478 if (!res) {
10479 if (vms->newmessages) {
10480 if (!res) {
10481 if (vms->newmessages == 1) {
10482 res = ast_play_and_wait(chan, "digits/1M");
10483 if (!res)
10484 res = ast_play_and_wait(chan, "vm-message");
10485 if (!res)
10486 res = ast_play_and_wait(chan, "vm-INBOXs");
10487 } else {
10488 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10489 if (!res)
10490 res = ast_play_and_wait(chan, "vm-messages");
10491 if (!res)
10492 res = ast_play_and_wait(chan, "vm-INBOX");
10493 }
10494 }
10495 if (vms->oldmessages && !res)
10496 res = ast_play_and_wait(chan, "vm-and");
10497 }
10498 if (vms->oldmessages) {
10499 if (!res) {
10500 if (vms->oldmessages == 1) {
10501 res = ast_play_and_wait(chan, "digits/1M");
10502 if (!res)
10503 res = ast_play_and_wait(chan, "vm-message");
10504 if (!res)
10505 res = ast_play_and_wait(chan, "vm-Olds");
10506 } else {
10507 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10508 if (!res)
10509 res = ast_play_and_wait(chan, "vm-messages");
10510 if (!res)
10511 res = ast_play_and_wait(chan, "vm-Old");
10512 }
10513 }
10514 }
10515 }
10516return res;
10517}
10518
10519/* BRAZILIAN PORTUGUESE syntax */
10520static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
10521 /* Introduce messages they have */
10522 int res;
10523 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10524 res = ast_play_and_wait(chan, "vm-nomessages");
10525 return res;
10526 } else {
10527 res = ast_play_and_wait(chan, "vm-youhave");
10528 }
10529 if (vms->newmessages) {
10530 if (!res)
10531 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10532 if (vms->newmessages == 1) {
10533 if (!res)
10534 res = ast_play_and_wait(chan, "vm-message");
10535 if (!res)
10536 res = ast_play_and_wait(chan, "vm-INBOXs");
10537 } else {
10538 if (!res)
10539 res = ast_play_and_wait(chan, "vm-messages");
10540 if (!res)
10541 res = ast_play_and_wait(chan, "vm-INBOX");
10542 }
10543 if (vms->oldmessages && !res)
10544 res = ast_play_and_wait(chan, "vm-and");
10545 }
10546 if (vms->oldmessages) {
10547 if (!res)
10548 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10549 if (vms->oldmessages == 1) {
10550 if (!res)
10551 res = ast_play_and_wait(chan, "vm-message");
10552 if (!res)
10553 res = ast_play_and_wait(chan, "vm-Olds");
10554 } else {
10555 if (!res)
10556 res = ast_play_and_wait(chan, "vm-messages");
10557 if (!res)
10558 res = ast_play_and_wait(chan, "vm-Old");
10559 }
10560 }
10561 return res;
10562}
10563
10564/* FRENCH syntax */
10565static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
10566{
10567 /* Introduce messages they have */
10568 int res;
10569 res = ast_play_and_wait(chan, "vm-youhave");
10570 if (!res) {
10571 if (vms->newmessages) {
10572 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10573 if (!res)
10574 res = ast_play_and_wait(chan, "vm-INBOX");
10575 if (vms->oldmessages && !res)
10576 res = ast_play_and_wait(chan, "vm-and");
10577 else if (!res) {
10578 if (vms->newmessages == 1)
10579 res = ast_play_and_wait(chan, "vm-message");
10580 else
10581 res = ast_play_and_wait(chan, "vm-messages");
10582 }
10583
10584 }
10585 if (!res && vms->oldmessages) {
10586 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10587 if (!res)
10588 res = ast_play_and_wait(chan, "vm-Old");
10589 if (!res) {
10590 if (vms->oldmessages == 1)
10591 res = ast_play_and_wait(chan, "vm-message");
10592 else
10593 res = ast_play_and_wait(chan, "vm-messages");
10594 }
10595 }
10596 if (!res) {
10597 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10598 res = ast_play_and_wait(chan, "vm-no");
10599 if (!res)
10600 res = ast_play_and_wait(chan, "vm-messages");
10601 }
10602 }
10603 }
10604 return res;
10605}
10606
10607/* DUTCH syntax */
10608static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
10609{
10610 /* Introduce messages they have */
10611 int res;
10612 res = ast_play_and_wait(chan, "vm-youhave");
10613 if (!res) {
10614 if (vms->newmessages) {
10615 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10616 if (!res) {
10617 if (vms->newmessages == 1)
10618 res = ast_play_and_wait(chan, "vm-INBOXs");
10619 else
10620 res = ast_play_and_wait(chan, "vm-INBOX");
10621 }
10622 if (vms->oldmessages && !res)
10623 res = ast_play_and_wait(chan, "vm-and");
10624 else if (!res) {
10625 if (vms->newmessages == 1)
10626 res = ast_play_and_wait(chan, "vm-message");
10627 else
10628 res = ast_play_and_wait(chan, "vm-messages");
10629 }
10630
10631 }
10632 if (!res && vms->oldmessages) {
10633 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10634 if (!res) {
10635 if (vms->oldmessages == 1)
10636 res = ast_play_and_wait(chan, "vm-Olds");
10637 else
10638 res = ast_play_and_wait(chan, "vm-Old");
10639 }
10640 if (!res) {
10641 if (vms->oldmessages == 1)
10642 res = ast_play_and_wait(chan, "vm-message");
10643 else
10644 res = ast_play_and_wait(chan, "vm-messages");
10645 }
10646 }
10647 if (!res) {
10648 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10649 res = ast_play_and_wait(chan, "vm-no");
10650 if (!res)
10651 res = ast_play_and_wait(chan, "vm-messages");
10652 }
10653 }
10654 }
10655 return res;
10656}
10657
10658/* PORTUGUESE syntax */
10659static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
10660{
10661 /* Introduce messages they have */
10662 int res;
10663 res = ast_play_and_wait(chan, "vm-youhave");
10664 if (!res) {
10665 if (vms->newmessages) {
10666 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10667 if (!res) {
10668 if (vms->newmessages == 1) {
10669 res = ast_play_and_wait(chan, "vm-message");
10670 if (!res)
10671 res = ast_play_and_wait(chan, "vm-INBOXs");
10672 } else {
10673 res = ast_play_and_wait(chan, "vm-messages");
10674 if (!res)
10675 res = ast_play_and_wait(chan, "vm-INBOX");
10676 }
10677 }
10678 if (vms->oldmessages && !res)
10679 res = ast_play_and_wait(chan, "vm-and");
10680 }
10681 if (!res && vms->oldmessages) {
10682 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10683 if (!res) {
10684 if (vms->oldmessages == 1) {
10685 res = ast_play_and_wait(chan, "vm-message");
10686 if (!res)
10687 res = ast_play_and_wait(chan, "vm-Olds");
10688 } else {
10689 res = ast_play_and_wait(chan, "vm-messages");
10690 if (!res)
10691 res = ast_play_and_wait(chan, "vm-Old");
10692 }
10693 }
10694 }
10695 if (!res) {
10696 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10697 res = ast_play_and_wait(chan, "vm-no");
10698 if (!res)
10699 res = ast_play_and_wait(chan, "vm-messages");
10700 }
10701 }
10702 }
10703 return res;
10704}
10705
10706
10707/* CZECH syntax */
10708/* in czech there must be declension of word new and message
10709 * czech : english : czech : english
10710 * --------------------------------------------------------
10711 * vm-youhave : you have
10712 * vm-novou : one new : vm-zpravu : message
10713 * vm-nove : 2-4 new : vm-zpravy : messages
10714 * vm-novych : 5-infinite new : vm-zprav : messages
10715 * vm-starou : one old
10716 * vm-stare : 2-4 old
10717 * vm-starych : 5-infinite old
10718 * jednu : one - falling 4.
10719 * vm-no : no ( no messages )
10720 */
10721
10722static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
10723{
10724 int res;
10725 res = ast_play_and_wait(chan, "vm-youhave");
10726 if (!res) {
10727 if (vms->newmessages) {
10728 if (vms->newmessages == 1) {
10729 res = ast_play_and_wait(chan, "digits/jednu");
10730 } else {
10731 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10732 }
10733 if (!res) {
10734 if (vms->newmessages == 1)
10735 res = ast_play_and_wait(chan, "vm-novou");
10736 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
10737 res = ast_play_and_wait(chan, "vm-nove");
10738 if (vms->newmessages > 4)
10739 res = ast_play_and_wait(chan, "vm-novych");
10740 }
10741 if (vms->oldmessages && !res)
10742 res = ast_play_and_wait(chan, "vm-and");
10743 else if (!res) {
10744 if (vms->newmessages == 1)
10745 res = ast_play_and_wait(chan, "vm-zpravu");
10746 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
10747 res = ast_play_and_wait(chan, "vm-zpravy");
10748 if (vms->newmessages > 4)
10749 res = ast_play_and_wait(chan, "vm-zprav");
10750 }
10751 }
10752 if (!res && vms->oldmessages) {
10753 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10754 if (!res) {
10755 if (vms->oldmessages == 1)
10756 res = ast_play_and_wait(chan, "vm-starou");
10757 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
10758 res = ast_play_and_wait(chan, "vm-stare");
10759 if (vms->oldmessages > 4)
10760 res = ast_play_and_wait(chan, "vm-starych");
10761 }
10762 if (!res) {
10763 if (vms->oldmessages == 1)
10764 res = ast_play_and_wait(chan, "vm-zpravu");
10765 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
10766 res = ast_play_and_wait(chan, "vm-zpravy");
10767 if (vms->oldmessages > 4)
10768 res = ast_play_and_wait(chan, "vm-zprav");
10769 }
10770 }
10771 if (!res) {
10772 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10773 res = ast_play_and_wait(chan, "vm-no");
10774 if (!res)
10775 res = ast_play_and_wait(chan, "vm-zpravy");
10776 }
10777 }
10778 }
10779 return res;
10780}
10781
10782/* CHINESE (Taiwan) syntax */
10783static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
10784{
10785 int res;
10786 /* Introduce messages they have */
10787 res = ast_play_and_wait(chan, "vm-you");
10788
10789 if (!res && vms->newmessages) {
10790 res = ast_play_and_wait(chan, "vm-have");
10791 if (!res)
10792 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10793 if (!res)
10794 res = ast_play_and_wait(chan, "vm-tong");
10795 if (!res)
10796 res = ast_play_and_wait(chan, "vm-INBOX");
10797 if (vms->oldmessages && !res)
10798 res = ast_play_and_wait(chan, "vm-and");
10799 else if (!res)
10800 res = ast_play_and_wait(chan, "vm-messages");
10801 }
10802 if (!res && vms->oldmessages) {
10803 res = ast_play_and_wait(chan, "vm-have");
10804 if (!res)
10805 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10806 if (!res)
10807 res = ast_play_and_wait(chan, "vm-tong");
10808 if (!res)
10809 res = ast_play_and_wait(chan, "vm-Old");
10810 if (!res)
10811 res = ast_play_and_wait(chan, "vm-messages");
10812 }
10813 if (!res && !vms->oldmessages && !vms->newmessages) {
10814 res = ast_play_and_wait(chan, "vm-haveno");
10815 if (!res)
10816 res = ast_play_and_wait(chan, "vm-messages");
10817 }
10818 return res;
10819}
10820
10821/* Vietnamese syntax */
10822static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
10823{
10824 int res;
10825
10826 /* Introduce messages they have */
10827 res = ast_play_and_wait(chan, "vm-youhave");
10828 if (!res) {
10829 if (vms->newmessages) {
10830 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10831 if (!res)
10832 res = ast_play_and_wait(chan, "vm-INBOX");
10833 if (vms->oldmessages && !res)
10834 res = ast_play_and_wait(chan, "vm-and");
10835 }
10836 if (!res && vms->oldmessages) {
10837 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10838 if (!res)
10839 res = ast_play_and_wait(chan, "vm-Old");
10840 }
10841 if (!res) {
10842 if (!vms->oldmessages && !vms->newmessages) {
10843 res = ast_play_and_wait(chan, "vm-no");
10844 if (!res)
10845 res = ast_play_and_wait(chan, "vm-message");
10846 }
10847 }
10848 }
10849 return res;
10850}
10851
10852static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
10853{
10854 char prefile[256];
10855
10856 /* Notify the user that the temp greeting is set and give them the option to remove it */
10857 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
10858 if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10859 RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
10860 if (ast_fileexists(prefile, NULL, NULL) > 0) {
10861 ast_play_and_wait(chan, "vm-tempgreetactive");
10862 }
10863 DISPOSE(prefile, -1);
10864 }
10865
10866 /* Play voicemail intro - syntax is different for different languages */
10867 if (0) {
10868 return 0;
10869 } else if (!strncasecmp(ast_channel_language(chan), "cs", 2)) { /* CZECH syntax */
10870 return vm_intro_cs(chan, vms);
10871 } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
10872 return vm_intro_de(chan, vms);
10873 } else if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH syntax */
10874 return vm_intro_es(chan, vms);
10875 } else if (!strncasecmp(ast_channel_language(chan), "fr", 2)) { /* FRENCH syntax */
10876 return vm_intro_fr(chan, vms);
10877 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
10878 return vm_intro_gr(chan, vms);
10879 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW syntax */
10880 return vm_intro_he(chan, vms);
10881 } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */
10882 return vm_intro_is(chan, vms);
10883 } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
10884 return vm_intro_it(chan, vms);
10885 } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE syntax */
10886 return vm_intro_ja(chan, vms);
10887 } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
10888 return vm_intro_nl(chan, vms);
10889 } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
10890 return vm_intro_no(chan, vms);
10891 } else if (!strncasecmp(ast_channel_language(chan), "da", 2)) { /* DANISH syntax */
10892 return vm_intro_da(chan, vms);
10893 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
10894 return vm_intro_pl(chan, vms);
10895 } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* BRAZILIAN PORTUGUESE syntax */
10896 return vm_intro_pt_BR(chan, vms);
10897 } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE syntax */
10898 return vm_intro_pt(chan, vms);
10899 } else if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* RUSSIAN syntax */
10900 return vm_intro_multilang(chan, vms, "n");
10901 } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
10902 return vm_intro_se(chan, vms);
10903 } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* UKRAINIAN syntax */
10904 return vm_intro_multilang(chan, vms, "n");
10905 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
10906 return vm_intro_vi(chan, vms);
10907 } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
10908 return vm_intro_zh(chan, vms);
10909 } else { /* Default to ENGLISH */
10910 return vm_intro_en(chan, vms);
10911 }
10912}
10913
10914static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
10915{
10916 int res = 0;
10917 /* Play instructions and wait for new command */
10918 while (!res) {
10919 if (vms->starting) {
10920 if (vms->lastmsg > -1) {
10921 if (skipadvanced)
10922 res = ast_play_and_wait(chan, "vm-onefor-full");
10923 else
10924 res = ast_play_and_wait(chan, "vm-onefor");
10925 if (!res)
10926 res = vm_play_folder_name(chan, vms->vmbox);
10927 }
10928 if (!res) {
10929 if (skipadvanced)
10930 res = ast_play_and_wait(chan, "vm-opts-full");
10931 else
10932 res = ast_play_and_wait(chan, "vm-opts");
10933 }
10934 } else {
10935 /* Added for additional help */
10936 if (skipadvanced) {
10937 res = ast_play_and_wait(chan, "vm-onefor-full");
10938 if (!res)
10939 res = vm_play_folder_name(chan, vms->vmbox);
10940 res = ast_play_and_wait(chan, "vm-opts-full");
10941 }
10942 /* Logic:
10943 * If the current message is not the first OR
10944 * if we're listening to the first new message and there are
10945 * also urgent messages, then prompt for navigation to the
10946 * previous message
10947 */
10948 if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
10949 res = ast_play_and_wait(chan, "vm-prev");
10950 }
10951 if (!res && !skipadvanced)
10952 res = ast_play_and_wait(chan, "vm-advopts");
10953 if (!res)
10954 res = ast_play_and_wait(chan, "vm-repeat");
10955 /* Logic:
10956 * If we're not listening to the last message OR
10957 * we're listening to the last urgent message and there are
10958 * also new non-urgent messages, then prompt for navigation
10959 * to the next message
10960 */
10961 if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
10962 (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
10963 res = ast_play_and_wait(chan, "vm-next");
10964 }
10965 if (!res) {
10966 int curmsg_deleted;
10967#ifdef IMAP_STORAGE
10968 ast_mutex_lock(&vms->lock);
10969#endif
10970 curmsg_deleted = vms->deleted[vms->curmsg];
10971#ifdef IMAP_STORAGE
10972 ast_mutex_unlock(&vms->lock);
10973#endif
10974 if (!nodelete) {
10975 if (!curmsg_deleted) {
10976 res = ast_play_and_wait(chan, "vm-delete");
10977 } else {
10978 res = ast_play_and_wait(chan, "vm-undelete");
10979 }
10980 }
10981 if (!res) {
10982 res = ast_play_and_wait(chan, "vm-toforward");
10983 }
10984 if (!res) {
10985 res = ast_play_and_wait(chan, "vm-savemessage");
10986 }
10987 }
10988 }
10989 if (!res) {
10990 res = ast_play_and_wait(chan, "vm-helpexit");
10991 }
10992 if (!res)
10993 res = ast_waitfordigit(chan, 6000);
10994 if (!res) {
10995 vms->repeats++;
10996 if (vms->repeats > 2) {
10997 res = 't';
10998 }
10999 }
11000 }
11001 return res;
11002}
11003
11004static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
11005{
11006 int res = 0;
11007 /* Play instructions and wait for new command */
11008 while (!res) {
11009 if (vms->starting) {
11010 if (vms->lastmsg > -1) {
11011 res = vm_play_folder_name(chan, vms->vmbox);
11012 if (!res)
11013 res = ast_play_and_wait(chan, "jp-wa");
11014 if (!res)
11015 res = ast_play_and_wait(chan, "digits/1");
11016 if (!res)
11017 res = ast_play_and_wait(chan, "jp-wo");
11018 if (!res)
11019 res = ast_play_and_wait(chan, "silence/1");
11020 }
11021 if (!res)
11022 res = ast_play_and_wait(chan, "vm-opts");
11023 } else {
11024 /* Added for additional help */
11025 if (skipadvanced) {
11026 res = vm_play_folder_name(chan, vms->vmbox);
11027 if (!res)
11028 res = ast_play_and_wait(chan, "jp-wa");
11029 if (!res)
11030 res = ast_play_and_wait(chan, "digits/1");
11031 if (!res)
11032 res = ast_play_and_wait(chan, "jp-wo");
11033 if (!res)
11034 res = ast_play_and_wait(chan, "silence/1");
11035 res = ast_play_and_wait(chan, "vm-opts-full");
11036 }
11037 /* Logic:
11038 * If the current message is not the first OR
11039 * if we're listening to the first new message and there are
11040 * also urgent messages, then prompt for navigation to the
11041 * previous message
11042 */
11043 if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
11044 res = ast_play_and_wait(chan, "vm-prev");
11045 }
11046 if (!res && !skipadvanced)
11047 res = ast_play_and_wait(chan, "vm-advopts");
11048 if (!res)
11049 res = ast_play_and_wait(chan, "vm-repeat");
11050 /* Logic:
11051 * If we're not listening to the last message OR
11052 * we're listening to the last urgent message and there are
11053 * also new non-urgent messages, then prompt for navigation
11054 * to the next message
11055 */
11056 if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
11057 (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
11058 res = ast_play_and_wait(chan, "vm-next");
11059 }
11060 if (!res) {
11061 int curmsg_deleted;
11062#ifdef IMAP_STORAGE
11063 ast_mutex_lock(&vms->lock);
11064#endif
11065 curmsg_deleted = vms->deleted[vms->curmsg];
11066#ifdef IMAP_STORAGE
11067 ast_mutex_unlock(&vms->lock);
11068#endif
11069 if (!curmsg_deleted) {
11070 res = ast_play_and_wait(chan, "vm-delete");
11071 } else {
11072 res = ast_play_and_wait(chan, "vm-undelete");
11073 }
11074 if (!res) {
11075 res = ast_play_and_wait(chan, "vm-toforward");
11076 }
11077 if (!res) {
11078 res = ast_play_and_wait(chan, "vm-savemessage");
11079 }
11080 }
11081 }
11082
11083 if (!res) {
11084 res = ast_play_and_wait(chan, "vm-helpexit");
11085 }
11086 if (!res)
11087 res = ast_waitfordigit(chan, 6000);
11088 if (!res) {
11089 vms->repeats++;
11090 if (vms->repeats > 2) {
11091 res = 't';
11092 }
11093 }
11094
11095 }
11096
11097 return res;
11098}
11099
11100static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
11101{
11102 int res = 0;
11103 /* Play instructions and wait for new command */
11104 while (!res) {
11105 if (vms->lastmsg > -1) {
11106 res = ast_play_and_wait(chan, "vm-listen");
11107 if (!res)
11108 res = vm_play_folder_name(chan, vms->vmbox);
11109 if (!res)
11110 res = ast_play_and_wait(chan, "press");
11111 if (!res)
11112 res = ast_play_and_wait(chan, "digits/1");
11113 }
11114 if (!res)
11115 res = ast_play_and_wait(chan, "vm-opts");
11116 if (!res) {
11117 vms->starting = 0;
11118 return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11119 }
11120 }
11121 return res;
11122}
11123
11124static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
11125{
11126 if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
11127 return vm_instructions_ja(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11128 } else if (vms->starting && !strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
11129 return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11130 } else { /* Default to ENGLISH */
11131 return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11132 }
11133}
11134
11135static int vm_newuser_setup(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11136{
11137 int cmd = 0;
11138 int duration = 0;
11139 int tries = 0;
11140 char newpassword[80] = "";
11141 char newpassword2[80] = "";
11142 char prefile[PATH_MAX] = "";
11143 unsigned char buf[256];
11144 int bytes = 0;
11145
11146 ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
11147 if (ast_adsi_available(chan)) {
11148 bytes += adsi_logo(buf + bytes);
11149 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
11150 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11151 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11152 bytes += ast_adsi_voice_mode(buf + bytes, 0);
11154 }
11155
11156 /* If forcename is set, have the user record their name */
11157 if (ast_test_flag(vmu, VM_FORCENAME)) {
11158 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
11159 if (ast_fileexists(prefile, NULL, NULL) < 1) {
11160 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11161 if (cmd < 0 || cmd == 't' || cmd == '#')
11162 return cmd;
11163 }
11164 }
11165
11166 /* If forcegreetings is set, have the user record their greetings */
11167 if (ast_test_flag(vmu, VM_FORCEGREET)) {
11168 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
11169 if (ast_fileexists(prefile, NULL, NULL) < 1) {
11170 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11171 if (cmd < 0 || cmd == 't' || cmd == '#')
11172 return cmd;
11173 }
11174
11175 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
11176 if (ast_fileexists(prefile, NULL, NULL) < 1) {
11177 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11178 if (cmd < 0 || cmd == 't' || cmd == '#')
11179 return cmd;
11180 }
11181 }
11182
11183 /*
11184 * Change the password last since new users will be able to skip over any steps this one comes before
11185 * by hanging up and calling back to voicemail main since the password is used to verify new user status.
11186 */
11187 for (;;) {
11188 newpassword[1] = '\0';
11189 newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
11190 if (cmd == '#')
11191 newpassword[0] = '\0';
11192 if (cmd < 0 || cmd == 't' || cmd == '#')
11193 return cmd;
11194 cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
11195 if (cmd < 0 || cmd == 't' || cmd == '#')
11196 return cmd;
11197 cmd = check_password(vmu, newpassword); /* perform password validation */
11198 if (cmd != 0) {
11199 ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
11201 } else {
11202 newpassword2[1] = '\0';
11203 newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
11204 if (cmd == '#')
11205 newpassword2[0] = '\0';
11206 if (cmd < 0 || cmd == 't' || cmd == '#')
11207 return cmd;
11208 cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
11209 if (cmd < 0 || cmd == 't' || cmd == '#')
11210 return cmd;
11211 if (!strcmp(newpassword, newpassword2))
11212 break;
11213 ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
11214 cmd = ast_play_and_wait(chan, vm_mismatch);
11215 }
11216 if (++tries == 3)
11217 return -1;
11218 if (cmd != 0) {
11220 }
11221 }
11223 vm_change_password(vmu, newpassword);
11225 vm_change_password_shell(vmu, newpassword);
11226
11227 ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
11228 cmd = ast_play_and_wait(chan, vm_passchanged);
11229
11230 return cmd;
11231}
11232
11233static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11234{
11235 int cmd = 0;
11236 int retries = 0;
11237 int duration = 0;
11238 char newpassword[80] = "";
11239 char newpassword2[80] = "";
11240 char prefile[PATH_MAX] = "";
11241 unsigned char buf[256];
11242 int bytes = 0;
11243 SCOPE_ENTER(3, "%s: %s entering mailbox options", ast_channel_name(chan), vms->username);
11244
11245 ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
11246 if (ast_adsi_available(chan)) {
11247 bytes += adsi_logo(buf + bytes);
11248 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
11249 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11250 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11251 bytes += ast_adsi_voice_mode(buf + bytes, 0);
11253 }
11254 while ((cmd >= 0) && (cmd != 't')) {
11255 if (cmd)
11256 retries = 0;
11257 switch (cmd) {
11258 case '1': /* Record your unavailable message */
11259 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
11260 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);
11261 break;
11262 case '2': /* Record your busy message */
11263 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
11264 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);
11265 break;
11266 case '3': /* Record greeting */
11267 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
11268 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);
11269 break;
11270 case '4': /* manage the temporary greeting */
11271 cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
11272 break;
11273 case '5': /* change password */
11274 if (vmu->password[0] == '-') {
11275 cmd = ast_play_and_wait(chan, "vm-no");
11276 break;
11277 }
11278 newpassword[1] = '\0';
11279 newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
11280 if (cmd == '#')
11281 newpassword[0] = '\0';
11282 else {
11283 if (cmd < 0)
11284 break;
11285 if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
11286 break;
11287 }
11288 }
11289 cmd = check_password(vmu, newpassword); /* perform password validation */
11290 if (cmd != 0) {
11291 ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
11293 if (!cmd) {
11295 }
11296 break;
11297 }
11298 newpassword2[1] = '\0';
11299 newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
11300 if (cmd == '#')
11301 newpassword2[0] = '\0';
11302 else {
11303 if (cmd < 0)
11304 break;
11305
11306 if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
11307 break;
11308 }
11309 }
11310 if (strcmp(newpassword, newpassword2)) {
11311 ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
11312 cmd = ast_play_and_wait(chan, vm_mismatch);
11313 if (!cmd) {
11315 }
11316 break;
11317 }
11318
11320 vm_change_password(vmu, newpassword);
11321 }
11323 vm_change_password_shell(vmu, newpassword);
11324 }
11325
11326 ast_debug(1, "User %s set password to %s of length %d\n",
11327 vms->username, newpassword, (int) strlen(newpassword));
11328 cmd = ast_play_and_wait(chan, vm_passchanged);
11329 break;
11330 case '*':
11331 cmd = 't';
11332 break;
11333 default:
11334 cmd = 0;
11335 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
11336 SCOPE_CALL(-1, RETRIEVE, prefile, -1, vmu->mailbox, vmu->context);
11337 if (ast_fileexists(prefile, NULL, NULL)) {
11338 cmd = ast_play_and_wait(chan, "vm-tmpexists");
11339 }
11340 SCOPE_CALL(-1, DISPOSE, prefile, -1);
11341 if (!cmd) {
11342 cmd = ast_play_and_wait(chan, "vm-options");
11343 }
11344 if (!cmd) {
11345 cmd = ast_waitfordigit(chan, 6000);
11346 }
11347 if (!cmd) {
11348 retries++;
11349 }
11350 if (retries > 3) {
11351 cmd = 't';
11352 }
11353 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
11354 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
11355 }
11356 }
11357 if (cmd == 't')
11358 cmd = 0;
11359 SCOPE_EXIT_RTN_VALUE(cmd, "Done\n");
11360}
11361
11362/*!
11363 * \brief The handler for 'record a temporary greeting'.
11364 * \param chan
11365 * \param vmu
11366 * \param vms
11367 * \param fmtc
11368 * \param record_gain
11369 *
11370 * This is option 4 from the mailbox options menu.
11371 * This function manages the following promptings:
11372 * 1: play / record / review the temporary greeting. : invokes play_record_review().
11373 * 2: remove (delete) the temporary greeting.
11374 * *: return to the main menu.
11375 *
11376 * \return zero on success, -1 on error.
11377 */
11378static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11379{
11380 int cmd = 0;
11381 int retries = 0;
11382 int duration = 0;
11383 char prefile[PATH_MAX] = "";
11384 unsigned char buf[256];
11385 int bytes = 0;
11386
11387 if (ast_adsi_available(chan)) {
11388 bytes += adsi_logo(buf + bytes);
11389 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
11390 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11391 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11392 bytes += ast_adsi_voice_mode(buf + bytes, 0);
11394 }
11395
11396 ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
11397 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
11398 while ((cmd >= 0) && (cmd != 't')) {
11399 if (cmd)
11400 retries = 0;
11401 RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
11402 if (ast_fileexists(prefile, NULL, NULL) <= 0) {
11403 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11404 if (cmd == -1) {
11405 break;
11406 }
11407 cmd = 't';
11408 } else {
11409 switch (cmd) {
11410 case '1':
11411 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11412 break;
11413 case '2':
11414 DELETE(prefile, -1, prefile, vmu);
11415 ast_play_and_wait(chan, "vm-tempremoved");
11416 cmd = 't';
11417 break;
11418 case '*':
11419 cmd = 't';
11420 break;
11421 default:
11422 cmd = ast_play_and_wait(chan,
11423 ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
11424 "vm-tempgreeting2" : "vm-tempgreeting");
11425 if (!cmd) {
11426 cmd = ast_waitfordigit(chan, 6000);
11427 }
11428 if (!cmd) {
11429 retries++;
11430 }
11431 if (retries > 3) {
11432 cmd = 't';
11433 }
11434 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
11435 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
11436 }
11437 }
11438 DISPOSE(prefile, -1);
11439 }
11440 if (cmd == 't')
11441 cmd = 0;
11442 return cmd;
11443}
11444
11445/*!
11446 * \brief Greek syntax for 'You have N messages' greeting.
11447 * \param chan
11448 * \param vms
11449 * \param vmu
11450 *
11451 * \return zero on success, -1 on error.
11452 */
11453static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11454{
11455 int cmd = 0;
11456
11457 if (vms->lastmsg > -1) {
11458 cmd = play_message(chan, vmu, vms);
11459 } else {
11460 cmd = ast_play_and_wait(chan, "vm-youhaveno");
11461 if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
11462 if (!cmd) {
11463 snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
11464 cmd = ast_play_and_wait(chan, vms->fn);
11465 }
11466 if (!cmd)
11467 cmd = ast_play_and_wait(chan, "vm-messages");
11468 } else {
11469 if (!cmd)
11470 cmd = ast_play_and_wait(chan, "vm-messages");
11471 if (!cmd) {
11472 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11473 cmd = ast_play_and_wait(chan, vms->fn);
11474 }
11475 }
11476 }
11477 return cmd;
11478}
11479
11480/* Hebrew Syntax */
11481static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11482{
11483 int cmd = 0;
11484
11485 if (vms->lastmsg > -1) {
11486 cmd = play_message(chan, vmu, vms);
11487 } else {
11488 if (!strcasecmp(vms->fn, "INBOX")) {
11489 cmd = ast_play_and_wait(chan, "vm-nonewmessages");
11490 } else {
11491 cmd = ast_play_and_wait(chan, "vm-nomessages");
11492 }
11493 }
11494 return cmd;
11495}
11496
11497/*!
11498 * \brief Default English syntax for 'You have N messages' greeting.
11499 * \param chan
11500 * \param vms
11501 * \param vmu
11502 *
11503 * \return zero on success, -1 on error.
11504 */
11505static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11506{
11507 int cmd = 0;
11508
11509 if (vms->lastmsg > -1) {
11510 cmd = play_message(chan, vmu, vms);
11511 } else {
11512 cmd = ast_play_and_wait(chan, "vm-youhave");
11513 if (!cmd)
11514 cmd = ast_play_and_wait(chan, "vm-no");
11515 if (!cmd) {
11516 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11517 cmd = ast_play_and_wait(chan, vms->fn);
11518 }
11519 if (!cmd)
11520 cmd = ast_play_and_wait(chan, "vm-messages");
11521 }
11522 return cmd;
11523}
11524
11525/*!
11526 *\brief Italian syntax for 'You have N messages' greeting.
11527 * \param chan
11528 * \param vms
11529 * \param vmu
11530 *
11531 * \return zero on success, -1 on error.
11532 */
11533static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11534{
11535 int cmd;
11536
11537 if (vms->lastmsg > -1) {
11538 cmd = play_message(chan, vmu, vms);
11539 } else {
11540 cmd = ast_play_and_wait(chan, "vm-no");
11541 if (!cmd)
11542 cmd = ast_play_and_wait(chan, "vm-message");
11543 if (!cmd) {
11544 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11545 cmd = ast_play_and_wait(chan, vms->fn);
11546 }
11547 }
11548 return cmd;
11549}
11550
11551/*!
11552 * \brief Japanese syntax for 'You have N messages' greeting.
11553 * \param chan
11554 * \param vms
11555 * \param vmu
11556 *
11557 * \return zero on success, -1 on error.
11558 */
11559static int vm_browse_messages_ja(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11560{
11561 int cmd = 0;
11562
11563 if (vms->lastmsg > -1) {
11564 cmd = play_message(chan, vmu, vms);
11565 } else {
11566 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11567 cmd = ast_play_and_wait(chan, vms->fn);
11568 if (!cmd)
11569 cmd = ast_play_and_wait(chan, "vm-messages");
11570 if (!cmd)
11571 cmd = ast_play_and_wait(chan, "jp-wa");
11572 if (!cmd)
11573 cmd = ast_play_and_wait(chan, "jp-arimasen");
11574 }
11575 return cmd;
11576}
11577
11578/*!
11579 * \brief Spanish syntax for 'You have N messages' greeting.
11580 * \param chan
11581 * \param vms
11582 * \param vmu
11583 *
11584 * \return zero on success, -1 on error.
11585 */
11586static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11587{
11588 int cmd;
11589
11590 if (vms->lastmsg > -1) {
11591 cmd = play_message(chan, vmu, vms);
11592 } else {
11593 cmd = ast_play_and_wait(chan, "vm-youhaveno");
11594 if (!cmd)
11595 cmd = ast_play_and_wait(chan, "vm-messages");
11596 if (!cmd) {
11597 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11598 cmd = ast_play_and_wait(chan, vms->fn);
11599 }
11600 }
11601 return cmd;
11602}
11603
11604/*!
11605 * \brief Portuguese syntax for 'You have N messages' greeting.
11606 * \param chan
11607 * \param vms
11608 * \param vmu
11609 *
11610 * \return zero on success, -1 on error.
11611 */
11612static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11613{
11614 int cmd;
11615
11616 if (vms->lastmsg > -1) {
11617 cmd = play_message(chan, vmu, vms);
11618 } else {
11619 cmd = ast_play_and_wait(chan, "vm-no");
11620 if (!cmd) {
11621 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11622 cmd = ast_play_and_wait(chan, vms->fn);
11623 }
11624 if (!cmd)
11625 cmd = ast_play_and_wait(chan, "vm-messages");
11626 }
11627 return cmd;
11628}
11629
11630/*!
11631 * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
11632 * \param chan
11633 * \param vms
11634 * \param vmu
11635 *
11636 * \return zero on success, -1 on error.
11637 */
11638static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11639{
11640 int cmd;
11641
11642 if (vms->lastmsg > -1) {
11643 cmd = play_message(chan, vmu, vms);
11644 } else {
11645 cmd = ast_play_and_wait(chan, "vm-you");
11646 if (!cmd)
11647 cmd = ast_play_and_wait(chan, "vm-haveno");
11648 if (!cmd)
11649 cmd = ast_play_and_wait(chan, "vm-messages");
11650 if (!cmd) {
11651 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11652 cmd = ast_play_and_wait(chan, vms->fn);
11653 }
11654 }
11655 return cmd;
11656}
11657
11658/*!
11659 * \brief Vietnamese syntax for 'You have N messages' greeting.
11660 * \param chan
11661 * \param vms
11662 * \param vmu
11663 *
11664 * \return zero on success, -1 on error.
11665 */
11666static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11667{
11668 int cmd = 0;
11669
11670 if (vms->lastmsg > -1) {
11671 cmd = play_message(chan, vmu, vms);
11672 } else {
11673 cmd = ast_play_and_wait(chan, "vm-no");
11674 if (!cmd) {
11675 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11676 cmd = ast_play_and_wait(chan, vms->fn);
11677 }
11678 }
11679 return cmd;
11680}
11681
11682/*!
11683 * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
11684 * \param chan The channel for the current user. We read the language property from this.
11685 * \param vms passed into the language-specific vm_browse_messages function.
11686 * \param vmu passed into the language-specific vm_browse_messages function.
11687 *
11688 * The method to be invoked is determined by the value of language code property in the user's channel.
11689 * The default (when unable to match) is to use english.
11690 *
11691 * \return zero on success, -1 on error.
11692 */
11693static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11694{
11695 if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH */
11696 return vm_browse_messages_es(chan, vms, vmu);
11697 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK */
11698 return vm_browse_messages_gr(chan, vms, vmu);
11699 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW */
11700 return vm_browse_messages_he(chan, vms, vmu);
11701 } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN */
11702 return vm_browse_messages_it(chan, vms, vmu);
11703 } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE */
11704 return vm_browse_messages_ja(chan, vms, vmu);
11705 } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE */
11706 return vm_browse_messages_pt(chan, vms, vmu);
11707 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE */
11708 return vm_browse_messages_vi(chan, vms, vmu);
11709 } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) */
11710 return vm_browse_messages_zh(chan, vms, vmu);
11711 } else { /* Default to English syntax */
11712 return vm_browse_messages_en(chan, vms, vmu);
11713 }
11714}
11715
11716static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
11717 struct ast_vm_user *res_vmu, const char *context, const char *prefix,
11718 int skipuser, int max_logins, int silent)
11719{
11720 int useadsi = 0, valid = 0, logretries = 0;
11721 char password[AST_MAX_EXTENSION], *passptr = NULL;
11722 struct ast_vm_user vmus, *vmu = NULL;
11723
11724 /* If ADSI is supported, setup login screen */
11725 adsi_begin(chan, &useadsi);
11726 if (!skipuser && useadsi)
11727 adsi_login(chan);
11728 if (!silent && !skipuser && ast_streamfile(chan, vm_login, ast_channel_language(chan))) {
11729 ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
11730 return -1;
11731 }
11732
11733 /* Authenticate them and get their mailbox/password */
11734
11735 while (!valid && (logretries < max_logins)) {
11736 /* Prompt for, and read in the username */
11737 if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
11738 ast_log(AST_LOG_WARNING, "Couldn't read username\n");
11739 return -1;
11740 }
11741 if (ast_strlen_zero(mailbox)) {
11742 if (ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str) {
11743 ast_copy_string(mailbox, ast_channel_caller(chan)->id.number.str, mailbox_size);
11744 } else {
11745 ast_verb(3, "Username not entered\n");
11746 return -1;
11747 }
11748 } else if (mailbox[0] == '*') {
11749 /* user entered '*' */
11750 ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
11751 if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
11752 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
11753 return -1;
11754 }
11755 ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
11756 mailbox[0] = '\0';
11757 }
11758
11759 if (useadsi)
11760 adsi_password(chan);
11761
11762 if (!ast_strlen_zero(prefix)) {
11763 char fullusername[80];
11764
11765 ast_copy_string(fullusername, prefix, sizeof(fullusername));
11766 strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
11767 ast_copy_string(mailbox, fullusername, mailbox_size);
11768 }
11769
11770 ast_debug(1, "Before find user for mailbox %s\n", mailbox);
11771 memset(&vmus, 0, sizeof(vmus));
11772 vmu = find_user(&vmus, context, mailbox);
11773 if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
11774 /* saved password is blank, so don't bother asking */
11775 password[0] = '\0';
11776 } else {
11778 if (!ast_check_hangup(chan)) {
11779 ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
11780 }
11781 free_user(vmu);
11782 return -1;
11783 }
11784 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
11785 ast_log(AST_LOG_NOTICE, "Unable to read password\n");
11786 free_user(vmu);
11787 return -1;
11788 } else if (password[0] == '*') {
11789 /* user entered '*' */
11790 ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
11791 if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
11792 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
11793 mailbox[0] = '*';
11794 free_user(vmu);
11795 return -1;
11796 }
11797 ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
11798 mailbox[0] = '\0';
11799 /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
11800 free_user(vmu);
11801 vmu = NULL;
11802 }
11803 }
11804
11805 if (vmu) {
11806 passptr = vmu->password;
11807 if (passptr[0] == '-') passptr++;
11808 }
11809 if (vmu && !strcmp(passptr, password))
11810 valid++;
11811 else {
11812 ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
11813 if (!ast_strlen_zero(prefix))
11814 mailbox[0] = '\0';
11815 }
11816 logretries++;
11817 if (!valid) {
11818 if (skipuser || logretries >= max_logins) {
11819 if (ast_streamfile(chan, "vm-incorrect", ast_channel_language(chan))) {
11820 ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
11821 free_user(vmu);
11822 return -1;
11823 }
11824 if (ast_waitstream(chan, "")) { /* Channel is hung up */
11825 free_user(vmu);
11826 return -1;
11827 }
11828 } else {
11829 if (useadsi)
11830 adsi_login(chan);
11831 if (ast_streamfile(chan, "vm-incorrect-mailbox", ast_channel_language(chan))) {
11832 ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
11833 free_user(vmu);
11834 return -1;
11835 }
11836 }
11837 }
11838 }
11839 if (!valid && (logretries >= max_logins)) {
11840 ast_stopstream(chan);
11841 ast_play_and_wait(chan, "vm-goodbye");
11842 free_user(vmu);
11843 return -1;
11844 }
11845 if (vmu && !skipuser) {
11846 *res_vmu = *vmu;
11847 }
11848 return 0;
11849}
11850
11852 struct ast_vm_user *vmu,
11853 struct vm_state *vms,
11854 const char *msg_id)
11855{
11856 if (message_range_and_existence_check(vms, &msg_id, 1, &vms->curmsg, vmu)) {
11857 return -1;
11858 }
11859 /* Found the msg, so play it back */
11860
11861 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
11862
11863#ifdef IMAP_STORAGE
11864 /*IMAP storage stores any prepended message from a forward
11865 * as a separate file from the rest of the message
11866 */
11867 if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
11868 wait_file(chan, vms, vms->introfn);
11869 }
11870#endif
11871 RETRIEVE(vms->curdir,vms->curmsg,vmu->mailbox, vmu->context);
11872
11873 if ((wait_file(chan, vms, vms->fn)) < 0) {
11874 ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
11875 } else {
11876#ifdef IMAP_STORAGE
11877 ast_mutex_lock(&vms->lock);
11878#endif
11879 vms->heard[vms->curmsg] = 1;
11880#ifdef IMAP_STORAGE
11881 ast_mutex_unlock(&vms->lock);
11882#endif
11883 }
11884 DISPOSE(vms->curdir, vms->curmsg);
11885 return 0;
11886}
11887
11888/*!
11889 * \brief Finds a message in a specific mailbox by msg_id and plays it to the channel
11890 *
11891 * \retval 0 Success
11892 * \retval -1 Failure
11893 */
11894static int play_message_by_id(struct ast_channel *chan, const char *mailbox, const char *context, const char *msg_id)
11895{
11896 struct vm_state vms;
11897 struct ast_vm_user *vmu = NULL, vmus;
11898 int res = 0;
11899 int open = 0;
11900 int played = 0;
11901 int i;
11902
11903 memset(&vmus, 0, sizeof(vmus));
11904 memset(&vms, 0, sizeof(vms));
11905
11906 if (!(vmu = find_user(&vmus, context, mailbox))) {
11907 goto play_msg_cleanup;
11908 }
11909
11910 /* Iterate through every folder, find the msg, and play it */
11911 for (i = 0; i < ARRAY_LEN(mailbox_folders) && !played; i++) {
11912 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
11913 vms.lastmsg = -1;
11914
11915 /* open the mailbox state */
11916 if ((res = open_mailbox(&vms, vmu, i)) < 0) {
11917 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
11918 res = -1;
11919 goto play_msg_cleanup;
11920 }
11921 open = 1;
11922
11923 /* play msg if it exists in this mailbox */
11924 if ((vms.lastmsg != -1) && !(play_message_by_id_helper(chan, vmu, &vms, msg_id))) {
11925 played = 1;
11926 }
11927
11928 /* close mailbox */
11929 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
11930 res = -1;
11931 goto play_msg_cleanup;
11932 }
11933 open = 0;
11934 }
11935
11936play_msg_cleanup:
11937 if (!played) {
11938 res = -1;
11939 }
11940
11941 if (vmu && open) {
11942 close_mailbox(&vms, vmu);
11943 }
11944
11945#ifdef IMAP_STORAGE
11946 if (vmu) {
11947 vmstate_delete(&vms);
11948 }
11949#endif
11950
11951 free_user(vmu);
11952
11953 return res;
11954}
11955
11956static int vm_playmsgexec(struct ast_channel *chan, const char *data)
11957{
11958 char *parse;
11959 char *mailbox = NULL;
11960 char *context = NULL;
11961 int res;
11962
11965 AST_APP_ARG(msg_id);
11966 );
11967
11968 if (ast_channel_state(chan) != AST_STATE_UP) {
11969 ast_debug(1, "Before ast_answer\n");
11970 ast_answer(chan);
11971 }
11972
11973 if (ast_strlen_zero(data)) {
11974 return -1;
11975 }
11976
11977 parse = ast_strdupa(data);
11979
11980 if (ast_strlen_zero(args.mailbox) || ast_strlen_zero(args.msg_id)) {
11981 return -1;
11982 }
11983
11984 if ((context = strchr(args.mailbox, '@'))) {
11985 *context++ = '\0';
11986 }
11987 mailbox = args.mailbox;
11988
11989 res = play_message_by_id(chan, mailbox, context, args.msg_id);
11990 pbx_builtin_setvar_helper(chan, "VOICEMAIL_PLAYBACKSTATUS", res ? "FAILED" : "SUCCESS");
11991
11992 return 0;
11993}
11994
11996{
11997#define VMBOX_STRING_HEADER_FORMAT "%-32.32s %-32.32s %-16.16s %-16.16s %-16.16s %-16.16s\n"
11998#define VMBOX_STRING_DATA_FORMAT "%-32.32s %-32.32s %-16.16s %-16.16s %-16.16s %-16.16s\n"
11999
12000 const char *mailbox = a->argv[3];
12001 const char *context = a->argv[4];
12002 struct vm_state vms;
12003 struct ast_vm_user *vmu = NULL, vmus;
12004 memset(&vmus, 0, sizeof(vmus));
12005 memset(&vms, 0, sizeof(vms));
12006
12007 if (!(vmu = find_user(&vmus, context, mailbox))) {
12008 ast_cli(a->fd, "Can't find voicemail user %s@%s\n", mailbox, context);
12009 return -1;
12010 }
12011
12012 ast_cli(a->fd, VMBOX_STRING_HEADER_FORMAT, "Full Name", "Email", "Pager", "Language", "Locale", "Time Zone");
12013 ast_cli(a->fd, VMBOX_STRING_DATA_FORMAT, vmu->fullname, vmu->email, vmu->pager, vmu->language, vmu->locale, vmu->zonetag);
12014
12015 return 0;
12016}
12017
12019{
12020#define VM_STRING_HEADER_FORMAT "%-8.8s %-32.32s %-32.32s %-9.9s %-6.6s %-30.30s\n"
12021 const char *mailbox = a->argv[3];
12022 const char *context = a->argv[4];
12023 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
12024 struct ast_vm_msg_snapshot *msg;
12025 int i;
12026
12027 /* Take a snapshot of the mailbox and walk through each folder's contents */
12028 mailbox_snapshot = ast_vm_mailbox_snapshot_create(mailbox, context, NULL, 0, AST_VM_SNAPSHOT_SORT_BY_ID, 0);
12029 if (!mailbox_snapshot) {
12030 ast_cli(a->fd, "Can't create snapshot for voicemail user %s@%s\n", mailbox, context);
12031 return -1;
12032 }
12033
12034 ast_cli(a->fd, VM_STRING_HEADER_FORMAT, "Folder", "Caller ID", "Date", "Duration", "Flag", "ID");
12035
12036 for (i = 0; i < mailbox_snapshot->folders; i++) {
12037 AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
12038 ast_cli(a->fd, VM_STRING_HEADER_FORMAT, msg->folder_name, msg->callerid, msg->origdate, msg->duration,
12039 msg->flag, msg->msg_id);
12040 }
12041 }
12042
12043 ast_cli(a->fd, "%d Message%s Total\n", mailbox_snapshot->total_msg_num, ESS(mailbox_snapshot->total_msg_num));
12044 /* done, destroy. */
12045 mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
12046
12047 return 0;
12048}
12049
12051{
12052 if (show_mailbox_details(a)){
12053 return -1;
12054 }
12055 ast_cli(a->fd, "\n");
12056 return show_mailbox_snapshot(a);
12057}
12058
12060{
12061 const char *from_mailbox = a->argv[2];
12062 const char *from_context = a->argv[3];
12063 const char *from_folder = a->argv[4];
12064 const char *id[] = { a->argv[5] };
12065 const char *to_mailbox = a->argv[6];
12066 const char *to_context = a->argv[7];
12067 const char *to_folder = a->argv[8];
12068 int ret = vm_msg_forward(from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder, 1, id, 0);
12069 if (ret) {
12070 ast_cli(a->fd, "Error forwarding message %s from mailbox %s@%s %s to mailbox %s@%s %s\n",
12071 id[0], from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder);
12072 } else {
12073 ast_cli(a->fd, "Forwarded message %s from mailbox %s@%s %s to mailbox %s@%s %s\n",
12074 id[0], from_mailbox, from_context, from_folder, to_mailbox, to_context, 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 *from_folder = a->argv[4];
12084 const char *id[] = { a->argv[5] };
12085 const char *to_folder = a->argv[6];
12086 int ret = vm_msg_move(mailbox, context, 1, from_folder, id, to_folder);
12087 if (ret) {
12088 ast_cli(a->fd, "Error moving message %s from mailbox %s@%s %s to %s\n",
12089 id[0], mailbox, context, from_folder, to_folder);
12090 } else {
12091 ast_cli(a->fd, "Moved message %s from mailbox %s@%s %s to %s\n",
12092 id[0], mailbox, context, from_folder, to_folder);
12093 }
12094 return ret;
12095}
12096
12098{
12099 const char *mailbox = a->argv[2];
12100 const char *context = a->argv[3];
12101 const char *folder = a->argv[4];
12102 const char *id[] = { a->argv[5] };
12103 int ret = vm_msg_remove(mailbox, context, 1, folder, id);
12104 if (ret) {
12105 ast_cli(a->fd, "Error removing message %s from mailbox %s@%s %s\n",
12106 id[0], mailbox, context, folder);
12107 } else {
12108 ast_cli(a->fd, "Removed message %s from mailbox %s@%s %s\n",
12109 id[0], mailbox, context, folder);
12110 }
12111 return ret;
12112}
12113
12115{
12116 const char *word = a->word;
12117 int pos = a->pos;
12118 int state = a->n;
12119 int which = 0;
12120 int wordlen;
12121 struct ast_vm_user *vmu;
12122 const char *context = "", *mailbox = "";
12123 char *ret = NULL;
12124
12125 /* 0 - voicemail; 1 - show; 2 - mailbox; 3 - <mailbox>; 4 - <context> */
12126 if (pos == 3) {
12127 wordlen = strlen(word);
12129 AST_LIST_TRAVERSE(&users, vmu, list) {
12130 if (!strncasecmp(word, vmu->mailbox , wordlen)) {
12131 if (mailbox && strcmp(mailbox, vmu->mailbox) && ++which > state) {
12132 ret = ast_strdup(vmu->mailbox);
12134 return ret;
12135 }
12136 mailbox = vmu->mailbox;
12137 }
12138 }
12140 } else if (pos == 4) {
12141 /* Only display contexts that match the user in pos 3 */
12142 const char *box = a->argv[3];
12143 wordlen = strlen(word);
12145 AST_LIST_TRAVERSE(&users, vmu, list) {
12146 if (!strncasecmp(word, vmu->context, wordlen) && !strcasecmp(box, vmu->mailbox)) {
12147 if (context && strcmp(context, vmu->context) && ++which > state) {
12148 ret = ast_strdup(vmu->context);
12150 return ret;
12151 }
12152 context = vmu->context;
12153 }
12154 }
12156 }
12157
12158 return ret;
12159}
12160
12161static char *handle_voicemail_show_mailbox(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12162{
12163 switch (cmd) {
12164 case CLI_INIT:
12165 e->command = "voicemail show mailbox";
12166 e->usage =
12167 "Usage: voicemail show mailbox <mailbox> <context>\n"
12168 " Show contents of mailbox <mailbox>@<context>\n";
12169 return NULL;
12170 case CLI_GENERATE:
12172 case CLI_HANDLER:
12173 break;
12174 }
12175
12176 if (a->argc != 5) {
12177 return CLI_SHOWUSAGE;
12178 }
12179
12181 return CLI_FAILURE;
12182 }
12183
12184 return CLI_SUCCESS;
12185}
12186
12187/* Handles filling in data for one of the following three formats (based on maxpos = 5|6|8):
12188
12189 maxpos = 5
12190 0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12191 maxpos = 6
12192 0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12193 6 - <to_folder>;
12194 maxpos = 8
12195 0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12196 6 - <to_mailbox>; 7 - <to_context>; 8 - <to_folder>;
12197
12198 Passing in the maximum expected position 'maxpos' helps us fill in the missing entries in one function
12199 instead of three by taking advantage of the overlap in the command sequence between forward, move and
12200 remove as each of these use nearly the same syntax up until their maximum number of arguments.
12201 The value of pos = 6 changes to be either <messageid> or <folder> based on maxpos being 6 or 8.
12202*/
12203
12204static char *complete_voicemail_move_message(struct ast_cli_args *a, int maxpos)
12205{
12206 const char *word = a->word;
12207 int pos = a->pos;
12208 int state = a->n;
12209 int which = 0;
12210 int wordlen;
12211 struct ast_vm_user *vmu;
12212 const char *context = "", *mailbox = "", *folder = "", *id = "";
12213 char *ret = NULL;
12214
12215 if (pos > maxpos) {
12216 /* If the passed in pos is above the max, return NULL to avoid 'over-filling' the cli */
12217 return NULL;
12218 }
12219
12220 /* if we are in pos 2 or pos 6 in 'forward' mode */
12221 if (pos == 2 || (pos == 6 && maxpos == 8)) {
12222 /* find users */
12223 wordlen = strlen(word);
12225 AST_LIST_TRAVERSE(&users, vmu, list) {
12226 if (!strncasecmp(word, vmu->mailbox , wordlen)) {
12227 if (mailbox && strcmp(mailbox, vmu->mailbox) && ++which > state) {
12228 ret = ast_strdup(vmu->mailbox);
12230 return ret;
12231 }
12232 mailbox = vmu->mailbox;
12233 }
12234 }
12236 } else if (pos == 3 || pos == 7) {
12237 /* find contexts that match the user */
12238 mailbox = (pos == 3) ? a->argv[2] : a->argv[6];
12239 wordlen = strlen(word);
12241 AST_LIST_TRAVERSE(&users, vmu, list) {
12242 if (!strncasecmp(word, vmu->context, wordlen) && !strcasecmp(mailbox, vmu->mailbox)) {
12243 if (context && strcmp(context, vmu->context) && ++which > state) {
12244 ret = ast_strdup(vmu->context);
12246 return ret;
12247 }
12248 context = vmu->context;
12249 }
12250 }
12252 } else if (pos == 4 || pos == 8 || (pos == 6 && maxpos == 6) ) {
12253 int i;
12254 /* Walk through the standard folders */
12255 wordlen = strlen(word);
12256 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
12257 if (folder && !strncasecmp(word, mailbox_folders[i], wordlen) && ++which > state) {
12258 return ast_strdup(mailbox_folders[i]);
12259 }
12260 folder = mailbox_folders[i];
12261 }
12262 } else if (pos == 5) {
12263 /* find messages in the folder */
12264 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
12265 struct ast_vm_msg_snapshot *msg;
12266 mailbox = a->argv[2];
12267 context = a->argv[3];
12268 folder = a->argv[4];
12269 wordlen = strlen(word);
12270
12271 /* Take a snapshot of the mailbox and snag the individual info */
12272 if ((mailbox_snapshot = ast_vm_mailbox_snapshot_create(mailbox, context, folder, 0, AST_VM_SNAPSHOT_SORT_BY_ID, 0))) {
12273 int i;
12274 /* we are only requesting the one folder, but we still need to know it's index */
12275 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
12276 if (!strcasecmp(mailbox_folders[i], folder)) {
12277 break;
12278 }
12279 }
12280 AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
12281 if (id && !strncasecmp(word, msg->msg_id, wordlen) && ++which > state) {
12282 ret = ast_strdup(msg->msg_id);
12283 break;
12284 }
12285 id = msg->msg_id;
12286 }
12287 /* done, destroy. */
12288 mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
12289 }
12290 }
12291
12292 return ret;
12293}
12294
12295static char *handle_voicemail_forward_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12296{
12297 switch (cmd) {
12298 case CLI_INIT:
12299 e->command = "voicemail forward";
12300 e->usage =
12301 "Usage: voicemail forward <from_mailbox> <from_context> <from_folder> <messageid> <to_mailbox> <to_context> <to_folder>\n"
12302 " Forward message <messageid> in mailbox <mailbox>@<context> <from_folder>\n"
12303 " to mailbox <mailbox>@<context> <to_folder>\n";
12304 return NULL;
12305 case CLI_GENERATE:
12307 case CLI_HANDLER:
12308 break;
12309 }
12310
12311 if (a->argc != 9) {
12312 return CLI_SHOWUSAGE;
12313 }
12314
12316 return CLI_FAILURE;
12317 }
12318
12319 return CLI_SUCCESS;
12320}
12321
12322static char *handle_voicemail_move_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12323{
12324 switch (cmd) {
12325 case CLI_INIT:
12326 e->command = "voicemail move";
12327 e->usage =
12328 "Usage: voicemail move <mailbox> <context> <from_folder> <messageid> <to_folder>\n"
12329 " Move message <messageid> in mailbox <mailbox>&<context> from <from_folder> to <to_folder>\n";
12330 return NULL;
12331 case CLI_GENERATE:
12333 case CLI_HANDLER:
12334 break;
12335 }
12336
12337 if (a->argc != 7) {
12338 return CLI_SHOWUSAGE;
12339 }
12340
12342 return CLI_FAILURE;
12343 }
12344
12345 return CLI_SUCCESS;
12346}
12347
12348static char *handle_voicemail_remove_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12349{
12350 switch (cmd) {
12351 case CLI_INIT:
12352 e->command = "voicemail remove";
12353 e->usage =
12354 "Usage: voicemail remove <mailbox> <context> <from_folder> <messageid>\n"
12355 " Remove message <messageid> from <from_folder> in mailbox <mailbox>@<context>\n";
12356 return NULL;
12357 case CLI_GENERATE:
12359 case CLI_HANDLER:
12360 break;
12361 }
12362
12363 if (a->argc != 6) {
12364 return CLI_SHOWUSAGE;
12365 }
12366
12368 return CLI_FAILURE;
12369 }
12370
12371 return CLI_SUCCESS;
12372}
12373
12374static int vm_execmain(struct ast_channel *chan, const char *data)
12375{
12376 /* XXX This is, admittedly, some pretty horrendous code. For some
12377 reason it just seemed a lot easier to do with GOTO's. I feel
12378 like I'm back in my GWBASIC days. XXX */
12379 int res = -1;
12380 int cmd = 0;
12381 int valid = 0;
12382 char prefixstr[80] ="";
12383 char ext_context[256]="";
12384 int box;
12385 int useadsi = 0;
12386 int skipuser = 0;
12387 struct vm_state vms = {{0}};
12388 struct ast_vm_user *vmu = NULL, vmus = {{0}};
12389 char *context = NULL;
12390 int silentexit = 0;
12391 struct ast_flags flags = { 0 };
12392 signed char record_gain = 0;
12393 int play_auto = 0;
12394 int play_folder = 0;
12395 int in_urgent = 0;
12396 int nodelete = 0;
12397#ifdef IMAP_STORAGE
12398 int deleted = 0;
12399#endif
12400 SCOPE_ENTER(3, "%s:\n", ast_channel_name(chan));
12401
12402 /* Add the vm_state to the active list and keep it active */
12403 vms.lastmsg = -1;
12404
12405 ast_test_suite_event_notify("START", "Message: vm_execmain started");
12406 if (ast_channel_state(chan) != AST_STATE_UP) {
12407 ast_debug(1, "Before ast_answer\n");
12408 ast_answer(chan);
12409 }
12410
12411 if (!ast_strlen_zero(data)) {
12412 char *opts[OPT_ARG_ARRAY_SIZE];
12413 char *parse;
12415 AST_APP_ARG(argv0);
12416 AST_APP_ARG(argv1);
12417 );
12418
12419 parse = ast_strdupa(data);
12420
12422
12423 if (args.argc == 2) {
12424 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
12425 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid option string '%s'\n", args.argv1);
12426 }
12428 int gain;
12429 if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
12430 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
12431 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
12432 } else {
12433 record_gain = (signed char) gain;
12434 }
12435 } else {
12436 ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
12437 }
12438 }
12440 play_auto = 1;
12441 if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
12442 /* See if it is a folder name first */
12443 if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
12444 if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
12445 play_folder = -1;
12446 }
12447 } else {
12448 play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
12449 }
12450 } else {
12451 ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
12452 }
12453 if (play_folder > 9 || play_folder < 0) {
12455 "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
12456 opts[OPT_ARG_PLAYFOLDER]);
12457 play_folder = 0;
12458 }
12459 }
12461 nodelete = 1;
12462 }
12463 } else {
12464 /* old style options parsing */
12465 while (*(args.argv0)) {
12466 if (*(args.argv0) == 's')
12468 else if (*(args.argv0) == 'p')
12470 else
12471 break;
12472 (args.argv0)++;
12473 }
12474
12475 }
12476
12477 valid = ast_test_flag(&flags, OPT_SILENT);
12478
12479 if ((context = strchr(args.argv0, '@')))
12480 *context++ = '\0';
12481
12483 ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
12484 else
12485 ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
12486
12487 if (!ast_strlen_zero(vms.username)) {
12488 if ((vmu = find_user(&vmus, context ,vms.username))) {
12489 skipuser++;
12490 } else {
12491 ast_log(LOG_WARNING, "Mailbox '%s%s%s' doesn't exist\n", vms.username, context ? "@": "", context ? context : "");
12492 valid = 0;
12493 }
12494 } else {
12495 valid = 0;
12496 }
12497 }
12498
12499 if (!valid)
12500 res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
12501
12502 ast_trace(-1, "vm_authenticate user: %s\n", vms.username);
12503
12504 if (vms.username[0] == '*') {
12505 ast_trace(-1, "user pressed * in context '%s'\n", ast_channel_context(chan));
12506
12507 /* user entered '*' */
12508 if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
12509 ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
12510 res = 0; /* prevent hangup */
12511 goto out;
12512 }
12513 }
12514
12515 if (!res) {
12516 valid = 1;
12517 if (!skipuser)
12518 vmu = &vmus;
12519 } else {
12520 res = 0;
12521 }
12522
12523 /* If ADSI is supported, setup login screen */
12524 adsi_begin(chan, &useadsi);
12525
12526 if (!valid) {
12527 ast_trace(-1, "Invalid user\n");
12528 goto out;
12529 }
12530 ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
12531
12532#ifdef IMAP_STORAGE
12533 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
12534 pthread_setspecific(ts_vmstate.key, &vms);
12535
12536 vms.interactive = 1;
12537 vms.updated = 1;
12538 if (vmu)
12539 ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
12540 vmstate_insert(&vms);
12541 init_vm_state(&vms);
12542#endif
12543
12544 /* Set language from config to override channel language */
12545 if (!ast_strlen_zero(vmu->language)) {
12546 ast_channel_lock(chan);
12547 ast_channel_language_set(chan, vmu->language);
12548 ast_channel_unlock(chan);
12549 }
12550
12551 /* Retrieve urgent, old and new message counts */
12552 ast_trace(-1, "Before open_mailbox\n");
12553 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
12554 if (res < 0) {
12555 ast_trace(-1, "open mailbox: %d\n", res);
12556 goto out;
12557 }
12558 vms.oldmessages = vms.lastmsg + 1;
12559 ast_trace(-1, "Number of old messages: %d\n", vms.oldmessages);
12560 /* check INBOX */
12561 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12562 if (res < 0) {
12563 ast_trace(-1, "open mailbox: %d\n", res);
12564 goto out;
12565 }
12566 vms.newmessages = vms.lastmsg + 1;
12567 ast_trace(-1, "Number of new messages: %d\n", vms.newmessages);
12568 /* Start in Urgent */
12569 in_urgent = 1;
12570 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11); /*11 is the Urgent folder */
12571 if (res < 0) {
12572 ast_trace(-1, "open mailbox: %d\n", res);
12573 goto out;
12574 }
12575 vms.urgentmessages = vms.lastmsg + 1;
12576 ast_trace(-1, "Number of urgent messages: %d\n", vms.urgentmessages);
12577
12578 /* Select proper mailbox FIRST!! */
12579 if (play_auto) {
12580 ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
12581 if (vms.urgentmessages) {
12582 in_urgent = 1;
12583 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11);
12584 } else {
12585 in_urgent = 0;
12586 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, play_folder);
12587 }
12588 if (res < 0) {
12589 ast_trace(-1, "open mailbox: %d\n", res);
12590 goto out;
12591 }
12592
12593 /* If there are no new messages, inform the user and hangup */
12594 if (vms.lastmsg == -1) {
12595 in_urgent = 0;
12596 cmd = vm_browse_messages(chan, &vms, vmu);
12597 res = 0;
12598 goto out;
12599 }
12600 } else {
12601 if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
12602 /* If we only have old messages start here */
12603 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
12604 in_urgent = 0;
12605 play_folder = 1;
12606 if (res < 0)
12607 goto out;
12608 } else if (!vms.urgentmessages && vms.newmessages) {
12609 /* If we have new messages but none are urgent */
12610 in_urgent = 0;
12611 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12612 if (res < 0)
12613 goto out;
12614 }
12615 }
12616
12617 if (useadsi)
12618 adsi_status(chan, &vms);
12619 res = 0;
12620
12621 /* Check to see if this is a new user */
12622 if (!strcasecmp(vmu->mailbox, vmu->password) &&
12624 if (ast_play_and_wait(chan, vm_newuser) == -1)
12625 ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
12626 cmd = vm_newuser_setup(chan, vmu, &vms, vmfmts, record_gain);
12627 if ((cmd == 't') || (cmd == '#')) {
12628 /* Timeout */
12629 ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
12630 res = 0;
12631 ast_trace(-1, "Timeout\n");
12632 goto out;
12633 } else if (cmd < 0) {
12634 /* Hangup */
12635 ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
12636 res = -1;
12637 ast_trace(-1, "Hangup\n");
12638 goto out;
12639 }
12640 }
12641#ifdef IMAP_STORAGE
12642 ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
12643 if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
12644 ast_debug(1, "*** QUOTA EXCEEDED!!\n");
12645 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
12646 }
12647 ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
12648 if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
12649 ast_log(AST_LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
12650 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
12651 }
12652#endif
12653
12654 ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
12655 if (play_auto) {
12656 cmd = '1';
12657 } else {
12658 cmd = vm_intro(chan, vmu, &vms);
12659 }
12660 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12661 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12662
12663 vms.repeats = 0;
12664 vms.starting = 1;
12665 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
12666 /* Run main menu */
12667 ast_trace(-1, "Main menu: %d %c\n", cmd, (cmd >= 32 && cmd <= 126 ? cmd : ' '));
12668 switch (cmd) {
12669 case '1': /* First message */
12670 vms.curmsg = 0;
12671 /* Fall through */
12672 case '5': /* Play current message */
12673 ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
12674 cmd = vm_browse_messages(chan, &vms, vmu);
12675 break;
12676 case '2': /* Change folders */
12677 ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
12678 if (useadsi)
12679 adsi_folders(chan, 0, "Change to folder...");
12680
12681 cmd = get_folder2(chan, "vm-changeto", 0);
12682 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12683 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12684 if (cmd == '#') {
12685 cmd = 0;
12686 } else if (cmd > 0) {
12687 cmd = cmd - '0';
12688 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12689 if (res == ERROR_LOCK_PATH) {
12690 ast_trace(-1, "close mailbox: %d\n", res);
12691 goto out;
12692 }
12693 /* If folder is not urgent, set in_urgent to zero! */
12694 if (cmd != 11) in_urgent = 0;
12695 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, cmd);
12696 if (res < 0) {
12697 ast_trace(-1, "open mailbox: %d\n", res);
12698 goto out;
12699 }
12700 play_folder = cmd;
12701 cmd = 0;
12702 }
12703 if (useadsi)
12704 adsi_status2(chan, &vms);
12705
12706 if (!cmd) {
12707 cmd = vm_play_folder_name(chan, vms.vmbox);
12708 }
12709
12710 vms.starting = 1;
12711 vms.curmsg = 0;
12712 break;
12713 case '3': /* Advanced options */
12714 ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
12715 cmd = 0;
12716 vms.repeats = 0;
12717 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
12718 switch (cmd) {
12719 case '1': /* Reply */
12720 if (vms.lastmsg > -1 && !vms.starting) {
12721 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
12722 if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
12723 res = cmd;
12724 ast_trace(-1, "advanced options: %d\n", cmd);
12725 goto out;
12726 }
12727 } else {
12728 cmd = ast_play_and_wait(chan, "vm-sorry");
12729 }
12730 cmd = 't';
12731 break;
12732 case '2': /* Callback */
12733 if (!vms.starting)
12734 ast_verb(3, "Callback Requested\n");
12735 if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
12736 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
12737 ast_trace(-1, "advanced options: %d\n", cmd);
12738 if (cmd == 9) {
12739 silentexit = 1;
12740 goto out;
12741 } else if (cmd == ERROR_LOCK_PATH) {
12742 res = cmd;
12743 goto out;
12744 }
12745 } else {
12746 cmd = ast_play_and_wait(chan, "vm-sorry");
12747 }
12748 cmd = 't';
12749 break;
12750 case '3': /* Envelope */
12751 if (vms.lastmsg > -1 && !vms.starting) {
12752 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
12753 if (cmd == ERROR_LOCK_PATH) {
12754 res = cmd;
12755 ast_trace(-1, "advanced options: %d\n", cmd);
12756 goto out;
12757 }
12758 } else {
12759 cmd = ast_play_and_wait(chan, "vm-sorry");
12760 }
12761 cmd = 't';
12762 break;
12763 case '4': /* Dialout */
12764 if (!ast_strlen_zero(vmu->dialout)) {
12765 cmd = dialout(chan, vmu, NULL, vmu->dialout);
12766 if (cmd == 9) {
12767 silentexit = 1;
12768 ast_trace(-1, "dialout: %d\n", cmd);
12769 goto out;
12770 }
12771 } else {
12772 cmd = ast_play_and_wait(chan, "vm-sorry");
12773 }
12774 cmd = 't';
12775 break;
12776
12777 case '5': /* Leave VoiceMail */
12778 if (ast_test_flag(vmu, VM_SVMAIL)) {
12779 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, forward_message, chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
12780 if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
12781 res = cmd;
12782 ast_trace(-1, "forward message: %d\n", cmd);
12783 goto out;
12784 }
12785 } else {
12786 cmd = ast_play_and_wait(chan, "vm-sorry");
12787 }
12788 cmd = 't';
12789 break;
12790
12791 case '*': /* Return to main menu */
12792 cmd = 't';
12793 break;
12794
12795 default:
12796 cmd = 0;
12797 if (!vms.starting) {
12798 cmd = ast_play_and_wait(chan, "vm-toreply");
12799 }
12800 if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
12801 cmd = ast_play_and_wait(chan, "vm-tocallback");
12802 }
12803 if (!cmd && !vms.starting) {
12804 cmd = ast_play_and_wait(chan, "vm-tohearenv");
12805 }
12806 if (!ast_strlen_zero(vmu->dialout) && !cmd) {
12807 cmd = ast_play_and_wait(chan, "vm-tomakecall");
12808 }
12809 if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
12810 cmd = ast_play_and_wait(chan, "vm-leavemsg");
12811 }
12812 if (!cmd) {
12813 cmd = ast_play_and_wait(chan, "vm-starmain");
12814 }
12815 if (!cmd) {
12816 cmd = ast_waitfordigit(chan, 6000);
12817 }
12818 if (!cmd) {
12819 vms.repeats++;
12820 }
12821 if (vms.repeats > 3) {
12822 cmd = 't';
12823 }
12824 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12825 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12826 }
12827 }
12828 if (cmd == 't') {
12829 cmd = 0;
12830 vms.repeats = 0;
12831 }
12832 break;
12833 case '4': /* Go to the previous message */
12834 ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
12835 if (vms.curmsg > 0) {
12836 vms.curmsg--;
12837 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12838 } else {
12839 /* Check if we were listening to new
12840 messages. If so, go to Urgent messages
12841 instead of saying "no more messages"
12842 */
12843 if (in_urgent == 0 && vms.urgentmessages > 0) {
12844 /* Check for Urgent messages */
12845 in_urgent = 1;
12846 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12847 if (res == ERROR_LOCK_PATH) {
12848 ast_trace(-1, "close mailbox: %d\n", res);
12849 goto out;
12850 }
12851 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11); /* Open Urgent folder */
12852 if (res < 0) {
12853 ast_trace(-1, "open mailbox: %d\n", res);
12854 goto out;
12855 }
12856 ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
12857 vms.curmsg = vms.lastmsg;
12858 if (vms.lastmsg < 0) {
12859 cmd = ast_play_and_wait(chan, "vm-nomore");
12860 }
12861 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12862 vms.curmsg = vms.lastmsg;
12863 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12864 } else {
12865 cmd = ast_play_and_wait(chan, "vm-nomore");
12866 }
12867 }
12868 break;
12869 case '6': /* Go to the next message */
12870 ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
12871 if (vms.curmsg < vms.lastmsg) {
12872 vms.curmsg++;
12873 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12874 } else {
12875 if (in_urgent && vms.newmessages > 0) {
12876 /* Check if we were listening to urgent
12877 * messages. If so, go to regular new messages
12878 * instead of saying "no more messages"
12879 */
12880 in_urgent = 0;
12881 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12882 if (res == ERROR_LOCK_PATH) {
12883 ast_trace(-1, "close mailbox: %d\n", res);
12884 goto out;
12885 }
12886 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12887 if (res < 0) {
12888 ast_trace(-1, "open mailbox: %d\n", res);
12889 goto out;
12890 }
12891 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12892 vms.curmsg = -1;
12893 if (vms.lastmsg < 0) {
12894 cmd = ast_play_and_wait(chan, "vm-nomore");
12895 }
12896 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12897 vms.curmsg = 0;
12898 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12899 } else {
12900 cmd = ast_play_and_wait(chan, "vm-nomore");
12901 }
12902 }
12903 break;
12904 case '7': /* Delete the current message */
12905 if (!nodelete && vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
12906 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
12907 if (useadsi)
12908 adsi_delete(chan, &vms);
12909 if (vms.deleted[vms.curmsg]) {
12910 if (play_folder == 0) {
12911 if (in_urgent) {
12912 vms.urgentmessages--;
12913 } else {
12914 vms.newmessages--;
12915 }
12916 }
12917 else if (play_folder == 1)
12918 vms.oldmessages--;
12919 cmd = ast_play_and_wait(chan, "vm-deleted");
12920 } else {
12921 if (play_folder == 0) {
12922 if (in_urgent) {
12923 vms.urgentmessages++;
12924 } else {
12925 vms.newmessages++;
12926 }
12927 }
12928 else if (play_folder == 1)
12929 vms.oldmessages++;
12930 cmd = ast_play_and_wait(chan, "vm-undeleted");
12931 }
12932 if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
12933 if (vms.curmsg < vms.lastmsg) {
12934 vms.curmsg++;
12935 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12936 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12937 vms.curmsg = 0;
12938 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12939 } else {
12940 /* Check if we were listening to urgent
12941 messages. If so, go to regular new messages
12942 instead of saying "no more messages"
12943 */
12944 if (in_urgent == 1) {
12945 /* Check for new messages */
12946 in_urgent = 0;
12947 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12948 if (res == ERROR_LOCK_PATH) {
12949 ast_trace(-1, "close mailbox: %d\n", res);
12950 goto out;
12951 }
12952 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12953 if (res < 0) {
12954 ast_trace(-1, "open mailbox: %d\n", res);
12955 goto out;
12956 }
12957 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12958 vms.curmsg = -1;
12959 if (vms.lastmsg < 0) {
12960 cmd = ast_play_and_wait(chan, "vm-nomore");
12961 }
12962 } else {
12963 cmd = ast_play_and_wait(chan, "vm-nomore");
12964 }
12965 }
12966 }
12967 } else /* Delete not valid if we haven't selected a message */
12968 cmd = 0;
12969#ifdef IMAP_STORAGE
12970 deleted = 1;
12971#endif
12972 break;
12973
12974 case '8': /* Forward the current message */
12975 if (vms.lastmsg > -1) {
12976 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, forward_message, chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
12977 if (cmd == ERROR_LOCK_PATH) {
12978 res = cmd;
12979 ast_trace(-1, "forward message: %d\n", res);
12980 goto out;
12981 }
12982 } else {
12983 /* Check if we were listening to urgent
12984 messages. If so, go to regular new messages
12985 instead of saying "no more messages"
12986 */
12987 if (in_urgent == 1 && vms.newmessages > 0) {
12988 /* Check for new messages */
12989 in_urgent = 0;
12990 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12991 if (res == ERROR_LOCK_PATH) {
12992 ast_trace(-1, "close mailbox: %d\n", res);
12993 goto out;
12994 }
12995 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12996 if (res < 0) {
12997 ast_trace(-1, "open mailbox: %d\n", res);
12998 goto out;
12999 }
13000 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
13001 vms.curmsg = -1;
13002 if (vms.lastmsg < 0) {
13003 cmd = ast_play_and_wait(chan, "vm-nomore");
13004 }
13005 } else {
13006 cmd = ast_play_and_wait(chan, "vm-nomore");
13007 }
13008 }
13009 break;
13010 case '9': /* Save message to folder */
13011 ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
13012 if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
13013 /* No message selected */
13014 cmd = 0;
13015 break;
13016 }
13017 if (useadsi)
13018 adsi_folders(chan, 1, "Save to folder...");
13019 cmd = get_folder2(chan, "vm-savefolder", 1);
13020 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
13021 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
13022 box = 0; /* Shut up compiler */
13023 if (cmd == '#') {
13024 cmd = 0;
13025 break;
13026 } else if (cmd > 0) {
13027 box = cmd = cmd - '0';
13028 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, &vms, vms.curmsg, cmd, NULL, 0);
13029 if (cmd == ERROR_LOCK_PATH) {
13030 res = cmd;
13031 ast_trace(-1, "save to folder: %d\n", res);
13032 goto out;
13033#ifndef IMAP_STORAGE
13034 } else if (!cmd) {
13035 vms.deleted[vms.curmsg] = 1;
13036#endif
13037 } else {
13038 vms.deleted[vms.curmsg] = 0;
13039 vms.heard[vms.curmsg] = 0;
13040 }
13041 }
13042 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
13043 if (useadsi)
13044 adsi_message(chan, &vms);
13045 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
13046 if (!cmd) {
13047 cmd = ast_play_and_wait(chan, "vm-message");
13048 if (!cmd)
13049 cmd = say_and_wait(chan, vms.curmsg + 1, ast_channel_language(chan));
13050 if (!cmd)
13051 cmd = ast_play_and_wait(chan, "vm-savedto");
13052 if (!cmd)
13053 cmd = vm_play_folder_name(chan, vms.fn);
13054 } else {
13055 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
13056 }
13058 if (vms.curmsg < vms.lastmsg) {
13059 vms.curmsg++;
13060 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
13061 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
13062 vms.curmsg = 0;
13063 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
13064 } else {
13065 /* Check if we were listening to urgent
13066 messages. If so, go to regular new messages
13067 instead of saying "no more messages"
13068 */
13069 if (in_urgent == 1 && vms.newmessages > 0) {
13070 /* Check for new messages */
13071 in_urgent = 0;
13072 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
13073 if (res == ERROR_LOCK_PATH) {
13074 ast_trace(-1, "close mailbox: %d\n", res);
13075 goto out;
13076 }
13077 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
13078 if (res < 0) {
13079 ast_trace(-1, "open mailbox: %d\n", res);
13080 goto out;
13081 }
13082 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
13083 vms.curmsg = -1;
13084 if (vms.lastmsg < 0) {
13085 cmd = ast_play_and_wait(chan, "vm-nomore");
13086 }
13087 } else {
13088 cmd = ast_play_and_wait(chan, "vm-nomore");
13089 }
13090 }
13091 }
13092 break;
13093 case '*': /* Help */
13094 if (!vms.starting) {
13095 if (!strncasecmp(ast_channel_language(chan), "ja", 2)) {
13096 cmd = vm_play_folder_name(chan, vms.vmbox);
13097 if (!cmd)
13098 cmd = ast_play_and_wait(chan, "jp-wa");
13099 if (!cmd)
13100 cmd = ast_play_and_wait(chan, "digits/1");
13101 if (!cmd)
13102 cmd = ast_play_and_wait(chan, "jp-wo");
13103 if (!cmd)
13104 cmd = ast_play_and_wait(chan, "silence/1");
13105 if (!cmd)
13106 cmd = ast_play_and_wait(chan, "vm-opts");
13107 if (!cmd)
13108 cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent, nodelete);
13109 break;
13110 }
13111 cmd = ast_play_and_wait(chan, "vm-onefor");
13112 if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
13113 cmd = ast_play_and_wait(chan, "vm-for");
13114 }
13115 if (!cmd)
13116 cmd = vm_play_folder_name(chan, vms.vmbox);
13117 if (!cmd)
13118 cmd = ast_play_and_wait(chan, "vm-opts");
13119 if (!cmd)
13120 cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent, nodelete);
13121 } else
13122 cmd = 0;
13123 break;
13124 case '0': /* Mailbox options */
13125 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, vm_options,chan, vmu, &vms, vmfmts, record_gain);
13126 if (useadsi)
13127 adsi_status(chan, &vms);
13128 /* Reopen play_folder */
13129 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, play_folder);
13130 if (res < 0) {
13131 ast_trace(-1, "open mailbox: %d\n", res);
13132 goto out;
13133 }
13134 vms.starting = 1;
13135 break;
13136 default: /* Nothing */
13137 ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
13138 cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent, nodelete);
13139 break;
13140 }
13141 }
13142 if ((cmd == 't') || (cmd == '#')) {
13143 /* Timeout */
13144 res = 0;
13145 } else {
13146 /* Hangup */
13147 res = -1;
13148 }
13149
13150out:
13151 if (res > -1) {
13152 ast_stopstream(chan);
13153 adsi_goodbye(chan);
13154 if (valid && res != OPERATOR_EXIT) {
13155 if (silentexit)
13156 res = ast_play_and_wait(chan, "vm-dialout");
13157 else
13158 res = ast_play_and_wait(chan, "vm-goodbye");
13159 }
13160 if ((valid && res > 0) || res == OPERATOR_EXIT) {
13161 res = 0;
13162 }
13163 if (useadsi)
13165 }
13166 if (vmu) {
13167 SCOPE_CALL(-1, close_mailbox, &vms, vmu);
13168 }
13169 if (valid) {
13170 int new = 0, old = 0, urgent = 0;
13171 snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
13172 /* Urgent flag not passwd to externnotify here */
13173 run_externnotify(vmu->context, vmu->mailbox, NULL);
13174 ast_app_inboxcount2(ext_context, &urgent, &new, &old);
13175 queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgent, new, old);
13176 }
13177#ifdef IMAP_STORAGE
13178 /* expunge message - use UID Expunge if supported on IMAP server*/
13179 ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
13180 if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
13181 ast_mutex_lock(&vms.lock);
13182#ifdef HAVE_IMAP_TK2006
13183 if (LEVELUIDPLUS (vms.mailstream)) {
13184 mail_expunge_full(vms.mailstream, NIL, EX_UID);
13185 } else
13186#endif
13187 mail_expunge(vms.mailstream);
13188 ast_mutex_unlock(&vms.lock);
13189 }
13190 /* before we delete the state, we should copy pertinent info
13191 * back to the persistent model */
13192 if (vmu) {
13193 vmstate_delete(&vms);
13194 }
13195#endif
13196 if (vmu)
13197 free_user(vmu);
13198
13199#ifdef IMAP_STORAGE
13200 pthread_setspecific(ts_vmstate.key, NULL);
13201#endif
13202 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d\n", res);
13203}
13204
13205static int vm_exec(struct ast_channel *chan, const char *data)
13206{
13207 int res = 0;
13208 char *tmp;
13209 struct leave_vm_options leave_options;
13210 struct ast_flags flags = { 0 };
13211 char *opts[OPT_ARG_ARRAY_SIZE];
13213 AST_APP_ARG(argv0);
13214 AST_APP_ARG(argv1);
13215 );
13216 SCOPE_ENTER(3, "%s\n", ast_channel_name(chan));
13217
13218 memset(&leave_options, 0, sizeof(leave_options));
13219
13220 if (!ast_strlen_zero(data)) {
13221 tmp = ast_strdupa(data);
13223 if (args.argc == 2) {
13224 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
13225 SCOPE_EXIT_RTN_VALUE(-1, "parse options failed for '%s'\n", args.argv1);
13226 }
13229 int gain;
13230
13231 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
13232 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
13233 } else {
13234 leave_options.record_gain = (signed char) gain;
13235 }
13236 }
13239 leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
13240 }
13241 }
13242 if (ast_test_flag(&flags, OPT_BEEP)) { /* Use custom beep (or none at all) */
13243 leave_options.beeptone = opts[OPT_ARG_BEEP_TONE];
13244 } else { /* Use default beep */
13245 leave_options.beeptone = "beep";
13246 }
13247 } else {
13248 char temp[256];
13249 res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
13250 if (res < 0) {
13251 SCOPE_EXIT_RTN_VALUE(res, "getdata failed. RC: %d", res);
13252 }
13253 if (ast_strlen_zero(temp)) {
13255 }
13256 args.argv0 = ast_strdupa(temp);
13257 }
13258
13259 if (ast_channel_state(chan) != AST_STATE_UP) {
13262 } else {
13263 ast_answer(chan);
13264 }
13265 }
13266
13267 res = SCOPE_CALL_WITH_INT_RESULT(-1, leave_voicemail, chan, args.argv0, &leave_options);
13268 if (res == 't') {
13269 ast_play_and_wait(chan, "vm-goodbye");
13270 res = 0;
13271 }
13272
13273 if (res == OPERATOR_EXIT) {
13274 res = 0;
13275 }
13276
13277 if (res == ERROR_LOCK_PATH) {
13278 ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
13279 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
13280 res = 0;
13281 }
13282
13283 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d", res);
13284}
13285
13286static 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)
13287{
13288 struct ast_variable *var;
13289 struct ast_category *cat;
13290 generate_msg_id(id);
13291
13292 var = ast_variable_new("msg_id", id, "");
13293 if (!var) {
13294 return -1;
13295 }
13296
13297 cat = ast_category_get(msg_cfg, "message", NULL);
13298 if (!cat) {
13299 ast_log(LOG_ERROR, "Voicemail data file %s/%d.txt has no [message] category?\n", dir, msg);
13301 return -1;
13302 }
13303
13305
13306 if (ast_config_text_file_save(filename, msg_cfg, "app_voicemail")) {
13307 ast_log(LOG_WARNING, "Unable to update %s to have a message ID\n", filename);
13308 return -1;
13309 }
13310
13311 UPDATE_MSG_ID(dir, msg, id, vmu, msg_cfg, folder);
13312 return 0;
13313}
13314
13315static struct ast_vm_user *find_or_create(const char *context, const char *box)
13316{
13317 struct ast_vm_user *vmu;
13318
13319 if (!ast_strlen_zero(box) && box[0] == '*') {
13320 ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character. The '*' character,"
13321 "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
13322 "\n\tpredefined extension 'a'. A mailbox or password beginning with '*' is not valid"
13323 "\n\tand will be ignored.\n", box, context);
13324 return NULL;
13325 }
13326
13327 AST_LIST_TRAVERSE(&users, vmu, list) {
13328 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
13329 if (strcasecmp(vmu->context, context)) {
13330 ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
13331 \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
13332 \n\tconfiguration creates an ambiguity that you likely do not want. Please\
13333 \n\tamend your voicemail.conf file to avoid this situation.\n", box);
13334 }
13335 ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
13336 return NULL;
13337 }
13338 if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
13339 ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
13340 return NULL;
13341 }
13342 }
13343
13344 if (!(vmu = ast_calloc(1, sizeof(*vmu))))
13345 return NULL;
13346
13347 ast_copy_string(vmu->context, context, sizeof(vmu->context));
13348 ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
13349
13351
13352 return vmu;
13353}
13354
13355static int append_mailbox(const char *context, const char *box, const char *data)
13356{
13357 /* Assumes lock is already held */
13358 char *tmp;
13359 char *stringp;
13360 char *s;
13361 struct ast_vm_user *vmu;
13362 char mailbox_full[MAX_VM_MAILBOX_LEN];
13363 int new = 0, old = 0, urgent = 0;
13364 char secretfn[PATH_MAX] = "";
13365
13366 tmp = ast_strdupa(data);
13367
13368 if (!(vmu = find_or_create(context, box)))
13369 return -1;
13370
13371 populate_defaults(vmu);
13372
13373 stringp = tmp;
13374 if ((s = strsep(&stringp, ","))) {
13375 if (!ast_strlen_zero(s) && s[0] == '*') {
13376 ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
13377 "\n\tmust be reset in voicemail.conf.\n", box);
13378 }
13379 /* assign password regardless of validity to prevent NULL password from being assigned */
13380 ast_copy_string(vmu->password, s, sizeof(vmu->password));
13381 }
13382 if (stringp && (s = strsep(&stringp, ","))) {
13383 ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
13384 }
13385 if (stringp && (s = strsep(&stringp, ","))) {
13386 vmu->email = ast_strdup(s);
13387 }
13388 if (stringp && (s = strsep(&stringp, ","))) {
13389 ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
13390 }
13391 if (stringp) {
13392 apply_options(vmu, stringp);
13393 }
13394
13395 switch (vmu->passwordlocation) {
13396 case OPT_PWLOC_SPOOLDIR:
13397 snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
13398 read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
13399 }
13400
13401 snprintf(mailbox_full, MAX_VM_MAILBOX_LEN, "%s%s%s",
13402 box,
13403 ast_strlen_zero(context) ? "" : "@",
13404 context);
13405
13406 inboxcount2(mailbox_full, &urgent, &new, &old);
13407#ifdef IMAP_STORAGE
13408 imap_logout(mailbox_full);
13409#endif
13410 queue_mwi_event(NULL, mailbox_full, urgent, new, old);
13411
13412 return 0;
13413}
13414
13415#ifdef TEST_FRAMEWORK
13416AST_TEST_DEFINE(test_voicemail_vmuser)
13417{
13418 int res = 0;
13419 struct ast_vm_user *vmu;
13420 /* language parameter seems to only be used for display in manager action */
13421 static const char options_string[] = "attach=yes|attachfmt=wav49|"
13422 "serveremail=someguy@digium.com|fromstring=Voicemail System|tz=central|delete=yes|saycid=yes|"
13423 "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|leaveurgent=yes|"
13424 "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
13425 "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
13426 "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
13427 "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
13428 "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
13429 "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
13430#ifdef IMAP_STORAGE
13431 static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
13432 "imapfolder=INBOX|imapvmshareid=6000|imapserver=imapserver|imapport=1234|imapflags=flagged";
13433#endif
13434
13435 switch (cmd) {
13436 case TEST_INIT:
13437 info->name = "vmuser";
13438 info->category = "/apps/app_voicemail/";
13439 info->summary = "Vmuser unit test";
13440 info->description =
13441 "This tests passing all supported parameters to apply_options, the voicemail user config parser";
13442 return AST_TEST_NOT_RUN;
13443 case TEST_EXECUTE:
13444 break;
13445 }
13446
13447 if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
13448 return AST_TEST_NOT_RUN;
13449 }
13450 populate_defaults(vmu);
13452
13453 apply_options(vmu, options_string);
13454
13455 if (!ast_test_flag(vmu, VM_ATTACH)) {
13456 ast_test_status_update(test, "Parse failure for attach option\n");
13457 res = 1;
13458 }
13459 if (strcasecmp(vmu->attachfmt, "wav49")) {
13460 ast_test_status_update(test, "Parse failure for attachfmt option\n");
13461 res = 1;
13462 }
13463 if (strcasecmp(vmu->fromstring, "Voicemail System")) {
13464 ast_test_status_update(test, "Parse failure for fromstring option\n");
13465 res = 1;
13466 }
13467 if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
13468 ast_test_status_update(test, "Parse failure for serveremail option\n");
13469 res = 1;
13470 }
13471 if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
13472 ast_test_status_update(test, "Parse failure for emailsubject option\n");
13473 res = 1;
13474 }
13475 if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
13476 ast_test_status_update(test, "Parse failure for emailbody option\n");
13477 res = 1;
13478 }
13479 if (strcasecmp(vmu->zonetag, "central")) {
13480 ast_test_status_update(test, "Parse failure for tz option\n");
13481 res = 1;
13482 }
13483 if (!ast_test_flag(vmu, VM_DELETE)) {
13484 ast_test_status_update(test, "Parse failure for delete option\n");
13485 res = 1;
13486 }
13487 if (!ast_test_flag(vmu, VM_SAYCID)) {
13488 ast_test_status_update(test, "Parse failure for saycid option\n");
13489 res = 1;
13490 }
13491 if (!ast_test_flag(vmu, VM_SVMAIL)) {
13492 ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
13493 res = 1;
13494 }
13495 if (!ast_test_flag(vmu, VM_REVIEW)) {
13496 ast_test_status_update(test, "Parse failure for review option\n");
13497 res = 1;
13498 }
13499 if (!ast_test_flag(vmu, VM_MARK_URGENT)) {
13500 ast_test_status_update(test, "Parse failure for leaveurgent option\n");
13501 res = 1;
13502 }
13503 if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
13504 ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
13505 res = 1;
13506 }
13507 if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
13508 ast_test_status_update(test, "Parse failure for messagewrap option\n");
13509 res = 1;
13510 }
13511 if (!ast_test_flag(vmu, VM_OPERATOR)) {
13512 ast_test_status_update(test, "Parse failure for operator option\n");
13513 res = 1;
13514 }
13515 if (!ast_test_flag(vmu, VM_ENVELOPE)) {
13516 ast_test_status_update(test, "Parse failure for envelope option\n");
13517 res = 1;
13518 }
13519 if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
13520 ast_test_status_update(test, "Parse failure for moveheard option\n");
13521 res = 1;
13522 }
13523 if (!ast_test_flag(vmu, VM_SAYDURATION)) {
13524 ast_test_status_update(test, "Parse failure for sayduration option\n");
13525 res = 1;
13526 }
13527 if (vmu->saydurationm != 5) {
13528 ast_test_status_update(test, "Parse failure for saydurationm option\n");
13529 res = 1;
13530 }
13531 if (!ast_test_flag(vmu, VM_FORCENAME)) {
13532 ast_test_status_update(test, "Parse failure for forcename option\n");
13533 res = 1;
13534 }
13535 if (!ast_test_flag(vmu, VM_FORCEGREET)) {
13536 ast_test_status_update(test, "Parse failure for forcegreetings option\n");
13537 res = 1;
13538 }
13539 if (strcasecmp(vmu->callback, "somecontext")) {
13540 ast_test_status_update(test, "Parse failure for callbacks option\n");
13541 res = 1;
13542 }
13543 if (strcasecmp(vmu->dialout, "somecontext2")) {
13544 ast_test_status_update(test, "Parse failure for dialout option\n");
13545 res = 1;
13546 }
13547 if (strcasecmp(vmu->exit, "somecontext3")) {
13548 ast_test_status_update(test, "Parse failure for exitcontext option\n");
13549 res = 1;
13550 }
13551 if (vmu->minsecs != 10) {
13552 ast_test_status_update(test, "Parse failure for minsecs option\n");
13553 res = 1;
13554 }
13555 if (vmu->maxsecs != 100) {
13556 ast_test_status_update(test, "Parse failure for maxsecs option\n");
13557 res = 1;
13558 }
13559 if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
13560 ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
13561 res = 1;
13562 }
13563 if (vmu->maxdeletedmsg != 50) {
13564 ast_test_status_update(test, "Parse failure for backupdeleted option\n");
13565 res = 1;
13566 }
13567 if (vmu->volgain != 1.3) {
13568 ast_test_status_update(test, "Parse failure for volgain option\n");
13569 res = 1;
13570 }
13572 ast_test_status_update(test, "Parse failure for passwordlocation option\n");
13573 res = 1;
13574 }
13575#ifdef IMAP_STORAGE
13576 apply_options(vmu, option_string2);
13577
13578 if (strcasecmp(vmu->imapuser, "imapuser")) {
13579 ast_test_status_update(test, "Parse failure for imapuser option\n");
13580 res = 1;
13581 }
13582 if (strcasecmp(vmu->imappassword, "imappasswd")) {
13583 ast_test_status_update(test, "Parse failure for imappasswd option\n");
13584 res = 1;
13585 }
13586 if (strcasecmp(vmu->imapfolder, "INBOX")) {
13587 ast_test_status_update(test, "Parse failure for imapfolder option\n");
13588 res = 1;
13589 }
13590 if (strcasecmp(vmu->imapvmshareid, "6000")) {
13591 ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
13592 res = 1;
13593 }
13594 if (strcasecmp(vmu->imapserver, "imapserver")) {
13595 ast_test_status_update(test, "Parse failure for imapserver option\n");
13596 res = 1;
13597 }
13598 if (strcasecmp(vmu->imapport, "1234")) {
13599 ast_test_status_update(test, "Parse failure for imapport option\n");
13600 res = 1;
13601 }
13602 if (strcasecmp(vmu->imapflags, "flagged")) {
13603 ast_test_status_update(test, "Parse failure for imapflags option\n");
13604 res = 1;
13605 }
13606#endif
13607
13608 free_user(vmu);
13609 force_reload_config(); /* Restore original config */
13610 return res ? AST_TEST_FAIL : AST_TEST_PASS;
13611}
13612#endif
13613
13614static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
13615{
13616 struct ast_vm_user svm;
13617 struct ast_vm_user *vmu = NULL;
13618 char *parse;
13619 char *mailbox;
13620 char *context;
13621 int res = 0;
13622
13624 AST_APP_ARG(mailbox_context);
13625 AST_APP_ARG(attribute);
13626 AST_APP_ARG(folder);
13627 );
13628
13629 buf[0] = '\0';
13630
13631 if (ast_strlen_zero(args)) {
13632 ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
13633 return -1;
13634 }
13635
13636 parse = ast_strdupa(args);
13637 AST_STANDARD_APP_ARGS(arg, parse);
13638
13639 if (ast_strlen_zero(arg.mailbox_context)
13640 || ast_strlen_zero(arg.attribute)
13641 || separate_mailbox(ast_strdupa(arg.mailbox_context), &mailbox, &context)) {
13642 ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
13643 return -1;
13644 }
13645
13646 memset(&svm, 0, sizeof(svm));
13647 vmu = find_user(&svm, context, mailbox);
13648
13649 if (!strncasecmp(arg.attribute, "exists", 5)) {
13650 ast_copy_string(buf, vmu ? "1" : "0", len);
13651 free_user(vmu);
13652 return 0;
13653 }
13654
13655 if (vmu) {
13656 if (!strncasecmp(arg.attribute, "password", 8)) {
13658 } else if (!strncasecmp(arg.attribute, "fullname", 8)) {
13660 } else if (!strncasecmp(arg.attribute, "email", 5)) {
13661 ast_copy_string(buf, vmu->email, len);
13662 } else if (!strncasecmp(arg.attribute, "pager", 5)) {
13663 ast_copy_string(buf, vmu->pager, len);
13664 } else if (!strncasecmp(arg.attribute, "language", 8)) {
13666 } else if (!strncasecmp(arg.attribute, "locale", 6)) {
13667 ast_copy_string(buf, vmu->locale, len);
13668 } else if (!strncasecmp(arg.attribute, "tz", 2)) {
13670 } else if (!strncasecmp(arg.attribute, "count", 5)) {
13671 char *mailbox_id;
13672
13673 mailbox_id = ast_alloca(strlen(mailbox) + strlen(context) + 2);
13674 sprintf(mailbox_id, "%s@%s", mailbox, context);/* Safe */
13675
13676 /* If mbxfolder is empty messagecount will default to INBOX */
13677 res = messagecount(mailbox_id, arg.folder);
13678 if (res < 0) {
13679 ast_log(LOG_ERROR, "Unable to retrieve message count for mailbox %s\n", arg.mailbox_context);
13680 free_user(vmu);
13681 return -1;
13682 }
13683 snprintf(buf, len, "%d", res);
13684 } else {
13685 ast_log(LOG_ERROR, "Unknown attribute '%s' for VM_INFO\n", arg.attribute);
13686 free_user(vmu);
13687 return -1;
13688 }
13689 free_user(vmu);
13690 }
13691
13692 return 0;
13693}
13694
13696 .name = "VM_INFO",
13697 .read = acf_vm_info,
13698};
13699
13700static int vmauthenticate(struct ast_channel *chan, const char *data)
13701{
13702 char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
13703 struct ast_vm_user vmus = {{0}};
13704 char *options = NULL;
13705 int silent = 0, skipuser = 0;
13706 int res = -1;
13707
13708 if (data) {
13709 s = ast_strdupa(data);
13710 user = strsep(&s, ",");
13711 options = strsep(&s, ",");
13712 if (user) {
13713 s = user;
13714 user = strsep(&s, "@");
13715 context = strsep(&s, "");
13716 if (!ast_strlen_zero(user))
13717 skipuser++;
13719 }
13720 }
13721
13722 if (options) {
13723 silent = (strchr(options, 's')) != NULL;
13724 }
13725
13726 if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
13727 pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
13728 pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
13729 ast_play_and_wait(chan, "auth-thankyou");
13730 res = 0;
13731 } else if (mailbox[0] == '*') {
13732 /* user entered '*' */
13733 if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
13734 res = 0; /* prevent hangup */
13735 }
13736 }
13737
13738 return res;
13739}
13740
13741static char *show_users_realtime(int fd, const char *context)
13742{
13743 struct ast_config *cfg;
13744 const char *cat = NULL;
13745
13746 if (!(cfg = ast_load_realtime_multientry("voicemail",
13747 "context", context, SENTINEL))) {
13748 return CLI_FAILURE;
13749 }
13750
13751 ast_cli(fd,
13752 "\n"
13753 "=============================================================\n"
13754 "=== Configured Voicemail Users ==============================\n"
13755 "=============================================================\n"
13756 "===\n");
13757
13758 while ((cat = ast_category_browse(cfg, cat))) {
13759 struct ast_variable *var = NULL;
13760 ast_cli(fd,
13761 "=== Mailbox ...\n"
13762 "===\n");
13763 for (var = ast_variable_browse(cfg, cat); var; var = var->next)
13764 ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
13765 ast_cli(fd,
13766 "===\n"
13767 "=== ---------------------------------------------------------\n"
13768 "===\n");
13769 }
13770
13771 ast_cli(fd,
13772 "=============================================================\n"
13773 "\n");
13774
13775 ast_config_destroy(cfg);
13776
13777 return CLI_SUCCESS;
13778}
13779
13780static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
13781{
13782 int which = 0;
13783 int wordlen;
13784 struct ast_vm_user *vmu;
13785 const char *context = "";
13786 char *ret;
13787
13788 /* 0 - voicemail; 1 - show; 2 - users; 3 - for; 4 - <context> */
13789 if (pos > 4)
13790 return NULL;
13791 wordlen = strlen(word);
13793 AST_LIST_TRAVERSE(&users, vmu, list) {
13794 if (!strncasecmp(word, vmu->context, wordlen)) {
13795 if (context && strcmp(context, vmu->context) && ++which > state) {
13796 ret = ast_strdup(vmu->context);
13798 return ret;
13799 }
13800 /* ignore repeated contexts ? */
13801 context = vmu->context;
13802 }
13803 }
13805 return NULL;
13806}
13807
13808/*! \brief Show a list of voicemail users in the CLI */
13809static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13810{
13811 struct ast_vm_user *vmu;
13812#define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
13813 const char *context = NULL;
13814 int users_counter = 0;
13815
13816 switch (cmd) {
13817 case CLI_INIT:
13818 e->command = "voicemail show users [for]";
13819 e->usage =
13820 "Usage: voicemail show users [for <context>]\n"
13821 " Lists all mailboxes currently set up\n";
13822 return NULL;
13823 case CLI_GENERATE:
13824 return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
13825 }
13826
13827 if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
13828 return CLI_SHOWUSAGE;
13829 if (a->argc == 5) {
13830 if (strcmp(a->argv[3],"for"))
13831 return CLI_SHOWUSAGE;
13832 context = a->argv[4];
13833 }
13834
13835 if (ast_check_realtime("voicemail")) {
13836 if (!context) {
13837 ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
13838 return CLI_SHOWUSAGE;
13839 }
13840 return show_users_realtime(a->fd, context);
13841 }
13842
13844 if (AST_LIST_EMPTY(&users)) {
13845 ast_cli(a->fd, "There are no voicemail users currently defined\n");
13847 return CLI_FAILURE;
13848 }
13849 if (!context) {
13850 ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
13851 } else {
13852 int count = 0;
13853 AST_LIST_TRAVERSE(&users, vmu, list) {
13854 if (!strcmp(context, vmu->context)) {
13855 count++;
13856 break;
13857 }
13858 }
13859 if (count) {
13860 ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
13861 } else {
13862 ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
13864 return CLI_FAILURE;
13865 }
13866 }
13867 AST_LIST_TRAVERSE(&users, vmu, list) {
13868 int newmsgs = 0, oldmsgs = 0;
13869 char count[12], tmp[256] = "";
13870
13871 if (!context || !strcmp(context, vmu->context)) {
13872 snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
13873 inboxcount(tmp, &newmsgs, &oldmsgs);
13874 snprintf(count, sizeof(count), "%d", newmsgs);
13875 ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
13876 users_counter++;
13877 }
13878 }
13880 ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
13881 return CLI_SUCCESS;
13882}
13883
13884/*! \brief Show a list of voicemail zones in the CLI */
13885static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13886{
13887 struct vm_zone *zone;
13888#define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
13889 char *res = CLI_SUCCESS;
13890
13891 switch (cmd) {
13892 case CLI_INIT:
13893 e->command = "voicemail show zones";
13894 e->usage =
13895 "Usage: voicemail show zones\n"
13896 " Lists zone message formats\n";
13897 return NULL;
13898 case CLI_GENERATE:
13899 return NULL;
13900 }
13901
13902 if (a->argc != 3)
13903 return CLI_SHOWUSAGE;
13904
13906 if (!AST_LIST_EMPTY(&zones)) {
13907 ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
13908 AST_LIST_TRAVERSE(&zones, zone, list) {
13909 ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
13910 }
13911 } else {
13912 ast_cli(a->fd, "There are no voicemail zones currently defined\n");
13913 res = CLI_FAILURE;
13914 }
13916
13917 return res;
13918}
13919
13920/*! \brief Show a list of voicemail zones in the CLI */
13921static char *handle_voicemail_show_aliases(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13922{
13923 struct ao2_iterator aliases;
13924 struct alias_mailbox_mapping *mapping;
13925#define ALIASES_OUTPUT_FORMAT "%-32s %-32s\n"
13926 char *res = CLI_SUCCESS;
13927
13928 switch (cmd) {
13929 case CLI_INIT:
13930 e->command = "voicemail show aliases";
13931 e->usage =
13932 "Usage: voicemail show aliases\n"
13933 " Lists mailbox aliases\n";
13934 return NULL;
13935 case CLI_GENERATE:
13936 return NULL;
13937 }
13938
13939 if (a->argc != 3)
13940 return CLI_SHOWUSAGE;
13941
13943 ast_cli(a->fd, "Aliases are not enabled\n");
13944 return res;
13945 }
13946
13947 ast_cli(a->fd, "Aliases context: %s\n", aliasescontext);
13948 ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, "Alias", "Mailbox");
13949
13951 while ((mapping = ao2_iterator_next(&aliases))) {
13952 ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, mapping->alias, mapping->mailbox);
13953 ao2_ref(mapping, -1);
13954 }
13956
13957 return res;
13958}
13959
13960/*! \brief Reload voicemail configuration from the CLI */
13961static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13962{
13963 switch (cmd) {
13964 case CLI_INIT:
13965 e->command = "voicemail reload";
13966 e->usage =
13967 "Usage: voicemail reload\n"
13968 " Reload voicemail configuration\n";
13969 return NULL;
13970 case CLI_GENERATE:
13971 return NULL;
13972 }
13973
13974 if (a->argc != 2)
13975 return CLI_SHOWUSAGE;
13976
13977 ast_cli(a->fd, "Reloading voicemail configuration...\n");
13978 load_config(1);
13979
13980 return CLI_SUCCESS;
13981}
13982
13983static struct ast_cli_entry cli_voicemail[] = {
13984 AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
13985 AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
13986 AST_CLI_DEFINE(handle_voicemail_show_aliases, "List mailbox aliases"),
13987 AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
13988 AST_CLI_DEFINE(handle_voicemail_show_mailbox, "Display a mailbox's content details"),
13989 AST_CLI_DEFINE(handle_voicemail_forward_message, "Forward message to another folder"),
13990 AST_CLI_DEFINE(handle_voicemail_move_message, "Move message to another folder"),
13992};
13993
13994static int poll_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
13995{
13996 int new = 0, old = 0, urgent = 0;
13997
13998 if (!mwi_state) {
13999 /* This should only occur due to allocation failure of a default mwi state object */
14000 return 0;
14001 }
14002
14003 inboxcount2(mwi_state->uniqueid, &urgent, &new, &old);
14004
14005#ifdef IMAP_STORAGE
14006 if (imap_poll_logout) {
14007 imap_logout(mwi_state->uniqueid);
14008 }
14009#endif
14010
14011 if (urgent != mwi_state->urgent_msgs || new != mwi_state->new_msgs || old != mwi_state->old_msgs) {
14012 queue_mwi_event(NULL, mwi_state->uniqueid, urgent, new, old);
14013 run_externnotify(NULL, mwi_state->uniqueid, NULL);
14014 }
14015
14016 return 0;
14017}
14018
14019static void *mb_poll_thread(void *data)
14020{
14021 while (poll_thread_run) {
14022 struct timespec ts = { 0, };
14023 struct timeval wait;
14024
14026
14027 if (!poll_thread_run) {
14028 break;
14029 }
14030
14032 ts.tv_sec = wait.tv_sec;
14033 ts.tv_nsec = wait.tv_usec * 1000;
14034
14038 }
14039
14040 return NULL;
14041}
14042
14043#ifdef IMAP_STORAGE
14044static void imap_logout(const char *mailbox_id)
14045{
14046 char *context;
14047 char *mailbox;
14048 struct ast_vm_user vmus;
14049 RAII_VAR(struct ast_vm_user *, vmu, NULL, free_user);
14050 struct vm_state *vms = NULL;
14051
14052 if (ast_strlen_zero(mailbox_id)
14053 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
14054 return;
14055 }
14056
14057 memset(&vmus, 0, sizeof(vmus));
14058
14059 if (!(vmu = find_user(&vmus, context, mailbox)) || vmu->imapuser[0] == '\0') {
14060 return;
14061 }
14062
14063 vms = get_vm_state_by_imapuser(vmu->imapuser, 0);
14064 if (!vms) {
14065 vms = get_vm_state_by_mailbox(mailbox, context, 0);
14066 }
14067 if (!vms) {
14068 return;
14069 }
14070
14071 ast_mutex_lock(&vms->lock);
14072 vms->mailstream = mail_close(vms->mailstream);
14073 ast_mutex_unlock(&vms->lock);
14074
14075 vmstate_delete(vms);
14076}
14077
14078static int imap_close_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
14079{
14080 if (mwi_state && !ast_strlen_zero(mwi_state->uniqueid)) {
14081 imap_logout(mwi_state->uniqueid);
14082 }
14083
14084 return 0;
14085}
14086
14087#endif
14088
14089static int mwi_handle_unsubscribe2(void *data)
14090{
14091 struct ast_mwi_state *mwi_state = data;
14092
14093 /*
14094 * Go ahead and clear the implicit MWI publisher here to avoid a leak. If a backing
14095 * configuration is available it'll re-initialize (reset the cached state) on its
14096 * next publish.
14097 */
14099
14100#ifdef IMAP_STORAGE
14101 imap_close_subscribed_mailbox(mwi_state, NULL);
14102#endif
14103
14104 ao2_ref(mwi_state, -1);
14105 return 0;
14106}
14107
14108static void mwi_handle_unsubscribe(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
14119static int mwi_handle_subscribe2(void *data)
14120{
14122 ao2_ref(data, -1);
14123 return 0;
14124}
14125
14126static void mwi_handle_subscribe(const char *id, struct ast_mwi_subscriber *sub)
14127{
14128 void *data = ast_mwi_subscriber_data(sub);
14129
14130 /* Don't bump data's reference. We'll just use the one returned above */
14132 /* A reference was returned for data when retrieving, so remove it on error */
14133 ao2_ref(data, -1);
14134 }
14135}
14136
14139 .on_unsubscribe = mwi_handle_unsubscribe,
14140};
14141
14142static void start_poll_thread(void)
14143{
14144 int errcode;
14146
14147 poll_thread_run = 1;
14148
14149 if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
14150 ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
14151 }
14152}
14153
14154static void stop_poll_thread(void)
14155{
14156 poll_thread_run = 0;
14157
14161
14162 pthread_join(poll_thread, NULL);
14164
14166}
14167
14168/*!
14169 * \brief Append vmu info string into given astman with event_name.
14170 * \return 0 failed. 1 otherwise.
14171*/
14173 struct mansession *s,
14174 struct ast_vm_user *vmu,
14175 const char* event_name,
14176 const char* actionid
14177 )
14178{
14179 int new;
14180 int old;
14181 char *mailbox;
14182 int ret;
14183
14184 if((s == NULL) || (vmu == NULL) || (event_name == NULL) || (actionid == NULL)) {
14185 ast_log(LOG_ERROR, "Wrong input parameter.");
14186 return 0;
14187 }
14188
14189 /* create mailbox string */
14190 if (!ast_strlen_zero(vmu->context)) {
14191 ret = ast_asprintf(&mailbox, "%s@%s", vmu->mailbox, vmu->context);
14192 } else {
14193 ret = ast_asprintf(&mailbox, "%s", vmu->mailbox);
14194 }
14195 if (ret == -1) {
14196 ast_log(LOG_ERROR, "Could not create mailbox string. err[%s]\n", strerror(errno));
14197 return 0;
14198 }
14199
14200 /* get mailbox count */
14201 ret = inboxcount(mailbox, &new, &old);
14202 ast_free(mailbox);
14203 if (ret == -1) {
14204 ast_log(LOG_ERROR, "Could not get mailbox count. user[%s], context[%s]\n",
14205 vmu->mailbox ?: "", vmu->context ?: "");
14206 return 0;
14207 }
14208
14209 astman_append(s,
14210 "Event: %s\r\n"
14211 "%s"
14212 "VMContext: %s\r\n"
14213 "VoiceMailbox: %s\r\n"
14214 "Fullname: %s\r\n"
14215 "Email: %s\r\n"
14216 "Pager: %s\r\n"
14217 "ServerEmail: %s\r\n"
14218 "FromString: %s\r\n"
14219 "MailCommand: %s\r\n"
14220 "Language: %s\r\n"
14221 "TimeZone: %s\r\n"
14222 "Callback: %s\r\n"
14223 "Dialout: %s\r\n"
14224 "UniqueID: %s\r\n"
14225 "ExitContext: %s\r\n"
14226 "SayDurationMinimum: %d\r\n"
14227 "SayEnvelope: %s\r\n"
14228 "SayCID: %s\r\n"
14229 "AttachMessage: %s\r\n"
14230 "AttachmentFormat: %s\r\n"
14231 "DeleteMessage: %s\r\n"
14232 "VolumeGain: %.2f\r\n"
14233 "CanReview: %s\r\n"
14234 "CanMarkUrgent: %s\r\n"
14235 "CallOperator: %s\r\n"
14236 "MaxMessageCount: %d\r\n"
14237 "MaxMessageLength: %d\r\n"
14238 "NewMessageCount: %d\r\n"
14239 "OldMessageCount: %d\r\n"
14240#ifdef IMAP_STORAGE
14241 "IMAPUser: %s\r\n"
14242 "IMAPServer: %s\r\n"
14243 "IMAPPort: %s\r\n"
14244 "IMAPFlags: %s\r\n"
14245#endif
14246 "\r\n",
14247
14248 event_name,
14249 actionid,
14250 vmu->context,
14251 vmu->mailbox,
14252 vmu->fullname,
14253 vmu->email,
14254 vmu->pager,
14257 mailcmd,
14258 vmu->language,
14259 vmu->zonetag,
14260 vmu->callback,
14261 vmu->dialout,
14262 vmu->uniqueid,
14263 vmu->exit,
14264 vmu->saydurationm,
14265 ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
14266 ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
14267 ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
14268 vmu->attachfmt,
14269 ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
14270 vmu->volgain,
14271 ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
14272 ast_test_flag(vmu, VM_MARK_URGENT) ? "Yes" : "No",
14273 ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
14274 vmu->maxmsg,
14275 vmu->maxsecs,
14276 new,
14277 old
14278#ifdef IMAP_STORAGE
14279 ,
14280 vmu->imapuser,
14281 vmu->imapserver,
14282 vmu->imapport,
14283 vmu->imapflags
14284#endif
14285 );
14286
14287 return 1;
14288
14289}
14290
14291
14292/*!
14293 * \brief Append vmbox info string into given astman with event_name.
14294 * \return 0 if unable to append details, 1 otherwise.
14295*/
14297 struct mansession *s,
14298 const struct message *m,
14299 struct ast_vm_user *vmu,
14300 const char* event_name,
14301 const char* actionid)
14302{
14303 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
14304 struct ast_vm_msg_snapshot *msg;
14305 int nummessages = 0;
14306 int i;
14307
14308 /* Take a snapshot of the mailbox */
14309 mailbox_snapshot = ast_vm_mailbox_snapshot_create(vmu->mailbox, vmu->context, NULL, 0, AST_VM_SNAPSHOT_SORT_BY_ID, 0);
14310 if (!mailbox_snapshot) {
14311 ast_log(LOG_ERROR, "Could not append voicemail box info for box %s@%s.",
14312 vmu->mailbox, vmu->context);
14313 return 0;
14314 }
14315
14316 astman_send_listack(s, m, "Voicemail box detail will follow", "start");
14317 /* walk through each folder's contents and append info for each message */
14318 for (i = 0; i < mailbox_snapshot->folders; i++) {
14319 AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
14320 astman_append(s,
14321 "Event: %s\r\n"
14322 "%s"
14323 "Folder: %s\r\n"
14324 "CallerID: %s\r\n"
14325 "Date: %s\r\n"
14326 "Duration: %s\r\n"
14327 "Flag: %s\r\n"
14328 "ID: %s\r\n"
14329 "\r\n",
14330 event_name,
14331 actionid,
14332 msg->folder_name,
14333 msg->callerid,
14334 msg->origdate,
14335 msg->duration,
14336 msg->flag,
14337 msg->msg_id
14338 );
14339 nummessages++;
14340 }
14341 }
14342
14343 /* done, destroy. */
14344 mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
14345 astman_send_list_complete_start(s, m, "VoicemailBoxDetailComplete", nummessages);
14347
14348 return 1;
14349}
14350
14351static int manager_match_mailbox(struct ast_mwi_state *mwi_state, void *data)
14352{
14353 const char *context = astman_get_header(data, "Context");
14354 const char *mailbox = astman_get_header(data, "Mailbox");
14355 const char *at;
14356
14357 if (!ast_strlen_zero(mwi_state->uniqueid)) {
14358 if (
14359 /* First case: everything matches */
14360 (ast_strlen_zero(context) && ast_strlen_zero(mailbox)) ||
14361 /* Second case: match the mailbox only */
14362 (ast_strlen_zero(context) && !ast_strlen_zero(mailbox) &&
14363 (at = strchr(mwi_state->uniqueid, '@')) &&
14364 strncmp(mailbox, mwi_state->uniqueid, at - mwi_state->uniqueid) == 0) ||
14365 /* Third case: match the context only */
14366 (!ast_strlen_zero(context) && ast_strlen_zero(mailbox) &&
14367 (at = strchr(mwi_state->uniqueid, '@')) &&
14368 strcmp(context, at + 1) == 0) ||
14369 /* Final case: match an exact specified mailbox */
14370 (!ast_strlen_zero(context) && !ast_strlen_zero(mailbox) &&
14371 (at = strchr(mwi_state->uniqueid, '@')) &&
14372 strncmp(mailbox, mwi_state->uniqueid, at - mwi_state->uniqueid) == 0 &&
14373 strcmp(context, at + 1) == 0)
14374 ) {
14375 poll_subscribed_mailbox(mwi_state, NULL);
14376 }
14377 }
14378
14379 return 0;
14380}
14381
14382static int manager_voicemail_refresh(struct mansession *s, const struct message *m)
14383{
14385 astman_send_ack(s, m, "Refresh sent");
14386 return RESULT_SUCCESS;
14387}
14388
14389static int manager_status_voicemail_user(struct mansession *s, const struct message *m)
14390{
14391 struct ast_vm_user *vmu = NULL;
14392 const char *id = astman_get_header(m, "ActionID");
14393 char actionid[128];
14394 struct ast_vm_user svm;
14395 int ret;
14396
14397 const char *context = astman_get_header(m, "Context");
14398 const char *mailbox = astman_get_header(m, "Mailbox");
14399
14401 astman_send_error(s, m, "Need 'Context' and 'Mailbox' parameters.");
14402 return RESULT_SUCCESS;
14403 }
14404
14405 actionid[0] = '\0';
14406 if (!ast_strlen_zero(id)) {
14407 snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14408 }
14409
14410 /* find user */
14411 memset(&svm, 0, sizeof(svm));
14412 vmu = find_user(&svm, context, mailbox);
14413 if (!vmu) {
14414 /* could not find it */
14415 astman_send_ack(s, m, "There is no voicemail user of the given info.");
14416 return RESULT_SUCCESS;
14417 }
14418
14419 astman_send_listack(s, m, "Voicemail user detail will follow", "start");
14420
14421 /* append vmu info event */
14422 ret = append_vmu_info_astman(s, vmu, "VoicemailUserDetail", actionid);
14423 free_user(vmu);
14424 if(ret == 0) {
14425 ast_log(LOG_ERROR, "Could not append voicemail user info.");
14426 }
14427
14428 astman_send_list_complete_start(s, m, "VoicemailUserDetailComplete", 1);
14430
14431 return RESULT_SUCCESS;
14432}
14433
14434/*! \brief Manager list voicemail users command */
14435static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
14436{
14437 struct ast_vm_user *vmu = NULL;
14438 const char *id = astman_get_header(m, "ActionID");
14439 char actionid[128];
14440 int num_users = 0;
14441 int ret;
14442
14443 actionid[0] = '\0';
14444 if (!ast_strlen_zero(id)) {
14445 snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14446 }
14447
14449
14450 if (AST_LIST_EMPTY(&users)) {
14451 astman_send_ack(s, m, "There are no voicemail users currently defined.");
14453 return RESULT_SUCCESS;
14454 }
14455
14456 astman_send_listack(s, m, "Voicemail user list will follow", "start");
14457
14458 AST_LIST_TRAVERSE(&users, vmu, list) {
14459 /* append vmu info event */
14460 ret = append_vmu_info_astman(s, vmu, "VoicemailUserEntry", actionid);
14461 if(ret == 0) {
14462 ast_log(LOG_ERROR, "Could not append voicemail user info.");
14463 continue;
14464 }
14465 ++num_users;
14466 }
14467
14468 astman_send_list_complete_start(s, m, "VoicemailUserEntryComplete", num_users);
14470
14472
14473 return RESULT_SUCCESS;
14474}
14475
14476static int manager_get_mailbox_summary(struct mansession *s, const struct message *m)
14477{
14478 struct ast_vm_user *vmu = NULL;
14479 const char *id = astman_get_header(m, "ActionID");
14480 char actionid[128];
14481 struct ast_vm_user svm;
14482
14483 const char *context = astman_get_header(m, "Context");
14484 const char *mailbox = astman_get_header(m, "Mailbox");
14485
14487 astman_send_error(s, m, "Need 'Context' and 'Mailbox' parameters.");
14488 return 0;
14489 }
14490
14491 actionid[0] = '\0';
14492 if (!ast_strlen_zero(id)) {
14493 snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14494 }
14495
14496 /* find user */
14497 memset(&svm, 0, sizeof(svm));
14498 vmu = find_user(&svm, context, mailbox);
14499 if (!vmu) {
14500 /* could not find it */
14501 astman_send_ack(s, m, "There is no voicemail user matching the given user.");
14502 return 0;
14503 }
14504
14505 /* Append the mailbox details */
14506 if (!append_vmbox_info_astman(s, m, vmu, "VoicemailBoxDetail", actionid)) {
14507 astman_send_error(s, m, "Unable to get mailbox info for the given user.");
14508 }
14509
14510 free_user(vmu);
14511 return 0;
14512}
14513
14514static int manager_voicemail_move(struct mansession *s, const struct message *m)
14515{
14516 const char *mailbox = astman_get_header(m, "Mailbox");
14517 const char *context = astman_get_header(m, "Context");
14518 const char *from_folder = astman_get_header(m, "Folder");
14519 const char *id[] = { astman_get_header(m, "ID") };
14520 const char *to_folder = astman_get_header(m, "ToFolder");
14521
14522 if (ast_strlen_zero(mailbox)) {
14523 astman_send_error(s, m, "Mailbox not specified, required");
14524 return 0;
14525 }
14526 if (ast_strlen_zero(context)) {
14527 astman_send_error(s, m, "Context not specified, required");
14528 return 0;
14529 }
14530 if (ast_strlen_zero(from_folder)) {
14531 astman_send_error(s, m, "Folder not specified, required");
14532 return 0;
14533 }
14534 if (ast_strlen_zero(id[0])) {
14535 astman_send_error(s, m, "ID not specified, required");
14536 return 0;
14537 }
14538 if (ast_strlen_zero(to_folder)) {
14539 astman_send_error(s, m, "ToFolder not specified, required");
14540 return 0;
14541 }
14542
14543 if (vm_msg_move(mailbox, context, 1, from_folder, id, to_folder)) {
14544 astman_send_ack(s, m, "Message move failed\n");
14545 } else {
14546 astman_send_ack(s, m, "Message move successful\n");
14547 }
14548
14549 return 0;
14550}
14551
14552static int manager_voicemail_remove(struct mansession *s, const struct message *m)
14553{
14554 const char *mailbox = astman_get_header(m, "Mailbox");
14555 const char *context = astman_get_header(m, "Context");
14556 const char *folder = astman_get_header(m, "Folder");
14557 const char *id[] = { astman_get_header(m, "ID") };
14558
14559 if (ast_strlen_zero(mailbox)) {
14560 astman_send_error(s, m, "Mailbox not specified, required");
14561 return 0;
14562 }
14563 if (ast_strlen_zero(context)) {
14564 astman_send_error(s, m, "Context not specified, required");
14565 return 0;
14566 }
14567 if (ast_strlen_zero(folder)) {
14568 astman_send_error(s, m, "Folder not specified, required");
14569 return 0;
14570 }
14571 if (ast_strlen_zero(id[0])) {
14572 astman_send_error(s, m, "ID not specified, required");
14573 return 0;
14574 }
14575
14576 if (vm_msg_remove(mailbox, context, 1, folder, id)) {
14577 astman_send_ack(s, m, "Message remove failed\n");
14578 } else {
14579 astman_send_ack(s, m, "Message remove successful\n");
14580 }
14581
14582 return 0;
14583}
14584
14585static int manager_voicemail_forward(struct mansession *s, const struct message *m)
14586{
14587 const char *from_mailbox = astman_get_header(m, "Mailbox");
14588 const char *from_context = astman_get_header(m, "Context");
14589 const char *from_folder = astman_get_header(m, "Folder");
14590 const char *id[] = { astman_get_header(m, "ID") };
14591 const char *to_mailbox = astman_get_header(m, "ToMailbox");
14592 const char *to_context = astman_get_header(m, "ToContext");
14593 const char *to_folder = astman_get_header(m, "ToFolder");
14594
14595 if (ast_strlen_zero(from_mailbox)) {
14596 astman_send_error(s, m, "Mailbox not specified, required");
14597 return 0;
14598 }
14599 if (ast_strlen_zero(from_context)) {
14600 astman_send_error(s, m, "Context not specified, required");
14601 return 0;
14602 }
14603 if (ast_strlen_zero(from_folder)) {
14604 astman_send_error(s, m, "Folder not specified, required");
14605 return 0;
14606 }
14607 if (ast_strlen_zero(id[0])) {
14608 astman_send_error(s, m, "ID not specified, required");
14609 return 0;
14610 }
14611 if (ast_strlen_zero(to_mailbox)) {
14612 astman_send_error(s, m, "ToMailbox not specified, required");
14613 return 0;
14614 }
14615 if (ast_strlen_zero(to_context)) {
14616 astman_send_error(s, m, "ToContext not specified, required");
14617 return 0;
14618 }
14619 if (ast_strlen_zero(to_folder)) {
14620 astman_send_error(s, m, "ToFolder not specified, required");
14621 return 0;
14622 }
14623
14624 if (vm_msg_forward(from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder, 1, id, 0)) {
14625 astman_send_ack(s, m, "Message forward failed\n");
14626 } else {
14627 astman_send_ack(s, m, "Message forward successful\n");
14628 }
14629
14630 return 0;
14631}
14632
14633/*! \brief Free the users structure. */
14634static void free_vm_users(void)
14635{
14636 struct ast_vm_user *current;
14638 while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
14641 }
14643}
14644
14645/*! \brief Free the zones structure. */
14646static void free_vm_zones(void)
14647{
14648 struct vm_zone *zcur;
14650 while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
14651 free_zone(zcur);
14653}
14654
14655static const char *substitute_escapes(const char *value)
14656{
14657 char *current;
14658
14659 /* Add 16 for fudge factor */
14660 struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
14661
14663
14664 /* Substitute strings \r, \n, and \t into the appropriate characters */
14665 for (current = (char *) value; *current; current++) {
14666 if (*current == '\\') {
14667 current++;
14668 if (!*current) {
14669 ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
14670 break;
14671 }
14672 switch (*current) {
14673 case '\\':
14674 ast_str_append(&str, 0, "\\");
14675 break;
14676 case 'r':
14677 ast_str_append(&str, 0, "\r");
14678 break;
14679 case 'n':
14680#ifdef IMAP_STORAGE
14681 if (!str->used || str->str[str->used - 1] != '\r') {
14682 ast_str_append(&str, 0, "\r");
14683 }
14684#endif
14685 ast_str_append(&str, 0, "\n");
14686 break;
14687 case 't':
14688 ast_str_append(&str, 0, "\t");
14689 break;
14690 default:
14691 ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
14692 break;
14693 }
14694 } else {
14695 ast_str_append(&str, 0, "%c", *current);
14696 }
14697 }
14698
14699 return ast_str_buffer(str);
14700}
14701
14702static int load_config_force(int reload, int force)
14703{
14704 struct ast_config *cfg;
14705 struct ast_flags config_flags = { reload && !force ? CONFIG_FLAG_FILEUNCHANGED : 0 };
14706 int res;
14707
14708 ast_unload_realtime("voicemail");
14709 ast_unload_realtime("voicemail_data");
14710
14711 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
14713 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
14714 ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
14715 return 0;
14716 }
14717 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
14718 ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
14719 return 0;
14720 } else {
14722 }
14723
14724 res = actual_load_config(reload, cfg);
14725
14726 ast_config_destroy(cfg);
14727
14728 return res;
14729}
14730
14731static int load_config(int reload)
14732{
14733 return load_config_force(reload, 0);
14734}
14735
14736#ifdef TEST_FRAMEWORK
14737static int load_config_from_memory(int reload, struct ast_config *cfg)
14738{
14739 ast_unload_realtime("voicemail");
14740 ast_unload_realtime("voicemail_data");
14741 return actual_load_config(reload, cfg);
14742}
14743#endif
14744
14745static struct alias_mailbox_mapping *alias_mailbox_mapping_create(const char *alias, const char *mailbox)
14746{
14747 struct alias_mailbox_mapping *mapping;
14748 size_t from_len = strlen(alias) + 1;
14749 size_t to_len = strlen(mailbox) + 1;
14750
14751 mapping = ao2_alloc(sizeof(*mapping) + from_len + to_len, NULL);
14752 if (!mapping) {
14753 return NULL;
14754 }
14755 mapping->alias = mapping->buf;
14756 mapping->mailbox = mapping->buf + from_len;
14757 ast_copy_string(mapping->alias, alias, from_len); /* Safe */
14758 ast_copy_string(mapping->mailbox, mailbox, to_len); /* Safe */
14759
14760 return mapping;
14761}
14762
14763static void load_aliases(struct ast_config *cfg)
14764{
14765 struct ast_variable *var;
14766
14768 return;
14769 }
14771 while (var) {
14772 struct alias_mailbox_mapping *mapping = alias_mailbox_mapping_create(var->name, var->value);
14773 if (mapping) {
14776 ao2_ref(mapping, -1);
14777 }
14778 var = var->next;
14779 }
14780}
14781
14782static void load_zonemessages(struct ast_config *cfg)
14783{
14784 struct ast_variable *var;
14785
14786 var = ast_variable_browse(cfg, "zonemessages");
14787 while (var) {
14788 if (var->value) {
14789 struct vm_zone *z;
14790 char *msg_format, *tzone;
14791 char storage[strlen(var->value) + 1];
14792
14793 z = ast_malloc(sizeof(*z));
14794 if (!z) {
14795 return;
14796 }
14797
14798 strcpy(storage, var->value); /* safe */
14799 msg_format = storage;
14800 tzone = strsep(&msg_format, "|,");
14801 if (msg_format) {
14802 ast_copy_string(z->name, var->name, sizeof(z->name));
14803 ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
14808 } else {
14809 ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
14810 ast_free(z);
14811 }
14812 }
14813 var = var->next;
14814 }
14815}
14816
14817static void load_users(struct ast_config *cfg)
14818{
14819 struct ast_variable *var;
14820 char *cat = NULL;
14821
14822 while ((cat = ast_category_browse(cfg, cat))) {
14823 if (strcasecmp(cat, "general") == 0
14824 || strcasecmp(cat, aliasescontext) == 0
14825 || strcasecmp(cat, "zonemessages") == 0) {
14826 continue;
14827 }
14828
14829 var = ast_variable_browse(cfg, cat);
14830 while (var) {
14831 append_mailbox(cat, var->name, var->value);
14832 var = var->next;
14833 }
14834 }
14835}
14836
14837static int actual_load_config(int reload, struct ast_config *cfg)
14838{
14839 const char *val;
14840 char *q, *stringp, *tmp;
14841 int x;
14842 unsigned int tmpadsi[4];
14843 long tps_queue_low;
14844 long tps_queue_high;
14845
14846#ifdef IMAP_STORAGE
14847 ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
14848#endif
14849 /* set audio control prompts */
14856
14857#ifdef IMAP_STORAGE
14858 ast_mwi_state_callback_all(imap_close_subscribed_mailbox, NULL);
14859#endif
14860
14861 /* Free all the users structure */
14862 free_vm_users();
14863
14864 /* Free all the zones structure */
14865 free_vm_zones();
14866
14867 /* Remove all aliases */
14870
14872
14873 memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
14874 memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
14875
14876 if (cfg) {
14877 /* General settings */
14878 aliasescontext[0] = '\0';
14879 val = ast_variable_retrieve(cfg, "general", "aliasescontext");
14881
14882 /* Attach voice message to mail message ? */
14883 if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
14884 val = "yes";
14886
14887 if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
14888 val = "no";
14890
14891 volgain = 0.0;
14892 if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
14893 sscanf(val, "%30lf", &volgain);
14894
14895#ifdef ODBC_STORAGE
14896 strcpy(odbc_database, "asterisk");
14897 if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
14898 ast_copy_string(odbc_database, val, sizeof(odbc_database));
14899 }
14900
14901 strcpy(odbc_table, "voicemessages");
14902 if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
14903 ast_copy_string(odbc_table, val, sizeof(odbc_table));
14904 }
14905 odbc_table_len = strlen(odbc_table);
14906
14908 if (!(val = ast_variable_retrieve(cfg, "general", "odbc_audio_on_disk")))
14909 val = "no";
14911
14912#endif
14913 /* Mail command */
14914 strcpy(mailcmd, SENDMAIL);
14915 if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
14916 ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
14917
14918 maxsilence = 0;
14919 if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
14920 maxsilence = atoi(val);
14921 if (maxsilence > 0)
14922 maxsilence *= 1000;
14923 }
14924
14925 if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
14926 maxmsg = MAXMSG;
14927 } else {
14928 maxmsg = atoi(val);
14929 if (maxmsg < 0) {
14930 ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
14931 maxmsg = MAXMSG;
14932 } else if (maxmsg > MAXMSGLIMIT) {
14933 ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
14935 }
14936 }
14937
14938 if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
14939 maxdeletedmsg = 0;
14940 } else {
14941 if (sscanf(val, "%30d", &x) == 1)
14942 maxdeletedmsg = x;
14943 else if (ast_true(val))
14945 else
14946 maxdeletedmsg = 0;
14947
14948 if (maxdeletedmsg < 0) {
14949 ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
14951 } else if (maxdeletedmsg > MAXMSGLIMIT) {
14952 ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
14954 }
14955 }
14956
14957 /* Load date format config for voicemail mail */
14958 if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
14960 }
14961
14962 /* Load date format config for voicemail pager mail */
14963 if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
14965 }
14966
14967 /* External password changing command */
14968 if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
14971 } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
14974 }
14975
14976 /* External password validation command */
14977 if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
14979 ast_debug(1, "found externpasscheck: %s\n", ext_pass_check_cmd);
14980 }
14981
14982#ifdef IMAP_STORAGE
14983 /* IMAP server address */
14984 if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
14985 ast_copy_string(imapserver, val, sizeof(imapserver));
14986 } else {
14987 ast_copy_string(imapserver, "localhost", sizeof(imapserver));
14988 }
14989 /* IMAP server port */
14990 if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
14991 ast_copy_string(imapport, val, sizeof(imapport));
14992 } else {
14993 ast_copy_string(imapport, "143", sizeof(imapport));
14994 }
14995 /* IMAP server flags */
14996 if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
14997 ast_copy_string(imapflags, val, sizeof(imapflags));
14998 }
14999 /* IMAP server master username */
15000 if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
15001 ast_copy_string(authuser, val, sizeof(authuser));
15002 }
15003 /* IMAP server master password */
15004 if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
15005 ast_copy_string(authpassword, val, sizeof(authpassword));
15006 }
15007 /* Expunge on exit */
15008 if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
15009 if (ast_false(val))
15010 expungeonhangup = 0;
15011 else
15012 expungeonhangup = 1;
15013 } else {
15014 expungeonhangup = 1;
15015 }
15016 /* IMAP voicemail folder */
15017 if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
15018 ast_copy_string(imapfolder, val, sizeof(imapfolder));
15019 } else {
15020 ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
15021 }
15022 if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
15023 ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
15024 }
15025 if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
15026 imapgreetings = ast_true(val);
15027 } else {
15028 imapgreetings = 0;
15029 }
15030 if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
15031 ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
15032 } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
15033 /* Also support greetingsfolder as documented in voicemail.conf.sample */
15034 ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
15035 } else {
15036 ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
15037 }
15038 if ((val = ast_variable_retrieve(cfg, "general", "imap_poll_logout"))) {
15039 imap_poll_logout = ast_true(val);
15040 } else {
15041 imap_poll_logout = 0;
15042 }
15043
15044 /* There is some very unorthodox casting done here. This is due
15045 * to the way c-client handles the argument passed in. It expects a
15046 * void pointer and casts the pointer directly to a long without
15047 * first dereferencing it. */
15048 if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
15049 mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
15050 } else {
15051 mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
15052 }
15053
15054 if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
15055 mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
15056 } else {
15057 mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
15058 }
15059
15060 if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
15061 mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
15062 } else {
15063 mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
15064 }
15065
15066 if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
15067 mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
15068 } else {
15069 mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
15070 }
15071
15072 /* Increment configuration version */
15073 imapversion++;
15074#endif
15075 /* External voicemail notify application */
15076 if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
15078 ast_debug(1, "found externnotify: %s\n", externnotify);
15079 } else {
15080 externnotify[0] = '\0';
15081 }
15082
15083 /* SMDI voicemail notification */
15084 if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
15085 ast_debug(1, "Enabled SMDI voicemail notification\n");
15086 if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
15088 } else {
15089 ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
15090 smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
15091 }
15092 if (!smdi_iface) {
15093 ast_log(AST_LOG_ERROR, "No valid SMDI interface specified, disabling SMDI voicemail notification\n");
15094 }
15095 }
15096
15097 /* Silence treshold */
15099 if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
15100 silencethreshold = atoi(val);
15101
15102 if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
15105
15106 vmmaxsecs = 0;
15107 if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
15108 if (sscanf(val, "%30d", &x) == 1) {
15109 vmmaxsecs = x;
15110 } else {
15111 ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
15112 }
15113 }
15114
15115 vmminsecs = 0;
15116 if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
15117 if (sscanf(val, "%30d", &x) == 1) {
15118 vmminsecs = x;
15119 if (maxsilence / 1000 >= vmminsecs) {
15120 ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
15121 }
15122 } else {
15123 ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
15124 }
15125 }
15126
15127 val = ast_variable_retrieve(cfg, "general", "format");
15128 if (!val) {
15129 val = "wav";
15130 } else {
15131 tmp = ast_strdupa(val);
15133 if (!val) {
15134 ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
15135 val = "wav";
15136 }
15137 }
15138 ast_copy_string(vmfmts, val, sizeof(vmfmts));
15139
15140 skipms = 3000;
15141 if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
15142 if (sscanf(val, "%30d", &x) == 1) {
15143 maxgreet = x;
15144 } else {
15145 ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
15146 }
15147 }
15148
15149 if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
15150 if (sscanf(val, "%30d", &x) == 1) {
15151 skipms = x;
15152 } else {
15153 ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
15154 }
15155 }
15156
15157 maxlogins = 3;
15158 if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
15159 if (sscanf(val, "%30d", &x) == 1) {
15160 maxlogins = x;
15161 } else {
15162 ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
15163 }
15164 }
15165
15167 if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
15168 if (sscanf(val, "%30d", &x) == 1) {
15169 minpassword = x;
15170 } else {
15171 ast_log(AST_LOG_WARNING, "Invalid minimum password length. Default to %d\n", minpassword);
15172 }
15173 }
15174
15175 /* Force new user to record name ? */
15176 if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
15177 val = "no";
15179
15180 /* Force new user to record greetings ? */
15181 if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
15182 val = "no";
15184
15185 if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
15186 ast_debug(1, "VM_CID Internal context string: %s\n", val);
15187 stringp = ast_strdupa(val);
15188 for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
15189 if (!ast_strlen_zero(stringp)) {
15190 q = strsep(&stringp, ",");
15191 while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
15192 q++;
15194 ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
15195 } else {
15196 cidinternalcontexts[x][0] = '\0';
15197 }
15198 }
15199 }
15200 if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
15201 ast_debug(1, "VM Review Option disabled globally\n");
15202 val = "no";
15203 }
15205
15206 if (!(val = ast_variable_retrieve(cfg, "general", "leaveurgent"))){
15207 val = "yes";
15208 } else if (ast_false(val)) {
15209 ast_debug(1, "VM leave urgent messages disabled globally\n");
15210 val = "no";
15211 }
15213
15214 /* Temporary greeting reminder */
15215 if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
15216 ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
15217 val = "no";
15218 } else {
15219 ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
15220 }
15222 if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
15223 ast_debug(1, "VM next message wrap disabled globally\n");
15224 val = "no";
15225 }
15227
15228 if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
15229 ast_debug(1, "VM Operator break disabled globally\n");
15230 val = "no";
15231 }
15233
15234 if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
15235 ast_debug(1, "VM CID Info before msg disabled globally\n");
15236 val = "no";
15237 }
15239
15240 if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
15241 ast_debug(1, "Send Voicemail msg disabled globally\n");
15242 val = "no";
15243 }
15245
15246 if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
15247 ast_debug(1, "ENVELOPE before msg enabled globally\n");
15248 val = "yes";
15249 }
15251
15252 if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
15253 ast_debug(1, "Move Heard enabled globally\n");
15254 val = "yes";
15255 }
15257
15258 if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
15259 ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
15260 val = "no";
15261 }
15263
15264 if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
15265 ast_debug(1, "Duration info before msg enabled globally\n");
15266 val = "yes";
15267 }
15269
15270 saydurationminfo = 2;
15271 if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
15272 if (sscanf(val, "%30d", &x) == 1) {
15273 saydurationminfo = x;
15274 } else {
15275 ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
15276 }
15277 }
15278
15279 if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
15280 ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
15281 val = "no";
15282 }
15284
15285 if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
15287 ast_debug(1, "found dialout context: %s\n", dialcontext);
15288 } else {
15289 dialcontext[0] = '\0';
15290 }
15291
15292 if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
15294 ast_debug(1, "found callback context: %s\n", callcontext);
15295 } else {
15296 callcontext[0] = '\0';
15297 }
15298
15299 if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
15301 ast_debug(1, "found operator context: %s\n", exitcontext);
15302 } else {
15303 exitcontext[0] = '\0';
15304 }
15305
15306 /* load password sounds configuration */
15307 if ((val = ast_variable_retrieve(cfg, "general", "vm-login")))
15309 if ((val = ast_variable_retrieve(cfg, "general", "vm-newuser")))
15311 if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
15313 if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
15315 if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
15317 if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
15319 if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
15321 if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
15323 if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
15325 }
15326 if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
15328 }
15329 /* load configurable audio prompts */
15330 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
15332 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
15334 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
15336 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
15338 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
15340
15341 if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
15342 val = "no";
15344
15345 if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
15346 val = "voicemail.conf";
15347 }
15348 if (!(strcmp(val, "spooldir"))) {
15350 } else {
15352 }
15353
15355 if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
15356 if (sscanf(val, "%30u", &poll_freq) != 1) {
15358 ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
15359 }
15360 }
15361
15362 poll_mailboxes = 0;
15363 if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
15365
15366 memset(fromstring, 0, sizeof(fromstring));
15367 memset(pagerfromstring, 0, sizeof(pagerfromstring));
15368 strcpy(charset, "ISO-8859-1");
15369 if (emailbody) {
15371 emailbody = NULL;
15372 }
15373 if (emailsubject) {
15376 }
15377 if (pagerbody) {
15379 pagerbody = NULL;
15380 }
15381 if (pagersubject) {
15384 }
15385 if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
15387 if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
15389 if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
15391 if ((val = ast_variable_retrieve(cfg, "general", "charset")))
15393 if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
15394 sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
15395 for (x = 0; x < 4; x++) {
15396 memcpy(&adsifdn[x], &tmpadsi[x], 1);
15397 }
15398 }
15399 if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
15400 sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
15401 for (x = 0; x < 4; x++) {
15402 memcpy(&adsisec[x], &tmpadsi[x], 1);
15403 }
15404 }
15405 if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
15406 if (atoi(val)) {
15407 adsiver = atoi(val);
15408 }
15409 }
15410 if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
15412 }
15413 if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
15414 ast_copy_string(locale, val, sizeof(locale));
15415 }
15416 if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
15418 }
15419 if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
15421 }
15422 if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
15424 }
15425 if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
15427 }
15428
15429 tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
15430 if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_high"))) {
15431 if (sscanf(val, "%30ld", &tps_queue_high) != 1 || tps_queue_high <= 0) {
15432 ast_log(AST_LOG_WARNING, "Invalid the taskprocessor high water alert trigger level '%s'\n", val);
15433 tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
15434 }
15435 }
15436 tps_queue_low = -1;
15437 if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_low"))) {
15438 if (sscanf(val, "%30ld", &tps_queue_low) != 1 ||
15439 tps_queue_low < -1 || tps_queue_high < tps_queue_low) {
15440 ast_log(AST_LOG_WARNING, "Invalid the taskprocessor low water clear alert level '%s'\n", val);
15441 tps_queue_low = -1;
15442 }
15443 }
15444 if (ast_taskprocessor_alert_set_levels(mwi_subscription_tps, tps_queue_low, tps_queue_high)) {
15445 ast_log(AST_LOG_WARNING, "Failed to set alert levels for voicemail taskprocessor.\n");
15446 }
15447
15448 /* load mailboxes from voicemail.conf */
15449
15450 /*
15451 * Aliases must be loaded before users or the aliases won't be notified
15452 * if there's existing voicemail in the user mailbox.
15453 */
15454 load_aliases(cfg);
15455
15456 load_zonemessages(cfg);
15457
15458 load_users(cfg);
15459
15461
15466
15467 return 0;
15468 } else {
15470 ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
15471 return 0;
15472 }
15473}
15474
15475static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
15476{
15477 int res = -1;
15478 char dir[PATH_MAX];
15479 snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
15480 ast_debug(2, "About to try retrieving name file %s\n", dir);
15481 RETRIEVE(dir, -1, mailbox, context);
15482 if (ast_fileexists(dir, NULL, NULL)) {
15483 res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
15484 }
15485 DISPOSE(dir, -1);
15486 return res;
15487}
15488
15489/*!
15490 * \internal
15491 * \brief Play a recorded user name for the mailbox to the specified channel.
15492 *
15493 * \param chan Where to play the recorded name file.
15494 * \param mailbox_id The mailbox name.
15495 *
15496 * \retval 0 Name played without interruption
15497 * \retval dtmf ASCII value of the DTMF which interrupted playback.
15498 * \retval -1 Unable to locate mailbox or hangup occurred.
15499 */
15500static int vm_sayname(struct ast_channel *chan, const char *mailbox_id)
15501{
15502 char *context;
15503 char *mailbox;
15504
15505 if (ast_strlen_zero(mailbox_id)
15506 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
15507 return -1;
15508 }
15509 return sayname(chan, mailbox, context);
15510}
15511
15512static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
15513 struct ast_config *pwconf;
15514 struct ast_flags config_flags = { 0 };
15515
15516 pwconf = ast_config_load(secretfn, config_flags);
15517 if (valid_config(pwconf)) {
15518 const char *val = ast_variable_retrieve(pwconf, "general", "password");
15519 if (val) {
15520 ast_copy_string(password, val, passwordlen);
15521 ast_config_destroy(pwconf);
15522 return;
15523 }
15524 ast_config_destroy(pwconf);
15525 }
15526 ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
15527}
15528
15529static int write_password_to_file(const char *secretfn, const char *password) {
15530 struct ast_config *conf;
15531 struct ast_category *cat;
15532 struct ast_variable *var;
15533 int res = -1;
15534
15535 if (!(conf = ast_config_new())) {
15536 ast_log(LOG_ERROR, "Error creating new config structure\n");
15537 return res;
15538 }
15539 if (!(cat = ast_category_new("general", "", 1))) {
15540 ast_log(LOG_ERROR, "Error creating new category structure\n");
15542 return res;
15543 }
15544 if (!(var = ast_variable_new("password", password, ""))) {
15545 ast_log(LOG_ERROR, "Error creating new variable structure\n");
15548 return res;
15549 }
15552 if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
15553 res = 0;
15554 } else {
15555 ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
15556 }
15557
15559 return res;
15560}
15561
15562static int vmsayname_exec(struct ast_channel *chan, const char *data)
15563{
15564 char *context;
15565 char *mailbox;
15566 int res;
15567
15568 if (ast_strlen_zero(data)
15569 || separate_mailbox(ast_strdupa(data), &mailbox, &context)) {
15570 ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
15571 return -1;
15572 }
15573
15574 if ((res = sayname(chan, mailbox, context)) < 0) {
15575 ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", mailbox, context);
15576 res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
15577 if (!res) {
15579 }
15580 }
15581
15582 return res;
15583}
15584
15585#ifdef TEST_FRAMEWORK
15586static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
15587{
15588 return 0;
15589}
15590
15591static struct ast_frame *fake_read(struct ast_channel *ast)
15592{
15593 return &ast_null_frame;
15594}
15595
15596AST_TEST_DEFINE(test_voicemail_vmsayname)
15597{
15598 char dir[PATH_MAX];
15599 char dir2[PATH_MAX];
15600 static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
15601 static const char TEST_EXTENSION[] = "1234";
15602
15603 struct ast_channel *test_channel1 = NULL;
15604 int res = -1;
15605 struct ast_format_cap *capabilities;
15606
15607 static const struct ast_channel_tech fake_tech = {
15608 .write = fake_write,
15609 .read = fake_read,
15610 };
15611
15612 switch (cmd) {
15613 case TEST_INIT:
15614 info->name = "vmsayname_exec";
15615 info->category = "/apps/app_voicemail/";
15616 info->summary = "Vmsayname unit test";
15617 info->description =
15618 "This tests passing various parameters to vmsayname";
15619 return AST_TEST_NOT_RUN;
15620 case TEST_EXECUTE:
15621 break;
15622 }
15623
15624 if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL,
15625 NULL, NULL, 0, 0, "TestChannel1"))) {
15626 goto exit_vmsayname_test;
15627 }
15628
15629 /* normally this is done in the channel driver */
15631 if (!capabilities) {
15632 goto exit_vmsayname_test;
15633 }
15636 ao2_ref(capabilities, -1);
15641 ast_channel_tech_set(test_channel1, &fake_tech);
15642
15643 ast_channel_unlock(test_channel1);
15644
15645 ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
15646 snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
15647 if (!(res = vmsayname_exec(test_channel1, dir))) {
15648 snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15649 if (ast_fileexists(dir, NULL, NULL)) {
15650 ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
15651 res = -1;
15652 goto exit_vmsayname_test;
15653 } else {
15654 /* no greeting already exists as expected, let's create one to fully test sayname */
15655 if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
15656 ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
15657 goto exit_vmsayname_test;
15658 }
15659 snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_DATA_DIR);
15660 snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15661 /* we're not going to hear the sound anyway, just use a valid gsm audio file */
15662 if ((res = symlink(dir, dir2))) {
15663 ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
15664 goto exit_vmsayname_test;
15665 }
15666 ast_test_status_update(test, "Test playing created mailbox greeting...\n");
15667 snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
15668 res = vmsayname_exec(test_channel1, dir);
15669
15670 /* TODO: there may be a better way to do this */
15671 unlink(dir2);
15672 snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15673 rmdir(dir2);
15674 snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
15675 rmdir(dir2);
15676 }
15677 }
15678
15679exit_vmsayname_test:
15680
15681 ast_hangup(test_channel1);
15682
15683 return res ? AST_TEST_FAIL : AST_TEST_PASS;
15684}
15685
15686struct test_files {
15687 char dir[256];
15688 char file[256];
15689 char txtfile[256];
15690};
15691
15692AST_TEST_DEFINE(test_voicemail_msgcount)
15693{
15694 int i, j, res = AST_TEST_PASS, syserr;
15695 struct ast_vm_user *vmu;
15696 struct ast_vm_user svm;
15697 struct vm_state vms;
15698#ifdef IMAP_STORAGE
15699 struct ast_channel *chan = NULL;
15700#endif
15701 /* Using ast_alloca instead of just declaring tmp as an array is a workaround for a GCC 10 issue with -Wrestrict */
15702 struct test_files *tmp = ast_alloca(sizeof(struct test_files) * 3);
15703 char syscmd[256];
15704 const char origweasels[] = "tt-weasels";
15705 const char testcontext[] = "test";
15706 const char testmailbox[] = "00000000";
15707 const char testspec[] = "00000000@test";
15708 FILE *txt;
15709 int new, old, urgent;
15710 const char *folders[3] = { "Old", "Urgent", "INBOX" };
15711 const int folder2mbox[3] = { 1, 11, 0 };
15712 const int expected_results[3][12] = {
15713 /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
15714 { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
15715 { 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
15716 { 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 2 },
15717 };
15718
15719 switch (cmd) {
15720 case TEST_INIT:
15721 info->name = "test_voicemail_msgcount";
15722 info->category = "/apps/app_voicemail/";
15723 info->summary = "Test Voicemail status checks";
15724 info->description =
15725 "Verify that message counts are correct when retrieved through the public API";
15726 return AST_TEST_NOT_RUN;
15727 case TEST_EXECUTE:
15728 break;
15729 }
15730
15731 /* Make sure the original path was completely empty */
15732 snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
15733 if ((syserr = ast_safe_system(syscmd))) {
15734 ast_test_status_update(test, "Unable to clear test directory: %s\n",
15735 syserr > 0 ? strerror(syserr) : "unable to fork()");
15736 return AST_TEST_FAIL;
15737 }
15738
15739#ifdef IMAP_STORAGE
15740 if (!(chan = ast_dummy_channel_alloc())) {
15741 ast_test_status_update(test, "Unable to create dummy channel\n");
15742 return AST_TEST_FAIL;
15743 }
15744#endif
15745
15746 memset(&svm, 0, sizeof(svm));
15747 if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
15748 !(vmu = find_or_create(testcontext, testmailbox))) {
15749 ast_test_status_update(test, "Cannot create vmu structure\n");
15750#ifdef IMAP_STORAGE
15751 chan = ast_channel_unref(chan);
15752#endif
15753 return AST_TEST_FAIL;
15754 }
15755
15756 populate_defaults(vmu);
15757 memset(&vms, 0, sizeof(vms));
15758
15759 /* Create temporary voicemail */
15760 for (i = 0; i < 3; i++) {
15761 create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
15762 make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
15763 snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
15764
15765 if (ast_fileexists(origweasels, "gsm", "en") > 0) {
15766 snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
15767 VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
15768 if ((syserr = ast_safe_system(syscmd))) {
15769 ast_test_status_update(test, "Unable to create test voicemail: %s\n",
15770 syserr > 0 ? strerror(syserr) : "unable to fork()");
15771#ifdef IMAP_STORAGE
15772 chan = ast_channel_unref(chan);
15773#endif
15774 free_user(vmu);
15775 return AST_TEST_FAIL;
15776 }
15777 }
15778
15779 if ((txt = fopen(tmp[i].txtfile, "w+"))) {
15780 fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
15781 fclose(txt);
15782 } else {
15783 ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
15784 res = AST_TEST_FAIL;
15785 break;
15786 }
15787 open_mailbox(&vms, vmu, folder2mbox[i]);
15788 STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent", NULL);
15789
15790 /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
15791 for (j = 0; j < 3; j++) {
15792 /* folder[2] is INBOX, __has_voicemail will default back to INBOX */
15793 if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
15794 ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
15795 testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
15796 res = AST_TEST_FAIL;
15797 }
15798 }
15799
15800 new = old = urgent = 0;
15801 if (ast_app_inboxcount(testspec, &new, &old)) {
15802 ast_test_status_update(test, "inboxcount returned failure\n");
15803 res = AST_TEST_FAIL;
15804 } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
15805 ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
15806 testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
15807 res = AST_TEST_FAIL;
15808 }
15809
15810 new = old = urgent = 0;
15811 if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
15812 ast_test_status_update(test, "inboxcount2 returned failure\n");
15813 res = AST_TEST_FAIL;
15814 } else if (old != expected_results[i][6 + 0] ||
15815 urgent != expected_results[i][6 + 1] ||
15816 new != expected_results[i][6 + 2] ) {
15817 ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
15818 testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
15819 res = AST_TEST_FAIL;
15820 }
15821
15822 new = old = urgent = 0;
15823 for (j = 0; j < 3; j++) {
15824 if (ast_app_messagecount(testspec, folders[j]) != expected_results[i][9 + j]) {
15825 ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
15826 testspec, folders[j], ast_app_messagecount(testspec, folders[j]), expected_results[i][9 + j]);
15827 res = AST_TEST_FAIL;
15828 }
15829 }
15830 }
15831
15832 for (i = 0; i < 3; i++) {
15833 /* This is necessary if the voicemails are stored on an ODBC/IMAP
15834 * server, in which case, the rm below will not affect the
15835 * voicemails. */
15836 DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
15837 DISPOSE(tmp[i].dir, 0);
15838 }
15839
15840 if (vms.deleted) {
15841 ast_free(vms.deleted);
15842 }
15843 if (vms.heard) {
15844 ast_free(vms.heard);
15845 }
15846
15847#ifdef IMAP_STORAGE
15848 chan = ast_channel_unref(chan);
15849#endif
15850
15851 /* And remove test directory */
15852 snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
15853 if ((syserr = ast_safe_system(syscmd))) {
15854 ast_test_status_update(test, "Unable to clear test directory: %s\n",
15855 syserr > 0 ? strerror(syserr) : "unable to fork()");
15856 }
15857
15858 free_user(vmu);
15859 force_reload_config(); /* Restore original config */
15860 return res;
15861}
15862
15863AST_TEST_DEFINE(test_voicemail_notify_endl)
15864{
15865 int res = AST_TEST_PASS;
15866 char testcontext[] = "test";
15867 char testmailbox[] = "00000000";
15868 char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
15869 char attach[256], attach2[256];
15870 char buf[256] = ""; /* No line should actually be longer than 80 */
15871 struct ast_channel *chan = NULL;
15872 struct ast_vm_user *vmu, vmus = {
15873 .flags = 0,
15874 };
15875 FILE *file;
15876 struct {
15877 char *name;
15878 enum { INT, FLAGVAL, STATIC, STRPTR } type;
15879 void *location;
15880 union {
15881 int intval;
15882 char *strval;
15883 } u;
15884 } test_items[] = {
15885 { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
15886 { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
15887 { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
15888 { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
15889 { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
15890 { "attach2", STRPTR, attach2, .u.strval = "" },
15891 { "attach", STRPTR, attach, .u.strval = "" },
15892 };
15893 int which;
15894
15895 switch (cmd) {
15896 case TEST_INIT:
15897 info->name = "test_voicemail_notify_endl";
15898 info->category = "/apps/app_voicemail/";
15899 info->summary = "Test Voicemail notification end-of-line";
15900 info->description =
15901 "Verify that notification emails use a consistent end-of-line character";
15902 return AST_TEST_NOT_RUN;
15903 case TEST_EXECUTE:
15904 break;
15905 }
15906
15907 snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_DATA_DIR);
15908 snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_DATA_DIR);
15909
15910 if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
15911 !(vmu = find_or_create(testcontext, testmailbox))) {
15912 ast_test_status_update(test, "Cannot create vmu structure\n");
15913 return AST_TEST_NOT_RUN;
15914 }
15915
15916 if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
15917 ast_test_status_update(test, "Cannot find vmu structure?!!\n");
15918 return AST_TEST_NOT_RUN;
15919 }
15920
15921 populate_defaults(vmu);
15922 vmu->email = ast_strdup("test2@example.net");
15923#ifdef IMAP_STORAGE
15924 /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
15925#endif
15926
15927 file = tmpfile();
15928 for (which = 0; which < ARRAY_LEN(test_items); which++) {
15929 /* Kill previous test, if any */
15930 rewind(file);
15931 if (ftruncate(fileno(file), 0)) {
15932 ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
15933 res = AST_TEST_FAIL;
15934 break;
15935 }
15936
15937 /* Make each change, in order, to the test mailbox */
15938 if (test_items[which].type == INT) {
15939 *((int *) test_items[which].location) = test_items[which].u.intval;
15940 } else if (test_items[which].type == FLAGVAL) {
15941 if (ast_test_flag(vmu, test_items[which].u.intval)) {
15942 ast_clear_flag(vmu, test_items[which].u.intval);
15943 } else {
15944 ast_set_flag(vmu, test_items[which].u.intval);
15945 }
15946 } else if (test_items[which].type == STATIC) {
15947 strcpy(test_items[which].location, test_items[which].u.strval);
15948 } else if (test_items[which].type == STRPTR) {
15949 test_items[which].location = test_items[which].u.strval;
15950 }
15951
15952 make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL, NULL);
15953 rewind(file);
15954 while (fgets(buf, sizeof(buf), file)) {
15955 if (
15956 (strlen(buf) > 1 &&
15957#ifdef IMAP_STORAGE
15958 buf[strlen(buf) - 2] != '\r'
15959#else
15960 buf[strlen(buf) - 2] == '\r'
15961#endif
15962 )
15963 || buf[strlen(buf) - 1] != '\n') {
15964 res = AST_TEST_FAIL;
15965 }
15966 }
15967 }
15968 fclose(file);
15969 free_user(vmu);
15970 force_reload_config(); /* Restore original config */
15971 return res;
15972}
15973
15974AST_TEST_DEFINE(test_voicemail_load_config)
15975{
15976 int res = AST_TEST_PASS;
15977 struct ast_vm_user *vmu;
15978 struct ast_config *cfg;
15979 char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
15980 int fd;
15981 FILE *file;
15982 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
15983
15984 switch (cmd) {
15985 case TEST_INIT:
15986 info->name = "test_voicemail_load_config";
15987 info->category = "/apps/app_voicemail/";
15988 info->summary = "Test loading Voicemail config";
15989 info->description =
15990 "Verify that configuration is loaded consistently. "
15991 "This is to test regressions of ASTERISK-18838 where it was noticed that "
15992 "some options were loaded after the mailboxes were instantiated, causing "
15993 "those options not to be set correctly.";
15994 return AST_TEST_NOT_RUN;
15995 case TEST_EXECUTE:
15996 break;
15997 }
15998
15999 /* build a config file by hand... */
16000 if ((fd = mkstemp(config_filename)) < 0) {
16001 return AST_TEST_FAIL;
16002 }
16003 if (!(file = fdopen(fd, "w"))) {
16004 close(fd);
16005 unlink(config_filename);
16006 return AST_TEST_FAIL;
16007 }
16008 fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
16009 fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
16010 fputs("00000002 => 9999,Mrs. Test\n", file);
16011 fclose(file);
16012
16013 if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
16014 res = AST_TEST_FAIL;
16015 goto cleanup;
16016 }
16017
16018 load_config_from_memory(1, cfg);
16019 ast_config_destroy(cfg);
16020
16021#define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
16022 ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
16023 u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
16024
16026 AST_LIST_TRAVERSE(&users, vmu, list) {
16027 if (!strcmp(vmu->mailbox, "00000001")) {
16028 if (0); /* trick to get CHECK to work */
16029 CHECK(vmu, callback, "othercontext")
16030 CHECK(vmu, locale, "nl_NL.UTF-8")
16031 CHECK(vmu, zonetag, "central")
16032 } else if (!strcmp(vmu->mailbox, "00000002")) {
16033 if (0); /* trick to get CHECK to work */
16034 CHECK(vmu, callback, "somecontext")
16035 CHECK(vmu, locale, "de_DE.UTF-8")
16036 CHECK(vmu, zonetag, "european")
16037 }
16038 }
16040
16041#undef CHECK
16042
16043 /* Forcibly restore the original config, to reinitialize after test */
16044 force_reload_config(); /* this might say "Failed to load configuration file." */
16045
16046cleanup:
16047 unlink(config_filename);
16048 return res;
16049}
16050
16051AST_TEST_DEFINE(test_voicemail_vm_info)
16052{
16053 struct ast_vm_user *vmu;
16054 struct ast_channel *chan = NULL;
16055 const char testcontext[] = "test";
16056 const char testmailbox[] = "00000000";
16057 const char vminfo_cmd[] = "VM_INFO";
16058 char vminfo_buf[256], vminfo_args[256];
16059 int res = AST_TEST_PASS;
16060 int test_ret = 0;
16061 int test_counter = 0;
16062
16063 struct {
16064 char *vminfo_test_args;
16065 char *vminfo_expected;
16066 int vminfo_ret;
16067 } test_items[] = {
16068 { "", "", -1 }, /* Missing argument */
16069 { "00000000@test,badparam", "", -1 }, /* Wrong argument */
16070 { "00000000@test", "", -1 }, /* Missing argument */
16071 { "00000000@test,exists", "1", 0 },
16072 { "11111111@test,exists", "0", 0 }, /* Invalid mailbox */
16073 { "00000000@test,email", "vm-info-test@example.net", 0 },
16074 { "11111111@test,email", "", 0 }, /* Invalid mailbox */
16075 { "00000000@test,fullname", "Test Framework Mailbox", 0 },
16076 { "00000000@test,pager", "vm-info-pager-test@example.net", 0 },
16077 { "00000000@test,locale", "en_US", 0 },
16078 { "00000000@test,tz", "central", 0 },
16079 { "00000000@test,language", "en", 0 },
16080 { "00000000@test,password", "9876", 0 },
16081 };
16082
16083 switch (cmd) {
16084 case TEST_INIT:
16085 info->name = "test_voicemail_vm_info";
16086 info->category = "/apps/app_voicemail/";
16087 info->summary = "VM_INFO unit test";
16088 info->description =
16089 "This tests passing various parameters to VM_INFO";
16090 return AST_TEST_NOT_RUN;
16091 case TEST_EXECUTE:
16092 break;
16093 }
16094
16095 if (!(chan = ast_dummy_channel_alloc())) {
16096 ast_test_status_update(test, "Unable to create dummy channel\n");
16097 return AST_TEST_FAIL;
16098 }
16099
16100 if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
16101 !(vmu = find_or_create(testcontext, testmailbox))) {
16102 ast_test_status_update(test, "Cannot create vmu structure\n");
16103 chan = ast_channel_unref(chan);
16104 return AST_TEST_FAIL;
16105 }
16106
16107 populate_defaults(vmu);
16108
16109 vmu->email = ast_strdup("vm-info-test@example.net");
16110 if (!vmu->email) {
16111 ast_test_status_update(test, "Cannot create vmu email\n");
16112 chan = ast_channel_unref(chan);
16113 return AST_TEST_FAIL;
16114 }
16115 ast_copy_string(vmu->fullname, "Test Framework Mailbox", sizeof(vmu->fullname));
16116 ast_copy_string(vmu->pager, "vm-info-pager-test@example.net", sizeof(vmu->pager));
16117 ast_copy_string(vmu->language, "en", sizeof(vmu->language));
16118 ast_copy_string(vmu->zonetag, "central", sizeof(vmu->zonetag));
16119 ast_copy_string(vmu->locale, "en_US", sizeof(vmu->zonetag));
16120 ast_copy_string(vmu->password, "9876", sizeof(vmu->password));
16121
16122 for (test_counter = 0; test_counter < ARRAY_LEN(test_items); test_counter++) {
16123 ast_copy_string(vminfo_args, test_items[test_counter].vminfo_test_args, sizeof(vminfo_args));
16124 test_ret = acf_vm_info(chan, vminfo_cmd, vminfo_args, vminfo_buf, sizeof(vminfo_buf));
16125 if (strcmp(vminfo_buf, test_items[test_counter].vminfo_expected)) {
16126 ast_test_status_update(test, "VM_INFO response was: '%s', but expected: '%s'\n", vminfo_buf, test_items[test_counter].vminfo_expected);
16127 res = AST_TEST_FAIL;
16128 }
16129 if (!(test_ret == test_items[test_counter].vminfo_ret)) {
16130 ast_test_status_update(test, "VM_INFO return code was: '%i', but expected '%i'\n", test_ret, test_items[test_counter].vminfo_ret);
16131 res = AST_TEST_FAIL;
16132 }
16133 }
16134
16135 chan = ast_channel_unref(chan);
16136 free_user(vmu);
16137 return res;
16138}
16139#endif /* defined(TEST_FRAMEWORK) */
16140
16141static const struct ast_vm_functions vm_table = {
16143 .module_name = AST_MODULE,
16144
16145 .has_voicemail = has_voicemail,
16146 .inboxcount = inboxcount,
16147 .inboxcount2 = inboxcount2,
16148 .messagecount = messagecount,
16149 .copy_recording_to_vm = msg_create_from_file,
16150 .index_to_foldername = vm_index_to_foldername,
16151 .mailbox_snapshot_create = vm_mailbox_snapshot_create,
16152 .mailbox_snapshot_destroy = vm_mailbox_snapshot_destroy,
16153 .msg_move = vm_msg_move,
16154 .msg_remove = vm_msg_remove,
16155 .msg_forward = vm_msg_forward,
16156 .msg_play = vm_msg_play,
16157};
16158
16161 .module_name = AST_MODULE,
16162
16163 .sayname = vm_sayname,
16164};
16165
16166static int reload(void)
16167{
16168 return load_config(1);
16169}
16170
16171static int unload_module(void)
16172{
16173 int res;
16174
16181 res |= ast_manager_unregister("VoicemailUsersList");
16182 res |= ast_manager_unregister("VoicemailUserStatus");
16183 res |= ast_manager_unregister("VoicemailRefresh");
16184 res |= ast_manager_unregister("VoicemailBoxSummary");
16185 res |= ast_manager_unregister("VoicemailMove");
16186 res |= ast_manager_unregister("VoicemailRemove");
16187 res |= ast_manager_unregister("VoicemailForward");
16188#ifdef TEST_FRAMEWORK
16189 res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
16190 res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
16191 res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
16192 res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
16193 res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
16194 res |= AST_TEST_UNREGISTER(test_voicemail_vm_info);
16195#endif
16199#ifdef TEST_FRAMEWORK
16200 ast_uninstall_vm_test_functions();
16201#endif
16203
16204 ao2_container_unregister("voicemail_alias_mailbox_mappings");
16206 ao2_container_unregister("voicemail_mailbox_alias_mappings");
16208
16211
16213 ast_unload_realtime("voicemail");
16214 ast_unload_realtime("voicemail_data");
16215
16216#ifdef IMAP_STORAGE
16217 ast_mwi_state_callback_all(imap_close_subscribed_mailbox, NULL);
16218#endif
16219 free_vm_users();
16220 free_vm_zones();
16221 return res;
16222}
16223
16224static void print_mappings(void *v_obj, void *where, ao2_prnt_fn *prnt)
16225{
16226 struct alias_mailbox_mapping *mapping = v_obj;
16227
16228 if (!mapping) {
16229 return;
16230 }
16231 prnt(where, "Alias: %s Mailbox: %s", mapping->alias, mapping->mailbox);
16232}
16233
16234/*!
16235 * \brief Load the module
16236 *
16237 * Module loading including tests for configuration or dependencies.
16238 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
16239 * or AST_MODULE_LOAD_SUCCESS.
16240 *
16241 * If a dependency, allocation or environment variable fails tests, return AST_MODULE_LOAD_FAILURE.
16242 *
16243 * If the module can't load the configuration file, can't register as a provider or
16244 * has another issue not fatal to Asterisk itself, return AST_MODULE_LOAD_DECLINE.
16245 *
16246 * On success return AST_MODULE_LOAD_SUCCESS.
16247 */
16248static int load_module(void)
16249{
16250 int res;
16251 my_umask = umask(0);
16252 umask(my_umask);
16253
16256 if (!inprocess_container) {
16258 }
16259
16261 alias_mailbox_mapping_hash_fn, NULL, alias_mailbox_mapping_cmp_fn);
16263 ast_log(LOG_ERROR, "Unable to create alias_mailbox_mappings container\n");
16266 }
16267 res = ao2_container_register("voicemail_alias_mailbox_mappings", alias_mailbox_mappings, print_mappings);
16268 if (res) {
16269 ast_log(LOG_ERROR, "Unable to register alias_mailbox_mappings container\n");
16273 }
16274
16276 mailbox_alias_mapping_hash_fn, NULL, mailbox_alias_mapping_cmp_fn);
16278 ast_log(LOG_ERROR, "Unable to create mailbox_alias_mappings container\n");
16280 ao2_container_unregister("voicemail_alias_mailbox_mappings");
16283 }
16284 res = ao2_container_register("voicemail_mailbox_alias_mappings", mailbox_alias_mappings, print_mappings);
16285 if (res) {
16286 ast_log(LOG_ERROR, "Unable to register mailbox_alias_mappings container\n");
16288 ao2_container_unregister("voicemail_alias_mailbox_mappings");
16292 }
16293
16294 /* compute the location of the voicemail spool directory */
16295 snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
16296
16297 if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
16298 ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor. MWI will not work\n");
16299 }
16300
16301 if ((res = load_config(0))) {
16302 unload_module();
16304 }
16305
16319#ifdef TEST_FRAMEWORK
16320 res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
16321 res |= AST_TEST_REGISTER(test_voicemail_msgcount);
16322 res |= AST_TEST_REGISTER(test_voicemail_vmuser);
16323 res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
16324 res |= AST_TEST_REGISTER(test_voicemail_load_config);
16325 res |= AST_TEST_REGISTER(test_voicemail_vm_info);
16326#endif
16327
16328 if (res) {
16329 ast_log(LOG_ERROR, "Failure registering applications, functions or tests\n");
16330 unload_module();
16332 }
16333
16334 /* ast_vm_register may return DECLINE if another module registered for vm */
16335 res = ast_vm_register(&vm_table);
16336 if (res) {
16337 ast_log(LOG_ERROR, "Failure registering as a voicemail provider\n");
16338 unload_module();
16340 }
16341
16342 /* ast_vm_greeter_register may return DECLINE if another module registered as a greeter */
16344 if (res) {
16345 ast_log(LOG_ERROR, "Failure registering as a greeter provider\n");
16346 unload_module();
16348 }
16349
16351
16352#ifdef TEST_FRAMEWORK
16353 ast_install_vm_test_functions(vm_test_create_user, vm_test_destroy_user);
16354#endif
16355
16356 ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
16357 ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
16358
16360}
16361
16362static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
16363{
16364 int cmd = 0;
16365 char destination[80] = "";
16366 int retries = 0;
16367
16368 if (!num) {
16369 ast_verb(3, "Destination number will be entered manually\n");
16370 while (retries < 3 && cmd != 't') {
16371 destination[1] = '\0';
16372 destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
16373 if (!cmd)
16374 destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
16375 if (!cmd)
16376 destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
16377 if (!cmd) {
16378 cmd = ast_waitfordigit(chan, 6000);
16379 if (cmd)
16380 destination[0] = cmd;
16381 }
16382 if (!cmd) {
16383 retries++;
16384 } else {
16385
16386 if (cmd < 0)
16387 return 0;
16388 if (cmd == '*') {
16389 ast_verb(3, "User hit '*' to cancel outgoing call\n");
16390 return 0;
16391 }
16392 if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0)
16393 retries++;
16394 else
16395 cmd = 't';
16396 }
16397 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
16398 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
16399 }
16400 if (retries >= 3) {
16401 return 0;
16402 }
16403
16404 } else {
16405 ast_verb(3, "Destination number is CID number '%s'\n", num);
16406 ast_copy_string(destination, num, sizeof(destination));
16407 }
16408
16409 if (!ast_strlen_zero(destination)) {
16410 if (destination[strlen(destination) -1 ] == '*')
16411 return 0;
16412 ast_verb(3, "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, ast_channel_context(chan));
16413 ast_channel_exten_set(chan, destination);
16414 ast_channel_context_set(chan, outgoing_context);
16415 ast_channel_priority_set(chan, 0);
16416 return 9;
16417 }
16418 return 0;
16419}
16420
16421/*!
16422 * \brief The advanced options within a message.
16423 * \param chan
16424 * \param vmu
16425 * \param vms
16426 * \param msg
16427 * \param option
16428 * \param record_gain
16429 *
16430 * Provides handling for the play message envelope, call the person back, or reply to message.
16431 *
16432 * \return zero on success, -1 on error.
16433 */
16434static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain)
16435{
16436 int res = 0;
16437 char filename[PATH_MAX];
16438 struct ast_config *msg_cfg = NULL;
16439 const char *origtime, *context;
16440 char *name, *num;
16441 int retries = 0;
16442 char *cid;
16443 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
16444
16445 vms->starting = 0;
16446
16447 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
16448
16449 /* Retrieve info from VM attribute file */
16450 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
16451 RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
16452 msg_cfg = ast_config_load(filename, config_flags);
16453 DISPOSE(vms->curdir, vms->curmsg);
16454 if (!valid_config(msg_cfg)) {
16455 ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
16456 return 0;
16457 }
16458
16459 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
16460 ast_config_destroy(msg_cfg);
16461 return 0;
16462 }
16463
16464 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
16465
16466 context = ast_variable_retrieve(msg_cfg, "message", "context");
16467 switch (option) {
16468 case 3: /* Play message envelope */
16469 if (!res) {
16470 res = play_message_datetime(chan, vmu, origtime, filename);
16471 }
16472 if (!res) {
16473 res = play_message_callerid(chan, vms, cid, context, 0, 1);
16474 }
16475
16476 res = 't';
16477 break;
16478
16479 case 2: /* Call back */
16480
16481 if (ast_strlen_zero(cid))
16482 break;
16483
16484 ast_callerid_parse(cid, &name, &num);
16485 while ((res > -1) && (res != 't')) {
16486 switch (res) {
16487 case '1':
16488 if (num) {
16489 /* Dial the CID number */
16490 res = dialout(chan, vmu, num, vmu->callback);
16491 if (res) {
16492 ast_config_destroy(msg_cfg);
16493 return 9;
16494 }
16495 } else {
16496 res = '2';
16497 }
16498 break;
16499
16500 case '2':
16501 /* Want to enter a different number, can only do this if there's a dialout context for this user */
16502 if (!ast_strlen_zero(vmu->dialout)) {
16503 res = dialout(chan, vmu, NULL, vmu->dialout);
16504 if (res) {
16505 ast_config_destroy(msg_cfg);
16506 return 9;
16507 }
16508 } else {
16509 ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
16510 res = ast_play_and_wait(chan, "vm-sorry");
16511 }
16512 ast_config_destroy(msg_cfg);
16513 return res;
16514 case '*':
16515 res = 't';
16516 break;
16517 case '3':
16518 case '4':
16519 case '5':
16520 case '6':
16521 case '7':
16522 case '8':
16523 case '9':
16524 case '0':
16525
16526 res = ast_play_and_wait(chan, "vm-sorry");
16527 retries++;
16528 break;
16529 default:
16530 if (num) {
16531 ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
16532 res = ast_play_and_wait(chan, "vm-num-i-have");
16533 if (!res)
16534 res = play_message_callerid(chan, vms, num, vmu->context, 1, 1);
16535 if (!res)
16536 res = ast_play_and_wait(chan, "vm-tocallnum");
16537 /* Only prompt for a caller-specified number if there is a dialout context specified */
16538 if (!ast_strlen_zero(vmu->dialout)) {
16539 if (!res)
16540 res = ast_play_and_wait(chan, "vm-calldiffnum");
16541 }
16542 } else {
16543 res = ast_play_and_wait(chan, "vm-nonumber");
16544 if (!ast_strlen_zero(vmu->dialout)) {
16545 if (!res)
16546 res = ast_play_and_wait(chan, "vm-toenternumber");
16547 }
16548 }
16549 if (!res) {
16550 res = ast_play_and_wait(chan, "vm-star-cancel");
16551 }
16552 if (!res) {
16553 res = ast_waitfordigit(chan, 6000);
16554 }
16555 if (!res) {
16556 retries++;
16557 if (retries > 3) {
16558 res = 't';
16559 }
16560 }
16561 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
16562 isprint(res) ? res : '?', isprint(res) ? res : '?');
16563 break;
16564
16565 }
16566 if (res == 't')
16567 res = 0;
16568 else if (res == '*')
16569 res = -1;
16570 }
16571 break;
16572
16573 case 1: /* Reply */
16574 /* Send reply directly to sender */
16575 if (ast_strlen_zero(cid))
16576 break;
16577
16578 ast_callerid_parse(cid, &name, &num);
16579 if (!num) {
16580 ast_verb(3, "No CID number available, no reply sent\n");
16581 if (!res)
16582 res = ast_play_and_wait(chan, "vm-nonumber");
16583 ast_config_destroy(msg_cfg);
16584 return res;
16585 } else {
16586 struct ast_vm_user vmu2, *vmu3;
16587 memset(&vmu2, 0, sizeof(vmu2));
16588 vmu3 = find_user(&vmu2, vmu->context, num);
16589 if (vmu3) {
16590 struct leave_vm_options leave_options;
16591 char mailbox[AST_MAX_EXTENSION * 2 + 2];
16592 snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
16593
16594 ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
16595
16596 memset(&leave_options, 0, sizeof(leave_options));
16597 leave_options.record_gain = record_gain;
16598 leave_options.beeptone = "beep";
16599 res = leave_voicemail(chan, mailbox, &leave_options);
16600 if (!res)
16601 res = 't';
16602 ast_config_destroy(msg_cfg);
16603 free_user(vmu3);
16604 return res;
16605 } else {
16606 /* Sender has no mailbox, can't reply */
16607 ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
16608 ast_play_and_wait(chan, "vm-nobox");
16609 res = 't';
16610 ast_config_destroy(msg_cfg);
16611 return res;
16612 }
16613 }
16614 res = 0;
16615
16616 break;
16617 }
16618
16619 ast_config_destroy(msg_cfg);
16620
16621#ifndef IMAP_STORAGE
16622 if (!res) {
16623 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
16624 vms->heard[msg] = 1;
16625 res = wait_file(chan, vms, vms->fn);
16626 }
16627#endif
16628 return res;
16629}
16630
16631static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
16632 int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
16633 signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro)
16634{
16635 /* Record message & let caller review or re-record it, or set options if applicable */
16636 int res = 0;
16637 int cmd = 0;
16638 int max_attempts = 3;
16639 int attempts = 0;
16640 int recorded = 0;
16641 int msg_exists = 0;
16642 signed char zero_gain = 0;
16643 char tempfile[PATH_MAX];
16644 char *acceptdtmf = "#";
16645 char *canceldtmf = "";
16646 int canceleddtmf = 0;
16647 SCOPE_ENTER(3, "%s: rf: %s fmt: %s type: %s vmu: %s\n",
16648 ast_channel_name(chan), recordfile, fmt, outsidecaller ? "msg" : "greeting",
16649 vmu->mailbox);
16650 /* Note that urgent and private are for flagging messages as such in the future */
16651
16652 /* barf if no pointer passed to store duration in */
16653 if (duration == NULL) {
16654 SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING, "%s\n", "Error play_record_review called without duration pointer\n");
16655 }
16656
16657 if (!outsidecaller)
16658 snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
16659 else
16660 ast_copy_string(tempfile, recordfile, sizeof(tempfile));
16661
16662 cmd = '3'; /* Want to start by recording */
16663
16664 while ((cmd >= 0) && (cmd != 't')) {
16665 switch (cmd) {
16666 case '1':
16667 if (!msg_exists) {
16668 /* In this case, 1 is to record a message */
16669 cmd = '3';
16670 break;
16671 } else {
16672 /* Otherwise 1 is to save the existing message */
16673 ast_verb(3, "Saving message as is\n");
16674 if (!outsidecaller) {
16675 ast_trace(-1, "Renaming greeting '%s' to '%s'\n", tempfile, recordfile);
16676 ast_filerename(tempfile, recordfile, NULL);
16677 }
16678 if (!forwardintro) {
16679 ast_stream_and_wait(chan, "vm-msgsaved", "");
16680 }
16681 if (!outsidecaller) {
16682 /* Saves to IMAP server only if imapgreeting=yes */
16683 ast_trace(-1, "Saving greeting '%s'\n", recordfile);
16684 SCOPE_CALL(-1, STORE, recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag, msg_id);
16685 SCOPE_CALL(-1, DISPOSE, recordfile, -1);
16686 }
16687 cmd = 't';
16688 SCOPE_EXIT_RTN_VALUE(res, "Message saved to %s\n", recordfile);
16689 }
16690 case '2':
16691 /* Review */
16692 ast_verb(3, "Reviewing the message\n");
16693 ast_trace(-1, "Reviewing '%s'\n", tempfile);
16694 cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
16695 break;
16696 case '3':
16697 msg_exists = 0;
16698 /* Record */
16699 if (recorded == 1)
16700 ast_verb(3, "Re-recording the message\n");
16701 else
16702 ast_verb(3, "Recording the message\n");
16703
16704 if (recorded && outsidecaller) {
16705 if (forwardintro) {
16706 cmd = ast_play_and_wait(chan, "vm-record-prepend");
16707 } else {
16708 cmd = ast_play_and_wait(chan, INTRO);
16709 }
16710 cmd = ast_play_and_wait(chan, "beep");
16711 }
16712 if (cmd == -1) {
16713 /* User has hung up, no options to give */
16714 ast_filedelete(tempfile, NULL);
16715 SCOPE_EXIT_RTN_VALUE(cmd, "User hung up before message could be rerecorded. Deleted '%s'\n", tempfile);
16716 }
16717 recorded = 1;
16718 /* 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 */
16719 if (record_gain)
16721 if (ast_test_flag(vmu, VM_OPERATOR))
16722 canceldtmf = "0";
16723 ast_trace(-1, "Recording '%s'\n", tempfile);
16724 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);
16725 if (strchr(canceldtmf, cmd)) {
16726 /* need this flag here to distinguish between pressing '0' during message recording or after */
16727 canceleddtmf = 1;
16728 }
16729 if (record_gain)
16730 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
16731 if (cmd == -1) {
16732 /* User has hung up, no options to give */
16733 if (!outsidecaller) {
16734 /* user was recording a greeting and they hung up, so let's delete the recording. */
16735 ast_filedelete(tempfile, NULL);
16736 }
16737 SCOPE_EXIT_RTN_VALUE(cmd, "User hung up after recording. %s %s\n",
16738 outsidecaller ? "Saved message " : "Deleted greeting \n", tempfile);
16739 }
16740 if (cmd == '0') {
16741 break;
16742 } else if (cmd == '*') {
16743 break;
16744#if 0
16745 } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
16746 /* Message is too short */
16747 ast_verb(3, "Message too short\n");
16748 cmd = ast_play_and_wait(chan, "vm-tooshort");
16749 cmd = ast_filedelete(tempfile, NULL);
16750 break;
16751 } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
16752 /* Message is all silence */
16753 ast_verb(3, "Nothing recorded\n");
16754 cmd = ast_filedelete(tempfile, NULL);
16755 cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
16756 if (!cmd)
16757 cmd = ast_play_and_wait(chan, "vm-speakup");
16758 break;
16759#endif
16760 } else {
16761 /* If all is well, a message exists */
16762 msg_exists = 1;
16763 cmd = 0;
16764 }
16765 break;
16766 case '4':
16767 if (outsidecaller && ast_test_flag(vmu, VM_MARK_URGENT)) { /* only mark vm messages */
16768 /* Mark Urgent */
16769 if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
16770 ast_verb(3, "marking message as Urgent\n");
16771 res = ast_play_and_wait(chan, "vm-marked-urgent");
16772 strcpy(flag, "Urgent");
16773 } else if (flag) {
16774 ast_verb(3, "UNmarking message as Urgent\n");
16775 res = ast_play_and_wait(chan, "vm-marked-nonurgent");
16776 strcpy(flag, "");
16777 } else {
16778 ast_play_and_wait(chan, "vm-sorry");
16779 }
16780 cmd = 0;
16781 } else {
16782 cmd = ast_play_and_wait(chan, "vm-sorry");
16783 }
16784 break;
16785 case '5':
16786 case '6':
16787 case '7':
16788 case '8':
16789 case '9':
16790 case '*':
16791 case '#':
16792 cmd = ast_play_and_wait(chan, "vm-sorry");
16793 break;
16794#if 0
16795/* XXX Commented out for the moment because of the dangers of deleting
16796 a message while recording (can put the message numbers out of sync) */
16797 case '*':
16798 /* Cancel recording, delete message, offer to take another message*/
16799 cmd = ast_play_and_wait(chan, "vm-deleted");
16800 cmd = ast_filedelete(tempfile, NULL);
16801 if (outsidecaller) {
16802 res = vm_exec(chan, NULL);
16803 return res;
16804 }
16805 else
16806 return 1;
16807#endif
16808 case '0':
16809 if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
16810 cmd = ast_play_and_wait(chan, "vm-sorry");
16811 break;
16812 }
16813 if (msg_exists || recorded) {
16814 ast_trace(-1, "Reviewing '%s'\n", tempfile);
16815 cmd = ast_play_and_wait(chan, "vm-saveoper");
16816 if (!cmd)
16817 cmd = ast_waitfordigit(chan, 3000);
16818 if (cmd == '1') {
16819 ast_trace(-1, "Saving '%s' to '%s'\n", tempfile, recordfile);
16820 ast_filerename(tempfile, recordfile, NULL);
16821 ast_play_and_wait(chan, "vm-msgsaved");
16822 cmd = '0';
16823 } else if (cmd == '4') {
16824 if (flag) {
16825 ast_play_and_wait(chan, "vm-marked-urgent");
16826 strcpy(flag, "Urgent");
16827 }
16828 ast_play_and_wait(chan, "vm-msgsaved");
16829 cmd = '0';
16830 } else {
16831 ast_trace(-1, "Deleting '%s'\n", tempfile);
16832 ast_play_and_wait(chan, "vm-deleted");
16833 SCOPE_CALL(-1, DELETE, tempfile, -1, tempfile, vmu);
16834 SCOPE_CALL(-1, DISPOSE, tempfile, -1);
16835 cmd = '0';
16836 }
16837 }
16838 return cmd;
16839 default:
16840 /* If the caller is an outside caller and the review option is enabled or it's forward intro
16841 allow them to review the message, but let the owner of the box review
16842 their OGM's */
16843 if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW) && !forwardintro) {
16844 SCOPE_EXIT_RTN_VALUE(cmd, "Done. Outside caller, review not set, no forwardintro\n");
16845 }
16846 if (msg_exists) {
16847 cmd = ast_play_and_wait(chan, "vm-review");
16848 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_MARK_URGENT)) {
16849 if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
16850 cmd = ast_play_and_wait(chan, "vm-review-urgent");
16851 } else if (flag) {
16852 cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
16853 }
16854 }
16855 } else {
16856 cmd = ast_play_and_wait(chan, "vm-torerecord");
16857 if (!cmd)
16858 cmd = ast_waitfordigit(chan, 600);
16859 }
16860
16861 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
16862 cmd = ast_play_and_wait(chan, "vm-reachoper");
16863 if (!cmd)
16864 cmd = ast_waitfordigit(chan, 600);
16865 }
16866#if 0
16867 if (!cmd)
16868 cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
16869#endif
16870 if (!cmd)
16871 cmd = ast_waitfordigit(chan, 6000);
16872 if (!cmd) {
16873 attempts++;
16874 }
16875 if (attempts > max_attempts) {
16876 cmd = 't';
16877 }
16878 }
16879 }
16880 if (!outsidecaller && (cmd == -1 || cmd == 't')) {
16881 /* Hang up or timeout, so delete the recording. */
16882 ast_trace(-1, "Deleting '%s' on hangup or timeout\n", tempfile);
16883 ast_filedelete(tempfile, NULL);
16884 }
16885
16886 if (cmd != 't' && outsidecaller)
16887 ast_play_and_wait(chan, "vm-goodbye");
16888
16889 SCOPE_EXIT_RTN_VALUE(cmd, "Done\n");
16890}
16891
16893{
16894 struct ast_vm_msg_snapshot *msg_snapshot;
16895
16896 if (!(msg_snapshot = ast_calloc(1, sizeof(*msg_snapshot)))) {
16897 return NULL;
16898 }
16899
16900 if (ast_string_field_init(msg_snapshot, 512)) {
16901 ast_free(msg_snapshot);
16902 return NULL;
16903 }
16904
16905 return msg_snapshot;
16906}
16907
16909{
16910 ast_string_field_free_memory(msg_snapshot);
16911 ast_free(msg_snapshot);
16912
16913 return NULL;
16914}
16915
16916#ifdef TEST_FRAMEWORK
16917
16918static int vm_test_destroy_user(const char *context, const char *mailbox)
16919{
16920 struct ast_vm_user *vmu;
16921
16924 if (!strcmp(context, vmu->context)
16925 && !strcmp(mailbox, vmu->mailbox)) {
16927 ast_free(vmu);
16928 break;
16929 }
16930 }
16933 return 0;
16934}
16935
16936static int vm_test_create_user(const char *context, const char *mailbox)
16937{
16938 struct ast_vm_user *vmu;
16939
16940 if (!(vmu = find_or_create(context, mailbox))) {
16941 return -1;
16942 }
16943 populate_defaults(vmu);
16944 return 0;
16945}
16946
16947#endif
16948
16949/*!
16950 * \brief Create and store off all the msgs in an open mailbox
16951 *
16952 * \note TODO XXX This function should work properly for all
16953 * voicemail storage options, but is far more expensive for
16954 * ODBC at the moment. This is because the RETRIEVE macro
16955 * not only pulls out the message's meta data file from the
16956 * database, but also the actual audio for each message, temporarily
16957 * writing it to the file system. This is an area that needs
16958 * to be made more efficient.
16959 */
16961 struct vm_state *vms,
16962 struct ast_vm_mailbox_snapshot *mailbox_snapshot,
16963 int snapshot_index,
16964 int mailbox_index,
16965 int descending,
16966 enum ast_vm_snapshot_sort_val sort_val)
16967{
16968 struct ast_vm_msg_snapshot *msg_snapshot;
16969 struct ast_vm_msg_snapshot *msg_snapshot_tmp;
16970 struct ast_config *msg_cfg;
16971 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
16972 char filename[PATH_MAX];
16973 const char *value;
16974
16975 for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
16976 int inserted = 0;
16977 /* Find the msg */
16978 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
16979 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
16980 RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
16981 msg_cfg = ast_config_load(filename, config_flags);
16982 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
16983 DISPOSE(vms->curdir, vms->curmsg);
16984 continue;
16985 }
16986
16987 /* Create the snapshot object */
16988 if (!(msg_snapshot = vm_msg_snapshot_alloc())) {
16989 ast_config_destroy(msg_cfg);
16990 return -1;
16991 }
16992
16993 /* Fill in the snapshot object */
16994 if ((value = ast_variable_retrieve(msg_cfg, "message", "msg_id"))) {
16995 ast_string_field_set(msg_snapshot, msg_id, value);
16996 } else {
16997 /* Message snapshots *really* should have a
16998 * message ID. Add one to the message config
16999 * if it does not already exist
17000 */
17001 char id[MSG_ID_LEN];
17002 if (!(add_message_id(msg_cfg, vms->curdir, vms->curmsg,
17003 filename, id, sizeof(id), vmu, mailbox_index))) {
17004 ast_string_field_set(msg_snapshot, msg_id, id);
17005 } else {
17006 ast_log(LOG_WARNING, "Unable to create a message ID for message %s/%d\n", vms->curdir, vms->curmsg);
17007 }
17008 }
17009 if ((value = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
17010 ast_string_field_set(msg_snapshot, callerid, value);
17011 }
17012 if ((value = ast_variable_retrieve(msg_cfg, "message", "callerchan"))) {
17013 ast_string_field_set(msg_snapshot, callerchan, value);
17014 }
17015 if ((value = ast_variable_retrieve(msg_cfg, "message", "exten"))) {
17016 ast_string_field_set(msg_snapshot, exten, value);
17017 }
17018 if ((value = ast_variable_retrieve(msg_cfg, "message", "origdate"))) {
17019 ast_string_field_set(msg_snapshot, origdate, value);
17020 }
17021 if ((value = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
17022 ast_string_field_set(msg_snapshot, origtime, value);
17023 }
17024 if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17025 ast_string_field_set(msg_snapshot, duration, value);
17026 }
17027 if ((value = ast_variable_retrieve(msg_cfg, "message", "flag"))) {
17028 ast_string_field_set(msg_snapshot, flag, value);
17029 }
17030 msg_snapshot->msg_number = vms->curmsg;
17031 ast_string_field_set(msg_snapshot, folder_name, mailbox_folders[mailbox_index]);
17032
17033 /* store msg snapshot in mailbox snapshot */
17034 switch (sort_val) {
17035 default:
17037 if (descending) {
17038 AST_LIST_INSERT_HEAD(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17039 } else {
17040 AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17041 }
17042 inserted = 1;
17043 break;
17045 AST_LIST_TRAVERSE_SAFE_BEGIN(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot_tmp, msg) {
17046 int val = strcmp(msg_snapshot->origtime, msg_snapshot_tmp->origtime);
17047 if (descending && val >= 0) {
17048 AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
17049 inserted = 1;
17050 break;
17051 } else if (!descending && val <= 0) {
17052 AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
17053 inserted = 1;
17054 break;
17055 }
17056 }
17058 break;
17059 }
17060
17061 if (!inserted) {
17062 AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17063 }
17064
17065 mailbox_snapshot->total_msg_num++;
17066
17067 /* cleanup configs and msg */
17068 ast_config_destroy(msg_cfg);
17069 DISPOSE(vms->curdir, vms->curmsg);
17070 }
17071
17072 return 0;
17073}
17074
17075static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_create(const char *mailbox,
17076 const char *context,
17077 const char *folder,
17078 int descending,
17079 enum ast_vm_snapshot_sort_val sort_val,
17080 int combine_INBOX_and_OLD)
17081{
17082 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
17083 struct vm_state vms;
17084 struct ast_vm_user *vmu = NULL, vmus;
17085 int res;
17086 int i;
17087 int this_index_only = -1;
17088 int open = 0;
17089 int inbox_index = get_folder_by_name("INBOX");
17090 int old_index = get_folder_by_name("Old");
17091 int urgent_index = get_folder_by_name("Urgent");
17092
17093 if (ast_strlen_zero(mailbox)) {
17094 ast_log(LOG_WARNING, "Cannot create a mailbox snapshot since no mailbox was specified\n");
17095 return NULL;
17096 }
17097
17098 memset(&vmus, 0, sizeof(vmus));
17099
17100 if (!(ast_strlen_zero(folder))) {
17101 /* find the folder index */
17102 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
17103 if (!strcasecmp(mailbox_folders[i], folder)) {
17104 this_index_only = i;
17105 break;
17106 }
17107 }
17108 if (this_index_only == -1) {
17109 /* Folder was specified and it did not match any folder in our list */
17110 return NULL;
17111 }
17112 }
17113
17114 if (!(vmu = find_user(&vmus, context, mailbox))) {
17115 ast_log(AST_LOG_WARNING, "Failed to create mailbox snapshot for unknown voicemail user %s@%s\n", mailbox, context);
17116 return NULL;
17117 }
17118
17119 if (!(mailbox_snapshot = ast_calloc(1, sizeof(*mailbox_snapshot)))) {
17120 ast_log(AST_LOG_ERROR, "Failed to allocate memory for mailbox snapshot\n");
17121 free_user(vmu);
17122 return NULL;
17123 }
17124
17125 if (!(mailbox_snapshot->snapshots = ast_calloc(ARRAY_LEN(mailbox_folders), sizeof(*mailbox_snapshot->snapshots)))) {
17126 ast_free(mailbox_snapshot);
17127 free_user(vmu);
17128 return NULL;
17129 }
17130
17131 mailbox_snapshot->folders = ARRAY_LEN(mailbox_folders);
17132
17133 for (i = 0; i < mailbox_snapshot->folders; i++) {
17134 int msg_folder_index = i;
17135
17136 /* We want this message in the snapshot if any of the following:
17137 * No folder was specified.
17138 * The specified folder matches the current folder.
17139 * The specified folder is INBOX AND we were asked to combine messages AND the current folder is either Old or Urgent.
17140 */
17141 if (!(this_index_only == -1 || this_index_only == i || (this_index_only == inbox_index && combine_INBOX_and_OLD && (i == old_index || i == urgent_index)))) {
17142 continue;
17143 }
17144
17145 /* Make sure that Old or Urgent messages are marked as being in INBOX. */
17146 if (combine_INBOX_and_OLD && (i == old_index || i == urgent_index)) {
17147 msg_folder_index = inbox_index;
17148 }
17149
17150 memset(&vms, 0, sizeof(vms));
17151 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17152 vms.lastmsg = -1;
17153 open = 0;
17154
17155 /* open the mailbox state */
17156 if ((res = open_mailbox(&vms, vmu, i)) < 0) {
17157 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17158 goto snapshot_cleanup;
17159 }
17160 open = 1;
17161
17162 /* Iterate through each msg, storing off info */
17163 if (vms.lastmsg != -1) {
17164 if ((vm_msg_snapshot_create(vmu, &vms, mailbox_snapshot, msg_folder_index, i, descending, sort_val))) {
17165 ast_log(LOG_WARNING, "Failed to create msg snapshots for %s@%s\n", mailbox, context);
17166 goto snapshot_cleanup;
17167 }
17168 }
17169
17170 /* close mailbox */
17171 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17172 goto snapshot_cleanup;
17173 }
17174 open = 0;
17175 }
17176
17177snapshot_cleanup:
17178 if (vmu && open) {
17179 close_mailbox(&vms, vmu);
17180 }
17181
17182#ifdef IMAP_STORAGE
17183 if (vmu) {
17184 vmstate_delete(&vms);
17185 }
17186#endif
17187
17188 free_user(vmu);
17189 return mailbox_snapshot;
17190}
17191
17193{
17194 int i;
17195 struct ast_vm_msg_snapshot *msg_snapshot;
17196
17197 for (i = 0; i < mailbox_snapshot->folders; i++) {
17198 while ((msg_snapshot = AST_LIST_REMOVE_HEAD(&mailbox_snapshot->snapshots[i], msg))) {
17199 msg_snapshot = vm_msg_snapshot_destroy(msg_snapshot);
17200 }
17201 }
17202 ast_free(mailbox_snapshot->snapshots);
17203 ast_free(mailbox_snapshot);
17204 return NULL;
17205}
17206
17207/*!
17208 * \brief common bounds checking and existence check for Voicemail API functions.
17209 *
17210 * \details
17211 * This is called by vm_msg_move, vm_msg_remove, and vm_msg_forward to
17212 * ensure that data passed in are valid. This ensures that given the
17213 * desired message IDs, they can be found.
17214 *
17215 * \param vms The voicemail state corresponding to an open mailbox
17216 * \param msg_ids An array of message identifiers
17217 * \param num_msgs The number of identifiers in msg_ids
17218 * \param[out] msg_nums The message indexes corresponding to the given
17219 * \param vmu
17220 * message IDs
17221 * \pre vms must have open_mailbox() called on it prior to this function.
17222 *
17223 * \retval -1 Failure
17224 * \retval 0 Success
17225 */
17226static 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)
17227{
17228 int i;
17229 int res = 0;
17230 for (i = 0; i < num_msgs; ++i) {
17231 const char *msg_id = msg_ids[i];
17232 int found = 0;
17233 for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
17234 const char *other_msg_id;
17235 char filename[PATH_MAX];
17236 struct ast_config *msg_cfg;
17237 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17238
17239 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
17240 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
17241 RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
17242 msg_cfg = ast_config_load(filename, config_flags);
17243 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17244 DISPOSE(vms->curdir, vms->curmsg);
17245 res = -1;
17246 goto done;
17247 }
17248
17249 other_msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
17250
17251 if (!ast_strlen_zero(other_msg_id) && !strcmp(other_msg_id, msg_id)) {
17252 /* Message found. We can get out of this inner loop
17253 * and move on to the next message to find
17254 */
17255 found = 1;
17256 msg_nums[i] = vms->curmsg;
17257 ast_config_destroy(msg_cfg);
17258 DISPOSE(vms->curdir, vms->curmsg);
17259 break;
17260 }
17261 ast_config_destroy(msg_cfg);
17262 DISPOSE(vms->curdir, vms->curmsg);
17263 }
17264 if (!found) {
17265 /* If we can't find one of the message IDs requested, then OH NO! */
17266 res = -1;
17267 goto done;
17268 }
17269 }
17270
17271done:
17272 return res;
17273}
17274
17275static void notify_new_state(struct ast_vm_user *vmu)
17276{
17277 int new = 0, old = 0, urgent = 0;
17278 char ext_context[1024];
17279
17280 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
17281 run_externnotify(vmu->context, vmu->mailbox, NULL);
17282 ast_app_inboxcount2(ext_context, &urgent, &new, &old);
17283 queue_mwi_event(NULL, ext_context, urgent, new, old);
17284}
17285
17286static int vm_msg_forward(const char *from_mailbox,
17287 const char *from_context,
17288 const char *from_folder,
17289 const char *to_mailbox,
17290 const char *to_context,
17291 const char *to_folder,
17292 size_t num_msgs,
17293 const char *msg_ids [],
17294 int delete_old)
17295{
17296 struct vm_state from_vms;
17297 struct ast_vm_user *vmu = NULL, vmus;
17298 struct ast_vm_user *to_vmu = NULL, to_vmus;
17299 struct ast_config *msg_cfg;
17300 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17301 char filename[PATH_MAX];
17302 int from_folder_index;
17303 int open = 0;
17304 int res = 0;
17305 int i;
17306 int *msg_nums;
17307
17308 if (ast_strlen_zero(from_mailbox) || ast_strlen_zero(to_mailbox)) {
17309 ast_log(LOG_WARNING, "Cannot forward message because either the from or to mailbox was not specified\n");
17310 return -1;
17311 }
17312
17313 if (!num_msgs) {
17314 ast_log(LOG_WARNING, "Invalid number of messages specified to forward: %zu\n", num_msgs);
17315 return -1;
17316 }
17317
17318 if (ast_strlen_zero(from_folder) || ast_strlen_zero(to_folder)) {
17319 ast_log(LOG_WARNING, "Cannot forward message because the from_folder or to_folder was not specified\n");
17320 return -1;
17321 }
17322
17323 memset(&vmus, 0, sizeof(vmus));
17324 memset(&to_vmus, 0, sizeof(to_vmus));
17325 memset(&from_vms, 0, sizeof(from_vms));
17326
17327 from_folder_index = get_folder_by_name(from_folder);
17328 if (from_folder_index == -1) {
17329 return -1;
17330 }
17331
17332 if (get_folder_by_name(to_folder) == -1) {
17333 return -1;
17334 }
17335
17336 if (!(vmu = find_user(&vmus, from_context, from_mailbox))) {
17337 ast_log(LOG_WARNING, "Can't find voicemail user to forward from (%s@%s)\n", from_mailbox, from_context);
17338 return -1;
17339 }
17340
17341 if (!(to_vmu = find_user(&to_vmus, to_context, to_mailbox))) {
17342 ast_log(LOG_WARNING, "Can't find voicemail user to forward to (%s@%s)\n", to_mailbox, to_context);
17343 free_user(vmu);
17344 return -1;
17345 }
17346
17347 ast_copy_string(from_vms.username, from_mailbox, sizeof(from_vms.username));
17348 from_vms.lastmsg = -1;
17349 open = 0;
17350
17351 /* open the mailbox state */
17352 if ((res = open_mailbox(&from_vms, vmu, from_folder_index)) < 0) {
17353 ast_log(LOG_WARNING, "Could not open mailbox %s\n", from_mailbox);
17354 res = -1;
17355 goto vm_forward_cleanup;
17356 }
17357
17358 open = 1;
17359
17360 if ((from_vms.lastmsg + 1) < num_msgs) {
17361 ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", from_folder, num_msgs);
17362 res = -1;
17363 goto vm_forward_cleanup;
17364 }
17365
17366 msg_nums = ast_alloca(sizeof(int) * num_msgs);
17367
17368 if ((res = message_range_and_existence_check(&from_vms, msg_ids, num_msgs, msg_nums, vmu) < 0)) {
17369 goto vm_forward_cleanup;
17370 }
17371
17372 /* Now we actually forward the messages */
17373 for (i = 0; i < num_msgs; i++) {
17374 int cur_msg = msg_nums[i];
17375 int duration = 0;
17376 const char *value;
17377
17378 make_file(from_vms.fn, sizeof(from_vms.fn), from_vms.curdir, cur_msg);
17379 snprintf(filename, sizeof(filename), "%s.txt", from_vms.fn);
17380 RETRIEVE(from_vms.curdir, cur_msg, vmu->mailbox, vmu->context);
17381 msg_cfg = ast_config_load(filename, config_flags);
17382 /* XXX This likely will not fail since we previously ensured that the
17383 * message we are looking for exists. However, there still could be some
17384 * circumstance where this fails, so atomicity is not guaranteed.
17385 */
17386 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17387 DISPOSE(from_vms.curdir, cur_msg);
17388 continue;
17389 }
17390 if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17391 duration = atoi(value);
17392 }
17393
17394 copy_message(NULL, vmu, from_folder_index, cur_msg, duration, to_vmu, vmfmts, from_vms.curdir, "", to_folder);
17395
17396 if (delete_old) {
17397 from_vms.deleted[cur_msg] = 1;
17398 }
17399 ast_config_destroy(msg_cfg);
17400 DISPOSE(from_vms.curdir, cur_msg);
17401 }
17402
17403 /* close mailbox */
17404 if ((res = close_mailbox(&from_vms, vmu) == ERROR_LOCK_PATH)) {
17405 res = -1;
17406 goto vm_forward_cleanup;
17407 }
17408 open = 0;
17409
17410vm_forward_cleanup:
17411 if (vmu && open) {
17412 close_mailbox(&from_vms, vmu);
17413 }
17414#ifdef IMAP_STORAGE
17415 if (vmu) {
17416 vmstate_delete(&from_vms);
17417 }
17418#endif
17419
17420 if (!res) {
17421 notify_new_state(to_vmu);
17422 }
17423
17424 free_user(vmu);
17425 free_user(to_vmu);
17426 return res;
17427}
17428
17429static int vm_msg_move(const char *mailbox,
17430 const char *context,
17431 size_t num_msgs,
17432 const char *oldfolder,
17433 const char *old_msg_ids [],
17434 const char *newfolder)
17435{
17436 struct vm_state vms;
17437 struct ast_vm_user *vmu = NULL, vmus;
17438 int old_folder_index;
17439 int new_folder_index;
17440 int open = 0;
17441 int res = 0;
17442 int i;
17443 int *old_msg_nums;
17444
17445 if (ast_strlen_zero(mailbox)) {
17446 ast_log(LOG_WARNING, "Cannot move message because no mailbox was specified\n");
17447 return -1;
17448 }
17449
17450 if (!num_msgs) {
17451 ast_log(LOG_WARNING, "Invalid number of messages specified to move: %zu\n", num_msgs);
17452 return -1;
17453 }
17454
17455 if (ast_strlen_zero(oldfolder) || ast_strlen_zero(newfolder)) {
17456 ast_log(LOG_WARNING, "Cannot move message because either oldfolder or newfolder was not specified\n");
17457 return -1;
17458 }
17459
17460 old_folder_index = get_folder_by_name(oldfolder);
17461 new_folder_index = get_folder_by_name(newfolder);
17462
17463 memset(&vmus, 0, sizeof(vmus));
17464 memset(&vms, 0, sizeof(vms));
17465
17466 if (old_folder_index == -1 || new_folder_index == -1) {
17467 return -1;
17468 }
17469
17470 if (!(vmu = find_user(&vmus, context, mailbox))) {
17471 return -1;
17472 }
17473
17474 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17475 vms.lastmsg = -1;
17476 open = 0;
17477
17478 /* open the mailbox state */
17479 if ((res = open_mailbox(&vms, vmu, old_folder_index)) < 0) {
17480 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17481 res = -1;
17482 goto vm_move_cleanup;
17483 }
17484
17485 open = 1;
17486
17487 if ((vms.lastmsg + 1) < num_msgs) {
17488 ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", oldfolder, num_msgs);
17489 res = -1;
17490 goto vm_move_cleanup;
17491 }
17492
17493 old_msg_nums = ast_alloca(sizeof(int) * num_msgs);
17494
17495 if ((res = message_range_and_existence_check(&vms, old_msg_ids, num_msgs, old_msg_nums, vmu)) < 0) {
17496 goto vm_move_cleanup;
17497 }
17498
17499 /* Now actually move the message */
17500 for (i = 0; i < num_msgs; ++i) {
17501 if (save_to_folder(vmu, &vms, old_msg_nums[i], new_folder_index, NULL, 0)) {
17502 res = -1;
17503 goto vm_move_cleanup;
17504 }
17505 vms.deleted[old_msg_nums[i]] = 1;
17506 }
17507
17508 /* close mailbox */
17509 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17510 res = -1;
17511 goto vm_move_cleanup;
17512 }
17513 open = 0;
17514
17515vm_move_cleanup:
17516 if (vmu && open) {
17517 close_mailbox(&vms, vmu);
17518 }
17519#ifdef IMAP_STORAGE
17520 if (vmu) {
17521 vmstate_delete(&vms);
17522 }
17523#endif
17524
17525 if (!res) {
17526 notify_new_state(vmu);
17527 }
17528
17529 free_user(vmu);
17530 return res;
17531}
17532
17533static int vm_msg_remove(const char *mailbox,
17534 const char *context,
17535 size_t num_msgs,
17536 const char *folder,
17537 const char *msgs[])
17538{
17539 struct vm_state vms;
17540 struct ast_vm_user *vmu = NULL, vmus;
17541 int folder_index;
17542 int open = 0;
17543 int res = 0;
17544 int i;
17545 int *msg_nums;
17546
17547 if (ast_strlen_zero(mailbox)) {
17548 ast_log(LOG_WARNING, "Cannot remove message because no mailbox was specified\n");
17549 return -1;
17550 }
17551
17552 if (!num_msgs) {
17553 ast_log(LOG_WARNING, "Invalid number of messages specified to remove: %zu\n", num_msgs);
17554 return -1;
17555 }
17556
17557 if (ast_strlen_zero(folder)) {
17558 ast_log(LOG_WARNING, "Cannot remove message because no folder was specified\n");
17559 return -1;
17560 }
17561
17562 memset(&vmus, 0, sizeof(vmus));
17563 memset(&vms, 0, sizeof(vms));
17564
17565 folder_index = get_folder_by_name(folder);
17566 if (folder_index == -1) {
17567 ast_log(LOG_WARNING, "Could not remove msgs from unknown folder %s\n", folder);
17568 return -1;
17569 }
17570
17571 if (!(vmu = find_user(&vmus, context, mailbox))) {
17572 ast_log(LOG_WARNING, "Can't find voicemail user to remove msg from (%s@%s)\n", mailbox, context);
17573 return -1;
17574 }
17575
17576 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17577 vms.lastmsg = -1;
17578 open = 0;
17579
17580 /* open the mailbox state */
17581 if ((res = open_mailbox(&vms, vmu, folder_index)) < 0) {
17582 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17583 res = -1;
17584 goto vm_remove_cleanup;
17585 }
17586
17587 open = 1;
17588
17589 if ((vms.lastmsg + 1) < num_msgs) {
17590 ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", folder, num_msgs);
17591 res = -1;
17592 goto vm_remove_cleanup;
17593 }
17594
17595 msg_nums = ast_alloca(sizeof(int) * num_msgs);
17596
17597 if ((res = message_range_and_existence_check(&vms, msgs, num_msgs, msg_nums, vmu)) < 0) {
17598 goto vm_remove_cleanup;
17599 }
17600
17601 for (i = 0; i < num_msgs; i++) {
17602 vms.deleted[msg_nums[i]] = 1;
17603 }
17604
17605 /* close mailbox */
17606 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17607 res = -1;
17608 ast_log(AST_LOG_ERROR, "Failed to close mailbox folder %s while removing msgs\n", folder);
17609 goto vm_remove_cleanup;
17610 }
17611 open = 0;
17612
17613vm_remove_cleanup:
17614 if (vmu && open) {
17615 close_mailbox(&vms, vmu);
17616 }
17617#ifdef IMAP_STORAGE
17618 if (vmu) {
17619 vmstate_delete(&vms);
17620 }
17621#endif
17622
17623 if (!res) {
17624 notify_new_state(vmu);
17625 }
17626
17627 free_user(vmu);
17628 return res;
17629}
17630
17631static int vm_msg_play(struct ast_channel *chan,
17632 const char *mailbox,
17633 const char *context,
17634 const char *folder,
17635 const char *msg_id,
17637{
17638 struct vm_state vms;
17639 struct ast_vm_user *vmu = NULL, vmus;
17640 int res = 0;
17641 int open = 0;
17642 int i;
17643 char filename[PATH_MAX];
17644 struct ast_config *msg_cfg;
17645 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17646 int duration = 0;
17647 const char *value;
17648
17649 if (ast_strlen_zero(mailbox)) {
17650 ast_log(LOG_WARNING, "Cannot play message because no mailbox was specified\n");
17651 return -1;
17652 }
17653
17654 if (ast_strlen_zero(folder)) {
17655 ast_log(LOG_WARNING, "Cannot play message because no folder was specified\n");
17656 return -1;
17657 }
17658
17659 if (ast_strlen_zero(msg_id)) {
17660 ast_log(LOG_WARNING, "Cannot play message because no message number was specified\n");
17661 return -1;
17662 }
17663
17664 memset(&vmus, 0, sizeof(vmus));
17665 memset(&vms, 0, sizeof(vms));
17666
17667 if (ast_strlen_zero(context)) {
17668 context = "default";
17669 }
17670
17671 if (!(vmu = find_user(&vmus, context, mailbox))) {
17672 return -1;
17673 }
17674
17675 i = get_folder_by_name(folder);
17676 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17677 vms.lastmsg = -1;
17678 if ((res = open_mailbox(&vms, vmu, i)) < 0) {
17679 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17680 goto play2_msg_cleanup;
17681 }
17682 open = 1;
17683
17684 if (message_range_and_existence_check(&vms, &msg_id, 1, &vms.curmsg, vmu)) {
17685 res = -1;
17686 goto play2_msg_cleanup;
17687 }
17688
17689 /* Find the msg */
17690 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
17691 snprintf(filename, sizeof(filename), "%s.txt", vms.fn);
17692 RETRIEVE(vms.curdir, vms.curmsg, vmu->mailbox, vmu->context);
17693
17694 msg_cfg = ast_config_load(filename, config_flags);
17695 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17696 DISPOSE(vms.curdir, vms.curmsg);
17697 res = -1;
17698 goto play2_msg_cleanup;
17699 }
17700 if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17701 duration = atoi(value);
17702 }
17703 ast_config_destroy(msg_cfg);
17704
17705#ifdef IMAP_STORAGE
17706 /*IMAP storage stores any prepended message from a forward
17707 * as a separate file from the rest of the message
17708 */
17709 if (!ast_strlen_zero(vms.introfn) && ast_fileexists(vms.introfn, NULL, NULL) > 0) {
17710 wait_file(chan, &vms, vms.introfn);
17711 }
17712#endif
17713 if (cb) {
17714 cb(chan, vms.fn, duration);
17715 } else if ((wait_file(chan, &vms, vms.fn)) < 0) {
17716 ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms.fn);
17717 } else {
17718 res = 0;
17719 }
17720
17721 vms.heard[vms.curmsg] = 1;
17722
17723 /* cleanup configs and msg */
17724 DISPOSE(vms.curdir, vms.curmsg);
17725
17726play2_msg_cleanup:
17727 if (vmu && open) {
17728 close_mailbox(&vms, vmu);
17729 }
17730
17731#ifdef IMAP_STORAGE
17732 if (vmu) {
17733 vmstate_delete(&vms);
17734 }
17735#endif
17736
17737 if (!res) {
17738 notify_new_state(vmu);
17739 }
17740
17741 free_user(vmu);
17742 return res;
17743}
17744
17745/* This is a workaround so that menuselect displays a proper description
17746 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
17747 */
17748
17750 .support_level = AST_MODULE_SUPPORT_CORE,
17751 .load = load_module,
17752 .unload = unload_module,
17753 .reload = reload,
17754 .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
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
static int load_config(void)
#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:1790
ast_mutex_t lock
Definition app_sla.c:337
struct sla_ringing_trunk * last
Definition app_sla.c:338
#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)
static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
vm_option_flags
@ 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)
#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 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 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.
vm_box
@ 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)
vm_passwordlocation
@ 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)
vm_option_args
@ 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 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
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_STRING_FIELD_CMP_FN(stype, field)
Creates a compare function for a structure string field.
Definition astobj2.h:2048
#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.
#define AO2_STRING_FIELD_HASH_FN(stype, field)
Creates a hash function for a structure string field.
Definition astobj2.h:2032
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
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
static int priority
static char language[MAX_LANGUAGE]
Definition chan_iax2.c:361
static const char type[]
static int transfer(void *data)
charset
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:3213
const char * ast_channel_name(const struct ast_channel *chan)
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition channel.c:2574
#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:2989
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:446
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:3025
#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:7496
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:6615
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition channel.c:2839
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:4332
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:2990
#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:1965
ast_channel_state
ast_channel states
@ AST_STATE_DOWN
@ AST_STATE_UP
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, int rdlock)
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition cli.h:45
#define CLI_SUCCESS
Definition cli.h:44
#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:75
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition dsp.c:2196
char * end
Definition eagi_proxy.c:73
char buf[BUFSIZE]
Definition eagi_proxy.c:66
static char * config_filename
Definition extconf.c:2118
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.
@ 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 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:2042
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition manager.c:2000
void ast_manager_publish_event(const char *type, int class_type, struct ast_json *obj)
Publish an event to AMI.
Definition manager.c:646
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:2078
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition manager.c:2032
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition manager.c:1661
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition manager.c:2086
void astman_append(struct mansession *s, const char *fmt,...)
Definition manager.c:1921
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition manager.c:7716
#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:145
const char * ext
Definition http.c:151
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:2150
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:1466
@ 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:2160
enum AST_LOCK_RESULT ast_lock_path(const char *path)
Lock a filesystem path.
Definition main/app.c:2615
#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:3208
#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:827
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:1617
#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:3067
int ast_unlock_path(const char *path)
Unlock a path.
Definition main/app.c:2631
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:3203
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:3324
int ast_update2_realtime(const char *family,...) attribute_sentinel
Update realtime configuration.
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition extconf.c:3272
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
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.
void ast_category_append(struct ast_config *config, struct ast_category *category)
Appends a category to a config.
Definition extconf.c:2831
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition extconf.c:1175
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.
struct ast_category * ast_category_new(const char *name, const char *in_file, int lineno)
Create a category.
Definition extconf.c:2786
#define ast_variable_new(name, value, filename)
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
#define CONFIG_STATUS_FILEUNCHANGED
@ CONFIG_FLAG_NOCACHE
@ CONFIG_FLAG_WITHCOMMENTS
@ CONFIG_FLAG_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Destroy realtime configuration.
void ast_category_destroy(struct ast_category *cat)
Definition extconf.c:2843
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition extconf.c:1287
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
int ast_store_realtime(const char *family,...) attribute_sentinel
Create realtime configuration.
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition extconf.c:1260
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition extconf.c:1213
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name, const char *filter)
Retrieve a category if it exists.
#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.
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
#define AST_LIST_HEAD_NOLOCK_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
#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.
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
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:193
#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:404
@ 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
#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:4211
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition pbx_app.c:483
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:1563
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition pbx.c:8825
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:4226
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 void check_password(void)
Definition pbx_dundi.c:2192
static char email[80]
Definition pbx_dundi.c:215
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 struct stasis_subscription * sub
Statsd channel stats. Exmaple of how to subscribe to Stasis events.
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 load_users(void)
static void cleanup(void)
Clean up any old apps that we don't need any more.
Definition res_stasis.c:327
static struct @522 args
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:8377
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:8341
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:8365
SMDI support for Asterisk.
int AST_OPTIONAL_API_NAME() ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
Unset the MWI indicator for a mailbox.
Definition res_smdi.c:320
int AST_OPTIONAL_API_NAME() 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_interface *AST_OPTIONAL_API_NAME() ast_smdi_interface_find(const char *iface_name)
Find an SMDI interface with the specified name.
Definition res_smdi.c:569
struct ast_smdi_mwi_message *AST_OPTIONAL_API_NAME() ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout, const char *station)
Definition res_smdi.c:562
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
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
size_t attribute_pure ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition strings.h:730
#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:2233
#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:2480
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:2201
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:2250
#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
char *attribute_pure ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition strings.h:761
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
struct ast_app::@406 list
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.
char context[AST_MAX_CONTEXT]
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:220
unsigned int flags
Definition utils.h:221
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.
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::@192 * snapshots
const ast_string_field origtime
struct ast_vm_msg_snapshot::@191 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 serveremail[80]
char callback[80]
char fullname[80]
char uniqueid[80]
char context[MAX_VM_CONTEXT_LEN]
char pager[80]
char mailbox[MAX_VM_MBOX_ID_LEN]
struct ast_vm_user::@86 list
char zonetag[80]
char password[80]
char fromstring[100]
char dialout[80]
char * emailsubject
char exit[80]
unsigned int flags
char locale[20]
char language[MAX_LANGUAGE]
char attachfmt[20]
All configuration options for http media cache.
structure to hold extensions
char mailbox[0]
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.
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
int * deleted
char curdir[PATH_MAX]
char curbox[80]
char username[80]
char intro[PATH_MAX]
char fn[PATH_MAX]
char context[80]
char vmbox[PATH_MAX]
char msg_format[512]
char name[80]
struct vm_zone::@87 list
char timezone[80]
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.
#define ast_taskprocessor_push(tps, task_exe, datap)
#define AST_TASKPROCESSOR_HIGH_WATER_LEVEL
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
static struct test_options options
static struct test_val a
static struct test_val d
static struct test_val c
#define TEST_EXTENSION
#define TEST_CONTEXT
Definitions to aid in the use of thread local storage.
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
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:2280
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:64
#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:981
int ast_file_is_readable(const char *filename)
Test that a file exists and is readable by the effective user.
Definition utils.c:3141
#define ast_pthread_create(a, b, c, d)
Definition utils.h:624
#define ast_set2_flag(p, value, flag)
Definition utils.h:95
#define ast_clear_flag(p, flag)
Definition utils.h:78
long int ast_random(void)
Definition utils.c:2346
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition utils.c:2513
#define ast_set_flag(p, flag)
Definition utils.h:71
#define ARRAY_LEN(a)
Definition utils.h:706
#define ast_copy_flags(dest, src, flagz)
Definition utils.h:85
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:700
#define AST_FLAGS_ALL
Definition utils.h:217