Asterisk - The Open Source Telephony Project GIT-master-f36a736
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 <synopsis>
115 Leave a Voicemail message.
116 </synopsis>
117 <syntax>
118 <parameter name="mailboxs" argsep="&amp;" required="true">
119 <argument name="mailbox1" argsep="@" required="true">
120 <argument name="mailbox" required="true" />
121 <argument name="context" />
122 </argument>
123 <argument name="mailbox2" argsep="@" multiple="true">
124 <argument name="mailbox" required="true" />
125 <argument name="context" />
126 </argument>
127 </parameter>
128 <parameter name="options">
129 <optionlist>
130 <option name="b">
131 <para>Play the <literal>busy</literal> greeting to the calling party.</para>
132 </option>
133 <option name="d">
134 <argument name="c" />
135 <para>Accept digits for a new extension in context <replaceable>c</replaceable>,
136 if played during the greeting. Context defaults to the current context.</para>
137 </option>
138 <option name="e">
139 <para>Play greetings as early media -- only answer the channel just
140 before accepting the voice message.</para>
141 </option>
142 <option name="g">
143 <argument name="#" required="true" />
144 <para>Use the specified amount of gain when recording the voicemail
145 message. The units are whole-number decibels (dB). Only works on supported
146 technologies, which is DAHDI only.</para>
147 </option>
148 <option name="s">
149 <para>Skip the playback of instructions for leaving a message to the
150 calling party.</para>
151 </option>
152 <option name="S">
153 <para>Skip the playback of instructions for leaving a message to the
154 calling party, but only if a greeting has been recorded by the
155 mailbox user.</para>
156 </option>
157 <option name="t">
158 <argument name="x" required="false" />
159 <para>Play a custom beep tone to the caller instead of the default one.
160 If this option is used but no file is specified, the beep is suppressed.</para>
161 </option>
162 <option name="u">
163 <para>Play the <literal>unavailable</literal> greeting.</para>
164 </option>
165 <option name="U">
166 <para>Mark message as <literal>URGENT</literal>.</para>
167 </option>
168 <option name="P">
169 <para>Mark message as <literal>PRIORITY</literal>.</para>
170 </option>
171 </optionlist>
172 </parameter>
173 </syntax>
174 <description>
175 <para>This application allows the calling party to leave a message for the specified
176 list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
177 the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
178 exist.</para>
179 <para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
180 <enumlist>
181 <enum name="0">
182 <para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
183 </enum>
184 <enum name="*">
185 <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
186 </enum>
187 </enumlist>
188 <para>This application will set the following channel variable upon completion:</para>
189 <variablelist>
190 <variable name="VMSTATUS">
191 <para>This indicates the status of the execution of the VoiceMail application.</para>
192 <value name="SUCCESS" />
193 <value name="USEREXIT" />
194 <value name="FAILED" />
195 </variable>
196 </variablelist>
197 </description>
198 <see-also>
199 <ref type="application">VoiceMailMain</ref>
200 </see-also>
201 </application>
202 <application name="VoiceMailMain" language="en_US">
203 <synopsis>
204 Check Voicemail messages.
205 </synopsis>
206 <syntax>
207 <parameter name="mailbox" required="true" argsep="@">
208 <argument name="mailbox" />
209 <argument name="context" />
210 </parameter>
211 <parameter name="options">
212 <optionlist>
213 <option name="p">
214 <para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
215 the mailbox that is entered by the caller.</para>
216 </option>
217 <option name="g">
218 <argument name="#" required="true" />
219 <para>Use the specified amount of gain when recording a voicemail message.
220 The units are whole-number decibels (dB).</para>
221 </option>
222 <option name="r">
223 <para>"Read only". Prevent user from deleting any messages.</para>
224 <para>This applies only to specific executions of VoiceMailMain, NOT the mailbox itself.</para>
225 </option>
226 <option name="s">
227 <para>Skip checking the passcode for the mailbox.</para>
228 </option>
229 <option name="a">
230 <argument name="folder" required="true" />
231 <para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
232 Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
233 <enumlist>
234 <enum name="0"><para>INBOX</para></enum>
235 <enum name="1"><para>Old</para></enum>
236 <enum name="2"><para>Work</para></enum>
237 <enum name="3"><para>Family</para></enum>
238 <enum name="4"><para>Friends</para></enum>
239 <enum name="5"><para>Cust1</para></enum>
240 <enum name="6"><para>Cust2</para></enum>
241 <enum name="7"><para>Cust3</para></enum>
242 <enum name="8"><para>Cust4</para></enum>
243 <enum name="9"><para>Cust5</para></enum>
244 </enumlist>
245 </option>
246 </optionlist>
247 </parameter>
248 </syntax>
249 <description>
250 <para>This application allows the calling party to check voicemail messages. A specific
251 <replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
252 may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
253 be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
254 <literal>default</literal> context will be used.</para>
255 <para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
256 or Password, and the extension exists:</para>
257 <enumlist>
258 <enum name="*">
259 <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
260 </enum>
261 </enumlist>
262 </description>
263 <see-also>
264 <ref type="application">VoiceMail</ref>
265 </see-also>
266 </application>
267 <application name="VMAuthenticate" language="en_US">
268 <synopsis>
269 Authenticate with Voicemail passwords.
270 </synopsis>
271 <syntax>
272 <parameter name="mailbox" required="true" argsep="@">
273 <argument name="mailbox" />
274 <argument name="context" />
275 </parameter>
276 <parameter name="options">
277 <optionlist>
278 <option name="s">
279 <para>Skip playing the initial prompts.</para>
280 </option>
281 </optionlist>
282 </parameter>
283 </syntax>
284 <description>
285 <para>This application behaves the same way as the Authenticate application, but the passwords
286 are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
287 specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
288 is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
289 mailbox.</para>
290 <para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
291 or Password, and the extension exists:</para>
292 <enumlist>
293 <enum name="*">
294 <para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
295 </enum>
296 </enumlist>
297 </description>
298 </application>
299 <application name="VoiceMailPlayMsg" language="en_US">
300 <synopsis>
301 Play a single voice mail msg from a mailbox by msg id.
302 </synopsis>
303 <syntax>
304 <parameter name="mailbox" required="true" argsep="@">
305 <argument name="mailbox" />
306 <argument name="context" />
307 </parameter>
308 <parameter name="msg_id" required="true">
309 <para>The msg id of the msg to play back. </para>
310 </parameter>
311 </syntax>
312 <description>
313 <para>This application sets the following channel variable upon completion:</para>
314 <variablelist>
315 <variable name="VOICEMAIL_PLAYBACKSTATUS">
316 <para>The status of the playback attempt as a text string.</para>
317 <value name="SUCCESS"/>
318 <value name="FAILED"/>
319 </variable>
320 </variablelist>
321 </description>
322 </application>
323 <application name="VMSayName" language="en_US">
324 <synopsis>
325 Play the name of a voicemail user
326 </synopsis>
327 <syntax>
328 <parameter name="mailbox" required="true" argsep="@">
329 <argument name="mailbox" />
330 <argument name="context" />
331 </parameter>
332 </syntax>
333 <description>
334 <para>This application will say the recorded name of the voicemail user specified as the
335 argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
336 <para>Similar to the Background() application, playback of the recorded
337 name can be interrupted by entering an extension, which will be searched
338 for in the current context.</para>
339 </description>
340 </application>
341 <function name="VM_INFO" language="en_US">
342 <synopsis>
343 Returns the selected attribute from a mailbox.
344 </synopsis>
345 <syntax argsep=",">
346 <parameter name="mailbox" argsep="@" required="true">
347 <argument name="mailbox" required="true" />
348 <argument name="context" />
349 </parameter>
350 <parameter name="attribute" required="true">
351 <optionlist>
352 <option name="count">
353 <para>Count of messages in specified <replaceable>folder</replaceable>.
354 If <replaceable>folder</replaceable> is not specified, defaults to <literal>INBOX</literal>.</para>
355 </option>
356 <option name="email">
357 <para>E-mail address associated with the mailbox.</para>
358 </option>
359 <option name="exists">
360 <para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.</para>
361 </option>
362 <option name="fullname">
363 <para>Full name associated with the mailbox.</para>
364 </option>
365 <option name="language">
366 <para>Mailbox language if overridden, otherwise the language of the channel.</para>
367 </option>
368 <option name="locale">
369 <para>Mailbox locale if overridden, otherwise global locale.</para>
370 </option>
371 <option name="pager">
372 <para>Pager e-mail address associated with the mailbox.</para>
373 </option>
374 <option name="password">
375 <para>Mailbox access password.</para>
376 </option>
377 <option name="tz">
378 <para>Mailbox timezone if overridden, otherwise global timezone</para>
379 </option>
380 </optionlist>
381 </parameter>
382 <parameter name="folder" required="false">
383 <para>If not specified, <literal>INBOX</literal> is assumed.</para>
384 </parameter>
385 </syntax>
386 <description>
387 <para>Returns the selected attribute from the specified <replaceable>mailbox</replaceable>.
388 If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
389 context. Where the <replaceable>folder</replaceable> can be specified, common folders
390 include <literal>INBOX</literal>, <literal>Old</literal>, <literal>Work</literal>,
391 <literal>Family</literal> and <literal>Friends</literal>.</para>
392 </description>
393 </function>
394 <manager name="VoicemailUsersList" language="en_US">
395 <synopsis>
396 List All Voicemail User Information.
397 </synopsis>
398 <syntax>
399 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
400 </syntax>
401 <description>
402 </description>
403 </manager>
404 <manager name="VoicemailUserStatus" language="en_US">
405 <synopsis>
406 Show the status of given voicemail user's info.
407 </synopsis>
408 <syntax>
409 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
410 <parameter name="Context" required="true">
411 <para>The context you want to check.</para>
412 </parameter>
413 <parameter name="Mailbox" required="true">
414 <para>The mailbox you want to check.</para>
415 </parameter>
416 </syntax>
417 <description>
418 <para>Retrieves the status of the given voicemail user.</para>
419 </description>
420 </manager>
421 <manager name="VoicemailRefresh" language="en_US">
422 <synopsis>
423 Tell Asterisk to poll mailboxes for a change
424 </synopsis>
425 <syntax>
426 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
427 <parameter name="Context" />
428 <parameter name="Mailbox" />
429 </syntax>
430 <description>
431 <para>Normally, MWI indicators are only sent when Asterisk itself
432 changes a mailbox. With external programs that modify the content
433 of a mailbox from outside the application, an option exists called
434 <literal>pollmailboxes</literal> that will cause voicemail to
435 continually scan all mailboxes on a system for changes. This can
436 cause a large amount of load on a system. This command allows
437 external applications to signal when a particular mailbox has
438 changed, thus permitting external applications to modify mailboxes
439 and MWI to work without introducing considerable CPU load.</para>
440 <para>If <replaceable>Context</replaceable> is not specified, all
441 mailboxes on the system will be polled for changes. If
442 <replaceable>Context</replaceable> is specified, but
443 <replaceable>Mailbox</replaceable> is omitted, then all mailboxes
444 within <replaceable>Context</replaceable> will be polled.
445 Otherwise, only a single mailbox will be polled for changes.</para>
446 </description>
447 </manager>
448 <manager name="VoicemailBoxSummary" language="en_US">
449 <synopsis>
450 Show the mailbox contents of given voicemail user.
451 </synopsis>
452 <syntax>
453 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
454 <parameter name="Context" required="true">
455 <para>The context you want to check.</para>
456 </parameter>
457 <parameter name="Mailbox" required="true">
458 <para>The mailbox you want to check.</para>
459 </parameter>
460 </syntax>
461 <description>
462 <para>Retrieves the contents of the given voicemail user's mailbox.</para>
463 </description>
464 </manager>
465 <manager name="VoicemailMove" language="en_US">
466 <synopsis>
467 Move Voicemail between mailbox folders of given user.
468 </synopsis>
469 <syntax>
470 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
471 <parameter name="Context" required="true">
472 <para>The context of the Voicemail you want to move.</para>
473 </parameter>
474 <parameter name="Mailbox" required="true">
475 <para>The mailbox of the Voicemail you want to move.</para>
476 </parameter>
477 <parameter name="Folder" required="true">
478 <para>The Folder containing the Voicemail you want to move.</para>
479 </parameter>
480 <parameter name="ID" required="true">
481 <para>The ID of the Voicemail you want to move.</para>
482 </parameter>
483 <parameter name="ToFolder" required="true">
484 <para>The Folder you want to move the Voicemail to.</para>
485 </parameter>
486 </syntax>
487 <description>
488 <para>Move a given Voicemail between Folders within a user's Mailbox.</para>
489 </description>
490 </manager>
491 <manager name="VoicemailRemove" language="en_US">
492 <synopsis>
493 Remove Voicemail from mailbox folder.
494 </synopsis>
495 <syntax>
496 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
497 <parameter name="Context" required="true">
498 <para>The context of the Voicemail you want to remove.</para>
499 </parameter>
500 <parameter name="Mailbox" required="true">
501 <para>The mailbox of the Voicemail you want to remove.</para>
502 </parameter>
503 <parameter name="Folder" required="true">
504 <para>The Folder containing the Voicemail you want to remove.</para>
505 </parameter>
506 <parameter name="ID" required="true">
507 <para>The ID of the Voicemail you want to remove.</para>
508 </parameter>
509 </syntax>
510 <description>
511 <para>Remove a given Voicemail from a user's Mailbox Folder.</para>
512 </description>
513 </manager>
514 <manager name="VoicemailForward" language="en_US">
515 <synopsis>
516 Forward Voicemail from one mailbox folder to another between given users.
517 </synopsis>
518 <syntax>
519 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
520 <parameter name="Context" required="true">
521 <para>The context of the Voicemail you want to move.</para>
522 </parameter>
523 <parameter name="Mailbox" required="true">
524 <para>The mailbox of the Voicemail you want to move.</para>
525 </parameter>
526 <parameter name="Folder" required="true">
527 <para>The Folder containing the Voicemail you want to move.</para>
528 </parameter>
529 <parameter name="ID" required="true">
530 <para>The ID of the Voicemail you want to move.</para>
531 </parameter>
532 <parameter name="ToContext" required="true">
533 <para>The context you want to move the Voicemail to.</para>
534 </parameter>
535 <parameter name="ToMailbox" required="true">
536 <para>The mailbox you want to move the Voicemail to.</para>
537 </parameter>
538 <parameter name="ToFolder" required="true">
539 <para>The Folder you want to move the Voicemail to.</para>
540 </parameter>
541 </syntax>
542 <description>
543 <para>Forward a given Voicemail from a user's Mailbox Folder to
544 another user's Mailbox Folder. Can be used to copy between
545 Folders within a mailbox by specifying the to context and user
546 as the same as the from.</para>
547 </description>
548 </manager>
549 <managerEvent language="en_US" name="VoicemailPasswordChange">
550 <managerEventInstance class="EVENT_FLAG_USER">
551 <synopsis>Raised in response to a mailbox password change.</synopsis>
552 <syntax>
553 <parameter name="Context">
554 <para>Mailbox context.</para>
555 </parameter>
556 <parameter name="Mailbox">
557 <para>Mailbox name.</para>
558 </parameter>
559 <parameter name="NewPassword">
560 <para>New password for mailbox.</para>
561 </parameter>
562 </syntax>
563 </managerEventInstance>
564 </managerEvent>
565 ***/
566
567#ifdef IMAP_STORAGE
568static char imapserver[48] = "localhost";
569static char imapport[8] = "143";
570static char imapflags[128];
571static char imapfolder[64] = "INBOX";
572static char imapparentfolder[64];
573static char greetingfolder[64] = "INBOX";
574static char authuser[32];
575static char authpassword[42];
576static int imapversion = 1;
577
578static int expungeonhangup = 1;
579static int imapgreetings;
580static int imap_poll_logout;
581static char delimiter;
582
583/* mail_open cannot be protected on a stream basis */
584ast_mutex_t mail_open_lock;
585
586struct vm_state;
587struct ast_vm_user;
588
589AST_THREADSTORAGE(ts_vmstate);
590
591/* Forward declarations for IMAP */
592static int init_mailstream(struct vm_state *vms, int box);
593static void write_file(char *filename, char *buffer, unsigned long len);
594static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
595static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
596static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
597static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
598static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
599static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
600static void vmstate_insert(struct vm_state *vms);
601static void vmstate_delete(struct vm_state *vms);
602static void set_update(MAILSTREAM * stream);
603static void init_vm_state(struct vm_state *vms);
604static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
605static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream);
606static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
607static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
608static 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);
609static 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);
610static void update_messages_by_imapuser(const char *user, unsigned long number);
611static int vm_delete(char *file);
612
613static int imap_remove_file (char *dir, int msgnum);
614static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
615static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
616static void check_quota(struct vm_state *vms, char *mailbox);
617static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
618static void imap_logout(const char *mailbox_id);
619
620struct vmstate {
621 struct vm_state *vms;
622 AST_LIST_ENTRY(vmstate) list;
623};
624
625static AST_LIST_HEAD_STATIC(vmstates, vmstate);
626
627#endif
628
629#define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
630
631#define COMMAND_TIMEOUT 5000
632/* Don't modify these here; set your umask at runtime instead */
633#define VOICEMAIL_DIR_MODE 0777
634#define VOICEMAIL_FILE_MODE 0666
635#define CHUNKSIZE 65536
636
637#define VOICEMAIL_CONFIG "voicemail.conf"
638#define ASTERISK_USERNAME "asterisk"
639
640/* Define fast-forward, pause, restart, and reverse keys
641 * while listening to a voicemail message - these are
642 * strings, not characters */
643#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
644#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
645#define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
646#define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
647#define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
648#define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
649
650/* Default mail command to mail voicemail. Change it with the
651 * mailcmd= command in voicemail.conf */
652#define SENDMAIL "/usr/sbin/sendmail -t"
653#define INTRO "vm-intro"
654
655#define MAX_MAIL_BODY_CONTENT_SIZE 134217728L // 128 Mbyte
656
657#define MAXMSG 100
658#define MAXMSGLIMIT 9999
659
660#define MINPASSWORD 0 /*!< Default minimum mailbox password length */
661
662#ifdef IMAP_STORAGE
663#define ENDL "\r\n"
664#else
665#define ENDL "\n"
666#endif
667
668#define MAX_DATETIME_FORMAT 512
669#define MAX_NUM_CID_CONTEXTS 10
670
671#define VM_REVIEW (1 << 0) /*!< After recording, permit the caller to review the recording before saving */
672#define VM_OPERATOR (1 << 1) /*!< Allow 0 to be pressed to go to 'o' extension */
673#define VM_SAYCID (1 << 2) /*!< Repeat the CallerID info during envelope playback */
674#define VM_SVMAIL (1 << 3) /*!< Allow the user to compose a new VM from within VoicemailMain */
675#define VM_ENVELOPE (1 << 4) /*!< Play the envelope information (who-from, time received, etc.) */
676#define VM_SAYDURATION (1 << 5) /*!< Play the length of the message during envelope playback */
677#define VM_SKIPAFTERCMD (1 << 6) /*!< After deletion, assume caller wants to go to the next message */
678#define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
679#define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
680#define VM_PBXSKIP (1 << 9) /*!< Skip the [PBX] preamble in the Subject line of emails */
681#define VM_DIRECTFORWARD (1 << 10) /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
682#define VM_ATTACH (1 << 11) /*!< Attach message to voicemail notifications? */
683#define VM_DELETE (1 << 12) /*!< Delete message after sending notification */
684#define VM_ALLOCED (1 << 13) /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
685#define VM_SEARCH (1 << 14) /*!< Search all contexts for a matching mailbox */
686#define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
687#define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
688#define VM_MESSAGEWRAP (1 << 17) /*!< Wrap around from the last message to the first, and vice-versa */
689#define VM_FWDURGAUTO (1 << 18) /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
690#define VM_EMAIL_EXT_RECS (1 << 19) /*!< Send voicemail emails when an external recording is added to a mailbox */
691#define VM_MARK_URGENT (1 << 20) /*!< After recording, permit the caller to mark the message as urgent */
692#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 */
693
694#define ERROR_LOCK_PATH -100
695#define ERROR_MAX_MSGS -101
696#define OPERATOR_EXIT 300
697
698#define MSGFILE_LEN (7) /*!< Length of the message file name: msgXXXX */
699
700enum vm_box {
708
710 OPT_SILENT = (1 << 0),
713 OPT_RECORDGAIN = (1 << 3),
715 OPT_AUTOPLAY = (1 << 6),
716 OPT_DTMFEXIT = (1 << 7),
720 OPT_BEEP = (1 << 11),
722 OPT_READONLY = (1 << 13),
723};
724
730 /* This *must* be the last value in this enum! */
732};
733
738};
739
754});
755
756static const char * const mailbox_folders[] = {
757#ifdef IMAP_STORAGE
758 imapfolder,
759#else
760 "INBOX",
761#endif
762 "Old",
763 "Work",
764 "Family",
765 "Friends",
766 "Cust1",
767 "Cust2",
768 "Cust3",
769 "Cust4",
770 "Cust5",
771 "Deleted",
772 "Urgent",
773};
774
775/*!
776 * \brief Reload voicemail.conf
777 * \param reload Whether this is a reload as opposed to module load
778 * \param force Forcefully reload the config, even it has not changed
779 * \retval 0 on success, nonzero on failure
780 */
781static int load_config_force(int reload, int force);
782
783/*! \brief Forcibly reload voicemail.conf, even if it has not changed.
784 * This is necessary after running unit tests. */
785#define force_reload_config() load_config_force(1, 1)
786
787static int load_config(int reload);
788#ifdef TEST_FRAMEWORK
789static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
790#endif
791static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
792
793/*! \page vmlang Voicemail Language Syntaxes Supported
794
795 \par Syntaxes supported, not really language codes.
796 \arg \b en - English
797 \arg \b de - German
798 \arg \b es - Spanish
799 \arg \b fr - French
800 \arg \b it - Italian
801 \arg \b nl - Dutch
802 \arg \b pt - Portuguese
803 \arg \b pt_BR - Portuguese (Brazil)
804 \arg \b gr - Greek
805 \arg \b no - Norwegian
806 \arg \b se - Swedish
807 \arg \b tw - Chinese (Taiwan)
808 \arg \b ua - Ukrainian
809
810German requires the following additional soundfile:
811\arg \b 1F einE (feminine)
812
813Spanish requires the following additional soundfile:
814\arg \b 1M un (masculine)
815
816Dutch, Portuguese & Spanish require the following additional soundfiles:
817\arg \b vm-INBOXs singular of 'new'
818\arg \b vm-Olds singular of 'old/heard/read'
819
820NB these are plural:
821\arg \b vm-INBOX nieuwe (nl)
822\arg \b vm-Old oude (nl)
823
824Polish uses:
825\arg \b vm-new-a 'new', feminine singular accusative
826\arg \b vm-new-e 'new', feminine plural accusative
827\arg \b vm-new-ych 'new', feminine plural genitive
828\arg \b vm-old-a 'old', feminine singular accusative
829\arg \b vm-old-e 'old', feminine plural accusative
830\arg \b vm-old-ych 'old', feminine plural genitive
831\arg \b digits/1-a 'one', not always same as 'digits/1'
832\arg \b digits/2-ie 'two', not always same as 'digits/2'
833
834Swedish uses:
835\arg \b vm-nytt singular of 'new'
836\arg \b vm-nya plural of 'new'
837\arg \b vm-gammalt singular of 'old'
838\arg \b vm-gamla plural of 'old'
839\arg \b digits/ett 'one', not always same as 'digits/1'
840
841Norwegian uses:
842\arg \b vm-ny singular of 'new'
843\arg \b vm-nye plural of 'new'
844\arg \b vm-gammel singular of 'old'
845\arg \b vm-gamle plural of 'old'
846
847Dutch also uses:
848\arg \b nl-om 'at'?
849
850Spanish also uses:
851\arg \b vm-youhaveno
852
853Italian requires the following additional soundfile:
854
855For vm_intro_it:
856\arg \b vm-nuovo new
857\arg \b vm-nuovi new plural
858\arg \b vm-vecchio old
859\arg \b vm-vecchi old plural
860
861Japanese requires the following additional soundfile:
862\arg \b jp-arimasu there is
863\arg \b jp-arimasen there is not
864\arg \b jp-oshitekudasai please press
865\arg \b jp-ni article ni
866\arg \b jp-ga article ga
867\arg \b jp-wa article wa
868\arg \b jp-wo article wo
869
870Chinese (Taiwan) requires the following additional soundfile:
871\arg \b vm-tong A class-word for call (tong1)
872\arg \b vm-ri A class-word for day (ri4)
873\arg \b vm-you You (ni3)
874\arg \b vm-haveno Have no (mei2 you3)
875\arg \b vm-have Have (you3)
876\arg \b vm-listen To listen (yao4 ting1)
877
878
879\note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
880spelled among others when you have to change folder. For the above reasons, vm-INBOX
881and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
882
883*/
884
885#define MAX_VM_MBOX_ID_LEN (AST_MAX_EXTENSION)
886#define MAX_VM_CONTEXT_LEN (AST_MAX_CONTEXT)
887/* MAX_VM_MAILBOX_LEN allows enough room for the '@' and NULL terminator */
888#define MAX_VM_MAILBOX_LEN (MAX_VM_MBOX_ID_LEN + MAX_VM_CONTEXT_LEN)
889
890/*! Structure for linked list of users
891 * Use ast_vm_user_destroy() to free one of these structures. */
893 char context[MAX_VM_CONTEXT_LEN];/*!< Voicemail context */
894 char mailbox[MAX_VM_MBOX_ID_LEN];/*!< Mailbox id, unique within vm context */
895 char password[80]; /*!< Secret pin code, numbers only */
896 char fullname[80]; /*!< Full name, for directory app */
897 char *email; /*!< E-mail address */
898 char *emailsubject; /*!< E-mail subject */
899 char *emailbody; /*!< E-mail body */
900 char pager[80]; /*!< E-mail address to pager (no attachment) */
901 char serveremail[80]; /*!< From: Mail address */
902 char fromstring[100]; /*!< From: Username */
903 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
904 char zonetag[80]; /*!< Time zone */
905 char locale[20]; /*!< The locale (for presentation of date/time) */
906 char callback[80];
907 char dialout[80];
908 char uniqueid[80]; /*!< Unique integer identifier */
909 char exit[80];
910 char attachfmt[20]; /*!< Attachment format */
911 unsigned int flags; /*!< VM_ flags */
913 int minsecs; /*!< Minimum number of seconds per message for this mailbox */
914 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
915 int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
916 int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
917 int passwordlocation; /*!< Storage location of the password */
918#ifdef IMAP_STORAGE
919 char imapserver[48]; /*!< IMAP server address */
920 char imapport[8]; /*!< IMAP server port */
921 char imapflags[128]; /*!< IMAP optional flags */
922 char imapuser[80]; /*!< IMAP server login */
923 char imappassword[80]; /*!< IMAP server password if authpassword not defined */
924 char imapfolder[64]; /*!< IMAP voicemail folder */
925 char imapvmshareid[80]; /*!< Shared mailbox ID to use rather than the dialed one */
926 int imapversion; /*!< If configuration changes, use the new values */
927#endif
928 double volgain; /*!< Volume gain for voicemails sent via email */
930};
931
932/*! Voicemail time zones */
933struct vm_zone {
935 char name[80];
936 char timezone[80];
937 char msg_format[512];
938};
939
940#define VMSTATE_MAX_MSG_ARRAY 256
941
942/*! Voicemail mailbox state */
943struct vm_state {
944 char curbox[80];
945 char username[80];
946 char context[80];
952 int *heard;
953 int dh_arraysize; /* used for deleted / heard allocation */
961#ifdef IMAP_STORAGE
963 int updated; /*!< decremented on each mail check until 1 -allows delay */
964 long *msgArray;
965 unsigned msg_array_max;
966 MAILSTREAM *mailstream;
967 int vmArrayIndex;
968 char imapuser[80]; /*!< IMAP server login */
969 char imapfolder[64]; /*!< IMAP voicemail folder */
970 char imapserver[48]; /*!< IMAP server address */
971 char imapport[8]; /*!< IMAP server port */
972 char imapflags[128]; /*!< IMAP optional flags */
973 int imapversion;
974 int interactive;
975 char introfn[PATH_MAX]; /*!< Name of prepended file */
976 unsigned int quota_limit;
977 unsigned int quota_usage;
978 struct vm_state *persist_vms;
979#endif
980};
981
982#ifdef ODBC_STORAGE
983static char odbc_database[80] = "asterisk";
984static char odbc_table[80] = "voicemessages";
985size_t odbc_table_len = sizeof(odbc_table);
986#define COUNT(a, b) odbc_count_messages(a,b)
987#define LAST_MSG_INDEX(a) odbc_last_message_index(a)
988#define RETRIEVE(a,b,c,d) odbc_retrieve_message(a,b)
989#define DISPOSE(a,b) odbc_remove_files(a,b)
990#define STORE(a,b,c,d,e,f,g,h,i,j,k) odbc_store_message(a,b,c,d)
991#define EXISTS(a,b,c,d) (odbc_message_exists(a,b))
992#define RENAME(a,b,c,d,e,f,g,h) (odbc_rename_message(a,b,c,d,e,f))
993#define COPY(a,b,c,d,e,f,g,h) (odbc_copy_message(a,b,c,d,e,f))
994#define DELETE(a,b,c,d) (odbc_delete_message(a,b))
995#define UPDATE_MSG_ID(a, b, c, d, e, f) (odbc_update_msg_id((a), (b), (c)))
996#else
997#ifdef IMAP_STORAGE
998#define DISPOSE(a,b) (imap_remove_file(a,b))
999#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))
1000#define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
1001#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
1002#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
1003#define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
1004#define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
1005#define UPDATE_MSG_ID(a, b, c, d, e, f) (vm_imap_update_msg_id((a), (b), (c), (d), (e), (f)))
1006#else
1007#define COUNT(a, b) count_messages(a,b)
1008#define LAST_MSG_INDEX(a) last_message_index(a)
1009#define RETRIEVE(a,b,c,d)
1010#define DISPOSE(a,b)
1011#define STORE(a,b,c,d,e,f,g,h,i,j,k)
1012#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
1013#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
1014#define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
1015#define DELETE(a,b,c,d) (vm_delete(c))
1016#define UPDATE_MSG_ID(a, b, c, d, e, f)
1017#endif
1018#endif
1019
1021
1022static char ext_pass_cmd[128];
1023static char ext_pass_check_cmd[128];
1024
1025static int my_umask;
1026
1027#define PWDCHANGE_INTERNAL (1 << 1)
1028#define PWDCHANGE_EXTERNAL (1 << 2)
1030
1031#ifdef ODBC_STORAGE
1032#define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
1033#else
1034# ifdef IMAP_STORAGE
1035# define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
1036# else
1037# define tdesc "Comedian Mail (Voicemail System)"
1038# endif
1039#endif
1040
1041static char userscontext[AST_MAX_EXTENSION] = "default";
1042
1043static char *addesc = "Comedian Mail";
1044
1045/* Leave a message */
1046static char *voicemail_app = "VoiceMail";
1047
1048/* Check mail, control, etc */
1049static char *voicemailmain_app = "VoiceMailMain";
1050
1051static char *vmauthenticate_app = "VMAuthenticate";
1052
1053static char *playmsg_app = "VoiceMailPlayMsg";
1054
1055static char *sayname_app = "VMSayName";
1056
1059static char zonetag[80];
1060static char locale[20];
1061static int maxsilence;
1062static int maxmsg = MAXMSG;
1063static int maxdeletedmsg;
1064static int silencethreshold = 128;
1066static char mailcmd[160] = SENDMAIL; /* Configurable mail cmd */
1067static char externnotify[160];
1069static char vmfmts[80] = "wav";
1070static double volgain;
1071static int vmminsecs;
1072static int vmmaxsecs;
1073static int maxgreet;
1074static int skipms = 3000;
1075static int maxlogins = 3;
1079
1080/*! Poll mailboxes for changes since there is something external to
1081 * app_voicemail that may change them. */
1082static unsigned int poll_mailboxes;
1083
1084/*! By default, poll every 30 seconds */
1085#define DEFAULT_POLL_FREQ 30
1086/*! Polling frequency */
1087static unsigned int poll_freq = DEFAULT_POLL_FREQ;
1088
1090static ast_cond_t poll_cond = PTHREAD_COND_INITIALIZER;
1092static unsigned char poll_thread_run;
1093
1095
1097 char *alias;
1098 char *mailbox;
1099 char buf[0];
1100};
1101
1103 char *alias;
1104 char *mailbox;
1105 char buf[0];
1106};
1107
1108#define MAPPING_BUCKETS 511
1112
1116
1117/* custom audio control prompts for voicemail playback */
1123
1124/* custom password sounds */
1125static char vm_login[80] = "vm-login";
1126static char vm_newuser[80] = "vm-newuser";
1127static char vm_password[80] = "vm-password";
1128static char vm_newpassword[80] = "vm-newpassword";
1129static char vm_passchanged[80] = "vm-passchanged";
1130static char vm_reenterpassword[80] = "vm-reenterpassword";
1131static char vm_mismatch[80] = "vm-mismatch";
1132static char vm_invalid_password[80] = "vm-invalid-password";
1133static char vm_pls_try_again[80] = "vm-pls-try-again";
1134
1135/*
1136 * XXX If we have the time, motivation, etc. to fix up this prompt, one of the following would be appropriate:
1137 * 1. create a sound along the lines of "Please try again. When done, press the pound key" which could be spliced
1138 * from existing sound clips. This would require some programming changes in the area of vm_forward options and also
1139 * app.c's __ast_play_and_record function
1140 * 2. create a sound prompt saying "Please try again. When done recording, press any key to stop and send the prepended
1141 * message." At the time of this comment, I think this would require new voice work to be commissioned.
1142 * 3. Something way different like providing instructions before a time out or a post-recording menu. This would require
1143 * more effort than either of the other two.
1144 */
1145static char vm_prepend_timeout[80] = "vm-then-pound";
1146
1147static struct ast_flags globalflags = {0};
1148
1149static int saydurationminfo = 2;
1150
1154
1156
1157
1158static char *emailbody;
1159static char *emailsubject;
1160static char *pagerbody;
1161static char *pagersubject;
1162static char fromstring[100];
1163static char pagerfromstring[100];
1164static char charset[32] = "ISO-8859-1";
1165
1166static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
1167static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
1168static int adsiver = 1;
1169static char emaildateformat[32] = "%A, %B %d, %Y at %r";
1170static char pagerdateformat[32] = "%A, %B %d, %Y at %r";
1171
1172/* Forward declarations - generic */
1173static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
1174static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu);
1175static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
1176static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
1177static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
1178 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
1179 signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro);
1180static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
1181static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
1182static 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);
1183static 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);
1184static void apply_options(struct ast_vm_user *vmu, const char *options);
1185static 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);
1186static int is_valid_dtmf(const char *key);
1187static void read_password_from_file(const char *secretfn, char *password, int passwordlen);
1188static int write_password_to_file(const char *secretfn, const char *password);
1189static const char *substitute_escapes(const char *value);
1190static 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);
1191static void notify_new_state(struct ast_vm_user *vmu);
1192static int append_vmu_info_astman(struct mansession *s, struct ast_vm_user *vmu, const char* event_name, const char* actionid);
1193static int append_vmbox_info_astman(struct mansession *s, const struct message *m, struct ast_vm_user *vmu, const char* event_name, const char* actionid);
1194
1195
1196/*!
1197 * Place a message in the indicated folder
1198 *
1199 * \param vmu Voicemail user
1200 * \param vms Current voicemail state for the user
1201 * \param msg The message number to save
1202 * \param box The folder into which the message should be saved
1203 * \param[out] newmsg The new message number of the saved message
1204 * \param move Tells whether to copy or to move the message
1205 *
1206 * \note the "move" parameter is only honored for IMAP voicemail presently
1207 * \retval 0 Success
1208 * \retval other Failure
1209 */
1210static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move);
1211
1212static 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);
1213static struct ast_vm_mailbox_snapshot *vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot);
1214
1215static 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);
1216static 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);
1217static int vm_msg_remove(const char *mailbox, const char *context, size_t num_msgs, const char *folder, const char *msgs[]);
1218static 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);
1219
1220#ifdef TEST_FRAMEWORK
1221static int vm_test_destroy_user(const char *context, const char *mailbox);
1222static int vm_test_create_user(const char *context, const char *mailbox);
1223#endif
1224
1225/*!
1226 * \internal
1227 * \brief Parse the given mailbox_id into mailbox and context.
1228 * \since 12.0.0
1229 *
1230 * \param mailbox_id The mailbox\@context string to separate.
1231 * \param mailbox Where the mailbox part will start.
1232 * \param context Where the context part will start. ("default" if not present)
1233 *
1234 * \retval 0 on success.
1235 * \retval -1 on error.
1236 */
1237static int separate_mailbox(char *mailbox_id, char **mailbox, char **context)
1238{
1239 if (ast_strlen_zero(mailbox_id) || !mailbox || !context) {
1240 return -1;
1241 }
1242 *context = mailbox_id;
1243 *mailbox = strsep(context, "@");
1244 if (ast_strlen_zero(*mailbox)) {
1245 return -1;
1246 }
1247 if (ast_strlen_zero(*context)) {
1248 *context = "default";
1249 }
1250 return 0;
1251}
1252
1254
1257 char *context;
1258 char mailbox[0];
1259};
1260
1261static int inprocess_hash_fn(const void *obj, const int flags)
1262{
1263 const struct inprocess *i = obj;
1264 return atoi(i->mailbox);
1265}
1266
1267static int inprocess_cmp_fn(void *obj, void *arg, int flags)
1268{
1269 struct inprocess *i = obj, *j = arg;
1270 if (strcmp(i->mailbox, j->mailbox)) {
1271 return 0;
1272 }
1273 return !strcmp(i->context, j->context) ? CMP_MATCH : 0;
1274}
1275
1276static int inprocess_count(const char *context, const char *mailbox, int delta)
1277{
1278 int context_len = strlen(context) + 1;
1279 int mailbox_len = strlen(mailbox) + 1;
1280 struct inprocess *i, *arg = ast_alloca(sizeof(*arg) + context_len + mailbox_len);
1281 arg->context = arg->mailbox + mailbox_len;
1282 ast_copy_string(arg->mailbox, mailbox, mailbox_len); /* SAFE */
1283 ast_copy_string(arg->context, context, context_len); /* SAFE */
1285 if ((i = ao2_find(inprocess_container, arg, 0))) {
1286 int ret = ast_atomic_fetchadd_int(&i->count, delta);
1288 ao2_ref(i, -1);
1289 return ret;
1290 }
1291 if (delta < 0) {
1292 ast_log(LOG_WARNING, "BUG: ref count decrement on non-existing object???\n");
1293 }
1294 if (!(i = ao2_alloc(sizeof(*i) + context_len + mailbox_len, NULL))) {
1296 return 0;
1297 }
1298 i->context = i->mailbox + mailbox_len;
1299 ast_copy_string(i->mailbox, mailbox, mailbox_len); /* SAFE */
1300 ast_copy_string(i->context, context, context_len); /* SAFE */
1301 i->count = delta;
1304 ao2_ref(i, -1);
1305 return 0;
1306}
1307
1308#if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
1309static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
1310#endif
1311
1312/*!
1313 * \brief Strips control and non 7-bit clean characters from input string.
1314 *
1315 * \note To map control and none 7-bit characters to a 7-bit clean characters
1316 * please use ast_str_encode_mine().
1317 */
1318static char *strip_control_and_high(const char *input, char *buf, size_t buflen)
1319{
1320 char *bufptr = buf;
1321 for (; *input; input++) {
1322 if (*input < 32) {
1323 continue;
1324 }
1325 *bufptr++ = *input;
1326 if (bufptr == buf + buflen - 1) {
1327 break;
1328 }
1329 }
1330 *bufptr = '\0';
1331 return buf;
1332}
1333
1334/*
1335 * These macros are currently only used by the ODBC code but
1336 * should be used anyplace we're allocating a PATH_MAX
1337 * sized buffer for something which will be much smaller
1338 * in practice.
1339 */
1340#ifdef ODBC_STORAGE
1341/*!
1342 * \internal
1343 * \brief Get the length needed for a message path base.
1344 *
1345 * \param dir The absolute path to a directory
1346 *
1347 * \note The directory provided should include all components
1348 * down to the folder level. For example:
1349 * /var/spool/asterisk/voicemail/default/1234/INBOX
1350 *
1351 * \returns Number of bytes needed for the message path base.
1352 */
1353static size_t get_msg_path_len(const char *dir)
1354{
1355 if (ast_strlen_zero(dir)) {
1356 return 0;
1357 }
1358 /* dir + '/' + MSGFILE_LEN + '\0' */
1359 return strlen(dir) + 1 + MSGFILE_LEN + 1;
1360}
1361
1362/*
1363 * The longest sound file extension we currently
1364 * have is "siren14" which is 7 characters long
1365 * but we'll make this 12 to be extra safe.
1366 */
1367#define MAX_SOUND_EXTEN_LEN 12
1368/*!
1369 * \internal
1370 * \brief Get the length needed for a message sound or metadata file path.
1371 *
1372 * \param dir The absolute path to a directory
1373 *
1374 * \note The directory provided should include all components
1375 * down to the folder level. For example:
1376 * /var/spool/asterisk/voicemail/default/1234/INBOX
1377 *
1378 * \returns Number of bytes needed for the message sound or metadata file path.
1379 */
1380static size_t get_msg_path_ext_len(const char *dir)
1381{
1382 if (ast_strlen_zero(dir)) {
1383 return 0;
1384 }
1385 /* dir + '/' + MSGFILE_LEN + extlen(including '.') + '\0' */
1386 return strlen(dir) + 1 + MSGFILE_LEN + MAX_SOUND_EXTEN_LEN + 1;
1387}
1388
1389/*!
1390 * \internal
1391 * \brief Allocate a buffer on the stack containing a message file path base.
1392 *
1393 * \param dir The absolute path to a directory
1394 * \param msgnum The message number or -1 for a prompt file
1395 *
1396 * \note The returned buffer will automatically be freed when it
1397 * goes out of scope.
1398 *
1399 * If passed (/var/spool/asterisk/voicemail/default/1234/INBOX, 0)
1400 * the returned path would be:
1401 * /var/spool/asterisk/voicemail/default/1234/INBOX/msg0000
1402 *
1403 * If passed (/var/spool/asterisk/voicemail/default/1234/unavail, -1)
1404 * the returned path would be:
1405 * /var/spool/asterisk/voicemail/default/1234/unavail
1406 * (no change)
1407 *
1408 * \returns A pointer to the buffer containing the path.
1409 */
1410#define MAKE_FILE_PTRA(dir, msgnum) \
1411({ \
1412 size_t __len = get_msg_path_len(dir); \
1413 char *__var; \
1414 if (msgnum < 0) { \
1415 __var = ast_strdupa(dir); \
1416 } else { \
1417 __var = ast_alloca(__len); \
1418 snprintf(__var, __len, "%s/msg%04d", dir, msgnum); \
1419 } \
1420 __var; \
1421})
1422
1423/*!
1424 * \internal
1425 * \brief Allocate a buffer on the stack for a message sound or metadata file path.
1426 *
1427 * \param dir The absolute path to a directory
1428 * \param msgnum The message number or -1 for a prompt file
1429 *
1430 * \note The returned buffer will automatically be freed when it
1431 * goes out of scope.
1432 *
1433 * If passed (/var/spool/asterisk/voicemail/default/1234/INBOX, 0, "txt")
1434 * the returned path would be:
1435 * /var/spool/asterisk/voicemail/default/1234/INBOX/msg0000.txt
1436 *
1437 * If passed (/var/spool/asterisk/voicemail/default/1234/unavail, -1, "WAV")
1438 * the returned path would be:
1439 * /var/spool/asterisk/voicemail/default/1234/unavail.WAV
1440 *
1441 * The buffer returned has enough space that you can safely re-use it
1442 * for other sound file extensions. For instance:
1443 *
1444 * char *fn = MAKE_FILE_PTRA(dir, msgnum);
1445 * char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
1446 * char *fmt = "g722";
1447 * ...do domething with full_fn then re-use it...
1448 * sprintf(full_fn, "%s.%s", fn, fmt); * Safe since the same dir and msgnum were used *
1449 *
1450 * \returns A pointer to the allocated buffer
1451 */
1452#define MAKE_FILE_EXT_PTRA(dir, msgnum, ext) \
1453({ \
1454 size_t __len = get_msg_path_ext_len(dir); \
1455 char *__var = ast_alloca(__len); \
1456 if (msgnum < 0) { \
1457 snprintf(__var, __len, "%s.%s", dir, ext); \
1458 } else { \
1459 snprintf(__var, __len, "%s/msg%04d.%s", dir, msgnum, ext); \
1460 } \
1461 __var; \
1462})
1463#endif
1464
1465/*!
1466 * \brief Sets default voicemail system options to a voicemail user.
1467 *
1468 * This applies select global settings to a newly created (dynamic) instance of a voicemail user.
1469 * - all the globalflags
1470 * - the saydurationminfo
1471 * - the callcontext
1472 * - the dialcontext
1473 * - the exitcontext
1474 * - vmmaxsecs, vmmaxmsg, maxdeletedmsg
1475 * - volume gain.
1476 * - emailsubject, emailbody set to NULL
1477 */
1478static void populate_defaults(struct ast_vm_user *vmu)
1479{
1482 if (saydurationminfo) {
1484 }
1485 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
1486 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
1487 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
1488 ast_copy_string(vmu->zonetag, zonetag, sizeof(vmu->zonetag));
1489 ast_copy_string(vmu->locale, locale, sizeof(vmu->locale));
1490 if (vmminsecs) {
1491 vmu->minsecs = vmminsecs;
1492 }
1493 if (vmmaxsecs) {
1494 vmu->maxsecs = vmmaxsecs;
1495 }
1496 if (maxmsg) {
1497 vmu->maxmsg = maxmsg;
1498 }
1499 if (maxdeletedmsg) {
1501 }
1502 vmu->volgain = volgain;
1503 ast_free(vmu->email);
1504 vmu->email = NULL;
1505 ast_free(vmu->emailsubject);
1506 vmu->emailsubject = NULL;
1507 ast_free(vmu->emailbody);
1508 vmu->emailbody = NULL;
1509#ifdef IMAP_STORAGE
1510 ast_copy_string(vmu->imapfolder, imapfolder, sizeof(vmu->imapfolder));
1511 ast_copy_string(vmu->imapserver, imapserver, sizeof(vmu->imapserver));
1512 ast_copy_string(vmu->imapport, imapport, sizeof(vmu->imapport));
1513 ast_copy_string(vmu->imapflags, imapflags, sizeof(vmu->imapflags));
1514#endif
1515}
1516
1517/*!
1518 * \brief Sets a specific property value.
1519 * \param vmu The voicemail user object to work with.
1520 * \param var The name of the property to be set.
1521 * \param value The value to be set to the property.
1522 *
1523 * The property name must be one of the understood properties. See the source for details.
1524 */
1525static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
1526{
1527 int x;
1528 if (!strcasecmp(var, "attach")) {
1530 } else if (!strcasecmp(var, "attachfmt")) {
1531 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
1532 } else if (!strcasecmp(var, "attachextrecs")) {
1534 } else if (!strcasecmp(var, "serveremail")) {
1535 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
1536 } else if (!strcasecmp(var, "fromstring")) {
1537 ast_copy_string(vmu->fromstring, value, sizeof(vmu->fromstring));
1538 } else if (!strcasecmp(var, "emailbody")) {
1539 ast_free(vmu->emailbody);
1541 } else if (!strcasecmp(var, "emailsubject")) {
1542 ast_free(vmu->emailsubject);
1544 } else if (!strcasecmp(var, "language")) {
1545 ast_copy_string(vmu->language, value, sizeof(vmu->language));
1546 } else if (!strcasecmp(var, "tz")) {
1547 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
1548 } else if (!strcasecmp(var, "locale")) {
1549 ast_copy_string(vmu->locale, value, sizeof(vmu->locale));
1550#ifdef IMAP_STORAGE
1551 } else if (!strcasecmp(var, "imapuser")) {
1552 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
1553 vmu->imapversion = imapversion;
1554 } else if (!strcasecmp(var, "imapserver")) {
1555 ast_copy_string(vmu->imapserver, value, sizeof(vmu->imapserver));
1556 vmu->imapversion = imapversion;
1557 } else if (!strcasecmp(var, "imapport")) {
1558 ast_copy_string(vmu->imapport, value, sizeof(vmu->imapport));
1559 vmu->imapversion = imapversion;
1560 } else if (!strcasecmp(var, "imapflags")) {
1561 ast_copy_string(vmu->imapflags, value, sizeof(vmu->imapflags));
1562 vmu->imapversion = imapversion;
1563 } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
1564 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
1565 vmu->imapversion = imapversion;
1566 } else if (!strcasecmp(var, "imapfolder")) {
1567 ast_copy_string(vmu->imapfolder, value, sizeof(vmu->imapfolder));
1568 vmu->imapversion = imapversion;
1569 } else if (!strcasecmp(var, "imapvmshareid")) {
1570 ast_copy_string(vmu->imapvmshareid, value, sizeof(vmu->imapvmshareid));
1571 vmu->imapversion = imapversion;
1572#endif
1573 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
1575 } else if (!strcasecmp(var, "saycid")){
1577 } else if (!strcasecmp(var, "sendvoicemail")){
1579 } else if (!strcasecmp(var, "review")){
1581 } else if (!strcasecmp(var, "leaveurgent")){
1583 } else if (!strcasecmp(var, "tempgreetwarn")){
1585 } else if (!strcasecmp(var, "messagewrap")){
1587 } else if (!strcasecmp(var, "operator")) {
1589 } else if (!strcasecmp(var, "envelope")){
1591 } else if (!strcasecmp(var, "moveheard")){
1593 } else if (!strcasecmp(var, "sayduration")){
1595 } else if (!strcasecmp(var, "saydurationm")){
1596 if (sscanf(value, "%30d", &x) == 1) {
1597 vmu->saydurationm = x;
1598 } else {
1599 ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
1600 }
1601 } else if (!strcasecmp(var, "forcename")){
1603 } else if (!strcasecmp(var, "forcegreetings")){
1605 } else if (!strcasecmp(var, "callback")) {
1606 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
1607 } else if (!strcasecmp(var, "dialout")) {
1608 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
1609 } else if (!strcasecmp(var, "exitcontext")) {
1610 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
1611 } else if (!strcasecmp(var, "minsecs")) {
1612 if (sscanf(value, "%30d", &x) == 1 && x >= 0) {
1613 vmu->minsecs = x;
1614 } else {
1615 ast_log(LOG_WARNING, "Invalid min message length of %s. Using global value %d\n", value, vmminsecs);
1616 vmu->minsecs = vmminsecs;
1617 }
1618 } else if (!strcasecmp(var, "maxmessage") || !strcasecmp(var, "maxsecs")) {
1619 vmu->maxsecs = atoi(value);
1620 if (vmu->maxsecs <= 0) {
1621 ast_log(AST_LOG_WARNING, "Invalid max message length of %s. Using global value %d\n", value, vmmaxsecs);
1622 vmu->maxsecs = vmmaxsecs;
1623 } else {
1624 vmu->maxsecs = atoi(value);
1625 }
1626 if (!strcasecmp(var, "maxmessage"))
1627 ast_log(AST_LOG_WARNING, "Option 'maxmessage' has been deprecated in favor of 'maxsecs'. Please make that change in your voicemail config.\n");
1628 } else if (!strcasecmp(var, "maxmsg")) {
1629 vmu->maxmsg = atoi(value);
1630 /* Accept maxmsg=0 (Greetings only voicemail) */
1631 if (vmu->maxmsg < 0) {
1632 ast_log(AST_LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %d\n", value, MAXMSG);
1633 vmu->maxmsg = MAXMSG;
1634 } else if (vmu->maxmsg > MAXMSGLIMIT) {
1635 ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %d. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
1636 vmu->maxmsg = MAXMSGLIMIT;
1637 }
1638 } else if (!strcasecmp(var, "nextaftercmd")) {
1640 } else if (!strcasecmp(var, "backupdeleted")) {
1641 if (sscanf(value, "%30d", &x) == 1)
1642 vmu->maxdeletedmsg = x;
1643 else if (ast_true(value))
1644 vmu->maxdeletedmsg = MAXMSG;
1645 else
1646 vmu->maxdeletedmsg = 0;
1647
1648 if (vmu->maxdeletedmsg < 0) {
1649 ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox backupdeleted=%s. Using default value %d\n", value, MAXMSG);
1650 vmu->maxdeletedmsg = MAXMSG;
1651 } else if (vmu->maxdeletedmsg > MAXMSGLIMIT) {
1652 ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %d. Cannot accept value backupdeleted=%s\n", MAXMSGLIMIT, value);
1654 }
1655 } else if (!strcasecmp(var, "volgain")) {
1656 sscanf(value, "%30lf", &vmu->volgain);
1657 } else if (!strcasecmp(var, "passwordlocation")) {
1658 if (!strcasecmp(value, "spooldir")) {
1660 } else {
1662 }
1663 } else if (!strcasecmp(var, "options")) {
1664 apply_options(vmu, value);
1665 }
1666}
1667
1668static char *vm_check_password_shell(char *command, char *buf, size_t len)
1669{
1670 int fds[2], pid = 0;
1671
1672 memset(buf, 0, len);
1673
1674 if (pipe(fds)) {
1675 snprintf(buf, len, "FAILURE: Pipe failed: %s", strerror(errno));
1676 } else {
1677 /* good to go*/
1678 pid = ast_safe_fork(0);
1679
1680 if (pid < 0) {
1681 /* ok maybe not */
1682 close(fds[0]);
1683 close(fds[1]);
1684 snprintf(buf, len, "FAILURE: Fork failed");
1685 } else if (pid) {
1686 /* parent */
1687 close(fds[1]);
1688 if (read(fds[0], buf, len) < 0) {
1689 ast_log(LOG_WARNING, "read() failed: %s\n", strerror(errno));
1690 }
1691 close(fds[0]);
1692 } else {
1693 /* child */
1695 AST_APP_ARG(v)[20];
1696 );
1697 char *mycmd = ast_strdupa(command);
1698
1699 close(fds[0]);
1700 dup2(fds[1], STDOUT_FILENO);
1701 close(fds[1]);
1702 ast_close_fds_above_n(STDOUT_FILENO);
1703
1704 AST_NONSTANDARD_APP_ARGS(arg, mycmd, ' ');
1705
1706 execv(arg.v[0], arg.v);
1707 printf("FAILURE: %s", strerror(errno));
1708 _exit(0);
1709 }
1710 }
1711 return buf;
1712}
1713
1714/*!
1715 * \brief Check that password meets minimum required length
1716 * \param vmu The voicemail user to change the password for.
1717 * \param password The password string to check
1718 *
1719 * \return zero on ok, 1 on not ok.
1720 */
1721static int check_password(struct ast_vm_user *vmu, char *password)
1722{
1723 /* check minimum length */
1724 if (strlen(password) < minpassword)
1725 return 1;
1726 /* check that password does not contain '*' character */
1727 if (!ast_strlen_zero(password) && password[0] == '*')
1728 return 1;
1730 char cmd[255], buf[255];
1731
1732 ast_debug(1, "Verify password policies for %s\n", password);
1733
1734 snprintf(cmd, sizeof(cmd), "%s %s %s %s %s", ext_pass_check_cmd, vmu->mailbox, vmu->context, vmu->password, password);
1735 if (vm_check_password_shell(cmd, buf, sizeof(buf))) {
1736 ast_debug(5, "Result: %s\n", buf);
1737 if (!strncasecmp(buf, "VALID", 5)) {
1738 ast_debug(3, "Passed password check: '%s'\n", buf);
1739 return 0;
1740 } else if (!strncasecmp(buf, "FAILURE", 7)) {
1741 ast_log(AST_LOG_WARNING, "Unable to execute password validation script: '%s'.\n", buf);
1742 return 0;
1743 } else {
1744 ast_log(AST_LOG_NOTICE, "Password doesn't match policies for user %s %s\n", vmu->mailbox, password);
1745 return 1;
1746 }
1747 }
1748 }
1749 return 0;
1750}
1751
1752/*!
1753 * \brief Performs a change of the voicemail passowrd in the realtime engine.
1754 * \param vmu The voicemail user to change the password for.
1755 * \param password The new value to be set to the password for this user.
1756 *
1757 * This only works if there is a realtime engine configured.
1758 * This is called from the (top level) vm_change_password.
1759 *
1760 * \return zero on success, -1 on error.
1761 */
1762static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
1763{
1764 int res = -1;
1765 if (!strcmp(vmu->password, password)) {
1766 /* No change (but an update would return 0 rows updated, so we opt out here) */
1767 return 0;
1768 }
1769
1770 if (strlen(password) > 10) {
1771 ast_realtime_require_field("voicemail", "password", RQ_CHAR, strlen(password), SENTINEL);
1772 }
1773 if (ast_update2_realtime("voicemail", "context", vmu->context, "mailbox", vmu->mailbox, SENTINEL, "password", password, SENTINEL) > 0) {
1774 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: realtime engine updated with new password\r\nPasswordSource: realtime");
1775 ast_copy_string(vmu->password, password, sizeof(vmu->password));
1776 res = 0;
1777 }
1778 return res;
1779}
1780
1781/*!
1782 * \brief Destructively Parse options and apply.
1783 */
1784static void apply_options(struct ast_vm_user *vmu, const char *options)
1785{
1786 char *stringp;
1787 char *s;
1788 char *var, *value;
1789 stringp = ast_strdupa(options);
1790 while ((s = strsep(&stringp, "|"))) {
1791 value = s;
1792 if ((var = strsep(&value, "=")) && value) {
1793 apply_option(vmu, var, value);
1794 }
1795 }
1796}
1797
1798/*!
1799 * \brief Loads the options specific to a voicemail user.
1800 *
1801 * This is called when a vm_user structure is being set up, such as from load_options.
1802 */
1804{
1805 for (; var; var = var->next) {
1806 if (!strcasecmp(var->name, "vmsecret")) {
1807 ast_copy_string(retval->password, var->value, sizeof(retval->password));
1808 } else if (!strcasecmp(var->name, "secret") || !strcasecmp(var->name, "password")) { /* don't overwrite vmsecret if it exists */
1809 if (ast_strlen_zero(retval->password)) {
1810 if (!ast_strlen_zero(var->value) && var->value[0] == '*') {
1811 ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
1812 "\n\tmust be reset in voicemail.conf.\n", retval->mailbox);
1813 } else {
1814 ast_copy_string(retval->password, var->value, sizeof(retval->password));
1815 }
1816 }
1817 } else if (!strcasecmp(var->name, "uniqueid")) {
1818 ast_copy_string(retval->uniqueid, var->value, sizeof(retval->uniqueid));
1819 } else if (!strcasecmp(var->name, "pager")) {
1820 ast_copy_string(retval->pager, var->value, sizeof(retval->pager));
1821 } else if (!strcasecmp(var->name, "email")) {
1822 ast_free(retval->email);
1823 retval->email = ast_strdup(var->value);
1824 } else if (!strcasecmp(var->name, "fullname")) {
1825 ast_copy_string(retval->fullname, var->value, sizeof(retval->fullname));
1826 } else if (!strcasecmp(var->name, "context")) {
1827 ast_copy_string(retval->context, var->value, sizeof(retval->context));
1828 } else if (!strcasecmp(var->name, "emailsubject")) {
1829 ast_free(retval->emailsubject);
1830 retval->emailsubject = ast_strdup(substitute_escapes(var->value));
1831 } else if (!strcasecmp(var->name, "emailbody")) {
1832 ast_free(retval->emailbody);
1833 retval->emailbody = ast_strdup(substitute_escapes(var->value));
1834#ifdef IMAP_STORAGE
1835 } else if (!strcasecmp(var->name, "imapuser")) {
1836 ast_copy_string(retval->imapuser, var->value, sizeof(retval->imapuser));
1837 retval->imapversion = imapversion;
1838 } else if (!strcasecmp(var->name, "imapserver")) {
1839 ast_copy_string(retval->imapserver, var->value, sizeof(retval->imapserver));
1840 retval->imapversion = imapversion;
1841 } else if (!strcasecmp(var->name, "imapport")) {
1842 ast_copy_string(retval->imapport, var->value, sizeof(retval->imapport));
1843 retval->imapversion = imapversion;
1844 } else if (!strcasecmp(var->name, "imapflags")) {
1845 ast_copy_string(retval->imapflags, var->value, sizeof(retval->imapflags));
1846 retval->imapversion = imapversion;
1847 } else if (!strcasecmp(var->name, "imappassword") || !strcasecmp(var->name, "imapsecret")) {
1848 ast_copy_string(retval->imappassword, var->value, sizeof(retval->imappassword));
1849 retval->imapversion = imapversion;
1850 } else if (!strcasecmp(var->name, "imapfolder")) {
1851 ast_copy_string(retval->imapfolder, var->value, sizeof(retval->imapfolder));
1852 retval->imapversion = imapversion;
1853 } else if (!strcasecmp(var->name, "imapvmshareid")) {
1854 ast_copy_string(retval->imapvmshareid, var->value, sizeof(retval->imapvmshareid));
1855 retval->imapversion = imapversion;
1856#endif
1857 } else
1858 apply_option(retval, var->name, var->value);
1859 }
1860}
1861
1862/*!
1863 * \brief Determines if a DTMF key entered is valid.
1864 * \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.
1865 *
1866 * Tests the character entered against the set of valid DTMF characters.
1867 * \return 1 if the character entered is a valid DTMF digit, 0 if the character is invalid.
1868 */
1869static int is_valid_dtmf(const char *key)
1870{
1871 int i;
1872 char *local_key = ast_strdupa(key);
1873
1874 for (i = 0; i < strlen(key); ++i) {
1875 if (!strchr(VALID_DTMF, *local_key)) {
1876 ast_log(AST_LOG_WARNING, "Invalid DTMF key \"%c\" used in voicemail configuration file\n", *local_key);
1877 return 0;
1878 }
1879 local_key++;
1880 }
1881 return 1;
1882}
1883
1884/*!
1885 * \brief Finds a voicemail user from the realtime engine.
1886 * \param ivm
1887 * \param context
1888 * \param mailbox
1889 *
1890 * 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.
1891 *
1892 * \return The ast_vm_user structure for the user that was found.
1893 */
1894static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1895{
1896 struct ast_variable *var;
1897 struct ast_vm_user *retval;
1898
1899 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
1900 if (ivm) {
1901 memset(retval, 0, sizeof(*retval));
1902 }
1904 if (!ivm) {
1906 }
1907 if (mailbox) {
1908 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
1909 }
1911 var = ast_load_realtime("voicemail", "mailbox", mailbox, SENTINEL);
1912 } else {
1913 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, SENTINEL);
1914 }
1915 if (var) {
1918 } else {
1919 if (!ivm)
1921 retval = NULL;
1922 }
1923 }
1924 return retval;
1925}
1926
1927/*!
1928 * \brief Finds a voicemail user from the users file or the realtime engine.
1929 * \param ivm
1930 * \param context
1931 * \param mailbox
1932 *
1933 * \return The ast_vm_user structure for the user that was found.
1934 */
1935static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
1936{
1937 /* This function could be made to generate one from a database, too */
1938 struct ast_vm_user *vmu = NULL, *cur;
1940
1942 context = "default";
1943
1944 AST_LIST_TRAVERSE(&users, cur, list) {
1945#ifdef IMAP_STORAGE
1946 if (cur->imapversion != imapversion) {
1947 continue;
1948 }
1949#endif
1950 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
1951 break;
1952 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
1953 break;
1954 }
1955 if (cur) {
1956 /* Make a copy, so that on a reload, we have no race */
1957 if ((vmu = (ivm ? ivm : ast_calloc(1, sizeof(*vmu))))) {
1958 ast_free(vmu->email);
1959 ast_free(vmu->emailbody);
1960 ast_free(vmu->emailsubject);
1961 *vmu = *cur;
1962 vmu->email = ast_strdup(cur->email);
1963 vmu->emailbody = ast_strdup(cur->emailbody);
1964 vmu->emailsubject = ast_strdup(cur->emailsubject);
1965 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
1966 AST_LIST_NEXT(vmu, list) = NULL;
1967 }
1968 }
1970 if (!vmu) {
1971 vmu = find_user_realtime(ivm, context, mailbox);
1972 }
1973 if (!vmu && !ast_strlen_zero(aliasescontext)) {
1974 struct alias_mailbox_mapping *mapping;
1975 char *search_string = ast_alloca(MAX_VM_MAILBOX_LEN);
1976
1977 snprintf(search_string, MAX_VM_MAILBOX_LEN, "%s%s%s",
1978 mailbox,
1979 ast_strlen_zero(context) ? "" : "@",
1980 S_OR(context, ""));
1981
1982 mapping = ao2_find(alias_mailbox_mappings, search_string, OBJ_SEARCH_KEY);
1983 if (mapping) {
1984 char *search_mailbox = NULL;
1985 char *search_context = NULL;
1986
1987 separate_mailbox(ast_strdupa(mapping->mailbox), &search_mailbox, &search_context);
1988 ao2_ref(mapping, -1);
1989 vmu = find_user(ivm, search_mailbox, search_context);
1990 }
1991 }
1992
1993 return vmu;
1994}
1995
1996/*!
1997 * \brief Resets a user password to a specified password.
1998 * \param context
1999 * \param mailbox
2000 * \param newpass
2001 *
2002 * This does the actual change password work, called by the vm_change_password() function.
2003 *
2004 * \return zero on success, -1 on error.
2005 */
2006static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
2007{
2008 /* This function could be made to generate one from a database, too */
2009 struct ast_vm_user *cur;
2010 int res = -1;
2012 AST_LIST_TRAVERSE(&users, cur, list) {
2013 if ((!context || !strcasecmp(context, cur->context)) &&
2014 (!strcasecmp(mailbox, cur->mailbox)))
2015 break;
2016 }
2017 if (cur) {
2018 ast_copy_string(cur->password, newpass, sizeof(cur->password));
2019 res = 0;
2020 }
2022 if (!res) {
2023 struct ast_json *json_object;
2024
2025 json_object = ast_json_pack("{s: s, s: s, s: s}",
2026 "Context", S_OR(context, "default"),
2027 "Mailbox", mailbox,
2028 "NewPassword", newpass);
2029 ast_manager_publish_event("VoicemailPasswordChange", EVENT_FLAG_SYSTEM | EVENT_FLAG_USER, json_object);
2030 ast_json_unref(json_object);
2031 }
2032 return res;
2033}
2034
2035/*!
2036 * \brief Check if configuration file is valid
2037 */
2038static inline int valid_config(const struct ast_config *cfg)
2039{
2040 return cfg && cfg != CONFIG_STATUS_FILEINVALID;
2041}
2042
2043/*!
2044 * \brief The handler for the change password option.
2045 * \param vmu The voicemail user to work with.
2046 * \param newpassword The new password (that has been gathered from the appropriate prompting).
2047 * 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.
2048 * It is also called when the user wants to change their password from menu option '5' on the mailbox options menu.
2049 */
2050static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
2051{
2052 struct ast_config *cfg = NULL;
2053 struct ast_variable *var = NULL;
2054 struct ast_category *cat = NULL;
2055 char *category = NULL;
2056 const char *tmp = NULL;
2057 struct ast_flags config_flags = { CONFIG_FLAG_WITHCOMMENTS };
2058 char secretfn[PATH_MAX] = "";
2059 int found = 0;
2060
2061 if (!change_password_realtime(vmu, newpassword))
2062 return;
2063
2064 /* check if we should store the secret in the spool directory next to the messages */
2065 switch (vmu->passwordlocation) {
2066 case OPT_PWLOC_SPOOLDIR:
2067 snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
2068 if (write_password_to_file(secretfn, newpassword) == 0) {
2069 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: secret.conf updated with new password\r\nPasswordSource: secret.conf");
2070 ast_verb(4, "Writing voicemail password to file %s succeeded\n", secretfn);
2071 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2072 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2073 break;
2074 } else {
2075 ast_log(LOG_WARNING, "Writing voicemail password to file %s failed, falling back to config file\n", secretfn);
2076 }
2077 /* Fall-through */
2079 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) && valid_config(cfg)) {
2080 while ((category = ast_category_browse(cfg, category))) {
2081 if (!strcasecmp(category, vmu->context)) {
2082 char *value = NULL;
2083 char *new = NULL;
2084 if (!(tmp = ast_variable_retrieve(cfg, category, vmu->mailbox))) {
2085 ast_log(AST_LOG_WARNING, "We could not find the mailbox.\n");
2086 break;
2087 }
2088 value = strstr(tmp, ",");
2089 if (!value) {
2090 new = ast_malloc(strlen(newpassword) + 1);
2091 sprintf(new, "%s", newpassword);
2092 } else {
2093 new = ast_malloc((strlen(value) + strlen(newpassword) + 1));
2094 sprintf(new, "%s%s", newpassword, value);
2095 }
2096 if (!(cat = ast_category_get(cfg, category, NULL))) {
2097 ast_log(AST_LOG_WARNING, "Failed to get category structure.\n");
2098 ast_free(new);
2099 break;
2100 }
2101 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
2102 found = 1;
2103 ast_free(new);
2104 }
2105 }
2106 /* save the results */
2107 if (found) {
2108 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: voicemail.conf updated with new password\r\nPasswordSource: voicemail.conf");
2109 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2110 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2111 ast_config_text_file_save(VOICEMAIL_CONFIG, cfg, "app_voicemail");
2112 ast_config_destroy(cfg);
2113 break;
2114 }
2115
2116 ast_config_destroy(cfg);
2117 }
2118 /* Fall-through */
2120 /* check users.conf and update the password stored for the mailbox */
2121 /* if no vmsecret entry exists create one. */
2122 if ((cfg = ast_config_load("users.conf", config_flags)) && valid_config(cfg)) {
2123 ast_debug(4, "we are looking for %s\n", vmu->mailbox);
2124 for (category = ast_category_browse(cfg, NULL); category; category = ast_category_browse(cfg, category)) {
2125 ast_debug(4, "users.conf: %s\n", category);
2126 if (!strcasecmp(category, vmu->mailbox)) {
2127 char new[strlen(newpassword) + 1];
2128 if (!ast_variable_retrieve(cfg, category, "vmsecret")) {
2129 ast_debug(3, "looks like we need to make vmsecret!\n");
2130 var = ast_variable_new("vmsecret", newpassword, "");
2131 } else {
2132 var = NULL;
2133 }
2134
2135 sprintf(new, "%s", newpassword);
2136 if (!(cat = ast_category_get(cfg, category, NULL))) {
2137 ast_debug(4, "failed to get category!\n");
2138 ast_free(var);
2139 break;
2140 }
2141 if (!var) {
2142 ast_variable_update(cat, "vmsecret", new, NULL, 0);
2143 } else {
2145 }
2146 found = 1;
2147 break;
2148 }
2149 }
2150 /* save the results and clean things up */
2151 if (found) {
2152 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: users.conf updated with new password\r\nPasswordSource: users.conf");
2153 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2154 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2155 ast_config_text_file_save("users.conf", cfg, "app_voicemail");
2156 }
2157
2158 ast_config_destroy(cfg);
2159 }
2160 }
2161}
2162
2163static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
2164{
2165 char buf[255];
2166 snprintf(buf, sizeof(buf), "%s %s %s %s", ext_pass_cmd, vmu->context, vmu->mailbox, newpassword);
2167 ast_debug(1, "External password: %s\n",buf);
2168 if (!ast_safe_system(buf)) {
2169 ast_test_suite_event_notify("PASSWORDCHANGED", "Message: external script updated with new password\r\nPasswordSource: external");
2170 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
2171 /* Reset the password in memory, too */
2172 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
2173 }
2174}
2175
2176/*!
2177 * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
2178 * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
2179 * \param len The length of the path string that was written out.
2180 * \param context
2181 * \param ext
2182 * \param folder
2183 *
2184 * The path is constructed as
2185 * VM_SPOOL_DIRcontext/ext/folder
2186 *
2187 * \return zero on success, -1 on error.
2188 */
2189static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
2190{
2191 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
2192}
2193
2194/*!
2195 * \brief Creates a file system path expression for a folder within the voicemail data folder and the appropriate context.
2196 * \param dest The variable to hold the output generated path expression. This buffer should be of size PATH_MAX.
2197 * \param len The length of the path string that was written out.
2198 * \param dir
2199 * \param num
2200 *
2201 * The path is constructed as
2202 * VM_SPOOL_DIRcontext/ext/folder
2203 *
2204 * \return zero on success, -1 on error.
2205 */
2206static int make_file(char *dest, const int len, const char *dir, const int num)
2207{
2208 return snprintf(dest, len, "%s/msg%04d", dir, num);
2209}
2210
2211/*! \brief basically mkdir -p $dest/$context/$ext/$folder
2212 * \param dest String. base directory.
2213 * \param len Length of dest.
2214 * \param context String. Ignored if is null or empty string.
2215 * \param ext String. Ignored if is null or empty string.
2216 * \param folder String. Ignored if is null or empty string.
2217 * \return -1 on failure, 0 on success.
2218 */
2219static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
2220{
2221 mode_t mode = VOICEMAIL_DIR_MODE;
2222 int res;
2223
2224 make_dir(dest, len, context, ext, folder);
2225 if ((res = ast_mkdir(dest, mode))) {
2226 ast_log(AST_LOG_WARNING, "ast_mkdir '%s' failed: %s\n", dest, strerror(res));
2227 return -1;
2228 }
2229 return 0;
2230}
2231
2232static const char *mbox(struct ast_vm_user *vmu, int id)
2233{
2234#ifdef IMAP_STORAGE
2235 if (vmu && id == 0) {
2236 return vmu->imapfolder;
2237 }
2238#endif
2239 return (id >= 0 && id < ARRAY_LEN(mailbox_folders)) ? mailbox_folders[id] : "Unknown";
2240}
2241
2242static const char *vm_index_to_foldername(int id)
2243{
2244 return mbox(NULL, id);
2245}
2246
2247
2248static int get_folder_by_name(const char *name)
2249{
2250 size_t i;
2251
2252 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
2253 if (strcasecmp(name, mailbox_folders[i]) == 0) {
2254 return i;
2255 }
2256 }
2257
2258 return -1;
2259}
2260
2261static void free_user(struct ast_vm_user *vmu)
2262{
2263 if (!vmu) {
2264 return;
2265 }
2266
2267 ast_free(vmu->email);
2268 vmu->email = NULL;
2269 ast_free(vmu->emailbody);
2270 vmu->emailbody = NULL;
2271 ast_free(vmu->emailsubject);
2272 vmu->emailsubject = NULL;
2273
2274 if (ast_test_flag(vmu, VM_ALLOCED)) {
2275 ast_free(vmu);
2276 }
2277}
2278
2279static void free_user_final(struct ast_vm_user *vmu)
2280{
2281 if (!vmu) {
2282 return;
2283 }
2284
2285 if (!ast_strlen_zero(vmu->mailbox)) {
2287 }
2288
2289 free_user(vmu);
2290}
2291
2292static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg) {
2293
2294 int arraysize = (vmu->maxmsg > count_msg ? vmu->maxmsg : count_msg);
2295
2296 /* remove old allocation */
2297 if (vms->deleted) {
2298 ast_free(vms->deleted);
2299 vms->deleted = NULL;
2300 }
2301 if (vms->heard) {
2302 ast_free(vms->heard);
2303 vms->heard = NULL;
2304 }
2305 vms->dh_arraysize = 0;
2306
2307 if (arraysize > 0) {
2308 if (!(vms->deleted = ast_calloc(arraysize, sizeof(int)))) {
2309 return -1;
2310 }
2311 if (!(vms->heard = ast_calloc(arraysize, sizeof(int)))) {
2312 ast_free(vms->deleted);
2313 vms->deleted = NULL;
2314 return -1;
2315 }
2316 vms->dh_arraysize = arraysize;
2317 }
2318
2319 return 0;
2320}
2321
2322/* All IMAP-specific functions should go in this block. This
2323 * keeps them from being spread out all over the code */
2324#ifdef IMAP_STORAGE
2325static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu)
2326{
2327 char arg[10];
2328 struct vm_state *vms;
2329 unsigned long messageNum;
2330
2331 /* If greetings aren't stored in IMAP, just delete the file */
2332 if (msgnum < 0 && !imapgreetings) {
2334 return;
2335 }
2336
2337 if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2338 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);
2339 return;
2340 }
2341
2342 if (msgnum < 0) {
2343 imap_delete_old_greeting(file, vms);
2344 return;
2345 }
2346
2347 /* find real message number based on msgnum */
2348 /* this may be an index into vms->msgArray based on the msgnum. */
2349 messageNum = vms->msgArray[msgnum];
2350 if (messageNum == 0) {
2351 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n", msgnum, messageNum);
2352 return;
2353 }
2354 ast_debug(3, "deleting msgnum %d, which is mailbox message %lu\n", msgnum, messageNum);
2355 /* delete message */
2356 snprintf (arg, sizeof(arg), "%lu", messageNum);
2357 ast_mutex_lock(&vms->lock);
2358 mail_setflag (vms->mailstream, arg, "\\DELETED");
2359 mail_expunge(vms->mailstream);
2360 ast_mutex_unlock(&vms->lock);
2361}
2362
2363static 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)
2364{
2365 struct ast_channel *chan;
2366 char *cid;
2367 char *cid_name;
2368 char *cid_num;
2369 struct vm_state *vms;
2370 const char *duration_str;
2371 int duration = 0;
2372
2373 /*
2374 * First, get things initially set up. If any of this fails, then
2375 * back out before doing anything substantial
2376 */
2377 vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0);
2378 if (!vms) {
2379 return;
2380 }
2381
2382 if (open_mailbox(vms, vmu, folder)) {
2383 return;
2384 }
2385
2386 chan = ast_dummy_channel_alloc();
2387 if (!chan) {
2388 close_mailbox(vms, vmu);
2389 return;
2390 }
2391
2392 /*
2393 * We need to make sure the new message we save has the same
2394 * callerid, flag, and duration as the original message
2395 */
2396 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
2397
2398 if (!ast_strlen_zero(cid)) {
2399 ast_callerid_parse(cid, &cid_name, &cid_num);
2401 if (!ast_strlen_zero(cid_name)) {
2402 ast_channel_caller(chan)->id.name.valid = 1;
2403 ast_channel_caller(chan)->id.name.str = ast_strdup(cid_name);
2404 }
2405 if (!ast_strlen_zero(cid_num)) {
2406 ast_channel_caller(chan)->id.number.valid = 1;
2407 ast_channel_caller(chan)->id.number.str = ast_strdup(cid_num);
2408 }
2409 }
2410
2411 duration_str = ast_variable_retrieve(msg_cfg, "message", "duration");
2412
2413 if (!ast_strlen_zero(duration_str)) {
2414 sscanf(duration_str, "%30d", &duration);
2415 }
2416
2417 /*
2418 * IMAP messages cannot be altered once delivered. So we have to delete the
2419 * current message and then re-add it with the updated message ID.
2420 *
2421 * Furthermore, there currently is no atomic way to create a new message and to
2422 * store it in an arbitrary folder. So we have to save it to the INBOX and then
2423 * move to the appropriate folder.
2424 */
2425 if (!imap_store_file(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, vmfmts,
2426 duration, vms, ast_variable_retrieve(msg_cfg, "message", "flag"), msg_id)) {
2427 if (folder != NEW_FOLDER) {
2428 save_to_folder(vmu, vms, msgnum, folder, NULL, 1);
2429 }
2430 vm_imap_delete(dir, msgnum, vmu);
2431 }
2432 close_mailbox(vms, vmu);
2433 ast_channel_unref(chan);
2434}
2435
2436static int imap_retrieve_greeting(const char *dir, const int msgnum, struct ast_vm_user *vmu)
2437{
2438 struct vm_state *vms_p;
2439 char *file, *filename;
2440 char dest[PATH_MAX];
2441 int i;
2442 BODY *body;
2443 int ret = 0;
2444 int curr_mbox;
2445
2446 /* This function is only used for retrieval of IMAP greetings
2447 * regular messages are not retrieved this way, nor are greetings
2448 * if they are stored locally*/
2449 if (msgnum > -1 || !imapgreetings) {
2450 return 0;
2451 } else {
2452 file = strrchr(ast_strdupa(dir), '/');
2453 if (file)
2454 *file++ = '\0';
2455 else {
2456 ast_debug(1, "Failed to procure file name from directory passed.\n");
2457 return -1;
2458 }
2459 }
2460
2461 /* check if someone is accessing this box right now... */
2462 if (!(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) &&
2463 !(vms_p = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2464 /* Unlike when retrieving a message, it is reasonable not to be able to find a
2465 * vm_state for a mailbox when trying to retrieve a greeting. Just create one,
2466 * that's all we need to do.
2467 */
2468 if (!(vms_p = create_vm_state_from_user(vmu))) {
2469 ast_log(LOG_NOTICE, "Unable to create vm_state object!\n");
2470 return -1;
2471 }
2472 }
2473
2474 /* Greetings will never have a prepended message */
2475 *vms_p->introfn = '\0';
2476
2477 ast_mutex_lock(&vms_p->lock);
2478
2479 /* get the current mailbox so that we can point the mailstream back to it later */
2480 curr_mbox = get_folder_by_name(vms_p->curbox);
2481
2482 if (init_mailstream(vms_p, GREETINGS_FOLDER) || !vms_p->mailstream) {
2483 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2484 ast_mutex_unlock(&vms_p->lock);
2485 return -1;
2486 }
2487
2488 /*XXX Yuck, this could probably be done a lot better */
2489 for (i = 0; i < vms_p->mailstream->nmsgs; i++) {
2490 mail_fetchstructure(vms_p->mailstream, i + 1, &body);
2491 /* We have the body, now we extract the file name of the first attachment. */
2492 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2493 char *attachment = body->nested.part->next->body.parameter->value;
2494 char copy[strlen(attachment) + 1];
2495
2496 strcpy(copy, attachment); /* safe */
2497 attachment = copy;
2498
2499 filename = strsep(&attachment, ".");
2500 if (!strcmp(filename, file)) {
2501 ast_copy_string(vms_p->fn, dir, sizeof(vms_p->fn));
2502 vms_p->msgArray[vms_p->curmsg] = i + 1;
2503 create_dirpath(dest, sizeof(dest), vmu->context, vms_p->username, "");
2504 save_body(body, vms_p, "2", attachment, 0);
2505 ret = 0;
2506 break;
2507 }
2508 } else {
2509 ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
2510 ret = -1;
2511 break;
2512 }
2513 }
2514
2515 if (curr_mbox != -1) {
2516 /* restore previous mbox stream */
2517 if (init_mailstream(vms_p, curr_mbox) || !vms_p->mailstream) {
2518 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
2519 ret = -1;
2520 }
2521 }
2522 ast_mutex_unlock(&vms_p->lock);
2523 return ret;
2524}
2525
2526static int imap_retrieve_file(const char *dir, const int msgnum, const char *mailbox, const char *context)
2527{
2528 BODY *body;
2529 char *header_content;
2530 char *attachedfilefmt;
2531 char buf[80];
2532 struct vm_state *vms;
2533 char text_file[PATH_MAX];
2534 FILE *text_file_ptr;
2535 int res = 0;
2536 struct ast_vm_user *vmu;
2537 int curr_mbox;
2538
2539 if (!(vmu = find_user(NULL, context, mailbox))) {
2540 ast_log(LOG_WARNING, "Couldn't find user with mailbox %s@%s\n", mailbox, context);
2541 return -1;
2542 }
2543
2544 if (msgnum < 0) {
2545 if (imapgreetings) {
2546 res = imap_retrieve_greeting(dir, msgnum, vmu);
2547 goto exit;
2548 } else {
2549 res = 0;
2550 goto exit;
2551 }
2552 }
2553
2554 /* Before anything can happen, we need a vm_state so that we can
2555 * actually access the imap server through the vms->mailstream
2556 */
2557 if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
2558 /* This should not happen. If it does, then I guess we'd
2559 * need to create the vm_state, extract which mailbox to
2560 * open, and then set up the msgArray so that the correct
2561 * IMAP message could be accessed. If I have seen correctly
2562 * though, the vms should be obtainable from the vmstates list
2563 * and should have its msgArray properly set up.
2564 */
2565 ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
2566 res = -1;
2567 goto exit;
2568 }
2569
2570 /* Ensure we have the correct mailbox open and have a valid mailstream for it */
2571 curr_mbox = get_folder_by_name(vms->curbox);
2572 if (curr_mbox < 0) {
2573 ast_debug(3, "Mailbox folder curbox not set, defaulting to Inbox\n");
2574 curr_mbox = 0;
2575 }
2576 init_mailstream(vms, curr_mbox);
2577 if (!vms->mailstream) {
2578 ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmu->mailbox);
2579 res = -1;
2580 goto exit;
2581 }
2582
2583 make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
2584 snprintf(vms->introfn, sizeof(vms->introfn), "%sintro", vms->fn);
2585
2586 /* Don't try to retrieve a message from IMAP if it already is on the file system */
2587 if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
2588 res = 0;
2589 goto exit;
2590 }
2591
2592 ast_debug(3, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
2593 if (vms->msgArray[msgnum] == 0) {
2594 ast_log(LOG_WARNING, "Trying to access unknown message\n");
2595 res = -1;
2596 goto exit;
2597 }
2598
2599 /* This will only work for new messages... */
2600 ast_mutex_lock(&vms->lock);
2601 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
2602 ast_mutex_unlock(&vms->lock);
2603 /* empty string means no valid header */
2604 if (ast_strlen_zero(header_content)) {
2605 ast_log(LOG_ERROR, "Could not fetch header for message number %ld\n", vms->msgArray[msgnum]);
2606 res = -1;
2607 goto exit;
2608 }
2609
2610 ast_mutex_lock(&vms->lock);
2611 mail_fetchstructure(vms->mailstream, vms->msgArray[msgnum], &body);
2612 ast_mutex_unlock(&vms->lock);
2613
2614 /* We have the body, now we extract the file name of the first attachment. */
2615 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
2616 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
2617 } else {
2618 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
2619 res = -1;
2620 goto exit;
2621 }
2622
2623 /* Find the format of the attached file */
2624
2625 strsep(&attachedfilefmt, ".");
2626 if (!attachedfilefmt) {
2627 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
2628 res = -1;
2629 goto exit;
2630 }
2631
2632 save_body(body, vms, "2", attachedfilefmt, 0);
2633 if (save_body(body, vms, "3", attachedfilefmt, 1)) {
2634 *vms->introfn = '\0';
2635 }
2636
2637 /* Get info from headers!! */
2638 snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
2639
2640 if (!(text_file_ptr = fopen(text_file, "w"))) {
2641 ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
2642 goto exit;
2643 }
2644
2645 fprintf(text_file_ptr, "%s\n", "[message]");
2646
2647 if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:", buf, sizeof(buf))) {
2648 fprintf(text_file_ptr, "callerid=\"%s\" ", S_OR(buf, ""));
2649 }
2650 if (get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:", buf, sizeof(buf))) {
2651 fprintf(text_file_ptr, "<%s>\n", S_OR(buf, ""));
2652 }
2653 if (get_header_by_tag(header_content, "X-Asterisk-VM-Context:", buf, sizeof(buf))) {
2654 fprintf(text_file_ptr, "context=%s\n", S_OR(buf, ""));
2655 }
2656 if (get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:", buf, sizeof(buf))) {
2657 fprintf(text_file_ptr, "origtime=%s\n", S_OR(buf, ""));
2658 }
2659 if (get_header_by_tag(header_content, "X-Asterisk-VM-Duration:", buf, sizeof(buf))) {
2660 fprintf(text_file_ptr, "duration=%s\n", S_OR(buf, ""));
2661 }
2662 if (get_header_by_tag(header_content, "X-Asterisk-VM-Category:", buf, sizeof(buf))) {
2663 fprintf(text_file_ptr, "category=%s\n", S_OR(buf, ""));
2664 }
2665 if (get_header_by_tag(header_content, "X-Asterisk-VM-Flag:", buf, sizeof(buf))) {
2666 fprintf(text_file_ptr, "flag=%s\n", S_OR(buf, ""));
2667 }
2668 if (get_header_by_tag(header_content, "X-Asterisk-VM-Message-ID:", buf, sizeof(buf))) {
2669 fprintf(text_file_ptr, "msg_id=%s\n", S_OR(buf, ""));
2670 }
2671 fclose(text_file_ptr);
2672
2673exit:
2674 free_user(vmu);
2675 return res;
2676}
2677
2678static int folder_int(const char *folder)
2679{
2680 /*assume a NULL folder means INBOX*/
2681 if (!folder) {
2682 return 0;
2683 }
2684 if (!strcasecmp(folder, imapfolder)) {
2685 return 0;
2686 } else if (!strcasecmp(folder, "Old")) {
2687 return 1;
2688 } else if (!strcasecmp(folder, "Work")) {
2689 return 2;
2690 } else if (!strcasecmp(folder, "Family")) {
2691 return 3;
2692 } else if (!strcasecmp(folder, "Friends")) {
2693 return 4;
2694 } else if (!strcasecmp(folder, "Cust1")) {
2695 return 5;
2696 } else if (!strcasecmp(folder, "Cust2")) {
2697 return 6;
2698 } else if (!strcasecmp(folder, "Cust3")) {
2699 return 7;
2700 } else if (!strcasecmp(folder, "Cust4")) {
2701 return 8;
2702 } else if (!strcasecmp(folder, "Cust5")) {
2703 return 9;
2704 } else if (!strcasecmp(folder, "Urgent")) {
2705 return 11;
2706 } else { /*assume they meant INBOX if folder is not found otherwise*/
2707 return 0;
2708 }
2709}
2710
2711static int __messagecount(const char *context, const char *mailbox, const char *folder)
2712{
2713 SEARCHPGM *pgm;
2714 SEARCHHEADER *hdr;
2715
2716 struct ast_vm_user *vmu, vmus;
2717 struct vm_state *vms_p;
2718 int ret = 0;
2719 int fold = folder_int(folder);
2720 int urgent = 0;
2721
2722 /* If URGENT, then look at INBOX */
2723 if (fold == 11) {
2724 fold = NEW_FOLDER;
2725 urgent = 1;
2726 }
2727
2729 return 0;
2730
2731 /* We have to get the user before we can open the stream! */
2732 memset(&vmus, 0, sizeof(vmus));
2733 vmu = find_user(&vmus, context, mailbox);
2734 if (!vmu) {
2735 ast_log(AST_LOG_WARNING, "Couldn't find mailbox %s in context %s\n", mailbox, context);
2736 free_user(vmu);
2737 return -1;
2738 } else {
2739 /* No IMAP account available */
2740 if (vmu->imapuser[0] == '\0') {
2741 ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2742 free_user(vmu);
2743 return -1;
2744 }
2745 }
2746
2747 /* No IMAP account available */
2748 if (vmu->imapuser[0] == '\0') {
2749 ast_log(AST_LOG_WARNING, "IMAP user not set for mailbox %s\n", vmu->mailbox);
2750 free_user(vmu);
2751 return -1;
2752 }
2753
2754 /* check if someone is accessing this box right now... */
2755 vms_p = get_vm_state_by_imapuser(vmu->imapuser, 1);
2756 if (!vms_p) {
2757 vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
2758 }
2759 if (vms_p) {
2760 ast_debug(3, "Returning before search - user is logged in\n");
2761 if (fold == 0) { /* INBOX */
2762 free_user(vmu);
2763 return urgent ? vms_p->urgentmessages : vms_p->newmessages;
2764 }
2765 if (fold == 1) { /* Old messages */
2766 free_user(vmu);
2767 return vms_p->oldmessages;
2768 }
2769 }
2770
2771 /* add one if not there... */
2772 vms_p = get_vm_state_by_imapuser(vmu->imapuser, 0);
2773 if (!vms_p) {
2774 vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
2775 }
2776
2777 if (!vms_p) {
2778 vms_p = create_vm_state_from_user(vmu);
2779 }
2780 ret = init_mailstream(vms_p, fold);
2781 if (!vms_p->mailstream) {
2782 ast_log(AST_LOG_ERROR, "Houston we have a problem - IMAP mailstream is NULL\n");
2783 free_user(vmu);
2784 return -1;
2785 }
2786 if (ret == 0) {
2787 ast_mutex_lock(&vms_p->lock);
2788 pgm = mail_newsearchpgm ();
2789 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)(!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
2790 hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", (char *) S_OR(context, "default"));
2791 pgm->header = hdr;
2792 if (fold != OLD_FOLDER) {
2793 pgm->unseen = 1;
2794 pgm->seen = 0;
2795 }
2796 /* In the special case where fold is 1 (old messages) we have to do things a bit
2797 * differently. Old messages are stored in the INBOX but are marked as "seen"
2798 */
2799 else {
2800 pgm->unseen = 0;
2801 pgm->seen = 1;
2802 }
2803 /* look for urgent messages */
2804 if (fold == NEW_FOLDER) {
2805 if (urgent) {
2806 pgm->flagged = 1;
2807 pgm->unflagged = 0;
2808 } else {
2809 pgm->flagged = 0;
2810 pgm->unflagged = 1;
2811 }
2812 }
2813 pgm->undeleted = 1;
2814 pgm->deleted = 0;
2815
2816 vms_p->vmArrayIndex = 0;
2817 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
2818 if (fold == 0 && urgent == 0)
2819 vms_p->newmessages = vms_p->vmArrayIndex;
2820 if (fold == 1)
2821 vms_p->oldmessages = vms_p->vmArrayIndex;
2822 if (fold == 0 && urgent == 1)
2823 vms_p->urgentmessages = vms_p->vmArrayIndex;
2824 /*Freeing the searchpgm also frees the searchhdr*/
2825 mail_free_searchpgm(&pgm);
2826 ast_mutex_unlock(&vms_p->lock);
2827 free_user(vmu);
2828 vms_p->updated = 0;
2829 return vms_p->vmArrayIndex;
2830 } else {
2831 ast_mutex_lock(&vms_p->lock);
2832 mail_ping(vms_p->mailstream);
2833 ast_mutex_unlock(&vms_p->lock);
2834 }
2835 free_user(vmu);
2836 return 0;
2837}
2838
2839static int imap_check_limits(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu, int msgnum)
2840{
2841 /* Check if mailbox is full */
2842 check_quota(vms, vmu->imapfolder);
2843 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
2844 ast_debug(1, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
2845 if (chan) {
2846 ast_play_and_wait(chan, "vm-mailboxfull");
2847 }
2848 return -1;
2849 }
2850
2851 /* Check if we have exceeded maxmsg */
2852 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));
2853 if (msgnum >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
2854 ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u >= %u)\n", msgnum, vmu->maxmsg);
2855 if (chan) {
2856 ast_play_and_wait(chan, "vm-mailboxfull");
2857 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
2858 }
2859 return -1;
2860 }
2861
2862 return 0;
2863}
2864
2865/*!
2866 * \brief Gets the number of messages that exist in a mailbox folder.
2867 * \param mailbox_id
2868 * \param folder
2869 *
2870 * This method is used when IMAP backend is used.
2871 * \return The number of messages in this mailbox folder (zero or more).
2872 */
2873static int messagecount(const char *mailbox_id, const char *folder)
2874{
2875 char *context;
2876 char *mailbox;
2877 int count;
2878
2879 if (ast_strlen_zero(mailbox_id)
2880 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
2881 return 0;
2882 }
2883
2884 if (ast_strlen_zero(folder) || !strcmp(folder, "INBOX")) {
2885 count = __messagecount(context, mailbox, "INBOX") + __messagecount(context, mailbox, "Urgent");
2886 } else {
2887 count = __messagecount(context, mailbox, folder);
2888 }
2889 return count < 0 ? 0 : count;
2890}
2891
2892static 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)
2893{
2894 char *myserveremail = serveremail;
2895 char fn[PATH_MAX];
2896 char introfn[PATH_MAX];
2897 char mailbox[256];
2898 char *stringp;
2899 FILE *p = NULL;
2900 char tmp[80] = "/tmp/astmail-XXXXXX";
2901 long len;
2902 void *buf;
2903 int tempcopy = 0;
2904 STRING str;
2905 int ret; /* for better error checking */
2906 char *imap_flags = NIL;
2907 int msgcount;
2908 int box = NEW_FOLDER;
2909
2910 snprintf(mailbox, sizeof(mailbox), "%s@%s", vmu->mailbox, vmu->context);
2911 msgcount = messagecount(mailbox, "INBOX") + messagecount(mailbox, "Old");
2912
2913 /* Back out early if this is a greeting and we don't want to store greetings in IMAP */
2914 if (msgnum < 0) {
2915 if(!imapgreetings) {
2916 return 0;
2917 } else {
2918 box = GREETINGS_FOLDER;
2919 }
2920 }
2921
2922 if (imap_check_limits(chan, vms, vmu, msgcount)) {
2923 return -1;
2924 }
2925
2926 /* Set urgent flag for IMAP message */
2927 if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
2928 ast_debug(3, "Setting message flag \\\\FLAGGED.\n");
2929 imap_flags = "\\FLAGGED";
2930 }
2931
2932 /* Attach only the first format */
2933 fmt = ast_strdupa(fmt);
2934 stringp = fmt;
2935 strsep(&stringp, "|");
2936
2937 if (!ast_strlen_zero(vmu->serveremail))
2938 myserveremail = vmu->serveremail;
2939
2940 if (msgnum > -1)
2941 make_file(fn, sizeof(fn), dir, msgnum);
2942 else
2943 ast_copy_string (fn, dir, sizeof(fn));
2944
2945 snprintf(introfn, sizeof(introfn), "%sintro", fn);
2946 if (ast_fileexists(introfn, NULL, NULL) <= 0) {
2947 *introfn = '\0';
2948 }
2949
2950 if (ast_strlen_zero(vmu->email)) {
2951 /* We need the vmu->email to be set when we call make_email_file, but
2952 * if we keep it set, a duplicate e-mail will be created. So at the end
2953 * of this function, we will revert back to an empty string if tempcopy
2954 * is 1.
2955 */
2956 vmu->email = ast_strdup(vmu->imapuser);
2957 tempcopy = 1;
2958 }
2959
2960 if (!strcmp(fmt, "wav49"))
2961 fmt = "WAV";
2962 ast_debug(3, "Storing file '%s', format '%s'\n", fn, fmt);
2963
2964 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2965 command hangs. */
2967 ast_log(AST_LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
2968 if (tempcopy) {
2969 ast_free(vmu->email);
2970 vmu->email = NULL;
2971 }
2972 return -1;
2973 }
2974
2975 if (msgnum < 0 && imapgreetings) {
2976 if ((ret = init_mailstream(vms, GREETINGS_FOLDER))) {
2977 ast_log(AST_LOG_WARNING, "Unable to open mailstream.\n");
2978 return -1;
2979 }
2980 imap_delete_old_greeting(fn, vms);
2981 }
2982
2983 make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, "INBOX",
2984 chan ? S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL) : NULL,
2985 chan ? S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL) : NULL,
2986 fn, introfn, fmt, duration, 1, chan, NULL, 1, flag, msg_id);
2987 /* read mail file to memory */
2988 len = ftell(p);
2989 rewind(p);
2990 if (!(buf = ast_malloc(len + 1))) {
2991 ast_log(AST_LOG_ERROR, "Can't allocate %ld bytes to read message\n", len + 1);
2992 fclose(p);
2993 if (tempcopy)
2994 *(vmu->email) = '\0';
2995 return -1;
2996 }
2997 if (fread(buf, 1, len, p) != len) {
2998 if (ferror(p)) {
2999 ast_log(LOG_ERROR, "Error while reading mail file: %s\n", tmp);
3000 return -1;
3001 }
3002 }
3003 ((char *) buf)[len] = '\0';
3004 INIT(&str, mail_string, buf, len);
3005 ret = init_mailstream(vms, box);
3006 if (ret == 0) {
3007 imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1);
3008 ast_mutex_lock(&vms->lock);
3009 if(!mail_append_full(vms->mailstream, mailbox, imap_flags, NIL, &str))
3010 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
3011 ast_mutex_unlock(&vms->lock);
3012 fclose(p);
3013 unlink(tmp);
3014 ast_free(buf);
3015 } else {
3016 ast_log(LOG_ERROR, "Could not initialize mailstream for %s\n", mailbox);
3017 fclose(p);
3018 unlink(tmp);
3019 ast_free(buf);
3020 return -1;
3021 }
3022 ast_debug(3, "%s stored\n", fn);
3023
3024 if (tempcopy)
3025 *(vmu->email) = '\0';
3026 inprocess_count(vmu->mailbox, vmu->context, -1);
3027 return 0;
3028
3029}
3030
3031/*!
3032 * \brief Gets the number of messages that exist in the inbox folder.
3033 * \param mailbox_context
3034 * \param newmsgs The variable that is updated with the count of new messages within this inbox.
3035 * \param oldmsgs The variable that is updated with the count of old messages within this inbox.
3036 * \param urgentmsgs The variable that is updated with the count of urgent messages within this inbox.
3037 *
3038 * This method is used when IMAP backend is used.
3039 * Simultaneously determines the count of new,old, and urgent messages. The total messages would then be the sum of these three.
3040 *
3041 * \return zero on success, -1 on error.
3042 */
3043
3044static int inboxcount2(const char *mailbox_context, int *urgentmsgs, int *newmsgs, int *oldmsgs)
3045{
3046 char tmp[PATH_MAX] = "";
3047 char *mailboxnc;
3048 char *context;
3049 char *mb;
3050 char *cur;
3051 int count = 0;
3052 if (newmsgs)
3053 *newmsgs = 0;
3054 if (oldmsgs)
3055 *oldmsgs = 0;
3056 if (urgentmsgs)
3057 *urgentmsgs = 0;
3058
3059 ast_debug(3, "Mailbox is set to %s\n", mailbox_context);
3060 /* If no mailbox, return immediately */
3061 if (ast_strlen_zero(mailbox_context))
3062 return 0;
3063
3064 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
3065 context = strchr(tmp, '@');
3066 if (strchr(mailbox_context, ',')) {
3067 int tmpnew, tmpold, tmpurgent;
3068 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
3069 mb = tmp;
3070 while ((cur = strsep(&mb, ", "))) {
3071 if (!ast_strlen_zero(cur)) {
3072 if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
3073 return -1;
3074 else {
3075 if (newmsgs)
3076 *newmsgs += tmpnew;
3077 if (oldmsgs)
3078 *oldmsgs += tmpold;
3079 if (urgentmsgs)
3080 *urgentmsgs += tmpurgent;
3081 }
3082 }
3083 }
3084 return 0;
3085 }
3086 if (context) {
3087 *context = '\0';
3088 mailboxnc = tmp;
3089 context++;
3090 } else {
3091 context = "default";
3092 mailboxnc = (char *) mailbox_context;
3093 }
3094
3095 if (newmsgs) {
3096 struct ast_vm_user *vmu = find_user(NULL, context, mailboxnc);
3097 if (!vmu) {
3098 ast_log(AST_LOG_ERROR, "Couldn't find mailbox %s in context %s\n", mailboxnc, context);
3099 return -1;
3100 }
3101 if ((count = __messagecount(context, mailboxnc, vmu->imapfolder)) < 0) {
3102 free_user(vmu);
3103 return -1;
3104 }
3105 *newmsgs = count;
3106 free_user(vmu);
3107 }
3108 if (oldmsgs) {
3109 if ((count = __messagecount(context, mailboxnc, "Old")) < 0) {
3110 return -1;
3111 }
3112 *oldmsgs = count;
3113 }
3114 if (urgentmsgs) {
3115 if ((count = __messagecount(context, mailboxnc, "Urgent")) < 0) {
3116 return -1;
3117 }
3118 *urgentmsgs = count;
3119 }
3120 return 0;
3121}
3122
3123/*!
3124 * \brief Determines if the given folder has messages.
3125 * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
3126 * \param folder the folder to look in
3127 *
3128 * This function is used when the mailbox is stored in an IMAP back end.
3129 * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
3130 * \return 1 if the folder has one or more messages. zero otherwise.
3131 */
3132
3133static int has_voicemail(const char *mailbox, const char *folder)
3134{
3135 char tmp[256], *tmp2, *box, *context;
3136 ast_copy_string(tmp, mailbox, sizeof(tmp));
3137 tmp2 = tmp;
3138 if (strchr(tmp2, ',') || strchr(tmp2, '&')) {
3139 while ((box = strsep(&tmp2, ",&"))) {
3140 if (!ast_strlen_zero(box)) {
3141 if (has_voicemail(box, folder)) {
3142 return 1;
3143 }
3144 }
3145 }
3146 }
3147 if ((context = strchr(tmp, '@'))) {
3148 *context++ = '\0';
3149 } else {
3150 context = "default";
3151 }
3152 return __messagecount(context, tmp, folder) > 0 ? 1 : 0;
3153}
3154
3155/*!
3156 * \brief Copies a message from one mailbox to another.
3157 * \param chan
3158 * \param vmu
3159 * \param imbox
3160 * \param msgnum
3161 * \param duration
3162 * \param recip
3163 * \param fmt
3164 * \param dir
3165 * \param flag, dest_folder
3166 *
3167 * This works with IMAP storage based mailboxes.
3168 *
3169 * \return zero on success, -1 on error.
3170 */
3171static 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)
3172{
3173 struct vm_state *sendvms = NULL;
3174 char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
3175 if (msgnum >= recip->maxmsg) {
3176 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
3177 return -1;
3178 }
3179 if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
3180 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
3181 return -1;
3182 }
3183 if (!get_vm_state_by_imapuser(recip->imapuser, 0)) {
3184 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
3185 return -1;
3186 }
3187 snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
3188 ast_mutex_lock(&sendvms->lock);
3189 if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(vmu, imbox)) == T)) {
3190 ast_mutex_unlock(&sendvms->lock);
3191 return 0;
3192 }
3193 ast_mutex_unlock(&sendvms->lock);
3194 ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
3195 return -1;
3196}
3197
3198static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
3199{
3200 char tmp[256], *t = tmp;
3201 size_t left = sizeof(tmp);
3202
3203 if (box == OLD_FOLDER) {
3204 ast_copy_string(vms->curbox, mbox(NULL, NEW_FOLDER), sizeof(vms->curbox));
3205 } else {
3206 ast_copy_string(vms->curbox, mbox(NULL, box), sizeof(vms->curbox));
3207 }
3208
3209 if (box == NEW_FOLDER) {
3210 ast_copy_string(vms->vmbox, "vm-INBOX", sizeof(vms->vmbox));
3211 } else {
3212 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(NULL, box));
3213 }
3214
3215 /* Build up server information */
3216 ast_build_string(&t, &left, "{%s:%s/imap", S_OR(vms->imapserver, imapserver), S_OR(vms->imapport, imapport));
3217
3218 /* Add authentication user if present */
3219 if (!ast_strlen_zero(authuser))
3220 ast_build_string(&t, &left, "/authuser=%s", authuser);
3221
3222 /* Add flags if present */
3223 if (!ast_strlen_zero(imapflags) || !(ast_strlen_zero(vms->imapflags))) {
3224 ast_build_string(&t, &left, "/%s", S_OR(vms->imapflags, imapflags));
3225 }
3226
3227 /* End with username */
3228#if 1
3229 ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
3230#else
3231 ast_build_string(&t, &left, "/user=%s/novalidate-cert}", vms->imapuser);
3232#endif
3233 if (box == NEW_FOLDER || box == OLD_FOLDER)
3234 snprintf(spec, len, "%s%s", tmp, use_folder? vms->imapfolder: "INBOX");
3235 else if (box == GREETINGS_FOLDER)
3236 snprintf(spec, len, "%s%s", tmp, greetingfolder);
3237 else { /* Other folders such as Friends, Family, etc... */
3238 if (!ast_strlen_zero(imapparentfolder)) {
3239 /* imapparentfolder would typically be set to INBOX */
3240 snprintf(spec, len, "%s%s%c%s", tmp, imapparentfolder, delimiter, mbox(NULL, box));
3241 } else {
3242 snprintf(spec, len, "%s%s", tmp, mbox(NULL, box));
3243 }
3244 }
3245}
3246
3247static int init_mailstream(struct vm_state *vms, int box)
3248{
3249 MAILSTREAM *stream = NIL;
3250 long debug;
3251 char tmp[256];
3252
3253 if (!vms) {
3254 ast_log(LOG_ERROR, "vm_state is NULL!\n");
3255 return -1;
3256 }
3257 ast_debug(3, "vm_state user is:%s\n", vms->imapuser);
3258 if (vms->mailstream == NIL || !vms->mailstream) {
3259 ast_debug(1, "mailstream not set.\n");
3260 } else {
3261 stream = vms->mailstream;
3262 }
3263 /* debug = T; user wants protocol telemetry? */
3264 debug = NIL; /* NO protocol telemetry? */
3265
3266 if (delimiter == '\0') { /* did not probe the server yet */
3267 char *cp;
3268#ifdef USE_SYSTEM_IMAP
3269#include <imap/linkage.c>
3270#elif defined(USE_SYSTEM_CCLIENT)
3271#include <c-client/linkage.c>
3272#else
3273#include "linkage.c"
3274#endif
3275 /* Connect to INBOX first to get folders delimiter */
3276 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
3277 ast_mutex_lock(&vms->lock);
3278 ast_mutex_lock(&mail_open_lock);
3279 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
3280 ast_mutex_unlock(&mail_open_lock);
3281 ast_mutex_unlock(&vms->lock);
3282 if (stream == NIL) {
3283 ast_log(LOG_ERROR, "Can't connect to imap server %s\n", tmp);
3284 return -1;
3285 }
3286 get_mailbox_delimiter(vms, stream);
3287 /* update delimiter in imapfolder */
3288 for (cp = vms->imapfolder; *cp; cp++)
3289 if (*cp == '/')
3290 *cp = delimiter;
3291 }
3292 /* Now connect to the target folder */
3293 imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
3294 ast_debug(3, "Before mail_open, server: %s, box:%d\n", tmp, box);
3295 ast_mutex_lock(&vms->lock);
3296 ast_mutex_lock(&mail_open_lock);
3297 vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
3298 /* Create the folder if it doesn't exist */
3299 if (vms->mailstream && !mail_status(vms->mailstream, tmp, SA_UIDNEXT)) {
3300 mail_create(vms->mailstream, tmp);
3301 }
3302 ast_mutex_unlock(&mail_open_lock);
3303 ast_mutex_unlock(&vms->lock);
3304 if (vms->mailstream == NIL) {
3305 return -1;
3306 } else {
3307 return 0;
3308 }
3309}
3310
3311static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
3312{
3313 SEARCHPGM *pgm;
3314 SEARCHHEADER *hdr;
3315 int urgent = 0;
3316
3317 /* If Urgent, then look at INBOX */
3318 if (box == 11) {
3319 box = NEW_FOLDER;
3320 urgent = 1;
3321 }
3322
3323 ast_copy_string(vms->imapuser, vmu->imapuser, sizeof(vms->imapuser));
3324 ast_copy_string(vms->imapfolder, vmu->imapfolder, sizeof(vms->imapfolder));
3325 ast_copy_string(vms->imapserver, vmu->imapserver, sizeof(vms->imapserver));
3326 ast_copy_string(vms->imapport, vmu->imapport, sizeof(vms->imapport));
3327 ast_copy_string(vms->imapflags, vmu->imapflags, sizeof(vms->imapflags));
3328 vms->imapversion = vmu->imapversion;
3329 ast_debug(3, "Before init_mailstream, user is %s\n", vmu->imapuser);
3330
3331 if (init_mailstream(vms, box) || !vms->mailstream) {
3332 ast_log(AST_LOG_ERROR, "Could not initialize mailstream\n");
3333 return -1;
3334 }
3335
3336 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
3337
3338 /* Check Quota */
3339 if (box == 0) {
3340 ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mbox(vmu, box));
3341 check_quota(vms, (char *) mbox(vmu, box));
3342 }
3343
3344 ast_mutex_lock(&vms->lock);
3345 pgm = mail_newsearchpgm();
3346
3347 /* Check IMAP folder for Asterisk messages only... */
3348 hdr = mail_newsearchheader("X-Asterisk-VM-Extension", (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : vmu->mailbox));
3349 hdr->next = mail_newsearchheader("X-Asterisk-VM-Context", vmu->context);
3350 pgm->header = hdr;
3351 pgm->deleted = 0;
3352 pgm->undeleted = 1;
3353
3354 /* if box = NEW_FOLDER, check for new, if box = OLD_FOLDER, check for read */
3355 if (box == NEW_FOLDER && urgent == 1) {
3356 pgm->unseen = 1;
3357 pgm->seen = 0;
3358 pgm->flagged = 1;
3359 pgm->unflagged = 0;
3360 } else if (box == NEW_FOLDER && urgent == 0) {
3361 pgm->unseen = 1;
3362 pgm->seen = 0;
3363 pgm->flagged = 0;
3364 pgm->unflagged = 1;
3365 } else if (box == OLD_FOLDER) {
3366 pgm->seen = 1;
3367 pgm->unseen = 0;
3368 }
3369
3370 ast_debug(3, "Before mail_search_full, user is %s\n", vmu->imapuser);
3371
3372 vms->vmArrayIndex = 0;
3373 mail_search_full (vms->mailstream, NULL, pgm, NIL);
3374 vms->lastmsg = vms->vmArrayIndex - 1;
3375 mail_free_searchpgm(&pgm);
3376 /* Since IMAP storage actually stores both old and new messages in the same IMAP folder,
3377 * ensure to allocate enough space to account for all of them. Warn if old messages
3378 * have not been checked first as that is required.
3379 */
3380 if (box == 0 && !vms->dh_arraysize) {
3381 ast_log(LOG_WARNING, "The code expects the old messages to be checked first, fix the code.\n");
3382 }
3383 if (vm_allocate_dh(vms, vmu, box == 0 ? vms->vmArrayIndex + vms->oldmessages : vms->lastmsg)) {
3384 ast_mutex_unlock(&vms->lock);
3385 return -1;
3386 }
3387
3388 ast_mutex_unlock(&vms->lock);
3389 return 0;
3390}
3391
3392static void write_file(char *filename, char *buffer, unsigned long len)
3393{
3394 FILE *output;
3395
3396 if (!filename || !buffer) {
3397 return;
3398 }
3399
3400 if (!(output = fopen(filename, "w"))) {
3401 ast_log(LOG_ERROR, "Unable to open/create file %s: %s\n", filename, strerror(errno));
3402 return;
3403 }
3404
3405 if (fwrite(buffer, len, 1, output) != 1) {
3406 if (ferror(output)) {
3407 ast_log(LOG_ERROR, "Short write while writing e-mail body: %s.\n", strerror(errno));
3408 }
3409 }
3410 fclose (output);
3411}
3412
3413static void update_messages_by_imapuser(const char *user, unsigned long number)
3414{
3415 struct vm_state *vms = get_vm_state_by_imapuser(user, 1);
3416
3417 if (!vms && !(vms = get_vm_state_by_imapuser(user, 0))) {
3418 return;
3419 }
3420
3421 ast_debug(3, "saving mailbox message number %lu as message %d. Interactive set to %d\n", number, vms->vmArrayIndex, vms->interactive);
3422
3423 /* Ensure we have room for the next message. */
3424 if (vms->vmArrayIndex >= vms->msg_array_max) {
3425 long *new_mem = ast_realloc(vms->msgArray, 2 * vms->msg_array_max * sizeof(long));
3426 if (!new_mem) {
3427 return;
3428 }
3429 vms->msgArray = new_mem;
3430 vms->msg_array_max *= 2;
3431 }
3432
3433 vms->msgArray[vms->vmArrayIndex++] = number;
3434}
3435
3436void mm_searched(MAILSTREAM *stream, unsigned long number)
3437{
3438 char *mailbox = stream->mailbox, buf[1024] = "", *user;
3439
3440 if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))))
3441 return;
3442
3443 update_messages_by_imapuser(user, number);
3444}
3445
3446static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
3447{
3448 struct ast_variable *var;
3449 struct ast_vm_user *vmu;
3450
3451 vmu = ast_calloc(1, sizeof *vmu);
3452 if (!vmu)
3453 return NULL;
3454
3455 populate_defaults(vmu);
3457
3458 var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
3459 if (var) {
3460 apply_options_full(vmu, var);
3462 return vmu;
3463 } else {
3464 ast_free(vmu);
3465 return NULL;
3466 }
3467}
3468
3469/* Interfaces to C-client */
3470
3471void mm_exists(MAILSTREAM * stream, unsigned long number)
3472{
3473 /* mail_ping will callback here if new mail! */
3474 ast_debug(4, "Entering EXISTS callback for message %ld\n", number);
3475 if (number == 0) return;
3476 set_update(stream);
3477}
3478
3479
3480void mm_expunged(MAILSTREAM * stream, unsigned long number)
3481{
3482 /* mail_ping will callback here if expunged mail! */
3483 ast_debug(4, "Entering EXPUNGE callback for message %ld\n", number);
3484 if (number == 0) return;
3485 set_update(stream);
3486}
3487
3488
3489void mm_flags(MAILSTREAM * stream, unsigned long number)
3490{
3491 /* mail_ping will callback here if read mail! */
3492 ast_debug(4, "Entering FLAGS callback for message %ld\n", number);
3493 if (number == 0) return;
3494 set_update(stream);
3495}
3496
3497
3498void mm_notify(MAILSTREAM * stream, char *string, long errflg)
3499{
3500 ast_debug(5, "Entering NOTIFY callback, errflag is %ld, string is %s\n", errflg, string);
3501 mm_log (string, errflg);
3502}
3503
3504
3505void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
3506{
3507 if (delimiter == '\0') {
3508 delimiter = delim;
3509 }
3510
3511 ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
3512 if (attributes & LATT_NOINFERIORS)
3513 ast_debug(5, "no inferiors\n");
3514 if (attributes & LATT_NOSELECT)
3515 ast_debug(5, "no select\n");
3516 if (attributes & LATT_MARKED)
3517 ast_debug(5, "marked\n");
3518 if (attributes & LATT_UNMARKED)
3519 ast_debug(5, "unmarked\n");
3520}
3521
3522
3523void mm_lsub(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
3524{
3525 ast_debug(5, "Delimiter set to %c and mailbox %s\n", delim, mailbox);
3526 if (attributes & LATT_NOINFERIORS)
3527 ast_debug(5, "no inferiors\n");
3528 if (attributes & LATT_NOSELECT)
3529 ast_debug(5, "no select\n");
3530 if (attributes & LATT_MARKED)
3531 ast_debug(5, "marked\n");
3532 if (attributes & LATT_UNMARKED)
3533 ast_debug(5, "unmarked\n");
3534}
3535
3536
3537void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
3538{
3539 struct ast_str *str;
3540
3541 if (!DEBUG_ATLEAST(5) || !(str = ast_str_create(256))) {
3542 return;
3543 }
3544
3545 ast_str_append(&str, 0, " Mailbox %s", mailbox);
3546 if (status->flags & SA_MESSAGES) {
3547 ast_str_append(&str, 0, ", %lu messages", status->messages);
3548 }
3549 if (status->flags & SA_RECENT) {
3550 ast_str_append(&str, 0, ", %lu recent", status->recent);
3551 }
3552 if (status->flags & SA_UNSEEN) {
3553 ast_str_append(&str, 0, ", %lu unseen", status->unseen);
3554 }
3555 if (status->flags & SA_UIDVALIDITY) {
3556 ast_str_append(&str, 0, ", %lu UID validity", status->uidvalidity);
3557 }
3558 if (status->flags & SA_UIDNEXT) {
3559 ast_str_append(&str, 0, ", %lu next UID", status->uidnext);
3560 }
3562
3563 ast_free(str);
3564}
3565
3566
3567void mm_log(char *string, long errflg)
3568{
3569 switch ((short) errflg) {
3570 case NIL:
3571 ast_debug(1, "IMAP Info: %s\n", string);
3572 break;
3573 case PARSE:
3574 case WARN:
3575 ast_log(AST_LOG_WARNING, "IMAP Warning: %s\n", string);
3576 break;
3577 case ERROR:
3578 ast_log(AST_LOG_ERROR, "IMAP Error: %s\n", string);
3579 break;
3580 }
3581}
3582
3583
3584void mm_dlog(char *string)
3585{
3586 ast_log(AST_LOG_NOTICE, "%s\n", string);
3587}
3588
3589
3590void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
3591{
3592 struct ast_vm_user *vmu;
3593
3594 ast_debug(4, "Entering callback mm_login\n");
3595
3596 ast_copy_string(user, mb->user, MAILTMPLEN);
3597
3598 /* We should only do this when necessary */
3599 if (!ast_strlen_zero(authpassword)) {
3600 ast_copy_string(pwd, authpassword, MAILTMPLEN);
3601 } else {
3602 AST_LIST_TRAVERSE(&users, vmu, list) {
3603 if (!strcasecmp(mb->user, vmu->imapuser)) {
3604 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
3605 break;
3606 }
3607 }
3608 if (!vmu) {
3609 if ((vmu = find_user_realtime_imapuser(mb->user))) {
3610 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
3611 free_user(vmu);
3612 }
3613 }
3614 }
3615}
3616
3617
3618void mm_critical(MAILSTREAM * stream)
3619{
3620}
3621
3622
3623void mm_nocritical(MAILSTREAM * stream)
3624{
3625}
3626
3627
3628long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
3629{
3630 kill (getpid (), SIGSTOP);
3631 return NIL;
3632}
3633
3634
3635void mm_fatal(char *string)
3636{
3637 ast_log(AST_LOG_ERROR, "IMAP access FATAL error: %s\n", string);
3638}
3639
3640/* C-client callback to handle quota */
3641static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
3642{
3643 struct vm_state *vms;
3644 char *mailbox = stream->mailbox, *user;
3645 char buf[1024] = "";
3646 unsigned long usage = 0, limit = 0;
3647
3648 while (pquota) {
3649 usage = pquota->usage;
3650 limit = pquota->limit;
3651 pquota = pquota->next;
3652 }
3653
3654 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)))) {
3655 ast_log(AST_LOG_ERROR, "No state found.\n");
3656 return;
3657 }
3658
3659 ast_debug(3, "User %s usage is %lu, limit is %lu\n", user, usage, limit);
3660
3661 vms->quota_usage = usage;
3662 vms->quota_limit = limit;
3663}
3664
3665static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len)
3666{
3667 char *start, *eol_pnt;
3668 int taglen;
3669
3671 return NULL;
3672
3673 taglen = strlen(tag) + 1;
3674 if (taglen < 1)
3675 return NULL;
3676
3677 if (!(start = strcasestr(header, tag)))
3678 return NULL;
3679
3680 /* Since we can be called multiple times we should clear our buffer */
3681 memset(buf, 0, len);
3682
3683 ast_copy_string(buf, start+taglen, len);
3684 if ((eol_pnt = strchr(buf,'\r')) || (eol_pnt = strchr(buf,'\n')))
3685 *eol_pnt = '\0';
3686 return buf;
3687}
3688
3689static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len)
3690{
3691 char *start, *eol_pnt, *quote;
3692
3694 return NULL;
3695
3696 if (!(start = strstr(mailbox, "/user=")))
3697 return NULL;
3698
3699 ast_copy_string(buf, start+6, len);
3700
3701 if (!(quote = strchr(buf, '"'))) {
3702 if ((eol_pnt = strchr(buf, '/')) || (eol_pnt = strchr(buf, '}'))) {
3703 *eol_pnt = '\0';
3704 }
3705 return buf;
3706 } else {
3707 if ((eol_pnt = strchr(quote + 1, '"'))) {
3708 *eol_pnt = '\0';
3709 }
3710 return quote + 1;
3711 }
3712}
3713
3714static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
3715{
3716 struct vm_state *vms_p;
3717
3718 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3719 if ((vms_p = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms_p->imapuser, vmu->imapuser) && !strcmp(vms_p->username, vmu->mailbox)) {
3720 return vms_p;
3721 }
3722 ast_debug(5, "Adding new vmstate for %s\n", vmu->imapuser);
3723 /* XXX: Is this correctly freed always? */
3724 if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
3725 return NULL;
3726 ast_copy_string(vms_p->imapuser, vmu->imapuser, sizeof(vms_p->imapuser));
3727 ast_copy_string(vms_p->imapfolder, vmu->imapfolder, sizeof(vms_p->imapfolder));
3728 ast_copy_string(vms_p->imapserver, vmu->imapserver, sizeof(vms_p->imapserver));
3729 ast_copy_string(vms_p->imapport, vmu->imapport, sizeof(vms_p->imapport));
3730 ast_copy_string(vms_p->imapflags, vmu->imapflags, sizeof(vms_p->imapflags));
3731 ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
3732 ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
3733 vms_p->mailstream = NIL; /* save for access from interactive entry point */
3734 vms_p->imapversion = vmu->imapversion;
3735 ast_debug(5, "Copied %s to %s\n", vmu->imapuser, vms_p->imapuser);
3736 vms_p->updated = 1;
3737 /* set mailbox to INBOX! */
3738 ast_copy_string(vms_p->curbox, mbox(vmu, 0), sizeof(vms_p->curbox));
3739 init_vm_state(vms_p);
3740 vmstate_insert(vms_p);
3741 return vms_p;
3742}
3743
3744static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive)
3745{
3746 struct vmstate *vlist = NULL;
3747
3748 if (interactive) {
3749 struct vm_state *vms;
3750 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3751 if ((vms = pthread_getspecific(ts_vmstate.key)) && !strcmp(vms->imapuser, user)) {
3752 return vms;
3753 }
3754 }
3755
3756 AST_LIST_LOCK(&vmstates);
3757 AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3758 if (!vlist->vms) {
3759 ast_debug(3, "error: vms is NULL for %s\n", user);
3760 continue;
3761 }
3762 if (vlist->vms->imapversion != imapversion) {
3763 continue;
3764 }
3765
3766 if (!strcmp(vlist->vms->imapuser, user) && (interactive == 2 || vlist->vms->interactive == interactive)) {
3767 AST_LIST_UNLOCK(&vmstates);
3768 return vlist->vms;
3769 }
3770 }
3771 AST_LIST_UNLOCK(&vmstates);
3772
3773 ast_debug(3, "%s not found in vmstates\n", user);
3774
3775 return NULL;
3776}
3777
3778static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
3779{
3780
3781 struct vmstate *vlist = NULL;
3782 const char *local_context = S_OR(context, "default");
3783
3784 if (interactive) {
3785 struct vm_state *vms;
3786 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
3787 if ((vms = pthread_getspecific(ts_vmstate.key)) &&
3788 !strcmp(vms->username,mailbox) && !strcmp(vms->context, local_context)) {
3789 return vms;
3790 }
3791 }
3792
3793 AST_LIST_LOCK(&vmstates);
3794 AST_LIST_TRAVERSE(&vmstates, vlist, list) {
3795 if (!vlist->vms) {
3796 ast_debug(3, "error: vms is NULL for %s\n", mailbox);
3797 continue;
3798 }
3799 if (vlist->vms->imapversion != imapversion) {
3800 continue;
3801 }
3802
3803 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);
3804
3805 if (!strcmp(vlist->vms->username, mailbox) && !strcmp(vlist->vms->context, local_context) && vlist->vms->interactive == interactive) {
3806 ast_debug(3, "Found it!\n");
3807 AST_LIST_UNLOCK(&vmstates);
3808 return vlist->vms;
3809 }
3810 }
3811 AST_LIST_UNLOCK(&vmstates);
3812
3813 ast_debug(3, "%s not found in vmstates\n", mailbox);
3814
3815 return NULL;
3816}
3817
3818static void vmstate_insert(struct vm_state *vms)
3819{
3820 struct vmstate *v;
3821 struct vm_state *altvms;
3822
3823 /* If interactive, it probably already exists, and we should
3824 use the one we already have since it is more up to date.
3825 We can compare the username to find the duplicate */
3826 if (vms->interactive == 1) {
3827 altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
3828 if (altvms) {
3829 ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3830 vms->newmessages = altvms->newmessages;
3831 vms->oldmessages = altvms->oldmessages;
3832 vms->vmArrayIndex = altvms->vmArrayIndex;
3833 /* XXX: no msgArray copying? */
3834 vms->lastmsg = altvms->lastmsg;
3835 vms->curmsg = altvms->curmsg;
3836 /* get a pointer to the persistent store */
3837 vms->persist_vms = altvms;
3838 /* Reuse the mailstream? */
3839#ifdef REALLY_FAST_EVEN_IF_IT_MEANS_RESOURCE_LEAKS
3840 vms->mailstream = altvms->mailstream;
3841#else
3842 vms->mailstream = NIL;
3843#endif
3844 }
3845 return;
3846 }
3847
3848 if (!(v = ast_calloc(1, sizeof(*v))))
3849 return;
3850
3851 v->vms = vms;
3852
3853 ast_debug(3, "Inserting vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3854
3855 AST_LIST_LOCK(&vmstates);
3856 AST_LIST_INSERT_TAIL(&vmstates, v, list);
3857 AST_LIST_UNLOCK(&vmstates);
3858}
3859
3860static void vmstate_delete(struct vm_state *vms)
3861{
3862 struct vmstate *vc = NULL;
3863 struct vm_state *altvms = NULL;
3864
3865 /* If interactive, we should copy pertinent info
3866 back to the persistent state (to make update immediate) */
3867 if (vms->interactive == 1 && (altvms = vms->persist_vms)) {
3868 ast_debug(3, "Duplicate mailbox %s, copying message info...\n", vms->username);
3869 altvms->newmessages = vms->newmessages;
3870 altvms->oldmessages = vms->oldmessages;
3871 altvms->updated = 1;
3872 vms->mailstream = mail_close(vms->mailstream);
3873
3874 /* Interactive states are not stored within the persistent list */
3875 return;
3876 }
3877
3878 ast_debug(3, "Removing vm_state for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3879
3880 AST_LIST_LOCK(&vmstates);
3881 AST_LIST_TRAVERSE_SAFE_BEGIN(&vmstates, vc, list) {
3882 if (vc->vms == vms) {
3884 break;
3885 }
3886 }
3888 AST_LIST_UNLOCK(&vmstates);
3889
3890 if (vc) {
3891 ast_mutex_destroy(&vc->vms->lock);
3892 ast_free(vc->vms->msgArray);
3893 vc->vms->msgArray = NULL;
3894 vc->vms->msg_array_max = 0;
3895 /* XXX: is no one supposed to free vms itself? */
3896 ast_free(vc);
3897 } else {
3898 ast_log(AST_LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n", vms->imapuser, vms->username);
3899 }
3900}
3901
3902static void set_update(MAILSTREAM * stream)
3903{
3904 struct vm_state *vms;
3905 char *mailbox = stream->mailbox, *user;
3906 char buf[1024] = "";
3907
3908 if (!(user = get_user_by_mailbox(mailbox, buf, sizeof(buf))) || !(vms = get_vm_state_by_imapuser(user, 0))) {
3909 if (user && DEBUG_ATLEAST(3))
3910 ast_log(AST_LOG_WARNING, "User %s mailbox not found for update.\n", user);
3911 return;
3912 }
3913
3914 ast_debug(3, "User %s mailbox set for update.\n", user);
3915
3916 vms->updated = 1; /* Set updated flag since mailbox changed */
3917}
3918
3919static void init_vm_state(struct vm_state *vms)
3920{
3921 vms->msg_array_max = VMSTATE_MAX_MSG_ARRAY;
3922 vms->msgArray = ast_calloc(vms->msg_array_max, sizeof(long));
3923 if (!vms->msgArray) {
3924 /* Out of mem? This can't be good. */
3925 vms->msg_array_max = 0;
3926 }
3927 vms->vmArrayIndex = 0;
3928 ast_mutex_init(&vms->lock);
3929}
3930
3931static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro)
3932{
3933 char *body_content;
3934 char *body_decoded;
3935 char *fn = is_intro ? vms->introfn : vms->fn;
3936 unsigned long len = 0;
3937 unsigned long newlen = 0;
3938 char filename[256];
3939
3940 if (!body || body == NIL)
3941 return -1;
3942
3943 ast_mutex_lock(&vms->lock);
3944 body_content = mail_fetchbody(vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
3945 ast_mutex_unlock(&vms->lock);
3948 "Msgno %ld, section %s. The body's content size %ld is huge (max %ld). User:%s, mailbox %s\n",
3949 vms->msgArray[vms->curmsg], section, len, MAX_MAIL_BODY_CONTENT_SIZE, vms->imapuser, vms->username);
3950 return -1;
3951 }
3952 if (body_content != NIL && len) {
3953 snprintf(filename, sizeof(filename), "%s.%s", fn, format);
3954 /* ast_debug(1, body_content); */
3955 body_decoded = rfc822_base64((unsigned char *) body_content, len, &newlen);
3956 /* If the body of the file is empty, return an error */
3957 if (!newlen || !body_decoded) {
3958 return -1;
3959 }
3960 write_file(filename, (char *) body_decoded, newlen);
3961 } else {
3962 ast_debug(5, "Body of message is NULL.\n");
3963 return -1;
3964 }
3965 return 0;
3966}
3967
3968/*!
3969 * \brief Get delimiter via mm_list callback
3970 * \param vms The voicemail state object
3971 * \param stream
3972 *
3973 * Determines the delimiter character that is used by the underlying IMAP based mail store.
3974 */
3975/* MUTEX should already be held */
3976static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream) {
3977 char tmp[50];
3978 snprintf(tmp, sizeof(tmp), "{%s}", S_OR(vms->imapserver, imapserver));
3979 mail_list(stream, tmp, "*");
3980}
3981
3982/*!
3983 * \brief Check Quota for user
3984 * \param vms a pointer to a vm_state struct, will use the mailstream property of this.
3985 * \param mailbox the mailbox to check the quota for.
3986 *
3987 * Calls imap_getquotaroot, which will populate its results into the vm_state vms input structure.
3988 */
3989static void check_quota(struct vm_state *vms, char *mailbox) {
3990 ast_mutex_lock(&vms->lock);
3991 mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
3992 ast_debug(3, "Mailbox name set to: %s, about to check quotas\n", mailbox);
3993 if (vms && vms->mailstream != NULL) {
3994 imap_getquotaroot(vms->mailstream, mailbox);
3995 } else {
3996 ast_log(AST_LOG_WARNING, "Mailstream not available for mailbox: %s\n", mailbox);
3997 }
3998 ast_mutex_unlock(&vms->lock);
3999}
4000
4001#endif /* IMAP_STORAGE */
4002
4003/*! \brief Lock file path
4004 * only return failure if ast_lock_path returns 'timeout',
4005 * not if the path does not exist or any other reason
4006 */
4007static int vm_lock_path(const char *path)
4008{
4009 switch (ast_lock_path(path)) {
4010 case AST_LOCK_TIMEOUT:
4011 return -1;
4012 default:
4013 return 0;
4014 }
4015}
4016
4017#define MSG_ID_LEN 256
4018
4019/* Used to attach a unique identifier to an msg_id */
4021
4022/*!
4023 * \brief Sets the destination string to a uniquely identifying msg_id string
4024 * \param dst pointer to a character buffer that should contain MSG_ID_LEN characters.
4025 */
4026static void generate_msg_id(char *dst);
4027
4028#ifdef ODBC_STORAGE
4029
4030/*!
4031 * \internal
4032 * \brief Create a buffer containing the SQL statement with the table name inserted.
4033 *
4034 * \param __sql_fmt The SQL statement with a single '%s' where the table name should be inserted.
4035 *
4036 * \note The buffer is allocated on the stack and should not be freed.
4037 *
4038 * \return A pointer to a buffer containing the SQL statement with the table name inserted.
4039 */
4040#define MAKE_SQL_PTRA(__sql_fmt) \
4041({ \
4042 /* The NULL terminator is included in odbc_table_len. */ \
4043 char *__sql = ast_alloca(strlen(__sql_fmt) + odbc_table_len); \
4044 sprintf(__sql, __sql_fmt, odbc_table); /* Safe */ \
4045 __sql; \
4046})
4047
4048/*!
4049 * \internal
4050 * \brief Create a buffer containing the SQL statement with the table name inserted twice.
4051 *
4052 * \param __sql_fmt The SQL statement with two '%s' where the table name should be inserted.
4053 *
4054 * \note The buffer is allocated on the stack and should not be freed.
4055 *
4056 * \return A pointer to a buffer containing the SQL statement with the table name inserted.
4057 */
4058#define MAKE_SQL_PTRA2(__sql_fmt) \
4059({ \
4060 /* The NULL terminator is included in odbc_table_len. */ \
4061 char *__sql = ast_alloca(strlen(__sql_fmt) + (odbc_table_len * 2)); \
4062 sprintf(__sql, __sql_fmt, odbc_table, odbc_table); /* Safe */ \
4063 __sql; \
4064})
4065
4066struct generic_prepare_struct {
4067 char *sql;
4068 int argc;
4069 char **argv;
4070};
4071
4072static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
4073{
4074 struct generic_prepare_struct *gps = data;
4075 int res, i;
4076 SQLHSTMT stmt;
4077
4078 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4079 if (!SQL_SUCCEEDED(res)) {
4080 ast_log(AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
4081 return NULL;
4082 }
4083 res = ast_odbc_prepare(obj, stmt, gps->sql);
4084 if (!SQL_SUCCEEDED(res)) {
4085 ast_log(AST_LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
4086 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4087 return NULL;
4088 }
4089 for (i = 0; i < gps->argc; i++)
4090 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
4091
4092 return stmt;
4093}
4094
4095static void odbc_update_msg_id(char *dir, int msg_num, char *msg_id)
4096{
4097 SQLHSTMT stmt;
4098 char *sql = MAKE_SQL_PTRA("UPDATE %s SET msg_id=? WHERE dir=? AND msgnum=?");
4099 struct odbc_obj *obj;
4100 char msg_num_str[20];
4101 char *argv[] = { msg_id, dir, msg_num_str };
4102 struct generic_prepare_struct gps = { .sql = sql, .argc = 3, .argv = argv };
4103 SCOPE_ENTER(3, "dir: %s msg_num: %d msg_id: %s\n", dir, msg_num, msg_id);
4104
4105 obj = ast_odbc_request_obj(odbc_database, 0);
4106 if (!obj) {
4107 SCOPE_EXIT_LOG_RTN(LOG_WARNING, "Unable to update message ID for message %d in %s\n", msg_num, dir);
4108 }
4109
4110 snprintf(msg_num_str, sizeof(msg_num_str), "%d", msg_num);
4112 if (!stmt) {
4113 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4114 } else {
4115 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4116 }
4119}
4120
4121#define AUDIO_ON_DISK_MAGIC "AUDMAGIC"
4122#define AUDIO_ON_DISK_MAGIC_LEN 8
4123
4124static void odbc_update_set_audmagic(char *dir, int msg_num)
4125{
4126 SQLHSTMT stmt;
4127 char *sql = MAKE_SQL_PTRA("UPDATE %s SET recording=? WHERE dir=? AND msgnum=?");
4128 struct odbc_obj *obj;
4129 SQLLEN datalen = AUDIO_ON_DISK_MAGIC_LEN;
4130 SQLLEN indlen = datalen;
4131 int res;
4132 char msg_num_str[20];
4133 SCOPE_ENTER(3, "dir: %s msg_num: %d\n", dir, msg_num);
4134
4135 obj = ast_odbc_request_obj(odbc_database, 0);
4136 if (!obj) {
4137 SCOPE_EXIT_LOG_RTN(LOG_WARNING, "Unable to request obj for message %d in %s\n", msg_num, dir);
4138 }
4139
4140 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4141 if (!SQL_SUCCEEDED(res)) {
4143 SCOPE_EXIT_LOG_RTN(LOG_WARNING, "Unable to allocate stmt for message %d in %s\n", msg_num, dir);
4144 }
4145
4146 snprintf(msg_num_str, sizeof(msg_num_str), "%d", msg_num);
4147
4148 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY,
4149 datalen, 0, (void *) AUDIO_ON_DISK_MAGIC,
4150 datalen, &indlen);
4151
4152 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
4153 strlen(dir), 0, (void *) dir, 0, NULL);
4154
4155 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR,
4156 strlen(msg_num_str), 0, (void *) msg_num_str, 0, NULL);
4157
4158 res = ast_odbc_execute_sql(obj, stmt, sql);
4159 if (!SQL_SUCCEEDED(res)) {
4160 ast_log(LOG_WARNING, "Unable to execute stmt for message %d in %s\n", msg_num, dir);
4161 ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute");
4162 }
4163 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4164 stmt = NULL;
4165
4167 SCOPE_EXIT_RTN("Done\n");
4168}
4169
4170static int odbc_store_message(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum);
4171
4172/*!
4173 * \brief Retrieves a file from an ODBC data store.
4174 * \param dir the path to the file to be retrieved.
4175 * \param msgnum the message number, such as within a mailbox folder.
4176 *
4177 * This method is used by the RETRIEVE macro when mailboxes are stored in an ODBC back end.
4178 * 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.
4179 *
4180 * The file is looked up by invoking a SQL on the odbc_table (default 'voicemessages') using the dir and msgnum input parameters.
4181 * The output is the message information file with the name msgnum and the extension .txt
4182 * and the message file with the extension of its format, in the directory with base file name of the msgnum.
4183 *
4184 * \return 0 on success, -1 on error.
4185 */
4186static int odbc_retrieve_message(char *dir, int msgnum)
4187{
4188 int x = 0;
4189 int res;
4190 int fd = -1;
4191 size_t fdlen = 0;
4192 void *fdm = MAP_FAILED;
4193 SQLSMALLINT colcount = 0;
4194 SQLHSTMT stmt;
4195 char *sql = MAKE_SQL_PTRA("SELECT * FROM %s WHERE dir=? AND msgnum=?");
4196 char fmt[80] = "";
4197 char *c;
4198 char coltitle[256];
4199 SQLSMALLINT collen;
4200 SQLSMALLINT datatype;
4201 SQLSMALLINT decimaldigits;
4202 SQLSMALLINT nullable;
4203 SQLULEN colsize;
4204 SQLLEN colsize2;
4205 FILE *f = NULL;
4206 char rowdata[80];
4207 char *fn = MAKE_FILE_PTRA(dir, msgnum);
4208 char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
4209 char msgnums[80];
4210 char *mailboxuser = NULL;
4211 char *mailboxcontext = NULL;
4212 char msg_id[MSG_ID_LEN] = "";
4213 char *argv[] = { dir, msgnums };
4214 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4215 struct odbc_obj *obj;
4216 int storage_conversion_to_disk = 0;
4217 int storage_conversion_to_odbc = 0;
4218 SCOPE_ENTER(3, "dir: %s msgnum: %d msgtype: %s\n", dir, msgnum, msgnum < 0 ? "Greeting" : "Message");
4219
4220 obj = ast_odbc_request_obj(odbc_database, 0);
4221 if (!obj) {
4222 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4223 }
4224
4225 ast_copy_string(fmt, vmfmts, sizeof(fmt));
4226 c = strchr(fmt, '|');
4227 if (c)
4228 *c = '\0';
4229 if (!strcasecmp(fmt, "wav49"))
4230 strcpy(fmt, "WAV");
4231
4232 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4233
4234 ast_trace(-1, "Opening '%s' for writing\n", full_fn);
4235 if (!(f = fopen(full_fn, "w+"))) {
4236 ast_log(AST_LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
4237 goto bail;
4238 }
4239
4240 sprintf(full_fn, "%s.%s", fn, fmt); /* Safe. We're just replacing the file exten. */
4241
4243 if (!stmt) {
4244 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4245 goto bail;
4246 }
4247
4248 res = SQLFetch(stmt);
4249 if (!SQL_SUCCEEDED(res)) {
4250 if (res != SQL_NO_DATA) {
4251 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4252 }
4253 goto bail_with_handle;
4254 }
4255
4256 res = SQLNumResultCols(stmt, &colcount);
4257 if (!SQL_SUCCEEDED(res)) {
4258 ast_log(AST_LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
4259 goto bail_with_handle;
4260 }
4261
4262 fprintf(f, "[message]\n");
4263 for (x = 0; x < colcount; x++) {
4264 rowdata[0] = '\0';
4265 colsize = 0;
4266 collen = sizeof(coltitle);
4267 res = SQLDescribeCol(stmt, x + 1, (unsigned char *) coltitle, sizeof(coltitle), &collen,
4268 &datatype, &colsize, &decimaldigits, &nullable);
4269 if (!SQL_SUCCEEDED(res)) {
4270 ast_log(AST_LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
4271 goto bail_with_handle;
4272 }
4273
4274 if (!strcasecmp(coltitle, "recording")) {
4275 off_t offset;
4276 char tmp[1] = "";
4277
4278 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
4279 fdlen = colsize2;
4280 ast_trace(-1, "Audio size: %ld\n", colsize2);
4281 if (colsize2 == AUDIO_ON_DISK_MAGIC_LEN) {
4282 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, AUDIO_ON_DISK_MAGIC_LEN, NULL);
4283 if (memcmp(rowdata, AUDIO_ON_DISK_MAGIC, AUDIO_ON_DISK_MAGIC_LEN) != 0) {
4284 ast_log(AST_LOG_WARNING, "Invalid audio magic number '0x%02X%02X%02X%02X%02X%02X%02X%02X' for '%s'\n",
4285 rowdata[0], rowdata[1], rowdata[2], rowdata[3], rowdata[4], rowdata[5], rowdata[6],
4286 rowdata[7], full_fn);
4287 goto bail_with_handle;
4288 }
4289 ast_trace(-1, "Audio is stored on disk. No need to write '%s'\n", full_fn);
4291 storage_conversion_to_odbc = 1;
4292 }
4293
4294 continue;
4295 }
4296
4297 ast_trace(-1, "Opening '%s' for writing\n", full_fn);
4298 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, VOICEMAIL_FILE_MODE);
4299 if (fd < 0) {
4300 ast_log(AST_LOG_WARNING, "Failed to open '%s' for writing: %s\n", full_fn, strerror(errno));
4301 goto bail_with_handle;
4302 }
4304 storage_conversion_to_disk = 1;
4305 }
4306
4307 lseek(fd, fdlen - 1, SEEK_SET);
4308 if (write(fd, tmp, 1) != 1) {
4309 close(fd);
4310 fd = -1;
4311 continue;
4312 }
4313 /* Read out in small chunks */
4314 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
4315 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
4316 ast_log(AST_LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
4317 goto bail_with_handle;
4318 }
4319 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
4320 munmap(fdm, CHUNKSIZE);
4321 if (!SQL_SUCCEEDED(res)) {
4322 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4323 unlink(full_fn);
4324 goto bail_with_handle;
4325 }
4326 }
4327 if (truncate(full_fn, fdlen) < 0) {
4328 ast_log(LOG_WARNING, "Unable to truncate '%s': %s\n", full_fn, strerror(errno));
4329 }
4330 ast_trace(-1, "Wrote %d bytes to '%s'\n", (int)fdlen, full_fn);
4331 } else {
4332 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4333 if (res == SQL_NULL_DATA && !strcasecmp(coltitle, "msg_id")) {
4334 /*
4335 * Generate msg_id if there wasn't one, but don't store it until we're
4336 * done with this connection.
4337 */
4338 generate_msg_id(msg_id);
4339 ast_trace(-1, "msg_id was NULL. Generating new one: %s\n", msg_id);
4340 snprintf(rowdata, sizeof(rowdata), "%s", msg_id);
4341 } else if (!strcasecmp(coltitle, "mailboxuser")) {
4342 mailboxuser = ast_strdupa(rowdata);
4343 } else if (!strcasecmp(coltitle, "mailboxcontext")) {
4344 mailboxcontext = ast_strdupa(rowdata);
4345 } else if (res == SQL_NULL_DATA && !strcasecmp(coltitle, "category")) {
4346 /* Ignore null column value for category */
4347 ast_trace(-1, "Ignoring null category column in ODBC voicemail retrieve_file.\n");
4348 continue;
4349 } else if (!SQL_SUCCEEDED(res)) {
4350 ast_log(AST_LOG_WARNING, "SQL Get Data error! coltitle=%s\n[%s]\n\n", coltitle, sql);
4351 goto bail_with_handle;
4352 }
4353 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir")) {
4354 fprintf(f, "%s=%s\n", coltitle, rowdata);
4355 }
4356 }
4357 }
4358
4359bail_with_handle:
4360 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4361
4362bail:
4363 if (f)
4364 fclose(f);
4365 if (fd > -1)
4366 close(fd);
4367
4369
4370 /* If res_odbc is configured to only allow a single database connection, we
4371 will deadlock if we try to do this before releasing the connection we
4372 were just using. */
4373 if (!ast_strlen_zero(msg_id)) {
4374 odbc_update_msg_id(dir, msgnum, msg_id);
4375 }
4376
4377 if (SQL_SUCCEEDED(res)) {
4378 if (storage_conversion_to_disk) {
4379 /*
4380 * If we're currently storing audio on disk but there was pre-existing
4381 * audio in the database, we need to update the database to set the
4382 * 'recording' column to AUDIO_ON_DISK_MAGIC.
4383 */
4384 SCOPE_CALL(-1, odbc_update_set_audmagic, dir, msgnum);
4385 }
4386 if (storage_conversion_to_odbc) {
4387 /*
4388 * If we're currently storing audio in the database but there was
4389 * pre-existing audio on disk, we need to add the audio back
4390 * into the database overwriting the AUDIO_ON_DISK_MAGIC
4391 * magic number.
4392 */
4393 SCOPE_CALL(-1, odbc_store_message, dir, mailboxuser, mailboxcontext, msgnum);
4394 }
4395 }
4396
4397 SCOPE_EXIT_RTN_VALUE(x - 1, "Done. msg_id: %s RC: %d\n", msg_id, x - 1);
4398}
4399
4400/*!
4401 * \brief Determines the highest message number in use for a given user and mailbox folder.
4402 * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
4403 *
4404 * This method is used when mailboxes are stored in an ODBC back end.
4405 * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
4406 *
4407 * \return the value of zero or greater to indicate the last message index in use, -1 to indicate none.
4408 */
4409static int odbc_last_message_index(char *dir)
4410{
4411 int x = -1;
4412 int res;
4413 SQLHSTMT stmt;
4414 char *sql = MAKE_SQL_PTRA("SELECT msgnum FROM %s WHERE dir=? order by msgnum desc");
4415 char rowdata[20];
4416 char *argv[] = { dir };
4417 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
4418 struct odbc_obj *obj;
4419 SCOPE_ENTER(3, "dir: %s\n", dir);
4420
4421 obj = ast_odbc_request_obj(odbc_database, 0);
4422 if (!obj) {
4423 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4424 }
4425
4427 if (!stmt) {
4428 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4429 goto bail;
4430 }
4431
4432 res = SQLFetch(stmt);
4433 if (!SQL_SUCCEEDED(res)) {
4434 if (res == SQL_NO_DATA) {
4435 ast_trace(-1, "Directory '%s' has no messages and therefore no index was retrieved.\n", dir);
4436 } else {
4437 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4438 }
4439 goto bail_with_handle;
4440 }
4441
4442 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4443 if (!SQL_SUCCEEDED(res)) {
4444 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4445 goto bail_with_handle;
4446 }
4447
4448 if (sscanf(rowdata, "%30d", &x) != 1) {
4449 ast_log(AST_LOG_WARNING, "Failed to read message index!\n");
4450 }
4451
4452bail_with_handle:
4453 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4454
4455bail:
4457
4458 SCOPE_EXIT_RTN_VALUE(x, "Done. Last message index: %d\n", x);
4459}
4460
4461/*!
4462 * \brief Determines if the specified message exists.
4463 * \param dir the folder the mailbox folder to look for messages.
4464 * \param msgnum the message index to query for.
4465 *
4466 * This method is used when mailboxes are stored in an ODBC back end.
4467 *
4468 * \return greater than zero if the message exists, zero when the message does not exist or on error.
4469 */
4470static int odbc_message_exists(char *dir, int msgnum)
4471{
4472 int x = 0;
4473 int res;
4474 SQLHSTMT stmt;
4475 char *sql = MAKE_SQL_PTRA("SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?");
4476 char rowdata[20];
4477 char msgnums[20];
4478 char *argv[] = { dir, msgnums };
4479 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4480 struct odbc_obj *obj;
4481 SCOPE_ENTER(3, "dir: %s msgnum: %d\n", dir, msgnum);
4482
4483 obj = ast_odbc_request_obj(odbc_database, 0);
4484 if (!obj) {
4485 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4486 }
4487
4488 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4490 if (!stmt) {
4491 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4492 goto bail;
4493 }
4494
4495 res = SQLFetch(stmt);
4496 if (!SQL_SUCCEEDED(res)) {
4497 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4498 goto bail_with_handle;
4499 }
4500
4501 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4502 if (!SQL_SUCCEEDED(res)) {
4503 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4504 goto bail_with_handle;
4505 }
4506
4507 if (sscanf(rowdata, "%30d", &x) != 1) {
4508 ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
4509 }
4510
4511bail_with_handle:
4512 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4513
4514bail:
4516 SCOPE_EXIT_RTN_VALUE(x, "Done. Msg %s\n", x ? "exists" : "does not exist");
4517}
4518
4519/*!
4520 * \brief returns the number of messages found.
4521 * \param vmu
4522 * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
4523 *
4524 * This method is used when mailboxes are stored in an ODBC back end.
4525 *
4526 * \return The count of messages being zero or more, less than zero on error.
4527 */
4528static int odbc_count_messages(struct ast_vm_user *vmu, char *dir)
4529{
4530 int x = -1;
4531 int res;
4532 SQLHSTMT stmt;
4533 char *sql = MAKE_SQL_PTRA("SELECT COUNT(*) FROM %s WHERE dir=?");
4534 char rowdata[20];
4535 char *argv[] = { dir };
4536 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
4537 struct odbc_obj *obj;
4538 SCOPE_ENTER(3, "dir: %s\n", dir);
4539
4540 obj = ast_odbc_request_obj(odbc_database, 0);
4541 if (!obj) {
4542 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4543 }
4544
4546 if (!stmt) {
4547 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4548 goto bail;
4549 }
4550
4551 res = SQLFetch(stmt);
4552 if (!SQL_SUCCEEDED(res)) {
4553 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
4554 goto bail_with_handle;
4555 }
4556
4557 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
4558 if (!SQL_SUCCEEDED(res)) {
4559 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
4560 goto bail_with_handle;
4561 }
4562
4563 if (sscanf(rowdata, "%30d", &x) != 1) {
4564 ast_log(AST_LOG_WARNING, "Failed to read message count!\n");
4565 }
4566
4567bail_with_handle:
4568 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4569
4570bail:
4572 SCOPE_EXIT_RTN_VALUE(x, "Done. Count %d\n", x);
4573}
4574
4575/*!
4576 * \brief Deletes a message from the mailbox folder.
4577 * \param sdir The mailbox folder to work in.
4578 * \param smsg The message index to be deleted.
4579 *
4580 * This method is used when mailboxes are stored in an ODBC back end.
4581 * The specified message is directly deleted from the database 'voicemessages' table.
4582 */
4583#define DELETE_SQL_FMT "DELETE FROM %s WHERE dir=? AND msgnum=?"
4584static void odbc_delete_message(const char *sdir, int smsg)
4585{
4586 SQLHSTMT stmt;
4587 char *sql = MAKE_SQL_PTRA(DELETE_SQL_FMT);
4588 char msgnums[20];
4589 char *argv[] = { NULL, msgnums };
4590 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
4591 struct odbc_obj *obj;
4592 SCOPE_ENTER(3, "sdir: %s smsg: %d\n", sdir, smsg);
4593
4594 obj = ast_odbc_request_obj(odbc_database, 0);
4595 if (!obj) {
4596 SCOPE_EXIT_LOG_RTN(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4597 }
4598
4599 argv[0] = ast_strdupa(sdir);
4600
4601 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4603 if (!stmt) {
4604 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4605 } else {
4606 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4607 }
4609
4611 char *src_fn = MAKE_FILE_PTRA(sdir, smsg);
4612 ast_trace(-1, "Audio stored on disk. Deleting '%s'\n", src_fn);
4613 ast_filedelete(src_fn, NULL);
4614 }
4615
4616 SCOPE_EXIT_RTN("Done\n");
4617}
4618
4619/*!
4620 * \brief Copies a voicemail from one mailbox to another.
4621 * \param sdir the folder for which to look for the message to be copied.
4622 * \param smsg the index of the message to be copied.
4623 * \param ddir the destination folder to copy the message into.
4624 * \param dmsg the index to be used for the copied message.
4625 * \param dmailboxuser The user who owns the mailbox tha contains the destination folder.
4626 * \param dmailboxcontext The context for the destination user.
4627 *
4628 * This method is used for the COPY macro when mailboxes are stored in an ODBC back end.
4629 */
4630#define COPY_SQL_FMT "INSERT INTO %s (dir, msgnum, msg_id, context, callerid, origtime, " \
4631 "duration, recording, flag, mailboxuser, mailboxcontext) " \
4632 "SELECT ?,?,msg_id,context,callerid,origtime,duration,recording,flag,?,? " \
4633 "FROM %s WHERE dir=? AND msgnum=?"
4634static void odbc_copy_message(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
4635{
4636 SQLHSTMT stmt;
4637 char *sql = MAKE_SQL_PTRA2(COPY_SQL_FMT);
4638 char msgnums[20];
4639 char msgnumd[20];
4640 struct odbc_obj *obj;
4641 char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
4642 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
4643 SCOPE_ENTER(3, "sdir: %s smsg: %d duser: %s dcontext: %s ddir: %s dmsg: %d\n",
4644 sdir, smsg, dmailboxuser, dmailboxcontext, ddir, dmsg);
4645
4646 SCOPE_CALL(-1, odbc_delete_message, ddir, dmsg);
4647
4648 obj = ast_odbc_request_obj(odbc_database, 0);
4649 if (!obj) {
4650 SCOPE_EXIT_LOG_RTN(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4651 }
4652
4653 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4654 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
4655
4657 if (!stmt)
4658 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
4659 else
4660 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4662
4664 char *src_fn = MAKE_FILE_PTRA(sdir, smsg);
4665 char *dst_fn = MAKE_FILE_PTRA(ddir, dmsg);
4666
4667 ast_trace(-1, "Audio stored on disk. Copying '%s' to '%s'\n", src_fn, dst_fn);
4668 ast_filecopy(src_fn, dst_fn, NULL);
4669 }
4670
4671 SCOPE_EXIT_RTN("Done\n");
4672}
4673#undef COPY_SQL_FMT
4674
4675struct insert_data {
4676 const char *dir;
4677 const char *msgnums;
4678 void *data;
4679 SQLLEN datalen;
4680 SQLLEN indlen;
4681 const char *context;
4682 const char *callerid;
4683 const char *origtime;
4684 const char *duration;
4685 const char *mailboxuser;
4686 const char *mailboxcontext;
4687 const char *category;
4688 const char *flag;
4689 const char *msg_id;
4690};
4691
4692#define STORE_SQL_FMT_CAT "INSERT INTO %s (dir, msgnum, recording, context, callerid, " \
4693 "origtime, duration, mailboxuser, mailboxcontext, flag, msg_id, category) " \
4694 "VALUES (?,?,?,?,?,?,?,?,?,?,?,?)"
4695#define STORE_SQL_FMT "INSERT INTO %s (dir, msgnum, recording, context, callerid, "\
4696 "origtime, duration, mailboxuser, mailboxcontext, flag, msg_id) "\
4697 "VALUES (?,?,?,?,?,?,?,?,?,?,?)"
4698
4699static SQLHSTMT odbc_insert_data_cb(struct odbc_obj *obj, void *vdata)
4700{
4701 struct insert_data *data = vdata;
4702 char *insert_sql;
4703 char *delete_sql;
4704 int res;
4705 SQLHSTMT stmt;
4706 SCOPE_ENTER(3, "dir: %s msgnums: %s msg_id: %s\n", data->dir, data->msgnums,
4707 data->msg_id);
4708
4709 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4710 if (!SQL_SUCCEEDED(res)) {
4711 SCOPE_EXIT_LOG_RTN_VALUE(NULL, AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
4712 }
4713
4714 /* Delete any existing row with the same dir and msgnum */
4715 delete_sql = MAKE_SQL_PTRA(DELETE_SQL_FMT);
4716 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
4717 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
4718 res = ast_odbc_execute_sql(obj, stmt, delete_sql);
4719 if (!SQL_SUCCEEDED(res)) {
4720 ast_trace(-1, "There wasn't an existing row. Good.\n");
4721 } else {
4722 ast_trace(-1, "There WAS an existing row. This is OK if we're replacing a message.\n");
4723 }
4724 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4725 stmt = NULL;
4726
4727 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
4728 if (!SQL_SUCCEEDED(res)) {
4729 SCOPE_EXIT_LOG_RTN_VALUE(NULL, AST_LOG_WARNING, "SQL Alloc Handle failed!\n");
4730 }
4731
4732 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->dir), 0, (void *) data->dir, 0, NULL);
4733 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msgnums), 0, (void *) data->msgnums, 0, NULL);
4734 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, data->datalen, 0, (void *) data->data, data->datalen, &data->indlen);
4735 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->context), 0, (void *) data->context, 0, NULL);
4736 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->callerid), 0, (void *) data->callerid, 0, NULL);
4737 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->origtime), 0, (void *) data->origtime, 0, NULL);
4738 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->duration), 0, (void *) data->duration, 0, NULL);
4739 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxuser), 0, (void *) data->mailboxuser, 0, NULL);
4740 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->mailboxcontext), 0, (void *) data->mailboxcontext, 0, NULL);
4741 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->flag), 0, (void *) data->flag, 0, NULL);
4742 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->msg_id), 0, (void *) data->msg_id, 0, NULL);
4743 if (!ast_strlen_zero(data->category)) {
4744 insert_sql = MAKE_SQL_PTRA(STORE_SQL_FMT_CAT);
4745 SQLBindParameter(stmt, 12, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(data->category), 0, (void *) data->category, 0, NULL);
4746 } else {
4747 insert_sql = MAKE_SQL_PTRA(STORE_SQL_FMT);
4748 }
4749 res = ast_odbc_execute_sql(obj, stmt, insert_sql);
4750 if (!SQL_SUCCEEDED(res)) {
4751 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n", insert_sql);
4752 ast_odbc_print_errors(SQL_HANDLE_STMT, stmt, "SQL Execute");
4753 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4754 stmt = NULL;
4755 }
4756
4757 SCOPE_EXIT_RTN_VALUE(stmt, "%s\n", stmt ? "Success" : "Failed");
4758}
4759
4760/*!
4761 * \brief Stores a voicemail into the database.
4762 * \param dir the folder the mailbox folder to store the message.
4763 * \param mailboxuser the user owning the mailbox folder.
4764 * \param mailboxcontext
4765 * \param msgnum the message index for the message to be stored.
4766 *
4767 * This method is used when mailboxes are stored in an ODBC back end.
4768 * The message sound file and information file is looked up on the file system.
4769 * A SQL query is invoked to store the message into the (MySQL) database.
4770 *
4771 * \return the zero on success -1 on error.
4772 */
4773static int odbc_store_message(const char *dir, const char *mailboxuser, const char *mailboxcontext, int msgnum)
4774{
4775 int res = 0;
4776 int fd = -1;
4777 void *fdm = MAP_FAILED;
4778 off_t fdlen = -1;
4779 SQLHSTMT stmt;
4780 char msgnums[20];
4781 char *fn = MAKE_FILE_PTRA(dir, msgnum);
4782 char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
4783 char fmt[80]="";
4784 char *c;
4785 struct ast_config *cfg = NULL;
4786 struct odbc_obj *obj;
4787 struct insert_data idata = { .msgnums = msgnums, .dir = dir, .mailboxuser = mailboxuser, .mailboxcontext = mailboxcontext,
4788 .context = "", .callerid = "", .origtime = "", .duration = "", .category = "", .flag = "", .msg_id = "" };
4789 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
4790 SCOPE_ENTER(3, "dir: %s user: %s context: %s msgnum: %d msgtype: %s\n",
4791 dir, mailboxuser, mailboxcontext, msgnum, msgnum < 0 ? "Greeting" : "Message");
4792
4793 obj = ast_odbc_request_obj(odbc_database, 0);
4794 if (!obj) {
4795 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4796 }
4797
4798 do {
4799 ast_copy_string(fmt, vmfmts, sizeof(fmt));
4800 c = strchr(fmt, '|');
4801 if (c)
4802 *c = '\0';
4803 if (!strcasecmp(fmt, "wav49"))
4804 strcpy(fmt, "WAV");
4805
4806 ast_trace(-1, "Formats: %s Using format: '%s'\n", vmfmts, fmt);
4807 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
4808
4809 ast_trace(-1, "Base path: '%s'\n", fn);
4810 ast_trace(-1, "Opening '%s'\n", full_fn);
4811 cfg = ast_config_load(full_fn, config_flags);
4812 if (!valid_config(cfg)) {
4813 if (msgnum < 0) {
4814 ast_trace(-1, "No information file found for '%s'. This is a greeting so this is OK.\n", full_fn);
4815 } else {
4816 ast_log(AST_LOG_WARNING, "Failed to open '%s'\n", full_fn);
4817 res = -1;
4818 break;
4819 }
4820 }
4821
4822 sprintf(full_fn, "%s.%s", fn, fmt); /* Safe */
4824 ast_trace(-1, "Audio stored on disk. No need to open '%s'\n", full_fn);
4825 } else {
4826 ast_trace(-1, "Opening '%s'\n", full_fn);
4827 fd = open(full_fn, O_RDWR);
4828 if (fd < 0) {
4829 ast_log(AST_LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
4830 res = -1;
4831 break;
4832 }
4833 }
4834
4835 if (valid_config(cfg)) {
4836 ast_trace(-1, "Using information file '%s'\n", fn);
4837 if (!(idata.context = ast_variable_retrieve(cfg, "message", "context"))) {
4838 idata.context = "";
4839 }
4840 if (!(idata.callerid = ast_variable_retrieve(cfg, "message", "callerid"))) {
4841 idata.callerid = "";
4842 }
4843 if (!(idata.origtime = ast_variable_retrieve(cfg, "message", "origtime"))) {
4844 idata.origtime = "";
4845 }
4846 if (!(idata.duration = ast_variable_retrieve(cfg, "message", "duration"))) {
4847 idata.duration = "";
4848 }
4849 if (!(idata.category = ast_variable_retrieve(cfg, "message", "category"))) {
4850 idata.category = "";
4851 }
4852 if (!(idata.flag = ast_variable_retrieve(cfg, "message", "flag"))) {
4853 idata.flag = "";
4854 }
4855 if (!(idata.msg_id = ast_variable_retrieve(cfg, "message", "msg_id"))) {
4856 idata.msg_id = "";
4857 }
4858 }
4859
4860 if (fd < 0) {
4861 ast_trace(-1, "Audio stored on disk. Not reading sound file '%s' but setting magic number.\n", full_fn);
4862 idata.data = AUDIO_ON_DISK_MAGIC;
4863 idata.datalen = idata.indlen = AUDIO_ON_DISK_MAGIC_LEN;
4864 } else {
4865 ast_trace(-1, "Reading sound file '%s'\n", full_fn);
4866 fdlen = lseek(fd, 0, SEEK_END);
4867 if (fdlen < 0 || lseek(fd, 0, SEEK_SET) < 0) {
4868 ast_log(AST_LOG_WARNING, "Failed to process sound file '%s': %s\n", full_fn, strerror(errno));
4869 res = -1;
4870 break;
4871 }
4872 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
4873 if (fdm == MAP_FAILED) {
4874 ast_log(AST_LOG_WARNING, "Memory map failed for sound file '%s'!\n", full_fn);
4875 res = -1;
4876 break;
4877 }
4878 idata.data = fdm;
4879 idata.datalen = idata.indlen = fdlen;
4880 }
4881
4882 if (ast_strlen_zero(idata.origtime)) {
4883 idata.origtime = "0";
4884 }
4885
4886 if (ast_strlen_zero(idata.duration)) {
4887 idata.duration = "0";
4888 }
4889
4890 if ((stmt = ast_odbc_direct_execute(obj, odbc_insert_data_cb, &idata))) {
4891 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4892 } else {
4893 res = -1;
4894 }
4895 } while (0);
4896
4898
4899 if (valid_config(cfg))
4900 ast_config_destroy(cfg);
4901 if (fdm != MAP_FAILED)
4902 munmap(fdm, fdlen);
4903 if (fd > -1)
4904 close(fd);
4905 SCOPE_EXIT_RTN_VALUE(res, "%s\n", res ? "Failed" : "Success");
4906}
4907#undef STORE_SQL_FMT
4908#undef STORE_SQL_FMT_CAT
4909
4910/*!
4911 * \brief Renames a message in a mailbox folder.
4912 * \param sdir The folder of the message to be renamed.
4913 * \param smsg The index of the message to be renamed.
4914 * \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.
4915 * \param mailboxcontext The context to be set for the message. Usually this will be the same as the original context.
4916 * \param ddir The destination folder for the message to be renamed into
4917 * \param dmsg The destination message for the message to be renamed.
4918 *
4919 * This method is used by the RENAME macro when mailboxes are stored in an ODBC back end.
4920 * The is usually used to resequence the messages in the mailbox, such as to delete messag index 0, it would be called successively to slide all the other messages down one index.
4921 * 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.
4922 */
4923static void odbc_rename_message(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
4924{
4925 SQLHSTMT stmt;
4926 char *sql = MAKE_SQL_PTRA("UPDATE %s SET dir=?, msgnum=? WHERE mailboxuser=? AND mailboxcontext=? AND dir=? AND msgnum=?");
4927 char msgnums[20];
4928 char msgnumd[20];
4929 struct odbc_obj *obj;
4930 char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
4931 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
4932 SCOPE_ENTER(3, "sdir: %s smsg: %d user: %s context: %s ddir: %s dmsg: %d\n", sdir, smsg,
4933 mailboxuser, mailboxcontext, ddir, dmsg);
4934
4935 SCOPE_CALL(-1, odbc_delete_message, ddir, dmsg);
4936
4937 obj = ast_odbc_request_obj(odbc_database, 0);
4938 if (!obj) {
4939 SCOPE_EXIT_LOG_RTN(AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
4940 }
4941
4942 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
4943 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
4944
4946 if (!stmt)
4947 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
4948 else
4949 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
4951
4953 char *src_fn = MAKE_FILE_PTRA(sdir, smsg);
4954 char *dst_fn = MAKE_FILE_PTRA(ddir, dmsg);
4955
4956 ast_trace(-1, "Recordings stored on disk. Renaming '%s' to '%s'\n", src_fn, dst_fn);
4957 ast_filerename(src_fn, dst_fn, NULL);
4958 }
4959
4960 SCOPE_EXIT_RTN("Done.\n");
4961}
4962
4963/*!
4964 * \brief Removes a voicemail message file.
4965 * \param dir the path to the message file.
4966 * \param msgnum the unique number for the message within the mailbox.
4967 *
4968 * Removes the message content file and the information file.
4969 * This method is used by the DISPOSE macro when mailboxes are stored in an ODBC back end.
4970 * Typical use is to clean up after a RETRIEVE operation.
4971 * Note that this does not remove the message from the mailbox folders, to do that we would use delete_file().
4972 * \return zero on success, -1 on error.
4973 */
4974static int odbc_remove_files(char *dir, int msgnum)
4975{
4976 char *fn = MAKE_FILE_PTRA(dir, msgnum);
4977 char *full_fn = MAKE_FILE_EXT_PTRA(dir, msgnum, "txt");
4978 SCOPE_ENTER(3, "dir: %s msgnum: %d\n", dir, msgnum);
4979
4981 ast_trace(-1, "Audio stored on disk. Keeping '%s' sound files\n", fn);
4982 } else {
4983 ast_trace(-1, "Audio stored in ODBC. Removing '%s' sound files\n", fn);
4984 ast_filedelete(fn, NULL);
4985 }
4986
4987 /* Always remove the information file */
4988 ast_trace(-1, "Removing '%s' information file\n", full_fn);
4989 unlink(full_fn);
4990 SCOPE_EXIT_RTN_VALUE(0, "Done.\n");
4991}
4992#else
4993#ifndef IMAP_STORAGE
4994/*!
4995 * \brief Find all .txt files - even if they are not in sequence from 0000.
4996 * \param vmu
4997 * \param dir
4998 *
4999 * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
5000 *
5001 * \return the count of messages, zero or more.
5002 */
5003static int count_messages(struct ast_vm_user *vmu, char *dir)
5004{
5005
5006 int vmcount = 0;
5007 DIR *vmdir = NULL;
5008 struct dirent *vment = NULL;
5009
5010 if (vm_lock_path(dir))
5011 return ERROR_LOCK_PATH;
5012
5013 if ((vmdir = opendir(dir))) {
5014 while ((vment = readdir(vmdir))) {
5015 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4)) {
5016 vmcount++;
5017 }
5018 }
5019 closedir(vmdir);
5020 }
5021 ast_unlock_path(dir);
5022
5023 return vmcount;
5024}
5025
5026/*!
5027 * \brief Renames a message in a mailbox folder.
5028 * \param sfn The path to the mailbox information and data file to be renamed.
5029 * \param dfn The path for where the message data and information files will be renamed to.
5030 *
5031 * This method is used by the RENAME macro when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
5032 */
5033static void rename_file(char *sfn, char *dfn)
5034{
5035 char stxt[PATH_MAX];
5036 char dtxt[PATH_MAX];
5037 ast_filerename(sfn, dfn, NULL);
5038 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
5039 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
5040 if (ast_check_realtime("voicemail_data")) {
5041 ast_update_realtime("voicemail_data", "filename", sfn, "filename", dfn, SENTINEL);
5042 }
5043 rename(stxt, dtxt);
5044}
5045
5046/*!
5047 * \brief Determines the highest message number in use for a given user and mailbox folder.
5048 * \param dir the folder the mailbox folder to look for messages. Used to construct the SQL where clause.
5049 *
5050 * This method is used when mailboxes are stored on the filesystem. (not ODBC and not IMAP).
5051 * Typical use to set the msgnum would be to take the value returned from this method and add one to it.
5052 *
5053 * \note Should always be called with a lock already set on dir.
5054 * \return the value of zero or greaterto indicate the last message index in use, -1 to indicate none.
5055 */
5056static int last_message_index(char *dir)
5057{
5058 int x;
5059 unsigned char map[MAXMSGLIMIT] = "";
5060 DIR *msgdir;
5061 struct dirent *msgdirent;
5062 int msgdirint;
5063 char extension[4];
5064 int stopcount = 0;
5065
5066 /* Reading the entire directory into a file map scales better than
5067 * doing a stat repeatedly on a predicted sequence. I suspect this
5068 * is partially due to stat(2) internally doing a readdir(2) itself to
5069 * find each file. */
5070 if (!(msgdir = opendir(dir))) {
5071 return -1;
5072 }
5073
5074 while ((msgdirent = readdir(msgdir))) {
5075 if (sscanf(msgdirent->d_name, "msg%30d.%3s", &msgdirint, extension) == 2 && !strcmp(extension, "txt") && msgdirint < MAXMSGLIMIT) {
5076 map[msgdirint] = 1;
5077 stopcount++;
5078 ast_debug(4, "%s map[%d] = %d, count = %d\n", dir, msgdirint, map[msgdirint], stopcount);
5079 }
5080 }
5081 closedir(msgdir);
5082
5083 for (x = 0; x < MAXMSGLIMIT && stopcount; x++) {
5084 stopcount -= map[x];
5085 }
5086
5087 return x - 1;
5088}
5089
5090#endif /* #ifndef IMAP_STORAGE */
5091#endif /* #else of #ifdef ODBC_STORAGE */
5092#ifndef IMAP_STORAGE
5093/*!
5094 * \brief Utility function to copy a file.
5095 * \param infile The path to the file to be copied. The file must be readable, it is opened in read only mode.
5096 * \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.
5097 *
5098 * 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.
5099 * The copy operation copies up to 4096 bytes at once.
5100 *
5101 * \return zero on success, -1 on error.
5102 */
5103static int copy(char *infile, char *outfile)
5104{
5105 int ifd;
5106 int ofd;
5107 int res = -1;
5108 int len;
5109 char buf[4096];
5110
5111#ifdef HARDLINK_WHEN_POSSIBLE
5112 /* Hard link if possible; saves disk space & is faster */
5113 if (!link(infile, outfile)) {
5114 return 0;
5115 }
5116#endif
5117
5118 if ((ifd = open(infile, O_RDONLY)) < 0) {
5119 ast_log(AST_LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
5120 return -1;
5121 }
5122
5123 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
5124 ast_log(AST_LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
5125 close(ifd);
5126 return -1;
5127 }
5128
5129 for (;;) {
5130 int wrlen;
5131
5132 len = read(ifd, buf, sizeof(buf));
5133 if (!len) {
5134 res = 0;
5135 break;
5136 }
5137
5138 if (len < 0) {
5139 ast_log(AST_LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
5140 break;
5141 }
5142
5143 wrlen = write(ofd, buf, len);
5144 if (errno == ENOMEM || errno == ENOSPC || wrlen != len) {
5145 ast_log(AST_LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, wrlen, len, strerror(errno));
5146 break;
5147 }
5148 }
5149
5150 close(ifd);
5151 close(ofd);
5152 if (res) {
5153 unlink(outfile);
5154 }
5155
5156 return res;
5157}
5158
5159/*!
5160 * \brief Copies a voicemail information (envelope) file.
5161 * \param frompath
5162 * \param topath
5163 *
5164 * Every voicemail has the data (.wav) file, and the information file.
5165 * This function performs the file system copying of the information file for a voicemail, handling the internal fields and their values.
5166 * This is used by the COPY macro when not using IMAP storage.
5167 */
5168static void copy_plain_file(char *frompath, char *topath)
5169{
5170 char frompath2[PATH_MAX], topath2[PATH_MAX];
5171 struct ast_variable *tmp, *var = NULL;
5172 const char *origmailbox = "", *context = "", *exten = "";
5173 const char *priority = "", *callerchan = "", *callerid = "", *origdate = "";
5174 const char *origtime = "", *category = "", *duration = "";
5175
5176 ast_filecopy(frompath, topath, NULL);
5177 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
5178 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
5179
5180 if (ast_check_realtime("voicemail_data")) {
5181 var = ast_load_realtime("voicemail_data", "filename", frompath, SENTINEL);
5182 /* This cycle converts ast_variable linked list, to va_list list of arguments, may be there is a better way to do it? */
5183 for (tmp = var; tmp; tmp = tmp->next) {
5184 if (!strcasecmp(tmp->name, "origmailbox")) {
5185 origmailbox = tmp->value;
5186 } else if (!strcasecmp(tmp->name, "context")) {
5187 context = tmp->value;
5188 } else if (!strcasecmp(tmp->name, "exten")) {
5189 exten = tmp->value;
5190 } else if (!strcasecmp(tmp->name, "priority")) {
5191 priority = tmp->value;
5192 } else if (!strcasecmp(tmp->name, "callerchan")) {
5193 callerchan = tmp->value;
5194 } else if (!strcasecmp(tmp->name, "callerid")) {
5195 callerid = tmp->value;
5196 } else if (!strcasecmp(tmp->name, "origdate")) {
5197 origdate = tmp->value;
5198 } else if (!strcasecmp(tmp->name, "origtime")) {
5199 origtime = tmp->value;
5200 } else if (!strcasecmp(tmp->name, "category")) {
5201 category = tmp->value;
5202 } else if (!strcasecmp(tmp->name, "duration")) {
5203 duration = tmp->value;
5204 }
5205 }
5206 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);
5207 }
5208 copy(frompath2, topath2);
5210}
5211#endif
5212
5213/*!
5214 * \brief Removes the voicemail sound and information file.
5215 * \param file The path to the sound file. This will be the folder and message index, without the extension.
5216 *
5217 * This is used by the DELETE macro when voicemails are stored on the file system.
5218 *
5219 * \return zero on success, -1 on error.
5220 */
5221static int vm_delete(char *file)
5222{
5223 char *txt;
5224 int txtsize = 0;
5225 int res = 0;
5226 SCOPE_ENTER(3, "file: %s\n", file);
5227
5228 txtsize = (strlen(file) + 5)*sizeof(char);
5229 txt = ast_alloca(txtsize);
5230 /* Sprintf here would safe because we alloca'd exactly the right length,
5231 * but trying to eliminate all sprintf's anyhow
5232 */
5233 if (ast_check_realtime("voicemail_data")) {
5234 ast_destroy_realtime("voicemail_data", "filename", file, SENTINEL);
5235 }
5236 snprintf(txt, txtsize, "%s.txt", file);
5237 ast_trace(-1, "unlinking '%s'\n", txt);
5238 unlink(txt);
5239 ast_trace(-1, "deleting sound files '%s'\n", file);
5240 res = ast_filedelete(file, NULL);
5241 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d\n", res);
5242}
5243
5244static 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)
5245{
5246 char callerid[256];
5247 char num[12];
5248 char fromdir[256], fromfile[256];
5249 struct ast_config *msg_cfg;
5250 const char *origcallerid, *origtime;
5251 char origcidname[80], origcidnum[80], origdate[80];
5252 int inttime;
5253 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
5254
5255 /* Prepare variables for substitution in email body and subject */
5256 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
5257 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
5258 snprintf(num, sizeof(num), "%d", msgnum);
5259 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", num);
5260 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
5261 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
5262 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
5263 ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
5264 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
5265 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
5266 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
5267 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
5268 pbx_builtin_setvar_helper(ast, "VM_FLAG", flag);
5269
5270 /* Retrieve info from VM attribute file */
5271 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
5272 make_file(fromfile, sizeof(fromfile), fromdir, msgnum - 1);
5273 if (strlen(fromfile) < sizeof(fromfile) - 5) {
5274 strcat(fromfile, ".txt");
5275 }
5276 if (!(msg_cfg = ast_config_load(fromfile, config_flags)) || !(valid_config(msg_cfg))) {
5277 ast_debug(1, "Config load for message text file '%s' failed\n", fromfile);
5278 return;
5279 }
5280
5281 if ((origcallerid = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
5282 pbx_builtin_setvar_helper(ast, "ORIG_VM_CALLERID", origcallerid);
5283 ast_callerid_split(origcallerid, origcidname, sizeof(origcidname), origcidnum, sizeof(origcidnum));
5284 pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNAME", origcidname);
5285 pbx_builtin_setvar_helper(ast, "ORIG_VM_CIDNUM", origcidnum);
5286 }
5287
5288 if ((origtime = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(origtime, "%30d", &inttime) == 1) {
5289 struct timeval tv = { inttime, };
5290 struct ast_tm tm;
5291 ast_localtime(&tv, &tm, NULL);
5292 ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5293 pbx_builtin_setvar_helper(ast, "ORIG_VM_DATE", origdate);
5294 }
5295 ast_config_destroy(msg_cfg);
5296}
5297
5298/*!
5299 * \brief Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
5300 * \param from The string to work with.
5301 * \param buf The buffer into which to write the modified quoted string.
5302 * \param maxlen Always zero, but see \see ast_str
5303 *
5304 * \return The destination string with quotes wrapped on it (the to field).
5305 */
5306static const char *ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
5307{
5308 const char *ptr;
5309
5310 /* We're only ever passing 0 to maxlen, so short output isn't possible */
5311 ast_str_set(buf, maxlen, "\"");
5312 for (ptr = from; *ptr; ptr++) {
5313 if (*ptr == '"' || *ptr == '\\') {
5314 ast_str_append(buf, maxlen, "\\%c", *ptr);
5315 } else {
5316 ast_str_append(buf, maxlen, "%c", *ptr);
5317 }
5318 }
5319 ast_str_append(buf, maxlen, "\"");
5320
5321 return ast_str_buffer(*buf);
5322}
5323
5324/*! \brief
5325 * fill in *tm for current time according to the proper timezone, if any.
5326 * \return tm so it can be used as a function argument.
5327 */
5328static const struct ast_tm *vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
5329{
5330 const struct vm_zone *z = NULL;
5331 struct timeval t = ast_tvnow();
5332
5333 /* Does this user have a timezone specified? */
5334 if (!ast_strlen_zero(vmu->zonetag)) {
5335 /* Find the zone in the list */
5337 AST_LIST_TRAVERSE(&zones, z, list) {
5338 if (!strcmp(z->name, vmu->zonetag))
5339 break;
5340 }
5342 }
5343 ast_localtime(&t, tm, z ? z->timezone : NULL);
5344 return tm;
5345}
5346
5347/*!\brief Check if the string would need encoding within the MIME standard, to
5348 * avoid confusing certain mail software that expects messages to be 7-bit
5349 * clean.
5350 */
5351static int check_mime(const char *str)
5352{
5353 for (; *str; str++) {
5354 if (*str > 126 || *str < 32 || strchr("()<>@,:;/\"[]?.=", *str)) {
5355 return 1;
5356 }
5357 }
5358 return 0;
5359}
5360
5361/*!\brief Encode a string according to the MIME rules for encoding strings
5362 * that are not 7-bit clean or contain control characters.
5363 *
5364 * Additionally, if the encoded string would exceed the MIME limit of 76
5365 * characters per line, then the encoding will be broken up into multiple
5366 * sections, separated by a space character, in order to facilitate
5367 * breaking up the associated header across multiple lines.
5368 *
5369 * \param end An expandable buffer for holding the result
5370 * \param maxlen Always zero, but see \see ast_str
5371 * \param start A string to be encoded
5372 * \param preamble The length of the first line already used for this string,
5373 * to ensure that each line maintains a maximum length of 76 chars.
5374 * \param postamble the length of any additional characters appended to the
5375 * line, used to ensure proper field wrapping.
5376 * \retval The encoded string.
5377 */
5378static const char *ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
5379{
5380 struct ast_str *tmp = ast_str_alloca(80);
5381 int first_section = 1;
5382
5384 ast_str_set(&tmp, -1, "=?%s?Q?", charset);
5385 for (; *start; start++) {
5386 int need_encoding = 0;
5387 if (*start < 33 || *start > 126 || strchr("()<>@,:;/\"[]?.=_", *start)) {
5388 need_encoding = 1;
5389 }
5390 if ((first_section && need_encoding && preamble + ast_str_strlen(tmp) > 70) ||
5391 (first_section && !need_encoding && preamble + ast_str_strlen(tmp) > 72) ||
5392 (!first_section && need_encoding && ast_str_strlen(tmp) > 70) ||
5393 (!first_section && !need_encoding && ast_str_strlen(tmp) > 72)) {
5394 /* Start new line */
5395 ast_str_append(end, maxlen, "%s%s?=", first_section ? "" : " ", ast_str_buffer(tmp));
5396 ast_str_set(&tmp, -1, "=?%s?Q?", charset);
5397 first_section = 0;
5398 }
5399 if (need_encoding && *start == ' ') {
5400 ast_str_append(&tmp, -1, "_");
5401 } else if (need_encoding) {
5402 ast_str_append(&tmp, -1, "=%hhX", *start);
5403 } else {
5404 ast_str_append(&tmp, -1, "%c", *start);
5405 }
5406 }
5407 ast_str_append(end, maxlen, "%s%s?=%s", first_section ? "" : " ", ast_str_buffer(tmp), ast_str_strlen(tmp) + postamble > 74 ? " " : "");
5408 return ast_str_buffer(*end);
5409}
5410
5411/*!
5412 * \brief Creates the email file to be sent to indicate a new voicemail exists for a user.
5413 * \param p The output file to generate the email contents into.
5414 * \param srcemail The email address to send the email to, presumably the email address for the owner of the mailbox.
5415 * \param vmu The voicemail user who is sending the voicemail.
5416 * \param msgnum The message index in the mailbox folder.
5417 * \param context
5418 * \param mailbox The voicemail box to read the voicemail to be notified in this email.
5419 * \param fromfolder
5420 * \param cidnum The caller ID number.
5421 * \param cidname The caller ID name.
5422 * \param attach the name of the sound file to be attached to the email, if attach_user_voicemail == 1.
5423 * \param attach2
5424 * \param format The message sound file format. i.e. .wav
5425 * \param duration The time of the message content, in seconds.
5426 * \param attach_user_voicemail if 1, the sound file is attached to the email.
5427 * \param chan
5428 * \param category
5429 * \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.
5430 * \param flag, msg_id
5431 *
5432 * 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.
5433 */
5434static void make_email_file(FILE *p,
5435 char *srcemail,
5436 struct ast_vm_user *vmu,
5437 int msgnum,
5438 char *context,
5439 char *mailbox,
5440 const char *fromfolder,
5441 char *cidnum,
5442 char *cidname,
5443 char *attach,
5444 char *attach2,
5445 char *format,
5446 int duration,
5447 int attach_user_voicemail,
5448 struct ast_channel *chan,
5449 const char *category,
5450 int imap,
5451 const char *flag,
5452 const char *msg_id)
5453{
5454 char date[256];
5455 char host[MAXHOSTNAMELEN] = "";
5456 char who[256];
5457 char bound[256];
5458 char dur[256];
5459 struct ast_tm tm;
5460 char enc_cidnum[256] = "", enc_cidname[256] = "";
5461 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
5462 char *greeting_attachment;
5463 char filename[256];
5464 int first_line;
5465 char *emailsbuf;
5466 char *email;
5467
5468 if (!str1 || !str2) {
5469 ast_free(str1);
5470 ast_free(str2);
5471 return;
5472 }
5473
5474 if (cidnum) {
5475 strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
5476 }
5477 if (cidname) {
5478 strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
5479 }
5480 gethostname(host, sizeof(host) - 1);
5481
5482 if (strchr(srcemail, '@')) {
5483 ast_copy_string(who, srcemail, sizeof(who));
5484 } else {
5485 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
5486 }
5487
5488 greeting_attachment = strrchr(ast_strdupa(attach), '/');
5489 if (greeting_attachment) {
5490 *greeting_attachment++ = '\0';
5491 }
5492
5493 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
5494 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
5495 fprintf(p, "Date: %s" ENDL, date);
5496
5497 /* Set date format for voicemail mail */
5498 ast_strftime_locale(date, sizeof(date), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5499
5501 struct ast_channel *ast;
5502 char *e_fromstring = !ast_strlen_zero(vmu->fromstring) ? vmu->fromstring : fromstring;
5503 if ((ast = ast_dummy_channel_alloc())) {
5504 char *ptr;
5505 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
5506 ast_str_substitute_variables(&str1, 0, ast, e_fromstring);
5507
5508 if (check_mime(ast_str_buffer(str1))) {
5509 first_line = 1;
5510 ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
5511 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5512 *ptr = '\0';
5513 fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
5514 first_line = 0;
5515 /* Substring is smaller, so this will never grow */
5516 ast_str_set(&str2, 0, "%s", ptr + 1);
5517 }
5518 fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
5519 } else {
5520 fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
5521 }
5522 ast = ast_channel_unref(ast);
5523 } else {
5524 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5525 }
5526 } else {
5527 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
5528 }
5529
5530 emailsbuf = ast_strdupa(vmu->email);
5531 fprintf(p, "To:");
5532 first_line = 1;
5533 while ((email = strsep(&emailsbuf, "|"))) {
5534 char *next = emailsbuf;
5535 if (check_mime(vmu->fullname)) {
5536 char *ptr;
5537 ast_str_encode_mime(&str2, 0, vmu->fullname, first_line ? strlen("To: ") : 0, strlen(email) + 3 + (next ? strlen(",") : 0));
5538 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5539 *ptr = '\0';
5540 fprintf(p, " %s" ENDL, ast_str_buffer(str2));
5541 /* Substring is smaller, so this will never grow */
5542 ast_str_set(&str2, 0, "%s", ptr + 1);
5543 }
5544 fprintf(p, " %s <%s>%s" ENDL, ast_str_buffer(str2), email, next ? "," : "");
5545 } else {
5546 fprintf(p, " %s <%s>%s" ENDL, ast_str_quote(&str2, 0, vmu->fullname), email, next ? "," : "");
5547 }
5548 first_line = 0;
5549 }
5550
5551 if (msgnum <= -1) {
5552 fprintf(p, "Subject: New greeting '%s' on %s." ENDL, greeting_attachment, date);
5554 char *e_subj = !ast_strlen_zero(vmu->emailsubject) ? vmu->emailsubject : emailsubject;
5555 struct ast_channel *ast;
5556 if ((ast = ast_dummy_channel_alloc())) {
5557 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5558 ast_str_substitute_variables(&str1, 0, ast, e_subj);
5559 if (check_mime(ast_str_buffer(str1))) {
5560 char *ptr;
5561 first_line = 1;
5562 ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
5563 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5564 *ptr = '\0';
5565 fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5566 first_line = 0;
5567 /* Substring is smaller, so this will never grow */
5568 ast_str_set(&str2, 0, "%s", ptr + 1);
5569 }
5570 fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
5571 } else {
5572 fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
5573 }
5574 ast = ast_channel_unref(ast);
5575 } else {
5576 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5577 }
5578 } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
5579 if (ast_strlen_zero(flag)) {
5580 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
5581 } else {
5582 fprintf(p, "Subject: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
5583 }
5584 } else {
5585 if (ast_strlen_zero(flag)) {
5586 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
5587 } else {
5588 fprintf(p, "Subject: [PBX]: New %s message %d in mailbox %s" ENDL, flag, msgnum + 1, mailbox);
5589 }
5590 }
5591
5592 fprintf(p, "Message-ID: <Asterisk-%d-%u-%s-%d@%s>" ENDL, msgnum + 1,
5593 (unsigned int) ast_random(), mailbox, (int) getpid(), host);
5594 if (imap) {
5595 /* additional information needed for IMAP searching */
5596 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
5597 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
5598 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
5599 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
5600#ifdef IMAP_STORAGE
5601 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, (!ast_strlen_zero(vmu->imapvmshareid) ? vmu->imapvmshareid : mailbox));
5602#else
5603 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
5604#endif
5605 /* flag added for Urgent */
5606 fprintf(p, "X-Asterisk-VM-Flag: %s" ENDL, S_OR(flag, ""));
5607 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan ? ast_channel_priority(chan) : 0);
5608 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
5609 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
5610 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
5611 if (!ast_strlen_zero(category)) {
5612 fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
5613 } else {
5614 fprintf(p, "X-Asterisk-VM-Category: " ENDL);
5615 }
5616 fprintf(p, "X-Asterisk-VM-Message-Type: %s" ENDL, msgnum > -1 ? "Message" : greeting_attachment);
5617 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
5618 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long) time(NULL));
5619 fprintf(p, "X-Asterisk-VM-Message-ID: %s" ENDL, msg_id);
5620 }
5621 if (!ast_strlen_zero(cidnum)) {
5622 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
5623 }
5624 if (!ast_strlen_zero(cidname)) {
5625 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
5626 }
5627 fprintf(p, "MIME-Version: 1.0" ENDL);
5628 if (attach_user_voicemail) {
5629 /* Something unique. */
5630 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%u", msgnum + 1, mailbox,
5631 (int) getpid(), (unsigned int) ast_random());
5632
5633 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
5634 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
5635 fprintf(p, "--%s" ENDL, bound);
5636 }
5637 fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
5638 if (msgnum <= -1) {
5639 fprintf(p, "This message is to let you know that your greeting '%s' was changed on %s." ENDL
5640 "Please do not delete this message, lest your greeting vanish with it." ENDL ENDL,
5641 greeting_attachment, date);
5642 } else if (emailbody || vmu->emailbody) {
5643 char* e_body = vmu->emailbody ? vmu->emailbody : emailbody;
5644 struct ast_channel *ast;
5645 if ((ast = ast_dummy_channel_alloc())) {
5646 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
5647 ast_str_substitute_variables(&str1, 0, ast, e_body);
5648#ifdef IMAP_STORAGE
5649 {
5650 /* Convert body to native line terminators for IMAP backend */
5651 char *line = ast_str_buffer(str1), *next;
5652 do {
5653 /* Terminate line before outputting it to the file */
5654 if ((next = strchr(line, '\n'))) {
5655 *next++ = '\0';
5656 }
5657 fprintf(p, "%s" ENDL, line);
5658 line = next;
5659 } while (!ast_strlen_zero(line));
5660 }
5661#else
5662 fprintf(p, "%s" ENDL, ast_str_buffer(str1));
5663#endif
5664 ast = ast_channel_unref(ast);
5665 } else {
5666 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5667 }
5668 } else {
5669 if (strcmp(vmu->mailbox, mailbox)) {
5670 /* Forwarded type */
5671 struct ast_config *msg_cfg;
5672 const char *v;
5673 int inttime;
5674 char fromdir[256], fromfile[256], origdate[80] = "", origcallerid[80] = "";
5675 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
5676 /* Retrieve info from VM attribute file */
5677 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, fromfolder);
5678 make_file(fromfile, sizeof(fromfile), fromdir, msgnum);
5679 if (strlen(fromfile) < sizeof(fromfile) - 5) {
5680 strcat(fromfile, ".txt");
5681 }
5682 if ((msg_cfg = ast_config_load(fromfile, config_flags)) && valid_config(msg_cfg)) {
5683 if ((v = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
5684 ast_copy_string(origcallerid, v, sizeof(origcallerid));
5685 }
5686
5687 /* You might be tempted to do origdate, except that a) it's in the wrong
5688 * format, and b) it's missing for IMAP recordings. */
5689 if ((v = ast_variable_retrieve(msg_cfg, "message", "origtime")) && sscanf(v, "%30d", &inttime) == 1) {
5690 struct timeval tv = { inttime, };
5691 struct ast_tm tm;
5692 ast_localtime(&tv, &tm, NULL);
5693 ast_strftime_locale(origdate, sizeof(origdate), emaildateformat, &tm, S_OR(vmu->locale, NULL));
5694 }
5695 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just forwarded"
5696 " a %s long message (number %d)" ENDL "in mailbox %s from %s, on %s" ENDL
5697 "(originally sent by %s on %s)" ENDL "so you might want to check it when you get a"
5698 " chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname, dur,
5699 msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")),
5700 date, origcallerid, origdate);
5701 ast_config_destroy(msg_cfg);
5702 } else {
5703 goto plain_message;
5704 }
5705 } else {
5706plain_message:
5707 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a "
5708 "%s long message (number %d)" ENDL "in mailbox %s from %s, on %s so you might" ENDL
5709 "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk"
5710 ENDL ENDL, vmu->fullname, dur, msgnum + 1, mailbox,
5711 (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
5712 }
5713 }
5714
5715 if (imap || attach_user_voicemail) {
5716 if (!ast_strlen_zero(attach2)) {
5717 snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
5718 ast_debug(5, "creating second attachment filename %s\n", filename);
5719 add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 0, msgnum);
5720 snprintf(filename, sizeof(filename), "msgintro%04d.%s", msgnum, format);
5721 ast_debug(5, "creating attachment filename %s\n", filename);
5722 add_email_attachment(p, vmu, format, attach2, greeting_attachment, mailbox, bound, filename, 1, msgnum);
5723 } else {
5724 snprintf(filename, sizeof(filename), "msg%04d.%s", msgnum, format);
5725 ast_debug(5, "creating attachment filename %s, no second attachment.\n", filename);
5726 add_email_attachment(p, vmu, format, attach, greeting_attachment, mailbox, bound, filename, 1, msgnum);
5727 }
5728 }
5729 ast_free(str1);
5730 ast_free(str2);
5731}
5732
5733static 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)
5734{
5735 char fname[PATH_MAX] = "";
5736 char sox_gain_tmpdir[PATH_MAX];
5737 char *file_to_delete = NULL, *dir_to_delete = NULL;
5738 int res;
5739 char altfname[PATH_MAX] = "";
5740 int altused = 0;
5741 char altformat[80] = "";
5742 char *c = NULL;
5743
5744 /* Eww. We want formats to tell us their own MIME type */
5745 char *mime_type = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
5746
5747 /* Users of multiple file formats need special attention. */
5748 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
5749 if (!ast_file_is_readable(fname)) {
5750 ast_copy_string(altformat, vmfmts, sizeof(altformat));
5751 c = strchr(altformat, '|');
5752 if (c) {
5753 *c = '\0';
5754 }
5755 ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s - trying first/alternate format %s\n", fname, strerror(errno), altformat);
5756 snprintf(altfname, sizeof(altfname), "%s.%s", attach, altformat);
5757 if (!ast_file_is_readable(altfname)) {
5758 ast_log(AST_LOG_WARNING, "Failed to open file: %s: %s - alternate format %s failure\n", altfname, strerror(errno), altformat);
5759 } else {
5760 altused = 1;
5761 }
5762 }
5763
5764 /* This 'while' loop will only execute once. We use it so that we can 'break' */
5765 while (vmu->volgain < -.001 || vmu->volgain > .001 || altused) {
5766 char tmpdir[PATH_MAX];
5767
5768 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
5769
5770 res = snprintf(sox_gain_tmpdir, sizeof(sox_gain_tmpdir), "%s/vm-gain-XXXXXX", tmpdir);
5771 if (res >= sizeof(sox_gain_tmpdir)) {
5772 ast_log(LOG_ERROR, "Failed to create temporary directory path %s: Out of buffer space\n", tmpdir);
5773 break;
5774 }
5775
5776 if (mkdtemp(sox_gain_tmpdir)) {
5777 int soxstatus = 0;
5778 char sox_gain_cmd[PATH_MAX];
5779
5780 ast_debug(3, "sox_gain_tmpdir: %s\n", sox_gain_tmpdir);
5781
5782 /* Save for later */
5783 dir_to_delete = sox_gain_tmpdir;
5784
5785 res = snprintf(fname, sizeof(fname), "%s/output.%s", sox_gain_tmpdir, format);
5786 if (res >= sizeof(fname)) {
5787 ast_log(LOG_ERROR, "Failed to create filename buffer for %s/output.%s: Too long\n", sox_gain_tmpdir, format);
5788 break;
5789 }
5790
5791 if (!altused) {
5792 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s",
5793 vmu->volgain, attach, format, fname);
5794 } else {
5795 if (!strcasecmp(format, "wav")) {
5796 if (vmu->volgain < -.001 || vmu->volgain > .001) {
5797 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s -e signed-integer -b 16 %s",
5798 vmu->volgain, attach, altformat, fname);
5799 } else {
5800 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox %s.%s -e signed-integer -b 16 %s",
5801 attach, altformat, fname);
5802 }
5803 } else {
5804 if (vmu->volgain < -.001 || vmu->volgain > .001) {
5805 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox -v %.4f %s.%s %s",
5806 vmu->volgain, attach, altformat, fname);
5807 } else {
5808 res = snprintf(sox_gain_cmd, sizeof(sox_gain_cmd), "sox %s.%s %s",
5809 attach, altformat, fname);
5810 }
5811 }
5812 }
5813
5814 if (res >= sizeof(sox_gain_cmd)) {
5815 ast_log(LOG_ERROR, "Failed to generate sox command, out of buffer space\n");
5816 break;
5817 }
5818
5819 soxstatus = ast_safe_system(sox_gain_cmd);
5820 if (!soxstatus) {
5821 /* Save for later */
5822 file_to_delete = fname;
5823 ast_debug(3, "VOLGAIN: Stored at: %s - Level: %.4f - Mailbox: %s\n", fname, vmu->volgain, mailbox);
5824 } else {
5825 ast_log(LOG_WARNING, "Sox failed to re-encode %s: %s (have you installed support for all sox file formats?)\n",
5826 fname,
5827 soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
5828 ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
5829 }
5830 }
5831
5832 break;
5833 }
5834
5835 if (!file_to_delete) {
5836 res = snprintf(fname, sizeof(fname), "%s.%s", attach, format);
5837 if (res >= sizeof(fname)) {
5838 ast_log(LOG_ERROR, "Failed to create filename buffer for %s.%s: Too long\n", attach, format);
5839 return -1;
5840 }
5841 }
5842
5843 fprintf(p, "--%s" ENDL, bound);
5844 if (msgnum > -1)
5845 fprintf(p, "Content-Type: %s%s; name=\"%s\"" ENDL, mime_type, format, filename);
5846 else
5847 fprintf(p, "Content-Type: %s%s; name=\"%s.%s\"" ENDL, mime_type, format, greeting_attachment, format);
5848 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
5849 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
5850 if (msgnum > -1)
5851 fprintf(p, "Content-Disposition: attachment; filename=\"%s\"" ENDL ENDL, filename);
5852 else
5853 fprintf(p, "Content-Disposition: attachment; filename=\"%s.%s\"" ENDL ENDL, greeting_attachment, format);
5855 if (last)
5856 fprintf(p, ENDL ENDL "--%s--" ENDL "." ENDL, bound);
5857
5858 if (file_to_delete) {
5859 unlink(file_to_delete);
5860 }
5861
5862 if (dir_to_delete) {
5863 rmdir(dir_to_delete);
5864 }
5865
5866 return 0;
5867}
5868
5869static int sendmail(char *srcemail,
5870 struct ast_vm_user *vmu,
5871 int msgnum,
5872 char *context,
5873 char *mailbox,
5874 const char *fromfolder,
5875 char *cidnum,
5876 char *cidname,
5877 char *attach,
5878 char *attach2,
5879 char *format,
5880 int duration,
5881 int attach_user_voicemail,
5882 struct ast_channel *chan,
5883 const char *category,
5884 const char *flag,
5885 const char *msg_id)
5886{
5887 FILE *p = NULL;
5888 char tmp[80] = "/tmp/astmail-XXXXXX";
5889 char tmp2[256];
5890 char *stringp;
5891
5892 if (vmu && ast_strlen_zero(vmu->email)) {
5893 ast_log(AST_LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
5894 return(0);
5895 }
5896
5897 /* Mail only the first format */
5898 format = ast_strdupa(format);
5899 stringp = format;
5900 strsep(&stringp, "|");
5901
5902 if (!strcmp(format, "wav49"))
5903 format = "WAV";
5904 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));
5905 /* Make a temporary file instead of piping directly to sendmail, in case the mail
5906 command hangs */
5908 ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
5909 return -1;
5910 } else {
5911 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);
5912 fclose(p);
5913 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
5914 ast_safe_system(tmp2);
5915 ast_debug(1, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
5916 }
5917 return 0;
5918}
5919
5920static 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)
5921{
5922 char enc_cidnum[256], enc_cidname[256];
5923 char date[256];
5924 char host[MAXHOSTNAMELEN] = "";
5925 char who[256];
5926 char dur[PATH_MAX];
5927 char tmp[80] = "/tmp/astmail-XXXXXX";
5928 char tmp2[PATH_MAX];
5929 struct ast_tm tm;
5930 FILE *p;
5931 struct ast_str *str1 = ast_str_create(16), *str2 = ast_str_create(16);
5932
5933 if (!str1 || !str2) {
5934 ast_free(str1);
5935 ast_free(str2);
5936 return -1;
5937 }
5938
5939 if (cidnum) {
5940 strip_control_and_high(cidnum, enc_cidnum, sizeof(enc_cidnum));
5941 }
5942 if (cidname) {
5943 strip_control_and_high(cidname, enc_cidname, sizeof(enc_cidname));
5944 }
5945
5947 ast_log(AST_LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
5948 ast_free(str1);
5949 ast_free(str2);
5950 return -1;
5951 }
5952 gethostname(host, sizeof(host)-1);
5953 if (strchr(srcemail, '@')) {
5954 ast_copy_string(who, srcemail, sizeof(who));
5955 } else {
5956 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
5957 }
5958 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
5959 ast_strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
5960 fprintf(p, "Date: %s\n", date);
5961
5962 /* Reformat for custom pager format */
5963 ast_strftime_locale(date, sizeof(date), pagerdateformat, vmu_tm(vmu, &tm), S_OR(vmu->locale, NULL));
5964
5966 struct ast_channel *ast;
5967 if ((ast = ast_dummy_channel_alloc())) {
5968 char *ptr;
5969 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, enc_cidnum, enc_cidname, dur, date, category, flag);
5971
5972 if (check_mime(ast_str_buffer(str1))) {
5973 int first_line = 1;
5974 ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("From: "), strlen(who) + 3);
5975 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5976 *ptr = '\0';
5977 fprintf(p, "%s %s" ENDL, first_line ? "From:" : "", ast_str_buffer(str2));
5978 first_line = 0;
5979 /* Substring is smaller, so this will never grow */
5980 ast_str_set(&str2, 0, "%s", ptr + 1);
5981 }
5982 fprintf(p, "%s %s <%s>" ENDL, first_line ? "From:" : "", ast_str_buffer(str2), who);
5983 } else {
5984 fprintf(p, "From: %s <%s>" ENDL, ast_str_quote(&str2, 0, ast_str_buffer(str1)), who);
5985 }
5986 ast = ast_channel_unref(ast);
5987 } else {
5988 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
5989 }
5990 } else {
5991 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
5992 }
5993
5994 if (check_mime(vmu->fullname)) {
5995 int first_line = 1;
5996 char *ptr;
5997 ast_str_encode_mime(&str2, 0, vmu->fullname, strlen("To: "), strlen(pager) + 3);
5998 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
5999 *ptr = '\0';
6000 fprintf(p, "%s %s" ENDL, first_line ? "To:" : "", ast_str_buffer(str2));
6001 first_line = 0;
6002 /* Substring is smaller, so this will never grow */
6003 ast_str_set(&str2, 0, "%s", ptr + 1);
6004 }
6005 fprintf(p, "%s %s <%s>" ENDL, first_line ? "To:" : "", ast_str_buffer(str2), pager);
6006 } else {
6007 fprintf(p, "To: %s <%s>" ENDL, ast_str_quote(&str2, 0, vmu->fullname), pager);
6008 }
6009
6011 struct ast_channel *ast;
6012 if ((ast = ast_dummy_channel_alloc())) {
6013 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
6015 if (check_mime(ast_str_buffer(str1))) {
6016 int first_line = 1;
6017 char *ptr;
6018 ast_str_encode_mime(&str2, 0, ast_str_buffer(str1), strlen("Subject: "), 0);
6019 while ((ptr = strchr(ast_str_buffer(str2), ' '))) {
6020 *ptr = '\0';
6021 fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
6022 first_line = 0;
6023 /* Substring is smaller, so this will never grow */
6024 ast_str_set(&str2, 0, "%s", ptr + 1);
6025 }
6026 fprintf(p, "%s %s" ENDL, first_line ? "Subject:" : "", ast_str_buffer(str2));
6027 } else {
6028 fprintf(p, "Subject: %s" ENDL, ast_str_buffer(str1));
6029 }
6030 ast = ast_channel_unref(ast);
6031 } else {
6032 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
6033 }
6034 } else {
6035 if (ast_strlen_zero(flag)) {
6036 fprintf(p, "Subject: New VM" ENDL);
6037 } else {
6038 fprintf(p, "Subject: New %s VM" ENDL, flag);
6039 }
6040 }
6041
6042 /* End of headers */
6043 fputs(ENDL, p);
6044
6045 if (pagerbody) {
6046 struct ast_channel *ast;
6047 if ((ast = ast_dummy_channel_alloc())) {
6048 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, fromfolder, cidnum, cidname, dur, date, category, flag);
6050 fprintf(p, "%s" ENDL, ast_str_buffer(str1));
6051 ast = ast_channel_unref(ast);
6052 } else {
6053 ast_log(AST_LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
6054 }
6055 } else {
6056 fprintf(p, "New %s long %s msg in box %s\n"
6057 "from %s, on %s", dur, flag, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
6058 }
6059
6060 fclose(p);
6061 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
6062 ast_safe_system(tmp2);
6063 ast_debug(1, "Sent page to %s with command '%s'\n", pager, mailcmd);
6064 ast_free(str1);
6065 ast_free(str2);
6066 return 0;
6067}
6068
6069/*!
6070 * \brief Gets the current date and time, as formatted string.
6071 * \param s The buffer to hold the output formatted date.
6072 * \param len the length of the buffer. Used to prevent buffer overflow in ast_strftime.
6073 *
6074 * The date format string used is "%a %b %e %r UTC %Y".
6075 *
6076 * \return zero on success, -1 on error.
6077 */
6078static int get_date(char *s, int len)
6079{
6080 struct ast_tm tm;
6081 struct timeval t = ast_tvnow();
6082
6083 ast_localtime(&t, &tm, "UTC");
6084
6085 return ast_strftime(s, len, "%a %b %e %r UTC %Y", &tm);
6086}
6087
6088static int invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
6089{
6090 int res;
6091 char fn[PATH_MAX];
6092 char dest[PATH_MAX];
6093
6094 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, context, ext);
6095
6096 if ((res = create_dirpath(dest, sizeof(dest), context, ext, ""))) {
6097 ast_log(AST_LOG_WARNING, "Failed to make directory(%s)\n", fn);
6098 return -1;
6099 }
6100
6101 RETRIEVE(fn, -1, ext, context);
6102 if (ast_fileexists(fn, NULL, NULL) > 0) {
6103 res = ast_stream_and_wait(chan, fn, ecodes);
6104 if (res) {
6105 DISPOSE(fn, -1);
6106 return res;
6107 }
6108 } else {
6109 /* Dispose just in case */
6110 DISPOSE(fn, -1);
6111 res = ast_stream_and_wait(chan, "vm-theperson", ecodes);
6112 if (res)
6113 return res;
6114 res = ast_say_digit_str(chan, ext, ecodes, ast_channel_language(chan));
6115 if (res)
6116 return res;
6117 }
6118 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", ecodes);
6119 return res;
6120}
6121
6122static void free_zone(struct vm_zone *z)
6123{
6124 ast_free(z);
6125}
6126
6127#ifdef ODBC_STORAGE
6128#define COUNT_MSGS_SQL_FMT "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'"
6129static int count_messages_in_folder(struct odbc_obj *odbc, const char *context, const char *mailbox, const char *folder, int *messages)
6130{
6131 int res;
6132 char sql[sizeof(COUNT_MSGS_SQL_FMT) + odbc_table_len + strlen(VM_SPOOL_DIR)
6133 + strlen(context) + strlen(mailbox) + strlen(folder)];
6134 char rowdata[20];
6135 SQLHSTMT stmt = NULL;
6136 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
6137 SCOPE_ENTER(3, "context: %s mb: %s folder: %s", context, mailbox, folder);
6138
6139 if (!messages) {
6140 SCOPE_EXIT_RTN_VALUE(0, "No messages pointer\n");
6141 }
6142
6143 snprintf(sql, sizeof(sql), COUNT_MSGS_SQL_FMT, odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
6144 if (!(stmt = ast_odbc_prepare_and_execute(odbc, generic_prepare, &gps))) {
6145 SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
6146 }
6147 res = SQLFetch(stmt);
6148 if (!SQL_SUCCEEDED(res)) {
6149 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6150 SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
6151 }
6152 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
6153 if (!SQL_SUCCEEDED(res)) {
6154 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6155 SCOPE_EXIT_LOG_RTN_VALUE(1, LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
6156 }
6157
6158 *messages = atoi(rowdata);
6159 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6160
6161 SCOPE_EXIT_RTN_VALUE(0, "messages: %d\n", *messages);
6162}
6163#undef COUNT_MSGS_SQL_FMT
6164
6165static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
6166{
6167 char tmp[PATH_MAX] = "";
6168 struct odbc_obj *obj;
6169 char *context;
6170 SCOPE_ENTER(3, "mb: %s", mailbox);
6171
6172 if (newmsgs)
6173 *newmsgs = 0;
6174 if (oldmsgs)
6175 *oldmsgs = 0;
6176 if (urgentmsgs)
6177 *urgentmsgs = 0;
6178
6179 /* If no mailbox, return immediately */
6180 if (ast_strlen_zero(mailbox)) {
6181 SCOPE_EXIT_RTN_VALUE(0, "No mailbox\n");
6182 }
6183
6184 ast_copy_string(tmp, mailbox, sizeof(tmp));
6185
6186 if (strchr(mailbox, ' ') || strchr(mailbox, ',')) {
6187 int u, n, o;
6188 char *next, *remaining = tmp;
6189 while ((next = strsep(&remaining, " ,"))) {
6190 if (inboxcount2(next, urgentmsgs ? &u : NULL, &n, &o)) {
6191 SCOPE_EXIT_RTN_VALUE(-1, "Failed to obtain message count for mailbox %s\n", next);
6192 }
6193 if (urgentmsgs) {
6194 *urgentmsgs += u;
6195 }
6196 if (newmsgs) {
6197 *newmsgs += n;
6198 }
6199 if (oldmsgs) {
6200 *oldmsgs += o;
6201 }
6202 }
6203 SCOPE_EXIT_RTN_VALUE(0, "Urgent: %d New: %d Old: %d\n", urgentmsgs ? *urgentmsgs : 0, newmsgs ? *newmsgs : 0, oldmsgs ? *oldmsgs : 0);
6204 }
6205
6206 context = strchr(tmp, '@');
6207 if (context) {
6208 *context = '\0';
6209 context++;
6210 } else
6211 context = "default";
6212
6213 obj = ast_odbc_request_obj(odbc_database, 0);
6214 if (!obj) {
6215 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
6216 }
6217
6218 if (count_messages_in_folder(obj, context, tmp, "INBOX", newmsgs)
6219 || count_messages_in_folder(obj, context, tmp, "Old", oldmsgs)
6220 || count_messages_in_folder(obj, context, tmp, "Urgent", urgentmsgs)) {
6221 ast_log(AST_LOG_WARNING, "Failed to obtain message count for mailbox %s@%s\n",
6222 tmp, context);
6223 }
6224
6226 SCOPE_EXIT_RTN_VALUE(0, "Urgent: %d New: %d Old: %d\n", urgentmsgs ? *urgentmsgs : 0, newmsgs ? *newmsgs : 0, oldmsgs ? *oldmsgs : 0);
6227}
6228
6229/*!
6230 * \brief Gets the number of messages that exist in a mailbox folder.
6231 * \param mailbox_id
6232 * \param folder
6233 *
6234 * This method is used when ODBC backend is used.
6235 * \return The number of messages in this mailbox folder (zero or more).
6236 */
6237#define MSGCOUNT_SQL_FMT_INBOX "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/INBOX' OR dir = '%s%s/%s/Urgent'"
6238#define MSGCOUNT_SQL_FMT "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'"
6239static int messagecount(const char *mailbox_id, const char *folder)
6240{
6241 struct odbc_obj *obj = NULL;
6242 char *context;
6243 char *mailbox;
6244 int nummsgs = 0;
6245 int res;
6246 SQLHSTMT stmt = NULL;
6247 char rowdata[20];
6248 struct generic_prepare_struct gps = { .argc = 0 };
6249 SCOPE_ENTER(3, "mb: %s folder: %s", mailbox_id, folder);
6250
6251 /* If no mailbox, return immediately */
6252 if (ast_strlen_zero(mailbox_id)
6253 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
6254 SCOPE_EXIT_RTN_VALUE(0, "Couldn't parse mailbox\n");
6255 }
6256
6257 if (ast_strlen_zero(folder)) {
6258 folder = "INBOX";
6259 }
6260
6261 obj = ast_odbc_request_obj(odbc_database, 0);
6262 if (!obj) {
6263 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
6264 }
6265
6266 if (!strcmp(folder, "INBOX")) {
6267 res = ast_asprintf(&gps.sql, MSGCOUNT_SQL_FMT_INBOX, odbc_table, VM_SPOOL_DIR, context, mailbox, VM_SPOOL_DIR, context, mailbox);
6268 } else {
6269 res = ast_asprintf(&gps.sql, MSGCOUNT_SQL_FMT, odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
6270 }
6271 if (res <= 0) {
6272 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "Failed to allocate memory for SQL statement for '%s'!\n", odbc_database);
6273 }
6274 ast_trace(-1, "SQL: %s\n", gps.sql);
6275
6277 if (!stmt) {
6278 ast_log(AST_LOG_WARNING, "SQL Execute error!\n[%s]\n\n", gps.sql);
6279 goto bail;
6280 }
6281 res = SQLFetch(stmt);
6282 if (!SQL_SUCCEEDED(res)) {
6283 ast_log(AST_LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", gps.sql);
6284 goto bail_with_handle;
6285 }
6286 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
6287 if (!SQL_SUCCEEDED(res)) {
6288 ast_log(AST_LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", gps.sql);
6289 goto bail_with_handle;
6290 }
6291 nummsgs = atoi(rowdata);
6292
6293bail_with_handle:
6294 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
6295
6296bail:
6298 ast_free(gps.sql);
6299 SCOPE_EXIT_RTN_VALUE(nummsgs, "Messages: %d\n", nummsgs);
6300}
6301#undef MSGCOUNT_SQL_FMT
6302#undef MSGCOUNT_SQL_FMT_INBOX
6303
6304/*!
6305 * \brief Determines if the given folder has messages.
6306 * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
6307 *
6308 * This function is used when the mailbox is stored in an ODBC back end.
6309 * This invokes the messagecount(). Here we are interested in the presence of messages (> 0) only, not the actual count.
6310 * \return 1 if the folder has one or more messages. zero otherwise.
6311 */
6312static int has_voicemail(const char *mailboxes, const char *folder)
6313{
6314 char *parse;
6315 char *mailbox;
6316
6317 parse = ast_strdupa(mailboxes);
6318 while ((mailbox = strsep(&parse, ",&"))) {
6319 if (messagecount(mailbox, folder)) {
6320 return 1;
6321 }
6322 }
6323 return 0;
6324}
6325#endif
6326#ifndef IMAP_STORAGE
6327/*!
6328 * \brief Copies a message from one mailbox to another.
6329 * \param chan
6330 * \param vmu
6331 * \param imbox
6332 * \param msgnum
6333 * \param duration
6334 * \param recip
6335 * \param fmt
6336 * \param dir
6337 * \param flag, dest_folder
6338 *
6339 * This is only used by file storage based mailboxes.
6340 *
6341 * \return zero on success, -1 on error.
6342 */
6343static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum,
6344 long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag,
6345 const char *dest_folder)
6346{
6347 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
6348 const char *frombox = mbox(vmu, imbox);
6349 const char *userfolder;
6350 int recipmsgnum;
6351 int res = 0;
6352 SCOPE_ENTER(3, "mb: %s imb: %d msgnum: %d recip: %s dir: %s dest_folder: %s",
6353 vmu->mailbox, imbox, msgnum, recip->mailbox, dir, dest_folder);
6354
6355 ast_log(AST_LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
6356
6357 if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) { /* If urgent, copy to Urgent folder */
6358 userfolder = "Urgent";
6359 } else if (!ast_strlen_zero(dest_folder)) {
6360 userfolder = dest_folder;
6361 } else {
6362 userfolder = "INBOX";
6363 }
6364
6365 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
6366 ast_trace(-1, "todir: %s\n", todir);
6367
6368 if (!dir)
6369 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
6370 else
6371 ast_copy_string(fromdir, dir, sizeof(fromdir));
6372
6373 ast_trace(-1, "fromdir: %s\n", fromdir);
6374
6375 make_file(frompath, sizeof(frompath), fromdir, msgnum);
6376 ast_trace(-1, "frompath: %s\n", frompath);
6377
6378 make_dir(todir, sizeof(todir), recip->context, recip->mailbox, userfolder);
6379 ast_trace(-1, "todir: %s\n", todir);
6380
6381 if (vm_lock_path(todir)) {
6383 }
6384
6385 recipmsgnum = LAST_MSG_INDEX(todir) + 1;
6386 ast_trace(-1, "recip msgnum: %d\n", recipmsgnum);
6387
6388 if (recipmsgnum < recip->maxmsg - (imbox ? 0 : inprocess_count(vmu->mailbox, vmu->context, 0))) {
6389 int exists = 0;
6390
6391 make_file(topath, sizeof(topath), todir, recipmsgnum);
6392 ast_trace(-1, "topath: %s\n", topath);
6393
6394 exists = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, fromdir, msgnum, frompath, chan ? ast_channel_language(chan) : "");
6395 if (exists) {
6396 SCOPE_CALL(-1, COPY, fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
6397 } else {
6398 SCOPE_CALL(-1, copy_plain_file,frompath, topath);
6399 SCOPE_CALL(-1, STORE, todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL, NULL, NULL);
6400 SCOPE_CALL(-1, vm_delete, topath);
6401
6402 }
6403 } else {
6404 ast_log(AST_LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
6405 res = -1;
6406 }
6407 ast_unlock_path(todir);
6408 if (chan) {
6409 struct ast_party_caller *caller = ast_channel_caller(chan);
6410 notify_new_message(chan, recip, NULL, recipmsgnum, duration, fmt,
6411 S_COR(caller->id.number.valid, caller->id.number.str, NULL),
6412 S_COR(caller->id.name.valid, caller->id.name.str, NULL),
6413 flag);
6414 }
6415
6416 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d\n", res);
6417}
6418#endif
6419#if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
6420
6421static int messagecount(const char *mailbox_id, const char *folder)
6422{
6423 char *context;
6424 char *mailbox;
6425
6426 if (ast_strlen_zero(mailbox_id)
6427 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
6428 return 0;
6429 }
6430
6431 return __has_voicemail(context, mailbox, folder, 0) + (folder && strcmp(folder, "INBOX") ? 0 : __has_voicemail(context, mailbox, "Urgent", 0));
6432}
6433
6434static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
6435{
6436 DIR *dir;
6437 struct dirent *de;
6438 char fn[256];
6439 int ret = 0;
6440 struct alias_mailbox_mapping *mapping;
6441 char *c;
6442 char *m;
6443
6444 /* If no mailbox, return immediately */
6446 return 0;
6447
6448 if (ast_strlen_zero(folder))
6449 folder = "INBOX";
6451 context = "default";
6452
6453 c = (char *)context;
6454 m = (char *)mailbox;
6455
6457 char tmp[MAX_VM_MAILBOX_LEN];
6458
6459 snprintf(tmp, MAX_VM_MAILBOX_LEN, "%s@%s", mailbox, context);
6461 if (mapping) {
6462 separate_mailbox(ast_strdupa(mapping->mailbox), &m, &c);
6463 ao2_ref(mapping, -1);
6464 }
6465 }
6466
6467 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, c, m, folder);
6468
6469 if (!(dir = opendir(fn)))
6470 return 0;
6471
6472 while ((de = readdir(dir))) {
6473 if (!strncasecmp(de->d_name, "msg", 3)) {
6474 if (shortcircuit) {
6475 ret = 1;
6476 break;
6477 } else if (!strncasecmp(de->d_name + 8, "txt", 3)) {
6478 ret++;
6479 }
6480 }
6481 }
6482
6483 closedir(dir);
6484
6485 return ret;
6486}
6487
6488/*!
6489 * \brief Determines if the given folder has messages.
6490 * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
6491 * \param folder the folder to look in
6492 *
6493 * This function is used when the mailbox is stored in a filesystem back end.
6494 * This invokes the __has_voicemail(). Here we are interested in the presence of messages (> 0) only, not the actual count.
6495 * \return 1 if the folder has one or more messages. zero otherwise.
6496 */
6497static int has_voicemail(const char *mailbox, const char *folder)
6498{
6499 char tmp[256], *tmp2 = tmp, *box, *context;
6500 ast_copy_string(tmp, mailbox, sizeof(tmp));
6501 if (ast_strlen_zero(folder)) {
6502 folder = "INBOX";
6503 }
6504 while ((box = strsep(&tmp2, ",&"))) {
6505 if ((context = strchr(box, '@')))
6506 *context++ = '\0';
6507 else
6508 context = "default";
6509 if (__has_voicemail(context, box, folder, 1))
6510 return 1;
6511 /* If we are checking INBOX, we should check Urgent as well */
6512 if (!strcmp(folder, "INBOX") && __has_voicemail(context, box, "Urgent", 1)) {
6513 return 1;
6514 }
6515 }
6516 return 0;
6517}
6518
6519/*!
6520 * \brief Check the given mailbox's message count.
6521 * \param mailbox The \@ delimited string for user\@context. If no context is found, uses 'default' for the context.
6522 * \param urgentmsgs urgent message count.
6523 * \param newmsgs new message count.
6524 * \param oldmsgs old message count pointer
6525 * \return -1 if error occurred, 0 otherwise.
6526 */
6527static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
6528{
6529 char tmp[256];
6530 char *context;
6531
6532 /* If no mailbox, return immediately */
6533 if (ast_strlen_zero(mailbox)) {
6534 return 0;
6535 }
6536
6537 if (newmsgs) {
6538 *newmsgs = 0;
6539 }
6540 if (oldmsgs) {
6541 *oldmsgs = 0;
6542 }
6543 if (urgentmsgs) {
6544 *urgentmsgs = 0;
6545 }
6546
6547 if (strchr(mailbox, ',')) {
6548 int tmpnew, tmpold, tmpurgent;
6549 char *mb, *cur;
6550
6551 ast_copy_string(tmp, mailbox, sizeof(tmp));
6552 mb = tmp;
6553 while ((cur = strsep(&mb, ", "))) {
6554 if (!ast_strlen_zero(cur)) {
6555 if (inboxcount2(cur, urgentmsgs ? &tmpurgent : NULL, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL)) {
6556 return -1;
6557 } else {
6558 if (newmsgs) {
6559 *newmsgs += tmpnew;
6560 }
6561 if (oldmsgs) {
6562 *oldmsgs += tmpold;
6563 }
6564 if (urgentmsgs) {
6565 *urgentmsgs += tmpurgent;
6566 }
6567 }
6568 }
6569 }
6570 return 0;
6571 }
6572
6573 ast_copy_string(tmp, mailbox, sizeof(tmp));
6574
6575 if ((context = strchr(tmp, '@'))) {
6576 *context++ = '\0';
6577 } else {
6578 context = "default";
6579 }
6580
6581 if (newmsgs) {
6582 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
6583 }
6584 if (oldmsgs) {
6585 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
6586 }
6587 if (urgentmsgs) {
6588 *urgentmsgs = __has_voicemail(context, tmp, "Urgent", 0);
6589 }
6590
6591 return 0;
6592}
6593
6594#endif
6595
6596/* Exactly the same function for file-based, ODBC-based, and IMAP-based, so why create 3 different copies? */
6597static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
6598{
6599 int urgentmsgs = 0;
6600 int res = inboxcount2(mailbox, &urgentmsgs, newmsgs, oldmsgs);
6601 if (newmsgs) {
6602 *newmsgs += urgentmsgs;
6603 }
6604 return res;
6605}
6606
6607static void run_externnotify(const char *context, const char *extension, const char *flag)
6608{
6609 char arguments[255];
6610 char ext_context[256] = "";
6611 int newvoicemails = 0, oldvoicemails = 0, urgentvoicemails = 0;
6612 struct ast_smdi_mwi_message *mwi_msg;
6613
6615 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
6616 else
6617 ast_copy_string(ext_context, extension, sizeof(ext_context));
6618
6619 if (smdi_iface) {
6620 if (ast_app_has_voicemail(ext_context, NULL))
6622 else
6624
6626 ast_log(AST_LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
6627 if (!strncmp(mwi_msg->cause, "INV", 3))
6628 ast_log(AST_LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
6629 else if (!strncmp(mwi_msg->cause, "BLK", 3))
6630 ast_log(AST_LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
6631 ast_log(AST_LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
6632 ao2_ref(mwi_msg, -1);
6633 } else {
6634 ast_debug(1, "Successfully executed SMDI MWI change for %s\n", extension);
6635 }
6636 }
6637
6639 if (inboxcount2(ext_context, &urgentvoicemails, &newvoicemails, &oldvoicemails)) {
6640 ast_log(AST_LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
6641 } else {
6642 snprintf(arguments, sizeof(arguments), "%s %s %s %d %d %d &",
6643 externnotify, S_OR(context, "\"\""),
6644 extension, newvoicemails,
6645 oldvoicemails, urgentvoicemails);
6646 ast_debug(1, "Executing %s\n", arguments);
6647 ast_safe_system(arguments);
6648 }
6649 }
6650}
6651
6652/*!
6653 * \brief Variables used for saving a voicemail.
6654 *
6655 * This includes the record gain, mode flags, and the exit context of the chanel that was used for leaving the voicemail.
6656 */
6657struct leave_vm_options {
6658 unsigned int flags;
6659 signed char record_gain;
6662};
6663
6664static void generate_msg_id(char *dst)
6665{
6666 /* msg id is time of msg_id generation plus an incrementing value
6667 * called each time a new msg_id is generated. This should achieve uniqueness,
6668 * but only in single system solutions.
6669 */
6670 unsigned int unique_counter = ast_atomic_fetchadd_int(&msg_id_incrementor, +1);
6671 snprintf(dst, MSG_ID_LEN, "%ld-%08x", (long) time(NULL), unique_counter);
6672}
6673
6674/*!
6675 * \internal
6676 * \brief Creates a voicemail based on a specified file to a mailbox.
6677 * \param recdata A vm_recording_data containing filename and voicemail txt info.
6678 * \retval -1 failure
6679 * \retval 0 success
6680 *
6681 * This is installed to the app.h voicemail functions and accommodates all voicemail
6682 * storage methods. It should probably be broken out along with leave_voicemail at
6683 * some point in the future.
6684 *
6685 * This function currently only works for a single recipient and only uses the format
6686 * specified in recording_ext.
6687 */
6689{
6690 /* voicemail recipient structure */
6691 struct ast_vm_user *recipient; /* points to svm once it's been created */
6692 struct ast_vm_user svm; /* struct storing the voicemail recipient */
6693
6694 /* File paths */
6695 char tmpdir[PATH_MAX]; /* directory temp files are stored in */
6696 char tmptxtfile[PATH_MAX]; /* tmp file for voicemail txt file */
6697 char desttxtfile[PATH_MAX]; /* final destination for txt file */
6698 char tmpaudiofile[PATH_MAX]; /* tmp file where audio is stored */
6699 char dir[PATH_MAX]; /* destination for tmp files on completion */
6700 char destination[PATH_MAX]; /* destination with msgXXXX. Basically <dir>/msgXXXX */
6701
6702 /* stuff that only seems to be needed for IMAP */
6703 #ifdef IMAP_STORAGE
6704 struct vm_state *vms = NULL;
6705 char ext_context[256] = "";
6706 int newmsgs = 0;
6707 int oldmsgs = 0;
6708 #endif
6709
6710 /* miscellaneous operational variables */
6711 int res = 0; /* Used to store error codes from functions */
6712 int txtdes /* File descriptor for the text file used to write the voicemail info */;
6713 FILE *txt; /* FILE pointer to text file used to write the voicemail info */
6714 char date[256]; /* string used to hold date of the voicemail (only used for ODBC) */
6715 int msgnum; /* the 4 digit number designated to the voicemail */
6716 int duration = 0; /* Length of the audio being recorded in seconds */
6717 struct ast_filestream *recording_fs; /*used to read the recording to get duration data */
6718
6719 /* We aren't currently doing anything with category, since it comes from a channel variable and
6720 * this function doesn't use channels, but this function could add that as an argument later. */
6721 const char *category = NULL; /* pointless for now */
6722 char msg_id[MSG_ID_LEN];
6723
6724 /* Start by checking to see if the file actually exists... */
6725 if (!(ast_fileexists(recdata->recording_file, recdata->recording_ext, NULL))) {
6726 ast_log(LOG_ERROR, "File: %s not found.\n", recdata->recording_file);
6727 return -1;
6728 }
6729
6730 memset(&svm, 0, sizeof(svm));
6731 if (!(recipient = find_user(&svm, recdata->context, recdata->mailbox))) {
6732 ast_log(LOG_ERROR, "No entry in voicemail config file for '%s@%s'\n", recdata->mailbox, recdata->context);
6733 return -1;
6734 }
6735
6736 /* determine duration in seconds */
6737 if ((recording_fs = ast_readfile(recdata->recording_file, recdata->recording_ext, NULL, 0, 0, VOICEMAIL_DIR_MODE))) {
6738 if (!ast_seekstream(recording_fs, 0, SEEK_END)) {
6739 long framelength = ast_tellstream(recording_fs);
6740 int sample_rate = ast_ratestream(recording_fs);
6741 if (sample_rate) {
6742 duration = (int) (framelength / sample_rate);
6743 } else {
6744 ast_log(LOG_ERROR,"Unable to determine sample rate of recording %s\n", recdata->recording_file);
6745 }
6746 }
6747 ast_closestream(recording_fs);
6748 }
6749
6750 /* If the duration was below the minimum duration for the user, let's just drop the whole thing now */
6751 if (duration < recipient->minsecs) {
6752 ast_log(LOG_NOTICE, "Copying recording to voicemail %s@%s skipped because duration was shorter than "
6753 "minmessage of recipient\n", recdata->mailbox, recdata->context);
6754 return -1;
6755 }
6756
6757 /* Note that this number must be dropped back to a net sum of zero before returning from this function */
6758
6759 if ((res = create_dirpath(tmpdir, sizeof(tmpdir), recipient->context, recdata->mailbox, "tmp"))) {
6760 ast_log(LOG_ERROR, "Failed to make directory.\n");
6761 }
6762
6763 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
6764 txtdes = mkstemp(tmptxtfile);
6765 if (txtdes < 0) {
6766 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
6767 /* Something screwed up. Abort. */
6768 ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
6769 free_user(recipient);
6770 return -1;
6771 }
6772
6773 /* Store information */
6774 txt = fdopen(txtdes, "w+");
6775 if (txt) {
6776 generate_msg_id(msg_id);
6777 get_date(date, sizeof(date));
6778 fprintf(txt,
6779 ";\n"
6780 "; Message Information file\n"
6781 ";\n"
6782 "[message]\n"
6783 "origmailbox=%s\n"
6784 "context=%s\n"
6785 "exten=%s\n"
6786 "rdnis=Unknown\n"
6787 "priority=%d\n"
6788 "callerchan=%s\n"
6789 "callerid=%s\n"
6790 "origdate=%s\n"
6791 "origtime=%ld\n"
6792 "category=%s\n"
6793 "msg_id=%s\n"
6794 "flag=\n" /* flags not supported in copy from file yet */
6795 "duration=%d\n", /* Don't have any reliable way to get duration of file. */
6796
6797 recdata->mailbox,
6798 S_OR(recdata->call_context, ""),
6799 S_OR(recdata->call_extension, ""),
6800 recdata->call_priority,
6801 S_OR(recdata->call_callerchan, "Unknown"),
6802 S_OR(recdata->call_callerid, "Unknown"),
6803 date, (long) time(NULL),
6804 S_OR(category, ""),
6805 msg_id,
6806 duration);
6807
6808 /* Since we are recording from a file, we shouldn't need to do anything else with
6809 * this txt file */
6810 fclose(txt);
6811
6812 } else {
6813 ast_log(LOG_WARNING, "Error opening text file for output\n");
6814 if (ast_check_realtime("voicemail_data")) {
6815 ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
6816 }
6817 free_user(recipient);
6818 return -1;
6819 }
6820
6821 /* At this point, the actual creation of a voicemail message should be finished.
6822 * Now we just need to copy the files being recorded into the receiving folder. */
6823
6824 create_dirpath(dir, sizeof(dir), recipient->context, recipient->mailbox, recdata->folder);
6825
6826#ifdef IMAP_STORAGE
6827 /* make recipient info into an inboxcount friendly string */
6828 snprintf(ext_context, sizeof(ext_context), "%s@%s", recipient->mailbox, recipient->context);
6829
6830 /* Is ext a mailbox? */
6831 /* must open stream for this user to get info! */
6832 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
6833 if (res < 0) {
6834 ast_log(LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
6835 free_user(recipient);
6836 unlink(tmptxtfile);
6837 return -1;
6838 }
6839 if (!(vms = get_vm_state_by_mailbox(recipient->mailbox, recipient->context, 0))) {
6840 /* It is possible under certain circumstances that inboxcount did not
6841 * create a vm_state when it was needed. This is a catchall which will
6842 * rarely be used.
6843 */
6844 if (!(vms = create_vm_state_from_user(recipient))) {
6845 ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
6846 free_user(recipient);
6847 unlink(tmptxtfile);
6848 return -1;
6849 }
6850 }
6851 vms->newmessages++;
6852
6853 /* here is a big difference! We add one to it later */
6854 msgnum = newmsgs + oldmsgs;
6855 ast_debug(3, "Messagecount set to %d\n", msgnum);
6856 snprintf(destination, sizeof(destination), "%simap/msg%s%04d", VM_SPOOL_DIR, recipient->mailbox, msgnum);
6857
6858 /* Check to see if we have enough room in the mailbox. If not, spit out an error and end
6859 * Note that imap_check_limits raises inprocess_count if successful */
6860 if ((res = imap_check_limits(NULL, vms, recipient, msgnum))) {
6861 ast_log(LOG_NOTICE, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
6862 inprocess_count(recipient->mailbox, recipient->context, -1);
6863 free_user(recipient);
6864 unlink(tmptxtfile);
6865 return -1;
6866 }
6867
6868#else
6869
6870 /* Check to see if the mailbox is full for ODBC/File storage */
6871 ast_debug(3, "mailbox = %d : inprocess = %d\n", COUNT(recipient, dir),
6872 inprocess_count(recipient->mailbox, recipient->context, 0));
6873 if (COUNT(recipient, dir) > recipient->maxmsg - inprocess_count(recipient->mailbox, recipient->context, +1)) {
6874 ast_log(AST_LOG_WARNING, "Didn't copy to voicemail. Mailbox for %s@%s is full.\n", recipient->mailbox, recipient->context);
6875 inprocess_count(recipient->mailbox, recipient->context, -1);
6876 free_user(recipient);
6877 unlink(tmptxtfile);
6878 return -1;
6879 }
6880
6881 msgnum = LAST_MSG_INDEX(dir) + 1;
6882#endif
6883
6884 /* Lock the directory receiving the voicemail since we want it to still exist when we attempt to copy the voicemail.
6885 * We need to unlock it before we return. */
6886 if (vm_lock_path(dir)) {
6887 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
6888 /* Delete files */
6889 ast_filedelete(tmptxtfile, NULL);
6890 unlink(tmptxtfile);
6891 free_user(recipient);
6892 return -1;
6893 }
6894
6895 make_file(destination, sizeof(destination), dir, msgnum);
6896
6897 make_file(tmpaudiofile, sizeof(tmpaudiofile), tmpdir, msgnum);
6898
6899 if (ast_filecopy(recdata->recording_file, tmpaudiofile, recdata->recording_ext)) {
6900 ast_log(LOG_ERROR, "Audio file failed to copy to tmp dir. Probably low disk space.\n");
6901
6902 inprocess_count(recipient->mailbox, recipient->context, -1);
6903 ast_unlock_path(dir);
6904 free_user(recipient);
6905 unlink(tmptxtfile);
6906 return -1;
6907 }
6908
6909 /* Alright, try to copy to the destination folder now. */
6910 if (ast_filerename(tmpaudiofile, destination, recdata->recording_ext)) {
6911 ast_log(LOG_ERROR, "Audio file failed to move to destination directory. Permissions/Overlap?\n");
6912 inprocess_count(recipient->mailbox, recipient->context, -1);
6913 ast_unlock_path(dir);
6914 free_user(recipient);
6915 unlink(tmptxtfile);
6916 return -1;
6917 }
6918
6919 snprintf(desttxtfile, sizeof(desttxtfile), "%s.txt", destination);
6920 rename(tmptxtfile, desttxtfile);
6921
6922 if (chmod(desttxtfile, VOICEMAIL_FILE_MODE) < 0) {
6923 ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", desttxtfile, strerror(errno));
6924 }
6925
6926
6927 ast_unlock_path(dir);
6928 inprocess_count(recipient->mailbox, recipient->context, -1);
6929
6930 /* If we copied something, we should store it either to ODBC or IMAP if we are using those. The STORE macro allows us
6931 * 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
6932 * time to create the voicemail database entry. */
6933 if (ast_fileexists(destination, NULL, NULL) > 0) {
6934 struct ast_channel *chan = NULL;
6935 char fmt[80];
6936 char clid[80];
6937 char cidnum[80], cidname[80];
6938 int send_email;
6939
6940 if (ast_check_realtime("voicemail_data")) {
6941 get_date(date, sizeof(date));
6942 ast_store_realtime("voicemail_data",
6943 "origmailbox", recdata->mailbox,
6944 "context", S_OR(recdata->context, ""),
6945 "exten", S_OR(recdata->call_extension, ""),
6946 "priority", recdata->call_priority,
6947 "callerchan", S_OR(recdata->call_callerchan, "Unknown"),
6948 "callerid", S_OR(recdata->call_callerid, "Unknown"),
6949 "origdate", date,
6950 "origtime", time(NULL),
6951 "category", S_OR(category, ""),
6952 "filename", tmptxtfile,
6953 "duration", duration,
6954 SENTINEL);
6955 }
6956
6957 STORE(dir, recipient->mailbox, recipient->context, msgnum, NULL, recipient, fmt, 0, vms, "", msg_id);
6958
6959 send_email = ast_test_flag(recipient, VM_EMAIL_EXT_RECS);
6960
6961 if (send_email) {
6962 /* Send an email if possible, fall back to just notifications if not. */
6963 ast_copy_string(fmt, recdata->recording_ext, sizeof(fmt));
6964 ast_copy_string(clid, recdata->call_callerid, sizeof(clid));
6965 ast_callerid_split(clid, cidname, sizeof(cidname), cidnum, sizeof(cidnum));
6966
6967 /* recdata->call_callerchan itself no longer exists, so we can't use the real channel. Use a dummy one. */
6968 chan = ast_dummy_channel_alloc();
6969 }
6970 if (chan) {
6971 notify_new_message(chan, recipient, NULL, msgnum, duration, fmt, cidnum, cidname, "");
6972 ast_channel_unref(chan);
6973 } else {
6974 if (send_email) { /* We tried and failed. */
6975 ast_log(LOG_WARNING, "Failed to allocate dummy channel, email will not be sent\n");
6976 }
6977 notify_new_state(recipient);
6978 }
6979 }
6980
6981 free_user(recipient);
6982 unlink(tmptxtfile);
6983 return 0;
6984}
6985
6986/*!
6987 * \brief Prompts the user and records a voicemail to a mailbox.
6988 * \param chan
6989 * \param ext
6990 * \param options OPT_BUSY_GREETING, OPT_UNAVAIL_GREETING
6991 *
6992 *
6993 *
6994 * \return zero on success, -1 on error.
6995 */
6996static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
6997{
6998#ifdef IMAP_STORAGE
6999 int newmsgs, oldmsgs;
7000#endif
7001 char txtfile[PATH_MAX];
7002 char tmptxtfile[PATH_MAX];
7003 struct vm_state *vms = NULL;
7004 char callerid[256];
7005 FILE *txt;
7006 char date[256];
7007 int txtdes;
7008 int res = 0;
7009 int msgnum;
7010 int duration = 0;
7011 int sound_duration = 0;
7012 int ouseexten = 0;
7013 int greeting_only = 0;
7014 char tmpdur[16];
7015 char priority[16];
7016 char origtime[16];
7017 char dir[PATH_MAX];
7018 char tmpdir[PATH_MAX];
7019 char fn[PATH_MAX];
7020 char prefile[PATH_MAX] = "";
7021 char tempfile[PATH_MAX] = "";
7022 char ext_context[256] = "";
7023 char fmt[80];
7024 char *context;
7025 char ecodes[17] = "#";
7026 struct ast_str *tmp = ast_str_create(16);
7027 char *tmpptr;
7028 struct ast_vm_user *vmu;
7029 struct ast_vm_user svm;
7030 const char *category = NULL;
7031 const char *code;
7032 const char *alldtmf = "0123456789ABCD*#";
7033 char flag[80];
7034 SCOPE_ENTER(3, "%s: %s\n", ast_channel_name(chan), ext);
7035
7036 if (!tmp) {
7037 SCOPE_EXIT_RTN_VALUE(-1, "Failed to allocate memory for tmp\n");
7038 }
7039
7040 ast_str_set(&tmp, 0, "%s", ext);
7042 if ((context = strchr(ext, '@'))) {
7043 *context++ = '\0';
7044 tmpptr = strchr(context, '&');
7045 } else {
7046 tmpptr = strchr(ext, '&');
7047 }
7048
7049 if (tmpptr)
7050 *tmpptr++ = '\0';
7051
7052 ast_channel_lock(chan);
7053 if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
7054 category = ast_strdupa(category);
7055 }
7056 ast_channel_unlock(chan);
7057
7059 ast_copy_string(flag, "Urgent", sizeof(flag));
7061 ast_copy_string(flag, "PRIORITY", sizeof(flag));
7062 } else {
7063 flag[0] = '\0';
7064 }
7065
7066 ast_debug(3, "Before find_user\n");
7067 memset(&svm, 0, sizeof(svm));
7068 if (!(vmu = find_user(&svm, context, ext))) {
7069 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7070 ast_free(tmp);
7072 "%s: Exten: %s: No entry in voicemail config file for '%s'\n", ast_channel_name(chan), ext, ext);
7073 }
7074
7075 /* If maxmsg is zero, act as a "greetings only" voicemail: Exit successfully without recording */
7076 if (vmu->maxmsg == 0) {
7077 greeting_only = 1;
7079 }
7080
7081 /* Setup pre-file if appropriate */
7082 if (strcmp(vmu->context, "default"))
7083 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
7084 else
7085 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
7086
7087 /* Set the path to the prefile. Will be one of
7088 VM_SPOOL_DIRcontext/ext/busy
7089 VM_SPOOL_DIRcontext/ext/unavail
7090 Depending on the flag set in options.
7091 */
7093 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
7095 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
7096 }
7097 ast_trace(-1, "prefile: %s\n", prefile);
7098 /* Set the path to the tmpfile as
7099 VM_SPOOL_DIR/context/ext/temp
7100 and attempt to create the folder structure.
7101 */
7102 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
7103 ast_trace(-1, "tempfile: %s\n", tempfile);
7104 if ((res = create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp"))) {
7105 free_user(vmu);
7106 ast_free(tmp);
7108 "%s: Exten: %s: Failed to make directory (%s)\n", ast_channel_name(chan), ext, tempfile);
7109 }
7110 SCOPE_CALL(-1, RETRIEVE, tempfile, -1, vmu->mailbox, vmu->context);
7111 if (ast_fileexists(tempfile, NULL, NULL) > 0) {
7112 ast_copy_string(prefile, tempfile, sizeof(prefile));
7113 ast_trace(-1, "new prefile: %s\n", prefile);
7114 }
7115
7116 SCOPE_CALL(-1, DISPOSE, tempfile, -1);
7117
7118 /* It's easier just to try to make it than to check for its existence */
7119#ifndef IMAP_STORAGE
7120 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
7121#else
7122 snprintf(dir, sizeof(dir), "%simap", VM_SPOOL_DIR);
7123 if (mkdir(dir, VOICEMAIL_DIR_MODE) && errno != EEXIST) {
7124 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dir, strerror(errno));
7125 }
7126#endif
7127
7128 /* Check current context for special extensions */
7129 if (ast_test_flag(vmu, VM_OPERATOR)) {
7130 if (!ast_strlen_zero(vmu->exit)) {
7131 if (ast_exists_extension(chan, vmu->exit, "o", 1,
7132 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7133 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
7134 ouseexten = 1;
7135 }
7136 } else if (ast_exists_extension(chan, ast_channel_context(chan), "o", 1,
7137 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7138 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
7139 ouseexten = 1;
7140 }
7141 }
7142
7143 if (!ast_strlen_zero(vmu->exit)) {
7144 if (ast_exists_extension(chan, vmu->exit, "a", 1,
7145 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7146 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
7147 }
7148 } else if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
7149 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7150 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
7151 }
7152
7154 for (code = alldtmf; *code; code++) {
7155 char e[2] = "";
7156 e[0] = *code;
7157 if (strchr(ecodes, e[0]) == NULL
7158 && ast_canmatch_extension(chan,
7159 (!ast_strlen_zero(options->exitcontext) ? options->exitcontext : ast_channel_context(chan)),
7160 e, 1, S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
7161 strncat(ecodes, e, sizeof(ecodes) - strlen(ecodes) - 1);
7162 }
7163 }
7164 }
7165
7166 /* Play the beginning intro if desired */
7167 if (!ast_strlen_zero(prefile)) {
7168#if defined(ODBC_STORAGE)
7169 int success = SCOPE_CALL_WITH_INT_RESULT(-1, RETRIEVE, prefile, -1, ext, context);
7170#elif defined(IMAP_STORAGE)
7171 SCOPE_CALL(-1, RETRIEVE, prefile, -1, ext, context);
7172#endif
7173
7174 if (ast_fileexists(prefile, NULL, NULL) > 0) {
7175 if (ast_streamfile(chan, prefile, ast_channel_language(chan)) > -1) {
7176 /* We know we have a greeting at this point, so squelch the instructions
7177 * if that is what is being asked of us */
7180 }
7181 res = ast_waitstream(chan, ecodes);
7182 }
7183#ifdef ODBC_STORAGE
7184 if (success == -1) {
7185 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
7186 ast_trace(-1, "Greeting '%s' not retrieved from database, but found in file storage. Inserting into database\n", prefile);
7187 SCOPE_CALL(-1, odbc_store_message, prefile, vmu->mailbox, vmu->context, -1);
7188 }
7189#endif
7190 } else {
7191 ast_trace(-1, "%s doesn't exist, doing what we can\n", prefile);
7192 res = invent_message(chan, vmu->context, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
7193 }
7194 SCOPE_CALL(-1, DISPOSE, prefile, -1);
7195 if (res < 0) {
7196 free_user(vmu);
7197 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7198 ast_free(tmp);
7199 SCOPE_EXIT_RTN_VALUE(-1, "Hang up during prefile playback\n");
7200 }
7201 }
7202 if (res == '#') {
7203 /* On a '#' we skip the instructions */
7205 res = 0;
7206 }
7207 if (!res && !ast_test_flag(options, OPT_SILENT)) {
7208 res = ast_stream_and_wait(chan, INTRO, ecodes);
7209 if (res == '#') {
7211 res = 0;
7212 }
7213 }
7214 if (res > 0)
7215 ast_stopstream(chan);
7216 /* Check for a '*' here in case the caller wants to escape from voicemail to something
7217 other than the operator -- an automated attendant or mailbox login for example */
7218 if (res == '*') {
7219 ast_channel_exten_set(chan, "a");
7220 if (!ast_strlen_zero(vmu->exit)) {
7221 ast_channel_context_set(chan, vmu->exit);
7222 }
7223 ast_channel_priority_set(chan, 0);
7224 free_user(vmu);
7225 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
7226 ast_free(tmp);
7227 SCOPE_EXIT_RTN_VALUE(0, "User escaped with '*'\n");
7228 }
7229
7230 /* Check for a '0' here */
7231 if (ast_test_flag(vmu, VM_OPERATOR) && res == '0') {
7232 transfer:
7233 if (ouseexten) {
7234 ast_channel_exten_set(chan, "o");
7235 if (!ast_strlen_zero(vmu->exit)) {
7236 ast_channel_context_set(chan, vmu->exit);
7237 }
7238 ast_play_and_wait(chan, "transfer");
7239 ast_channel_priority_set(chan, 0);
7240 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
7241 }
7242 free_user(vmu);
7243 ast_free(tmp);
7244 SCOPE_EXIT_RTN_VALUE(OPERATOR_EXIT, "User escaped with '0'\n");
7245 }
7246
7247 /* Allow all other digits to exit Voicemail and return to the dialplan */
7248 if (ast_test_flag(options, OPT_DTMFEXIT) && res > 0) {
7249 if (!ast_strlen_zero(options->exitcontext)) {
7250 ast_channel_context_set(chan, options->exitcontext);
7251 }
7252 free_user(vmu);
7253 ast_free(tmp);
7254 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
7255 SCOPE_EXIT_RTN_VALUE(res, "User escaped back to dialplan '%c'\n", res);
7256 }
7257
7258 if (greeting_only) {
7259 ast_debug(3, "Greetings only VM (maxmsg=0), Skipping voicemail recording\n");
7260 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
7261 res = 0;
7262 goto leave_vm_out;
7263 }
7264
7265 if (res < 0) {
7266 free_user(vmu);
7267 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7268 ast_free(tmp);
7269 SCOPE_EXIT_RTN_VALUE(-1, "Other error '%d'\n", res);
7270 }
7271 /* The meat of recording the message... All the announcements and beeps have been played*/
7272 if (ast_channel_state(chan) != AST_STATE_UP) {
7273 ast_answer(chan);
7274 }
7275 ast_copy_string(fmt, vmfmts, sizeof(fmt));
7276 if (!ast_strlen_zero(fmt)) {
7277 char msg_id[MSG_ID_LEN] = "";
7278 msgnum = 0;
7279
7280#ifdef IMAP_STORAGE
7281 /* Is ext a mailbox? */
7282 /* must open stream for this user to get info! */
7283 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
7284 if (res < 0) {
7285 ast_log(AST_LOG_NOTICE, "Can not leave voicemail, unable to count messages\n");
7286 free_user(vmu);
7287 ast_free(tmp);
7288 return -1;
7289 }
7290 if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
7291 /* It is possible under certain circumstances that inboxcount did not
7292 * create a vm_state when it was needed. This is a catchall which will
7293 * rarely be used.
7294 */
7295 if (!(vms = create_vm_state_from_user(vmu))) {
7296 ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
7297 free_user(vmu);
7298 ast_free(tmp);
7299 return -1;
7300 }
7301 }
7302 vms->newmessages++;
7303
7304 /* here is a big difference! We add one to it later */
7305 msgnum = newmsgs + oldmsgs;
7306 ast_debug(3, "Messagecount set to %d\n", msgnum);
7307 snprintf(fn, sizeof(fn), "%simap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
7308 /* set variable for compatibility */
7309 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
7310
7311 if ((res = imap_check_limits(chan, vms, vmu, msgnum))) {
7312 goto leave_vm_out;
7313 }
7314#else
7315 if (COUNT(vmu, dir) >= vmu->maxmsg - inprocess_count(vmu->mailbox, vmu->context, +1)) {
7316 res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
7317 if (!res)
7318 res = ast_waitstream(chan, "");
7319 ast_log(AST_LOG_WARNING, "No more messages possible\n");
7320 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7321 inprocess_count(vmu->mailbox, vmu->context, -1);
7322 goto leave_vm_out;
7323 }
7324
7325#endif
7326 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
7327 ast_trace(-1, "Tempfile: %s\n", tmptxtfile);
7328 txtdes = mkstemp(tmptxtfile);
7329 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
7330 if (txtdes < 0) {
7331 res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
7332 if (!res)
7333 res = ast_waitstream(chan, "");
7334 ast_log(AST_LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
7335 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7336 inprocess_count(vmu->mailbox, vmu->context, -1);
7337 goto leave_vm_out;
7338 }
7339
7340 /* Now play the beep once we have the message number for our next message. */
7341 if (res >= 0) {
7342 /* Unless we're *really* silent, try to send the beep */
7343 /* Play default or custom beep, unless no beep desired */
7344 if (!ast_strlen_zero(options->beeptone)) {
7345 res = ast_stream_and_wait(chan, options->beeptone, "");
7346 }
7347 }
7348
7349 /* Store information in real-time storage */
7350 if (ast_check_realtime("voicemail_data")) {
7351 snprintf(priority, sizeof(priority), "%d", ast_channel_priority(chan));
7352 snprintf(origtime, sizeof(origtime), "%ld", (long) time(NULL));
7353 get_date(date, sizeof(date));
7354 ast_callerid_merge(callerid, sizeof(callerid),
7355 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7356 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7357 "Unknown");
7358 ast_store_realtime("voicemail_data",
7359 "origmailbox", ext,
7360 "context", ast_channel_context(chan),
7361 "exten", ast_channel_exten(chan),
7362 "priority", priority,
7363 "callerchan", ast_channel_name(chan),
7364 "callerid", callerid,
7365 "origdate", date,
7366 "origtime", origtime,
7367 "category", S_OR(category, ""),
7368 "filename", tmptxtfile,
7369 SENTINEL);
7370 }
7371
7372 /* Store information */
7373 txt = fdopen(txtdes, "w+");
7374 if (txt) {
7375 generate_msg_id(msg_id);
7376 get_date(date, sizeof(date));
7377 ast_callerid_merge(callerid, sizeof(callerid),
7378 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7379 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7380 "Unknown");
7381 fprintf(txt,
7382 ";\n"
7383 "; Message Information file\n"
7384 ";\n"
7385 "[message]\n"
7386 "origmailbox=%s\n"
7387 "context=%s\n"
7388 "exten=%s\n"
7389 "rdnis=%s\n"
7390 "priority=%d\n"
7391 "callerchan=%s\n"
7392 "callerid=%s\n"
7393 "origdate=%s\n"
7394 "origtime=%ld\n"
7395 "category=%s\n"
7396 "msg_id=%s\n",
7397 ext,
7398 ast_channel_context(chan),
7399 ast_channel_exten(chan),
7400 S_COR(ast_channel_redirecting(chan)->from.number.valid,
7401 ast_channel_redirecting(chan)->from.number.str, "unknown"),
7403 ast_channel_name(chan),
7404 callerid,
7405 date, (long) time(NULL),
7406 category ? category : "",
7407 msg_id);
7408 ast_trace(-1, "Saving txt file mbox: %s msg_id: %s\n", ext, msg_id);
7409 } else {
7410 ast_log(AST_LOG_WARNING, "Error opening text file for output\n");
7411 inprocess_count(vmu->mailbox, vmu->context, -1);
7412 if (ast_check_realtime("voicemail_data")) {
7413 ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7414 }
7415 res = ast_streamfile(chan, "vm-mailboxfull", ast_channel_language(chan));
7416 goto leave_vm_out;
7417 }
7418
7419 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);
7420
7421 /* At this point, either we were instructed to make the message Urgent
7422 by arguments to VoiceMail or during the review process by the person
7423 leaving the message. So we update the directory where we want this
7424 message to go. */
7425 if (!strcmp(flag, "Urgent")) {
7426 create_dirpath(dir, sizeof(dir), vmu->context, ext, "Urgent");
7427 }
7428
7429 if (txt) {
7430 fprintf(txt, "flag=%s\n", flag);
7431 if (sound_duration < vmu->minsecs) {
7432 fclose(txt);
7433 ast_verb(3, "Recording was %d seconds long but needs to be at least %d - abandoning\n", sound_duration, vmu->minsecs);
7434 ast_filedelete(tmptxtfile, NULL);
7435 unlink(tmptxtfile);
7436 if (ast_check_realtime("voicemail_data")) {
7437 ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7438 }
7439 inprocess_count(vmu->mailbox, vmu->context, -1);
7440 } else {
7441 fprintf(txt, "duration=%d\n", duration);
7442 fclose(txt);
7443 if (vm_lock_path(dir)) {
7444 ast_log(AST_LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
7445 /* Delete files */
7446 ast_filedelete(tmptxtfile, NULL);
7447 unlink(tmptxtfile);
7448 inprocess_count(vmu->mailbox, vmu->context, -1);
7449 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
7450 ast_debug(1, "The recorded media file is gone, so we should remove the .txt file too!\n");
7451 unlink(tmptxtfile);
7452 ast_unlock_path(dir);
7453 inprocess_count(vmu->mailbox, vmu->context, -1);
7454 if (ast_check_realtime("voicemail_data")) {
7455 ast_destroy_realtime("voicemail_data", "filename", tmptxtfile, SENTINEL);
7456 }
7457 } else {
7458#ifndef IMAP_STORAGE
7459 msgnum = LAST_MSG_INDEX(dir) + 1;
7460#endif
7461 make_file(fn, sizeof(fn), dir, msgnum);
7462
7463 /* assign a variable with the name of the voicemail file */
7464#ifndef IMAP_STORAGE
7465 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
7466#else
7467 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
7468#endif
7469
7470 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
7471
7472 ast_trace(-1, "Renaming recordings '%s' -> fn '%s'\n", tmptxtfile, fn);
7473 /* ast_filerename renames the recordings but not the txt file */
7474 ast_filerename(tmptxtfile, fn, NULL);
7475
7476 ast_trace(-1, "Renaming txt file '%s' -> fn '%s'\n", tmptxtfile, txtfile);
7477 rename(tmptxtfile, txtfile);
7478 inprocess_count(vmu->mailbox, vmu->context, -1);
7479
7480 /* Properly set permissions on voicemail text descriptor file.
7481 Unfortunately mkstemp() makes this file 0600 on most unix systems. */
7482 if (chmod(txtfile, VOICEMAIL_FILE_MODE) < 0)
7483 ast_log(AST_LOG_ERROR, "Couldn't set permissions on voicemail text file %s: %s", txtfile, strerror(errno));
7484
7485 ast_unlock_path(dir);
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 */
7493 if (ast_fileexists(fn, NULL, NULL) > 0) {
7494 SCOPE_CALL(-1, STORE, dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms, flag, msg_id);
7495 }
7496
7497 /* Are there to be more recipients of this message? */
7498 while (tmpptr) {
7499 struct ast_vm_user recipu, *recip;
7500 char *exten, *cntx;
7501
7502 exten = strsep(&tmpptr, "&");
7503 cntx = strchr(exten, '@');
7504 if (cntx) {
7505 *cntx = '\0';
7506 cntx++;
7507 }
7508 memset(&recipu, 0, sizeof(recipu));
7509 if ((recip = find_user(&recipu, cntx, exten))) {
7510 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir, flag, NULL);
7511 free_user(recip);
7512 }
7513 }
7514
7515 /* Notification needs to happen after the copy, though. */
7516 if (ast_fileexists(fn, NULL, NULL)) {
7517#ifdef IMAP_STORAGE
7518 notify_new_message(chan, vmu, vms, msgnum, duration, fmt,
7519 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7520 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7521 flag);
7522#else
7523 notify_new_message(chan, vmu, NULL, msgnum, duration, fmt,
7524 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
7525 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
7526 flag);
7527#endif
7528 }
7529
7530 /* Disposal needs to happen after the optional move and copy */
7531 if (ast_fileexists(fn, NULL, NULL)) {
7532 SCOPE_CALL(-1, DISPOSE, dir, msgnum);
7533 }
7534 }
7535 }
7536 } else {
7537 inprocess_count(vmu->mailbox, vmu->context, -1);
7538 }
7539 if (res == '0') {
7540 goto transfer;
7541 } else if (res > 0 && res != 't')
7542 res = 0;
7543
7544 if (sound_duration < vmu->minsecs)
7545 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
7546 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7547 else
7548 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
7549 } else
7550 ast_log(AST_LOG_WARNING, "No format for saving voicemail?\n");
7551leave_vm_out:
7552 free_user(vmu);
7553
7554#ifdef IMAP_STORAGE
7555 /* expunge message - use UID Expunge if supported on IMAP server*/
7556 ast_debug(3, "*** Checking if we can expunge, expungeonhangup set to %d\n", expungeonhangup);
7557 if (expungeonhangup == 1 && vms->mailstream != NULL) {
7558 ast_mutex_lock(&vms->lock);
7559#ifdef HAVE_IMAP_TK2006
7560 if (LEVELUIDPLUS (vms->mailstream)) {
7561 mail_expunge_full(vms->mailstream, NIL, EX_UID);
7562 } else
7563#endif
7564 mail_expunge(vms->mailstream);
7565 ast_mutex_unlock(&vms->lock);
7566 }
7567#endif
7568
7569 ast_free(tmp);
7570 SCOPE_EXIT_RTN_VALUE(res, "Done: '%d'\n", res);
7571}
7572
7573#if !defined(IMAP_STORAGE)
7574static int resequence_mailbox(struct ast_vm_user *vmu, char *dir, int stopcount)
7575{
7576 /* we know the actual number of messages, so stop process when number is hit */
7577
7578 int x, dest;
7579 char sfn[PATH_MAX];
7580 char dfn[PATH_MAX];
7581
7582 if (vm_lock_path(dir)) {
7583 return ERROR_LOCK_PATH;
7584 }
7585
7586 for (x = 0, dest = 0; dest != stopcount && x < MAXMSGLIMIT; x++) {
7587 make_file(sfn, sizeof(sfn), dir, x);
7588 if (EXISTS(dir, x, sfn, NULL)) {
7589
7590 if (x != dest) {
7591 make_file(dfn, sizeof(dfn), dir, dest);
7592 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
7593 }
7594
7595 dest++;
7596 }
7597 }
7598 ast_unlock_path(dir);
7599
7600 return dest;
7601}
7602#endif
7603
7604static int say_and_wait(struct ast_channel *chan, int num, const char *language)
7605{
7606 int d;
7607 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, NULL);
7608 return d;
7609}
7610
7611static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move)
7612{
7613#ifdef IMAP_STORAGE
7614 /* we must use mbox(x) folder names, and copy the message there */
7615 /* simple. huh? */
7616 char sequence[10];
7617 char mailbox[256];
7618 int res;
7619 int curr_mbox;
7620
7621 /* get the real IMAP message number for this message */
7622 snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
7623
7624 ast_debug(3, "Copying sequence %s to mailbox %s\n", sequence, mbox(vmu, box));
7625 ast_mutex_lock(&vms->lock);
7626 /* if save to Old folder, put in INBOX as read */
7627 if (box == OLD_FOLDER) {
7628 mail_setflag(vms->mailstream, sequence, "\\Seen");
7629 } else if (box == NEW_FOLDER) {
7630 mail_clearflag(vms->mailstream, sequence, "\\Seen");
7631 }
7632 if (!strcasecmp(mbox(vmu, NEW_FOLDER), vms->curbox) && (box == NEW_FOLDER || box == OLD_FOLDER)) {
7633 ast_mutex_unlock(&vms->lock);
7634 return 0;
7635 }
7636
7637 /* get the current mailbox so that we can point the mailstream back to it later */
7638 curr_mbox = get_folder_by_name(vms->curbox);
7639
7640 /* Create the folder if it doesn't exist */
7641 imap_mailbox_name(mailbox, sizeof(mailbox), vms, box, 1); /* Get the full mailbox name */
7642 if (vms->mailstream && !mail_status(vms->mailstream, mailbox, SA_UIDNEXT)) {
7643 if (mail_create(vms->mailstream, mailbox) != NIL) {
7644 ast_log(AST_LOG_NOTICE, "Folder %s created!\n", mbox(vmu, box));
7645 }
7646 }
7647
7648 /* restore previous mbox stream */
7649 if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
7650 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
7651 res = -1;
7652 } else {
7653 if (move) {
7654 res = !mail_move(vms->mailstream, sequence, (char *) mbox(vmu, box));
7655 } else {
7656 res = !mail_copy(vms->mailstream, sequence, (char *) mbox(vmu, box));
7657 }
7658 }
7659 ast_mutex_unlock(&vms->lock);
7660 return res;
7661#else
7662 char *dir = vms->curdir;
7663 char *username = vms->username;
7664 char *context = vmu->context;
7665 char sfn[PATH_MAX];
7666 char dfn[PATH_MAX];
7667 char ddir[PATH_MAX];
7668 const char *dbox = mbox(vmu, box);
7669 int x, i;
7670 SCOPE_ENTER(3, "dir: %s msg: %d box: %d dbox: %s move? %d \n", dir, msg, box, dbox, move);
7671
7672 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
7673 ast_trace(-1, "ddir: %s\n", ddir);
7674
7675 if (vm_lock_path(ddir)) {
7676 SCOPE_EXIT_RTN_VALUE(ERROR_LOCK_PATH, "Failed to lock path %s\n", ddir);
7677 }
7678
7679 x = LAST_MSG_INDEX(ddir) + 1;
7680
7681 if (box == 10 && x >= vmu->maxdeletedmsg) { /* "Deleted" folder*/
7682 ast_trace(-1, "Deleting message %d\n", msg);
7683 x--;
7684 for (i = 1; i <= x; i++) {
7685 /* Push files down a "slot". The oldest file (msg0000) will be deleted. */
7686 make_file(sfn, sizeof(sfn), ddir, i);
7687 make_file(dfn, sizeof(dfn), ddir, i - 1);
7688 if (EXISTS(ddir, i, sfn, NULL)) {
7689 SCOPE_CALL(-1, RENAME, ddir, i, vmu->mailbox, vmu->context, ddir, i - 1, sfn, dfn);
7690 } else
7691 break;
7692 }
7693 } else {
7694 if (x >= vmu->maxmsg) {
7695 ast_unlock_path(ddir);
7696 SCOPE_EXIT_RTN_VALUE(ERROR_MAX_MSGS, "Max messages reached\n");
7697 }
7698 }
7699 make_file(sfn, sizeof(sfn), dir, msg);
7700 make_file(dfn, sizeof(dfn), ddir, x);
7701 if (strcmp(sfn, dfn)) {
7702 ast_trace(-1, "Copying message '%s' to '%s'\n", sfn, dfn);
7703 SCOPE_CALL(-1, COPY, dir, msg, ddir, x, username, context, sfn, dfn);
7704 }
7705 ast_unlock_path(ddir);
7706
7707 if (newmsg) {
7708 *newmsg = x;
7709 }
7710 SCOPE_EXIT_RTN_VALUE(0, "Done\n");
7711#endif
7712}
7713
7714static int adsi_logo(unsigned char *buf)
7715{
7716 int bytes = 0;
7717 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
7718 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
7719 return bytes;
7720}
7721
7722static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
7723{
7724 unsigned char buf[256];
7725 int bytes = 0;
7726 int x;
7727 char num[5];
7728
7729 *useadsi = 0;
7730 bytes += ast_adsi_data_mode(buf + bytes);
7732
7733 bytes = 0;
7734 bytes += adsi_logo(buf);
7735 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
7736#ifdef DISPLAY
7737 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
7738#endif
7739 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7740 bytes += ast_adsi_data_mode(buf + bytes);
7742
7744 bytes = 0;
7745 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
7746 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
7747 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7748 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7750 return 0;
7751 }
7752
7753#ifdef DISPLAY
7754 /* Add a dot */
7755 bytes = 0;
7756 bytes += ast_adsi_logo(buf);
7757 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
7758 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
7759 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7761#endif
7762 bytes = 0;
7763 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
7764 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
7765 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advanced", "3", 1);
7766 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
7767 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
7768 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
7770
7771#ifdef DISPLAY
7772 /* Add another dot */
7773 bytes = 0;
7774 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
7775 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7776
7777 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7779#endif
7780
7781 bytes = 0;
7782 /* These buttons we load but don't use yet */
7783 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
7784 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
7785 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
7786 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
7787 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
7788 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
7790
7791#ifdef DISPLAY
7792 /* Add another dot */
7793 bytes = 0;
7794 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
7795 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7797#endif
7798
7799 bytes = 0;
7800 for (x = 0; x < 5; x++) {
7801 snprintf(num, sizeof(num), "%d", x);
7802 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(NULL, x), mbox(NULL, x), num, 1);
7803 }
7804 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
7806
7807#ifdef DISPLAY
7808 /* Add another dot */
7809 bytes = 0;
7810 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
7811 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7813#endif
7814
7815 if (ast_adsi_end_download(chan)) {
7816 bytes = 0;
7817 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
7818 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
7819 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7820 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7822 return 0;
7823 }
7824 bytes = 0;
7825 bytes += ast_adsi_download_disconnect(buf + bytes);
7826 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7828
7829 ast_debug(1, "Done downloading scripts...\n");
7830
7831#ifdef DISPLAY
7832 /* Add last dot */
7833 bytes = 0;
7834 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
7835 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7836#endif
7837 ast_debug(1, "Restarting session...\n");
7838
7839 bytes = 0;
7840 /* Load the session now */
7841 if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
7842 *useadsi = 1;
7843 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
7844 } else
7845 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
7846
7848 return 0;
7849}
7850
7851static void adsi_begin(struct ast_channel *chan, int *useadsi)
7852{
7853 int x;
7854 if (!ast_adsi_available(chan))
7855 return;
7856 x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
7857 if (x < 0) {
7858 *useadsi = 0;
7860 return;
7861 }
7862 if (!x) {
7863 if (adsi_load_vmail(chan, useadsi)) {
7864 ast_log(AST_LOG_WARNING, "Unable to upload voicemail scripts\n");
7865 return;
7866 }
7867 } else
7868 *useadsi = 1;
7869}
7870
7871static void adsi_login(struct ast_channel *chan)
7872{
7873 unsigned char buf[256];
7874 int bytes = 0;
7875 unsigned char keys[8];
7876 int x;
7877 if (!ast_adsi_available(chan))
7878 return;
7879
7880 for (x = 0; x < 8; x++)
7881 keys[x] = 0;
7882 /* Set one key for next */
7883 keys[3] = ADSI_KEY_APPS + 3;
7884
7885 bytes += adsi_logo(buf + bytes);
7886 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
7887 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
7888 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7889 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
7890 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
7891 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
7892 bytes += ast_adsi_set_keys(buf + bytes, keys);
7893 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7895}
7896
7897static void adsi_password(struct ast_channel *chan)
7898{
7899 unsigned char buf[256];
7900 int bytes = 0;
7901 unsigned char keys[8];
7902 int x;
7903 if (!ast_adsi_available(chan))
7904 return;
7905
7906 for (x = 0; x < 8; x++)
7907 keys[x] = 0;
7908 /* Set one key for next */
7909 keys[3] = ADSI_KEY_APPS + 3;
7910
7911 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7912 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
7913 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
7914 bytes += ast_adsi_set_keys(buf + bytes, keys);
7915 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7917}
7918
7919static void adsi_folders(struct ast_channel *chan, int start, char *label)
7920{
7921 unsigned char buf[256];
7922 int bytes = 0;
7923 unsigned char keys[8];
7924 int x, y;
7925
7926 if (!ast_adsi_available(chan))
7927 return;
7928
7929 for (x = 0; x < 5; x++) {
7930 y = ADSI_KEY_APPS + 12 + start + x;
7931 if (y > ADSI_KEY_APPS + 12 + 4)
7932 y = 0;
7933 keys[x] = ADSI_KEY_SKT | y;
7934 }
7935 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
7936 keys[6] = 0;
7937 keys[7] = 0;
7938
7939 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
7940 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
7941 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
7942 bytes += ast_adsi_set_keys(buf + bytes, keys);
7943 bytes += ast_adsi_voice_mode(buf + bytes, 0);
7944
7946}
7947
7948static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
7949{
7950 int bytes = 0;
7951 unsigned char buf[256];
7952 char buf1[256], buf2[256];
7953 char fn2[PATH_MAX];
7954
7955 char cid[256] = "";
7956 char *val;
7957 char *name, *num;
7958 char datetime[21] = "";
7959 FILE *f;
7960
7961 unsigned char keys[8];
7962
7963 int x;
7964
7965 if (!ast_adsi_available(chan))
7966 return;
7967
7968 /* Retrieve important info */
7969 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
7970 f = fopen(fn2, "r");
7971 if (f) {
7972 while (!feof(f)) {
7973 if (!fgets((char *) buf, sizeof(buf), f)) {
7974 continue;
7975 }
7976 if (!feof(f)) {
7977 char *stringp = NULL;
7978 stringp = (char *) buf;
7979 strsep(&stringp, "=");
7980 val = strsep(&stringp, "=");
7981 if (!ast_strlen_zero(val)) {
7982 if (!strcmp((char *) buf, "callerid"))
7983 ast_copy_string(cid, val, sizeof(cid));
7984 if (!strcmp((char *) buf, "origdate"))
7985 ast_copy_string(datetime, val, sizeof(datetime));
7986 }
7987 }
7988 }
7989 fclose(f);
7990 }
7991 /* New meaning for keys */
7992 for (x = 0; x < 5; x++)
7993 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
7994 keys[6] = 0x0;
7995 keys[7] = 0x0;
7996
7997 if (!vms->curmsg) {
7998 /* No prev key, provide "Folder" instead */
7999 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8000 }
8001 if (vms->curmsg >= vms->lastmsg) {
8002 /* If last message ... */
8003 if (vms->curmsg) {
8004 /* but not only message, provide "Folder" instead */
8005 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8006 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8007
8008 } else {
8009 /* Otherwise if only message, leave blank */
8010 keys[3] = 1;
8011 }
8012 }
8013
8014 if (!ast_strlen_zero(cid)) {
8015 ast_callerid_parse(cid, &name, &num);
8016 if (!name)
8017 name = num;
8018 } else {
8019 name = "Unknown Caller";
8020 }
8021
8022 /* If deleted, show "undeleted" */
8023#ifdef IMAP_STORAGE
8024 ast_mutex_lock(&vms->lock);
8025#endif
8026 if (vms->deleted[vms->curmsg]) {
8027 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
8028 }
8029#ifdef IMAP_STORAGE
8030 ast_mutex_unlock(&vms->lock);
8031#endif
8032
8033 /* Except "Exit" */
8034 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
8035 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
8036 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
8037 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
8038
8039 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8040 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8041 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
8042 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
8043 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8044 bytes += ast_adsi_set_keys(buf + bytes, keys);
8045 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8046
8048}
8049
8050static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
8051{
8052 int bytes = 0;
8053 unsigned char buf[256];
8054 unsigned char keys[8];
8055
8056 int x;
8057
8058 if (!ast_adsi_available(chan))
8059 return;
8060
8061 /* New meaning for keys */
8062 for (x = 0; x < 5; x++)
8063 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
8064
8065 keys[6] = 0x0;
8066 keys[7] = 0x0;
8067
8068 if (!vms->curmsg) {
8069 /* No prev key, provide "Folder" instead */
8070 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8071 }
8072 if (vms->curmsg >= vms->lastmsg) {
8073 /* If last message ... */
8074 if (vms->curmsg) {
8075 /* but not only message, provide "Folder" instead */
8076 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
8077 } else {
8078 /* Otherwise if only message, leave blank */
8079 keys[3] = 1;
8080 }
8081 }
8082
8083 /* If deleted, show "undeleted" */
8084#ifdef IMAP_STORAGE
8085 ast_mutex_lock(&vms->lock);
8086#endif
8087 if (vms->deleted[vms->curmsg]) {
8088 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
8089 }
8090#ifdef IMAP_STORAGE
8091 ast_mutex_unlock(&vms->lock);
8092#endif
8093
8094 /* Except "Exit" */
8095 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
8096 bytes += ast_adsi_set_keys(buf + bytes, keys);
8097 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8098
8100}
8101
8102static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
8103{
8104 unsigned char buf[256] = "";
8105 char buf1[256] = "", buf2[256] = "";
8106 int bytes = 0;
8107 unsigned char keys[8];
8108 int x;
8109
8110 char *newm = (vms->newmessages == 1) ? "message" : "messages";
8111 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
8112 if (!ast_adsi_available(chan))
8113 return;
8114 if (vms->newmessages) {
8115 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
8116 if (vms->oldmessages) {
8117 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
8118 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
8119 } else {
8120 snprintf(buf2, sizeof(buf2), "%s.", newm);
8121 }
8122 } else if (vms->oldmessages) {
8123 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
8124 snprintf(buf2, sizeof(buf2), "%s.", oldm);
8125 } else {
8126 strcpy(buf1, "You have no messages.");
8127 buf2[0] = ' ';
8128 buf2[1] = '\0';
8129 }
8130 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8131 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8132 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8133
8134 for (x = 0; x < 6; x++)
8135 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
8136 keys[6] = 0;
8137 keys[7] = 0;
8138
8139 /* Don't let them listen if there are none */
8140 if (vms->lastmsg < 0)
8141 keys[0] = 1;
8142 bytes += ast_adsi_set_keys(buf + bytes, keys);
8143
8144 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8145
8147}
8148
8149static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
8150{
8151 unsigned char buf[256] = "";
8152 char buf1[256] = "", buf2[256] = "";
8153 int bytes = 0;
8154 unsigned char keys[8];
8155 int x;
8156
8157 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
8158
8159 if (!ast_adsi_available(chan))
8160 return;
8161
8162 /* Original command keys */
8163 for (x = 0; x < 6; x++)
8164 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
8165
8166 keys[6] = 0;
8167 keys[7] = 0;
8168
8169 if ((vms->lastmsg + 1) < 1)
8170 keys[0] = 0;
8171
8172 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
8173 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
8174
8175 if (vms->lastmsg + 1)
8176 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
8177 else
8178 strcpy(buf2, "no messages.");
8179 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
8180 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
8181 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
8182 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8183 bytes += ast_adsi_set_keys(buf + bytes, keys);
8184
8185 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8186
8188
8189}
8190
8191/*
8192static void adsi_clear(struct ast_channel *chan)
8193{
8194 char buf[256];
8195 int bytes=0;
8196 if (!ast_adsi_available(chan))
8197 return;
8198 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8199 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8200
8201 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
8202}
8203*/
8204
8205static void adsi_goodbye(struct ast_channel *chan)
8206{
8207 unsigned char buf[256];
8208 int bytes = 0;
8209
8210 if (!ast_adsi_available(chan))
8211 return;
8212 bytes += adsi_logo(buf + bytes);
8213 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
8214 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
8215 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
8216 bytes += ast_adsi_voice_mode(buf + bytes, 0);
8217
8219}
8220
8221/*!\brief get_folder: Folder menu
8222 * Plays "press 1 for INBOX messages" etc.
8223 * Should possibly be internationalized
8224 */
8225static int get_folder(struct ast_channel *chan, int start)
8226{
8227 int x;
8228 int d;
8229 char fn[PATH_MAX];
8230 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
8231 if (d)
8232 return d;
8233 for (x = start; x < 5; x++) { /* For all folders */
8234 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), NULL)))
8235 return d;
8236 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
8237 if (d)
8238 return d;
8239 snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
8240
8241 /* The inbox folder can have its name changed under certain conditions
8242 * so this checks if the sound file exists for the inbox folder name and
8243 * if it doesn't, plays the default name instead. */
8244 if (x == 0) {
8245 if (ast_fileexists(fn, NULL, NULL)) {
8246 d = vm_play_folder_name(chan, fn);
8247 } else {
8248 ast_verb(4, "Failed to find file %s; falling back to INBOX\n", fn);
8249 d = vm_play_folder_name(chan, "vm-INBOX");
8250 }
8251 } else {
8252 ast_test_suite_event_notify("PLAYBACK", "Message: folder name %s", fn);
8253 d = vm_play_folder_name(chan, fn);
8254 }
8255
8256 if (d)
8257 return d;
8258 d = ast_waitfordigit(chan, 500);
8259 if (d)
8260 return d;
8261 }
8262
8263 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
8264 if (d)
8265 return d;
8266 d = ast_waitfordigit(chan, 4000);
8267 return d;
8268}
8269
8270/* Japanese Syntax */
8271static int get_folder_ja(struct ast_channel *chan, int start)
8272{
8273 int x;
8274 int d;
8275 char fn[256];
8276 for (x = start; x < 5; x++) { /* For all folders */
8277 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, ast_channel_language(chan), (char *) NULL))) {
8278 return d;
8279 }
8280 snprintf(fn, sizeof(fn), "vm-%s", mbox(NULL, x)); /* Folder name */
8281 d = vm_play_folder_name(chan, fn);
8282 if (d) {
8283 return d;
8284 }
8285 d = ast_waitfordigit(chan, 500);
8286 if (d) {
8287 return d;
8288 }
8289 }
8290 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
8291 if (d) {
8292 return d;
8293 }
8294 d = ast_waitfordigit(chan, 4000);
8295 return d;
8296}
8297
8298/*!
8299 * \brief plays a prompt and waits for a keypress.
8300 * \param chan
8301 * \param fn the name of the voice prompt file to be played. For example, 'vm-changeto', 'vm-savefolder'
8302 * \param start Does not appear to be used at this time.
8303 *
8304 * This is used by the main menu option to move a message to a folder or to save a message into a folder.
8305 * After playing the message identified by the fn parameter value, it calls get_folder(), which plays the
8306 * prompting for the number inputs that correspond to the available folders.
8307 *
8308 * \return zero on success, or -1 on error.
8309 */
8310static int get_folder2(struct ast_channel *chan, char *fn, int start)
8311{
8312 int res = 0;
8313 int loops = 0;
8314
8315 res = ast_play_and_wait(chan, fn); /* Folder name */
8316 while (((res < '0') || (res > '9')) &&
8317 (res != '#') && (res >= 0) &&
8318 loops < 4) {
8319 /* res = get_folder(chan, 0); */
8320 if (!strcasecmp(ast_channel_language(chan), "ja")) { /* Japanese syntax */
8321 res = get_folder_ja(chan, 0);
8322 } else { /* Default syntax */
8323 res = get_folder(chan, 0);
8324 }
8325 loops++;
8326 }
8327 if (loops == 4) { /* give up */
8328 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", '#', '#');
8329 return '#';
8330 }
8331 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8332 isprint(res) ? res : '?', isprint(res) ? res : '?');
8333 return res;
8334}
8335
8336/*!
8337 * \brief presents the option to prepend to an existing message when forwarding it.
8338 * \param chan
8339 * \param vmu
8340 * \param curdir
8341 * \param curmsg
8342 * \param vm_fmts
8343 * \param context
8344 * \param record_gain
8345 * \param duration
8346 * \param vms
8347 * \param flag
8348 *
8349 * Presents a prompt for 1 to prepend the current message, 2 to forward the message without prepending, or * to return to the main menu.
8350 *
8351 * This is invoked from forward_message() when performing a forward operation (option 8 from main menu).
8352 * \return zero on success, -1 on error.
8353 */
8354static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir,
8355 int curmsg, char *vm_fmts, char *context, signed char record_gain, long *duration,
8356 struct vm_state *vms, char *flag)
8357{
8358 int cmd = 0;
8359 int retries = 0, prepend_duration = 0, already_recorded = 0;
8360 char msgfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
8361 char textfile[PATH_MAX];
8362 struct ast_config *msg_cfg;
8363 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8364#ifndef IMAP_STORAGE
8365 signed char zero_gain = 0;
8366#else
8367 const char *msg_id = NULL;
8368#endif
8369 const char *duration_str;
8370 SCOPE_ENTER(3, "mbox: %s msgnum: %d curdir: %s", vmu->mailbox, curmsg, curdir);
8371
8372 /* Must always populate duration correctly */
8373 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
8374 ast_trace(-1, "msgfile: %s\n", msgfile);
8375 strcpy(textfile, msgfile);
8376 strcpy(backup, msgfile);
8377 strcpy(backup_textfile, msgfile);
8378 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
8379 strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
8380 strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
8381
8382 if ((msg_cfg = ast_config_load(textfile, config_flags)) && valid_config(msg_cfg) && (duration_str = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
8383 *duration = atoi(duration_str);
8384 } else {
8385 *duration = 0;
8386 }
8387
8388 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
8389 if (cmd)
8390 retries = 0;
8391 switch (cmd) {
8392 case '1':
8393
8394#ifdef IMAP_STORAGE
8395 /* Record new intro file */
8396 if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
8397 msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
8398 }
8399 make_file(vms->introfn, sizeof(vms->introfn), curdir, curmsg);
8400 strncat(vms->introfn, "intro", sizeof(vms->introfn));
8401 ast_play_and_wait(chan, "vm-record-prepend");
8402 ast_play_and_wait(chan, "beep");
8403 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);
8404 if (cmd == -1) {
8405 break;
8406 }
8407 cmd = 't';
8408#else
8409
8410 /* prepend a message to the current message, update the metadata and return */
8411 ast_trace(-1, "Prepending to message %d\n", curmsg);
8412
8413 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
8414 ast_trace(-1, "msgfile: %s\n", msgfile);
8415
8416 strcpy(textfile, msgfile);
8417 strncat(textfile, ".txt", sizeof(textfile) - 1);
8418 *duration = 0;
8419
8420 /* if we can't read the message metadata, stop now */
8421 if (!valid_config(msg_cfg)) {
8422 cmd = 0;
8423 break;
8424 }
8425
8426 /* Back up the original file, so we can retry the prepend and restore it after forward. */
8427#ifndef IMAP_STORAGE
8428 if (already_recorded) {
8429 ast_trace(-1, "Restoring '%s' to '%s'\n", backup, msgfile);
8430 ast_filecopy(backup, msgfile, NULL);
8431 copy(backup_textfile, textfile);
8432 }
8433 else {
8434 ast_trace(-1, "Backing up '%s' to '%s'\n", backup, msgfile);
8435 ast_filecopy(msgfile, backup, NULL);
8436 copy(textfile, backup_textfile);
8437 }
8438#endif
8439 already_recorded = 1;
8440
8441 if (record_gain)
8442 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
8443
8444 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, ast_play_and_prepend, chan, NULL, msgfile, 0, vm_fmts, &prepend_duration, NULL, 1, silencethreshold, maxsilence);
8445
8446 if (cmd == 'S') { /* If we timed out, tell the user it didn't work properly and clean up the files */
8447 ast_stream_and_wait(chan, vm_pls_try_again, ""); /* this might be removed if a proper vm_prepend_timeout is ever recorded */
8449 ast_filerename(backup, msgfile, NULL);
8450 }
8451
8452 if (record_gain)
8453 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
8454
8455
8456 if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
8457 *duration = atoi(duration_str);
8458
8459 if (prepend_duration) {
8460 struct ast_category *msg_cat;
8461 /* need enough space for a maximum-length message duration */
8462 char duration_buf[12];
8463
8464 *duration += prepend_duration;
8465 ast_trace(-1, "Prepending duration: %d total duration: %ld\n", prepend_duration, *duration);
8466 msg_cat = ast_category_get(msg_cfg, "message", NULL);
8467 snprintf(duration_buf, sizeof(duration_buf), "%ld", *duration);
8468 if (!ast_variable_update(msg_cat, "duration", duration_buf, NULL, 0)) {
8469 ast_config_text_file_save(textfile, msg_cfg, "app_voicemail");
8470 }
8471 }
8472
8473#endif
8474 break;
8475 case '2':
8476 /* NULL out introfile so we know there is no intro! */
8477#ifdef IMAP_STORAGE
8478 *vms->introfn = '\0';
8479#endif
8480 cmd = 't';
8481 break;
8482 case '*':
8483 cmd = '*';
8484 break;
8485 default:
8486 /* If time_out and return to menu, reset already_recorded */
8487 already_recorded = 0;
8488
8489 cmd = ast_play_and_wait(chan, "vm-forwardoptions");
8490 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
8491 if (!cmd) {
8492 cmd = ast_play_and_wait(chan, "vm-starmain");
8493 /* "press star to return to the main menu" */
8494 }
8495 if (!cmd) {
8496 cmd = ast_waitfordigit(chan, 6000);
8497 }
8498 if (!cmd) {
8499 retries++;
8500 }
8501 if (retries > 3) {
8502 cmd = '*'; /* Let's cancel this beast */
8503 }
8504 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8505 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
8506 }
8507 }
8508
8509 if (valid_config(msg_cfg))
8510 ast_config_destroy(msg_cfg);
8511 if (prepend_duration)
8512 *duration = prepend_duration;
8513
8514 if (already_recorded && cmd == -1) {
8515 /* restore original message if prepention cancelled */
8516 ast_trace(-1, "Restoring '%s' to '%s'\n", backup, msgfile);
8517 ast_filerename(backup, msgfile, NULL);
8518 rename(backup_textfile, textfile);
8519 }
8520
8521 if (cmd == 't' || cmd == 'S') { /* XXX entering this block with a value of 'S' is probably no longer possible. */
8522 cmd = 0;
8523 }
8524 SCOPE_EXIT_RTN_VALUE(cmd, "Done. CMD: %d %c\n", cmd, cmd >= 32 && cmd < 127 ? cmd : '?');
8525}
8526
8527static void queue_mwi_event(const char *channel_id, const char *box, int urgent, int new, int old)
8528{
8529 char *mailbox;
8530 char *context;
8531
8533 return;
8534 }
8535
8536 ast_debug(3, "Queueing event for mailbox %s New: %d Old: %d\n", box, new + urgent, old);
8537 ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
8538
8540 struct ao2_iterator *aliases;
8541 struct mailbox_alias_mapping *mapping;
8542
8544 while ((mapping = ao2_iterator_next(aliases))) {
8545 char alias[strlen(mapping->alias) + 1];
8546 strcpy(alias, mapping->alias); /* safe */
8547 mailbox = NULL;
8548 context = NULL;
8549 ast_debug(3, "Found alias mapping: %s -> %s\n", mapping->alias, box);
8551 ast_publish_mwi_state_channel(mailbox, context, new + urgent, old, channel_id);
8552 ao2_ref(mapping, -1);
8553 }
8555 }
8556}
8557
8558/*!
8559 * \brief Sends email notification that a user has a new voicemail waiting for them.
8560 * \param chan
8561 * \param vmu
8562 * \param vms
8563 * \param msgnum
8564 * \param duration
8565 * \param fmt
8566 * \param cidnum The Caller ID phone number value.
8567 * \param cidname The Caller ID name value.
8568 * \param flag
8569 *
8570 * \return zero on success, -1 on error.
8571 */
8572static 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)
8573{
8574 char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
8575 int newmsgs = 0, oldmsgs = 0, urgentmsgs = 0;
8576 const char *category;
8577 char *myserveremail = serveremail;
8578
8579 ast_channel_lock(chan);
8580 if ((category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY"))) {
8581 category = ast_strdupa(category);
8582 }
8583 ast_channel_unlock(chan);
8584
8585#ifndef IMAP_STORAGE
8586 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, !ast_strlen_zero(flag) && !strcmp(flag, "Urgent") ? "Urgent" : "INBOX");
8587#else
8588 snprintf(todir, sizeof(todir), "%simap", VM_SPOOL_DIR);
8589#endif
8590 make_file(fn, sizeof(fn), todir, msgnum);
8591 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
8592
8593 if (!ast_strlen_zero(vmu->attachfmt)) {
8594 if (strstr(fmt, vmu->attachfmt))
8595 fmt = vmu->attachfmt;
8596 else
8597 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);
8598 }
8599
8600 /* Attach only the first format */
8601 fmt = ast_strdupa(fmt);
8602 stringp = fmt;
8603 strsep(&stringp, "|");
8604
8605 if (!ast_strlen_zero(vmu->serveremail))
8606 myserveremail = vmu->serveremail;
8607
8608 if (!ast_strlen_zero(vmu->email)) {
8609 int attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
8610 char *msg_id = NULL;
8611#ifdef IMAP_STORAGE
8612 struct ast_config *msg_cfg;
8613 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8614 char filename[PATH_MAX];
8615
8616 snprintf(filename, sizeof(filename), "%s.txt", fn);
8617 msg_cfg = ast_config_load(filename, config_flags);
8618 if (msg_cfg && msg_cfg != CONFIG_STATUS_FILEINVALID) {
8619 msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
8620 ast_config_destroy(msg_cfg);
8621 }
8622#endif
8623
8624 if (attach_user_voicemail)
8625 RETRIEVE(todir, msgnum, vmu->mailbox, vmu->context);
8626
8627 /* XXX possible imap issue, should category be NULL XXX */
8628 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);
8629
8630 if (attach_user_voicemail)
8631 DISPOSE(todir, msgnum);
8632 }
8633
8634 if (!ast_strlen_zero(vmu->pager)) {
8635 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, mbox(vmu, 0), cidnum, cidname, duration, vmu, category, flag);
8636 }
8637
8638 if (ast_test_flag(vmu, VM_DELETE))
8639 DELETE(todir, msgnum, fn, vmu);
8640
8641 /* Leave voicemail for someone */
8642 if (ast_app_has_voicemail(ext_context, NULL))
8643 ast_app_inboxcount2(ext_context, &urgentmsgs, &newmsgs, &oldmsgs);
8644
8645 queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgentmsgs, newmsgs, oldmsgs);
8646 run_externnotify(vmu->context, vmu->mailbox, flag);
8647
8648#ifdef IMAP_STORAGE
8649 vm_delete(fn); /* Delete the file, but not the IMAP message */
8650 if (ast_test_flag(vmu, VM_DELETE)) { /* Delete the IMAP message if delete = yes */
8651 vm_imap_delete(NULL, vms->curmsg, vmu);
8652 vms->newmessages--; /* Fix new message count */
8653 }
8654#endif
8655
8656 return 0;
8657}
8658
8659/*!
8660 * \brief Sends a voicemail message to a mailbox recipient.
8661 * \param chan
8662 * \param context
8663 * \param vms
8664 * \param sender
8665 * \param fmt
8666 * \param is_new_message Used to indicate the mode for which this method was invoked.
8667 * Will be 0 when called to forward an existing message (option 8)
8668 * Will be 1 when called to leave a message (option 3->5)
8669 * \param record_gain
8670 * \param urgent
8671 *
8672 * Reads the destination mailbox(es) from keypad input for CID, or if use_directory feature is enabled, the Directory.
8673 *
8674 * When in the leave message mode (is_new_message == 1):
8675 * - allow the leaving of a message for ourselves. (Will not allow us to forward a message to ourselves, when is_new_message == 0).
8676 * - attempt to determine the context and mailbox, and then invoke leave_message() function to record and store the message.
8677 *
8678 * When in the forward message mode (is_new_message == 0):
8679 * - retrieves the current message to be forwarded
8680 * - copies the original message to a temporary file, so updates to the envelope can be done.
8681 * - determines the target mailbox and folders
8682 * - copies the message into the target mailbox, using copy_message() or by generating the message into an email attachment if using imap folders.
8683 *
8684 * \return zero on success, -1 on error.
8685 */
8686static 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)
8687{
8688#ifdef IMAP_STORAGE
8689 int todircount = 0;
8690 struct vm_state *dstvms;
8691#endif
8692 char username[70]="";
8693 char fn[PATH_MAX]; /* for playback of name greeting */
8694 char ecodes[16] = "#";
8695 int res = 0, cmd = 0;
8696 struct ast_vm_user *receiver = NULL, *vmtmp;
8698 char *stringp;
8699 const char *s;
8700 const char mailbox_context[256];
8701 int saved_messages = 0;
8702 int valid_extensions = 0;
8703 char *dir;
8704 int curmsg;
8705 char urgent_str[7] = "";
8706 int prompt_played = 0;
8707#ifndef IMAP_STORAGE
8708 char msgfile[PATH_MAX], textfile[PATH_MAX], backup[PATH_MAX], backup_textfile[PATH_MAX];
8709#endif
8710 SCOPE_ENTER(3, "%s: user: %s dir: %s msg: %d\n", ast_channel_name(chan),
8711 vms->username, vms->curdir, vms->curmsg);
8712
8714 ast_copy_string(urgent_str, urgent ? "Urgent" : "", sizeof(urgent_str));
8715 }
8716
8717 if (vms == NULL) {
8718 SCOPE_EXIT_RTN_VALUE(-1, "vms is NULL\n");
8719 }
8720 dir = vms->curdir;
8721 curmsg = vms->curmsg;
8722
8723 ast_test_suite_event_notify("FORWARD", "Message: entering forward message menu");
8724 while (!res && !valid_extensions) {
8725 int use_directory = 0;
8727 int done = 0;
8728 int retries = 0;
8729 cmd = 0;
8730 while ((cmd >= 0) && !done ){
8731 if (cmd)
8732 retries = 0;
8733 switch (cmd) {
8734 case '1':
8735 use_directory = 0;
8736 done = 1;
8737 break;
8738 case '2':
8739 use_directory = 1;
8740 done = 1;
8741 break;
8742 case '*':
8743 cmd = 't';
8744 done = 1;
8745 break;
8746 default:
8747 /* Press 1 to enter an extension press 2 to use the directory */
8748 cmd = ast_play_and_wait(chan, "vm-forward");
8749 if (!cmd) {
8750 cmd = ast_waitfordigit(chan, 3000);
8751 }
8752 if (!cmd) {
8753 retries++;
8754 }
8755 if (retries > 3) {
8756 cmd = 't';
8757 done = 1;
8758 }
8759 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
8760 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
8761 }
8762 }
8763 if (cmd < 0 || cmd == 't')
8764 break;
8765 }
8766
8767 if (use_directory) {
8768 /* use app_directory */
8769
8770 struct ast_app* directory_app;
8771
8772 directory_app = pbx_findapp("Directory");
8773 if (directory_app) {
8774 char vmcontext[256];
8775 char old_context[strlen(ast_channel_context(chan)) + 1];
8776 char old_exten[strlen(ast_channel_exten(chan)) + 1];
8777 int old_priority;
8778 /* make backup copies */
8779 strcpy(old_context, ast_channel_context(chan)); /* safe */
8780 strcpy(old_exten, ast_channel_exten(chan)); /* safe */
8781 old_priority = ast_channel_priority(chan);
8782
8783 /* call the Directory, changes the channel */
8784 snprintf(vmcontext, sizeof(vmcontext), "%s,,v", context ? context : "default");
8785 res = pbx_exec(chan, directory_app, vmcontext);
8786
8787 ast_copy_string(username, ast_channel_exten(chan), sizeof(username));
8788
8789 /* restore the old context, exten, and priority */
8790 ast_channel_context_set(chan, old_context);
8791 ast_channel_exten_set(chan, old_exten);
8792 ast_channel_priority_set(chan, old_priority);
8793 } else {
8794 ast_log(AST_LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
8796 }
8797 } else {
8798 /* Ask for an extension */
8799 res = ast_streamfile(chan, "vm-extension", ast_channel_language(chan)); /* "extension" */
8800 prompt_played++;
8801 if (res || prompt_played > 4)
8802 break;
8803 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#")) < 0)
8804 break;
8805 }
8806
8807 /* start all over if no username */
8808 if (ast_strlen_zero(username))
8809 continue;
8810 stringp = username;
8811 s = strsep(&stringp, "*");
8812 /* start optimistic */
8813 valid_extensions = 1;
8814 while (s) {
8815 snprintf((char*)mailbox_context, sizeof(mailbox_context), "%s@%s", s, context ? context : "default");
8816 if ((is_new_message == 1 || strcmp(s, sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
8817 int oldmsgs;
8818 int newmsgs;
8819 int capacity;
8820
8821 if (inboxcount(mailbox_context, &newmsgs, &oldmsgs)) {
8822 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", mailbox_context);
8823 /* Shouldn't happen, but allow trying another extension if it does */
8824 res = ast_play_and_wait(chan, "pbx-invalid");
8825 valid_extensions = 0;
8826 break;
8827 }
8828#ifdef IMAP_STORAGE
8829 if (!(dstvms = get_vm_state_by_mailbox(s, context, 0))) {
8830 if (!(dstvms = create_vm_state_from_user(receiver))) {
8831 ast_log(AST_LOG_ERROR, "Couldn't allocate necessary space\n");
8832 /* Shouldn't happen, but allow trying another extension if it does */
8833 res = ast_play_and_wait(chan, "pbx-invalid");
8834 valid_extensions = 0;
8835 break;
8836 }
8837 }
8838 check_quota(dstvms, imapfolder);
8839 if (dstvms->quota_limit && dstvms->quota_usage >= dstvms->quota_limit) {
8840 ast_log(LOG_NOTICE, "Mailbox '%s' is exceeded quota %u >= %u\n", mailbox_context, dstvms->quota_usage, dstvms->quota_limit);
8841 res = ast_play_and_wait(chan, "vm-mailboxfull");
8842 valid_extensions = 0;
8843 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8844 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8845 free_user(vmtmp);
8846 }
8847 break;
8848 }
8849#endif
8850 capacity = receiver->maxmsg - inprocess_count(receiver->mailbox, receiver->context, +1);
8851 if ((newmsgs + oldmsgs) >= capacity) {
8852 ast_log(LOG_NOTICE, "Mailbox '%s' is full with capacity of %d, prompting for another extension.\n", mailbox_context, capacity);
8853 res = ast_play_and_wait(chan, "vm-mailboxfull");
8854 valid_extensions = 0;
8855 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8856 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8857 free_user(vmtmp);
8858 }
8859 inprocess_count(receiver->mailbox, receiver->context, -1);
8860 break;
8861 }
8862 AST_LIST_INSERT_HEAD(&extensions, receiver, list);
8863 } else {
8864 /* XXX Optimization for the future. When we encounter a single bad extension,
8865 * bailing out on all of the extensions may not be the way to go. We should
8866 * probably just bail on that single extension, then allow the user to enter
8867 * several more. XXX
8868 */
8869 while ((receiver = AST_LIST_REMOVE_HEAD(&extensions, list))) {
8870 free_user(receiver);
8871 }
8872 ast_log(LOG_NOTICE, "'%s' is not a valid mailbox\n", mailbox_context);
8873 /* "I am sorry, that's not a valid extension. Please try again." */
8874 res = ast_play_and_wait(chan, "pbx-invalid");
8875 valid_extensions = 0;
8876 break;
8877 }
8878
8879 /* play name if available, else play extension number */
8880 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, receiver->context, s);
8881 SCOPE_CALL(-1, RETRIEVE, fn, -1, s, receiver->context);
8882 if (ast_fileexists(fn, NULL, NULL) > 0) {
8883 res = ast_stream_and_wait(chan, fn, ecodes);
8884 if (res) {
8885 SCOPE_CALL(-1, DISPOSE, fn, -1);
8886 return res;
8887 }
8888 } else {
8889 res = ast_say_digit_str(chan, s, ecodes, ast_channel_language(chan));
8890 }
8891 SCOPE_CALL(-1, DISPOSE, fn, -1);
8892
8893 s = strsep(&stringp, "*");
8894 }
8895 /* break from the loop of reading the extensions */
8896 if (valid_extensions)
8897 break;
8898 }
8899 /* check if we're clear to proceed */
8900 if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
8901 return res;
8902 if (is_new_message == 1) {
8903 struct leave_vm_options leave_options;
8904 char mailbox[AST_MAX_EXTENSION * 2 + 2];
8905 snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
8906
8907 /* Send VoiceMail */
8908 memset(&leave_options, 0, sizeof(leave_options));
8909 leave_options.record_gain = record_gain;
8910 leave_options.beeptone = "beep";
8911 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, leave_voicemail, chan, mailbox, &leave_options);
8912 } else {
8913 /* Forward VoiceMail */
8914 long duration = 0;
8915 struct vm_state vmstmp;
8916 int copy_msg_result = 0;
8917#ifdef IMAP_STORAGE
8918 char filename[PATH_MAX];
8919 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
8920 const char *msg_id = NULL;
8921 struct ast_config *msg_cfg;
8922#endif
8923 memcpy(&vmstmp, vms, sizeof(vmstmp));
8924
8925 SCOPE_CALL(-1, RETRIEVE, dir, curmsg, sender->mailbox, sender->context);
8926#ifdef IMAP_STORAGE
8927 make_file(filename, sizeof(filename), dir, curmsg);
8928 strncat(filename, ".txt", sizeof(filename) - strlen(filename) - 1);
8929 msg_cfg = ast_config_load(filename, config_flags);
8930 if (msg_cfg && msg_cfg == CONFIG_STATUS_FILEINVALID) {
8931 msg_id = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "msg_id"));
8932 ast_config_destroy(msg_cfg);
8933 }
8934#endif
8935
8936 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);
8937 if (!cmd) {
8938 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
8939#ifdef IMAP_STORAGE
8940 int attach_user_voicemail;
8941 char *myserveremail = serveremail;
8942
8943 /* get destination mailbox */
8944 dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
8945 if (!dstvms) {
8946 dstvms = create_vm_state_from_user(vmtmp);
8947 }
8948 if (dstvms) {
8949 init_mailstream(dstvms, 0);
8950 if (!dstvms->mailstream) {
8951 ast_log(AST_LOG_ERROR, "IMAP mailstream for %s is NULL\n", vmtmp->mailbox);
8952 } else {
8953 copy_msg_result = STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, curmsg, chan, vmtmp, fmt, duration, dstvms, urgent_str, msg_id);
8954 run_externnotify(vmtmp->context, vmtmp->mailbox, urgent_str);
8955 }
8956 } else {
8957 ast_log(AST_LOG_ERROR, "Could not find state information for mailbox %s\n", vmtmp->mailbox);
8958 }
8959 if (!ast_strlen_zero(vmtmp->serveremail))
8960 myserveremail = vmtmp->serveremail;
8961 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
8962 /* NULL category for IMAP storage */
8963 sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox,
8964 dstvms->curbox,
8965 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL),
8966 S_COR(ast_channel_caller(chan)->id.name.valid, ast_channel_caller(chan)->id.name.str, NULL),
8967 vmstmp.fn, vmstmp.introfn, fmt, duration, attach_user_voicemail, chan,
8968 NULL, urgent_str, msg_id);
8969#else
8970 copy_msg_result = SCOPE_CALL_WITH_INT_RESULT(-1, copy_message, chan, sender, 0, curmsg, duration, vmtmp, fmt, dir, urgent_str, NULL);
8971#endif
8972 saved_messages++;
8974 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
8975 free_user(vmtmp);
8976 if (res)
8977 break;
8978 }
8980 if (saved_messages > 0 && !copy_msg_result) {
8981 /* give confirmation that the message was saved */
8982 /* commented out since we can't forward batches yet
8983 if (saved_messages == 1)
8984 res = ast_play_and_wait(chan, "vm-message");
8985 else
8986 res = ast_play_and_wait(chan, "vm-messages");
8987 if (!res)
8988 res = ast_play_and_wait(chan, "vm-saved"); */
8989 res = ast_play_and_wait(chan, "vm-msgforwarded");
8990 }
8991#ifndef IMAP_STORAGE
8992 else {
8993 /* with IMAP, mailbox full warning played by imap_check_limits */
8994 res = ast_play_and_wait(chan, "vm-mailboxfull");
8995 }
8996 /* Restore original message without prepended message if backup exists */
8997 make_file(msgfile, sizeof(msgfile), dir, curmsg);
8998 strcpy(textfile, msgfile);
8999 strcpy(backup, msgfile);
9000 strcpy(backup_textfile, msgfile);
9001 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
9002 strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
9003 strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
9004 if (ast_fileexists(backup, NULL, NULL) > 0) {
9005 ast_filerename(backup, msgfile, NULL);
9006 rename(backup_textfile, textfile);
9007 }
9008#endif
9009 }
9010 SCOPE_CALL(-1, DISPOSE, dir, curmsg);
9011#ifndef IMAP_STORAGE
9012 if (cmd) { /* assuming hangup, cleanup backup file */
9013 make_file(msgfile, sizeof(msgfile), dir, curmsg);
9014 strcpy(textfile, msgfile);
9015 strcpy(backup_textfile, msgfile);
9016 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
9017 strncat(backup_textfile, "-bak.txt", sizeof(backup_textfile) - strlen(backup_textfile) - 1);
9018 rename(backup_textfile, textfile);
9019 }
9020#endif
9021 }
9022
9023 /* If anything failed above, we still have this list to free */
9024 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list))) {
9025 inprocess_count(vmtmp->mailbox, vmtmp->context, -1);
9026 free_user(vmtmp);
9027 }
9028 SCOPE_EXIT_RTN_VALUE(res ? res : cmd, "Done. res: %d cmd: %d\n", res, cmd);
9029}
9030
9031static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
9032{
9033 int res;
9034 if ((res = ast_stream_and_wait(chan, file, AST_DIGIT_ANY)) < 0)
9035 ast_log(AST_LOG_WARNING, "Unable to play message %s\n", file);
9036 return res;
9037}
9038
9039static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
9040{
9041 ast_test_suite_event_notify("PLAYVOICE", "Message: Playing %s", file);
9043}
9044
9045static int play_message_category(struct ast_channel *chan, const char *category)
9046{
9047 int res = 0;
9048
9049 if (!ast_strlen_zero(category))
9050 res = ast_play_and_wait(chan, category);
9051
9052 if (res) {
9053 ast_log(AST_LOG_WARNING, "No sound file for category '%s' was found.\n", category);
9054 res = 0;
9055 }
9056
9057 return res;
9058}
9059
9060static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
9061{
9062 int res = 0;
9063 struct vm_zone *the_zone = NULL;
9064 time_t t;
9065
9066 if (ast_get_time_t(origtime, &t, 0, NULL)) {
9067 ast_log(AST_LOG_WARNING, "Couldn't find origtime in %s\n", filename);
9068 return 0;
9069 }
9070
9071 /* Does this user have a timezone specified? */
9072 if (!ast_strlen_zero(vmu->zonetag)) {
9073 /* Find the zone in the list */
9074 struct vm_zone *z;
9077 if (!strcmp(z->name, vmu->zonetag)) {
9078 the_zone = z;
9079 break;
9080 }
9081 }
9083 }
9084
9085/* No internal variable parsing for now, so we'll comment it out for the time being */
9086#if 0
9087 /* Set the DIFF_* variables */
9088 ast_localtime(&t, &time_now, NULL);
9089 tv_now = ast_tvnow();
9090 ast_localtime(&tv_now, &time_then, NULL);
9091
9092 /* Day difference */
9093 if (time_now.tm_year == time_then.tm_year)
9094 snprintf(temp, sizeof(temp), "%d", time_now.tm_yday);
9095 else
9096 snprintf(temp, sizeof(temp), "%d", (time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
9097 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
9098
9099 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
9100#endif
9101 if (the_zone) {
9102 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), the_zone->msg_format, the_zone->timezone);
9103 } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
9104 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9105 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
9106 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q H 'digits/kai' M ", NULL);
9107 } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */
9108 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9109 } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
9110 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);
9111 } else if (!strcasecmp(ast_channel_language(chan),"ja")) { /* Japanese syntax */
9112 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "PHM q 'jp-ni' 'vm-received'", NULL);
9113 } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
9114 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/nl-om' HM", NULL);
9115 } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
9116 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q 'digits/at' HM", NULL);
9117 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
9118 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' Q HM", NULL);
9119 } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* Brazilian PORTUGUESE syntax */
9120 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);
9121 } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
9122 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' dB 'digits/at' k 'and' M", NULL);
9123 } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
9124 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "qR 'vm-received'", NULL);
9125 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
9126 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);
9127 } else {
9128 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, ast_channel_language(chan), "'vm-received' q 'digits/at' IMp", NULL);
9129 }
9130#if 0
9131 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
9132#endif
9133 return res;
9134}
9135
9136
9137
9138static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback, int saycidnumber)
9139{
9140 int res = 0;
9141 int i;
9142 char *callerid, *name;
9143 char prefile[PATH_MAX] = "";
9144
9145 /* If voicemail cid is not enabled, or we didn't get cid or context from
9146 * the attribute file, leave now.
9147 *
9148 * TODO Still need to change this so that if this function is called by the
9149 * message envelope (and someone is explicitly requesting to hear the CID),
9150 * it does not check to see if CID is enabled in the config file.
9151 */
9152 if ((cid == NULL)||(context == NULL))
9153 return res;
9154
9155 /* Strip off caller ID number from name */
9156 ast_debug(1, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
9157 ast_callerid_parse(cid, &name, &callerid);
9158 if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
9159 /* Check for internal contexts and only */
9160 /* say extension when the call didn't come from an internal context in the list */
9161 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
9162 ast_debug(1, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
9163 if ((strcmp(cidinternalcontexts[i], context) == 0))
9164 break;
9165 }
9166 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
9167 if (!res) {
9168 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
9169 if (!ast_strlen_zero(prefile)) {
9170 /* See if we can find a recorded name for this callerid
9171 * and if found, use that instead of saying number. */
9172 if (ast_fileexists(prefile, NULL, NULL) > 0) {
9173 ast_verb(3, "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
9174 if (!callback)
9175 res = wait_file2(chan, vms, "vm-from");
9176 res = ast_stream_and_wait(chan, prefile, "");
9177 } else {
9178 ast_verb(3, "Playing envelope info: message from '%s'\n", callerid);
9179 /* Say "from extension" as one saying to sound smoother */
9180 if (!callback)
9181 res = wait_file2(chan, vms, "vm-from-extension");
9182 res = ast_say_digit_str(chan, callerid, "", ast_channel_language(chan));
9183 }
9184 }
9185 }
9186 } else if (!res) {
9187 ast_debug(1, "VM-CID: Numeric caller id: (%s)\n", callerid);
9188 /* If there is a recording for this numeric callerid then play that */
9189 if (!callback) {
9190 /* See if we can find a recorded name for this person instead of their extension number */
9191 snprintf(prefile, sizeof(prefile), "%s/recordings/callerids/%s", ast_config_AST_SPOOL_DIR, callerid);
9192 if (!saycidnumber && ast_fileexists(prefile, NULL, NULL) > 0) {
9193 ast_verb(3, "Playing recorded name for CID number '%s' - '%s'\n", callerid,prefile);
9194 wait_file2(chan, vms, "vm-from");
9195 res = ast_stream_and_wait(chan, prefile, "");
9196 ast_verb(3, "Played recorded name result '%d'\n", res);
9197 } else {
9198 /* Since this is all nicely figured out, why not say "from phone number" in this case" */
9199 wait_file2(chan, vms, "vm-from-phonenumber");
9200 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
9201 }
9202 } else {
9203 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, ast_channel_language(chan));
9204 }
9205 }
9206 } else {
9207 /* Number unknown */
9208 ast_debug(1, "VM-CID: From an unknown number\n");
9209 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
9210 res = wait_file2(chan, vms, "vm-unknown-caller");
9211 }
9212 return res;
9213}
9214
9215static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
9216{
9217 int res = 0;
9218 int durationm;
9219 int durations;
9220 /* Verify that we have a duration for the message */
9221 if (duration == NULL)
9222 return res;
9223
9224 /* Convert from seconds to minutes */
9225 durations = atoi(duration);
9226 durationm = (durations / 60);
9227
9228 ast_debug(1, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
9229
9230 if ((!res) && (durationm >= minduration)) {
9231 res = wait_file2(chan, vms, "vm-duration");
9232
9233 /* POLISH syntax */
9234 if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9235 div_t num = div(durationm, 10);
9236
9237 if (durationm == 1) {
9238 res = ast_play_and_wait(chan, "digits/1z");
9239 res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
9240 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
9241 if (num.rem == 2) {
9242 if (!num.quot) {
9243 res = ast_play_and_wait(chan, "digits/2-ie");
9244 } else {
9245 res = say_and_wait(chan, durationm - 2 , ast_channel_language(chan));
9246 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
9247 }
9248 } else {
9249 res = say_and_wait(chan, durationm, ast_channel_language(chan));
9250 }
9251 res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
9252 } else {
9253 res = say_and_wait(chan, durationm, ast_channel_language(chan));
9254 res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
9255 }
9256 /* DEFAULT syntax */
9257 } else {
9258 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
9259 res = wait_file2(chan, vms, "vm-minutes");
9260 }
9261 }
9262 return res;
9263}
9264
9265static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
9266{
9267 int res = 0;
9268 char filename[PATH_MAX], *cid;
9269 const char *origtime, *context, *category, *duration, *flag;
9270 struct ast_config *msg_cfg;
9271 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
9272 SCOPE_ENTER(3, "%s: user: %s dir: %s msg: %d\n", ast_channel_name(chan),
9273 vms->username, vms->curdir, vms->curmsg);
9274
9275 vms->starting = 0;
9276 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
9277 adsi_message(chan, vms);
9278 if (!vms->curmsg) {
9279 res = wait_file2(chan, vms, "vm-first"); /* "First" */
9280 } else if (vms->curmsg == vms->lastmsg) {
9281 res = wait_file2(chan, vms, "vm-last"); /* "last" */
9282 }
9283
9284 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
9285 SCOPE_CALL(-1, RETRIEVE, vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
9286 msg_cfg = ast_config_load(filename, config_flags);
9287 if (!valid_config(msg_cfg)) {
9288 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
9289 return 0;
9290 }
9291 flag = ast_variable_retrieve(msg_cfg, "message", "flag");
9292
9293 /* Play the word urgent if we are listening to urgent messages */
9294 if (!ast_strlen_zero(flag) && !strcmp(flag, "Urgent")) {
9295 res = wait_file2(chan, vms, "vm-Urgent"); /* "urgent" */
9296 }
9297
9298 if (!res) {
9299 /* XXX Why are we playing messages above, and then playing the same language-specific stuff here? */
9300 /* POLISH syntax */
9301 if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9302 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9303 int ten, one;
9304 char nextmsg[256];
9305 ten = (vms->curmsg + 1) / 10;
9306 one = (vms->curmsg + 1) % 10;
9307
9308 if (vms->curmsg < 20) {
9309 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
9310 res = wait_file2(chan, vms, nextmsg);
9311 } else {
9312 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
9313 res = wait_file2(chan, vms, nextmsg);
9314 if (one > 0) {
9315 if (!res) {
9316 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
9317 res = wait_file2(chan, vms, nextmsg);
9318 }
9319 }
9320 }
9321 }
9322 if (!res)
9323 res = wait_file2(chan, vms, "vm-message");
9324 /* HEBREW syntax */
9325 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
9326 if (!vms->curmsg) {
9327 res = wait_file2(chan, vms, "vm-message");
9328 res = wait_file2(chan, vms, "vm-first");
9329 } else if (vms->curmsg == vms->lastmsg) {
9330 res = wait_file2(chan, vms, "vm-message");
9331 res = wait_file2(chan, vms, "vm-last");
9332 } else {
9333 res = wait_file2(chan, vms, "vm-message");
9334 res = wait_file2(chan, vms, "vm-number");
9335 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9336 }
9337 /* ICELANDIC syntax */
9338 } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) {
9339 res = wait_file2(chan, vms, "vm-message");
9340 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9341 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), "n");
9342 }
9343 /* VIETNAMESE syntax */
9344 } else if (!strncasecmp(ast_channel_language(chan), "vi", 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 } else {
9357 if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
9358 res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
9359 } else { /* DEFAULT syntax */
9360 res = wait_file2(chan, vms, "vm-message");
9361 }
9362 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
9363 if (!res) {
9364 ast_test_suite_event_notify("PLAYBACK", "Message: message number");
9365 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, ast_channel_language(chan), NULL);
9366 }
9367 }
9368 }
9369 }
9370
9371 if (!valid_config(msg_cfg)) {
9372 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
9373 }
9374
9375 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
9376 SCOPE_CALL(-1, DISPOSE, vms->curdir, vms->curmsg);
9377 ast_config_destroy(msg_cfg);
9378 SCOPE_EXIT_LOG_RTN_VALUE(0, AST_LOG_WARNING, "No origtime?!\n");
9379 }
9380
9381 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
9382 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
9383 category = ast_variable_retrieve(msg_cfg, "message", "category");
9384
9385 context = ast_variable_retrieve(msg_cfg, "message", "context");
9386 if (!res) {
9387 res = play_message_category(chan, category);
9388 }
9389 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE))) {
9390 res = play_message_datetime(chan, vmu, origtime, filename);
9391 }
9392 if ((!res) && (ast_test_flag(vmu, VM_SAYCID))) {
9393 res = play_message_callerid(chan, vms, cid, context, 0, 0);
9394 }
9395 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION))) {
9396 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
9397 }
9398 /* Allow pressing '1' to skip envelope / callerid */
9399 if (res == '1') {
9400 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c", res, res);
9401 res = 0;
9402 }
9403 ast_config_destroy(msg_cfg);
9404
9405 if (!res) {
9406 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
9407#ifdef IMAP_STORAGE
9408 ast_mutex_lock(&vms->lock);
9409#endif
9410 vms->heard[vms->curmsg] = 1;
9411#ifdef IMAP_STORAGE
9412 ast_mutex_unlock(&vms->lock);
9413 /*IMAP storage stores any prepended message from a forward
9414 * as a separate file from the rest of the message
9415 */
9416 if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
9417 wait_file(chan, vms, vms->introfn);
9418 }
9419#endif
9420 if ((res = wait_file(chan, vms, vms->fn)) < 0) {
9421 ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
9422 res = 0;
9423 }
9424 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
9425 isprint(res) ? res : '?', isprint(res) ? res : '?');
9426 }
9427 SCOPE_CALL(-1, DISPOSE, vms->curdir, vms->curmsg);
9428 SCOPE_EXIT_RTN_VALUE(res, "Done: RC: %d\n", res);
9429}
9430
9431#ifdef IMAP_STORAGE
9432static int imap_remove_file(char *dir, int msgnum)
9433{
9434 char fn[PATH_MAX];
9435 char full_fn[PATH_MAX];
9436 char intro[PATH_MAX] = {0,};
9437
9438 if (msgnum > -1) {
9439 make_file(fn, sizeof(fn), dir, msgnum);
9440 snprintf(intro, sizeof(intro), "%sintro", fn);
9441 } else
9442 ast_copy_string(fn, dir, sizeof(fn));
9443
9444 if ((msgnum < 0 && imapgreetings) || msgnum > -1) {
9445 ast_filedelete(fn, NULL);
9446 if (!ast_strlen_zero(intro)) {
9448 }
9449 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
9450 unlink(full_fn);
9451 }
9452 return 0;
9453}
9454
9455
9456
9457static int imap_delete_old_greeting (char *dir, struct vm_state *vms)
9458{
9459 char *file, *filename;
9460 char arg[11];
9461 int i;
9462 BODY* body;
9463 int curr_mbox;
9464
9465 file = strrchr(ast_strdupa(dir), '/');
9466 if (file) {
9467 *file++ = '\0';
9468 } else {
9469 ast_log(AST_LOG_ERROR, "Failed to procure file name from directory passed. You should never see this.\n");
9470 return -1;
9471 }
9472
9473 ast_mutex_lock(&vms->lock);
9474
9475 /* get the current mailbox so that we can point the mailstream back to it later */
9476 curr_mbox = get_folder_by_name(vms->curbox);
9477
9478 if (init_mailstream(vms, GREETINGS_FOLDER) || !vms->mailstream) {
9479 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
9480 ast_mutex_unlock(&vms->lock);
9481 return -1;
9482 }
9483
9484 for (i = 0; i < vms->mailstream->nmsgs; i++) {
9485 mail_fetchstructure(vms->mailstream, i + 1, &body);
9486 /* We have the body, now we extract the file name of the first attachment. */
9487 if (body->nested.part->next && body->nested.part->next->body.parameter->value) {
9488 char *attachment = body->nested.part->next->body.parameter->value;
9489 char copy[strlen(attachment) + 1];
9490
9491 strcpy(copy, attachment); /* safe */
9492 attachment = copy;
9493
9494 filename = strsep(&attachment, ".");
9495 if (!strcmp(filename, file)) {
9496 snprintf(arg, sizeof(arg), "%d", i + 1);
9497 mail_setflag(vms->mailstream, arg, "\\DELETED");
9498 }
9499 } else {
9500 ast_log(AST_LOG_ERROR, "There is no file attached to this IMAP message.\n");
9501 ast_mutex_unlock(&vms->lock);
9502 return -1;
9503 }
9504 }
9505 mail_expunge(vms->mailstream);
9506
9507 if (curr_mbox != -1) {
9508 /* restore previous mbox stream */
9509 if (init_mailstream(vms, curr_mbox) || !vms->mailstream) {
9510 ast_log(AST_LOG_ERROR, "IMAP mailstream is NULL or can't init_mailstream\n");
9511 }
9512 }
9513
9514 ast_mutex_unlock(&vms->lock);
9515 return 0;
9516}
9517
9518#elif !defined(IMAP_STORAGE)
9519static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
9520{
9521 int count_msg, last_msg;
9522 SCOPE_ENTER(3, "user: %s dir: %s msg: %d box %d\n",
9523 vms->username, vms->curdir, vms->curmsg, box);
9524
9525 ast_copy_string(vms->curbox, mbox(vmu, box), sizeof(vms->curbox));
9526
9527 /* Rename the member vmbox HERE so that we don't try to return before
9528 * we know what's going on.
9529 */
9530 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
9531
9532 /* Faster to make the directory than to check if it exists. */
9533 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
9534
9535 /* traverses directory using readdir (or select query for ODBC) */
9536 count_msg = COUNT(vmu, vms->curdir);
9537 if (count_msg < 0) {
9538 SCOPE_EXIT_RTN_VALUE(count_msg, "msgs: %d\n", count_msg);
9539 } else {
9540 vms->lastmsg = count_msg - 1;
9541 }
9542
9543 if (vm_allocate_dh(vms, vmu, count_msg)) {
9544 SCOPE_EXIT_RTN_VALUE(-1, "failed to allocate dh\n");
9545 }
9546
9547 /*
9548 The following test is needed in case sequencing gets messed up.
9549 There appears to be more than one way to mess up sequence, so
9550 we will not try to find all of the root causes--just fix it when
9551 detected.
9552 */
9553
9554 if (vm_lock_path(vms->curdir)) {
9555 SCOPE_EXIT_LOG_RTN_VALUE(ERROR_LOCK_PATH, AST_LOG_ERROR, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
9556 }
9557
9558 /* for local storage, checks directory for messages up to MAXMSGLIMIT */
9559 last_msg = LAST_MSG_INDEX(vms->curdir);
9560 ast_unlock_path(vms->curdir);
9561
9562 if (last_msg < -1) {
9563 SCOPE_EXIT_RTN_VALUE(last_msg, "last msg: %d\n", last_msg);
9564 } else if (vms->lastmsg != last_msg) {
9565 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);
9566 resequence_mailbox(vmu, vms->curdir, count_msg);
9567 }
9568
9569 SCOPE_EXIT_RTN_VALUE(0, "Done\n");
9570}
9571#endif
9572
9573static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
9574{
9575 int x = 0;
9576 int last_msg_idx = 0;
9577
9578#ifndef IMAP_STORAGE
9579 int res = 0, nummsg;
9580 char fn2[PATH_MAX];
9581#endif
9582 SCOPE_ENTER(3, "user: %s dir: %s msg: %d\n",
9583 vms->username, vms->curdir, vms->curmsg);
9584
9585 if (vms->lastmsg <= -1) {
9586 ast_trace(-1, "No messages in mailbox\n");
9587 goto done;
9588 }
9589
9590 vms->curmsg = -1;
9591#ifndef IMAP_STORAGE
9592 /* Get the deleted messages fixed */
9593 if (vm_lock_path(vms->curdir)) {
9594 SCOPE_EXIT_RTN_VALUE(ERROR_LOCK_PATH, "Could not open mailbox %s: mailbox is locked\n", vms->curdir);
9595 }
9596
9597 /* update count as message may have arrived while we've got mailbox open */
9598 last_msg_idx = LAST_MSG_INDEX(vms->curdir);
9599 if (last_msg_idx != vms->lastmsg) {
9600 ast_log(AST_LOG_NOTICE, "%d messages received after mailbox opened.\n", last_msg_idx - vms->lastmsg);
9601 }
9602
9603 /* must check up to last detected message, just in case it is erroneously greater than maxmsg */
9604 for (x = 0; x < last_msg_idx + 1; x++) {
9605 if (!vms->deleted[x] && ((strcasecmp(vms->curbox, "INBOX") && strcasecmp(vms->curbox, "Urgent")) || !vms->heard[x] || (vms->heard[x] && !ast_test_flag(vmu, VM_MOVEHEARD)))) {
9606 /* Save this message. It's not in INBOX or hasn't been heard */
9607 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9608 if (!EXISTS(vms->curdir, x, vms->fn, NULL)) {
9609 break;
9610 }
9611 vms->curmsg++;
9612 make_file(fn2, sizeof(fn2), vms->curdir, vms->curmsg);
9613 if (strcmp(vms->fn, fn2)) {
9614 SCOPE_CALL(-1, RENAME, vms->curdir, x, vmu->mailbox, vmu->context, vms->curdir, vms->curmsg, vms->fn, fn2);
9615 }
9616 } else if ((!strcasecmp(vms->curbox, "INBOX") || !strcasecmp(vms->curbox, "Urgent")) && vms->heard[x] && ast_test_flag(vmu, VM_MOVEHEARD) && !vms->deleted[x]) {
9617 /* Move to old folder before deleting */
9618 res = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, vms, x, 1, NULL, 0);
9619 if (res == ERROR_LOCK_PATH || res == ERROR_MAX_MSGS) {
9620 /* If save failed do not delete the message */
9621 ast_log(AST_LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
9622 vms->deleted[x] = 0;
9623 vms->heard[x] = 0;
9624 --x;
9625 }
9626 } else if (vms->deleted[x] && vmu->maxdeletedmsg) {
9627 /* Move to deleted folder */
9628 res = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, vms, x, 10, NULL, 0);
9629 if (res == ERROR_LOCK_PATH) {
9630 ast_trace(-1, "Unable to lock path. Not moving message to deleted folder.\n");
9631 /* If save failed do not delete the message */
9632 vms->deleted[x] = 0;
9633 vms->heard[x] = 0;
9634 --x;
9635 }
9636 } else if (vms->deleted[x] && ast_check_realtime("voicemail_data")) {
9637 /* If realtime storage enabled - we should explicitly delete this message,
9638 cause RENAME() will overwrite files, but will keep duplicate records in RT-storage */
9639 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9640 res = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, vms->curdir, x, vms->fn, NULL);
9641 if (res) {
9642 SCOPE_CALL(-1, DELETE, vms->curdir, x, vms->fn, vmu);
9643 }
9644 }
9645 }
9646
9647 /* Delete ALL remaining messages */
9648 nummsg = x - 1;
9649 for (x = vms->curmsg + 1; x <= nummsg; x++) {
9650 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
9651 res = SCOPE_CALL_WITH_INT_RESULT(-1, EXISTS, vms->curdir, x, vms->fn, NULL);
9652 if (res) {
9653 SCOPE_CALL(-1, DELETE, vms->curdir, x, vms->fn, vmu);
9654 }
9655 }
9656 ast_unlock_path(vms->curdir);
9657#else /* defined(IMAP_STORAGE) */
9658 ast_mutex_lock(&vms->lock);
9659 if (vms->deleted) {
9660 /* Since we now expunge after each delete, deleting in reverse order
9661 * ensures that no reordering occurs between each step. */
9662 last_msg_idx = vms->dh_arraysize;
9663 for (x = last_msg_idx - 1; x >= 0; x--) {
9664 if (vms->deleted[x]) {
9665 ast_debug(3, "IMAP delete of %d\n", x);
9666 DELETE(vms->curdir, x, vms->fn, vmu);
9667 }
9668 }
9669 }
9670#endif
9671
9672done:
9673 if (vms->deleted) {
9674 ast_free(vms->deleted);
9675 vms->deleted = NULL;
9676 }
9677 if (vms->heard) {
9678 ast_free(vms->heard);
9679 vms->heard = NULL;
9680 }
9681 vms->dh_arraysize = 0;
9682#ifdef IMAP_STORAGE
9683 ast_mutex_unlock(&vms->lock);
9684#endif
9685
9686 SCOPE_EXIT_RTN_VALUE(0, "Done\n");
9687}
9688
9689/* In Greek even though we CAN use a syntax like "friends messages"
9690 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
9691 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
9692 * syntax for the above three categories which is more elegant.
9693 */
9694
9695static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
9696{
9697 int cmd;
9698 char *buf;
9699
9700 buf = ast_alloca(strlen(box) + 2);
9701 strcpy(buf, box);
9702 strcat(buf, "s");
9703
9704 if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")){
9705 cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
9706 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
9707 } else {
9708 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
9709 return cmd ? cmd : ast_play_and_wait(chan, box); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
9710 }
9711}
9712
9713static int vm_play_folder_name_ja(struct ast_channel *chan, char *box)
9714{
9715 int cmd;
9716
9717 if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
9718 cmd = ast_play_and_wait(chan, box);
9719 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9720 } else {
9721 cmd = ast_play_and_wait(chan, box);
9722 return cmd;
9723 }
9724}
9725
9726static int vm_play_folder_name_pl(struct ast_channel *chan, char *box)
9727{
9728 int cmd;
9729
9730 if (!strcasecmp(box, "vm-INBOX") || !strcasecmp(box, "vm-Old")) {
9731 if (!strcasecmp(box, "vm-INBOX"))
9732 cmd = ast_play_and_wait(chan, "vm-new-e");
9733 else
9734 cmd = ast_play_and_wait(chan, "vm-old-e");
9735 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9736 } else {
9737 cmd = ast_play_and_wait(chan, "vm-messages");
9738 return cmd ? cmd : ast_play_and_wait(chan, box);
9739 }
9740}
9741
9742static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
9743{
9744 int cmd;
9745
9746 if (!strcasecmp(box, "vm-Family") || !strcasecmp(box, "vm-Friends") || !strcasecmp(box, "vm-Work")){
9747 cmd = ast_play_and_wait(chan, "vm-messages");
9748 return cmd ? cmd : ast_play_and_wait(chan, box);
9749 } else {
9750 cmd = ast_play_and_wait(chan, box);
9751 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
9752 }
9753}
9754
9755static int vm_play_folder_name(struct ast_channel *chan, char *box)
9756{
9757 int cmd;
9758
9759 if ( !strncasecmp(ast_channel_language(chan), "it", 2) ||
9760 !strncasecmp(ast_channel_language(chan), "es", 2) ||
9761 !strncasecmp(ast_channel_language(chan), "pt", 2)) { /* Italian, Spanish, or Portuguese syntax */
9762 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
9763 return cmd ? cmd : ast_play_and_wait(chan, box);
9764 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) {
9765 return vm_play_folder_name_gr(chan, box);
9766 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* Hebrew syntax */
9767 return ast_play_and_wait(chan, box);
9768 } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
9769 return vm_play_folder_name_ja(chan, box);
9770 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) {
9771 return vm_play_folder_name_pl(chan, box);
9772 } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* Ukrainian syntax */
9773 return vm_play_folder_name_ua(chan, box);
9774 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) {
9775 return ast_play_and_wait(chan, box);
9776 } else { /* Default English */
9777 cmd = ast_play_and_wait(chan, box);
9778 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
9779 }
9780}
9781
9782/* GREEK SYNTAX
9783 In greek the plural for old/new is
9784 different so we need the following files
9785 We also need vm-denExeteMynhmata because
9786 this syntax is different.
9787
9788 -> vm-Olds.wav : "Palia"
9789 -> vm-INBOXs.wav : "Nea"
9790 -> vm-denExeteMynhmata : "den exete mynhmata"
9791*/
9792
9793
9794static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
9795{
9796 int res = 0;
9797
9798 if (vms->newmessages) {
9799 res = ast_play_and_wait(chan, "vm-youhave");
9800 if (!res)
9802 if (!res) {
9803 if (vms->newmessages == 1) {
9804 res = ast_play_and_wait(chan, "vm-INBOX");
9805 if (!res)
9806 res = ast_play_and_wait(chan, "vm-message");
9807 } else {
9808 res = ast_play_and_wait(chan, "vm-INBOXs");
9809 if (!res)
9810 res = ast_play_and_wait(chan, "vm-messages");
9811 }
9812 }
9813 } else if (vms->oldmessages){
9814 res = ast_play_and_wait(chan, "vm-youhave");
9815 if (!res)
9817 if (vms->oldmessages == 1){
9818 res = ast_play_and_wait(chan, "vm-Old");
9819 if (!res)
9820 res = ast_play_and_wait(chan, "vm-message");
9821 } else {
9822 res = ast_play_and_wait(chan, "vm-Olds");
9823 if (!res)
9824 res = ast_play_and_wait(chan, "vm-messages");
9825 }
9826 } else if (!vms->oldmessages && !vms->newmessages)
9827 res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
9828 return res;
9829}
9830
9831/* Version of vm_intro() designed to work for many languages.
9832 *
9833 * It is hoped that this function can prevent the proliferation of
9834 * language-specific vm_intro() functions and in time replace the language-
9835 * specific functions which already exist. An examination of the language-
9836 * specific functions revealed that they all corrected the same deficiencies
9837 * in vm_intro_en() (which was the default function). Namely:
9838 *
9839 * 1) The vm-Old and vm-INBOX sound files were overloaded. The English
9840 * wording of the voicemail greeting hides this problem. For example,
9841 * vm-INBOX contains only the word "new". This means that both of these
9842 * sequences produce valid utterances:
9843 * * vm-youhave digit/1 vm-INBOX vm-message (you have one new message)
9844 * * vm-press digit/1 vm-for vm-INBOX vm-messages (press 1 for new messages)
9845 * However, if we rerecord vm-INBOX to say "the new" (which is unavoidable
9846 * in many languages) the first utterance becomes "you have 1 the new message".
9847 * 2) The function contains hardcoded rules for pluralizing the word "message".
9848 * These rules are correct for English, but not for many other languages.
9849 * 3) No attempt is made to pluralize the adjectives ("old" and "new") as
9850 * required in many languages.
9851 * 4) The gender of the word for "message" is not specified. This is a problem
9852 * because in many languages the gender of the number in phrases such
9853 * as "you have one new message" must match the gender of the word
9854 * meaning "message".
9855 *
9856 * Fixing these problems for each new language has meant duplication of effort.
9857 * This new function solves the problems in the following general ways:
9858 * 1) Add new sound files vm-new and vm-old. These can be linked to vm-INBOX
9859 * and vm-Old respectively for those languages where it makes sense.
9860 * 2) Call ast_say_counted_noun() to put the proper gender and number prefix
9861 * on vm-message.
9862 * 3) Call ast_say_counted_adjective() to put the proper gender and number
9863 * prefix on vm-new and vm-old (none for English).
9864 * 4) Pass the gender of the language's word for "message" as an argument to
9865 * this function which is can in turn pass on to the functions which
9866 * say numbers and put endings on nouns and adjectives.
9867 *
9868 * All languages require these messages:
9869 * vm-youhave "You have..."
9870 * vm-and "and"
9871 * vm-no "no" (in the sense of "none", as in "you have no messages")
9872 *
9873 * To use it for English, you will need these additional sound files:
9874 * vm-new "new"
9875 * vm-message "message", singular
9876 * vm-messages "messages", plural
9877 *
9878 * If you use it for Russian and other slavic languages, you will need these additional sound files:
9879 *
9880 * vm-newn "novoye" (singular, neuter)
9881 * vm-newx "novikh" (counting plural form, genative plural)
9882 * vm-message "sobsheniye" (singular form)
9883 * vm-messagex1 "sobsheniya" (first counting plural form, genative singular)
9884 * vm-messagex2 "sobsheniy" (second counting plural form, genative plural)
9885 * digits/1n "odno" (neuter singular for phrases such as "one message" or "thirty one messages")
9886 * digits/2n "dva" (neuter singular)
9887 */
9888static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
9889{
9890 int res;
9891 int lastnum = 0;
9892
9893 res = ast_play_and_wait(chan, "vm-youhave");
9894
9895 if (!res && vms->newmessages) {
9896 lastnum = vms->newmessages;
9897
9898 if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
9899 res = ast_say_counted_adjective(chan, lastnum, "vm-new", message_gender);
9900 }
9901
9902 if (!res && vms->oldmessages) {
9903 res = ast_play_and_wait(chan, "vm-and");
9904 }
9905 }
9906
9907 if (!res && vms->oldmessages) {
9908 lastnum = vms->oldmessages;
9909
9910 if (!(res = ast_say_number(chan, lastnum, AST_DIGIT_ANY, ast_channel_language(chan), message_gender))) {
9911 res = ast_say_counted_adjective(chan, lastnum, "vm-old", message_gender);
9912 }
9913 }
9914
9915 if (!res) {
9916 if (lastnum == 0) {
9917 res = ast_play_and_wait(chan, "vm-no");
9918 }
9919 if (!res) {
9920 res = ast_say_counted_noun(chan, lastnum, "vm-message");
9921 }
9922 }
9923
9924 return res;
9925}
9926
9927/* Default Hebrew syntax */
9928static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
9929{
9930 int res = 0;
9931
9932 /* Introduce messages they have */
9933 if (!res) {
9934 if ((vms->newmessages) || (vms->oldmessages)) {
9935 res = ast_play_and_wait(chan, "vm-youhave");
9936 }
9937 /*
9938 * The word "shtei" refers to the number 2 in hebrew when performing a count
9939 * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
9940 * an element, this is one of them.
9941 */
9942 if (vms->newmessages) {
9943 if (!res) {
9944 if (vms->newmessages == 1) {
9945 res = ast_play_and_wait(chan, "vm-INBOX1");
9946 } else {
9947 if (vms->newmessages == 2) {
9948 res = ast_play_and_wait(chan, "vm-shtei");
9949 } else {
9950 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9951 }
9952 res = ast_play_and_wait(chan, "vm-INBOX");
9953 }
9954 }
9955 if (vms->oldmessages && !res) {
9956 res = ast_play_and_wait(chan, "vm-and");
9957 if (vms->oldmessages == 1) {
9958 res = ast_play_and_wait(chan, "vm-Old1");
9959 } else {
9960 if (vms->oldmessages == 2) {
9961 res = ast_play_and_wait(chan, "vm-shtei");
9962 } else {
9963 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9964 }
9965 res = ast_play_and_wait(chan, "vm-Old");
9966 }
9967 }
9968 }
9969 if (!res && vms->oldmessages && !vms->newmessages) {
9970 if (!res) {
9971 if (vms->oldmessages == 1) {
9972 res = ast_play_and_wait(chan, "vm-Old1");
9973 } else {
9974 if (vms->oldmessages == 2) {
9975 res = ast_play_and_wait(chan, "vm-shtei");
9976 } else {
9977 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
9978 }
9979 res = ast_play_and_wait(chan, "vm-Old");
9980 }
9981 }
9982 }
9983 if (!res) {
9984 if (!vms->oldmessages && !vms->newmessages) {
9985 if (!res) {
9986 res = ast_play_and_wait(chan, "vm-nomessages");
9987 }
9988 }
9989 }
9990 }
9991 return res;
9992}
9993
9994/* Japanese syntax */
9995static int vm_intro_ja(struct ast_channel *chan,struct vm_state *vms)
9996{
9997 /* Introduce messages they have */
9998 int res;
9999 if (vms->newmessages) {
10000 res = ast_play_and_wait(chan, "vm-INBOX");
10001 if (!res)
10002 res = ast_play_and_wait(chan, "vm-message");
10003 if (!res)
10004 res = ast_play_and_wait(chan, "jp-ga");
10005 if (!res)
10006 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10007 if (vms->oldmessages && !res)
10008 res = ast_play_and_wait(chan, "silence/1");
10009
10010 }
10011 if (vms->oldmessages) {
10012 res = ast_play_and_wait(chan, "vm-Old");
10013 if (!res)
10014 res = ast_play_and_wait(chan, "vm-message");
10015 if (!res)
10016 res = ast_play_and_wait(chan, "jp-ga");
10017 if (!res)
10018 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10019 }
10020 if (!vms->oldmessages && !vms->newmessages) {
10021 res = ast_play_and_wait(chan, "vm-messages");
10022 if (!res)
10023 res = ast_play_and_wait(chan, "jp-wa");
10024 if (!res)
10025 res = ast_play_and_wait(chan, "jp-arimasen");
10026 }
10027 else {
10028 res = ast_play_and_wait(chan, "jp-arimasu");
10029 }
10030 return res;
10031} /* Japanese */
10032
10033/* Default English syntax */
10034static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
10035{
10036 int res;
10037
10038 /* Introduce messages they have */
10039 res = ast_play_and_wait(chan, "vm-youhave");
10040 if (!res) {
10041 if (vms->urgentmessages) {
10042 res = say_and_wait(chan, vms->urgentmessages, ast_channel_language(chan));
10043 if (!res)
10044 res = ast_play_and_wait(chan, "vm-Urgent");
10045 if ((vms->oldmessages || vms->newmessages) && !res) {
10046 res = ast_play_and_wait(chan, "vm-and");
10047 } else if (!res) {
10048 if (vms->urgentmessages == 1)
10049 res = ast_play_and_wait(chan, "vm-message");
10050 else
10051 res = ast_play_and_wait(chan, "vm-messages");
10052 }
10053 }
10054 if (vms->newmessages) {
10055 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10056 if (!res)
10057 res = ast_play_and_wait(chan, "vm-INBOX");
10058 if (vms->oldmessages && !res)
10059 res = ast_play_and_wait(chan, "vm-and");
10060 else if (!res) {
10061 if (vms->newmessages == 1)
10062 res = ast_play_and_wait(chan, "vm-message");
10063 else
10064 res = ast_play_and_wait(chan, "vm-messages");
10065 }
10066
10067 }
10068 if (!res && vms->oldmessages) {
10069 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10070 if (!res)
10071 res = ast_play_and_wait(chan, "vm-Old");
10072 if (!res) {
10073 if (vms->oldmessages == 1)
10074 res = ast_play_and_wait(chan, "vm-message");
10075 else
10076 res = ast_play_and_wait(chan, "vm-messages");
10077 }
10078 }
10079 if (!res) {
10080 if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
10081 res = ast_play_and_wait(chan, "vm-no");
10082 if (!res)
10083 res = ast_play_and_wait(chan, "vm-messages");
10084 }
10085 }
10086 }
10087 return res;
10088}
10089
10090/* ICELANDIC syntax */
10091static int vm_intro_is(struct ast_channel *chan, struct vm_state *vms)
10092{
10093 int res;
10094
10095 /* Introduce messages they have */
10096 res = ast_play_and_wait(chan, "vm-youhave");
10097 if (!res) {
10098 if (vms->urgentmessages) {
10099 /* Digits 1-4 are spoken in neutral and plural when talking about messages,
10100 however, feminine is used for 1 as it is the same as the neutral for plural,
10101 and singular neutral is the same after 1. */
10102 if (vms->urgentmessages < 5) {
10103 char recname[16];
10104 if (vms->urgentmessages == 1)
10105 snprintf(recname, sizeof(recname), "digits/1kvk");
10106 else
10107 snprintf(recname, sizeof(recname), "digits/%dhk", vms->urgentmessages);
10108 res = ast_play_and_wait(chan, recname);
10109 } else if (!res)
10110 res = ast_play_and_wait(chan, "vm-Urgent");
10111 if ((vms->oldmessages || vms->newmessages) && !res) {
10112 res = ast_play_and_wait(chan, "vm-and");
10113 } else if (!res)
10114 res = ast_play_and_wait(chan, "vm-messages");
10115 }
10116 if (vms->newmessages) {
10117 if (vms->newmessages < 5) {
10118 char recname[16];
10119 if (vms->newmessages == 1)
10120 snprintf(recname, sizeof(recname), "digits/1kvk");
10121 else
10122 snprintf(recname, sizeof(recname), "digits/%dhk", vms->newmessages);
10123 res = ast_play_and_wait(chan, recname);
10124 } else
10125 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10126 if (!res)
10127 res = ast_play_and_wait(chan, "vm-INBOX");
10128 if (vms->oldmessages && !res)
10129 res = ast_play_and_wait(chan, "vm-and");
10130 else if (!res)
10131 res = ast_play_and_wait(chan, "vm-messages");
10132 }
10133 if (!res && vms->oldmessages) {
10134 if (vms->oldmessages < 5) {
10135 char recname[16];
10136 if (vms->oldmessages == 1)
10137 snprintf(recname, sizeof(recname), "digits/1kvk");
10138 else
10139 snprintf(recname, sizeof(recname), "digits/%dhk", vms->oldmessages);
10140 res = ast_play_and_wait(chan, recname);
10141 } else
10142 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10143 if (!res)
10144 res = ast_play_and_wait(chan, "vm-Old");
10145 if (!res)
10146 res = ast_play_and_wait(chan, "vm-messages");
10147 }
10148 if (!res) {
10149 if (!vms->urgentmessages && !vms->oldmessages && !vms->newmessages) {
10150 res = ast_play_and_wait(chan, "vm-no");
10151 if (!res)
10152 res = ast_play_and_wait(chan, "vm-messages");
10153 }
10154 }
10155 }
10156 return res;
10157}
10158
10159/* ITALIAN syntax */
10160static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
10161{
10162 /* Introduce messages they have */
10163 int res;
10164 if (!vms->oldmessages && !vms->newmessages &&!vms->urgentmessages)
10165 res = ast_play_and_wait(chan, "vm-no") ||
10166 ast_play_and_wait(chan, "vm-message");
10167 else
10168 res = ast_play_and_wait(chan, "vm-youhave");
10169 if (!res && vms->newmessages) {
10170 res = (vms->newmessages == 1) ?
10171 ast_play_and_wait(chan, "digits/un") ||
10172 ast_play_and_wait(chan, "vm-nuovo") ||
10173 ast_play_and_wait(chan, "vm-message") :
10174 /* 2 or more new messages */
10175 say_and_wait(chan, vms->newmessages, ast_channel_language(chan)) ||
10176 ast_play_and_wait(chan, "vm-nuovi") ||
10177 ast_play_and_wait(chan, "vm-messages");
10178 if (!res && vms->oldmessages)
10179 res = ast_play_and_wait(chan, "vm-and");
10180 }
10181 if (!res && vms->oldmessages) {
10182 res = (vms->oldmessages == 1) ?
10183 ast_play_and_wait(chan, "digits/un") ||
10184 ast_play_and_wait(chan, "vm-vecchio") ||
10185 ast_play_and_wait(chan, "vm-message") :
10186 /* 2 or more old messages */
10187 say_and_wait(chan, vms->oldmessages, ast_channel_language(chan)) ||
10188 ast_play_and_wait(chan, "vm-vecchi") ||
10189 ast_play_and_wait(chan, "vm-messages");
10190 }
10191 return res;
10192}
10193
10194/* POLISH syntax */
10195static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
10196{
10197 /* Introduce messages they have */
10198 int res;
10199 div_t num;
10200
10201 if (!vms->oldmessages && !vms->newmessages) {
10202 res = ast_play_and_wait(chan, "vm-no");
10203 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10204 return res;
10205 } else {
10206 res = ast_play_and_wait(chan, "vm-youhave");
10207 }
10208
10209 if (vms->newmessages) {
10210 num = div(vms->newmessages, 10);
10211 if (vms->newmessages == 1) {
10212 res = ast_play_and_wait(chan, "digits/1-a");
10213 res = res ? res : ast_play_and_wait(chan, "vm-new-a");
10214 res = res ? res : ast_play_and_wait(chan, "vm-message");
10215 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
10216 if (num.rem == 2) {
10217 if (!num.quot) {
10218 res = ast_play_and_wait(chan, "digits/2-ie");
10219 } else {
10220 res = say_and_wait(chan, vms->newmessages - 2 , ast_channel_language(chan));
10221 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
10222 }
10223 } else {
10224 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10225 }
10226 res = res ? res : ast_play_and_wait(chan, "vm-new-e");
10227 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10228 } else {
10229 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10230 res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
10231 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10232 }
10233 if (!res && vms->oldmessages)
10234 res = ast_play_and_wait(chan, "vm-and");
10235 }
10236 if (!res && vms->oldmessages) {
10237 num = div(vms->oldmessages, 10);
10238 if (vms->oldmessages == 1) {
10239 res = ast_play_and_wait(chan, "digits/1-a");
10240 res = res ? res : ast_play_and_wait(chan, "vm-old-a");
10241 res = res ? res : ast_play_and_wait(chan, "vm-message");
10242 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
10243 if (num.rem == 2) {
10244 if (!num.quot) {
10245 res = ast_play_and_wait(chan, "digits/2-ie");
10246 } else {
10247 res = say_and_wait(chan, vms->oldmessages - 2 , ast_channel_language(chan));
10248 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
10249 }
10250 } else {
10251 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10252 }
10253 res = res ? res : ast_play_and_wait(chan, "vm-old-e");
10254 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10255 } else {
10256 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10257 res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
10258 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10259 }
10260 }
10261
10262 return res;
10263}
10264
10265/* SWEDISH syntax */
10266static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
10267{
10268 /* Introduce messages they have */
10269 int res;
10270
10271 res = ast_play_and_wait(chan, "vm-youhave");
10272 if (res)
10273 return res;
10274
10275 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10276 res = ast_play_and_wait(chan, "vm-no");
10277 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10278 return res;
10279 }
10280
10281 if (vms->newmessages) {
10282 if (vms->newmessages == 1) {
10283 res = ast_play_and_wait(chan, "digits/ett");
10284 res = res ? res : ast_play_and_wait(chan, "vm-nytt");
10285 res = res ? res : ast_play_and_wait(chan, "vm-message");
10286 } else {
10287 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10288 res = res ? res : ast_play_and_wait(chan, "vm-nya");
10289 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10290 }
10291 if (!res && vms->oldmessages)
10292 res = ast_play_and_wait(chan, "vm-and");
10293 }
10294 if (!res && vms->oldmessages) {
10295 if (vms->oldmessages == 1) {
10296 res = ast_play_and_wait(chan, "digits/ett");
10297 res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
10298 res = res ? res : ast_play_and_wait(chan, "vm-message");
10299 } else {
10300 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10301 res = res ? res : ast_play_and_wait(chan, "vm-gamla");
10302 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10303 }
10304 }
10305
10306 return res;
10307}
10308
10309/* NORWEGIAN syntax */
10310static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
10311{
10312 /* Introduce messages they have */
10313 int res;
10314
10315 res = ast_play_and_wait(chan, "vm-youhave");
10316 if (res)
10317 return res;
10318
10319 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10320 res = ast_play_and_wait(chan, "vm-no");
10321 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10322 return res;
10323 }
10324
10325 if (vms->newmessages) {
10326 if (vms->newmessages == 1) {
10327 res = ast_play_and_wait(chan, "digits/1");
10328 res = res ? res : ast_play_and_wait(chan, "vm-ny");
10329 res = res ? res : ast_play_and_wait(chan, "vm-message");
10330 } else {
10331 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10332 res = res ? res : ast_play_and_wait(chan, "vm-nye");
10333 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10334 }
10335 if (!res && vms->oldmessages)
10336 res = ast_play_and_wait(chan, "vm-and");
10337 }
10338 if (!res && vms->oldmessages) {
10339 if (vms->oldmessages == 1) {
10340 res = ast_play_and_wait(chan, "digits/1");
10341 res = res ? res : ast_play_and_wait(chan, "vm-gamel");
10342 res = res ? res : ast_play_and_wait(chan, "vm-message");
10343 } else {
10344 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10345 res = res ? res : ast_play_and_wait(chan, "vm-gamle");
10346 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10347 }
10348 }
10349
10350 return res;
10351}
10352
10353/* Danish syntax */
10354static int vm_intro_da(struct ast_channel *chan, struct vm_state *vms)
10355{
10356 /* Introduce messages they have */
10357 int res;
10358
10359 res = ast_play_and_wait(chan, "vm-youhave");
10360 if (res)
10361 return res;
10362
10363 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10364 res = ast_play_and_wait(chan, "vm-no");
10365 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10366 return res;
10367 }
10368
10369 if (vms->newmessages) {
10370 if ((vms->newmessages == 1)) {
10371 res = ast_play_and_wait(chan, "digits/1");
10372 res = res ? res : ast_play_and_wait(chan, "vm-INBOX");
10373 res = res ? res : ast_play_and_wait(chan, "vm-message");
10374 } else {
10375 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10376 res = res ? res : ast_play_and_wait(chan, "vm-INBOXs");
10377 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10378 }
10379 if (!res && vms->oldmessages)
10380 res = ast_play_and_wait(chan, "vm-and");
10381 }
10382 if (!res && vms->oldmessages) {
10383 if (vms->oldmessages == 1) {
10384 res = ast_play_and_wait(chan, "digits/1");
10385 res = res ? res : ast_play_and_wait(chan, "vm-Old");
10386 res = res ? res : ast_play_and_wait(chan, "vm-message");
10387 } else {
10388 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10389 res = res ? res : ast_play_and_wait(chan, "vm-Olds");
10390 res = res ? res : ast_play_and_wait(chan, "vm-messages");
10391 }
10392 }
10393
10394 return res;
10395}
10396
10397
10398/* GERMAN syntax */
10399static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
10400{
10401 /* Introduce messages they have */
10402 int res;
10403 res = ast_play_and_wait(chan, "vm-youhave");
10404 if (!res) {
10405 if (vms->newmessages) {
10406 if (vms->newmessages == 1)
10407 res = ast_play_and_wait(chan, "digits/1F");
10408 else
10409 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10410 if (!res)
10411 res = ast_play_and_wait(chan, "vm-INBOX");
10412 if (vms->oldmessages && !res)
10413 res = ast_play_and_wait(chan, "vm-and");
10414 else if (!res) {
10415 if (vms->newmessages == 1)
10416 res = ast_play_and_wait(chan, "vm-message");
10417 else
10418 res = ast_play_and_wait(chan, "vm-messages");
10419 }
10420
10421 }
10422 if (!res && vms->oldmessages) {
10423 if (vms->oldmessages == 1)
10424 res = ast_play_and_wait(chan, "digits/1F");
10425 else
10426 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10427 if (!res)
10428 res = ast_play_and_wait(chan, "vm-Old");
10429 if (!res) {
10430 if (vms->oldmessages == 1)
10431 res = ast_play_and_wait(chan, "vm-message");
10432 else
10433 res = ast_play_and_wait(chan, "vm-messages");
10434 }
10435 }
10436 if (!res) {
10437 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10438 res = ast_play_and_wait(chan, "vm-no");
10439 if (!res)
10440 res = ast_play_and_wait(chan, "vm-messages");
10441 }
10442 }
10443 }
10444 return res;
10445}
10446
10447/* SPANISH syntax */
10448static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
10449{
10450 /* Introduce messages they have */
10451 int res;
10452 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10453 res = ast_play_and_wait(chan, "vm-youhaveno");
10454 if (!res)
10455 res = ast_play_and_wait(chan, "vm-messages");
10456 } else {
10457 res = ast_play_and_wait(chan, "vm-youhave");
10458 }
10459 if (!res) {
10460 if (vms->newmessages) {
10461 if (!res) {
10462 if (vms->newmessages == 1) {
10463 res = ast_play_and_wait(chan, "digits/1M");
10464 if (!res)
10465 res = ast_play_and_wait(chan, "vm-message");
10466 if (!res)
10467 res = ast_play_and_wait(chan, "vm-INBOXs");
10468 } else {
10469 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10470 if (!res)
10471 res = ast_play_and_wait(chan, "vm-messages");
10472 if (!res)
10473 res = ast_play_and_wait(chan, "vm-INBOX");
10474 }
10475 }
10476 if (vms->oldmessages && !res)
10477 res = ast_play_and_wait(chan, "vm-and");
10478 }
10479 if (vms->oldmessages) {
10480 if (!res) {
10481 if (vms->oldmessages == 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-Olds");
10487 } else {
10488 res = say_and_wait(chan, vms->oldmessages, 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-Old");
10493 }
10494 }
10495 }
10496 }
10497return res;
10498}
10499
10500/* BRAZILIAN PORTUGUESE syntax */
10501static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms) {
10502 /* Introduce messages they have */
10503 int res;
10504 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10505 res = ast_play_and_wait(chan, "vm-nomessages");
10506 return res;
10507 } else {
10508 res = ast_play_and_wait(chan, "vm-youhave");
10509 }
10510 if (vms->newmessages) {
10511 if (!res)
10512 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10513 if (vms->newmessages == 1) {
10514 if (!res)
10515 res = ast_play_and_wait(chan, "vm-message");
10516 if (!res)
10517 res = ast_play_and_wait(chan, "vm-INBOXs");
10518 } else {
10519 if (!res)
10520 res = ast_play_and_wait(chan, "vm-messages");
10521 if (!res)
10522 res = ast_play_and_wait(chan, "vm-INBOX");
10523 }
10524 if (vms->oldmessages && !res)
10525 res = ast_play_and_wait(chan, "vm-and");
10526 }
10527 if (vms->oldmessages) {
10528 if (!res)
10529 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10530 if (vms->oldmessages == 1) {
10531 if (!res)
10532 res = ast_play_and_wait(chan, "vm-message");
10533 if (!res)
10534 res = ast_play_and_wait(chan, "vm-Olds");
10535 } else {
10536 if (!res)
10537 res = ast_play_and_wait(chan, "vm-messages");
10538 if (!res)
10539 res = ast_play_and_wait(chan, "vm-Old");
10540 }
10541 }
10542 return res;
10543}
10544
10545/* FRENCH syntax */
10546static int vm_intro_fr(struct ast_channel *chan, struct vm_state *vms)
10547{
10548 /* Introduce messages they have */
10549 int res;
10550 res = ast_play_and_wait(chan, "vm-youhave");
10551 if (!res) {
10552 if (vms->newmessages) {
10553 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10554 if (!res)
10555 res = ast_play_and_wait(chan, "vm-INBOX");
10556 if (vms->oldmessages && !res)
10557 res = ast_play_and_wait(chan, "vm-and");
10558 else if (!res) {
10559 if (vms->newmessages == 1)
10560 res = ast_play_and_wait(chan, "vm-message");
10561 else
10562 res = ast_play_and_wait(chan, "vm-messages");
10563 }
10564
10565 }
10566 if (!res && vms->oldmessages) {
10567 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10568 if (!res)
10569 res = ast_play_and_wait(chan, "vm-Old");
10570 if (!res) {
10571 if (vms->oldmessages == 1)
10572 res = ast_play_and_wait(chan, "vm-message");
10573 else
10574 res = ast_play_and_wait(chan, "vm-messages");
10575 }
10576 }
10577 if (!res) {
10578 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10579 res = ast_play_and_wait(chan, "vm-no");
10580 if (!res)
10581 res = ast_play_and_wait(chan, "vm-messages");
10582 }
10583 }
10584 }
10585 return res;
10586}
10587
10588/* DUTCH syntax */
10589static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
10590{
10591 /* Introduce messages they have */
10592 int res;
10593 res = ast_play_and_wait(chan, "vm-youhave");
10594 if (!res) {
10595 if (vms->newmessages) {
10596 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10597 if (!res) {
10598 if (vms->newmessages == 1)
10599 res = ast_play_and_wait(chan, "vm-INBOXs");
10600 else
10601 res = ast_play_and_wait(chan, "vm-INBOX");
10602 }
10603 if (vms->oldmessages && !res)
10604 res = ast_play_and_wait(chan, "vm-and");
10605 else if (!res) {
10606 if (vms->newmessages == 1)
10607 res = ast_play_and_wait(chan, "vm-message");
10608 else
10609 res = ast_play_and_wait(chan, "vm-messages");
10610 }
10611
10612 }
10613 if (!res && vms->oldmessages) {
10614 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10615 if (!res) {
10616 if (vms->oldmessages == 1)
10617 res = ast_play_and_wait(chan, "vm-Olds");
10618 else
10619 res = ast_play_and_wait(chan, "vm-Old");
10620 }
10621 if (!res) {
10622 if (vms->oldmessages == 1)
10623 res = ast_play_and_wait(chan, "vm-message");
10624 else
10625 res = ast_play_and_wait(chan, "vm-messages");
10626 }
10627 }
10628 if (!res) {
10629 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10630 res = ast_play_and_wait(chan, "vm-no");
10631 if (!res)
10632 res = ast_play_and_wait(chan, "vm-messages");
10633 }
10634 }
10635 }
10636 return res;
10637}
10638
10639/* PORTUGUESE syntax */
10640static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
10641{
10642 /* Introduce messages they have */
10643 int res;
10644 res = ast_play_and_wait(chan, "vm-youhave");
10645 if (!res) {
10646 if (vms->newmessages) {
10647 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10648 if (!res) {
10649 if (vms->newmessages == 1) {
10650 res = ast_play_and_wait(chan, "vm-message");
10651 if (!res)
10652 res = ast_play_and_wait(chan, "vm-INBOXs");
10653 } else {
10654 res = ast_play_and_wait(chan, "vm-messages");
10655 if (!res)
10656 res = ast_play_and_wait(chan, "vm-INBOX");
10657 }
10658 }
10659 if (vms->oldmessages && !res)
10660 res = ast_play_and_wait(chan, "vm-and");
10661 }
10662 if (!res && vms->oldmessages) {
10663 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, ast_channel_language(chan), "f");
10664 if (!res) {
10665 if (vms->oldmessages == 1) {
10666 res = ast_play_and_wait(chan, "vm-message");
10667 if (!res)
10668 res = ast_play_and_wait(chan, "vm-Olds");
10669 } else {
10670 res = ast_play_and_wait(chan, "vm-messages");
10671 if (!res)
10672 res = ast_play_and_wait(chan, "vm-Old");
10673 }
10674 }
10675 }
10676 if (!res) {
10677 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10678 res = ast_play_and_wait(chan, "vm-no");
10679 if (!res)
10680 res = ast_play_and_wait(chan, "vm-messages");
10681 }
10682 }
10683 }
10684 return res;
10685}
10686
10687
10688/* CZECH syntax */
10689/* in czech there must be declension of word new and message
10690 * czech : english : czech : english
10691 * --------------------------------------------------------
10692 * vm-youhave : you have
10693 * vm-novou : one new : vm-zpravu : message
10694 * vm-nove : 2-4 new : vm-zpravy : messages
10695 * vm-novych : 5-infinite new : vm-zprav : messages
10696 * vm-starou : one old
10697 * vm-stare : 2-4 old
10698 * vm-starych : 5-infinite old
10699 * jednu : one - falling 4.
10700 * vm-no : no ( no messages )
10701 */
10702
10703static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
10704{
10705 int res;
10706 res = ast_play_and_wait(chan, "vm-youhave");
10707 if (!res) {
10708 if (vms->newmessages) {
10709 if (vms->newmessages == 1) {
10710 res = ast_play_and_wait(chan, "digits/jednu");
10711 } else {
10712 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10713 }
10714 if (!res) {
10715 if (vms->newmessages == 1)
10716 res = ast_play_and_wait(chan, "vm-novou");
10717 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
10718 res = ast_play_and_wait(chan, "vm-nove");
10719 if (vms->newmessages > 4)
10720 res = ast_play_and_wait(chan, "vm-novych");
10721 }
10722 if (vms->oldmessages && !res)
10723 res = ast_play_and_wait(chan, "vm-and");
10724 else if (!res) {
10725 if (vms->newmessages == 1)
10726 res = ast_play_and_wait(chan, "vm-zpravu");
10727 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
10728 res = ast_play_and_wait(chan, "vm-zpravy");
10729 if (vms->newmessages > 4)
10730 res = ast_play_and_wait(chan, "vm-zprav");
10731 }
10732 }
10733 if (!res && vms->oldmessages) {
10734 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10735 if (!res) {
10736 if (vms->oldmessages == 1)
10737 res = ast_play_and_wait(chan, "vm-starou");
10738 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
10739 res = ast_play_and_wait(chan, "vm-stare");
10740 if (vms->oldmessages > 4)
10741 res = ast_play_and_wait(chan, "vm-starych");
10742 }
10743 if (!res) {
10744 if (vms->oldmessages == 1)
10745 res = ast_play_and_wait(chan, "vm-zpravu");
10746 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
10747 res = ast_play_and_wait(chan, "vm-zpravy");
10748 if (vms->oldmessages > 4)
10749 res = ast_play_and_wait(chan, "vm-zprav");
10750 }
10751 }
10752 if (!res) {
10753 if (!vms->oldmessages && !vms->newmessages && !vms->urgentmessages) {
10754 res = ast_play_and_wait(chan, "vm-no");
10755 if (!res)
10756 res = ast_play_and_wait(chan, "vm-zpravy");
10757 }
10758 }
10759 }
10760 return res;
10761}
10762
10763/* CHINESE (Taiwan) syntax */
10764static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
10765{
10766 int res;
10767 /* Introduce messages they have */
10768 res = ast_play_and_wait(chan, "vm-you");
10769
10770 if (!res && vms->newmessages) {
10771 res = ast_play_and_wait(chan, "vm-have");
10772 if (!res)
10773 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10774 if (!res)
10775 res = ast_play_and_wait(chan, "vm-tong");
10776 if (!res)
10777 res = ast_play_and_wait(chan, "vm-INBOX");
10778 if (vms->oldmessages && !res)
10779 res = ast_play_and_wait(chan, "vm-and");
10780 else if (!res)
10781 res = ast_play_and_wait(chan, "vm-messages");
10782 }
10783 if (!res && vms->oldmessages) {
10784 res = ast_play_and_wait(chan, "vm-have");
10785 if (!res)
10786 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10787 if (!res)
10788 res = ast_play_and_wait(chan, "vm-tong");
10789 if (!res)
10790 res = ast_play_and_wait(chan, "vm-Old");
10791 if (!res)
10792 res = ast_play_and_wait(chan, "vm-messages");
10793 }
10794 if (!res && !vms->oldmessages && !vms->newmessages) {
10795 res = ast_play_and_wait(chan, "vm-haveno");
10796 if (!res)
10797 res = ast_play_and_wait(chan, "vm-messages");
10798 }
10799 return res;
10800}
10801
10802/* Vietnamese syntax */
10803static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
10804{
10805 int res;
10806
10807 /* Introduce messages they have */
10808 res = ast_play_and_wait(chan, "vm-youhave");
10809 if (!res) {
10810 if (vms->newmessages) {
10811 res = say_and_wait(chan, vms->newmessages, ast_channel_language(chan));
10812 if (!res)
10813 res = ast_play_and_wait(chan, "vm-INBOX");
10814 if (vms->oldmessages && !res)
10815 res = ast_play_and_wait(chan, "vm-and");
10816 }
10817 if (!res && vms->oldmessages) {
10818 res = say_and_wait(chan, vms->oldmessages, ast_channel_language(chan));
10819 if (!res)
10820 res = ast_play_and_wait(chan, "vm-Old");
10821 }
10822 if (!res) {
10823 if (!vms->oldmessages && !vms->newmessages) {
10824 res = ast_play_and_wait(chan, "vm-no");
10825 if (!res)
10826 res = ast_play_and_wait(chan, "vm-message");
10827 }
10828 }
10829 }
10830 return res;
10831}
10832
10833static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
10834{
10835 char prefile[256];
10836
10837 /* Notify the user that the temp greeting is set and give them the option to remove it */
10838 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
10839 if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
10840 RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
10841 if (ast_fileexists(prefile, NULL, NULL) > 0) {
10842 ast_play_and_wait(chan, "vm-tempgreetactive");
10843 }
10844 DISPOSE(prefile, -1);
10845 }
10846
10847 /* Play voicemail intro - syntax is different for different languages */
10848 if (0) {
10849 return 0;
10850 } else if (!strncasecmp(ast_channel_language(chan), "cs", 2)) { /* CZECH syntax */
10851 return vm_intro_cs(chan, vms);
10852 } else if (!strncasecmp(ast_channel_language(chan), "cz", 2)) { /* deprecated CZECH syntax */
10853 static int deprecation_warning = 0;
10854 if (deprecation_warning++ % 10 == 0) {
10855 ast_log(LOG_WARNING, "cz is not a standard language code. Please switch to using cs instead.\n");
10856 }
10857 return vm_intro_cs(chan, vms);
10858 } else if (!strncasecmp(ast_channel_language(chan), "de", 2)) { /* GERMAN syntax */
10859 return vm_intro_de(chan, vms);
10860 } else if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH syntax */
10861 return vm_intro_es(chan, vms);
10862 } else if (!strncasecmp(ast_channel_language(chan), "fr", 2)) { /* FRENCH syntax */
10863 return vm_intro_fr(chan, vms);
10864 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK syntax */
10865 return vm_intro_gr(chan, vms);
10866 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW syntax */
10867 return vm_intro_he(chan, vms);
10868 } else if (!strncasecmp(ast_channel_language(chan), "is", 2)) { /* ICELANDIC syntax */
10869 return vm_intro_is(chan, vms);
10870 } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN syntax */
10871 return vm_intro_it(chan, vms);
10872 } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE syntax */
10873 return vm_intro_ja(chan, vms);
10874 } else if (!strncasecmp(ast_channel_language(chan), "nl", 2)) { /* DUTCH syntax */
10875 return vm_intro_nl(chan, vms);
10876 } else if (!strncasecmp(ast_channel_language(chan), "no", 2)) { /* NORWEGIAN syntax */
10877 return vm_intro_no(chan, vms);
10878 } else if (!strncasecmp(ast_channel_language(chan), "da", 2)) { /* DANISH syntax */
10879 return vm_intro_da(chan, vms);
10880 } else if (!strncasecmp(ast_channel_language(chan), "pl", 2)) { /* POLISH syntax */
10881 return vm_intro_pl(chan, vms);
10882 } else if (!strncasecmp(ast_channel_language(chan), "pt_BR", 5)) { /* BRAZILIAN PORTUGUESE syntax */
10883 return vm_intro_pt_BR(chan, vms);
10884 } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE syntax */
10885 return vm_intro_pt(chan, vms);
10886 } else if (!strncasecmp(ast_channel_language(chan), "ru", 2)) { /* RUSSIAN syntax */
10887 return vm_intro_multilang(chan, vms, "n");
10888 } else if (!strncasecmp(ast_channel_language(chan), "se", 2)) { /* SWEDISH syntax */
10889 return vm_intro_se(chan, vms);
10890 } else if (!strncasecmp(ast_channel_language(chan), "ua", 2)) { /* UKRAINIAN syntax */
10891 return vm_intro_multilang(chan, vms, "n");
10892 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE syntax */
10893 return vm_intro_vi(chan, vms);
10894 } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
10895 return vm_intro_zh(chan, vms);
10896 } else { /* Default to ENGLISH */
10897 return vm_intro_en(chan, vms);
10898 }
10899}
10900
10901static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
10902{
10903 int res = 0;
10904 /* Play instructions and wait for new command */
10905 while (!res) {
10906 if (vms->starting) {
10907 if (vms->lastmsg > -1) {
10908 if (skipadvanced)
10909 res = ast_play_and_wait(chan, "vm-onefor-full");
10910 else
10911 res = ast_play_and_wait(chan, "vm-onefor");
10912 if (!res)
10913 res = vm_play_folder_name(chan, vms->vmbox);
10914 }
10915 if (!res) {
10916 if (skipadvanced)
10917 res = ast_play_and_wait(chan, "vm-opts-full");
10918 else
10919 res = ast_play_and_wait(chan, "vm-opts");
10920 }
10921 } else {
10922 /* Added for additional help */
10923 if (skipadvanced) {
10924 res = ast_play_and_wait(chan, "vm-onefor-full");
10925 if (!res)
10926 res = vm_play_folder_name(chan, vms->vmbox);
10927 res = ast_play_and_wait(chan, "vm-opts-full");
10928 }
10929 /* Logic:
10930 * If the current message is not the first OR
10931 * if we're listening to the first new message and there are
10932 * also urgent messages, then prompt for navigation to the
10933 * previous message
10934 */
10935 if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
10936 res = ast_play_and_wait(chan, "vm-prev");
10937 }
10938 if (!res && !skipadvanced)
10939 res = ast_play_and_wait(chan, "vm-advopts");
10940 if (!res)
10941 res = ast_play_and_wait(chan, "vm-repeat");
10942 /* Logic:
10943 * If we're not listening to the last message OR
10944 * we're listening to the last urgent message and there are
10945 * also new non-urgent messages, then prompt for navigation
10946 * to the next message
10947 */
10948 if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
10949 (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
10950 res = ast_play_and_wait(chan, "vm-next");
10951 }
10952 if (!res) {
10953 int curmsg_deleted;
10954#ifdef IMAP_STORAGE
10955 ast_mutex_lock(&vms->lock);
10956#endif
10957 curmsg_deleted = vms->deleted[vms->curmsg];
10958#ifdef IMAP_STORAGE
10959 ast_mutex_unlock(&vms->lock);
10960#endif
10961 if (!nodelete) {
10962 if (!curmsg_deleted) {
10963 res = ast_play_and_wait(chan, "vm-delete");
10964 } else {
10965 res = ast_play_and_wait(chan, "vm-undelete");
10966 }
10967 }
10968 if (!res) {
10969 res = ast_play_and_wait(chan, "vm-toforward");
10970 }
10971 if (!res) {
10972 res = ast_play_and_wait(chan, "vm-savemessage");
10973 }
10974 }
10975 }
10976 if (!res) {
10977 res = ast_play_and_wait(chan, "vm-helpexit");
10978 }
10979 if (!res)
10980 res = ast_waitfordigit(chan, 6000);
10981 if (!res) {
10982 vms->repeats++;
10983 if (vms->repeats > 2) {
10984 res = 't';
10985 }
10986 }
10987 }
10988 return res;
10989}
10990
10991static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
10992{
10993 int res = 0;
10994 /* Play instructions and wait for new command */
10995 while (!res) {
10996 if (vms->starting) {
10997 if (vms->lastmsg > -1) {
10998 res = vm_play_folder_name(chan, vms->vmbox);
10999 if (!res)
11000 res = ast_play_and_wait(chan, "jp-wa");
11001 if (!res)
11002 res = ast_play_and_wait(chan, "digits/1");
11003 if (!res)
11004 res = ast_play_and_wait(chan, "jp-wo");
11005 if (!res)
11006 res = ast_play_and_wait(chan, "silence/1");
11007 }
11008 if (!res)
11009 res = ast_play_and_wait(chan, "vm-opts");
11010 } else {
11011 /* Added for additional help */
11012 if (skipadvanced) {
11013 res = vm_play_folder_name(chan, vms->vmbox);
11014 if (!res)
11015 res = ast_play_and_wait(chan, "jp-wa");
11016 if (!res)
11017 res = ast_play_and_wait(chan, "digits/1");
11018 if (!res)
11019 res = ast_play_and_wait(chan, "jp-wo");
11020 if (!res)
11021 res = ast_play_and_wait(chan, "silence/1");
11022 res = ast_play_and_wait(chan, "vm-opts-full");
11023 }
11024 /* Logic:
11025 * If the current message is not the first OR
11026 * if we're listening to the first new message and there are
11027 * also urgent messages, then prompt for navigation to the
11028 * previous message
11029 */
11030 if (vms->curmsg || (!in_urgent && vms->urgentmessages > 0) || (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0)) {
11031 res = ast_play_and_wait(chan, "vm-prev");
11032 }
11033 if (!res && !skipadvanced)
11034 res = ast_play_and_wait(chan, "vm-advopts");
11035 if (!res)
11036 res = ast_play_and_wait(chan, "vm-repeat");
11037 /* Logic:
11038 * If we're not listening to the last message OR
11039 * we're listening to the last urgent message and there are
11040 * also new non-urgent messages, then prompt for navigation
11041 * to the next message
11042 */
11043 if (!res && ((vms->curmsg != vms->lastmsg) || (in_urgent && vms->newmessages > 0) ||
11044 (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms->lastmsg > 0) )) {
11045 res = ast_play_and_wait(chan, "vm-next");
11046 }
11047 if (!res) {
11048 int curmsg_deleted;
11049#ifdef IMAP_STORAGE
11050 ast_mutex_lock(&vms->lock);
11051#endif
11052 curmsg_deleted = vms->deleted[vms->curmsg];
11053#ifdef IMAP_STORAGE
11054 ast_mutex_unlock(&vms->lock);
11055#endif
11056 if (!curmsg_deleted) {
11057 res = ast_play_and_wait(chan, "vm-delete");
11058 } else {
11059 res = ast_play_and_wait(chan, "vm-undelete");
11060 }
11061 if (!res) {
11062 res = ast_play_and_wait(chan, "vm-toforward");
11063 }
11064 if (!res) {
11065 res = ast_play_and_wait(chan, "vm-savemessage");
11066 }
11067 }
11068 }
11069
11070 if (!res) {
11071 res = ast_play_and_wait(chan, "vm-helpexit");
11072 }
11073 if (!res)
11074 res = ast_waitfordigit(chan, 6000);
11075 if (!res) {
11076 vms->repeats++;
11077 if (vms->repeats > 2) {
11078 res = 't';
11079 }
11080 }
11081
11082 }
11083
11084 return res;
11085}
11086
11087static int vm_instructions_zh(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
11088{
11089 int res = 0;
11090 /* Play instructions and wait for new command */
11091 while (!res) {
11092 if (vms->lastmsg > -1) {
11093 res = ast_play_and_wait(chan, "vm-listen");
11094 if (!res)
11095 res = vm_play_folder_name(chan, vms->vmbox);
11096 if (!res)
11097 res = ast_play_and_wait(chan, "press");
11098 if (!res)
11099 res = ast_play_and_wait(chan, "digits/1");
11100 }
11101 if (!res)
11102 res = ast_play_and_wait(chan, "vm-opts");
11103 if (!res) {
11104 vms->starting = 0;
11105 return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11106 }
11107 }
11108 return res;
11109}
11110
11111static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
11112{
11113 if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* Japanese syntax */
11114 return vm_instructions_ja(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11115 } else if (vms->starting && !strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) syntax */
11116 return vm_instructions_zh(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11117 } else { /* Default to ENGLISH */
11118 return vm_instructions_en(chan, vmu, vms, skipadvanced, in_urgent, nodelete);
11119 }
11120}
11121
11122static int vm_newuser_setup(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11123{
11124 int cmd = 0;
11125 int duration = 0;
11126 int tries = 0;
11127 char newpassword[80] = "";
11128 char newpassword2[80] = "";
11129 char prefile[PATH_MAX] = "";
11130 unsigned char buf[256];
11131 int bytes = 0;
11132
11133 ast_test_suite_event_notify("NEWUSER", "Message: entering new user state");
11134 if (ast_adsi_available(chan)) {
11135 bytes += adsi_logo(buf + bytes);
11136 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
11137 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11138 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11139 bytes += ast_adsi_voice_mode(buf + bytes, 0);
11141 }
11142
11143 /* If forcename is set, have the user record their name */
11144 if (ast_test_flag(vmu, VM_FORCENAME)) {
11145 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
11146 if (ast_fileexists(prefile, NULL, NULL) < 1) {
11147 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11148 if (cmd < 0 || cmd == 't' || cmd == '#')
11149 return cmd;
11150 }
11151 }
11152
11153 /* If forcegreetings is set, have the user record their greetings */
11154 if (ast_test_flag(vmu, VM_FORCEGREET)) {
11155 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
11156 if (ast_fileexists(prefile, NULL, NULL) < 1) {
11157 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11158 if (cmd < 0 || cmd == 't' || cmd == '#')
11159 return cmd;
11160 }
11161
11162 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
11163 if (ast_fileexists(prefile, NULL, NULL) < 1) {
11164 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11165 if (cmd < 0 || cmd == 't' || cmd == '#')
11166 return cmd;
11167 }
11168 }
11169
11170 /*
11171 * Change the password last since new users will be able to skip over any steps this one comes before
11172 * by hanging up and calling back to voicemail main since the password is used to verify new user status.
11173 */
11174 for (;;) {
11175 newpassword[1] = '\0';
11176 newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
11177 if (cmd == '#')
11178 newpassword[0] = '\0';
11179 if (cmd < 0 || cmd == 't' || cmd == '#')
11180 return cmd;
11181 cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#");
11182 if (cmd < 0 || cmd == 't' || cmd == '#')
11183 return cmd;
11184 cmd = check_password(vmu, newpassword); /* perform password validation */
11185 if (cmd != 0) {
11186 ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
11188 } else {
11189 newpassword2[1] = '\0';
11190 newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
11191 if (cmd == '#')
11192 newpassword2[0] = '\0';
11193 if (cmd < 0 || cmd == 't' || cmd == '#')
11194 return cmd;
11195 cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#");
11196 if (cmd < 0 || cmd == 't' || cmd == '#')
11197 return cmd;
11198 if (!strcmp(newpassword, newpassword2))
11199 break;
11200 ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
11201 cmd = ast_play_and_wait(chan, vm_mismatch);
11202 }
11203 if (++tries == 3)
11204 return -1;
11205 if (cmd != 0) {
11207 }
11208 }
11210 vm_change_password(vmu, newpassword);
11212 vm_change_password_shell(vmu, newpassword);
11213
11214 ast_debug(1, "User %s set password to %s of length %d\n", vms->username, newpassword, (int) strlen(newpassword));
11215 cmd = ast_play_and_wait(chan, vm_passchanged);
11216
11217 return cmd;
11218}
11219
11220static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11221{
11222 int cmd = 0;
11223 int retries = 0;
11224 int duration = 0;
11225 char newpassword[80] = "";
11226 char newpassword2[80] = "";
11227 char prefile[PATH_MAX] = "";
11228 unsigned char buf[256];
11229 int bytes = 0;
11230 SCOPE_ENTER(3, "%s: %s entering mailbox options", ast_channel_name(chan), vms->username);
11231
11232 ast_test_suite_event_notify("VMOPTIONS", "Message: entering mailbox options");
11233 if (ast_adsi_available(chan)) {
11234 bytes += adsi_logo(buf + bytes);
11235 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
11236 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11237 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11238 bytes += ast_adsi_voice_mode(buf + bytes, 0);
11240 }
11241 while ((cmd >= 0) && (cmd != 't')) {
11242 if (cmd)
11243 retries = 0;
11244 switch (cmd) {
11245 case '1': /* Record your unavailable message */
11246 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
11247 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);
11248 break;
11249 case '2': /* Record your busy message */
11250 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
11251 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);
11252 break;
11253 case '3': /* Record greeting */
11254 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
11255 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);
11256 break;
11257 case '4': /* manage the temporary greeting */
11258 cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
11259 break;
11260 case '5': /* change password */
11261 if (vmu->password[0] == '-') {
11262 cmd = ast_play_and_wait(chan, "vm-no");
11263 break;
11264 }
11265 newpassword[1] = '\0';
11266 newpassword[0] = cmd = ast_play_and_wait(chan, vm_newpassword);
11267 if (cmd == '#')
11268 newpassword[0] = '\0';
11269 else {
11270 if (cmd < 0)
11271 break;
11272 if ((cmd = ast_readstring(chan, newpassword + strlen(newpassword), sizeof(newpassword) - 1, 2000, 10000, "#")) < 0) {
11273 break;
11274 }
11275 }
11276 cmd = check_password(vmu, newpassword); /* perform password validation */
11277 if (cmd != 0) {
11278 ast_log(AST_LOG_NOTICE, "Invalid password for user %s (%s)\n", vms->username, newpassword);
11280 if (!cmd) {
11282 }
11283 break;
11284 }
11285 newpassword2[1] = '\0';
11286 newpassword2[0] = cmd = ast_play_and_wait(chan, vm_reenterpassword);
11287 if (cmd == '#')
11288 newpassword2[0] = '\0';
11289 else {
11290 if (cmd < 0)
11291 break;
11292
11293 if ((cmd = ast_readstring(chan, newpassword2 + strlen(newpassword2), sizeof(newpassword2) - 1, 2000, 10000, "#")) < 0) {
11294 break;
11295 }
11296 }
11297 if (strcmp(newpassword, newpassword2)) {
11298 ast_log(AST_LOG_NOTICE, "Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
11299 cmd = ast_play_and_wait(chan, vm_mismatch);
11300 if (!cmd) {
11302 }
11303 break;
11304 }
11305
11307 vm_change_password(vmu, newpassword);
11308 }
11310 vm_change_password_shell(vmu, newpassword);
11311 }
11312
11313 ast_debug(1, "User %s set password to %s of length %d\n",
11314 vms->username, newpassword, (int) strlen(newpassword));
11315 cmd = ast_play_and_wait(chan, vm_passchanged);
11316 break;
11317 case '*':
11318 cmd = 't';
11319 break;
11320 default:
11321 cmd = 0;
11322 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
11323 SCOPE_CALL(-1, RETRIEVE, prefile, -1, vmu->mailbox, vmu->context);
11324 if (ast_fileexists(prefile, NULL, NULL)) {
11325 cmd = ast_play_and_wait(chan, "vm-tmpexists");
11326 }
11327 SCOPE_CALL(-1, DISPOSE, prefile, -1);
11328 if (!cmd) {
11329 cmd = ast_play_and_wait(chan, "vm-options");
11330 }
11331 if (!cmd) {
11332 cmd = ast_waitfordigit(chan, 6000);
11333 }
11334 if (!cmd) {
11335 retries++;
11336 }
11337 if (retries > 3) {
11338 cmd = 't';
11339 }
11340 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
11341 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
11342 }
11343 }
11344 if (cmd == 't')
11345 cmd = 0;
11346 SCOPE_EXIT_RTN_VALUE(cmd, "Done\n");
11347}
11348
11349/*!
11350 * \brief The handler for 'record a temporary greeting'.
11351 * \param chan
11352 * \param vmu
11353 * \param vms
11354 * \param fmtc
11355 * \param record_gain
11356 *
11357 * This is option 4 from the mailbox options menu.
11358 * This function manages the following promptings:
11359 * 1: play / record / review the temporary greeting. : invokes play_record_review().
11360 * 2: remove (delete) the temporary greeting.
11361 * *: return to the main menu.
11362 *
11363 * \return zero on success, -1 on error.
11364 */
11365static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
11366{
11367 int cmd = 0;
11368 int retries = 0;
11369 int duration = 0;
11370 char prefile[PATH_MAX] = "";
11371 unsigned char buf[256];
11372 int bytes = 0;
11373
11374 if (ast_adsi_available(chan)) {
11375 bytes += adsi_logo(buf + bytes);
11376 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
11377 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
11378 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
11379 bytes += ast_adsi_voice_mode(buf + bytes, 0);
11381 }
11382
11383 ast_test_suite_event_notify("TEMPGREETING", "Message: entering temp greeting options");
11384 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
11385 while ((cmd >= 0) && (cmd != 't')) {
11386 if (cmd)
11387 retries = 0;
11388 RETRIEVE(prefile, -1, vmu->mailbox, vmu->context);
11389 if (ast_fileexists(prefile, NULL, NULL) <= 0) {
11390 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11391 if (cmd == -1) {
11392 break;
11393 }
11394 cmd = 't';
11395 } else {
11396 switch (cmd) {
11397 case '1':
11398 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, NULL, record_gain, vms, NULL, NULL, 0);
11399 break;
11400 case '2':
11401 DELETE(prefile, -1, prefile, vmu);
11402 ast_play_and_wait(chan, "vm-tempremoved");
11403 cmd = 't';
11404 break;
11405 case '*':
11406 cmd = 't';
11407 break;
11408 default:
11409 cmd = ast_play_and_wait(chan,
11410 ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
11411 "vm-tempgreeting2" : "vm-tempgreeting");
11412 if (!cmd) {
11413 cmd = ast_waitfordigit(chan, 6000);
11414 }
11415 if (!cmd) {
11416 retries++;
11417 }
11418 if (retries > 3) {
11419 cmd = 't';
11420 }
11421 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
11422 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
11423 }
11424 }
11425 DISPOSE(prefile, -1);
11426 }
11427 if (cmd == 't')
11428 cmd = 0;
11429 return cmd;
11430}
11431
11432/*!
11433 * \brief Greek syntax for 'You have N messages' greeting.
11434 * \param chan
11435 * \param vms
11436 * \param vmu
11437 *
11438 * \return zero on success, -1 on error.
11439 */
11440static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11441{
11442 int cmd = 0;
11443
11444 if (vms->lastmsg > -1) {
11445 cmd = play_message(chan, vmu, vms);
11446 } else {
11447 cmd = ast_play_and_wait(chan, "vm-youhaveno");
11448 if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
11449 if (!cmd) {
11450 snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
11451 cmd = ast_play_and_wait(chan, vms->fn);
11452 }
11453 if (!cmd)
11454 cmd = ast_play_and_wait(chan, "vm-messages");
11455 } else {
11456 if (!cmd)
11457 cmd = ast_play_and_wait(chan, "vm-messages");
11458 if (!cmd) {
11459 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11460 cmd = ast_play_and_wait(chan, vms->fn);
11461 }
11462 }
11463 }
11464 return cmd;
11465}
11466
11467/* Hebrew Syntax */
11468static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11469{
11470 int cmd = 0;
11471
11472 if (vms->lastmsg > -1) {
11473 cmd = play_message(chan, vmu, vms);
11474 } else {
11475 if (!strcasecmp(vms->fn, "INBOX")) {
11476 cmd = ast_play_and_wait(chan, "vm-nonewmessages");
11477 } else {
11478 cmd = ast_play_and_wait(chan, "vm-nomessages");
11479 }
11480 }
11481 return cmd;
11482}
11483
11484/*!
11485 * \brief Default English syntax for 'You have N messages' greeting.
11486 * \param chan
11487 * \param vms
11488 * \param vmu
11489 *
11490 * \return zero on success, -1 on error.
11491 */
11492static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11493{
11494 int cmd = 0;
11495
11496 if (vms->lastmsg > -1) {
11497 cmd = play_message(chan, vmu, vms);
11498 } else {
11499 cmd = ast_play_and_wait(chan, "vm-youhave");
11500 if (!cmd)
11501 cmd = ast_play_and_wait(chan, "vm-no");
11502 if (!cmd) {
11503 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11504 cmd = ast_play_and_wait(chan, vms->fn);
11505 }
11506 if (!cmd)
11507 cmd = ast_play_and_wait(chan, "vm-messages");
11508 }
11509 return cmd;
11510}
11511
11512/*!
11513 *\brief Italian syntax for 'You have N messages' greeting.
11514 * \param chan
11515 * \param vms
11516 * \param vmu
11517 *
11518 * \return zero on success, -1 on error.
11519 */
11520static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11521{
11522 int cmd;
11523
11524 if (vms->lastmsg > -1) {
11525 cmd = play_message(chan, vmu, vms);
11526 } else {
11527 cmd = ast_play_and_wait(chan, "vm-no");
11528 if (!cmd)
11529 cmd = ast_play_and_wait(chan, "vm-message");
11530 if (!cmd) {
11531 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11532 cmd = ast_play_and_wait(chan, vms->fn);
11533 }
11534 }
11535 return cmd;
11536}
11537
11538/*!
11539 * \brief Japanese syntax for 'You have N messages' greeting.
11540 * \param chan
11541 * \param vms
11542 * \param vmu
11543 *
11544 * \return zero on success, -1 on error.
11545 */
11546static int vm_browse_messages_ja(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11547{
11548 int cmd = 0;
11549
11550 if (vms->lastmsg > -1) {
11551 cmd = play_message(chan, vmu, vms);
11552 } else {
11553 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11554 cmd = ast_play_and_wait(chan, vms->fn);
11555 if (!cmd)
11556 cmd = ast_play_and_wait(chan, "vm-messages");
11557 if (!cmd)
11558 cmd = ast_play_and_wait(chan, "jp-wa");
11559 if (!cmd)
11560 cmd = ast_play_and_wait(chan, "jp-arimasen");
11561 }
11562 return cmd;
11563}
11564
11565/*!
11566 * \brief Spanish syntax for 'You have N messages' greeting.
11567 * \param chan
11568 * \param vms
11569 * \param vmu
11570 *
11571 * \return zero on success, -1 on error.
11572 */
11573static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11574{
11575 int cmd;
11576
11577 if (vms->lastmsg > -1) {
11578 cmd = play_message(chan, vmu, vms);
11579 } else {
11580 cmd = ast_play_and_wait(chan, "vm-youhaveno");
11581 if (!cmd)
11582 cmd = ast_play_and_wait(chan, "vm-messages");
11583 if (!cmd) {
11584 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11585 cmd = ast_play_and_wait(chan, vms->fn);
11586 }
11587 }
11588 return cmd;
11589}
11590
11591/*!
11592 * \brief Portuguese syntax for 'You have N messages' greeting.
11593 * \param chan
11594 * \param vms
11595 * \param vmu
11596 *
11597 * \return zero on success, -1 on error.
11598 */
11599static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11600{
11601 int cmd;
11602
11603 if (vms->lastmsg > -1) {
11604 cmd = play_message(chan, vmu, vms);
11605 } else {
11606 cmd = ast_play_and_wait(chan, "vm-no");
11607 if (!cmd) {
11608 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11609 cmd = ast_play_and_wait(chan, vms->fn);
11610 }
11611 if (!cmd)
11612 cmd = ast_play_and_wait(chan, "vm-messages");
11613 }
11614 return cmd;
11615}
11616
11617/*!
11618 * \brief Chinese (Taiwan)syntax for 'You have N messages' greeting.
11619 * \param chan
11620 * \param vms
11621 * \param vmu
11622 *
11623 * \return zero on success, -1 on error.
11624 */
11625static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11626{
11627 int cmd;
11628
11629 if (vms->lastmsg > -1) {
11630 cmd = play_message(chan, vmu, vms);
11631 } else {
11632 cmd = ast_play_and_wait(chan, "vm-you");
11633 if (!cmd)
11634 cmd = ast_play_and_wait(chan, "vm-haveno");
11635 if (!cmd)
11636 cmd = ast_play_and_wait(chan, "vm-messages");
11637 if (!cmd) {
11638 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11639 cmd = ast_play_and_wait(chan, vms->fn);
11640 }
11641 }
11642 return cmd;
11643}
11644
11645/*!
11646 * \brief Vietnamese syntax for 'You have N messages' greeting.
11647 * \param chan
11648 * \param vms
11649 * \param vmu
11650 *
11651 * \return zero on success, -1 on error.
11652 */
11653static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11654{
11655 int cmd = 0;
11656
11657 if (vms->lastmsg > -1) {
11658 cmd = play_message(chan, vmu, vms);
11659 } else {
11660 cmd = ast_play_and_wait(chan, "vm-no");
11661 if (!cmd) {
11662 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
11663 cmd = ast_play_and_wait(chan, vms->fn);
11664 }
11665 }
11666 return cmd;
11667}
11668
11669/*!
11670 * \brief Top level method to invoke the language variant vm_browse_messages_XX function.
11671 * \param chan The channel for the current user. We read the language property from this.
11672 * \param vms passed into the language-specific vm_browse_messages function.
11673 * \param vmu passed into the language-specific vm_browse_messages function.
11674 *
11675 * The method to be invoked is determined by the value of language code property in the user's channel.
11676 * The default (when unable to match) is to use english.
11677 *
11678 * \return zero on success, -1 on error.
11679 */
11680static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
11681{
11682 if (!strncasecmp(ast_channel_language(chan), "es", 2)) { /* SPANISH */
11683 return vm_browse_messages_es(chan, vms, vmu);
11684 } else if (!strncasecmp(ast_channel_language(chan), "gr", 2)) { /* GREEK */
11685 return vm_browse_messages_gr(chan, vms, vmu);
11686 } else if (!strncasecmp(ast_channel_language(chan), "he", 2)) { /* HEBREW */
11687 return vm_browse_messages_he(chan, vms, vmu);
11688 } else if (!strncasecmp(ast_channel_language(chan), "it", 2)) { /* ITALIAN */
11689 return vm_browse_messages_it(chan, vms, vmu);
11690 } else if (!strncasecmp(ast_channel_language(chan), "ja", 2)) { /* JAPANESE */
11691 return vm_browse_messages_ja(chan, vms, vmu);
11692 } else if (!strncasecmp(ast_channel_language(chan), "pt", 2)) { /* PORTUGUESE */
11693 return vm_browse_messages_pt(chan, vms, vmu);
11694 } else if (!strncasecmp(ast_channel_language(chan), "vi", 2)) { /* VIETNAMESE */
11695 return vm_browse_messages_vi(chan, vms, vmu);
11696 } else if (!strncasecmp(ast_channel_language(chan), "zh", 2)) { /* CHINESE (Taiwan) */
11697 return vm_browse_messages_zh(chan, vms, vmu);
11698 } else { /* Default to English syntax */
11699 return vm_browse_messages_en(chan, vms, vmu);
11700 }
11701}
11702
11703static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
11704 struct ast_vm_user *res_vmu, const char *context, const char *prefix,
11705 int skipuser, int max_logins, int silent)
11706{
11707 int useadsi = 0, valid = 0, logretries = 0;
11708 char password[AST_MAX_EXTENSION], *passptr = NULL;
11709 struct ast_vm_user vmus, *vmu = NULL;
11710
11711 /* If ADSI is supported, setup login screen */
11712 adsi_begin(chan, &useadsi);
11713 if (!skipuser && useadsi)
11714 adsi_login(chan);
11715 if (!silent && !skipuser && ast_streamfile(chan, vm_login, ast_channel_language(chan))) {
11716 ast_log(AST_LOG_WARNING, "Couldn't stream login file\n");
11717 return -1;
11718 }
11719
11720 /* Authenticate them and get their mailbox/password */
11721
11722 while (!valid && (logretries < max_logins)) {
11723 /* Prompt for, and read in the username */
11724 if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
11725 ast_log(AST_LOG_WARNING, "Couldn't read username\n");
11726 return -1;
11727 }
11728 if (ast_strlen_zero(mailbox)) {
11729 if (ast_channel_caller(chan)->id.number.valid && ast_channel_caller(chan)->id.number.str) {
11730 ast_copy_string(mailbox, ast_channel_caller(chan)->id.number.str, mailbox_size);
11731 } else {
11732 ast_verb(3, "Username not entered\n");
11733 return -1;
11734 }
11735 } else if (mailbox[0] == '*') {
11736 /* user entered '*' */
11737 ast_verb(4, "Mailbox begins with '*', attempting jump to extension 'a'\n");
11738 if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
11739 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
11740 return -1;
11741 }
11742 ast_verb(4, "Jump to extension 'a' failed; setting mailbox to NULL\n");
11743 mailbox[0] = '\0';
11744 }
11745
11746 if (useadsi)
11747 adsi_password(chan);
11748
11749 if (!ast_strlen_zero(prefix)) {
11750 char fullusername[80];
11751
11752 ast_copy_string(fullusername, prefix, sizeof(fullusername));
11753 strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
11754 ast_copy_string(mailbox, fullusername, mailbox_size);
11755 }
11756
11757 ast_debug(1, "Before find user for mailbox %s\n", mailbox);
11758 memset(&vmus, 0, sizeof(vmus));
11759 vmu = find_user(&vmus, context, mailbox);
11760 if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
11761 /* saved password is blank, so don't bother asking */
11762 password[0] = '\0';
11763 } else {
11765 if (!ast_check_hangup(chan)) {
11766 ast_log(AST_LOG_WARNING, "Unable to stream password file\n");
11767 }
11768 free_user(vmu);
11769 return -1;
11770 }
11771 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
11772 ast_log(AST_LOG_NOTICE, "Unable to read password\n");
11773 free_user(vmu);
11774 return -1;
11775 } else if (password[0] == '*') {
11776 /* user entered '*' */
11777 ast_verb(4, "Password begins with '*', attempting jump to extension 'a'\n");
11778 if (ast_exists_extension(chan, ast_channel_context(chan), "a", 1,
11779 S_COR(ast_channel_caller(chan)->id.number.valid, ast_channel_caller(chan)->id.number.str, NULL))) {
11780 mailbox[0] = '*';
11781 free_user(vmu);
11782 return -1;
11783 }
11784 ast_verb(4, "Jump to extension 'a' failed; setting mailbox and user to NULL\n");
11785 mailbox[0] = '\0';
11786 /* if the password entered was '*', do not let a user mailbox be created if the extension 'a' is not defined */
11787 free_user(vmu);
11788 vmu = NULL;
11789 }
11790 }
11791
11792 if (vmu) {
11793 passptr = vmu->password;
11794 if (passptr[0] == '-') passptr++;
11795 }
11796 if (vmu && !strcmp(passptr, password))
11797 valid++;
11798 else {
11799 ast_verb(3, "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
11800 if (!ast_strlen_zero(prefix))
11801 mailbox[0] = '\0';
11802 }
11803 logretries++;
11804 if (!valid) {
11805 if (skipuser || logretries >= max_logins) {
11806 if (ast_streamfile(chan, "vm-incorrect", ast_channel_language(chan))) {
11807 ast_log(AST_LOG_WARNING, "Unable to stream incorrect message\n");
11808 free_user(vmu);
11809 return -1;
11810 }
11811 if (ast_waitstream(chan, "")) { /* Channel is hung up */
11812 free_user(vmu);
11813 return -1;
11814 }
11815 } else {
11816 if (useadsi)
11817 adsi_login(chan);
11818 if (ast_streamfile(chan, "vm-incorrect-mailbox", ast_channel_language(chan))) {
11819 ast_log(AST_LOG_WARNING, "Unable to stream incorrect mailbox message\n");
11820 free_user(vmu);
11821 return -1;
11822 }
11823 }
11824 }
11825 }
11826 if (!valid && (logretries >= max_logins)) {
11827 ast_stopstream(chan);
11828 ast_play_and_wait(chan, "vm-goodbye");
11829 free_user(vmu);
11830 return -1;
11831 }
11832 if (vmu && !skipuser) {
11833 *res_vmu = *vmu;
11834 }
11835 return 0;
11836}
11837
11839 struct ast_vm_user *vmu,
11840 struct vm_state *vms,
11841 const char *msg_id)
11842{
11843 if (message_range_and_existence_check(vms, &msg_id, 1, &vms->curmsg, vmu)) {
11844 return -1;
11845 }
11846 /* Found the msg, so play it back */
11847
11848 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
11849
11850#ifdef IMAP_STORAGE
11851 /*IMAP storage stores any prepended message from a forward
11852 * as a separate file from the rest of the message
11853 */
11854 if (!ast_strlen_zero(vms->introfn) && ast_fileexists(vms->introfn, NULL, NULL) > 0) {
11855 wait_file(chan, vms, vms->introfn);
11856 }
11857#endif
11858 RETRIEVE(vms->curdir,vms->curmsg,vmu->mailbox, vmu->context);
11859
11860 if ((wait_file(chan, vms, vms->fn)) < 0) {
11861 ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms->fn);
11862 } else {
11863#ifdef IMAP_STORAGE
11864 ast_mutex_lock(&vms->lock);
11865#endif
11866 vms->heard[vms->curmsg] = 1;
11867#ifdef IMAP_STORAGE
11868 ast_mutex_unlock(&vms->lock);
11869#endif
11870 }
11871 DISPOSE(vms->curdir, vms->curmsg);
11872 return 0;
11873}
11874
11875/*!
11876 * \brief Finds a message in a specific mailbox by msg_id and plays it to the channel
11877 *
11878 * \retval 0 Success
11879 * \retval -1 Failure
11880 */
11881static int play_message_by_id(struct ast_channel *chan, const char *mailbox, const char *context, const char *msg_id)
11882{
11883 struct vm_state vms;
11884 struct ast_vm_user *vmu = NULL, vmus;
11885 int res = 0;
11886 int open = 0;
11887 int played = 0;
11888 int i;
11889
11890 memset(&vmus, 0, sizeof(vmus));
11891 memset(&vms, 0, sizeof(vms));
11892
11893 if (!(vmu = find_user(&vmus, context, mailbox))) {
11894 goto play_msg_cleanup;
11895 }
11896
11897 /* Iterate through every folder, find the msg, and play it */
11898 for (i = 0; i < ARRAY_LEN(mailbox_folders) && !played; i++) {
11899 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
11900 vms.lastmsg = -1;
11901
11902 /* open the mailbox state */
11903 if ((res = open_mailbox(&vms, vmu, i)) < 0) {
11904 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
11905 res = -1;
11906 goto play_msg_cleanup;
11907 }
11908 open = 1;
11909
11910 /* play msg if it exists in this mailbox */
11911 if ((vms.lastmsg != -1) && !(play_message_by_id_helper(chan, vmu, &vms, msg_id))) {
11912 played = 1;
11913 }
11914
11915 /* close mailbox */
11916 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
11917 res = -1;
11918 goto play_msg_cleanup;
11919 }
11920 open = 0;
11921 }
11922
11923play_msg_cleanup:
11924 if (!played) {
11925 res = -1;
11926 }
11927
11928 if (vmu && open) {
11929 close_mailbox(&vms, vmu);
11930 }
11931
11932#ifdef IMAP_STORAGE
11933 if (vmu) {
11934 vmstate_delete(&vms);
11935 }
11936#endif
11937
11938 free_user(vmu);
11939
11940 return res;
11941}
11942
11943static int vm_playmsgexec(struct ast_channel *chan, const char *data)
11944{
11945 char *parse;
11946 char *mailbox = NULL;
11947 char *context = NULL;
11948 int res;
11949
11952 AST_APP_ARG(msg_id);
11953 );
11954
11955 if (ast_channel_state(chan) != AST_STATE_UP) {
11956 ast_debug(1, "Before ast_answer\n");
11957 ast_answer(chan);
11958 }
11959
11960 if (ast_strlen_zero(data)) {
11961 return -1;
11962 }
11963
11964 parse = ast_strdupa(data);
11966
11967 if (ast_strlen_zero(args.mailbox) || ast_strlen_zero(args.msg_id)) {
11968 return -1;
11969 }
11970
11971 if ((context = strchr(args.mailbox, '@'))) {
11972 *context++ = '\0';
11973 }
11974 mailbox = args.mailbox;
11975
11976 res = play_message_by_id(chan, mailbox, context, args.msg_id);
11977 pbx_builtin_setvar_helper(chan, "VOICEMAIL_PLAYBACKSTATUS", res ? "FAILED" : "SUCCESS");
11978
11979 return 0;
11980}
11981
11983{
11984#define VMBOX_STRING_HEADER_FORMAT "%-32.32s %-32.32s %-16.16s %-16.16s %-16.16s %-16.16s\n"
11985#define VMBOX_STRING_DATA_FORMAT "%-32.32s %-32.32s %-16.16s %-16.16s %-16.16s %-16.16s\n"
11986
11987 const char *mailbox = a->argv[3];
11988 const char *context = a->argv[4];
11989 struct vm_state vms;
11990 struct ast_vm_user *vmu = NULL, vmus;
11991 memset(&vmus, 0, sizeof(vmus));
11992 memset(&vms, 0, sizeof(vms));
11993
11994 if (!(vmu = find_user(&vmus, context, mailbox))) {
11995 ast_cli(a->fd, "Can't find voicemail user %s@%s\n", mailbox, context);
11996 return -1;
11997 }
11998
11999 ast_cli(a->fd, VMBOX_STRING_HEADER_FORMAT, "Full Name", "Email", "Pager", "Language", "Locale", "Time Zone");
12000 ast_cli(a->fd, VMBOX_STRING_DATA_FORMAT, vmu->fullname, vmu->email, vmu->pager, vmu->language, vmu->locale, vmu->zonetag);
12001
12002 return 0;
12003}
12004
12006{
12007#define VM_STRING_HEADER_FORMAT "%-8.8s %-32.32s %-32.32s %-9.9s %-6.6s %-30.30s\n"
12008 const char *mailbox = a->argv[3];
12009 const char *context = a->argv[4];
12010 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
12011 struct ast_vm_msg_snapshot *msg;
12012 int i;
12013
12014 /* Take a snapshot of the mailbox and walk through each folder's contents */
12016 if (!mailbox_snapshot) {
12017 ast_cli(a->fd, "Can't create snapshot for voicemail user %s@%s\n", mailbox, context);
12018 return -1;
12019 }
12020
12021 ast_cli(a->fd, VM_STRING_HEADER_FORMAT, "Folder", "Caller ID", "Date", "Duration", "Flag", "ID");
12022
12023 for (i = 0; i < mailbox_snapshot->folders; i++) {
12024 AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
12025 ast_cli(a->fd, VM_STRING_HEADER_FORMAT, msg->folder_name, msg->callerid, msg->origdate, msg->duration,
12026 msg->flag, msg->msg_id);
12027 }
12028 }
12029
12030 ast_cli(a->fd, "%d Message%s Total\n", mailbox_snapshot->total_msg_num, ESS(mailbox_snapshot->total_msg_num));
12031 /* done, destroy. */
12032 mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
12033
12034 return 0;
12035}
12036
12038{
12039 if (show_mailbox_details(a)){
12040 return -1;
12041 }
12042 ast_cli(a->fd, "\n");
12043 return show_mailbox_snapshot(a);
12044}
12045
12047{
12048 const char *from_mailbox = a->argv[2];
12049 const char *from_context = a->argv[3];
12050 const char *from_folder = a->argv[4];
12051 const char *id[] = { a->argv[5] };
12052 const char *to_mailbox = a->argv[6];
12053 const char *to_context = a->argv[7];
12054 const char *to_folder = a->argv[8];
12055 int ret = vm_msg_forward(from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder, 1, id, 0);
12056 if (ret) {
12057 ast_cli(a->fd, "Error forwarding message %s from mailbox %s@%s %s to mailbox %s@%s %s\n",
12058 id[0], from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder);
12059 } else {
12060 ast_cli(a->fd, "Forwarded message %s from mailbox %s@%s %s to mailbox %s@%s %s\n",
12061 id[0], from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder);
12062 }
12063 return ret;
12064}
12065
12067{
12068 const char *mailbox = a->argv[2];
12069 const char *context = a->argv[3];
12070 const char *from_folder = a->argv[4];
12071 const char *id[] = { a->argv[5] };
12072 const char *to_folder = a->argv[6];
12073 int ret = vm_msg_move(mailbox, context, 1, from_folder, id, to_folder);
12074 if (ret) {
12075 ast_cli(a->fd, "Error moving message %s from mailbox %s@%s %s to %s\n",
12076 id[0], mailbox, context, from_folder, to_folder);
12077 } else {
12078 ast_cli(a->fd, "Moved message %s from mailbox %s@%s %s to %s\n",
12079 id[0], mailbox, context, from_folder, to_folder);
12080 }
12081 return ret;
12082}
12083
12085{
12086 const char *mailbox = a->argv[2];
12087 const char *context = a->argv[3];
12088 const char *folder = a->argv[4];
12089 const char *id[] = { a->argv[5] };
12090 int ret = vm_msg_remove(mailbox, context, 1, folder, id);
12091 if (ret) {
12092 ast_cli(a->fd, "Error removing message %s from mailbox %s@%s %s\n",
12093 id[0], mailbox, context, folder);
12094 } else {
12095 ast_cli(a->fd, "Removed message %s from mailbox %s@%s %s\n",
12096 id[0], mailbox, context, folder);
12097 }
12098 return ret;
12099}
12100
12102{
12103 const char *word = a->word;
12104 int pos = a->pos;
12105 int state = a->n;
12106 int which = 0;
12107 int wordlen;
12108 struct ast_vm_user *vmu;
12109 const char *context = "", *mailbox = "";
12110 char *ret = NULL;
12111
12112 /* 0 - voicemail; 1 - show; 2 - mailbox; 3 - <mailbox>; 4 - <context> */
12113 if (pos == 3) {
12114 wordlen = strlen(word);
12116 AST_LIST_TRAVERSE(&users, vmu, list) {
12117 if (!strncasecmp(word, vmu->mailbox , wordlen)) {
12118 if (mailbox && strcmp(mailbox, vmu->mailbox) && ++which > state) {
12119 ret = ast_strdup(vmu->mailbox);
12121 return ret;
12122 }
12123 mailbox = vmu->mailbox;
12124 }
12125 }
12127 } else if (pos == 4) {
12128 /* Only display contexts that match the user in pos 3 */
12129 const char *box = a->argv[3];
12130 wordlen = strlen(word);
12132 AST_LIST_TRAVERSE(&users, vmu, list) {
12133 if (!strncasecmp(word, vmu->context, wordlen) && !strcasecmp(box, vmu->mailbox)) {
12134 if (context && strcmp(context, vmu->context) && ++which > state) {
12135 ret = ast_strdup(vmu->context);
12137 return ret;
12138 }
12139 context = vmu->context;
12140 }
12141 }
12143 }
12144
12145 return ret;
12146}
12147
12148static char *handle_voicemail_show_mailbox(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12149{
12150 switch (cmd) {
12151 case CLI_INIT:
12152 e->command = "voicemail show mailbox";
12153 e->usage =
12154 "Usage: voicemail show mailbox <mailbox> <context>\n"
12155 " Show contents of mailbox <mailbox>@<context>\n";
12156 return NULL;
12157 case CLI_GENERATE:
12159 case CLI_HANDLER:
12160 break;
12161 }
12162
12163 if (a->argc != 5) {
12164 return CLI_SHOWUSAGE;
12165 }
12166
12168 return CLI_FAILURE;
12169 }
12170
12171 return CLI_SUCCESS;
12172}
12173
12174/* Handles filling in data for one of the following three formats (based on maxpos = 5|6|8):
12175
12176 maxpos = 5
12177 0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12178 maxpos = 6
12179 0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12180 6 - <to_folder>;
12181 maxpos = 8
12182 0 - voicemail; 1 - forward; 2 - <from_mailbox>; 3 - <from_context>; 4 - <from_folder>; 5 - <messageid>;
12183 6 - <to_mailbox>; 7 - <to_context>; 8 - <to_folder>;
12184
12185 Passing in the maximum expected position 'maxpos' helps us fill in the missing entries in one function
12186 instead of three by taking advantage of the overlap in the command sequence between forward, move and
12187 remove as each of these use nearly the same syntax up until their maximum number of arguments.
12188 The value of pos = 6 changes to be either <messageid> or <folder> based on maxpos being 6 or 8.
12189*/
12190
12191static char *complete_voicemail_move_message(struct ast_cli_args *a, int maxpos)
12192{
12193 const char *word = a->word;
12194 int pos = a->pos;
12195 int state = a->n;
12196 int which = 0;
12197 int wordlen;
12198 struct ast_vm_user *vmu;
12199 const char *context = "", *mailbox = "", *folder = "", *id = "";
12200 char *ret = NULL;
12201
12202 if (pos > maxpos) {
12203 /* If the passed in pos is above the max, return NULL to avoid 'over-filling' the cli */
12204 return NULL;
12205 }
12206
12207 /* if we are in pos 2 or pos 6 in 'forward' mode */
12208 if (pos == 2 || (pos == 6 && maxpos == 8)) {
12209 /* find users */
12210 wordlen = strlen(word);
12212 AST_LIST_TRAVERSE(&users, vmu, list) {
12213 if (!strncasecmp(word, vmu->mailbox , wordlen)) {
12214 if (mailbox && strcmp(mailbox, vmu->mailbox) && ++which > state) {
12215 ret = ast_strdup(vmu->mailbox);
12217 return ret;
12218 }
12219 mailbox = vmu->mailbox;
12220 }
12221 }
12223 } else if (pos == 3 || pos == 7) {
12224 /* find contexts that match the user */
12225 mailbox = (pos == 3) ? a->argv[2] : a->argv[6];
12226 wordlen = strlen(word);
12228 AST_LIST_TRAVERSE(&users, vmu, list) {
12229 if (!strncasecmp(word, vmu->context, wordlen) && !strcasecmp(mailbox, vmu->mailbox)) {
12230 if (context && strcmp(context, vmu->context) && ++which > state) {
12231 ret = ast_strdup(vmu->context);
12233 return ret;
12234 }
12235 context = vmu->context;
12236 }
12237 }
12239 } else if (pos == 4 || pos == 8 || (pos == 6 && maxpos == 6) ) {
12240 int i;
12241 /* Walk through the standard folders */
12242 wordlen = strlen(word);
12243 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
12244 if (folder && !strncasecmp(word, mailbox_folders[i], wordlen) && ++which > state) {
12245 return ast_strdup(mailbox_folders[i]);
12246 }
12247 folder = mailbox_folders[i];
12248 }
12249 } else if (pos == 5) {
12250 /* find messages in the folder */
12251 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
12252 struct ast_vm_msg_snapshot *msg;
12253 mailbox = a->argv[2];
12254 context = a->argv[3];
12255 folder = a->argv[4];
12256 wordlen = strlen(word);
12257
12258 /* Take a snapshot of the mailbox and snag the individual info */
12259 if ((mailbox_snapshot = ast_vm_mailbox_snapshot_create(mailbox, context, folder, 0, AST_VM_SNAPSHOT_SORT_BY_ID, 0))) {
12260 int i;
12261 /* we are only requesting the one folder, but we still need to know it's index */
12262 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
12263 if (!strcasecmp(mailbox_folders[i], folder)) {
12264 break;
12265 }
12266 }
12267 AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
12268 if (id && !strncasecmp(word, msg->msg_id, wordlen) && ++which > state) {
12269 ret = ast_strdup(msg->msg_id);
12270 break;
12271 }
12272 id = msg->msg_id;
12273 }
12274 /* done, destroy. */
12275 mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
12276 }
12277 }
12278
12279 return ret;
12280}
12281
12282static char *handle_voicemail_forward_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12283{
12284 switch (cmd) {
12285 case CLI_INIT:
12286 e->command = "voicemail forward";
12287 e->usage =
12288 "Usage: voicemail forward <from_mailbox> <from_context> <from_folder> <messageid> <to_mailbox> <to_context> <to_folder>\n"
12289 " Forward message <messageid> in mailbox <mailbox>@<context> <from_folder>\n"
12290 " to mailbox <mailbox>@<context> <to_folder>\n";
12291 return NULL;
12292 case CLI_GENERATE:
12294 case CLI_HANDLER:
12295 break;
12296 }
12297
12298 if (a->argc != 9) {
12299 return CLI_SHOWUSAGE;
12300 }
12301
12303 return CLI_FAILURE;
12304 }
12305
12306 return CLI_SUCCESS;
12307}
12308
12309static char *handle_voicemail_move_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12310{
12311 switch (cmd) {
12312 case CLI_INIT:
12313 e->command = "voicemail move";
12314 e->usage =
12315 "Usage: voicemail move <mailbox> <context> <from_folder> <messageid> <to_folder>\n"
12316 " Move message <messageid> in mailbox <mailbox>&<context> from <from_folder> to <to_folder>\n";
12317 return NULL;
12318 case CLI_GENERATE:
12320 case CLI_HANDLER:
12321 break;
12322 }
12323
12324 if (a->argc != 7) {
12325 return CLI_SHOWUSAGE;
12326 }
12327
12329 return CLI_FAILURE;
12330 }
12331
12332 return CLI_SUCCESS;
12333}
12334
12335static char *handle_voicemail_remove_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
12336{
12337 switch (cmd) {
12338 case CLI_INIT:
12339 e->command = "voicemail remove";
12340 e->usage =
12341 "Usage: voicemail remove <mailbox> <context> <from_folder> <messageid>\n"
12342 " Remove message <messageid> from <from_folder> in mailbox <mailbox>@<context>\n";
12343 return NULL;
12344 case CLI_GENERATE:
12346 case CLI_HANDLER:
12347 break;
12348 }
12349
12350 if (a->argc != 6) {
12351 return CLI_SHOWUSAGE;
12352 }
12353
12355 return CLI_FAILURE;
12356 }
12357
12358 return CLI_SUCCESS;
12359}
12360
12361static int vm_execmain(struct ast_channel *chan, const char *data)
12362{
12363 /* XXX This is, admittedly, some pretty horrendous code. For some
12364 reason it just seemed a lot easier to do with GOTO's. I feel
12365 like I'm back in my GWBASIC days. XXX */
12366 int res = -1;
12367 int cmd = 0;
12368 int valid = 0;
12369 char prefixstr[80] ="";
12370 char ext_context[256]="";
12371 int box;
12372 int useadsi = 0;
12373 int skipuser = 0;
12374 struct vm_state vms = {{0}};
12375 struct ast_vm_user *vmu = NULL, vmus = {{0}};
12376 char *context = NULL;
12377 int silentexit = 0;
12378 struct ast_flags flags = { 0 };
12379 signed char record_gain = 0;
12380 int play_auto = 0;
12381 int play_folder = 0;
12382 int in_urgent = 0;
12383 int nodelete = 0;
12384#ifdef IMAP_STORAGE
12385 int deleted = 0;
12386#endif
12387 SCOPE_ENTER(3, "%s:\n", ast_channel_name(chan));
12388
12389 /* Add the vm_state to the active list and keep it active */
12390 vms.lastmsg = -1;
12391
12392 ast_test_suite_event_notify("START", "Message: vm_execmain started");
12393 if (ast_channel_state(chan) != AST_STATE_UP) {
12394 ast_debug(1, "Before ast_answer\n");
12395 ast_answer(chan);
12396 }
12397
12398 if (!ast_strlen_zero(data)) {
12399 char *opts[OPT_ARG_ARRAY_SIZE];
12400 char *parse;
12402 AST_APP_ARG(argv0);
12403 AST_APP_ARG(argv1);
12404 );
12405
12406 parse = ast_strdupa(data);
12407
12409
12410 if (args.argc == 2) {
12411 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
12412 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid option string '%s'\n", args.argv1);
12413 }
12415 int gain;
12416 if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
12417 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
12418 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
12419 } else {
12420 record_gain = (signed char) gain;
12421 }
12422 } else {
12423 ast_log(AST_LOG_WARNING, "Invalid Gain level set with option g\n");
12424 }
12425 }
12427 play_auto = 1;
12428 if (!ast_strlen_zero(opts[OPT_ARG_PLAYFOLDER])) {
12429 /* See if it is a folder name first */
12430 if (isdigit(opts[OPT_ARG_PLAYFOLDER][0])) {
12431 if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%30d", &play_folder) != 1) {
12432 play_folder = -1;
12433 }
12434 } else {
12435 play_folder = get_folder_by_name(opts[OPT_ARG_PLAYFOLDER]);
12436 }
12437 } else {
12438 ast_log(AST_LOG_WARNING, "Invalid folder set with option a\n");
12439 }
12440 if (play_folder > 9 || play_folder < 0) {
12442 "Invalid value '%s' provided for folder autoplay option. Defaulting to 'INBOX'\n",
12443 opts[OPT_ARG_PLAYFOLDER]);
12444 play_folder = 0;
12445 }
12446 }
12448 nodelete = 1;
12449 }
12450 } else {
12451 /* old style options parsing */
12452 while (*(args.argv0)) {
12453 if (*(args.argv0) == 's')
12455 else if (*(args.argv0) == 'p')
12457 else
12458 break;
12459 (args.argv0)++;
12460 }
12461
12462 }
12463
12464 valid = ast_test_flag(&flags, OPT_SILENT);
12465
12466 if ((context = strchr(args.argv0, '@')))
12467 *context++ = '\0';
12468
12470 ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
12471 else
12472 ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
12473
12474 if (!ast_strlen_zero(vms.username)) {
12475 if ((vmu = find_user(&vmus, context ,vms.username))) {
12476 skipuser++;
12477 } else {
12478 ast_log(LOG_WARNING, "Mailbox '%s%s%s' doesn't exist\n", vms.username, context ? "@": "", context ? context : "");
12479 valid = 0;
12480 }
12481 } else {
12482 valid = 0;
12483 }
12484 }
12485
12486 if (!valid)
12487 res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
12488
12489 ast_trace(-1, "vm_authenticate user: %s\n", vms.username);
12490
12491 if (vms.username[0] == '*') {
12492 ast_trace(-1, "user pressed * in context '%s'\n", ast_channel_context(chan));
12493
12494 /* user entered '*' */
12495 if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
12496 ast_test_suite_event_notify("REDIRECT", "Message: redirecting user to 'a' extension");
12497 res = 0; /* prevent hangup */
12498 goto out;
12499 }
12500 }
12501
12502 if (!res) {
12503 valid = 1;
12504 if (!skipuser)
12505 vmu = &vmus;
12506 } else {
12507 res = 0;
12508 }
12509
12510 /* If ADSI is supported, setup login screen */
12511 adsi_begin(chan, &useadsi);
12512
12513 if (!valid) {
12514 ast_trace(-1, "Invalid user\n");
12515 goto out;
12516 }
12517 ast_test_suite_event_notify("AUTHENTICATED", "Message: vm_user authenticated");
12518
12519#ifdef IMAP_STORAGE
12520 pthread_once(&ts_vmstate.once, ts_vmstate.key_init);
12521 pthread_setspecific(ts_vmstate.key, &vms);
12522
12523 vms.interactive = 1;
12524 vms.updated = 1;
12525 if (vmu)
12526 ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
12527 vmstate_insert(&vms);
12528 init_vm_state(&vms);
12529#endif
12530
12531 /* Set language from config to override channel language */
12532 if (!ast_strlen_zero(vmu->language)) {
12533 ast_channel_lock(chan);
12534 ast_channel_language_set(chan, vmu->language);
12535 ast_channel_unlock(chan);
12536 }
12537
12538 /* Retrieve urgent, old and new message counts */
12539 ast_trace(-1, "Before open_mailbox\n");
12540 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
12541 if (res < 0) {
12542 ast_trace(-1, "open mailbox: %d\n", res);
12543 goto out;
12544 }
12545 vms.oldmessages = vms.lastmsg + 1;
12546 ast_trace(-1, "Number of old messages: %d\n", vms.oldmessages);
12547 /* check INBOX */
12548 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12549 if (res < 0) {
12550 ast_trace(-1, "open mailbox: %d\n", res);
12551 goto out;
12552 }
12553 vms.newmessages = vms.lastmsg + 1;
12554 ast_trace(-1, "Number of new messages: %d\n", vms.newmessages);
12555 /* Start in Urgent */
12556 in_urgent = 1;
12557 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11); /*11 is the Urgent folder */
12558 if (res < 0) {
12559 ast_trace(-1, "open mailbox: %d\n", res);
12560 goto out;
12561 }
12562 vms.urgentmessages = vms.lastmsg + 1;
12563 ast_trace(-1, "Number of urgent messages: %d\n", vms.urgentmessages);
12564
12565 /* Select proper mailbox FIRST!! */
12566 if (play_auto) {
12567 ast_test_suite_event_notify("AUTOPLAY", "Message: auto-playing messages");
12568 if (vms.urgentmessages) {
12569 in_urgent = 1;
12570 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11);
12571 } else {
12572 in_urgent = 0;
12573 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, play_folder);
12574 }
12575 if (res < 0) {
12576 ast_trace(-1, "open mailbox: %d\n", res);
12577 goto out;
12578 }
12579
12580 /* If there are no new messages, inform the user and hangup */
12581 if (vms.lastmsg == -1) {
12582 in_urgent = 0;
12583 cmd = vm_browse_messages(chan, &vms, vmu);
12584 res = 0;
12585 goto out;
12586 }
12587 } else {
12588 if (!vms.newmessages && !vms.urgentmessages && vms.oldmessages) {
12589 /* If we only have old messages start here */
12590 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, OLD_FOLDER); /* Count all messages, even Urgent */
12591 in_urgent = 0;
12592 play_folder = 1;
12593 if (res < 0)
12594 goto out;
12595 } else if (!vms.urgentmessages && vms.newmessages) {
12596 /* If we have new messages but none are urgent */
12597 in_urgent = 0;
12598 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12599 if (res < 0)
12600 goto out;
12601 }
12602 }
12603
12604 if (useadsi)
12605 adsi_status(chan, &vms);
12606 res = 0;
12607
12608 /* Check to see if this is a new user */
12609 if (!strcasecmp(vmu->mailbox, vmu->password) &&
12611 if (ast_play_and_wait(chan, vm_newuser) == -1)
12612 ast_log(AST_LOG_WARNING, "Couldn't stream new user file\n");
12613 cmd = vm_newuser_setup(chan, vmu, &vms, vmfmts, record_gain);
12614 if ((cmd == 't') || (cmd == '#')) {
12615 /* Timeout */
12616 ast_test_suite_event_notify("TIMEOUT", "Message: response from user timed out");
12617 res = 0;
12618 ast_trace(-1, "Timeout\n");
12619 goto out;
12620 } else if (cmd < 0) {
12621 /* Hangup */
12622 ast_test_suite_event_notify("HANGUP", "Message: hangup detected");
12623 res = -1;
12624 ast_trace(-1, "Hangup\n");
12625 goto out;
12626 }
12627 }
12628#ifdef IMAP_STORAGE
12629 ast_debug(3, "Checking quotas: comparing %u to %u\n", vms.quota_usage, vms.quota_limit);
12630 if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
12631 ast_debug(1, "*** QUOTA EXCEEDED!!\n");
12632 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
12633 }
12634 ast_debug(3, "Checking quotas: User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
12635 if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
12636 ast_log(AST_LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n", (vms.newmessages + vms.oldmessages), vmu->maxmsg);
12637 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
12638 }
12639#endif
12640
12641 ast_test_suite_event_notify("INTRO", "Message: playing intro menu");
12642 if (play_auto) {
12643 cmd = '1';
12644 } else {
12645 cmd = vm_intro(chan, vmu, &vms);
12646 }
12647 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12648 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12649
12650 vms.repeats = 0;
12651 vms.starting = 1;
12652 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
12653 /* Run main menu */
12654 ast_trace(-1, "Main menu: %d %c\n", cmd, (cmd >= 32 && cmd <= 126 ? cmd : ' '));
12655 switch (cmd) {
12656 case '1': /* First message */
12657 vms.curmsg = 0;
12658 /* Fall through */
12659 case '5': /* Play current message */
12660 ast_test_suite_event_notify("BROWSE", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
12661 cmd = vm_browse_messages(chan, &vms, vmu);
12662 break;
12663 case '2': /* Change folders */
12664 ast_test_suite_event_notify("CHANGEFOLDER", "Message: browsing to a different folder");
12665 if (useadsi)
12666 adsi_folders(chan, 0, "Change to folder...");
12667
12668 cmd = get_folder2(chan, "vm-changeto", 0);
12669 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12670 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12671 if (cmd == '#') {
12672 cmd = 0;
12673 } else if (cmd > 0) {
12674 cmd = cmd - '0';
12675 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12676 if (res == ERROR_LOCK_PATH) {
12677 ast_trace(-1, "close mailbox: %d\n", res);
12678 goto out;
12679 }
12680 /* If folder is not urgent, set in_urgent to zero! */
12681 if (cmd != 11) in_urgent = 0;
12682 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, cmd);
12683 if (res < 0) {
12684 ast_trace(-1, "open mailbox: %d\n", res);
12685 goto out;
12686 }
12687 play_folder = cmd;
12688 cmd = 0;
12689 }
12690 if (useadsi)
12691 adsi_status2(chan, &vms);
12692
12693 if (!cmd) {
12694 cmd = vm_play_folder_name(chan, vms.vmbox);
12695 }
12696
12697 vms.starting = 1;
12698 vms.curmsg = 0;
12699 break;
12700 case '3': /* Advanced options */
12701 ast_test_suite_event_notify("ADVOPTIONS", "Message: entering advanced options menu");
12702 cmd = 0;
12703 vms.repeats = 0;
12704 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
12705 switch (cmd) {
12706 case '1': /* Reply */
12707 if (vms.lastmsg > -1 && !vms.starting) {
12708 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
12709 if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
12710 res = cmd;
12711 ast_trace(-1, "advanced options: %d\n", cmd);
12712 goto out;
12713 }
12714 } else {
12715 cmd = ast_play_and_wait(chan, "vm-sorry");
12716 }
12717 cmd = 't';
12718 break;
12719 case '2': /* Callback */
12720 if (!vms.starting)
12721 ast_verb(3, "Callback Requested\n");
12722 if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
12723 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
12724 ast_trace(-1, "advanced options: %d\n", cmd);
12725 if (cmd == 9) {
12726 silentexit = 1;
12727 goto out;
12728 } else if (cmd == ERROR_LOCK_PATH) {
12729 res = cmd;
12730 goto out;
12731 }
12732 } else {
12733 cmd = ast_play_and_wait(chan, "vm-sorry");
12734 }
12735 cmd = 't';
12736 break;
12737 case '3': /* Envelope */
12738 if (vms.lastmsg > -1 && !vms.starting) {
12739 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
12740 if (cmd == ERROR_LOCK_PATH) {
12741 res = cmd;
12742 ast_trace(-1, "advanced options: %d\n", cmd);
12743 goto out;
12744 }
12745 } else {
12746 cmd = ast_play_and_wait(chan, "vm-sorry");
12747 }
12748 cmd = 't';
12749 break;
12750 case '4': /* Dialout */
12751 if (!ast_strlen_zero(vmu->dialout)) {
12752 cmd = dialout(chan, vmu, NULL, vmu->dialout);
12753 if (cmd == 9) {
12754 silentexit = 1;
12755 ast_trace(-1, "dialout: %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
12764 case '5': /* Leave VoiceMail */
12765 if (ast_test_flag(vmu, VM_SVMAIL)) {
12766 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, forward_message, chan, context, &vms, vmu, vmfmts, 1, record_gain, 0);
12767 if (cmd == ERROR_LOCK_PATH || cmd == OPERATOR_EXIT) {
12768 res = cmd;
12769 ast_trace(-1, "forward message: %d\n", cmd);
12770 goto out;
12771 }
12772 } else {
12773 cmd = ast_play_and_wait(chan, "vm-sorry");
12774 }
12775 cmd = 't';
12776 break;
12777
12778 case '*': /* Return to main menu */
12779 cmd = 't';
12780 break;
12781
12782 default:
12783 cmd = 0;
12784 if (!vms.starting) {
12785 cmd = ast_play_and_wait(chan, "vm-toreply");
12786 }
12787 if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
12788 cmd = ast_play_and_wait(chan, "vm-tocallback");
12789 }
12790 if (!cmd && !vms.starting) {
12791 cmd = ast_play_and_wait(chan, "vm-tohearenv");
12792 }
12793 if (!ast_strlen_zero(vmu->dialout) && !cmd) {
12794 cmd = ast_play_and_wait(chan, "vm-tomakecall");
12795 }
12796 if (ast_test_flag(vmu, VM_SVMAIL) && !cmd) {
12797 cmd = ast_play_and_wait(chan, "vm-leavemsg");
12798 }
12799 if (!cmd) {
12800 cmd = ast_play_and_wait(chan, "vm-starmain");
12801 }
12802 if (!cmd) {
12803 cmd = ast_waitfordigit(chan, 6000);
12804 }
12805 if (!cmd) {
12806 vms.repeats++;
12807 }
12808 if (vms.repeats > 3) {
12809 cmd = 't';
12810 }
12811 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
12812 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
12813 }
12814 }
12815 if (cmd == 't') {
12816 cmd = 0;
12817 vms.repeats = 0;
12818 }
12819 break;
12820 case '4': /* Go to the previous message */
12821 ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg - 1, vms.curmsg - 1);
12822 if (vms.curmsg > 0) {
12823 vms.curmsg--;
12824 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12825 } else {
12826 /* Check if we were listening to new
12827 messages. If so, go to Urgent messages
12828 instead of saying "no more messages"
12829 */
12830 if (in_urgent == 0 && vms.urgentmessages > 0) {
12831 /* Check for Urgent messages */
12832 in_urgent = 1;
12833 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12834 if (res == ERROR_LOCK_PATH) {
12835 ast_trace(-1, "close mailbox: %d\n", res);
12836 goto out;
12837 }
12838 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, 11); /* Open Urgent folder */
12839 if (res < 0) {
12840 ast_trace(-1, "open mailbox: %d\n", res);
12841 goto out;
12842 }
12843 ast_debug(1, "No more new messages, opened INBOX and got %d Urgent messages\n", vms.lastmsg + 1);
12844 vms.curmsg = vms.lastmsg;
12845 if (vms.lastmsg < 0) {
12846 cmd = ast_play_and_wait(chan, "vm-nomore");
12847 }
12848 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12849 vms.curmsg = vms.lastmsg;
12850 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12851 } else {
12852 cmd = ast_play_and_wait(chan, "vm-nomore");
12853 }
12854 }
12855 break;
12856 case '6': /* Go to the next message */
12857 ast_test_suite_event_notify("PREVMSG", "Message: browsing message %d\r\nVoicemail: %d", vms.curmsg + 1, vms.curmsg + 1);
12858 if (vms.curmsg < vms.lastmsg) {
12859 vms.curmsg++;
12860 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12861 } else {
12862 if (in_urgent && vms.newmessages > 0) {
12863 /* Check if we were listening to urgent
12864 * messages. If so, go to regular new messages
12865 * instead of saying "no more messages"
12866 */
12867 in_urgent = 0;
12868 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12869 if (res == ERROR_LOCK_PATH) {
12870 ast_trace(-1, "close mailbox: %d\n", res);
12871 goto out;
12872 }
12873 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12874 if (res < 0) {
12875 ast_trace(-1, "open mailbox: %d\n", res);
12876 goto out;
12877 }
12878 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12879 vms.curmsg = -1;
12880 if (vms.lastmsg < 0) {
12881 cmd = ast_play_and_wait(chan, "vm-nomore");
12882 }
12883 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12884 vms.curmsg = 0;
12885 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12886 } else {
12887 cmd = ast_play_and_wait(chan, "vm-nomore");
12888 }
12889 }
12890 break;
12891 case '7': /* Delete the current message */
12892 if (!nodelete && vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
12893 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
12894 if (useadsi)
12895 adsi_delete(chan, &vms);
12896 if (vms.deleted[vms.curmsg]) {
12897 if (play_folder == 0) {
12898 if (in_urgent) {
12899 vms.urgentmessages--;
12900 } else {
12901 vms.newmessages--;
12902 }
12903 }
12904 else if (play_folder == 1)
12905 vms.oldmessages--;
12906 cmd = ast_play_and_wait(chan, "vm-deleted");
12907 } else {
12908 if (play_folder == 0) {
12909 if (in_urgent) {
12910 vms.urgentmessages++;
12911 } else {
12912 vms.newmessages++;
12913 }
12914 }
12915 else if (play_folder == 1)
12916 vms.oldmessages++;
12917 cmd = ast_play_and_wait(chan, "vm-undeleted");
12918 }
12919 if (ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
12920 if (vms.curmsg < vms.lastmsg) {
12921 vms.curmsg++;
12922 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12923 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
12924 vms.curmsg = 0;
12925 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
12926 } else {
12927 /* Check if we were listening to urgent
12928 messages. If so, go to regular new messages
12929 instead of saying "no more messages"
12930 */
12931 if (in_urgent == 1) {
12932 /* Check for new messages */
12933 in_urgent = 0;
12934 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12935 if (res == ERROR_LOCK_PATH) {
12936 ast_trace(-1, "close mailbox: %d\n", res);
12937 goto out;
12938 }
12939 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12940 if (res < 0) {
12941 ast_trace(-1, "open mailbox: %d\n", res);
12942 goto out;
12943 }
12944 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12945 vms.curmsg = -1;
12946 if (vms.lastmsg < 0) {
12947 cmd = ast_play_and_wait(chan, "vm-nomore");
12948 }
12949 } else {
12950 cmd = ast_play_and_wait(chan, "vm-nomore");
12951 }
12952 }
12953 }
12954 } else /* Delete not valid if we haven't selected a message */
12955 cmd = 0;
12956#ifdef IMAP_STORAGE
12957 deleted = 1;
12958#endif
12959 break;
12960
12961 case '8': /* Forward the current message */
12962 if (vms.lastmsg > -1) {
12963 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, forward_message, chan, context, &vms, vmu, vmfmts, 0, record_gain, in_urgent);
12964 if (cmd == ERROR_LOCK_PATH) {
12965 res = cmd;
12966 ast_trace(-1, "forward message: %d\n", res);
12967 goto out;
12968 }
12969 } else {
12970 /* Check if we were listening to urgent
12971 messages. If so, go to regular new messages
12972 instead of saying "no more messages"
12973 */
12974 if (in_urgent == 1 && vms.newmessages > 0) {
12975 /* Check for new messages */
12976 in_urgent = 0;
12977 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
12978 if (res == ERROR_LOCK_PATH) {
12979 ast_trace(-1, "close mailbox: %d\n", res);
12980 goto out;
12981 }
12982 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
12983 if (res < 0) {
12984 ast_trace(-1, "open mailbox: %d\n", res);
12985 goto out;
12986 }
12987 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
12988 vms.curmsg = -1;
12989 if (vms.lastmsg < 0) {
12990 cmd = ast_play_and_wait(chan, "vm-nomore");
12991 }
12992 } else {
12993 cmd = ast_play_and_wait(chan, "vm-nomore");
12994 }
12995 }
12996 break;
12997 case '9': /* Save message to folder */
12998 ast_test_suite_event_notify("SAVEMSG", "Message: saving message %d\r\nVoicemail: %d", vms.curmsg, vms.curmsg);
12999 if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
13000 /* No message selected */
13001 cmd = 0;
13002 break;
13003 }
13004 if (useadsi)
13005 adsi_folders(chan, 1, "Save to folder...");
13006 cmd = get_folder2(chan, "vm-savefolder", 1);
13007 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
13008 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
13009 box = 0; /* Shut up compiler */
13010 if (cmd == '#') {
13011 cmd = 0;
13012 break;
13013 } else if (cmd > 0) {
13014 box = cmd = cmd - '0';
13015 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, save_to_folder, vmu, &vms, vms.curmsg, cmd, NULL, 0);
13016 if (cmd == ERROR_LOCK_PATH) {
13017 res = cmd;
13018 ast_trace(-1, "save to folder: %d\n", res);
13019 goto out;
13020#ifndef IMAP_STORAGE
13021 } else if (!cmd) {
13022 vms.deleted[vms.curmsg] = 1;
13023#endif
13024 } else {
13025 vms.deleted[vms.curmsg] = 0;
13026 vms.heard[vms.curmsg] = 0;
13027 }
13028 }
13029 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
13030 if (useadsi)
13031 adsi_message(chan, &vms);
13032 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(vmu, box));
13033 if (!cmd) {
13034 cmd = ast_play_and_wait(chan, "vm-message");
13035 if (!cmd)
13036 cmd = say_and_wait(chan, vms.curmsg + 1, ast_channel_language(chan));
13037 if (!cmd)
13038 cmd = ast_play_and_wait(chan, "vm-savedto");
13039 if (!cmd)
13040 cmd = vm_play_folder_name(chan, vms.fn);
13041 } else {
13042 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
13043 }
13045 if (vms.curmsg < vms.lastmsg) {
13046 vms.curmsg++;
13047 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
13048 } else if (ast_test_flag(vmu, VM_MESSAGEWRAP) && vms.lastmsg > 0) {
13049 vms.curmsg = 0;
13050 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, play_message, chan, vmu, &vms);
13051 } else {
13052 /* Check if we were listening to urgent
13053 messages. If so, go to regular new messages
13054 instead of saying "no more messages"
13055 */
13056 if (in_urgent == 1 && vms.newmessages > 0) {
13057 /* Check for new messages */
13058 in_urgent = 0;
13059 res = SCOPE_CALL_WITH_INT_RESULT(-1, close_mailbox, &vms, vmu);
13060 if (res == ERROR_LOCK_PATH) {
13061 ast_trace(-1, "close mailbox: %d\n", res);
13062 goto out;
13063 }
13064 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, NEW_FOLDER);
13065 if (res < 0) {
13066 ast_trace(-1, "open mailbox: %d\n", res);
13067 goto out;
13068 }
13069 ast_debug(1, "No more urgent messages, opened INBOX and got %d new messages\n", vms.lastmsg + 1);
13070 vms.curmsg = -1;
13071 if (vms.lastmsg < 0) {
13072 cmd = ast_play_and_wait(chan, "vm-nomore");
13073 }
13074 } else {
13075 cmd = ast_play_and_wait(chan, "vm-nomore");
13076 }
13077 }
13078 }
13079 break;
13080 case '*': /* Help */
13081 if (!vms.starting) {
13082 if (!strncasecmp(ast_channel_language(chan), "ja", 2)) {
13083 cmd = vm_play_folder_name(chan, vms.vmbox);
13084 if (!cmd)
13085 cmd = ast_play_and_wait(chan, "jp-wa");
13086 if (!cmd)
13087 cmd = ast_play_and_wait(chan, "digits/1");
13088 if (!cmd)
13089 cmd = ast_play_and_wait(chan, "jp-wo");
13090 if (!cmd)
13091 cmd = ast_play_and_wait(chan, "silence/1");
13092 if (!cmd)
13093 cmd = ast_play_and_wait(chan, "vm-opts");
13094 if (!cmd)
13095 cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent, nodelete);
13096 break;
13097 }
13098 cmd = ast_play_and_wait(chan, "vm-onefor");
13099 if (!strncasecmp(ast_channel_language(chan), "he", 2)) {
13100 cmd = ast_play_and_wait(chan, "vm-for");
13101 }
13102 if (!cmd)
13103 cmd = vm_play_folder_name(chan, vms.vmbox);
13104 if (!cmd)
13105 cmd = ast_play_and_wait(chan, "vm-opts");
13106 if (!cmd)
13107 cmd = vm_instructions(chan, vmu, &vms, 1, in_urgent, nodelete);
13108 } else
13109 cmd = 0;
13110 break;
13111 case '0': /* Mailbox options */
13112 cmd = SCOPE_CALL_WITH_INT_RESULT(-1, vm_options,chan, vmu, &vms, vmfmts, record_gain);
13113 if (useadsi)
13114 adsi_status(chan, &vms);
13115 /* Reopen play_folder */
13116 res = SCOPE_CALL_WITH_INT_RESULT(-1, open_mailbox, &vms, vmu, play_folder);
13117 if (res < 0) {
13118 ast_trace(-1, "open mailbox: %d\n", res);
13119 goto out;
13120 }
13121 vms.starting = 1;
13122 break;
13123 default: /* Nothing */
13124 ast_test_suite_event_notify("PLAYBACK", "Message: instructions");
13125 cmd = vm_instructions(chan, vmu, &vms, 0, in_urgent, nodelete);
13126 break;
13127 }
13128 }
13129 if ((cmd == 't') || (cmd == '#')) {
13130 /* Timeout */
13131 res = 0;
13132 } else {
13133 /* Hangup */
13134 res = -1;
13135 }
13136
13137out:
13138 if (res > -1) {
13139 ast_stopstream(chan);
13140 adsi_goodbye(chan);
13141 if (valid && res != OPERATOR_EXIT) {
13142 if (silentexit)
13143 res = ast_play_and_wait(chan, "vm-dialout");
13144 else
13145 res = ast_play_and_wait(chan, "vm-goodbye");
13146 }
13147 if ((valid && res > 0) || res == OPERATOR_EXIT) {
13148 res = 0;
13149 }
13150 if (useadsi)
13152 }
13153 if (vmu) {
13154 SCOPE_CALL(-1, close_mailbox, &vms, vmu);
13155 }
13156 if (valid) {
13157 int new = 0, old = 0, urgent = 0;
13158 snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
13159 /* Urgent flag not passwd to externnotify here */
13160 run_externnotify(vmu->context, vmu->mailbox, NULL);
13161 ast_app_inboxcount2(ext_context, &urgent, &new, &old);
13162 queue_mwi_event(ast_channel_uniqueid(chan), ext_context, urgent, new, old);
13163 }
13164#ifdef IMAP_STORAGE
13165 /* expunge message - use UID Expunge if supported on IMAP server*/
13166 ast_debug(3, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n", deleted, expungeonhangup);
13167 if (vmu && deleted == 1 && expungeonhangup == 1 && vms.mailstream != NULL) {
13168 ast_mutex_lock(&vms.lock);
13169#ifdef HAVE_IMAP_TK2006
13170 if (LEVELUIDPLUS (vms.mailstream)) {
13171 mail_expunge_full(vms.mailstream, NIL, EX_UID);
13172 } else
13173#endif
13174 mail_expunge(vms.mailstream);
13175 ast_mutex_unlock(&vms.lock);
13176 }
13177 /* before we delete the state, we should copy pertinent info
13178 * back to the persistent model */
13179 if (vmu) {
13180 vmstate_delete(&vms);
13181 }
13182#endif
13183 if (vmu)
13184 free_user(vmu);
13185
13186#ifdef IMAP_STORAGE
13187 pthread_setspecific(ts_vmstate.key, NULL);
13188#endif
13189 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d\n", res);
13190}
13191
13192static int vm_exec(struct ast_channel *chan, const char *data)
13193{
13194 int res = 0;
13195 char *tmp;
13196 struct leave_vm_options leave_options;
13197 struct ast_flags flags = { 0 };
13198 char *opts[OPT_ARG_ARRAY_SIZE];
13200 AST_APP_ARG(argv0);
13201 AST_APP_ARG(argv1);
13202 );
13203 SCOPE_ENTER(3, "%s\n", ast_channel_name(chan));
13204
13205 memset(&leave_options, 0, sizeof(leave_options));
13206
13207 if (!ast_strlen_zero(data)) {
13208 tmp = ast_strdupa(data);
13210 if (args.argc == 2) {
13211 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
13212 SCOPE_EXIT_RTN_VALUE(-1, "parse options failed for '%s'\n", args.argv1);
13213 }
13216 int gain;
13217
13218 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%30d", &gain) != 1) {
13219 SCOPE_EXIT_LOG_RTN_VALUE(-1, AST_LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
13220 } else {
13221 leave_options.record_gain = (signed char) gain;
13222 }
13223 }
13226 leave_options.exitcontext = opts[OPT_ARG_DTMFEXIT];
13227 }
13228 }
13229 if (ast_test_flag(&flags, OPT_BEEP)) { /* Use custom beep (or none at all) */
13230 leave_options.beeptone = opts[OPT_ARG_BEEP_TONE];
13231 } else { /* Use default beep */
13232 leave_options.beeptone = "beep";
13233 }
13234 } else {
13235 char temp[256];
13236 res = ast_app_getdata(chan, "vm-whichbox", temp, sizeof(temp) - 1, 0);
13237 if (res < 0) {
13238 SCOPE_EXIT_RTN_VALUE(res, "getdata failed. RC: %d", res);
13239 }
13240 if (ast_strlen_zero(temp)) {
13242 }
13243 args.argv0 = ast_strdupa(temp);
13244 }
13245
13246 if (ast_channel_state(chan) != AST_STATE_UP) {
13249 } else {
13250 ast_answer(chan);
13251 }
13252 }
13253
13254 res = SCOPE_CALL_WITH_INT_RESULT(-1, leave_voicemail, chan, args.argv0, &leave_options);
13255 if (res == 't') {
13256 ast_play_and_wait(chan, "vm-goodbye");
13257 res = 0;
13258 }
13259
13260 if (res == OPERATOR_EXIT) {
13261 res = 0;
13262 }
13263
13264 if (res == ERROR_LOCK_PATH) {
13265 ast_log(AST_LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
13266 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
13267 res = 0;
13268 }
13269
13270 SCOPE_EXIT_RTN_VALUE(res, "Done. RC: %d", res);
13271}
13272
13273static 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)
13274{
13275 struct ast_variable *var;
13276 struct ast_category *cat;
13277 generate_msg_id(id);
13278
13279 var = ast_variable_new("msg_id", id, "");
13280 if (!var) {
13281 return -1;
13282 }
13283
13284 cat = ast_category_get(msg_cfg, "message", NULL);
13285 if (!cat) {
13286 ast_log(LOG_ERROR, "Voicemail data file %s/%d.txt has no [message] category?\n", dir, msg);
13288 return -1;
13289 }
13290
13292
13293 if (ast_config_text_file_save(filename, msg_cfg, "app_voicemail")) {
13294 ast_log(LOG_WARNING, "Unable to update %s to have a message ID\n", filename);
13295 return -1;
13296 }
13297
13298 UPDATE_MSG_ID(dir, msg, id, vmu, msg_cfg, folder);
13299 return 0;
13300}
13301
13302static struct ast_vm_user *find_or_create(const char *context, const char *box)
13303{
13304 struct ast_vm_user *vmu;
13305
13306 if (!ast_strlen_zero(box) && box[0] == '*') {
13307 ast_log(LOG_WARNING, "Mailbox %s in context %s begins with '*' character. The '*' character,"
13308 "\n\twhen it is the first character in a mailbox or password, is used to jump to a"
13309 "\n\tpredefined extension 'a'. A mailbox or password beginning with '*' is not valid"
13310 "\n\tand will be ignored.\n", box, context);
13311 return NULL;
13312 }
13313
13314 AST_LIST_TRAVERSE(&users, vmu, list) {
13315 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(box, vmu->mailbox)) {
13316 if (strcasecmp(vmu->context, context)) {
13317 ast_log(LOG_WARNING, "\nIt has been detected that you have defined mailbox '%s' in separate\
13318 \n\tcontexts and that you have the 'searchcontexts' option on. This type of\
13319 \n\tconfiguration creates an ambiguity that you likely do not want. Please\
13320 \n\tamend your voicemail.conf file to avoid this situation.\n", box);
13321 }
13322 ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s\n", box);
13323 return NULL;
13324 }
13325 if (!strcasecmp(context, vmu->context) && !strcasecmp(box, vmu->mailbox)) {
13326 ast_log(LOG_WARNING, "Ignoring duplicated mailbox %s in context %s\n", box, context);
13327 return NULL;
13328 }
13329 }
13330
13331 if (!(vmu = ast_calloc(1, sizeof(*vmu))))
13332 return NULL;
13333
13334 ast_copy_string(vmu->context, context, sizeof(vmu->context));
13335 ast_copy_string(vmu->mailbox, box, sizeof(vmu->mailbox));
13336
13338
13339 return vmu;
13340}
13341
13342static int append_mailbox(const char *context, const char *box, const char *data)
13343{
13344 /* Assumes lock is already held */
13345 char *tmp;
13346 char *stringp;
13347 char *s;
13348 struct ast_vm_user *vmu;
13349 char mailbox_full[MAX_VM_MAILBOX_LEN];
13350 int new = 0, old = 0, urgent = 0;
13351 char secretfn[PATH_MAX] = "";
13352
13353 tmp = ast_strdupa(data);
13354
13355 if (!(vmu = find_or_create(context, box)))
13356 return -1;
13357
13358 populate_defaults(vmu);
13359
13360 stringp = tmp;
13361 if ((s = strsep(&stringp, ","))) {
13362 if (!ast_strlen_zero(s) && s[0] == '*') {
13363 ast_log(LOG_WARNING, "Invalid password detected for mailbox %s. The password"
13364 "\n\tmust be reset in voicemail.conf.\n", box);
13365 }
13366 /* assign password regardless of validity to prevent NULL password from being assigned */
13367 ast_copy_string(vmu->password, s, sizeof(vmu->password));
13368 }
13369 if (stringp && (s = strsep(&stringp, ","))) {
13370 ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
13371 }
13372 if (stringp && (s = strsep(&stringp, ","))) {
13373 vmu->email = ast_strdup(s);
13374 }
13375 if (stringp && (s = strsep(&stringp, ","))) {
13376 ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
13377 }
13378 if (stringp) {
13379 apply_options(vmu, stringp);
13380 }
13381
13382 switch (vmu->passwordlocation) {
13383 case OPT_PWLOC_SPOOLDIR:
13384 snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, vmu->context, vmu->mailbox);
13385 read_password_from_file(secretfn, vmu->password, sizeof(vmu->password));
13386 }
13387
13388 snprintf(mailbox_full, MAX_VM_MAILBOX_LEN, "%s%s%s",
13389 box,
13390 ast_strlen_zero(context) ? "" : "@",
13391 context);
13392
13393 inboxcount2(mailbox_full, &urgent, &new, &old);
13394#ifdef IMAP_STORAGE
13395 imap_logout(mailbox_full);
13396#endif
13397 queue_mwi_event(NULL, mailbox_full, urgent, new, old);
13398
13399 return 0;
13400}
13401
13402#ifdef TEST_FRAMEWORK
13403AST_TEST_DEFINE(test_voicemail_vmuser)
13404{
13405 int res = 0;
13406 struct ast_vm_user *vmu;
13407 /* language parameter seems to only be used for display in manager action */
13408 static const char options_string[] = "attach=yes|attachfmt=wav49|"
13409 "serveremail=someguy@digium.com|fromstring=Voicemail System|tz=central|delete=yes|saycid=yes|"
13410 "sendvoicemail=yes|review=yes|tempgreetwarn=yes|messagewrap=yes|operator=yes|leaveurgent=yes|"
13411 "envelope=yes|moveheard=yes|sayduration=yes|saydurationm=5|forcename=yes|"
13412 "forcegreetings=yes|callback=somecontext|dialout=somecontext2|"
13413 "exitcontext=somecontext3|minsecs=10|maxsecs=100|nextaftercmd=yes|"
13414 "backupdeleted=50|volgain=1.3|passwordlocation=spooldir|emailbody="
13415 "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message|emailsubject="
13416 "[PBX]: New message \\\\${VM_MSGNUM}\\\\ in mailbox ${VM_MAILBOX}";
13417#ifdef IMAP_STORAGE
13418 static const char option_string2[] = "imapuser=imapuser|imappassword=imappasswd|"
13419 "imapfolder=INBOX|imapvmshareid=6000|imapserver=imapserver|imapport=1234|imapflags=flagged";
13420#endif
13421
13422 switch (cmd) {
13423 case TEST_INIT:
13424 info->name = "vmuser";
13425 info->category = "/apps/app_voicemail/";
13426 info->summary = "Vmuser unit test";
13427 info->description =
13428 "This tests passing all supported parameters to apply_options, the voicemail user config parser";
13429 return AST_TEST_NOT_RUN;
13430 case TEST_EXECUTE:
13431 break;
13432 }
13433
13434 if (!(vmu = ast_calloc(1, sizeof(*vmu)))) {
13435 return AST_TEST_NOT_RUN;
13436 }
13437 populate_defaults(vmu);
13439
13440 apply_options(vmu, options_string);
13441
13442 if (!ast_test_flag(vmu, VM_ATTACH)) {
13443 ast_test_status_update(test, "Parse failure for attach option\n");
13444 res = 1;
13445 }
13446 if (strcasecmp(vmu->attachfmt, "wav49")) {
13447 ast_test_status_update(test, "Parse failure for attachfmt option\n");
13448 res = 1;
13449 }
13450 if (strcasecmp(vmu->fromstring, "Voicemail System")) {
13451 ast_test_status_update(test, "Parse failure for fromstring option\n");
13452 res = 1;
13453 }
13454 if (strcasecmp(vmu->serveremail, "someguy@digium.com")) {
13455 ast_test_status_update(test, "Parse failure for serveremail option\n");
13456 res = 1;
13457 }
13458 if (!vmu->emailsubject || strcasecmp(vmu->emailsubject, "[PBX]: New message \\${VM_MSGNUM}\\ in mailbox ${VM_MAILBOX}")) {
13459 ast_test_status_update(test, "Parse failure for emailsubject option\n");
13460 res = 1;
13461 }
13462 if (!vmu->emailbody || strcasecmp(vmu->emailbody, "Dear ${VM_NAME}:\n\n\tYou were just left a ${VM_DUR} long message")) {
13463 ast_test_status_update(test, "Parse failure for emailbody option\n");
13464 res = 1;
13465 }
13466 if (strcasecmp(vmu->zonetag, "central")) {
13467 ast_test_status_update(test, "Parse failure for tz option\n");
13468 res = 1;
13469 }
13470 if (!ast_test_flag(vmu, VM_DELETE)) {
13471 ast_test_status_update(test, "Parse failure for delete option\n");
13472 res = 1;
13473 }
13474 if (!ast_test_flag(vmu, VM_SAYCID)) {
13475 ast_test_status_update(test, "Parse failure for saycid option\n");
13476 res = 1;
13477 }
13478 if (!ast_test_flag(vmu, VM_SVMAIL)) {
13479 ast_test_status_update(test, "Parse failure for sendvoicemail option\n");
13480 res = 1;
13481 }
13482 if (!ast_test_flag(vmu, VM_REVIEW)) {
13483 ast_test_status_update(test, "Parse failure for review option\n");
13484 res = 1;
13485 }
13486 if (!ast_test_flag(vmu, VM_MARK_URGENT)) {
13487 ast_test_status_update(test, "Parse failure for leaveurgent option\n");
13488 res = 1;
13489 }
13490 if (!ast_test_flag(vmu, VM_TEMPGREETWARN)) {
13491 ast_test_status_update(test, "Parse failure for tempgreetwarm option\n");
13492 res = 1;
13493 }
13494 if (!ast_test_flag(vmu, VM_MESSAGEWRAP)) {
13495 ast_test_status_update(test, "Parse failure for messagewrap option\n");
13496 res = 1;
13497 }
13498 if (!ast_test_flag(vmu, VM_OPERATOR)) {
13499 ast_test_status_update(test, "Parse failure for operator option\n");
13500 res = 1;
13501 }
13502 if (!ast_test_flag(vmu, VM_ENVELOPE)) {
13503 ast_test_status_update(test, "Parse failure for envelope option\n");
13504 res = 1;
13505 }
13506 if (!ast_test_flag(vmu, VM_MOVEHEARD)) {
13507 ast_test_status_update(test, "Parse failure for moveheard option\n");
13508 res = 1;
13509 }
13510 if (!ast_test_flag(vmu, VM_SAYDURATION)) {
13511 ast_test_status_update(test, "Parse failure for sayduration option\n");
13512 res = 1;
13513 }
13514 if (vmu->saydurationm != 5) {
13515 ast_test_status_update(test, "Parse failure for saydurationm option\n");
13516 res = 1;
13517 }
13518 if (!ast_test_flag(vmu, VM_FORCENAME)) {
13519 ast_test_status_update(test, "Parse failure for forcename option\n");
13520 res = 1;
13521 }
13522 if (!ast_test_flag(vmu, VM_FORCEGREET)) {
13523 ast_test_status_update(test, "Parse failure for forcegreetings option\n");
13524 res = 1;
13525 }
13526 if (strcasecmp(vmu->callback, "somecontext")) {
13527 ast_test_status_update(test, "Parse failure for callbacks option\n");
13528 res = 1;
13529 }
13530 if (strcasecmp(vmu->dialout, "somecontext2")) {
13531 ast_test_status_update(test, "Parse failure for dialout option\n");
13532 res = 1;
13533 }
13534 if (strcasecmp(vmu->exit, "somecontext3")) {
13535 ast_test_status_update(test, "Parse failure for exitcontext option\n");
13536 res = 1;
13537 }
13538 if (vmu->minsecs != 10) {
13539 ast_test_status_update(test, "Parse failure for minsecs option\n");
13540 res = 1;
13541 }
13542 if (vmu->maxsecs != 100) {
13543 ast_test_status_update(test, "Parse failure for maxsecs option\n");
13544 res = 1;
13545 }
13546 if (!ast_test_flag(vmu, VM_SKIPAFTERCMD)) {
13547 ast_test_status_update(test, "Parse failure for nextaftercmd option\n");
13548 res = 1;
13549 }
13550 if (vmu->maxdeletedmsg != 50) {
13551 ast_test_status_update(test, "Parse failure for backupdeleted option\n");
13552 res = 1;
13553 }
13554 if (vmu->volgain != 1.3) {
13555 ast_test_status_update(test, "Parse failure for volgain option\n");
13556 res = 1;
13557 }
13559 ast_test_status_update(test, "Parse failure for passwordlocation option\n");
13560 res = 1;
13561 }
13562#ifdef IMAP_STORAGE
13563 apply_options(vmu, option_string2);
13564
13565 if (strcasecmp(vmu->imapuser, "imapuser")) {
13566 ast_test_status_update(test, "Parse failure for imapuser option\n");
13567 res = 1;
13568 }
13569 if (strcasecmp(vmu->imappassword, "imappasswd")) {
13570 ast_test_status_update(test, "Parse failure for imappasswd option\n");
13571 res = 1;
13572 }
13573 if (strcasecmp(vmu->imapfolder, "INBOX")) {
13574 ast_test_status_update(test, "Parse failure for imapfolder option\n");
13575 res = 1;
13576 }
13577 if (strcasecmp(vmu->imapvmshareid, "6000")) {
13578 ast_test_status_update(test, "Parse failure for imapvmshareid option\n");
13579 res = 1;
13580 }
13581 if (strcasecmp(vmu->imapserver, "imapserver")) {
13582 ast_test_status_update(test, "Parse failure for imapserver option\n");
13583 res = 1;
13584 }
13585 if (strcasecmp(vmu->imapport, "1234")) {
13586 ast_test_status_update(test, "Parse failure for imapport option\n");
13587 res = 1;
13588 }
13589 if (strcasecmp(vmu->imapflags, "flagged")) {
13590 ast_test_status_update(test, "Parse failure for imapflags option\n");
13591 res = 1;
13592 }
13593#endif
13594
13595 free_user(vmu);
13596 force_reload_config(); /* Restore original config */
13597 return res ? AST_TEST_FAIL : AST_TEST_PASS;
13598}
13599#endif
13600
13601static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
13602{
13603 struct ast_vm_user svm;
13604 struct ast_vm_user *vmu = NULL;
13605 char *parse;
13606 char *mailbox;
13607 char *context;
13608 int res = 0;
13609
13611 AST_APP_ARG(mailbox_context);
13612 AST_APP_ARG(attribute);
13613 AST_APP_ARG(folder);
13614 );
13615
13616 buf[0] = '\0';
13617
13618 if (ast_strlen_zero(args)) {
13619 ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
13620 return -1;
13621 }
13622
13623 parse = ast_strdupa(args);
13624 AST_STANDARD_APP_ARGS(arg, parse);
13625
13626 if (ast_strlen_zero(arg.mailbox_context)
13627 || ast_strlen_zero(arg.attribute)
13628 || separate_mailbox(ast_strdupa(arg.mailbox_context), &mailbox, &context)) {
13629 ast_log(LOG_ERROR, "VM_INFO requires an argument (<mailbox>[@<context>],attribute[,folder])\n");
13630 return -1;
13631 }
13632
13633 memset(&svm, 0, sizeof(svm));
13634 vmu = find_user(&svm, context, mailbox);
13635
13636 if (!strncasecmp(arg.attribute, "exists", 5)) {
13637 ast_copy_string(buf, vmu ? "1" : "0", len);
13638 free_user(vmu);
13639 return 0;
13640 }
13641
13642 if (vmu) {
13643 if (!strncasecmp(arg.attribute, "password", 8)) {
13645 } else if (!strncasecmp(arg.attribute, "fullname", 8)) {
13647 } else if (!strncasecmp(arg.attribute, "email", 5)) {
13648 ast_copy_string(buf, vmu->email, len);
13649 } else if (!strncasecmp(arg.attribute, "pager", 5)) {
13650 ast_copy_string(buf, vmu->pager, len);
13651 } else if (!strncasecmp(arg.attribute, "language", 8)) {
13653 } else if (!strncasecmp(arg.attribute, "locale", 6)) {
13654 ast_copy_string(buf, vmu->locale, len);
13655 } else if (!strncasecmp(arg.attribute, "tz", 2)) {
13657 } else if (!strncasecmp(arg.attribute, "count", 5)) {
13658 char *mailbox_id;
13659
13660 mailbox_id = ast_alloca(strlen(mailbox) + strlen(context) + 2);
13661 sprintf(mailbox_id, "%s@%s", mailbox, context);/* Safe */
13662
13663 /* If mbxfolder is empty messagecount will default to INBOX */
13664 res = messagecount(mailbox_id, arg.folder);
13665 if (res < 0) {
13666 ast_log(LOG_ERROR, "Unable to retrieve message count for mailbox %s\n", arg.mailbox_context);
13667 free_user(vmu);
13668 return -1;
13669 }
13670 snprintf(buf, len, "%d", res);
13671 } else {
13672 ast_log(LOG_ERROR, "Unknown attribute '%s' for VM_INFO\n", arg.attribute);
13673 free_user(vmu);
13674 return -1;
13675 }
13676 free_user(vmu);
13677 }
13678
13679 return 0;
13680}
13681
13683 .name = "VM_INFO",
13684 .read = acf_vm_info,
13685};
13686
13687static int vmauthenticate(struct ast_channel *chan, const char *data)
13688{
13689 char *s, *user = NULL, *context = NULL, mailbox[AST_MAX_EXTENSION] = "";
13690 struct ast_vm_user vmus = {{0}};
13691 char *options = NULL;
13692 int silent = 0, skipuser = 0;
13693 int res = -1;
13694
13695 if (data) {
13696 s = ast_strdupa(data);
13697 user = strsep(&s, ",");
13698 options = strsep(&s, ",");
13699 if (user) {
13700 s = user;
13701 user = strsep(&s, "@");
13702 context = strsep(&s, "");
13703 if (!ast_strlen_zero(user))
13704 skipuser++;
13706 }
13707 }
13708
13709 if (options) {
13710 silent = (strchr(options, 's')) != NULL;
13711 }
13712
13713 if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
13714 pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
13715 pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
13716 ast_play_and_wait(chan, "auth-thankyou");
13717 res = 0;
13718 } else if (mailbox[0] == '*') {
13719 /* user entered '*' */
13720 if (!ast_goto_if_exists(chan, ast_channel_context(chan), "a", 1)) {
13721 res = 0; /* prevent hangup */
13722 }
13723 }
13724
13725 return res;
13726}
13727
13728static char *show_users_realtime(int fd, const char *context)
13729{
13730 struct ast_config *cfg;
13731 const char *cat = NULL;
13732
13733 if (!(cfg = ast_load_realtime_multientry("voicemail",
13734 "context", context, SENTINEL))) {
13735 return CLI_FAILURE;
13736 }
13737
13738 ast_cli(fd,
13739 "\n"
13740 "=============================================================\n"
13741 "=== Configured Voicemail Users ==============================\n"
13742 "=============================================================\n"
13743 "===\n");
13744
13745 while ((cat = ast_category_browse(cfg, cat))) {
13746 struct ast_variable *var = NULL;
13747 ast_cli(fd,
13748 "=== Mailbox ...\n"
13749 "===\n");
13750 for (var = ast_variable_browse(cfg, cat); var; var = var->next)
13751 ast_cli(fd, "=== ==> %s: %s\n", var->name, var->value);
13752 ast_cli(fd,
13753 "===\n"
13754 "=== ---------------------------------------------------------\n"
13755 "===\n");
13756 }
13757
13758 ast_cli(fd,
13759 "=============================================================\n"
13760 "\n");
13761
13762 ast_config_destroy(cfg);
13763
13764 return CLI_SUCCESS;
13765}
13766
13767static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
13768{
13769 int which = 0;
13770 int wordlen;
13771 struct ast_vm_user *vmu;
13772 const char *context = "";
13773 char *ret;
13774
13775 /* 0 - voicemail; 1 - show; 2 - users; 3 - for; 4 - <context> */
13776 if (pos > 4)
13777 return NULL;
13778 wordlen = strlen(word);
13780 AST_LIST_TRAVERSE(&users, vmu, list) {
13781 if (!strncasecmp(word, vmu->context, wordlen)) {
13782 if (context && strcmp(context, vmu->context) && ++which > state) {
13783 ret = ast_strdup(vmu->context);
13785 return ret;
13786 }
13787 /* ignore repeated contexts ? */
13788 context = vmu->context;
13789 }
13790 }
13792 return NULL;
13793}
13794
13795/*! \brief Show a list of voicemail users in the CLI */
13796static char *handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13797{
13798 struct ast_vm_user *vmu;
13799#define HVSU_OUTPUT_FORMAT "%-10s %-5s %-25s %-10s %6s\n"
13800 const char *context = NULL;
13801 int users_counter = 0;
13802
13803 switch (cmd) {
13804 case CLI_INIT:
13805 e->command = "voicemail show users [for]";
13806 e->usage =
13807 "Usage: voicemail show users [for <context>]\n"
13808 " Lists all mailboxes currently set up\n";
13809 return NULL;
13810 case CLI_GENERATE:
13811 return complete_voicemail_show_users(a->line, a->word, a->pos, a->n);
13812 }
13813
13814 if ((a->argc < 3) || (a->argc > 5) || (a->argc == 4))
13815 return CLI_SHOWUSAGE;
13816 if (a->argc == 5) {
13817 if (strcmp(a->argv[3],"for"))
13818 return CLI_SHOWUSAGE;
13819 context = a->argv[4];
13820 }
13821
13822 if (ast_check_realtime("voicemail")) {
13823 if (!context) {
13824 ast_cli(a->fd, "You must specify a specific context to show users from realtime!\n");
13825 return CLI_SHOWUSAGE;
13826 }
13827 return show_users_realtime(a->fd, context);
13828 }
13829
13831 if (AST_LIST_EMPTY(&users)) {
13832 ast_cli(a->fd, "There are no voicemail users currently defined\n");
13834 return CLI_FAILURE;
13835 }
13836 if (!context) {
13837 ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
13838 } else {
13839 int count = 0;
13840 AST_LIST_TRAVERSE(&users, vmu, list) {
13841 if (!strcmp(context, vmu->context)) {
13842 count++;
13843 break;
13844 }
13845 }
13846 if (count) {
13847 ast_cli(a->fd, HVSU_OUTPUT_FORMAT, "Context", "Mbox", "User", "Zone", "NewMsg");
13848 } else {
13849 ast_cli(a->fd, "No such voicemail context \"%s\"\n", context);
13851 return CLI_FAILURE;
13852 }
13853 }
13854 AST_LIST_TRAVERSE(&users, vmu, list) {
13855 int newmsgs = 0, oldmsgs = 0;
13856 char count[12], tmp[256] = "";
13857
13858 if (!context || !strcmp(context, vmu->context)) {
13859 snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
13860 inboxcount(tmp, &newmsgs, &oldmsgs);
13861 snprintf(count, sizeof(count), "%d", newmsgs);
13862 ast_cli(a->fd, HVSU_OUTPUT_FORMAT, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
13863 users_counter++;
13864 }
13865 }
13867 ast_cli(a->fd, "%d voicemail users configured.\n", users_counter);
13868 return CLI_SUCCESS;
13869}
13870
13871/*! \brief Show a list of voicemail zones in the CLI */
13872static char *handle_voicemail_show_zones(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13873{
13874 struct vm_zone *zone;
13875#define HVSZ_OUTPUT_FORMAT "%-15s %-20s %-45s\n"
13876 char *res = CLI_SUCCESS;
13877
13878 switch (cmd) {
13879 case CLI_INIT:
13880 e->command = "voicemail show zones";
13881 e->usage =
13882 "Usage: voicemail show zones\n"
13883 " Lists zone message formats\n";
13884 return NULL;
13885 case CLI_GENERATE:
13886 return NULL;
13887 }
13888
13889 if (a->argc != 3)
13890 return CLI_SHOWUSAGE;
13891
13893 if (!AST_LIST_EMPTY(&zones)) {
13894 ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, "Zone", "Timezone", "Message Format");
13895 AST_LIST_TRAVERSE(&zones, zone, list) {
13896 ast_cli(a->fd, HVSZ_OUTPUT_FORMAT, zone->name, zone->timezone, zone->msg_format);
13897 }
13898 } else {
13899 ast_cli(a->fd, "There are no voicemail zones currently defined\n");
13900 res = CLI_FAILURE;
13901 }
13903
13904 return res;
13905}
13906
13907/*! \brief Show a list of voicemail zones in the CLI */
13908static char *handle_voicemail_show_aliases(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13909{
13910 struct ao2_iterator aliases;
13911 struct alias_mailbox_mapping *mapping;
13912#define ALIASES_OUTPUT_FORMAT "%-32s %-32s\n"
13913 char *res = CLI_SUCCESS;
13914
13915 switch (cmd) {
13916 case CLI_INIT:
13917 e->command = "voicemail show aliases";
13918 e->usage =
13919 "Usage: voicemail show aliases\n"
13920 " Lists mailbox aliases\n";
13921 return NULL;
13922 case CLI_GENERATE:
13923 return NULL;
13924 }
13925
13926 if (a->argc != 3)
13927 return CLI_SHOWUSAGE;
13928
13930 ast_cli(a->fd, "Aliases are not enabled\n");
13931 return res;
13932 }
13933
13934 ast_cli(a->fd, "Aliases context: %s\n", aliasescontext);
13935 ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, "Alias", "Mailbox");
13936
13938 while ((mapping = ao2_iterator_next(&aliases))) {
13939 ast_cli(a->fd, ALIASES_OUTPUT_FORMAT, mapping->alias, mapping->mailbox);
13940 ao2_ref(mapping, -1);
13941 }
13943
13944 return res;
13945}
13946
13947/*! \brief Reload voicemail configuration from the CLI */
13948static char *handle_voicemail_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
13949{
13950 switch (cmd) {
13951 case CLI_INIT:
13952 e->command = "voicemail reload";
13953 e->usage =
13954 "Usage: voicemail reload\n"
13955 " Reload voicemail configuration\n";
13956 return NULL;
13957 case CLI_GENERATE:
13958 return NULL;
13959 }
13960
13961 if (a->argc != 2)
13962 return CLI_SHOWUSAGE;
13963
13964 ast_cli(a->fd, "Reloading voicemail configuration...\n");
13965 load_config(1);
13966
13967 return CLI_SUCCESS;
13968}
13969
13970static struct ast_cli_entry cli_voicemail[] = {
13971 AST_CLI_DEFINE(handle_voicemail_show_users, "List defined voicemail boxes"),
13972 AST_CLI_DEFINE(handle_voicemail_show_zones, "List zone message formats"),
13973 AST_CLI_DEFINE(handle_voicemail_show_aliases, "List mailbox aliases"),
13974 AST_CLI_DEFINE(handle_voicemail_reload, "Reload voicemail configuration"),
13975 AST_CLI_DEFINE(handle_voicemail_show_mailbox, "Display a mailbox's content details"),
13976 AST_CLI_DEFINE(handle_voicemail_forward_message, "Forward message to another folder"),
13977 AST_CLI_DEFINE(handle_voicemail_move_message, "Move message to another folder"),
13979};
13980
13981static int poll_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
13982{
13983 int new = 0, old = 0, urgent = 0;
13984
13985 if (!mwi_state) {
13986 /* This should only occur due to allocation failure of a default mwi state object */
13987 return 0;
13988 }
13989
13990 inboxcount2(mwi_state->uniqueid, &urgent, &new, &old);
13991
13992#ifdef IMAP_STORAGE
13993 if (imap_poll_logout) {
13994 imap_logout(mwi_state->uniqueid);
13995 }
13996#endif
13997
13998 if (urgent != mwi_state->urgent_msgs || new != mwi_state->new_msgs || old != mwi_state->old_msgs) {
13999 queue_mwi_event(NULL, mwi_state->uniqueid, urgent, new, old);
14000 run_externnotify(NULL, mwi_state->uniqueid, NULL);
14001 }
14002
14003 return 0;
14004}
14005
14006static void *mb_poll_thread(void *data)
14007{
14008 while (poll_thread_run) {
14009 struct timespec ts = { 0, };
14010 struct timeval wait;
14011
14013
14014 if (!poll_thread_run) {
14015 break;
14016 }
14017
14019 ts.tv_sec = wait.tv_sec;
14020 ts.tv_nsec = wait.tv_usec * 1000;
14021
14025 }
14026
14027 return NULL;
14028}
14029
14030#ifdef IMAP_STORAGE
14031static void imap_logout(const char *mailbox_id)
14032{
14033 char *context;
14034 char *mailbox;
14035 struct ast_vm_user vmus;
14036 RAII_VAR(struct ast_vm_user *, vmu, NULL, free_user);
14037 struct vm_state *vms = NULL;
14038
14039 if (ast_strlen_zero(mailbox_id)
14040 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
14041 return;
14042 }
14043
14044 memset(&vmus, 0, sizeof(vmus));
14045
14046 if (!(vmu = find_user(&vmus, context, mailbox)) || vmu->imapuser[0] == '\0') {
14047 return;
14048 }
14049
14050 vms = get_vm_state_by_imapuser(vmu->imapuser, 0);
14051 if (!vms) {
14052 vms = get_vm_state_by_mailbox(mailbox, context, 0);
14053 }
14054 if (!vms) {
14055 return;
14056 }
14057
14058 ast_mutex_lock(&vms->lock);
14059 vms->mailstream = mail_close(vms->mailstream);
14060 ast_mutex_unlock(&vms->lock);
14061
14062 vmstate_delete(vms);
14063}
14064
14065static int imap_close_subscribed_mailbox(struct ast_mwi_state *mwi_state, void *data)
14066{
14067 if (mwi_state && !ast_strlen_zero(mwi_state->uniqueid)) {
14068 imap_logout(mwi_state->uniqueid);
14069 }
14070
14071 return 0;
14072}
14073
14074#endif
14075
14076static int mwi_handle_unsubscribe2(void *data)
14077{
14078 struct ast_mwi_state *mwi_state = data;
14079
14080 /*
14081 * Go ahead and clear the implicit MWI publisher here to avoid a leak. If a backing
14082 * configuration is available it'll re-initialize (reset the cached state) on its
14083 * next publish.
14084 */
14086
14087#ifdef IMAP_STORAGE
14088 imap_close_subscribed_mailbox(mwi_state, NULL);
14089#endif
14090
14091 ao2_ref(mwi_state, -1);
14092 return 0;
14093}
14094
14095static void mwi_handle_unsubscribe(const char *id, struct ast_mwi_subscriber *sub)
14096{
14097 void *data = ast_mwi_subscriber_data(sub);
14098
14099 /* Don't bump data's reference. We'll just use the one returned above */
14101 /* A reference was returned for data when retrieving, so remove it on error */
14102 ao2_ref(data, -1);
14103 }
14104}
14105
14106static int mwi_handle_subscribe2(void *data)
14107{
14109 ao2_ref(data, -1);
14110 return 0;
14111}
14112
14113static void mwi_handle_subscribe(const char *id, struct ast_mwi_subscriber *sub)
14114{
14115 void *data = ast_mwi_subscriber_data(sub);
14116
14117 /* Don't bump data's reference. We'll just use the one returned above */
14119 /* A reference was returned for data when retrieving, so remove it on error */
14120 ao2_ref(data, -1);
14121 }
14122}
14123
14126 .on_unsubscribe = mwi_handle_unsubscribe,
14127};
14128
14129static void start_poll_thread(void)
14130{
14131 int errcode;
14133
14134 poll_thread_run = 1;
14135
14136 if ((errcode = ast_pthread_create(&poll_thread, NULL, mb_poll_thread, NULL))) {
14137 ast_log(LOG_ERROR, "Could not create thread: %s\n", strerror(errcode));
14138 }
14139}
14140
14141static void stop_poll_thread(void)
14142{
14143 poll_thread_run = 0;
14144
14148
14149 pthread_join(poll_thread, NULL);
14151
14153}
14154
14155/*!
14156 * \brief Append vmu info string into given astman with event_name.
14157 * \return 0 failed. 1 otherwise.
14158*/
14160 struct mansession *s,
14161 struct ast_vm_user *vmu,
14162 const char* event_name,
14163 const char* actionid
14164 )
14165{
14166 int new;
14167 int old;
14168 char *mailbox;
14169 int ret;
14170
14171 if((s == NULL) || (vmu == NULL) || (event_name == NULL) || (actionid == NULL)) {
14172 ast_log(LOG_ERROR, "Wrong input parameter.");
14173 return 0;
14174 }
14175
14176 /* create mailbox string */
14177 if (!ast_strlen_zero(vmu->context)) {
14178 ret = ast_asprintf(&mailbox, "%s@%s", vmu->mailbox, vmu->context);
14179 } else {
14180 ret = ast_asprintf(&mailbox, "%s", vmu->mailbox);
14181 }
14182 if (ret == -1) {
14183 ast_log(LOG_ERROR, "Could not create mailbox string. err[%s]\n", strerror(errno));
14184 return 0;
14185 }
14186
14187 /* get mailbox count */
14188 ret = inboxcount(mailbox, &new, &old);
14190 if (ret == -1) {
14191 ast_log(LOG_ERROR, "Could not get mailbox count. user[%s], context[%s]\n",
14192 vmu->mailbox ?: "", vmu->context ?: "");
14193 return 0;
14194 }
14195
14196 astman_append(s,
14197 "Event: %s\r\n"
14198 "%s"
14199 "VMContext: %s\r\n"
14200 "VoiceMailbox: %s\r\n"
14201 "Fullname: %s\r\n"
14202 "Email: %s\r\n"
14203 "Pager: %s\r\n"
14204 "ServerEmail: %s\r\n"
14205 "FromString: %s\r\n"
14206 "MailCommand: %s\r\n"
14207 "Language: %s\r\n"
14208 "TimeZone: %s\r\n"
14209 "Callback: %s\r\n"
14210 "Dialout: %s\r\n"
14211 "UniqueID: %s\r\n"
14212 "ExitContext: %s\r\n"
14213 "SayDurationMinimum: %d\r\n"
14214 "SayEnvelope: %s\r\n"
14215 "SayCID: %s\r\n"
14216 "AttachMessage: %s\r\n"
14217 "AttachmentFormat: %s\r\n"
14218 "DeleteMessage: %s\r\n"
14219 "VolumeGain: %.2f\r\n"
14220 "CanReview: %s\r\n"
14221 "CanMarkUrgent: %s\r\n"
14222 "CallOperator: %s\r\n"
14223 "MaxMessageCount: %d\r\n"
14224 "MaxMessageLength: %d\r\n"
14225 "NewMessageCount: %d\r\n"
14226 "OldMessageCount: %d\r\n"
14227#ifdef IMAP_STORAGE
14228 "IMAPUser: %s\r\n"
14229 "IMAPServer: %s\r\n"
14230 "IMAPPort: %s\r\n"
14231 "IMAPFlags: %s\r\n"
14232#endif
14233 "\r\n",
14234
14235 event_name,
14236 actionid,
14237 vmu->context,
14238 vmu->mailbox,
14239 vmu->fullname,
14240 vmu->email,
14241 vmu->pager,
14244 mailcmd,
14245 vmu->language,
14246 vmu->zonetag,
14247 vmu->callback,
14248 vmu->dialout,
14249 vmu->uniqueid,
14250 vmu->exit,
14251 vmu->saydurationm,
14252 ast_test_flag(vmu, VM_ENVELOPE) ? "Yes" : "No",
14253 ast_test_flag(vmu, VM_SAYCID) ? "Yes" : "No",
14254 ast_test_flag(vmu, VM_ATTACH) ? "Yes" : "No",
14255 vmu->attachfmt,
14256 ast_test_flag(vmu, VM_DELETE) ? "Yes" : "No",
14257 vmu->volgain,
14258 ast_test_flag(vmu, VM_REVIEW) ? "Yes" : "No",
14259 ast_test_flag(vmu, VM_MARK_URGENT) ? "Yes" : "No",
14260 ast_test_flag(vmu, VM_OPERATOR) ? "Yes" : "No",
14261 vmu->maxmsg,
14262 vmu->maxsecs,
14263 new,
14264 old
14265#ifdef IMAP_STORAGE
14266 ,
14267 vmu->imapuser,
14268 vmu->imapserver,
14269 vmu->imapport,
14270 vmu->imapflags
14271#endif
14272 );
14273
14274 return 1;
14275
14276}
14277
14278
14279/*!
14280 * \brief Append vmbox info string into given astman with event_name.
14281 * \return 0 if unable to append details, 1 otherwise.
14282*/
14284 struct mansession *s,
14285 const struct message *m,
14286 struct ast_vm_user *vmu,
14287 const char* event_name,
14288 const char* actionid)
14289{
14290 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
14291 struct ast_vm_msg_snapshot *msg;
14292 int nummessages = 0;
14293 int i;
14294
14295 /* Take a snapshot of the mailbox */
14296 mailbox_snapshot = ast_vm_mailbox_snapshot_create(vmu->mailbox, vmu->context, NULL, 0, AST_VM_SNAPSHOT_SORT_BY_ID, 0);
14297 if (!mailbox_snapshot) {
14298 ast_log(LOG_ERROR, "Could not append voicemail box info for box %s@%s.",
14299 vmu->mailbox, vmu->context);
14300 return 0;
14301 }
14302
14303 astman_send_listack(s, m, "Voicemail box detail will follow", "start");
14304 /* walk through each folder's contents and append info for each message */
14305 for (i = 0; i < mailbox_snapshot->folders; i++) {
14306 AST_LIST_TRAVERSE(&((mailbox_snapshot)->snapshots[i]), msg, msg) {
14307 astman_append(s,
14308 "Event: %s\r\n"
14309 "%s"
14310 "Folder: %s\r\n"
14311 "CallerID: %s\r\n"
14312 "Date: %s\r\n"
14313 "Duration: %s\r\n"
14314 "Flag: %s\r\n"
14315 "ID: %s\r\n"
14316 "\r\n",
14317 event_name,
14318 actionid,
14319 msg->folder_name,
14320 msg->callerid,
14321 msg->origdate,
14322 msg->duration,
14323 msg->flag,
14324 msg->msg_id
14325 );
14326 nummessages++;
14327 }
14328 }
14329
14330 /* done, destroy. */
14331 mailbox_snapshot = ast_vm_mailbox_snapshot_destroy(mailbox_snapshot);
14332 astman_send_list_complete_start(s, m, "VoicemailBoxDetailComplete", nummessages);
14334
14335 return 1;
14336}
14337
14338static int manager_match_mailbox(struct ast_mwi_state *mwi_state, void *data)
14339{
14340 const char *context = astman_get_header(data, "Context");
14341 const char *mailbox = astman_get_header(data, "Mailbox");
14342 const char *at;
14343
14344 if (!ast_strlen_zero(mwi_state->uniqueid)) {
14345 if (
14346 /* First case: everything matches */
14348 /* Second case: match the mailbox only */
14350 (at = strchr(mwi_state->uniqueid, '@')) &&
14351 strncmp(mailbox, mwi_state->uniqueid, at - mwi_state->uniqueid) == 0) ||
14352 /* Third case: match the context only */
14354 (at = strchr(mwi_state->uniqueid, '@')) &&
14355 strcmp(context, at + 1) == 0) ||
14356 /* Final case: match an exact specified mailbox */
14358 (at = strchr(mwi_state->uniqueid, '@')) &&
14359 strncmp(mailbox, mwi_state->uniqueid, at - mwi_state->uniqueid) == 0 &&
14360 strcmp(context, at + 1) == 0)
14361 ) {
14362 poll_subscribed_mailbox(mwi_state, NULL);
14363 }
14364 }
14365
14366 return 0;
14367}
14368
14369static int manager_voicemail_refresh(struct mansession *s, const struct message *m)
14370{
14372 astman_send_ack(s, m, "Refresh sent");
14373 return RESULT_SUCCESS;
14374}
14375
14376static int manager_status_voicemail_user(struct mansession *s, const struct message *m)
14377{
14378 struct ast_vm_user *vmu = NULL;
14379 const char *id = astman_get_header(m, "ActionID");
14380 char actionid[128];
14381 struct ast_vm_user svm;
14382 int ret;
14383
14384 const char *context = astman_get_header(m, "Context");
14385 const char *mailbox = astman_get_header(m, "Mailbox");
14386
14388 astman_send_error(s, m, "Need 'Context' and 'Mailbox' parameters.");
14389 return RESULT_SUCCESS;
14390 }
14391
14392 actionid[0] = '\0';
14393 if (!ast_strlen_zero(id)) {
14394 snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14395 }
14396
14397 /* find user */
14398 memset(&svm, 0, sizeof(svm));
14399 vmu = find_user(&svm, context, mailbox);
14400 if (!vmu) {
14401 /* could not find it */
14402 astman_send_ack(s, m, "There is no voicemail user of the given info.");
14403 return RESULT_SUCCESS;
14404 }
14405
14406 astman_send_listack(s, m, "Voicemail user detail will follow", "start");
14407
14408 /* append vmu info event */
14409 ret = append_vmu_info_astman(s, vmu, "VoicemailUserDetail", actionid);
14410 free_user(vmu);
14411 if(ret == 0) {
14412 ast_log(LOG_ERROR, "Could not append voicemail user info.");
14413 }
14414
14415 astman_send_list_complete_start(s, m, "VoicemailUserDetailComplete", 1);
14417
14418 return RESULT_SUCCESS;
14419}
14420
14421/*! \brief Manager list voicemail users command */
14422static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
14423{
14424 struct ast_vm_user *vmu = NULL;
14425 const char *id = astman_get_header(m, "ActionID");
14426 char actionid[128];
14427 int num_users = 0;
14428 int ret;
14429
14430 actionid[0] = '\0';
14431 if (!ast_strlen_zero(id)) {
14432 snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14433 }
14434
14436
14437 if (AST_LIST_EMPTY(&users)) {
14438 astman_send_ack(s, m, "There are no voicemail users currently defined.");
14440 return RESULT_SUCCESS;
14441 }
14442
14443 astman_send_listack(s, m, "Voicemail user list will follow", "start");
14444
14445 AST_LIST_TRAVERSE(&users, vmu, list) {
14446 /* append vmu info event */
14447 ret = append_vmu_info_astman(s, vmu, "VoicemailUserEntry", actionid);
14448 if(ret == 0) {
14449 ast_log(LOG_ERROR, "Could not append voicemail user info.");
14450 continue;
14451 }
14452 ++num_users;
14453 }
14454
14455 astman_send_list_complete_start(s, m, "VoicemailUserEntryComplete", num_users);
14457
14459
14460 return RESULT_SUCCESS;
14461}
14462
14463static int manager_get_mailbox_summary(struct mansession *s, const struct message *m)
14464{
14465 struct ast_vm_user *vmu = NULL;
14466 const char *id = astman_get_header(m, "ActionID");
14467 char actionid[128];
14468 struct ast_vm_user svm;
14469
14470 const char *context = astman_get_header(m, "Context");
14471 const char *mailbox = astman_get_header(m, "Mailbox");
14472
14474 astman_send_error(s, m, "Need 'Context' and 'Mailbox' parameters.");
14475 return 0;
14476 }
14477
14478 actionid[0] = '\0';
14479 if (!ast_strlen_zero(id)) {
14480 snprintf(actionid, sizeof(actionid), "ActionID: %s\r\n", id);
14481 }
14482
14483 /* find user */
14484 memset(&svm, 0, sizeof(svm));
14485 vmu = find_user(&svm, context, mailbox);
14486 if (!vmu) {
14487 /* could not find it */
14488 astman_send_ack(s, m, "There is no voicemail user matching the given user.");
14489 return 0;
14490 }
14491
14492 /* Append the mailbox details */
14493 if (!append_vmbox_info_astman(s, m, vmu, "VoicemailBoxDetail", actionid)) {
14494 astman_send_error(s, m, "Unable to get mailbox info for the given user.");
14495 }
14496
14497 free_user(vmu);
14498 return 0;
14499}
14500
14501static int manager_voicemail_move(struct mansession *s, const struct message *m)
14502{
14503 const char *mailbox = astman_get_header(m, "Mailbox");
14504 const char *context = astman_get_header(m, "Context");
14505 const char *from_folder = astman_get_header(m, "Folder");
14506 const char *id[] = { astman_get_header(m, "ID") };
14507 const char *to_folder = astman_get_header(m, "ToFolder");
14508
14509 if (ast_strlen_zero(mailbox)) {
14510 astman_send_error(s, m, "Mailbox not specified, required");
14511 return 0;
14512 }
14513 if (ast_strlen_zero(context)) {
14514 astman_send_error(s, m, "Context not specified, required");
14515 return 0;
14516 }
14517 if (ast_strlen_zero(from_folder)) {
14518 astman_send_error(s, m, "Folder not specified, required");
14519 return 0;
14520 }
14521 if (ast_strlen_zero(id[0])) {
14522 astman_send_error(s, m, "ID not specified, required");
14523 return 0;
14524 }
14525 if (ast_strlen_zero(to_folder)) {
14526 astman_send_error(s, m, "ToFolder not specified, required");
14527 return 0;
14528 }
14529
14530 if (vm_msg_move(mailbox, context, 1, from_folder, id, to_folder)) {
14531 astman_send_ack(s, m, "Message move failed\n");
14532 } else {
14533 astman_send_ack(s, m, "Message move successful\n");
14534 }
14535
14536 return 0;
14537}
14538
14539static int manager_voicemail_remove(struct mansession *s, const struct message *m)
14540{
14541 const char *mailbox = astman_get_header(m, "Mailbox");
14542 const char *context = astman_get_header(m, "Context");
14543 const char *folder = astman_get_header(m, "Folder");
14544 const char *id[] = { astman_get_header(m, "ID") };
14545
14546 if (ast_strlen_zero(mailbox)) {
14547 astman_send_error(s, m, "Mailbox not specified, required");
14548 return 0;
14549 }
14550 if (ast_strlen_zero(context)) {
14551 astman_send_error(s, m, "Context not specified, required");
14552 return 0;
14553 }
14554 if (ast_strlen_zero(folder)) {
14555 astman_send_error(s, m, "Folder not specified, required");
14556 return 0;
14557 }
14558 if (ast_strlen_zero(id[0])) {
14559 astman_send_error(s, m, "ID not specified, required");
14560 return 0;
14561 }
14562
14563 if (vm_msg_remove(mailbox, context, 1, folder, id)) {
14564 astman_send_ack(s, m, "Message remove failed\n");
14565 } else {
14566 astman_send_ack(s, m, "Message remove successful\n");
14567 }
14568
14569 return 0;
14570}
14571
14572static int manager_voicemail_forward(struct mansession *s, const struct message *m)
14573{
14574 const char *from_mailbox = astman_get_header(m, "Mailbox");
14575 const char *from_context = astman_get_header(m, "Context");
14576 const char *from_folder = astman_get_header(m, "Folder");
14577 const char *id[] = { astman_get_header(m, "ID") };
14578 const char *to_mailbox = astman_get_header(m, "ToMailbox");
14579 const char *to_context = astman_get_header(m, "ToContext");
14580 const char *to_folder = astman_get_header(m, "ToFolder");
14581
14583 astman_send_error(s, m, "Mailbox not specified, required");
14584 return 0;
14585 }
14586 if (ast_strlen_zero(from_context)) {
14587 astman_send_error(s, m, "Context not specified, required");
14588 return 0;
14589 }
14590 if (ast_strlen_zero(from_folder)) {
14591 astman_send_error(s, m, "Folder not specified, required");
14592 return 0;
14593 }
14594 if (ast_strlen_zero(id[0])) {
14595 astman_send_error(s, m, "ID not specified, required");
14596 return 0;
14597 }
14598 if (ast_strlen_zero(to_mailbox)) {
14599 astman_send_error(s, m, "ToMailbox not specified, required");
14600 return 0;
14601 }
14602 if (ast_strlen_zero(to_context)) {
14603 astman_send_error(s, m, "ToContext not specified, required");
14604 return 0;
14605 }
14606 if (ast_strlen_zero(to_folder)) {
14607 astman_send_error(s, m, "ToFolder not specified, required");
14608 return 0;
14609 }
14610
14611 if (vm_msg_forward(from_mailbox, from_context, from_folder, to_mailbox, to_context, to_folder, 1, id, 0)) {
14612 astman_send_ack(s, m, "Message forward failed\n");
14613 } else {
14614 astman_send_ack(s, m, "Message forward successful\n");
14615 }
14616
14617 return 0;
14618}
14619
14620/*! \brief Free the users structure. */
14621static void free_vm_users(void)
14622{
14623 struct ast_vm_user *current;
14625 while ((current = AST_LIST_REMOVE_HEAD(&users, list))) {
14628 }
14630}
14631
14632/*! \brief Free the zones structure. */
14633static void free_vm_zones(void)
14634{
14635 struct vm_zone *zcur;
14637 while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list)))
14638 free_zone(zcur);
14640}
14641
14642static const char *substitute_escapes(const char *value)
14643{
14644 char *current;
14645
14646 /* Add 16 for fudge factor */
14647 struct ast_str *str = ast_str_thread_get(&ast_str_thread_global_buf, strlen(value) + 16);
14648
14650
14651 /* Substitute strings \r, \n, and \t into the appropriate characters */
14652 for (current = (char *) value; *current; current++) {
14653 if (*current == '\\') {
14654 current++;
14655 if (!*current) {
14656 ast_log(AST_LOG_NOTICE, "Incomplete escape at end of value.\n");
14657 break;
14658 }
14659 switch (*current) {
14660 case '\\':
14661 ast_str_append(&str, 0, "\\");
14662 break;
14663 case 'r':
14664 ast_str_append(&str, 0, "\r");
14665 break;
14666 case 'n':
14667#ifdef IMAP_STORAGE
14668 if (!str->used || str->str[str->used - 1] != '\r') {
14669 ast_str_append(&str, 0, "\r");
14670 }
14671#endif
14672 ast_str_append(&str, 0, "\n");
14673 break;
14674 case 't':
14675 ast_str_append(&str, 0, "\t");
14676 break;
14677 default:
14678 ast_log(AST_LOG_NOTICE, "Substitution routine does not support this character: \\%c\n", *current);
14679 break;
14680 }
14681 } else {
14682 ast_str_append(&str, 0, "%c", *current);
14683 }
14684 }
14685
14686 return ast_str_buffer(str);
14687}
14688
14689static int load_config_force(int reload, int force)
14690{
14691 struct ast_config *cfg, *ucfg;
14692 struct ast_flags config_flags = { reload && !force ? CONFIG_FLAG_FILEUNCHANGED : 0 };
14693 int res;
14694
14695 ast_unload_realtime("voicemail");
14696 ast_unload_realtime("voicemail_data");
14697
14698 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
14699 if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEUNCHANGED) {
14700 return 0;
14701 } else if (ucfg == CONFIG_STATUS_FILEINVALID) {
14702 ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
14703 ucfg = NULL;
14704 }
14706 if ((cfg = ast_config_load(VOICEMAIL_CONFIG, config_flags)) == CONFIG_STATUS_FILEINVALID) {
14707 ast_config_destroy(ucfg);
14708 ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
14709 return 0;
14710 }
14711 } else if (cfg == CONFIG_STATUS_FILEINVALID) {
14712 ast_log(LOG_ERROR, "Config file " VOICEMAIL_CONFIG " is in an invalid format. Aborting.\n");
14713 return 0;
14714 } else {
14716 if ((ucfg = ast_config_load("users.conf", config_flags)) == CONFIG_STATUS_FILEINVALID) {
14717 ast_log(LOG_ERROR, "Config file users.conf is in an invalid format. Avoiding.\n");
14718 ucfg = NULL;
14719 }
14720 }
14721
14722 res = actual_load_config(reload, cfg, ucfg);
14723
14724 ast_config_destroy(cfg);
14725 ast_config_destroy(ucfg);
14726
14727 return res;
14728}
14729
14730static int load_config(int reload)
14731{
14732 return load_config_force(reload, 0);
14733}
14734
14735#ifdef TEST_FRAMEWORK
14736static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg)
14737{
14738 ast_unload_realtime("voicemail");
14739 ast_unload_realtime("voicemail_data");
14740 return actual_load_config(reload, cfg, ucfg);
14741}
14742#endif
14743
14744static struct alias_mailbox_mapping *alias_mailbox_mapping_create(const char *alias, const char *mailbox)
14745{
14746 struct alias_mailbox_mapping *mapping;
14747 size_t from_len = strlen(alias) + 1;
14748 size_t to_len = strlen(mailbox) + 1;
14749
14750 mapping = ao2_alloc(sizeof(*mapping) + from_len + to_len, NULL);
14751 if (!mapping) {
14752 return NULL;
14753 }
14754 mapping->alias = mapping->buf;
14755 mapping->mailbox = mapping->buf + from_len;
14756 ast_copy_string(mapping->alias, alias, from_len); /* Safe */
14757 ast_copy_string(mapping->mailbox, mailbox, to_len); /* Safe */
14758
14759 return mapping;
14760}
14761
14762static void load_aliases(struct ast_config *cfg)
14763{
14764 struct ast_variable *var;
14765
14767 return;
14768 }
14770 while (var) {
14771 struct alias_mailbox_mapping *mapping = alias_mailbox_mapping_create(var->name, var->value);
14772 if (mapping) {
14775 ao2_ref(mapping, -1);
14776 }
14777 var = var->next;
14778 }
14779}
14780
14781static void load_zonemessages(struct ast_config *cfg)
14782{
14783 struct ast_variable *var;
14784
14785 var = ast_variable_browse(cfg, "zonemessages");
14786 while (var) {
14787 if (var->value) {
14788 struct vm_zone *z;
14789 char *msg_format, *tzone;
14790 char storage[strlen(var->value) + 1];
14791
14792 z = ast_malloc(sizeof(*z));
14793 if (!z) {
14794 return;
14795 }
14796
14797 strcpy(storage, var->value); /* safe */
14798 msg_format = storage;
14799 tzone = strsep(&msg_format, "|,");
14800 if (msg_format) {
14801 ast_copy_string(z->name, var->name, sizeof(z->name));
14802 ast_copy_string(z->timezone, tzone, sizeof(z->timezone));
14807 } else {
14808 ast_log(AST_LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
14809 ast_free(z);
14810 }
14811 }
14812 var = var->next;
14813 }
14814}
14815
14816static void load_users(struct ast_config *cfg)
14817{
14818 struct ast_variable *var;
14819 char *cat = NULL;
14820
14821 while ((cat = ast_category_browse(cfg, cat))) {
14822 if (strcasecmp(cat, "general") == 0
14823 || strcasecmp(cat, aliasescontext) == 0
14824 || strcasecmp(cat, "zonemessages") == 0) {
14825 continue;
14826 }
14827
14828 var = ast_variable_browse(cfg, cat);
14829 while (var) {
14830 append_mailbox(cat, var->name, var->value);
14831 var = var->next;
14832 }
14833 }
14834}
14835
14836static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
14837{
14838 struct ast_vm_user *current;
14839 char *cat;
14840 const char *val;
14841 char *q, *stringp, *tmp;
14842 int x;
14843 unsigned int tmpadsi[4];
14844 char secretfn[PATH_MAX] = "";
14845 long tps_queue_low;
14846 long tps_queue_high;
14847
14848#ifdef IMAP_STORAGE
14849 ast_copy_string(imapparentfolder, "\0", sizeof(imapparentfolder));
14850#endif
14851 /* set audio control prompts */
14857
14858#ifdef IMAP_STORAGE
14859 ast_mwi_state_callback_all(imap_close_subscribed_mailbox, NULL);
14860#endif
14861
14862 /* Free all the users structure */
14863 free_vm_users();
14864
14865 /* Free all the zones structure */
14866 free_vm_zones();
14867
14868 /* Remove all aliases */
14871
14873
14874 memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
14875 memset(ext_pass_check_cmd, 0, sizeof(ext_pass_check_cmd));
14876
14877 if (cfg) {
14878 /* General settings */
14879
14880 if (!(val = ast_variable_retrieve(cfg, "general", "userscontext")))
14881 val = "default";
14883
14884 aliasescontext[0] = '\0';
14885 val = ast_variable_retrieve(cfg, "general", "aliasescontext");
14887
14888 /* Attach voice message to mail message ? */
14889 if (!(val = ast_variable_retrieve(cfg, "general", "attach")))
14890 val = "yes";
14892
14893 if (!(val = ast_variable_retrieve(cfg, "general", "searchcontexts")))
14894 val = "no";
14896
14897 volgain = 0.0;
14898 if ((val = ast_variable_retrieve(cfg, "general", "volgain")))
14899 sscanf(val, "%30lf", &volgain);
14900
14901#ifdef ODBC_STORAGE
14902 strcpy(odbc_database, "asterisk");
14903 if ((val = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
14904 ast_copy_string(odbc_database, val, sizeof(odbc_database));
14905 }
14906
14907 strcpy(odbc_table, "voicemessages");
14908 if ((val = ast_variable_retrieve(cfg, "general", "odbctable"))) {
14909 ast_copy_string(odbc_table, val, sizeof(odbc_table));
14910 }
14911 odbc_table_len = strlen(odbc_table);
14912
14914 if (!(val = ast_variable_retrieve(cfg, "general", "odbc_audio_on_disk")))
14915 val = "no";
14917
14918#endif
14919 /* Mail command */
14920 strcpy(mailcmd, SENDMAIL);
14921 if ((val = ast_variable_retrieve(cfg, "general", "mailcmd")))
14922 ast_copy_string(mailcmd, val, sizeof(mailcmd)); /* User setting */
14923
14924 maxsilence = 0;
14925 if ((val = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
14926 maxsilence = atoi(val);
14927 if (maxsilence > 0)
14928 maxsilence *= 1000;
14929 }
14930
14931 if (!(val = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
14932 maxmsg = MAXMSG;
14933 } else {
14934 maxmsg = atoi(val);
14935 if (maxmsg < 0) {
14936 ast_log(AST_LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", val, MAXMSG);
14937 maxmsg = MAXMSG;
14938 } else if (maxmsg > MAXMSGLIMIT) {
14939 ast_log(AST_LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
14941 }
14942 }
14943
14944 if (!(val = ast_variable_retrieve(cfg, "general", "backupdeleted"))) {
14945 maxdeletedmsg = 0;
14946 } else {
14947 if (sscanf(val, "%30d", &x) == 1)
14948 maxdeletedmsg = x;
14949 else if (ast_true(val))
14951 else
14952 maxdeletedmsg = 0;
14953
14954 if (maxdeletedmsg < 0) {
14955 ast_log(AST_LOG_WARNING, "Invalid number of deleted messages saved per mailbox '%s'. Using default value %i\n", val, MAXMSG);
14957 } else if (maxdeletedmsg > MAXMSGLIMIT) {
14958 ast_log(AST_LOG_WARNING, "Maximum number of deleted messages saved per mailbox is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, val);
14960 }
14961 }
14962
14963 /* Load date format config for voicemail mail */
14964 if ((val = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
14966 }
14967
14968 /* Load date format config for voicemail pager mail */
14969 if ((val = ast_variable_retrieve(cfg, "general", "pagerdateformat"))) {
14971 }
14972
14973 /* External password changing command */
14974 if ((val = ast_variable_retrieve(cfg, "general", "externpass"))) {
14977 } else if ((val = ast_variable_retrieve(cfg, "general", "externpassnotify"))) {
14980 }
14981
14982 /* External password validation command */
14983 if ((val = ast_variable_retrieve(cfg, "general", "externpasscheck"))) {
14985 ast_debug(1, "found externpasscheck: %s\n", ext_pass_check_cmd);
14986 }
14987
14988#ifdef IMAP_STORAGE
14989 /* IMAP server address */
14990 if ((val = ast_variable_retrieve(cfg, "general", "imapserver"))) {
14991 ast_copy_string(imapserver, val, sizeof(imapserver));
14992 } else {
14993 ast_copy_string(imapserver, "localhost", sizeof(imapserver));
14994 }
14995 /* IMAP server port */
14996 if ((val = ast_variable_retrieve(cfg, "general", "imapport"))) {
14997 ast_copy_string(imapport, val, sizeof(imapport));
14998 } else {
14999 ast_copy_string(imapport, "143", sizeof(imapport));
15000 }
15001 /* IMAP server flags */
15002 if ((val = ast_variable_retrieve(cfg, "general", "imapflags"))) {
15003 ast_copy_string(imapflags, val, sizeof(imapflags));
15004 }
15005 /* IMAP server master username */
15006 if ((val = ast_variable_retrieve(cfg, "general", "authuser"))) {
15007 ast_copy_string(authuser, val, sizeof(authuser));
15008 }
15009 /* IMAP server master password */
15010 if ((val = ast_variable_retrieve(cfg, "general", "authpassword"))) {
15011 ast_copy_string(authpassword, val, sizeof(authpassword));
15012 }
15013 /* Expunge on exit */
15014 if ((val = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
15015 if (ast_false(val))
15016 expungeonhangup = 0;
15017 else
15018 expungeonhangup = 1;
15019 } else {
15020 expungeonhangup = 1;
15021 }
15022 /* IMAP voicemail folder */
15023 if ((val = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
15024 ast_copy_string(imapfolder, val, sizeof(imapfolder));
15025 } else {
15026 ast_copy_string(imapfolder, "INBOX", sizeof(imapfolder));
15027 }
15028 if ((val = ast_variable_retrieve(cfg, "general", "imapparentfolder"))) {
15029 ast_copy_string(imapparentfolder, val, sizeof(imapparentfolder));
15030 }
15031 if ((val = ast_variable_retrieve(cfg, "general", "imapgreetings"))) {
15032 imapgreetings = ast_true(val);
15033 } else {
15034 imapgreetings = 0;
15035 }
15036 if ((val = ast_variable_retrieve(cfg, "general", "greetingfolder"))) {
15037 ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
15038 } else if ((val = ast_variable_retrieve(cfg, "general", "greetingsfolder"))) {
15039 /* Also support greetingsfolder as documented in voicemail.conf.sample */
15040 ast_copy_string(greetingfolder, val, sizeof(greetingfolder));
15041 } else {
15042 ast_copy_string(greetingfolder, imapfolder, sizeof(greetingfolder));
15043 }
15044 if ((val = ast_variable_retrieve(cfg, "general", "imap_poll_logout"))) {
15045 imap_poll_logout = ast_true(val);
15046 } else {
15047 imap_poll_logout = 0;
15048 }
15049
15050 /* There is some very unorthodox casting done here. This is due
15051 * to the way c-client handles the argument passed in. It expects a
15052 * void pointer and casts the pointer directly to a long without
15053 * first dereferencing it. */
15054 if ((val = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
15055 mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(val)));
15056 } else {
15057 mail_parameters(NIL, SET_READTIMEOUT, (void *) 60L);
15058 }
15059
15060 if ((val = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
15061 mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(val)));
15062 } else {
15063 mail_parameters(NIL, SET_WRITETIMEOUT, (void *) 60L);
15064 }
15065
15066 if ((val = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
15067 mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(val)));
15068 } else {
15069 mail_parameters(NIL, SET_OPENTIMEOUT, (void *) 60L);
15070 }
15071
15072 if ((val = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
15073 mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(val)));
15074 } else {
15075 mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) 60L);
15076 }
15077
15078 /* Increment configuration version */
15079 imapversion++;
15080#endif
15081 /* External voicemail notify application */
15082 if ((val = ast_variable_retrieve(cfg, "general", "externnotify"))) {
15084 ast_debug(1, "found externnotify: %s\n", externnotify);
15085 } else {
15086 externnotify[0] = '\0';
15087 }
15088
15089 /* SMDI voicemail notification */
15090 if ((val = ast_variable_retrieve(cfg, "general", "smdienable")) && ast_true(val)) {
15091 ast_debug(1, "Enabled SMDI voicemail notification\n");
15092 if ((val = ast_variable_retrieve(cfg, "general", "smdiport"))) {
15094 } else {
15095 ast_debug(1, "No SMDI interface set, trying default (/dev/ttyS0)\n");
15096 smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
15097 }
15098 if (!smdi_iface) {
15099 ast_log(AST_LOG_ERROR, "No valid SMDI interface specfied, disabling SMDI voicemail notification\n");
15100 }
15101 }
15102
15103 /* Silence treshold */
15105 if ((val = ast_variable_retrieve(cfg, "general", "silencethreshold")))
15106 silencethreshold = atoi(val);
15107
15108 if (!(val = ast_variable_retrieve(cfg, "general", "serveremail")))
15111
15112 vmmaxsecs = 0;
15113 if ((val = ast_variable_retrieve(cfg, "general", "maxsecs"))) {
15114 if (sscanf(val, "%30d", &x) == 1) {
15115 vmmaxsecs = x;
15116 } else {
15117 ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
15118 }
15119 } else if ((val = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
15120 static int maxmessage_deprecate = 0;
15121 if (maxmessage_deprecate == 0) {
15122 maxmessage_deprecate = 1;
15123 ast_log(AST_LOG_WARNING, "Setting 'maxmessage' has been deprecated in favor of 'maxsecs'.\n");
15124 }
15125 if (sscanf(val, "%30d", &x) == 1) {
15126 vmmaxsecs = x;
15127 } else {
15128 ast_log(AST_LOG_WARNING, "Invalid max message time length\n");
15129 }
15130 }
15131
15132 vmminsecs = 0;
15133 if ((val = ast_variable_retrieve(cfg, "general", "minsecs"))) {
15134 if (sscanf(val, "%30d", &x) == 1) {
15135 vmminsecs = x;
15136 if (maxsilence / 1000 >= vmminsecs) {
15137 ast_log(AST_LOG_WARNING, "maxsilence should be less than minsecs or you may get empty messages\n");
15138 }
15139 } else {
15140 ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
15141 }
15142 } else if ((val = ast_variable_retrieve(cfg, "general", "minmessage"))) {
15143 static int maxmessage_deprecate = 0;
15144 if (maxmessage_deprecate == 0) {
15145 maxmessage_deprecate = 1;
15146 ast_log(AST_LOG_WARNING, "Setting 'minmessage' has been deprecated in favor of 'minsecs'.\n");
15147 }
15148 if (sscanf(val, "%30d", &x) == 1) {
15149 vmminsecs = x;
15150 if (maxsilence / 1000 >= vmminsecs) {
15151 ast_log(AST_LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
15152 }
15153 } else {
15154 ast_log(AST_LOG_WARNING, "Invalid min message time length\n");
15155 }
15156 }
15157
15158 val = ast_variable_retrieve(cfg, "general", "format");
15159 if (!val) {
15160 val = "wav";
15161 } else {
15162 tmp = ast_strdupa(val);
15164 if (!val) {
15165 ast_log(LOG_ERROR, "Error processing format string, defaulting to format 'wav'\n");
15166 val = "wav";
15167 }
15168 }
15169 ast_copy_string(vmfmts, val, sizeof(vmfmts));
15170
15171 skipms = 3000;
15172 if ((val = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
15173 if (sscanf(val, "%30d", &x) == 1) {
15174 maxgreet = x;
15175 } else {
15176 ast_log(AST_LOG_WARNING, "Invalid max message greeting length\n");
15177 }
15178 }
15179
15180 if ((val = ast_variable_retrieve(cfg, "general", "skipms"))) {
15181 if (sscanf(val, "%30d", &x) == 1) {
15182 skipms = x;
15183 } else {
15184 ast_log(AST_LOG_WARNING, "Invalid skipms value\n");
15185 }
15186 }
15187
15188 maxlogins = 3;
15189 if ((val = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
15190 if (sscanf(val, "%30d", &x) == 1) {
15191 maxlogins = x;
15192 } else {
15193 ast_log(AST_LOG_WARNING, "Invalid max failed login attempts\n");
15194 }
15195 }
15196
15198 if ((val = ast_variable_retrieve(cfg, "general", "minpassword"))) {
15199 if (sscanf(val, "%30d", &x) == 1) {
15200 minpassword = x;
15201 } else {
15202 ast_log(AST_LOG_WARNING, "Invalid minimum password length. Default to %d\n", minpassword);
15203 }
15204 }
15205
15206 /* Force new user to record name ? */
15207 if (!(val = ast_variable_retrieve(cfg, "general", "forcename")))
15208 val = "no";
15210
15211 /* Force new user to record greetings ? */
15212 if (!(val = ast_variable_retrieve(cfg, "general", "forcegreetings")))
15213 val = "no";
15215
15216 if ((val = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))) {
15217 ast_debug(1, "VM_CID Internal context string: %s\n", val);
15218 stringp = ast_strdupa(val);
15219 for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
15220 if (!ast_strlen_zero(stringp)) {
15221 q = strsep(&stringp, ",");
15222 while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
15223 q++;
15225 ast_debug(1, "VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
15226 } else {
15227 cidinternalcontexts[x][0] = '\0';
15228 }
15229 }
15230 }
15231 if (!(val = ast_variable_retrieve(cfg, "general", "review"))){
15232 ast_debug(1, "VM Review Option disabled globally\n");
15233 val = "no";
15234 }
15236
15237 if (!(val = ast_variable_retrieve(cfg, "general", "leaveurgent"))){
15238 val = "yes";
15239 } else if (ast_false(val)) {
15240 ast_debug(1, "VM leave urgent messages disabled globally\n");
15241 val = "no";
15242 }
15244
15245 /* Temporary greeting reminder */
15246 if (!(val = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
15247 ast_debug(1, "VM Temporary Greeting Reminder Option disabled globally\n");
15248 val = "no";
15249 } else {
15250 ast_debug(1, "VM Temporary Greeting Reminder Option enabled globally\n");
15251 }
15253 if (!(val = ast_variable_retrieve(cfg, "general", "messagewrap"))){
15254 ast_debug(1, "VM next message wrap disabled globally\n");
15255 val = "no";
15256 }
15258
15259 if (!(val = ast_variable_retrieve(cfg, "general", "operator"))){
15260 ast_debug(1, "VM Operator break disabled globally\n");
15261 val = "no";
15262 }
15264
15265 if (!(val = ast_variable_retrieve(cfg, "general", "saycid"))) {
15266 ast_debug(1, "VM CID Info before msg disabled globally\n");
15267 val = "no";
15268 }
15270
15271 if (!(val = ast_variable_retrieve(cfg, "general", "sendvoicemail"))){
15272 ast_debug(1, "Send Voicemail msg disabled globally\n");
15273 val = "no";
15274 }
15276
15277 if (!(val = ast_variable_retrieve(cfg, "general", "envelope"))) {
15278 ast_debug(1, "ENVELOPE before msg enabled globally\n");
15279 val = "yes";
15280 }
15282
15283 if (!(val = ast_variable_retrieve(cfg, "general", "moveheard"))) {
15284 ast_debug(1, "Move Heard enabled globally\n");
15285 val = "yes";
15286 }
15288
15289 if (!(val = ast_variable_retrieve(cfg, "general", "forward_urgent_auto"))) {
15290 ast_debug(1, "Autoset of Urgent flag on forwarded Urgent messages disabled globally\n");
15291 val = "no";
15292 }
15294
15295 if (!(val = ast_variable_retrieve(cfg, "general", "sayduration"))) {
15296 ast_debug(1, "Duration info before msg enabled globally\n");
15297 val = "yes";
15298 }
15300
15301 saydurationminfo = 2;
15302 if ((val = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
15303 if (sscanf(val, "%30d", &x) == 1) {
15304 saydurationminfo = x;
15305 } else {
15306 ast_log(AST_LOG_WARNING, "Invalid min duration for say duration\n");
15307 }
15308 }
15309
15310 if (!(val = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
15311 ast_debug(1, "We are not going to skip to the next msg after save/delete\n");
15312 val = "no";
15313 }
15315
15316 if ((val = ast_variable_retrieve(cfg, "general", "dialout"))) {
15318 ast_debug(1, "found dialout context: %s\n", dialcontext);
15319 } else {
15320 dialcontext[0] = '\0';
15321 }
15322
15323 if ((val = ast_variable_retrieve(cfg, "general", "callback"))) {
15325 ast_debug(1, "found callback context: %s\n", callcontext);
15326 } else {
15327 callcontext[0] = '\0';
15328 }
15329
15330 if ((val = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
15332 ast_debug(1, "found operator context: %s\n", exitcontext);
15333 } else {
15334 exitcontext[0] = '\0';
15335 }
15336
15337 /* load password sounds configuration */
15338 if ((val = ast_variable_retrieve(cfg, "general", "vm-login")))
15340 if ((val = ast_variable_retrieve(cfg, "general", "vm-newuser")))
15342 if ((val = ast_variable_retrieve(cfg, "general", "vm-password")))
15344 if ((val = ast_variable_retrieve(cfg, "general", "vm-newpassword")))
15346 if ((val = ast_variable_retrieve(cfg, "general", "vm-invalid-password")))
15348 if ((val = ast_variable_retrieve(cfg, "general", "vm-passchanged")))
15350 if ((val = ast_variable_retrieve(cfg, "general", "vm-reenterpassword")))
15352 if ((val = ast_variable_retrieve(cfg, "general", "vm-mismatch")))
15354 if ((val = ast_variable_retrieve(cfg, "general", "vm-pls-try-again"))) {
15356 }
15357 if ((val = ast_variable_retrieve(cfg, "general", "vm-prepend-timeout"))) {
15359 }
15360 /* load configurable audio prompts */
15361 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-forward-key")) && is_valid_dtmf(val))
15363 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-reverse-key")) && is_valid_dtmf(val))
15365 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-pause-key")) && is_valid_dtmf(val))
15367 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-restart-key")) && is_valid_dtmf(val))
15369 if ((val = ast_variable_retrieve(cfg, "general", "listen-control-stop-key")) && is_valid_dtmf(val))
15371
15372 if (!(val = ast_variable_retrieve(cfg, "general", "usedirectory")))
15373 val = "no";
15375
15376 if (!(val = ast_variable_retrieve(cfg, "general", "passwordlocation"))) {
15377 val = "voicemail.conf";
15378 }
15379 if (!(strcmp(val, "spooldir"))) {
15381 } else {
15383 }
15384
15386 if ((val = ast_variable_retrieve(cfg, "general", "pollfreq"))) {
15387 if (sscanf(val, "%30u", &poll_freq) != 1) {
15389 ast_log(AST_LOG_ERROR, "'%s' is not a valid value for the pollfreq option!\n", val);
15390 }
15391 }
15392
15393 poll_mailboxes = 0;
15394 if ((val = ast_variable_retrieve(cfg, "general", "pollmailboxes")))
15396
15397 memset(fromstring, 0, sizeof(fromstring));
15398 memset(pagerfromstring, 0, sizeof(pagerfromstring));
15399 strcpy(charset, "ISO-8859-1");
15400 if (emailbody) {
15402 emailbody = NULL;
15403 }
15404 if (emailsubject) {
15407 }
15408 if (pagerbody) {
15410 pagerbody = NULL;
15411 }
15412 if (pagersubject) {
15415 }
15416 if ((val = ast_variable_retrieve(cfg, "general", "pbxskip")))
15418 if ((val = ast_variable_retrieve(cfg, "general", "fromstring")))
15420 if ((val = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
15422 if ((val = ast_variable_retrieve(cfg, "general", "charset")))
15424 if ((val = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
15425 sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
15426 for (x = 0; x < 4; x++) {
15427 memcpy(&adsifdn[x], &tmpadsi[x], 1);
15428 }
15429 }
15430 if ((val = ast_variable_retrieve(cfg, "general", "adsisec"))) {
15431 sscanf(val, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
15432 for (x = 0; x < 4; x++) {
15433 memcpy(&adsisec[x], &tmpadsi[x], 1);
15434 }
15435 }
15436 if ((val = ast_variable_retrieve(cfg, "general", "adsiver"))) {
15437 if (atoi(val)) {
15438 adsiver = atoi(val);
15439 }
15440 }
15441 if ((val = ast_variable_retrieve(cfg, "general", "tz"))) {
15443 }
15444 if ((val = ast_variable_retrieve(cfg, "general", "locale"))) {
15445 ast_copy_string(locale, val, sizeof(locale));
15446 }
15447 if ((val = ast_variable_retrieve(cfg, "general", "emailsubject"))) {
15449 }
15450 if ((val = ast_variable_retrieve(cfg, "general", "emailbody"))) {
15452 }
15453 if ((val = ast_variable_retrieve(cfg, "general", "pagersubject"))) {
15455 }
15456 if ((val = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
15458 }
15459
15460 tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
15461 if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_high"))) {
15462 if (sscanf(val, "%30ld", &tps_queue_high) != 1 || tps_queue_high <= 0) {
15463 ast_log(AST_LOG_WARNING, "Invalid the taskprocessor high water alert trigger level '%s'\n", val);
15464 tps_queue_high = AST_TASKPROCESSOR_HIGH_WATER_LEVEL;
15465 }
15466 }
15467 tps_queue_low = -1;
15468 if ((val = ast_variable_retrieve(cfg, "general", "tps_queue_low"))) {
15469 if (sscanf(val, "%30ld", &tps_queue_low) != 1 ||
15470 tps_queue_low < -1 || tps_queue_high < tps_queue_low) {
15471 ast_log(AST_LOG_WARNING, "Invalid the taskprocessor low water clear alert level '%s'\n", val);
15472 tps_queue_low = -1;
15473 }
15474 }
15475 if (ast_taskprocessor_alert_set_levels(mwi_subscription_tps, tps_queue_low, tps_queue_high)) {
15476 ast_log(AST_LOG_WARNING, "Failed to set alert levels for voicemail taskprocessor.\n");
15477 }
15478
15479 /* load mailboxes from users.conf */
15480 if (ucfg) {
15481 for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
15482 if (!strcasecmp(cat, "general")) {
15483 continue;
15484 }
15485 if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
15486 continue;
15487 if ((current = find_or_create(userscontext, cat))) {
15490 ast_copy_string(current->context, userscontext, sizeof(current->context));
15491 if (!ast_strlen_zero(current->password) && current->passwordlocation == OPT_PWLOC_VOICEMAILCONF) {
15492 current->passwordlocation = OPT_PWLOC_USERSCONF;
15493 }
15494
15495 switch (current->passwordlocation) {
15496 case OPT_PWLOC_SPOOLDIR:
15497 snprintf(secretfn, sizeof(secretfn), "%s%s/%s/secret.conf", VM_SPOOL_DIR, current->context, current->mailbox);
15498 read_password_from_file(secretfn, current->password, sizeof(current->password));
15499 }
15500 }
15501 }
15502 }
15503
15504 /* load mailboxes from voicemail.conf */
15505
15506 /*
15507 * Aliases must be loaded before users or the aliases won't be notified
15508 * if there's existing voicemail in the user mailbox.
15509 */
15510 load_aliases(cfg);
15511
15512 load_zonemessages(cfg);
15513
15514 load_users(cfg);
15515
15517
15522
15523 return 0;
15524 } else {
15526 ast_log(AST_LOG_WARNING, "Failed to load configuration file.\n");
15527 return 0;
15528 }
15529}
15530
15531static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
15532{
15533 int res = -1;
15534 char dir[PATH_MAX];
15535 snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, context, mailbox);
15536 ast_debug(2, "About to try retrieving name file %s\n", dir);
15537 RETRIEVE(dir, -1, mailbox, context);
15538 if (ast_fileexists(dir, NULL, NULL)) {
15539 res = ast_stream_and_wait(chan, dir, AST_DIGIT_ANY);
15540 }
15541 DISPOSE(dir, -1);
15542 return res;
15543}
15544
15545/*!
15546 * \internal
15547 * \brief Play a recorded user name for the mailbox to the specified channel.
15548 *
15549 * \param chan Where to play the recorded name file.
15550 * \param mailbox_id The mailbox name.
15551 *
15552 * \retval 0 Name played without interruption
15553 * \retval dtmf ASCII value of the DTMF which interrupted playback.
15554 * \retval -1 Unable to locate mailbox or hangup occurred.
15555 */
15556static int vm_sayname(struct ast_channel *chan, const char *mailbox_id)
15557{
15558 char *context;
15559 char *mailbox;
15560
15561 if (ast_strlen_zero(mailbox_id)
15562 || separate_mailbox(ast_strdupa(mailbox_id), &mailbox, &context)) {
15563 return -1;
15564 }
15565 return sayname(chan, mailbox, context);
15566}
15567
15568static void read_password_from_file(const char *secretfn, char *password, int passwordlen) {
15569 struct ast_config *pwconf;
15570 struct ast_flags config_flags = { 0 };
15571
15572 pwconf = ast_config_load(secretfn, config_flags);
15573 if (valid_config(pwconf)) {
15574 const char *val = ast_variable_retrieve(pwconf, "general", "password");
15575 if (val) {
15576 ast_copy_string(password, val, passwordlen);
15577 ast_config_destroy(pwconf);
15578 return;
15579 }
15580 ast_config_destroy(pwconf);
15581 }
15582 ast_log(LOG_NOTICE, "Failed reading voicemail password from %s, using secret from config file\n", secretfn);
15583}
15584
15585static int write_password_to_file(const char *secretfn, const char *password) {
15586 struct ast_config *conf;
15587 struct ast_category *cat;
15588 struct ast_variable *var;
15589 int res = -1;
15590
15591 if (!(conf = ast_config_new())) {
15592 ast_log(LOG_ERROR, "Error creating new config structure\n");
15593 return res;
15594 }
15595 if (!(cat = ast_category_new("general", "", 1))) {
15596 ast_log(LOG_ERROR, "Error creating new category structure\n");
15598 return res;
15599 }
15600 if (!(var = ast_variable_new("password", password, ""))) {
15601 ast_log(LOG_ERROR, "Error creating new variable structure\n");
15604 return res;
15605 }
15608 if (!ast_config_text_file_save(secretfn, conf, "app_voicemail")) {
15609 res = 0;
15610 } else {
15611 ast_log(LOG_ERROR, "Error writing voicemail password to %s\n", secretfn);
15612 }
15613
15615 return res;
15616}
15617
15618static int vmsayname_exec(struct ast_channel *chan, const char *data)
15619{
15620 char *context;
15621 char *mailbox;
15622 int res;
15623
15624 if (ast_strlen_zero(data)
15626 ast_log(LOG_WARNING, "VMSayName requires argument mailbox@context\n");
15627 return -1;
15628 }
15629
15630 if ((res = sayname(chan, mailbox, context)) < 0) {
15631 ast_debug(3, "Greeting not found for '%s@%s', falling back to mailbox number.\n", mailbox, context);
15632 res = ast_stream_and_wait(chan, "vm-extension", AST_DIGIT_ANY);
15633 if (!res) {
15635 }
15636 }
15637
15638 return res;
15639}
15640
15641#ifdef TEST_FRAMEWORK
15642static int fake_write(struct ast_channel *ast, struct ast_frame *frame)
15643{
15644 return 0;
15645}
15646
15647static struct ast_frame *fake_read(struct ast_channel *ast)
15648{
15649 return &ast_null_frame;
15650}
15651
15652AST_TEST_DEFINE(test_voicemail_vmsayname)
15653{
15654 char dir[PATH_MAX];
15655 char dir2[PATH_MAX];
15656 static const char TEST_CONTEXT[] = "very_long_unique_context_so_that_nobody_will_ever_have_the_same_one_configured_3141592653";
15657 static const char TEST_EXTENSION[] = "1234";
15658
15659 struct ast_channel *test_channel1 = NULL;
15660 int res = -1;
15661 struct ast_format_cap *capabilities;
15662
15663 static const struct ast_channel_tech fake_tech = {
15664 .write = fake_write,
15665 .read = fake_read,
15666 };
15667
15668 switch (cmd) {
15669 case TEST_INIT:
15670 info->name = "vmsayname_exec";
15671 info->category = "/apps/app_voicemail/";
15672 info->summary = "Vmsayname unit test";
15673 info->description =
15674 "This tests passing various parameters to vmsayname";
15675 return AST_TEST_NOT_RUN;
15676 case TEST_EXECUTE:
15677 break;
15678 }
15679
15680 if (!(test_channel1 = ast_channel_alloc(0, AST_STATE_DOWN, NULL, NULL, NULL, NULL,
15681 NULL, NULL, 0, 0, "TestChannel1"))) {
15682 goto exit_vmsayname_test;
15683 }
15684
15685 /* normally this is done in the channel driver */
15687 if (!capabilities) {
15688 goto exit_vmsayname_test;
15689 }
15692 ao2_ref(capabilities, -1);
15697 ast_channel_tech_set(test_channel1, &fake_tech);
15698
15699 ast_channel_unlock(test_channel1);
15700
15701 ast_test_status_update(test, "Test playing of extension when greeting is not available...\n");
15702 snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
15703 if (!(res = vmsayname_exec(test_channel1, dir))) {
15704 snprintf(dir, sizeof(dir), "%s%s/%s/greet", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15705 if (ast_fileexists(dir, NULL, NULL)) {
15706 ast_test_status_update(test, "This should not happen, most likely means clean up from previous test failed\n");
15707 res = -1;
15708 goto exit_vmsayname_test;
15709 } else {
15710 /* no greeting already exists as expected, let's create one to fully test sayname */
15711 if ((res = create_dirpath(dir, sizeof(dir), TEST_CONTEXT, TEST_EXTENSION, ""))) {
15712 ast_log(AST_LOG_WARNING, "Failed to make test directory\n");
15713 goto exit_vmsayname_test;
15714 }
15715 snprintf(dir, sizeof(dir), "%s/sounds/beep.gsm", ast_config_AST_DATA_DIR);
15716 snprintf(dir2, sizeof(dir2), "%s%s/%s/greet.gsm", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15717 /* we're not going to hear the sound anyway, just use a valid gsm audio file */
15718 if ((res = symlink(dir, dir2))) {
15719 ast_log(LOG_WARNING, "Symlink reported %s\n", strerror(errno));
15720 goto exit_vmsayname_test;
15721 }
15722 ast_test_status_update(test, "Test playing created mailbox greeting...\n");
15723 snprintf(dir, sizeof(dir), "%s@%s", TEST_EXTENSION, TEST_CONTEXT); /* not a dir, don't get confused */
15724 res = vmsayname_exec(test_channel1, dir);
15725
15726 /* TODO: there may be a better way to do this */
15727 unlink(dir2);
15728 snprintf(dir2, sizeof(dir2), "%s%s/%s", VM_SPOOL_DIR, TEST_CONTEXT, TEST_EXTENSION);
15729 rmdir(dir2);
15730 snprintf(dir2, sizeof(dir2), "%s%s", VM_SPOOL_DIR, TEST_CONTEXT);
15731 rmdir(dir2);
15732 }
15733 }
15734
15735exit_vmsayname_test:
15736
15737 ast_hangup(test_channel1);
15738
15739 return res ? AST_TEST_FAIL : AST_TEST_PASS;
15740}
15741
15742struct test_files {
15743 char dir[256];
15744 char file[256];
15745 char txtfile[256];
15746};
15747
15748AST_TEST_DEFINE(test_voicemail_msgcount)
15749{
15750 int i, j, res = AST_TEST_PASS, syserr;
15751 struct ast_vm_user *vmu;
15752 struct ast_vm_user svm;
15753 struct vm_state vms;
15754#ifdef IMAP_STORAGE
15755 struct ast_channel *chan = NULL;
15756#endif
15757 /* Using ast_alloca instead of just declaring tmp as an array is a workaround for a GCC 10 issue with -Wrestrict */
15758 struct test_files *tmp = ast_alloca(sizeof(struct test_files) * 3);
15759 char syscmd[256];
15760 const char origweasels[] = "tt-weasels";
15761 const char testcontext[] = "test";
15762 const char testmailbox[] = "00000000";
15763 const char testspec[] = "00000000@test";
15764 FILE *txt;
15765 int new, old, urgent;
15766 const char *folders[3] = { "Old", "Urgent", "INBOX" };
15767 const int folder2mbox[3] = { 1, 11, 0 };
15768 const int expected_results[3][12] = {
15769 /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
15770 { 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0 },
15771 { 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
15772 { 1, 1, 1, 1, 0, 2, 1, 1, 1, 1, 1, 2 },
15773 };
15774
15775 switch (cmd) {
15776 case TEST_INIT:
15777 info->name = "test_voicemail_msgcount";
15778 info->category = "/apps/app_voicemail/";
15779 info->summary = "Test Voicemail status checks";
15780 info->description =
15781 "Verify that message counts are correct when retrieved through the public API";
15782 return AST_TEST_NOT_RUN;
15783 case TEST_EXECUTE:
15784 break;
15785 }
15786
15787 /* Make sure the original path was completely empty */
15788 snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
15789 if ((syserr = ast_safe_system(syscmd))) {
15790 ast_test_status_update(test, "Unable to clear test directory: %s\n",
15791 syserr > 0 ? strerror(syserr) : "unable to fork()");
15792 return AST_TEST_FAIL;
15793 }
15794
15795#ifdef IMAP_STORAGE
15796 if (!(chan = ast_dummy_channel_alloc())) {
15797 ast_test_status_update(test, "Unable to create dummy channel\n");
15798 return AST_TEST_FAIL;
15799 }
15800#endif
15801
15802 memset(&svm, 0, sizeof(svm));
15803 if (!(vmu = find_user(&svm, testcontext, testmailbox)) &&
15804 !(vmu = find_or_create(testcontext, testmailbox))) {
15805 ast_test_status_update(test, "Cannot create vmu structure\n");
15806#ifdef IMAP_STORAGE
15807 chan = ast_channel_unref(chan);
15808#endif
15809 return AST_TEST_FAIL;
15810 }
15811
15812 populate_defaults(vmu);
15813 memset(&vms, 0, sizeof(vms));
15814
15815 /* Create temporary voicemail */
15816 for (i = 0; i < 3; i++) {
15817 create_dirpath(tmp[i].dir, sizeof(tmp[i].dir), testcontext, testmailbox, folders[i]);
15818 make_file(tmp[i].file, sizeof(tmp[i].file), tmp[i].dir, 0);
15819 snprintf(tmp[i].txtfile, sizeof(tmp[i].txtfile), "%s.txt", tmp[i].file);
15820
15821 if (ast_fileexists(origweasels, "gsm", "en") > 0) {
15822 snprintf(syscmd, sizeof(syscmd), "cp \"%s/sounds/en/%s.gsm\" \"%s/%s/%s/%s/msg0000.gsm\"", ast_config_AST_DATA_DIR, origweasels,
15823 VM_SPOOL_DIR, testcontext, testmailbox, folders[i]);
15824 if ((syserr = ast_safe_system(syscmd))) {
15825 ast_test_status_update(test, "Unable to create test voicemail: %s\n",
15826 syserr > 0 ? strerror(syserr) : "unable to fork()");
15827#ifdef IMAP_STORAGE
15828 chan = ast_channel_unref(chan);
15829#endif
15830 free_user(vmu);
15831 return AST_TEST_FAIL;
15832 }
15833 }
15834
15835 if ((txt = fopen(tmp[i].txtfile, "w+"))) {
15836 fprintf(txt, "; just a stub\n[message]\nflag=%s\n", strcmp(folders[i], "Urgent") ? "" : "Urgent");
15837 fclose(txt);
15838 } else {
15839 ast_test_status_update(test, "Unable to write message file '%s'\n", tmp[i].txtfile);
15840 res = AST_TEST_FAIL;
15841 break;
15842 }
15843 open_mailbox(&vms, vmu, folder2mbox[i]);
15844 STORE(tmp[i].dir, testmailbox, testcontext, 0, chan, vmu, "gsm", 600, &vms, strcmp(folders[i], "Urgent") ? "" : "Urgent", NULL);
15845
15846 /* hasvm-old, hasvm-urgent, hasvm-new, ic-old, ic-urgent, ic-new, ic2-old, ic2-urgent, ic2-new, mc-old, mc-urgent, mc-new */
15847 for (j = 0; j < 3; j++) {
15848 /* folder[2] is INBOX, __has_voicemail will default back to INBOX */
15849 if (ast_app_has_voicemail(testspec, (j==2 ? NULL : folders[j])) != expected_results[i][0 + j]) {
15850 ast_test_status_update(test, "has_voicemail(%s, %s) returned %d and we expected %d\n",
15851 testspec, folders[j], ast_app_has_voicemail(testspec, folders[j]), expected_results[i][0 + j]);
15852 res = AST_TEST_FAIL;
15853 }
15854 }
15855
15856 new = old = urgent = 0;
15857 if (ast_app_inboxcount(testspec, &new, &old)) {
15858 ast_test_status_update(test, "inboxcount returned failure\n");
15859 res = AST_TEST_FAIL;
15860 } else if (old != expected_results[i][3 + 0] || new != expected_results[i][3 + 2]) {
15861 ast_test_status_update(test, "inboxcount(%s) returned old=%d (expected %d) and new=%d (expected %d)\n",
15862 testspec, old, expected_results[i][3 + 0], new, expected_results[i][3 + 2]);
15863 res = AST_TEST_FAIL;
15864 }
15865
15866 new = old = urgent = 0;
15867 if (ast_app_inboxcount2(testspec, &urgent, &new, &old)) {
15868 ast_test_status_update(test, "inboxcount2 returned failure\n");
15869 res = AST_TEST_FAIL;
15870 } else if (old != expected_results[i][6 + 0] ||
15871 urgent != expected_results[i][6 + 1] ||
15872 new != expected_results[i][6 + 2] ) {
15873 ast_test_status_update(test, "inboxcount2(%s) returned old=%d (expected %d), urgent=%d (expected %d), and new=%d (expected %d)\n",
15874 testspec, old, expected_results[i][6 + 0], urgent, expected_results[i][6 + 1], new, expected_results[i][6 + 2]);
15875 res = AST_TEST_FAIL;
15876 }
15877
15878 new = old = urgent = 0;
15879 for (j = 0; j < 3; j++) {
15880 if (ast_app_messagecount(testspec, folders[j]) != expected_results[i][9 + j]) {
15881 ast_test_status_update(test, "messagecount(%s, %s) returned %d and we expected %d\n",
15882 testspec, folders[j], ast_app_messagecount(testspec, folders[j]), expected_results[i][9 + j]);
15883 res = AST_TEST_FAIL;
15884 }
15885 }
15886 }
15887
15888 for (i = 0; i < 3; i++) {
15889 /* This is necessary if the voicemails are stored on an ODBC/IMAP
15890 * server, in which case, the rm below will not affect the
15891 * voicemails. */
15892 DELETE(tmp[i].dir, 0, tmp[i].file, vmu);
15893 DISPOSE(tmp[i].dir, 0);
15894 }
15895
15896 if (vms.deleted) {
15897 ast_free(vms.deleted);
15898 }
15899 if (vms.heard) {
15900 ast_free(vms.heard);
15901 }
15902
15903#ifdef IMAP_STORAGE
15904 chan = ast_channel_unref(chan);
15905#endif
15906
15907 /* And remove test directory */
15908 snprintf(syscmd, sizeof(syscmd), "rm -rf \"%s%s/%s\"", VM_SPOOL_DIR, testcontext, testmailbox);
15909 if ((syserr = ast_safe_system(syscmd))) {
15910 ast_test_status_update(test, "Unable to clear test directory: %s\n",
15911 syserr > 0 ? strerror(syserr) : "unable to fork()");
15912 }
15913
15914 free_user(vmu);
15915 force_reload_config(); /* Restore original config */
15916 return res;
15917}
15918
15919AST_TEST_DEFINE(test_voicemail_notify_endl)
15920{
15921 int res = AST_TEST_PASS;
15922 char testcontext[] = "test";
15923 char testmailbox[] = "00000000";
15924 char from[] = "test@example.net", cidnum[] = "1234", cidname[] = "Mark Spencer", format[] = "gsm";
15925 char attach[256], attach2[256];
15926 char buf[256] = ""; /* No line should actually be longer than 80 */
15927 struct ast_channel *chan = NULL;
15928 struct ast_vm_user *vmu, vmus = {
15929 .flags = 0,
15930 };
15931 FILE *file;
15932 struct {
15933 char *name;
15934 enum { INT, FLAGVAL, STATIC, STRPTR } type;
15935 void *location;
15936 union {
15937 int intval;
15938 char *strval;
15939 } u;
15940 } test_items[] = {
15941 { "plain jane config", STATIC, vmus.password, .u.strval = "1234" }, /* No, this doesn't change this test any. */
15942 { "emailsubject", STRPTR, vmus.emailsubject, .u.strval = "Oogly boogly\xf8koogly with what appears to be UTF-8" },
15943 { "emailbody", STRPTR, vmus.emailbody, .u.strval = "This is a test\n\twith multiple\nlines\nwithin\n" },
15944 { "serveremail", STATIC, vmus.serveremail, .u.strval = "\"\xf8Something\xe8that\xd8seems to have UTF-8 chars\" <test@example.net>" },
15945 { "attachment flag", FLAGVAL, &vmus.flags, .u.intval = VM_ATTACH },
15946 { "attach2", STRPTR, attach2, .u.strval = "" },
15947 { "attach", STRPTR, attach, .u.strval = "" },
15948 };
15949 int which;
15950
15951 switch (cmd) {
15952 case TEST_INIT:
15953 info->name = "test_voicemail_notify_endl";
15954 info->category = "/apps/app_voicemail/";
15955 info->summary = "Test Voicemail notification end-of-line";
15956 info->description =
15957 "Verify that notification emails use a consistent end-of-line character";
15958 return AST_TEST_NOT_RUN;
15959 case TEST_EXECUTE:
15960 break;
15961 }
15962
15963 snprintf(attach, sizeof(attach), "%s/sounds/en/tt-weasels", ast_config_AST_DATA_DIR);
15964 snprintf(attach2, sizeof(attach2), "%s/sounds/en/tt-somethingwrong", ast_config_AST_DATA_DIR);
15965
15966 if (!(vmu = find_user(&vmus, testcontext, testmailbox)) &&
15967 !(vmu = find_or_create(testcontext, testmailbox))) {
15968 ast_test_status_update(test, "Cannot create vmu structure\n");
15969 return AST_TEST_NOT_RUN;
15970 }
15971
15972 if (vmu != &vmus && !(vmu = find_user(&vmus, testcontext, testmailbox))) {
15973 ast_test_status_update(test, "Cannot find vmu structure?!!\n");
15974 return AST_TEST_NOT_RUN;
15975 }
15976
15977 populate_defaults(vmu);
15978 vmu->email = ast_strdup("test2@example.net");
15979#ifdef IMAP_STORAGE
15980 /* TODO When we set up the IMAP server test, we'll need to have credentials for the VMU structure added here */
15981#endif
15982
15983 file = tmpfile();
15984 for (which = 0; which < ARRAY_LEN(test_items); which++) {
15985 /* Kill previous test, if any */
15986 rewind(file);
15987 if (ftruncate(fileno(file), 0)) {
15988 ast_test_status_update(test, "Cannot truncate test output file: %s\n", strerror(errno));
15989 res = AST_TEST_FAIL;
15990 break;
15991 }
15992
15993 /* Make each change, in order, to the test mailbox */
15994 if (test_items[which].type == INT) {
15995 *((int *) test_items[which].location) = test_items[which].u.intval;
15996 } else if (test_items[which].type == FLAGVAL) {
15997 if (ast_test_flag(vmu, test_items[which].u.intval)) {
15998 ast_clear_flag(vmu, test_items[which].u.intval);
15999 } else {
16000 ast_set_flag(vmu, test_items[which].u.intval);
16001 }
16002 } else if (test_items[which].type == STATIC) {
16003 strcpy(test_items[which].location, test_items[which].u.strval);
16004 } else if (test_items[which].type == STRPTR) {
16005 test_items[which].location = test_items[which].u.strval;
16006 }
16007
16008 make_email_file(file, from, vmu, 0, testcontext, testmailbox, "INBOX", cidnum, cidname, attach, attach2, format, 999, 1, chan, NULL, 0, NULL, NULL);
16009 rewind(file);
16010 while (fgets(buf, sizeof(buf), file)) {
16011 if (
16012 (strlen(buf) > 1 &&
16013#ifdef IMAP_STORAGE
16014 buf[strlen(buf) - 2] != '\r'
16015#else
16016 buf[strlen(buf) - 2] == '\r'
16017#endif
16018 )
16019 || buf[strlen(buf) - 1] != '\n') {
16020 res = AST_TEST_FAIL;
16021 }
16022 }
16023 }
16024 fclose(file);
16025 free_user(vmu);
16026 force_reload_config(); /* Restore original config */
16027 return res;
16028}
16029
16030AST_TEST_DEFINE(test_voicemail_load_config)
16031{
16032 int res = AST_TEST_PASS;
16033 struct ast_vm_user *vmu;
16034 struct ast_config *cfg;
16035 char config_filename[32] = "/tmp/voicemail.conf.XXXXXX";
16036 int fd;
16037 FILE *file;
16038 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
16039
16040 switch (cmd) {
16041 case TEST_INIT:
16042 info->name = "test_voicemail_load_config";
16043 info->category = "/apps/app_voicemail/";
16044 info->summary = "Test loading Voicemail config";
16045 info->description =
16046 "Verify that configuration is loaded consistently. "
16047 "This is to test regressions of ASTERISK-18838 where it was noticed that "
16048 "some options were loaded after the mailboxes were instantiated, causing "
16049 "those options not to be set correctly.";
16050 return AST_TEST_NOT_RUN;
16051 case TEST_EXECUTE:
16052 break;
16053 }
16054
16055 /* build a config file by hand... */
16056 if ((fd = mkstemp(config_filename)) < 0) {
16057 return AST_TEST_FAIL;
16058 }
16059 if (!(file = fdopen(fd, "w"))) {
16060 close(fd);
16061 unlink(config_filename);
16062 return AST_TEST_FAIL;
16063 }
16064 fputs("[general]\ncallback=somecontext\nlocale=de_DE.UTF-8\ntz=european\n[test]", file);
16065 fputs("00000001 => 9999,Mr. Test,,,callback=othercontext|locale=nl_NL.UTF-8|tz=central\n", file);
16066 fputs("00000002 => 9999,Mrs. Test\n", file);
16067 fclose(file);
16068
16069 if (!(cfg = ast_config_load(config_filename, config_flags)) || !valid_config(cfg)) {
16070 res = AST_TEST_FAIL;
16071 goto cleanup;
16072 }
16073
16074 load_config_from_memory(1, cfg, NULL);
16075 ast_config_destroy(cfg);
16076
16077#define CHECK(u, attr, value) else if (strcmp(u->attr, value)) { \
16078 ast_test_status_update(test, "mailbox %s should have %s '%s', but has '%s'\n", \
16079 u->mailbox, #attr, value, u->attr); res = AST_TEST_FAIL; break; }
16080
16082 AST_LIST_TRAVERSE(&users, vmu, list) {
16083 if (!strcmp(vmu->mailbox, "00000001")) {
16084 if (0); /* trick to get CHECK to work */
16085 CHECK(vmu, callback, "othercontext")
16086 CHECK(vmu, locale, "nl_NL.UTF-8")
16087 CHECK(vmu, zonetag, "central")
16088 } else if (!strcmp(vmu->mailbox, "00000002")) {
16089 if (0); /* trick to get CHECK to work */
16090 CHECK(vmu, callback, "somecontext")
16091 CHECK(vmu, locale, "de_DE.UTF-8")
16092 CHECK(vmu, zonetag, "european")
16093 }
16094 }
16096
16097#undef CHECK
16098
16099 /* Forcibly restore the original config, to reinitialize after test */
16100 force_reload_config(); /* this might say "Failed to load configuration file." */
16101
16102cleanup:
16103 unlink(config_filename);
16104 return res;
16105}
16106
16107AST_TEST_DEFINE(test_voicemail_vm_info)
16108{
16109 struct ast_vm_user *vmu;
16110 struct ast_channel *chan = NULL;
16111 const char testcontext[] = "test";
16112 const char testmailbox[] = "00000000";
16113 const char vminfo_cmd[] = "VM_INFO";
16114 char vminfo_buf[256], vminfo_args[256];
16115 int res = AST_TEST_PASS;
16116 int test_ret = 0;
16117 int test_counter = 0;
16118
16119 struct {
16120 char *vminfo_test_args;
16121 char *vminfo_expected;
16122 int vminfo_ret;
16123 } test_items[] = {
16124 { "", "", -1 }, /* Missing argument */
16125 { "00000000@test,badparam", "", -1 }, /* Wrong argument */
16126 { "00000000@test", "", -1 }, /* Missing argument */
16127 { "00000000@test,exists", "1", 0 },
16128 { "11111111@test,exists", "0", 0 }, /* Invalid mailbox */
16129 { "00000000@test,email", "vm-info-test@example.net", 0 },
16130 { "11111111@test,email", "", 0 }, /* Invalid mailbox */
16131 { "00000000@test,fullname", "Test Framework Mailbox", 0 },
16132 { "00000000@test,pager", "vm-info-pager-test@example.net", 0 },
16133 { "00000000@test,locale", "en_US", 0 },
16134 { "00000000@test,tz", "central", 0 },
16135 { "00000000@test,language", "en", 0 },
16136 { "00000000@test,password", "9876", 0 },
16137 };
16138
16139 switch (cmd) {
16140 case TEST_INIT:
16141 info->name = "test_voicemail_vm_info";
16142 info->category = "/apps/app_voicemail/";
16143 info->summary = "VM_INFO unit test";
16144 info->description =
16145 "This tests passing various parameters to VM_INFO";
16146 return AST_TEST_NOT_RUN;
16147 case TEST_EXECUTE:
16148 break;
16149 }
16150
16151 if (!(chan = ast_dummy_channel_alloc())) {
16152 ast_test_status_update(test, "Unable to create dummy channel\n");
16153 return AST_TEST_FAIL;
16154 }
16155
16156 if (!(vmu = find_user(NULL, testcontext, testmailbox)) &&
16157 !(vmu = find_or_create(testcontext, testmailbox))) {
16158 ast_test_status_update(test, "Cannot create vmu structure\n");
16159 chan = ast_channel_unref(chan);
16160 return AST_TEST_FAIL;
16161 }
16162
16163 populate_defaults(vmu);
16164
16165 vmu->email = ast_strdup("vm-info-test@example.net");
16166 if (!vmu->email) {
16167 ast_test_status_update(test, "Cannot create vmu email\n");
16168 chan = ast_channel_unref(chan);
16169 return AST_TEST_FAIL;
16170 }
16171 ast_copy_string(vmu->fullname, "Test Framework Mailbox", sizeof(vmu->fullname));
16172 ast_copy_string(vmu->pager, "vm-info-pager-test@example.net", sizeof(vmu->pager));
16173 ast_copy_string(vmu->language, "en", sizeof(vmu->language));
16174 ast_copy_string(vmu->zonetag, "central", sizeof(vmu->zonetag));
16175 ast_copy_string(vmu->locale, "en_US", sizeof(vmu->zonetag));
16176 ast_copy_string(vmu->password, "9876", sizeof(vmu->password));
16177
16178 for (test_counter = 0; test_counter < ARRAY_LEN(test_items); test_counter++) {
16179 ast_copy_string(vminfo_args, test_items[test_counter].vminfo_test_args, sizeof(vminfo_args));
16180 test_ret = acf_vm_info(chan, vminfo_cmd, vminfo_args, vminfo_buf, sizeof(vminfo_buf));
16181 if (strcmp(vminfo_buf, test_items[test_counter].vminfo_expected)) {
16182 ast_test_status_update(test, "VM_INFO respose was: '%s', but expected: '%s'\n", vminfo_buf, test_items[test_counter].vminfo_expected);
16183 res = AST_TEST_FAIL;
16184 }
16185 if (!(test_ret == test_items[test_counter].vminfo_ret)) {
16186 ast_test_status_update(test, "VM_INFO return code was: '%i', but expected '%i'\n", test_ret, test_items[test_counter].vminfo_ret);
16187 res = AST_TEST_FAIL;
16188 }
16189 }
16190
16191 chan = ast_channel_unref(chan);
16192 free_user(vmu);
16193 return res;
16194}
16195#endif /* defined(TEST_FRAMEWORK) */
16196
16197static const struct ast_vm_functions vm_table = {
16199 .module_name = AST_MODULE,
16200
16201 .has_voicemail = has_voicemail,
16202 .inboxcount = inboxcount,
16203 .inboxcount2 = inboxcount2,
16204 .messagecount = messagecount,
16205 .copy_recording_to_vm = msg_create_from_file,
16206 .index_to_foldername = vm_index_to_foldername,
16207 .mailbox_snapshot_create = vm_mailbox_snapshot_create,
16208 .mailbox_snapshot_destroy = vm_mailbox_snapshot_destroy,
16209 .msg_move = vm_msg_move,
16210 .msg_remove = vm_msg_remove,
16211 .msg_forward = vm_msg_forward,
16212 .msg_play = vm_msg_play,
16213};
16214
16217 .module_name = AST_MODULE,
16218
16219 .sayname = vm_sayname,
16220};
16221
16222static int reload(void)
16223{
16224 return load_config(1);
16225}
16226
16227static int unload_module(void)
16228{
16229 int res;
16230
16237 res |= ast_manager_unregister("VoicemailUsersList");
16238 res |= ast_manager_unregister("VoicemailUserStatus");
16239 res |= ast_manager_unregister("VoicemailRefresh");
16240 res |= ast_manager_unregister("VoicemailBoxSummary");
16241 res |= ast_manager_unregister("VoicemailMove");
16242 res |= ast_manager_unregister("VoicemailRemove");
16243 res |= ast_manager_unregister("VoicemailForward");
16244#ifdef TEST_FRAMEWORK
16245 res |= AST_TEST_UNREGISTER(test_voicemail_vmsayname);
16246 res |= AST_TEST_UNREGISTER(test_voicemail_msgcount);
16247 res |= AST_TEST_UNREGISTER(test_voicemail_vmuser);
16248 res |= AST_TEST_UNREGISTER(test_voicemail_notify_endl);
16249 res |= AST_TEST_UNREGISTER(test_voicemail_load_config);
16250 res |= AST_TEST_UNREGISTER(test_voicemail_vm_info);
16251#endif
16255#ifdef TEST_FRAMEWORK
16256 ast_uninstall_vm_test_functions();
16257#endif
16259
16260 ao2_container_unregister("voicemail_alias_mailbox_mappings");
16262 ao2_container_unregister("voicemail_mailbox_alias_mappings");
16264
16267
16269 ast_unload_realtime("voicemail");
16270 ast_unload_realtime("voicemail_data");
16271
16272#ifdef IMAP_STORAGE
16273 ast_mwi_state_callback_all(imap_close_subscribed_mailbox, NULL);
16274#endif
16275 free_vm_users();
16276 free_vm_zones();
16277 return res;
16278}
16279
16280static void print_mappings(void *v_obj, void *where, ao2_prnt_fn *prnt)
16281{
16282 struct alias_mailbox_mapping *mapping = v_obj;
16283
16284 if (!mapping) {
16285 return;
16286 }
16287 prnt(where, "Alias: %s Mailbox: %s", mapping->alias, mapping->mailbox);
16288}
16289
16290/*!
16291 * \brief Load the module
16292 *
16293 * Module loading including tests for configuration or dependencies.
16294 * This function can return AST_MODULE_LOAD_FAILURE, AST_MODULE_LOAD_DECLINE,
16295 * or AST_MODULE_LOAD_SUCCESS.
16296 *
16297 * If a dependency, allocation or environment variable fails tests, return AST_MODULE_LOAD_FAILURE.
16298 *
16299 * If the module can't load the configuration file, can't register as a provider or
16300 * has another issue not fatal to Asterisk itself, return AST_MODULE_LOAD_DECLINE.
16301 *
16302 * On success return AST_MODULE_LOAD_SUCCESS.
16303 */
16304static int load_module(void)
16305{
16306 int res;
16307 my_umask = umask(0);
16308 umask(my_umask);
16309
16312 if (!inprocess_container) {
16314 }
16315
16317 alias_mailbox_mapping_hash_fn, NULL, alias_mailbox_mapping_cmp_fn);
16319 ast_log(LOG_ERROR, "Unable to create alias_mailbox_mappings container\n");
16322 }
16323 res = ao2_container_register("voicemail_alias_mailbox_mappings", alias_mailbox_mappings, print_mappings);
16324 if (res) {
16325 ast_log(LOG_ERROR, "Unable to register alias_mailbox_mappings container\n");
16329 }
16330
16332 mailbox_alias_mapping_hash_fn, NULL, mailbox_alias_mapping_cmp_fn);
16334 ast_log(LOG_ERROR, "Unable to create mailbox_alias_mappings container\n");
16336 ao2_container_unregister("voicemail_alias_mailbox_mappings");
16339 }
16340 res = ao2_container_register("voicemail_mailbox_alias_mappings", mailbox_alias_mappings, print_mappings);
16341 if (res) {
16342 ast_log(LOG_ERROR, "Unable to register mailbox_alias_mappings container\n");
16344 ao2_container_unregister("voicemail_alias_mailbox_mappings");
16348 }
16349
16350 /* compute the location of the voicemail spool directory */
16351 snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
16352
16353 if (!(mwi_subscription_tps = ast_taskprocessor_get("app_voicemail", 0))) {
16354 ast_log(AST_LOG_WARNING, "failed to reference mwi subscription taskprocessor. MWI will not work\n");
16355 }
16356
16357 if ((res = load_config(0))) {
16358 unload_module();
16360 }
16361
16375#ifdef TEST_FRAMEWORK
16376 res |= AST_TEST_REGISTER(test_voicemail_vmsayname);
16377 res |= AST_TEST_REGISTER(test_voicemail_msgcount);
16378 res |= AST_TEST_REGISTER(test_voicemail_vmuser);
16379 res |= AST_TEST_REGISTER(test_voicemail_notify_endl);
16380 res |= AST_TEST_REGISTER(test_voicemail_load_config);
16381 res |= AST_TEST_REGISTER(test_voicemail_vm_info);
16382#endif
16383
16384 if (res) {
16385 ast_log(LOG_ERROR, "Failure registering applications, functions or tests\n");
16386 unload_module();
16388 }
16389
16390 /* ast_vm_register may return DECLINE if another module registered for vm */
16391 res = ast_vm_register(&vm_table);
16392 if (res) {
16393 ast_log(LOG_ERROR, "Failure registering as a voicemail provider\n");
16394 unload_module();
16396 }
16397
16398 /* ast_vm_greeter_register may return DECLINE if another module registered as a greeter */
16400 if (res) {
16401 ast_log(LOG_ERROR, "Failure registering as a greeter provider\n");
16402 unload_module();
16404 }
16405
16407
16408#ifdef TEST_FRAMEWORK
16409 ast_install_vm_test_functions(vm_test_create_user, vm_test_destroy_user);
16410#endif
16411
16412 ast_realtime_require_field("voicemail", "uniqueid", RQ_UINTEGER3, 11, "password", RQ_CHAR, 10, SENTINEL);
16413 ast_realtime_require_field("voicemail_data", "filename", RQ_CHAR, 30, "duration", RQ_UINTEGER3, 5, SENTINEL);
16414
16416}
16417
16418static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
16419{
16420 int cmd = 0;
16421 char destination[80] = "";
16422 int retries = 0;
16423
16424 if (!num) {
16425 ast_verb(3, "Destination number will be entered manually\n");
16426 while (retries < 3 && cmd != 't') {
16427 destination[1] = '\0';
16428 destination[0] = cmd = ast_play_and_wait(chan, "vm-enter-num-to-call");
16429 if (!cmd)
16430 destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
16431 if (!cmd)
16432 destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
16433 if (!cmd) {
16434 cmd = ast_waitfordigit(chan, 6000);
16435 if (cmd)
16436 destination[0] = cmd;
16437 }
16438 if (!cmd) {
16439 retries++;
16440 } else {
16441
16442 if (cmd < 0)
16443 return 0;
16444 if (cmd == '*') {
16445 ast_verb(3, "User hit '*' to cancel outgoing call\n");
16446 return 0;
16447 }
16448 if ((cmd = ast_readstring(chan, destination + strlen(destination), sizeof(destination) - 1, 6000, 10000, "#")) < 0)
16449 retries++;
16450 else
16451 cmd = 't';
16452 }
16453 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
16454 isprint(cmd) ? cmd : '?', isprint(cmd) ? cmd : '?');
16455 }
16456 if (retries >= 3) {
16457 return 0;
16458 }
16459
16460 } else {
16461 ast_verb(3, "Destination number is CID number '%s'\n", num);
16462 ast_copy_string(destination, num, sizeof(destination));
16463 }
16464
16465 if (!ast_strlen_zero(destination)) {
16466 if (destination[strlen(destination) -1 ] == '*')
16467 return 0;
16468 ast_verb(3, "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, ast_channel_context(chan));
16469 ast_channel_exten_set(chan, destination);
16470 ast_channel_context_set(chan, outgoing_context);
16471 ast_channel_priority_set(chan, 0);
16472 return 9;
16473 }
16474 return 0;
16475}
16476
16477/*!
16478 * \brief The advanced options within a message.
16479 * \param chan
16480 * \param vmu
16481 * \param vms
16482 * \param msg
16483 * \param option
16484 * \param record_gain
16485 *
16486 * Provides handling for the play message envelope, call the person back, or reply to message.
16487 *
16488 * \return zero on success, -1 on error.
16489 */
16490static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain)
16491{
16492 int res = 0;
16493 char filename[PATH_MAX];
16494 struct ast_config *msg_cfg = NULL;
16495 const char *origtime, *context;
16496 char *name, *num;
16497 int retries = 0;
16498 char *cid;
16499 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE, };
16500
16501 vms->starting = 0;
16502
16503 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
16504
16505 /* Retrieve info from VM attribute file */
16506 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
16507 RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
16508 msg_cfg = ast_config_load(filename, config_flags);
16509 DISPOSE(vms->curdir, vms->curmsg);
16510 if (!valid_config(msg_cfg)) {
16511 ast_log(AST_LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
16512 return 0;
16513 }
16514
16515 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
16516 ast_config_destroy(msg_cfg);
16517 return 0;
16518 }
16519
16520 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
16521
16522 context = ast_variable_retrieve(msg_cfg, "message", "context");
16523 switch (option) {
16524 case 3: /* Play message envelope */
16525 if (!res) {
16526 res = play_message_datetime(chan, vmu, origtime, filename);
16527 }
16528 if (!res) {
16529 res = play_message_callerid(chan, vms, cid, context, 0, 1);
16530 }
16531
16532 res = 't';
16533 break;
16534
16535 case 2: /* Call back */
16536
16537 if (ast_strlen_zero(cid))
16538 break;
16539
16540 ast_callerid_parse(cid, &name, &num);
16541 while ((res > -1) && (res != 't')) {
16542 switch (res) {
16543 case '1':
16544 if (num) {
16545 /* Dial the CID number */
16546 res = dialout(chan, vmu, num, vmu->callback);
16547 if (res) {
16548 ast_config_destroy(msg_cfg);
16549 return 9;
16550 }
16551 } else {
16552 res = '2';
16553 }
16554 break;
16555
16556 case '2':
16557 /* Want to enter a different number, can only do this if there's a dialout context for this user */
16558 if (!ast_strlen_zero(vmu->dialout)) {
16559 res = dialout(chan, vmu, NULL, vmu->dialout);
16560 if (res) {
16561 ast_config_destroy(msg_cfg);
16562 return 9;
16563 }
16564 } else {
16565 ast_verb(3, "Caller can not specify callback number - no dialout context available\n");
16566 res = ast_play_and_wait(chan, "vm-sorry");
16567 }
16568 ast_config_destroy(msg_cfg);
16569 return res;
16570 case '*':
16571 res = 't';
16572 break;
16573 case '3':
16574 case '4':
16575 case '5':
16576 case '6':
16577 case '7':
16578 case '8':
16579 case '9':
16580 case '0':
16581
16582 res = ast_play_and_wait(chan, "vm-sorry");
16583 retries++;
16584 break;
16585 default:
16586 if (num) {
16587 ast_verb(3, "Confirm CID number '%s' is number to use for callback\n", num);
16588 res = ast_play_and_wait(chan, "vm-num-i-have");
16589 if (!res)
16590 res = play_message_callerid(chan, vms, num, vmu->context, 1, 1);
16591 if (!res)
16592 res = ast_play_and_wait(chan, "vm-tocallnum");
16593 /* Only prompt for a caller-specified number if there is a dialout context specified */
16594 if (!ast_strlen_zero(vmu->dialout)) {
16595 if (!res)
16596 res = ast_play_and_wait(chan, "vm-calldiffnum");
16597 }
16598 } else {
16599 res = ast_play_and_wait(chan, "vm-nonumber");
16600 if (!ast_strlen_zero(vmu->dialout)) {
16601 if (!res)
16602 res = ast_play_and_wait(chan, "vm-toenternumber");
16603 }
16604 }
16605 if (!res) {
16606 res = ast_play_and_wait(chan, "vm-star-cancel");
16607 }
16608 if (!res) {
16609 res = ast_waitfordigit(chan, 6000);
16610 }
16611 if (!res) {
16612 retries++;
16613 if (retries > 3) {
16614 res = 't';
16615 }
16616 }
16617 ast_test_suite_event_notify("USERPRESS", "Message: User pressed %c\r\nDTMF: %c",
16618 isprint(res) ? res : '?', isprint(res) ? res : '?');
16619 break;
16620
16621 }
16622 if (res == 't')
16623 res = 0;
16624 else if (res == '*')
16625 res = -1;
16626 }
16627 break;
16628
16629 case 1: /* Reply */
16630 /* Send reply directly to sender */
16631 if (ast_strlen_zero(cid))
16632 break;
16633
16634 ast_callerid_parse(cid, &name, &num);
16635 if (!num) {
16636 ast_verb(3, "No CID number available, no reply sent\n");
16637 if (!res)
16638 res = ast_play_and_wait(chan, "vm-nonumber");
16639 ast_config_destroy(msg_cfg);
16640 return res;
16641 } else {
16642 struct ast_vm_user vmu2, *vmu3;
16643 memset(&vmu2, 0, sizeof(vmu2));
16644 vmu3 = find_user(&vmu2, vmu->context, num);
16645 if (vmu3) {
16646 struct leave_vm_options leave_options;
16647 char mailbox[AST_MAX_EXTENSION * 2 + 2];
16648 snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
16649
16650 ast_verb(3, "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
16651
16652 memset(&leave_options, 0, sizeof(leave_options));
16653 leave_options.record_gain = record_gain;
16654 leave_options.beeptone = "beep";
16655 res = leave_voicemail(chan, mailbox, &leave_options);
16656 if (!res)
16657 res = 't';
16658 ast_config_destroy(msg_cfg);
16659 free_user(vmu3);
16660 return res;
16661 } else {
16662 /* Sender has no mailbox, can't reply */
16663 ast_verb(3, "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
16664 ast_play_and_wait(chan, "vm-nobox");
16665 res = 't';
16666 ast_config_destroy(msg_cfg);
16667 return res;
16668 }
16669 }
16670 res = 0;
16671
16672 break;
16673 }
16674
16675 ast_config_destroy(msg_cfg);
16676
16677#ifndef IMAP_STORAGE
16678 if (!res) {
16679 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
16680 vms->heard[msg] = 1;
16681 res = wait_file(chan, vms, vms->fn);
16682 }
16683#endif
16684 return res;
16685}
16686
16687static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
16688 int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir,
16689 signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro)
16690{
16691 /* Record message & let caller review or re-record it, or set options if applicable */
16692 int res = 0;
16693 int cmd = 0;
16694 int max_attempts = 3;
16695 int attempts = 0;
16696 int recorded = 0;
16697 int msg_exists = 0;
16698 signed char zero_gain = 0;
16699 char tempfile[PATH_MAX];
16700 char *acceptdtmf = "#";
16701 char *canceldtmf = "";
16702 int canceleddtmf = 0;
16703 SCOPE_ENTER(3, "%s: rf: %s fmt: %s type: %s vmu: %s\n",
16704 ast_channel_name(chan), recordfile, fmt, outsidecaller ? "msg" : "greeting",
16705 vmu->mailbox);
16706 /* Note that urgent and private are for flagging messages as such in the future */
16707
16708 /* barf if no pointer passed to store duration in */
16709 if (duration == NULL) {
16710 SCOPE_EXIT_LOG_RTN_VALUE(-1, LOG_WARNING, "%s\n", "Error play_record_review called without duration pointer\n");
16711 }
16712
16713 if (!outsidecaller)
16714 snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
16715 else
16716 ast_copy_string(tempfile, recordfile, sizeof(tempfile));
16717
16718 cmd = '3'; /* Want to start by recording */
16719
16720 while ((cmd >= 0) && (cmd != 't')) {
16721 switch (cmd) {
16722 case '1':
16723 if (!msg_exists) {
16724 /* In this case, 1 is to record a message */
16725 cmd = '3';
16726 break;
16727 } else {
16728 /* Otherwise 1 is to save the existing message */
16729 ast_verb(3, "Saving message as is\n");
16730 if (!outsidecaller) {
16731 ast_trace(-1, "Renaming greeting '%s' to '%s'\n", tempfile, recordfile);
16732 ast_filerename(tempfile, recordfile, NULL);
16733 }
16734 if (!forwardintro) {
16735 ast_stream_and_wait(chan, "vm-msgsaved", "");
16736 }
16737 if (!outsidecaller) {
16738 /* Saves to IMAP server only if imapgreeting=yes */
16739 ast_trace(-1, "Saving greeting '%s'\n", recordfile);
16740 SCOPE_CALL(-1, STORE, recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms, flag, msg_id);
16741 SCOPE_CALL(-1, DISPOSE, recordfile, -1);
16742 }
16743 cmd = 't';
16744 SCOPE_EXIT_RTN_VALUE(res, "Message saved to %s\n", recordfile);
16745 }
16746 case '2':
16747 /* Review */
16748 ast_verb(3, "Reviewing the message\n");
16749 ast_trace(-1, "Reviewing '%s'\n", tempfile);
16750 cmd = ast_stream_and_wait(chan, tempfile, AST_DIGIT_ANY);
16751 break;
16752 case '3':
16753 msg_exists = 0;
16754 /* Record */
16755 if (recorded == 1)
16756 ast_verb(3, "Re-recording the message\n");
16757 else
16758 ast_verb(3, "Recording the message\n");
16759
16760 if (recorded && outsidecaller) {
16761 if (forwardintro) {
16762 cmd = ast_play_and_wait(chan, "vm-record-prepend");
16763 } else {
16764 cmd = ast_play_and_wait(chan, INTRO);
16765 }
16766 cmd = ast_play_and_wait(chan, "beep");
16767 }
16768 if (cmd == -1) {
16769 /* User has hung up, no options to give */
16770 ast_filedelete(tempfile, NULL);
16771 SCOPE_EXIT_RTN_VALUE(cmd, "User hung up before message could be rerecorded. Deleted '%s'\n", tempfile);
16772 }
16773 recorded = 1;
16774 /* 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 */
16775 if (record_gain)
16777 if (ast_test_flag(vmu, VM_OPERATOR))
16778 canceldtmf = "0";
16779 ast_trace(-1, "Recording '%s'\n", tempfile);
16780 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);
16781 if (strchr(canceldtmf, cmd)) {
16782 /* need this flag here to distinguish between pressing '0' during message recording or after */
16783 canceleddtmf = 1;
16784 }
16785 if (record_gain)
16786 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
16787 if (cmd == -1) {
16788 /* User has hung up, no options to give */
16789 if (!outsidecaller) {
16790 /* user was recording a greeting and they hung up, so let's delete the recording. */
16791 ast_filedelete(tempfile, NULL);
16792 }
16793 SCOPE_EXIT_RTN_VALUE(cmd, "User hung up after recording. %s %s\n",
16794 outsidecaller ? "Saved message " : "Deleted greeting \n", tempfile);
16795 }
16796 if (cmd == '0') {
16797 break;
16798 } else if (cmd == '*') {
16799 break;
16800#if 0
16801 } else if (vmu->review && sound_duration && (*sound_duration < 5)) {
16802 /* Message is too short */
16803 ast_verb(3, "Message too short\n");
16804 cmd = ast_play_and_wait(chan, "vm-tooshort");
16805 cmd = ast_filedelete(tempfile, NULL);
16806 break;
16807 } else if (vmu->review && (cmd == 2 && sound_duration && *sound_duration < (maxsilence + 3))) {
16808 /* Message is all silence */
16809 ast_verb(3, "Nothing recorded\n");
16810 cmd = ast_filedelete(tempfile, NULL);
16811 cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
16812 if (!cmd)
16813 cmd = ast_play_and_wait(chan, "vm-speakup");
16814 break;
16815#endif
16816 } else {
16817 /* If all is well, a message exists */
16818 msg_exists = 1;
16819 cmd = 0;
16820 }
16821 break;
16822 case '4':
16823 if (outsidecaller && ast_test_flag(vmu, VM_MARK_URGENT)) { /* only mark vm messages */
16824 /* Mark Urgent */
16825 if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
16826 ast_verb(3, "marking message as Urgent\n");
16827 res = ast_play_and_wait(chan, "vm-marked-urgent");
16828 strcpy(flag, "Urgent");
16829 } else if (flag) {
16830 ast_verb(3, "UNmarking message as Urgent\n");
16831 res = ast_play_and_wait(chan, "vm-marked-nonurgent");
16832 strcpy(flag, "");
16833 } else {
16834 ast_play_and_wait(chan, "vm-sorry");
16835 }
16836 cmd = 0;
16837 } else {
16838 cmd = ast_play_and_wait(chan, "vm-sorry");
16839 }
16840 break;
16841 case '5':
16842 case '6':
16843 case '7':
16844 case '8':
16845 case '9':
16846 case '*':
16847 case '#':
16848 cmd = ast_play_and_wait(chan, "vm-sorry");
16849 break;
16850#if 0
16851/* XXX Commented out for the moment because of the dangers of deleting
16852 a message while recording (can put the message numbers out of sync) */
16853 case '*':
16854 /* Cancel recording, delete message, offer to take another message*/
16855 cmd = ast_play_and_wait(chan, "vm-deleted");
16856 cmd = ast_filedelete(tempfile, NULL);
16857 if (outsidecaller) {
16858 res = vm_exec(chan, NULL);
16859 return res;
16860 }
16861 else
16862 return 1;
16863#endif
16864 case '0':
16865 if (!ast_test_flag(vmu, VM_OPERATOR) || (!canceleddtmf && !outsidecaller)) {
16866 cmd = ast_play_and_wait(chan, "vm-sorry");
16867 break;
16868 }
16869 if (msg_exists || recorded) {
16870 ast_trace(-1, "Reviewing '%s'\n", tempfile);
16871 cmd = ast_play_and_wait(chan, "vm-saveoper");
16872 if (!cmd)
16873 cmd = ast_waitfordigit(chan, 3000);
16874 if (cmd == '1') {
16875 ast_trace(-1, "Saving '%s' to '%s'\n", tempfile, recordfile);
16876 ast_filerename(tempfile, recordfile, NULL);
16877 ast_play_and_wait(chan, "vm-msgsaved");
16878 cmd = '0';
16879 } else if (cmd == '4') {
16880 if (flag) {
16881 ast_play_and_wait(chan, "vm-marked-urgent");
16882 strcpy(flag, "Urgent");
16883 }
16884 ast_play_and_wait(chan, "vm-msgsaved");
16885 cmd = '0';
16886 } else {
16887 ast_trace(-1, "Deleting '%s'\n", tempfile);
16888 ast_play_and_wait(chan, "vm-deleted");
16889 SCOPE_CALL(-1, DELETE, tempfile, -1, tempfile, vmu);
16890 SCOPE_CALL(-1, DISPOSE, tempfile, -1);
16891 cmd = '0';
16892 }
16893 }
16894 return cmd;
16895 default:
16896 /* If the caller is an outside caller and the review option is enabled or it's forward intro
16897 allow them to review the message, but let the owner of the box review
16898 their OGM's */
16899 if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW) && !forwardintro) {
16900 SCOPE_EXIT_RTN_VALUE(cmd, "Done. Outside caller, review not set, no forwardintro\n");
16901 }
16902 if (msg_exists) {
16903 cmd = ast_play_and_wait(chan, "vm-review");
16904 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_MARK_URGENT)) {
16905 if ((flag && ast_strlen_zero(flag)) || (!ast_strlen_zero(flag) && strcmp(flag, "Urgent"))) {
16906 cmd = ast_play_and_wait(chan, "vm-review-urgent");
16907 } else if (flag) {
16908 cmd = ast_play_and_wait(chan, "vm-review-nonurgent");
16909 }
16910 }
16911 } else {
16912 cmd = ast_play_and_wait(chan, "vm-torerecord");
16913 if (!cmd)
16914 cmd = ast_waitfordigit(chan, 600);
16915 }
16916
16917 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
16918 cmd = ast_play_and_wait(chan, "vm-reachoper");
16919 if (!cmd)
16920 cmd = ast_waitfordigit(chan, 600);
16921 }
16922#if 0
16923 if (!cmd)
16924 cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
16925#endif
16926 if (!cmd)
16927 cmd = ast_waitfordigit(chan, 6000);
16928 if (!cmd) {
16929 attempts++;
16930 }
16931 if (attempts > max_attempts) {
16932 cmd = 't';
16933 }
16934 }
16935 }
16936 if (!outsidecaller && (cmd == -1 || cmd == 't')) {
16937 /* Hang up or timeout, so delete the recording. */
16938 ast_trace(-1, "Deleting '%s' on hangup or timeout\n", tempfile);
16939 ast_filedelete(tempfile, NULL);
16940 }
16941
16942 if (cmd != 't' && outsidecaller)
16943 ast_play_and_wait(chan, "vm-goodbye");
16944
16945 SCOPE_EXIT_RTN_VALUE(cmd, "Done\n");
16946}
16947
16949{
16950 struct ast_vm_msg_snapshot *msg_snapshot;
16951
16952 if (!(msg_snapshot = ast_calloc(1, sizeof(*msg_snapshot)))) {
16953 return NULL;
16954 }
16955
16956 if (ast_string_field_init(msg_snapshot, 512)) {
16957 ast_free(msg_snapshot);
16958 return NULL;
16959 }
16960
16961 return msg_snapshot;
16962}
16963
16965{
16966 ast_string_field_free_memory(msg_snapshot);
16967 ast_free(msg_snapshot);
16968
16969 return NULL;
16970}
16971
16972#ifdef TEST_FRAMEWORK
16973
16974static int vm_test_destroy_user(const char *context, const char *mailbox)
16975{
16976 struct ast_vm_user *vmu;
16977
16980 if (!strcmp(context, vmu->context)
16981 && !strcmp(mailbox, vmu->mailbox)) {
16983 ast_free(vmu);
16984 break;
16985 }
16986 }
16989 return 0;
16990}
16991
16992static int vm_test_create_user(const char *context, const char *mailbox)
16993{
16994 struct ast_vm_user *vmu;
16995
16996 if (!(vmu = find_or_create(context, mailbox))) {
16997 return -1;
16998 }
16999 populate_defaults(vmu);
17000 return 0;
17001}
17002
17003#endif
17004
17005/*!
17006 * \brief Create and store off all the msgs in an open mailbox
17007 *
17008 * \note TODO XXX This function should work properly for all
17009 * voicemail storage options, but is far more expensive for
17010 * ODBC at the moment. This is because the RETRIEVE macro
17011 * not only pulls out the message's meta data file from the
17012 * database, but also the actual audio for each message, temporarily
17013 * writing it to the file system. This is an area that needs
17014 * to be made more efficient.
17015 */
17017 struct vm_state *vms,
17018 struct ast_vm_mailbox_snapshot *mailbox_snapshot,
17019 int snapshot_index,
17020 int mailbox_index,
17021 int descending,
17022 enum ast_vm_snapshot_sort_val sort_val)
17023{
17024 struct ast_vm_msg_snapshot *msg_snapshot;
17025 struct ast_vm_msg_snapshot *msg_snapshot_tmp;
17026 struct ast_config *msg_cfg;
17027 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17028 char filename[PATH_MAX];
17029 const char *value;
17030
17031 for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
17032 int inserted = 0;
17033 /* Find the msg */
17034 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
17035 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
17036 RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
17037 msg_cfg = ast_config_load(filename, config_flags);
17038 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17039 DISPOSE(vms->curdir, vms->curmsg);
17040 continue;
17041 }
17042
17043 /* Create the snapshot object */
17044 if (!(msg_snapshot = vm_msg_snapshot_alloc())) {
17045 ast_config_destroy(msg_cfg);
17046 return -1;
17047 }
17048
17049 /* Fill in the snapshot object */
17050 if ((value = ast_variable_retrieve(msg_cfg, "message", "msg_id"))) {
17051 ast_string_field_set(msg_snapshot, msg_id, value);
17052 } else {
17053 /* Message snapshots *really* should have a
17054 * message ID. Add one to the message config
17055 * if it does not already exist
17056 */
17057 char id[MSG_ID_LEN];
17058 if (!(add_message_id(msg_cfg, vms->curdir, vms->curmsg,
17059 filename, id, sizeof(id), vmu, mailbox_index))) {
17060 ast_string_field_set(msg_snapshot, msg_id, id);
17061 } else {
17062 ast_log(LOG_WARNING, "Unable to create a message ID for message %s/%d\n", vms->curdir, vms->curmsg);
17063 }
17064 }
17065 if ((value = ast_variable_retrieve(msg_cfg, "message", "callerid"))) {
17066 ast_string_field_set(msg_snapshot, callerid, value);
17067 }
17068 if ((value = ast_variable_retrieve(msg_cfg, "message", "callerchan"))) {
17069 ast_string_field_set(msg_snapshot, callerchan, value);
17070 }
17071 if ((value = ast_variable_retrieve(msg_cfg, "message", "exten"))) {
17072 ast_string_field_set(msg_snapshot, exten, value);
17073 }
17074 if ((value = ast_variable_retrieve(msg_cfg, "message", "origdate"))) {
17075 ast_string_field_set(msg_snapshot, origdate, value);
17076 }
17077 if ((value = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
17078 ast_string_field_set(msg_snapshot, origtime, value);
17079 }
17080 if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17081 ast_string_field_set(msg_snapshot, duration, value);
17082 }
17083 if ((value = ast_variable_retrieve(msg_cfg, "message", "flag"))) {
17084 ast_string_field_set(msg_snapshot, flag, value);
17085 }
17086 msg_snapshot->msg_number = vms->curmsg;
17087 ast_string_field_set(msg_snapshot, folder_name, mailbox_folders[mailbox_index]);
17088
17089 /* store msg snapshot in mailbox snapshot */
17090 switch (sort_val) {
17091 default:
17093 if (descending) {
17094 AST_LIST_INSERT_HEAD(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17095 } else {
17096 AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17097 }
17098 inserted = 1;
17099 break;
17101 AST_LIST_TRAVERSE_SAFE_BEGIN(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot_tmp, msg) {
17102 int val = strcmp(msg_snapshot->origtime, msg_snapshot_tmp->origtime);
17103 if (descending && val >= 0) {
17104 AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
17105 inserted = 1;
17106 break;
17107 } else if (!descending && val <= 0) {
17108 AST_LIST_INSERT_BEFORE_CURRENT(msg_snapshot, msg);
17109 inserted = 1;
17110 break;
17111 }
17112 }
17114 break;
17115 }
17116
17117 if (!inserted) {
17118 AST_LIST_INSERT_TAIL(&mailbox_snapshot->snapshots[snapshot_index], msg_snapshot, msg);
17119 }
17120
17121 mailbox_snapshot->total_msg_num++;
17122
17123 /* cleanup configs and msg */
17124 ast_config_destroy(msg_cfg);
17125 DISPOSE(vms->curdir, vms->curmsg);
17126 }
17127
17128 return 0;
17129}
17130
17132 const char *context,
17133 const char *folder,
17134 int descending,
17135 enum ast_vm_snapshot_sort_val sort_val,
17136 int combine_INBOX_and_OLD)
17137{
17138 struct ast_vm_mailbox_snapshot *mailbox_snapshot;
17139 struct vm_state vms;
17140 struct ast_vm_user *vmu = NULL, vmus;
17141 int res;
17142 int i;
17143 int this_index_only = -1;
17144 int open = 0;
17145 int inbox_index = get_folder_by_name("INBOX");
17146 int old_index = get_folder_by_name("Old");
17147 int urgent_index = get_folder_by_name("Urgent");
17148
17149 if (ast_strlen_zero(mailbox)) {
17150 ast_log(LOG_WARNING, "Cannot create a mailbox snapshot since no mailbox was specified\n");
17151 return NULL;
17152 }
17153
17154 memset(&vmus, 0, sizeof(vmus));
17155
17156 if (!(ast_strlen_zero(folder))) {
17157 /* find the folder index */
17158 for (i = 0; i < ARRAY_LEN(mailbox_folders); i++) {
17159 if (!strcasecmp(mailbox_folders[i], folder)) {
17160 this_index_only = i;
17161 break;
17162 }
17163 }
17164 if (this_index_only == -1) {
17165 /* Folder was specified and it did not match any folder in our list */
17166 return NULL;
17167 }
17168 }
17169
17170 if (!(vmu = find_user(&vmus, context, mailbox))) {
17171 ast_log(AST_LOG_WARNING, "Failed to create mailbox snapshot for unknown voicemail user %s@%s\n", mailbox, context);
17172 return NULL;
17173 }
17174
17175 if (!(mailbox_snapshot = ast_calloc(1, sizeof(*mailbox_snapshot)))) {
17176 ast_log(AST_LOG_ERROR, "Failed to allocate memory for mailbox snapshot\n");
17177 free_user(vmu);
17178 return NULL;
17179 }
17180
17181 if (!(mailbox_snapshot->snapshots = ast_calloc(ARRAY_LEN(mailbox_folders), sizeof(*mailbox_snapshot->snapshots)))) {
17182 ast_free(mailbox_snapshot);
17183 free_user(vmu);
17184 return NULL;
17185 }
17186
17187 mailbox_snapshot->folders = ARRAY_LEN(mailbox_folders);
17188
17189 for (i = 0; i < mailbox_snapshot->folders; i++) {
17190 int msg_folder_index = i;
17191
17192 /* We want this message in the snapshot if any of the following:
17193 * No folder was specified.
17194 * The specified folder matches the current folder.
17195 * The specified folder is INBOX AND we were asked to combine messages AND the current folder is either Old or Urgent.
17196 */
17197 if (!(this_index_only == -1 || this_index_only == i || (this_index_only == inbox_index && combine_INBOX_and_OLD && (i == old_index || i == urgent_index)))) {
17198 continue;
17199 }
17200
17201 /* Make sure that Old or Urgent messages are marked as being in INBOX. */
17202 if (combine_INBOX_and_OLD && (i == old_index || i == urgent_index)) {
17203 msg_folder_index = inbox_index;
17204 }
17205
17206 memset(&vms, 0, sizeof(vms));
17207 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17208 vms.lastmsg = -1;
17209 open = 0;
17210
17211 /* open the mailbox state */
17212 if ((res = open_mailbox(&vms, vmu, i)) < 0) {
17213 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17214 goto snapshot_cleanup;
17215 }
17216 open = 1;
17217
17218 /* Iterate through each msg, storing off info */
17219 if (vms.lastmsg != -1) {
17220 if ((vm_msg_snapshot_create(vmu, &vms, mailbox_snapshot, msg_folder_index, i, descending, sort_val))) {
17221 ast_log(LOG_WARNING, "Failed to create msg snapshots for %s@%s\n", mailbox, context);
17222 goto snapshot_cleanup;
17223 }
17224 }
17225
17226 /* close mailbox */
17227 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17228 goto snapshot_cleanup;
17229 }
17230 open = 0;
17231 }
17232
17233snapshot_cleanup:
17234 if (vmu && open) {
17235 close_mailbox(&vms, vmu);
17236 }
17237
17238#ifdef IMAP_STORAGE
17239 if (vmu) {
17240 vmstate_delete(&vms);
17241 }
17242#endif
17243
17244 free_user(vmu);
17245 return mailbox_snapshot;
17246}
17247
17249{
17250 int i;
17251 struct ast_vm_msg_snapshot *msg_snapshot;
17252
17253 for (i = 0; i < mailbox_snapshot->folders; i++) {
17254 while ((msg_snapshot = AST_LIST_REMOVE_HEAD(&mailbox_snapshot->snapshots[i], msg))) {
17255 msg_snapshot = vm_msg_snapshot_destroy(msg_snapshot);
17256 }
17257 }
17258 ast_free(mailbox_snapshot->snapshots);
17259 ast_free(mailbox_snapshot);
17260 return NULL;
17261}
17262
17263/*!
17264 * \brief common bounds checking and existence check for Voicemail API functions.
17265 *
17266 * \details
17267 * This is called by vm_msg_move, vm_msg_remove, and vm_msg_forward to
17268 * ensure that data passed in are valid. This ensures that given the
17269 * desired message IDs, they can be found.
17270 *
17271 * \param vms The voicemail state corresponding to an open mailbox
17272 * \param msg_ids An array of message identifiers
17273 * \param num_msgs The number of identifiers in msg_ids
17274 * \param[out] msg_nums The message indexes corresponding to the given
17275 * \param vmu
17276 * message IDs
17277 * \pre vms must have open_mailbox() called on it prior to this function.
17278 *
17279 * \retval -1 Failure
17280 * \retval 0 Success
17281 */
17282static 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)
17283{
17284 int i;
17285 int res = 0;
17286 for (i = 0; i < num_msgs; ++i) {
17287 const char *msg_id = msg_ids[i];
17288 int found = 0;
17289 for (vms->curmsg = 0; vms->curmsg <= vms->lastmsg; vms->curmsg++) {
17290 const char *other_msg_id;
17291 char filename[PATH_MAX];
17292 struct ast_config *msg_cfg;
17293 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17294
17295 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
17296 snprintf(filename, sizeof(filename), "%s.txt", vms->fn);
17297 RETRIEVE(vms->curdir, vms->curmsg, vmu->mailbox, vmu->context);
17298 msg_cfg = ast_config_load(filename, config_flags);
17299 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17300 DISPOSE(vms->curdir, vms->curmsg);
17301 res = -1;
17302 goto done;
17303 }
17304
17305 other_msg_id = ast_variable_retrieve(msg_cfg, "message", "msg_id");
17306
17307 if (!ast_strlen_zero(other_msg_id) && !strcmp(other_msg_id, msg_id)) {
17308 /* Message found. We can get out of this inner loop
17309 * and move on to the next message to find
17310 */
17311 found = 1;
17312 msg_nums[i] = vms->curmsg;
17313 ast_config_destroy(msg_cfg);
17314 DISPOSE(vms->curdir, vms->curmsg);
17315 break;
17316 }
17317 ast_config_destroy(msg_cfg);
17318 DISPOSE(vms->curdir, vms->curmsg);
17319 }
17320 if (!found) {
17321 /* If we can't find one of the message IDs requested, then OH NO! */
17322 res = -1;
17323 goto done;
17324 }
17325 }
17326
17327done:
17328 return res;
17329}
17330
17331static void notify_new_state(struct ast_vm_user *vmu)
17332{
17333 int new = 0, old = 0, urgent = 0;
17334 char ext_context[1024];
17335
17336 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
17337 run_externnotify(vmu->context, vmu->mailbox, NULL);
17338 ast_app_inboxcount2(ext_context, &urgent, &new, &old);
17339 queue_mwi_event(NULL, ext_context, urgent, new, old);
17340}
17341
17342static int vm_msg_forward(const char *from_mailbox,
17343 const char *from_context,
17344 const char *from_folder,
17345 const char *to_mailbox,
17346 const char *to_context,
17347 const char *to_folder,
17348 size_t num_msgs,
17349 const char *msg_ids [],
17350 int delete_old)
17351{
17352 struct vm_state from_vms;
17353 struct ast_vm_user *vmu = NULL, vmus;
17354 struct ast_vm_user *to_vmu = NULL, to_vmus;
17355 struct ast_config *msg_cfg;
17356 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17357 char filename[PATH_MAX];
17358 int from_folder_index;
17359 int open = 0;
17360 int res = 0;
17361 int i;
17362 int *msg_nums;
17363
17364 if (ast_strlen_zero(from_mailbox) || ast_strlen_zero(to_mailbox)) {
17365 ast_log(LOG_WARNING, "Cannot forward message because either the from or to mailbox was not specified\n");
17366 return -1;
17367 }
17368
17369 if (!num_msgs) {
17370 ast_log(LOG_WARNING, "Invalid number of messages specified to forward: %zu\n", num_msgs);
17371 return -1;
17372 }
17373
17374 if (ast_strlen_zero(from_folder) || ast_strlen_zero(to_folder)) {
17375 ast_log(LOG_WARNING, "Cannot forward message because the from_folder or to_folder was not specified\n");
17376 return -1;
17377 }
17378
17379 memset(&vmus, 0, sizeof(vmus));
17380 memset(&to_vmus, 0, sizeof(to_vmus));
17381 memset(&from_vms, 0, sizeof(from_vms));
17382
17383 from_folder_index = get_folder_by_name(from_folder);
17384 if (from_folder_index == -1) {
17385 return -1;
17386 }
17387
17388 if (get_folder_by_name(to_folder) == -1) {
17389 return -1;
17390 }
17391
17392 if (!(vmu = find_user(&vmus, from_context, from_mailbox))) {
17393 ast_log(LOG_WARNING, "Can't find voicemail user to forward from (%s@%s)\n", from_mailbox, from_context);
17394 return -1;
17395 }
17396
17397 if (!(to_vmu = find_user(&to_vmus, to_context, to_mailbox))) {
17398 ast_log(LOG_WARNING, "Can't find voicemail user to forward to (%s@%s)\n", to_mailbox, to_context);
17399 free_user(vmu);
17400 return -1;
17401 }
17402
17403 ast_copy_string(from_vms.username, from_mailbox, sizeof(from_vms.username));
17404 from_vms.lastmsg = -1;
17405 open = 0;
17406
17407 /* open the mailbox state */
17408 if ((res = open_mailbox(&from_vms, vmu, from_folder_index)) < 0) {
17409 ast_log(LOG_WARNING, "Could not open mailbox %s\n", from_mailbox);
17410 res = -1;
17411 goto vm_forward_cleanup;
17412 }
17413
17414 open = 1;
17415
17416 if ((from_vms.lastmsg + 1) < num_msgs) {
17417 ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", from_folder, num_msgs);
17418 res = -1;
17419 goto vm_forward_cleanup;
17420 }
17421
17422 msg_nums = ast_alloca(sizeof(int) * num_msgs);
17423
17424 if ((res = message_range_and_existence_check(&from_vms, msg_ids, num_msgs, msg_nums, vmu) < 0)) {
17425 goto vm_forward_cleanup;
17426 }
17427
17428 /* Now we actually forward the messages */
17429 for (i = 0; i < num_msgs; i++) {
17430 int cur_msg = msg_nums[i];
17431 int duration = 0;
17432 const char *value;
17433
17434 make_file(from_vms.fn, sizeof(from_vms.fn), from_vms.curdir, cur_msg);
17435 snprintf(filename, sizeof(filename), "%s.txt", from_vms.fn);
17436 RETRIEVE(from_vms.curdir, cur_msg, vmu->mailbox, vmu->context);
17437 msg_cfg = ast_config_load(filename, config_flags);
17438 /* XXX This likely will not fail since we previously ensured that the
17439 * message we are looking for exists. However, there still could be some
17440 * circumstance where this fails, so atomicity is not guaranteed.
17441 */
17442 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17443 DISPOSE(from_vms.curdir, cur_msg);
17444 continue;
17445 }
17446 if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17447 duration = atoi(value);
17448 }
17449
17450 copy_message(NULL, vmu, from_folder_index, cur_msg, duration, to_vmu, vmfmts, from_vms.curdir, "", to_folder);
17451
17452 if (delete_old) {
17453 from_vms.deleted[cur_msg] = 1;
17454 }
17455 ast_config_destroy(msg_cfg);
17456 DISPOSE(from_vms.curdir, cur_msg);
17457 }
17458
17459 /* close mailbox */
17460 if ((res = close_mailbox(&from_vms, vmu) == ERROR_LOCK_PATH)) {
17461 res = -1;
17462 goto vm_forward_cleanup;
17463 }
17464 open = 0;
17465
17466vm_forward_cleanup:
17467 if (vmu && open) {
17468 close_mailbox(&from_vms, vmu);
17469 }
17470#ifdef IMAP_STORAGE
17471 if (vmu) {
17472 vmstate_delete(&from_vms);
17473 }
17474#endif
17475
17476 if (!res) {
17477 notify_new_state(to_vmu);
17478 }
17479
17480 free_user(vmu);
17481 free_user(to_vmu);
17482 return res;
17483}
17484
17485static int vm_msg_move(const char *mailbox,
17486 const char *context,
17487 size_t num_msgs,
17488 const char *oldfolder,
17489 const char *old_msg_ids [],
17490 const char *newfolder)
17491{
17492 struct vm_state vms;
17493 struct ast_vm_user *vmu = NULL, vmus;
17494 int old_folder_index;
17495 int new_folder_index;
17496 int open = 0;
17497 int res = 0;
17498 int i;
17499 int *old_msg_nums;
17500
17501 if (ast_strlen_zero(mailbox)) {
17502 ast_log(LOG_WARNING, "Cannot move message because no mailbox was specified\n");
17503 return -1;
17504 }
17505
17506 if (!num_msgs) {
17507 ast_log(LOG_WARNING, "Invalid number of messages specified to move: %zu\n", num_msgs);
17508 return -1;
17509 }
17510
17511 if (ast_strlen_zero(oldfolder) || ast_strlen_zero(newfolder)) {
17512 ast_log(LOG_WARNING, "Cannot move message because either oldfolder or newfolder was not specified\n");
17513 return -1;
17514 }
17515
17516 old_folder_index = get_folder_by_name(oldfolder);
17517 new_folder_index = get_folder_by_name(newfolder);
17518
17519 memset(&vmus, 0, sizeof(vmus));
17520 memset(&vms, 0, sizeof(vms));
17521
17522 if (old_folder_index == -1 || new_folder_index == -1) {
17523 return -1;
17524 }
17525
17526 if (!(vmu = find_user(&vmus, context, mailbox))) {
17527 return -1;
17528 }
17529
17530 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17531 vms.lastmsg = -1;
17532 open = 0;
17533
17534 /* open the mailbox state */
17535 if ((res = open_mailbox(&vms, vmu, old_folder_index)) < 0) {
17536 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17537 res = -1;
17538 goto vm_move_cleanup;
17539 }
17540
17541 open = 1;
17542
17543 if ((vms.lastmsg + 1) < num_msgs) {
17544 ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", oldfolder, num_msgs);
17545 res = -1;
17546 goto vm_move_cleanup;
17547 }
17548
17549 old_msg_nums = ast_alloca(sizeof(int) * num_msgs);
17550
17551 if ((res = message_range_and_existence_check(&vms, old_msg_ids, num_msgs, old_msg_nums, vmu)) < 0) {
17552 goto vm_move_cleanup;
17553 }
17554
17555 /* Now actually move the message */
17556 for (i = 0; i < num_msgs; ++i) {
17557 if (save_to_folder(vmu, &vms, old_msg_nums[i], new_folder_index, NULL, 0)) {
17558 res = -1;
17559 goto vm_move_cleanup;
17560 }
17561 vms.deleted[old_msg_nums[i]] = 1;
17562 }
17563
17564 /* close mailbox */
17565 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17566 res = -1;
17567 goto vm_move_cleanup;
17568 }
17569 open = 0;
17570
17571vm_move_cleanup:
17572 if (vmu && open) {
17573 close_mailbox(&vms, vmu);
17574 }
17575#ifdef IMAP_STORAGE
17576 if (vmu) {
17577 vmstate_delete(&vms);
17578 }
17579#endif
17580
17581 if (!res) {
17582 notify_new_state(vmu);
17583 }
17584
17585 free_user(vmu);
17586 return res;
17587}
17588
17589static int vm_msg_remove(const char *mailbox,
17590 const char *context,
17591 size_t num_msgs,
17592 const char *folder,
17593 const char *msgs[])
17594{
17595 struct vm_state vms;
17596 struct ast_vm_user *vmu = NULL, vmus;
17597 int folder_index;
17598 int open = 0;
17599 int res = 0;
17600 int i;
17601 int *msg_nums;
17602
17603 if (ast_strlen_zero(mailbox)) {
17604 ast_log(LOG_WARNING, "Cannot remove message because no mailbox was specified\n");
17605 return -1;
17606 }
17607
17608 if (!num_msgs) {
17609 ast_log(LOG_WARNING, "Invalid number of messages specified to remove: %zu\n", num_msgs);
17610 return -1;
17611 }
17612
17613 if (ast_strlen_zero(folder)) {
17614 ast_log(LOG_WARNING, "Cannot remove message because no folder was specified\n");
17615 return -1;
17616 }
17617
17618 memset(&vmus, 0, sizeof(vmus));
17619 memset(&vms, 0, sizeof(vms));
17620
17621 folder_index = get_folder_by_name(folder);
17622 if (folder_index == -1) {
17623 ast_log(LOG_WARNING, "Could not remove msgs from unknown folder %s\n", folder);
17624 return -1;
17625 }
17626
17627 if (!(vmu = find_user(&vmus, context, mailbox))) {
17628 ast_log(LOG_WARNING, "Can't find voicemail user to remove msg from (%s@%s)\n", mailbox, context);
17629 return -1;
17630 }
17631
17632 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17633 vms.lastmsg = -1;
17634 open = 0;
17635
17636 /* open the mailbox state */
17637 if ((res = open_mailbox(&vms, vmu, folder_index)) < 0) {
17638 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17639 res = -1;
17640 goto vm_remove_cleanup;
17641 }
17642
17643 open = 1;
17644
17645 if ((vms.lastmsg + 1) < num_msgs) {
17646 ast_log(LOG_WARNING, "Folder %s has less than %zu messages\n", folder, num_msgs);
17647 res = -1;
17648 goto vm_remove_cleanup;
17649 }
17650
17651 msg_nums = ast_alloca(sizeof(int) * num_msgs);
17652
17653 if ((res = message_range_and_existence_check(&vms, msgs, num_msgs, msg_nums, vmu)) < 0) {
17654 goto vm_remove_cleanup;
17655 }
17656
17657 for (i = 0; i < num_msgs; i++) {
17658 vms.deleted[msg_nums[i]] = 1;
17659 }
17660
17661 /* close mailbox */
17662 if ((res = close_mailbox(&vms, vmu) == ERROR_LOCK_PATH)) {
17663 res = -1;
17664 ast_log(AST_LOG_ERROR, "Failed to close mailbox folder %s while removing msgs\n", folder);
17665 goto vm_remove_cleanup;
17666 }
17667 open = 0;
17668
17669vm_remove_cleanup:
17670 if (vmu && open) {
17671 close_mailbox(&vms, vmu);
17672 }
17673#ifdef IMAP_STORAGE
17674 if (vmu) {
17675 vmstate_delete(&vms);
17676 }
17677#endif
17678
17679 if (!res) {
17680 notify_new_state(vmu);
17681 }
17682
17683 free_user(vmu);
17684 return res;
17685}
17686
17687static int vm_msg_play(struct ast_channel *chan,
17688 const char *mailbox,
17689 const char *context,
17690 const char *folder,
17691 const char *msg_id,
17693{
17694 struct vm_state vms;
17695 struct ast_vm_user *vmu = NULL, vmus;
17696 int res = 0;
17697 int open = 0;
17698 int i;
17699 char filename[PATH_MAX];
17700 struct ast_config *msg_cfg;
17701 struct ast_flags config_flags = { CONFIG_FLAG_NOCACHE };
17702 int duration = 0;
17703 const char *value;
17704
17705 if (ast_strlen_zero(mailbox)) {
17706 ast_log(LOG_WARNING, "Cannot play message because no mailbox was specified\n");
17707 return -1;
17708 }
17709
17710 if (ast_strlen_zero(folder)) {
17711 ast_log(LOG_WARNING, "Cannot play message because no folder was specified\n");
17712 return -1;
17713 }
17714
17715 if (ast_strlen_zero(msg_id)) {
17716 ast_log(LOG_WARNING, "Cannot play message because no message number was specified\n");
17717 return -1;
17718 }
17719
17720 memset(&vmus, 0, sizeof(vmus));
17721 memset(&vms, 0, sizeof(vms));
17722
17723 if (ast_strlen_zero(context)) {
17724 context = "default";
17725 }
17726
17727 if (!(vmu = find_user(&vmus, context, mailbox))) {
17728 return -1;
17729 }
17730
17731 i = get_folder_by_name(folder);
17732 ast_copy_string(vms.username, mailbox, sizeof(vms.username));
17733 vms.lastmsg = -1;
17734 if ((res = open_mailbox(&vms, vmu, i)) < 0) {
17735 ast_log(LOG_WARNING, "Could not open mailbox %s\n", mailbox);
17736 goto play2_msg_cleanup;
17737 }
17738 open = 1;
17739
17740 if (message_range_and_existence_check(&vms, &msg_id, 1, &vms.curmsg, vmu)) {
17741 res = -1;
17742 goto play2_msg_cleanup;
17743 }
17744
17745 /* Find the msg */
17746 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
17747 snprintf(filename, sizeof(filename), "%s.txt", vms.fn);
17748 RETRIEVE(vms.curdir, vms.curmsg, vmu->mailbox, vmu->context);
17749
17750 msg_cfg = ast_config_load(filename, config_flags);
17751 if (!msg_cfg || msg_cfg == CONFIG_STATUS_FILEINVALID) {
17752 DISPOSE(vms.curdir, vms.curmsg);
17753 res = -1;
17754 goto play2_msg_cleanup;
17755 }
17756 if ((value = ast_variable_retrieve(msg_cfg, "message", "duration"))) {
17757 duration = atoi(value);
17758 }
17759 ast_config_destroy(msg_cfg);
17760
17761#ifdef IMAP_STORAGE
17762 /*IMAP storage stores any prepended message from a forward
17763 * as a separate file from the rest of the message
17764 */
17765 if (!ast_strlen_zero(vms.introfn) && ast_fileexists(vms.introfn, NULL, NULL) > 0) {
17766 wait_file(chan, &vms, vms.introfn);
17767 }
17768#endif
17769 if (cb) {
17770 cb(chan, vms.fn, duration);
17771 } else if ((wait_file(chan, &vms, vms.fn)) < 0) {
17772 ast_log(AST_LOG_WARNING, "Playback of message %s failed\n", vms.fn);
17773 } else {
17774 res = 0;
17775 }
17776
17777 vms.heard[vms.curmsg] = 1;
17778
17779 /* cleanup configs and msg */
17780 DISPOSE(vms.curdir, vms.curmsg);
17781
17782play2_msg_cleanup:
17783 if (vmu && open) {
17784 close_mailbox(&vms, vmu);
17785 }
17786
17787#ifdef IMAP_STORAGE
17788 if (vmu) {
17789 vmstate_delete(&vms);
17790 }
17791#endif
17792
17793 if (!res) {
17794 notify_new_state(vmu);
17795 }
17796
17797 free_user(vmu);
17798 return res;
17799}
17800
17801/* This is a workaround so that menuselect displays a proper description
17802 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
17803 */
17804
17806 .support_level = AST_MODULE_SUPPORT_CORE,
17807 .load = load_module,
17808 .unload = unload_module,
17809 .reload = reload,
17810 .optional_modules = "res_adsi,res_smdi",
ADSI Support (built upon Caller*ID)
#define ADSI_KEY_APPS
Definition: adsi.h:109
int ast_adsi_display(unsigned char *buf, int page, int line, int just, int wrap, char *col1, char *col2)
Loads a line of info into the display.
Definition: adsi.c:274
int ast_adsi_load_soft_key(unsigned char *buf, int key, const char *llabel, const char *slabel, char *ret, int data)
Creates "load soft key" parameters.
Definition: adsi.c:296
#define ADSI_MSG_DOWNLOAD
Definition: adsi.h:33
int ast_adsi_data_mode(unsigned char *buf)
Puts CPE in data mode.
Definition: adsi.c:219
int ast_adsi_unload_session(struct ast_channel *chan)
Definition: adsi.c:87
#define ADSI_JUST_CENT
Definition: adsi.h:114
#define ADSI_KEY_SKT
Definition: adsi.h:117
#define ADSI_MSG_DISPLAY
Definition: adsi.h:32
int ast_adsi_begin_download(struct ast_channel *chan, char *service, unsigned char *fdn, unsigned char *sec, int version)
Definition: adsi.c:32
#define ADSI_JUST_LEFT
Definition: adsi.h:112
int ast_adsi_set_line(unsigned char *buf, int page, int line)
Sets the current line and page.
Definition: adsi.c:285
#define ADSI_COMM_PAGE
Definition: adsi.h:107
int ast_adsi_input_control(unsigned char *buf, int page, int line, int display, int format, int just)
Set input information.
Definition: adsi.c:318
int ast_adsi_input_format(unsigned char *buf, int num, int dir, int wrap, char *format1, char *format2)
Set input format.
Definition: adsi.c:329
int ast_adsi_load_session(struct ast_channel *chan, unsigned char *app, int ver, int data)
Check if scripts for a given app are already loaded. Version may be -1, if any version is okay,...
Definition: adsi.c:76
int ast_adsi_end_download(struct ast_channel *chan)
Definition: adsi.c:43
int ast_adsi_download_disconnect(unsigned char *buf)
Disconnects (and hopefully saves) a downloaded script.
Definition: adsi.c:208
int ast_adsi_available(struct ast_channel *chan)
Returns non-zero if Channel does or might support ADSI.
Definition: adsi.c:263
int ast_adsi_transmit_message(struct ast_channel *chan, unsigned char *msg, int msglen, int msgtype)
Definition: adsi.c:98
#define ADSI_DIR_FROM_LEFT
Definition: adsi.h:120
int ast_adsi_set_keys(unsigned char *buf, unsigned char *keys)
Set which soft keys should be displayed.
Definition: adsi.c:307
int ast_adsi_voice_mode(unsigned char *buf, int when)
Puts CPE in voice mode.
Definition: adsi.c:252
#define AST_MODULE
jack_status_t status
Definition: app_jack.c:146
const char * str
Definition: app_jack.c:147
enum queue_result id
Definition: app_queue.c:1667
ast_mutex_t lock
Definition: app_sla.c:331
struct sla_ringing_trunk * last
Definition: app_sla.c:332
#define VMBOX_STRING_DATA_FORMAT
static char * vm_check_password_shell(char *command, char *buf, size_t len)
static int vm_browse_messages_vi(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Vietnamese syntax for 'You have N messages' greeting.
static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box, int *newmsg, int move)
static char pagerfromstring[100]
static char vm_password[80]
static char vm_invalid_password[80]
#define VM_SEARCH
#define VM_ODBC_AUDIO_ON_DISK
static char * handle_voicemail_show_users(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail users in the CLI.
static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Default English syntax for 'You have N messages' greeting.
static char listen_control_reverse_key[12]
#define MAX_VM_CONTEXT_LEN
#define EXISTS(a, b, c, d)
static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
static void adsi_password(struct ast_channel *chan)
static char serveremail[80]
#define VM_SAYCID
static int get_folder(struct ast_channel *chan, int start)
get_folder: Folder menu Plays "press 1 for INBOX messages" etc. Should possibly be internationalized
#define VMSTATE_MAX_MSG_ARRAY
static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
Prompts the user and records a voicemail to a mailbox.
static void adsi_folders(struct ast_channel *chan, int start, char *label)
static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
#define VM_FWDURGAUTO
static int mwi_handle_unsubscribe2(void *data)
#define VM_SAYDURATION
#define VOICEMAIL_DIR_MODE
static int make_file(char *dest, const int len, const char *dir, const int num)
Creates a file system path expression for a folder within the voicemail data folder and the appropria...
static int manager_voicemail_refresh(struct mansession *s, const struct message *m)
static int inprocess_hash_fn(const void *obj, const int flags)
static struct ast_custom_function vm_info_acf
static int pwdchange
static const char * ast_str_encode_mime(struct ast_str **end, ssize_t maxlen, const char *start, size_t preamble, size_t postamble)
Encode a string according to the MIME rules for encoding strings that are not 7-bit clean or contain ...
static int show_messages_for_mailbox(struct ast_cli_args *a)
static int vm_msg_play(struct ast_channel *chan, const char *mailbox, const char *context, const char *folder, const char *msg_num, ast_vm_msg_play_cb cb)
#define VM_TEMPGREETWARN
static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap, const char *flag, const char *msg_id)
Creates the email file to be sent to indicate a new voicemail exists for a user.
static int manager_list_voicemail_users(struct mansession *s, const struct message *m)
Manager list voicemail users command.
static int skipms
#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY
static int vm_delete(char *file)
Removes the voicemail sound and information file.
static char mailcmd[160]
static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
static int msg_id_incrementor
#define ERROR_LOCK_PATH
static const struct ast_vm_functions vm_table
#define CHUNKSIZE
static void free_zone(struct vm_zone *z)
static int vmminsecs
static void adsi_begin(struct ast_channel *chan, int *useadsi)
static char * handle_voicemail_show_mailbox(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void read_password_from_file(const char *secretfn, char *password, int passwordlen)
static int say_and_wait(struct ast_channel *chan, int num, const char *language)
static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
Loads the options specific to a voicemail user.
static void populate_defaults(struct ast_vm_user *vmu)
Sets default voicemail system options to a voicemail user.
static struct ast_taskprocessor * mwi_subscription_tps
static int vm_browse_messages_zh(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Chinese (Taiwan)syntax for 'You have N messages' greeting.
#define VM_OPERATOR
static char vm_newpassword[80]
static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
static int vm_execmain(struct ast_channel *chan, const char *data)
static void free_user(struct ast_vm_user *vmu)
AO2_STRING_FIELD_CMP_FN(alias_mailbox_mapping, alias)
static int vm_play_folder_name_gr(struct ast_channel *chan, char *box)
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 invent_message(struct ast_channel *chan, char *context, char *ext, int busy, char *ecodes)
static int vm_instructions(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
#define UPDATE_MSG_ID(a, b, c, d, e, f)
static int vm_instructions_en(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
#define VM_STRING_HEADER_FORMAT
#define VM_MARK_URGENT
#define MAPPING_BUCKETS
static void generate_msg_id(char *dst)
Sets the destination string to a uniquely identifying msg_id string.
static struct ast_flags globalflags
static char listen_control_forward_key[12]
static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, int *sound_duration, const char *unlockdir, signed char record_gain, struct vm_state *vms, char *flag, const char *msg_id, int forwardintro)
static int vmauthenticate(struct ast_channel *chan, const char *data)
#define MSG_ID_LEN
static char vm_login[80]
static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
#define INTRO
#define VM_DIRECTFORWARD
static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Portuguese syntax for 'You have N messages' greeting.
static char vm_newuser[80]
#define VMBOX_STRING_HEADER_FORMAT
static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *attach, char *attach2, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, const char *flag, const char *msg_id)
static const char * ast_str_quote(struct ast_str **buf, ssize_t maxlen, const char *from)
Wraps a character sequence in double quotes, escaping occurences of quotes within the string.
static ast_mutex_t poll_lock
static int check_mime(const char *str)
Check if the string would need encoding within the MIME standard, to avoid confusing certain mail sof...
static const char * vm_index_to_foldername(int id)
static unsigned char adsisec[4]
static int play_message_category(struct ast_channel *chan, const char *category)
static struct ast_vm_user * find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
Finds a voicemail user from the realtime engine.
static const char * substitute_escapes(const char *value)
static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
Performs a change of the voicemail passowrd in the realtime engine.
static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
Resets a user password to a specified password.
static int count_messages(struct ast_vm_user *vmu, char *dir)
Find all .txt files - even if they are not in sequence from 0000.
#define VM_SKIPAFTERCMD
static pthread_t poll_thread
static char zonetag[80]
static int vm_intro_multilang(struct ast_channel *chan, struct vm_state *vms, const char message_gender[])
static int vm_exec(struct ast_channel *chan, const char *data)
static struct ast_vm_msg_snapshot * vm_msg_snapshot_alloc(void)
static char * pagersubject
static int manager_voicemail_forward(struct mansession *s, const struct message *m)
static int maxmsg
static char vmfmts[80]
static int manager_voicemail_move(struct mansession *s, const struct message *m)
#define COPY(a, b, c, d, e, f, g, h)
static void load_zonemessages(struct ast_config *cfg)
static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Top level method to invoke the language variant vm_browse_messages_XX function.
static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
static int vm_playmsgexec(struct ast_channel *chan, const char *data)
static int vm_intro_de(struct ast_channel *chan, struct vm_state *vms)
static int passwordlocation
static int get_folder_ja(struct ast_channel *chan, int start)
static char vm_mismatch[80]
static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
static const struct ast_app_option vm_app_options[128]
static char * voicemail_app
#define COUNT(a, b)
static char * playmsg_app
static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Spanish syntax for 'You have N messages' greeting.
static void notify_new_state(struct ast_vm_user *vmu)
static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
#define ASTERISK_USERNAME
static int vm_newuser_setup(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
static int valid_config(const struct ast_config *cfg)
Check if configuration file is valid.
#define OPERATOR_EXIT
static char vm_pls_try_again[80]
static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback, int saycidnumber)
#define DISPOSE(a, b)
static int write_password_to_file(const char *secretfn, const char *password)
static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
#define VM_PBXSKIP
static int append_vmbox_info_astman(struct mansession *s, const struct message *m, struct ast_vm_user *vmu, const char *event_name, const char *actionid)
Append vmbox info string into given astman with event_name.
static int msg_create_from_file(struct ast_vm_recording_data *recdata)
static char * complete_voicemail_show_mailbox(struct ast_cli_args *a)
#define VOICEMAIL_CONFIG
static const struct ast_vm_greeter_functions vm_greeter_table
static void rename_file(char *sfn, char *dfn)
Renames a message in a mailbox folder.
static int move_message_from_mailbox(struct ast_cli_args *a)
static struct alias_mailbox_mapping * alias_mailbox_mapping_create(const char *alias, const char *mailbox)
static struct ast_vm_mailbox_snapshot * vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot)
struct ast_mwi_observer mwi_observer
static char ext_pass_check_cmd[128]
static int vm_intro_pt_BR(struct ast_channel *chan, struct vm_state *vms)
static int append_vmu_info_astman(struct mansession *s, struct ast_vm_user *vmu, const char *event_name, const char *actionid)
Append vmu info string into given astman with event_name.
static char * pagerbody
static int separate_mailbox(char *mailbox_id, char **mailbox, char **context)
static int vm_msg_snapshot_create(struct ast_vm_user *vmu, struct vm_state *vms, struct ast_vm_mailbox_snapshot *mailbox_snapshot, int snapshot_index, int mailbox_index, int descending, enum ast_vm_snapshot_sort_val sort_val)
Create and store off all the msgs in an open mailbox.
static char dialcontext[AST_MAX_CONTEXT]
static int vmsayname_exec(struct ast_channel *chan, const char *data)
#define DEFAULT_POLL_FREQ
static int vm_intro_vi(struct ast_channel *chan, struct vm_state *vms)
#define VM_ENVELOPE
static int messagecount(const char *mailbox_id, const char *folder)
static int manager_voicemail_remove(struct mansession *s, const struct message *m)
static void stop_poll_thread(void)
AO2_STRING_FIELD_HASH_FN(alias_mailbox_mapping, alias)
#define MAXMSG
static char aliasescontext[MAX_VM_CONTEXT_LEN]
static int vm_sayname(struct ast_channel *chan, const char *mailbox_id)
#define DELETE(a, b, c, d)
static void print_mappings(void *v_obj, void *where, ao2_prnt_fn *prnt)
static struct ast_cli_entry cli_voicemail[]
#define DEFAULT_LISTEN_CONTROL_RESTART_KEY
static char listen_control_stop_key[12]
static char fromstring[100]
static int vm_browse_messages_ja(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Japanese syntax for 'You have N messages' greeting.
#define PWDCHANGE_INTERNAL
static char * handle_voicemail_forward_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static int vm_intro_cs(struct ast_channel *chan, struct vm_state *vms)
static void mwi_handle_unsubscribe(const char *id, struct ast_mwi_subscriber *sub)
static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
The handler for the change password option.
#define VM_FORCEGREET
#define VM_MESSAGEWRAP
static void * mb_poll_thread(void *data)
static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64]
static void run_externnotify(const char *context, const char *extension, const char *flag)
static int append_mailbox(const char *context, const char *box, const char *data)
static int vm_lock_path(const char *path)
Lock file path only return failure if ast_lock_path returns 'timeout', not if the path does not exist...
static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
static int vm_intro_nl(struct ast_channel *chan, struct vm_state *vms)
static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
#define DEFAULT_LISTEN_CONTROL_STOP_KEY
static int acf_vm_info(struct ast_channel *chan, const char *cmd, char *args, char *buf, size_t len)
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
static int vm_intro_zh(struct ast_channel *chan, struct vm_state *vms)
static char emaildateformat[32]
static char vm_reenterpassword[80]
static void adsi_login(struct ast_channel *chan)
static int manager_get_mailbox_summary(struct mansession *s, const struct message *m)
static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain)
The advanced options within a message.
#define MAX_VM_MAILBOX_LEN
static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
static int vm_intro_pt(struct ast_channel *chan, struct vm_state *vms)
static int check_password(struct ast_vm_user *vmu, char *password)
Check that password meets minimum required length.
static int play_message_by_id_helper(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, const char *msg_id)
static int load_config_force(int reload, int force)
Reload voicemail.conf.
static int saydurationminfo
#define VM_ATTACH
struct ao2_container * inprocess_container
static int vm_allocate_dh(struct vm_state *vms, struct ast_vm_user *vmu, int count_msg)
static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, const char *fromfolder, char *cidnum, char *cidname, char *dur, char *date, const char *category, const char *flag)
static char * sayname_app
static void free_user_final(struct ast_vm_user *vmu)
static int vm_instructions_ja(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int skipadvanced, int in_urgent, int nodelete)
static void load_users(struct ast_config *cfg)
static int message_range_and_existence_check(struct vm_state *vms, const char *msg_ids[], size_t num_msgs, int *msg_nums, struct ast_vm_user *vmu)
common bounds checking and existence check for Voicemail API functions.
static int get_folder2(struct ast_channel *chan, char *fn, int start)
plays a prompt and waits for a keypress.
static int vm_msg_forward(const char *from_mailbox, const char *from_context, const char *from_folder, const char *to_mailbox, const char *to_context, const char *to_folder, size_t num_msgs, const char *msg_ids[], int delete_old)
#define ENDL
static void adsi_goodbye(struct ast_channel *chan)
#define HVSU_OUTPUT_FORMAT
static int maxgreet
#define SENDMAIL
#define VM_FORCENAME
static char ext_pass_cmd[128]
static int is_valid_dtmf(const char *key)
Determines if a DTMF key entered is valid.
static char pagerdateformat[32]
static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
The handler for 'record a temporary greeting'.
static double volgain
static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg)
static int get_folder_by_name(const char *name)
static int adsiver
#define HVSZ_OUTPUT_FORMAT
static int maxlogins
static char * emailsubject
static int inprocess_count(const char *context, const char *mailbox, int delta)
static struct ast_vm_user * find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
Finds a voicemail user from the users file or the realtime engine.
static struct ao2_container * mailbox_alias_mappings
static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
Creates a file system path expression for a folder within the voicemail data folder and the appropria...
static void apply_options(struct ast_vm_user *vmu, const char *options)
Destructively Parse options and apply.
static int add_email_attachment(FILE *p, struct ast_vm_user *vmu, char *format, char *attach, char *greeting_attachment, char *mailbox, char *bound, char *filename, int last, int msgnum)
#define VM_MOVEHEARD
static char externnotify[160]
static int adsi_logo(unsigned char *buf)
static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
static unsigned char poll_thread_run
static char listen_control_restart_key[12]
static int add_message_id(struct ast_config *msg_cfg, char *dir, int msg, char *filename, char *id, size_t id_size, struct ast_vm_user *vmu, int folder)
static int play_message_by_id(struct ast_channel *chan, const char *mailbox, const char *context, const char *msg_id)
Finds a message in a specific mailbox by msg_id and plays it to the channel.
static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
basically mkdir -p $dest/$context/$ext/$folder
#define MAX_VM_MBOX_ID_LEN
static char * handle_voicemail_show_aliases(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Show a list of voicemail zones in the CLI.
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_USERSCONF
@ OPT_PWLOC_SPOOLDIR
@ OPT_PWLOC_VOICEMAILCONF
static char vm_prepend_timeout[80]
static char userscontext[AST_MAX_EXTENSION]
static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size, struct ast_vm_user *res_vmu, const char *context, const char *prefix, int skipuser, int max_logins, int silent)
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 int load_config(int reload)
static void free_vm_zones(void)
Free the zones structure.
static int reload(void)
static int mwi_handle_subscribe2(void *data)
static int forward_message(struct ast_channel *chan, char *context, struct vm_state *vms, struct ast_vm_user *sender, char *fmt, int is_new_message, signed char record_gain, int urgent)
Sends a voicemail message to a mailbox recipient.
#define VM_SVMAIL
static int vm_intro_es(struct ast_channel *chan, struct vm_state *vms)
#define PWDCHANGE_EXTERNAL
static struct ast_vm_user * find_or_create(const char *context, const char *box)
static char locale[20]
static unsigned char adsifdn[4]
static void queue_mwi_event(const char *channel_id, const char *box, int urgent, int new, int old)
static int manager_status_voicemail_user(struct mansession *s, const struct message *m)
#define VOICEMAIL_FILE_MODE
static int maxsilence
static char * handle_voicemail_remove_message(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define LAST_MSG_INDEX(a)
static void load_aliases(struct ast_config *cfg)
static int vmmaxsecs
#define tdesc
static struct ao2_container * alias_mailbox_mappings
static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
static char * addesc
static const struct ast_tm * vmu_tm(const struct ast_vm_user *vmu, struct ast_tm *tm)
fill in *tm for current time according to the proper timezone, if any.
static int vm_play_folder_name_ua(struct ast_channel *chan, char *box)
#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY
static char * show_users_realtime(int fd, const char *context)
static int remove_message_from_mailbox(struct ast_cli_args *a)
static int has_voicemail(const char *mailbox, const char *folder)
Determines if the given folder has messages.
static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msgnum, long duration, char *fmt, char *cidnum, char *cidname, const char *flag)
Sends email notification that a user has a new voicemail waiting for them.
#define VM_EMAIL_EXT_RECS
static int my_umask
static struct ast_vm_mailbox_snapshot * vm_mailbox_snapshot_create(const char *mailbox, const char *context, const char *folder, int descending, enum ast_vm_snapshot_sort_val sort_val, int combine_INBOX_and_OLD)
static int manager_match_mailbox(struct ast_mwi_state *mwi_state, void *data)
static int vm_intro_ja(struct ast_channel *chan, struct vm_state *vms)
#define MAXMSGLIMIT
#define ERROR_MAX_MSGS
static int inboxcount2(const char *mailbox, int *urgentmsgs, int *newmsgs, int *oldmsgs)
Check the given mailbox's message count.
static char exitcontext[AST_MAX_CONTEXT]
static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
static int vm_msg_move(const char *mailbox, const char *context, size_t num_msgs, const char *oldfolder, const char *old_msg_ids[], const char *newfolder)
static int sayname(struct ast_channel *chan, const char *mailbox, const char *context)
static int get_date(char *s, int len)
Gets the current date and time, as formatted string.
#define VM_REVIEW
static const char *const mailbox_folders[]
#define VM_ALLOCED
static int show_mailbox_details(struct ast_cli_args *a)
static const char * mbox(struct ast_vm_user *vmu, int id)
static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir, const char *flag, const char *dest_folder)
Copies a message from one mailbox to another.
static char * vmauthenticate_app
#define MSGFILE_LEN
static int vm_play_folder_name_ja(struct ast_channel *chan, char *box)
#define force_reload_config()
Forcibly reload voicemail.conf, even if it has not changed. This is necessary after running unit test...
static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
Sets a specific property value.
static char vm_passchanged[80]
static struct ast_vm_msg_snapshot * vm_msg_snapshot_destroy(struct ast_vm_msg_snapshot *msg_snapshot)
static char * voicemailmain_app
#define ALIASES_OUTPUT_FORMAT
static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
Greek syntax for 'You have N messages' greeting.
static struct ast_smdi_interface * smdi_iface
static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
static void free_vm_users(void)
Free the users structure.
static int vm_intro_no(struct ast_channel *chan, struct vm_state *vms)
#define var
Definition: ast_expr2f.c:605
if(!yyg->yy_init)
Definition: ast_expr2f.c:854
static int input(yyscan_t yyscanner)
Definition: ast_expr2f.c:1570
Asterisk main include file. File version handling, generic pbx functions.
#define PATH_MAX
Definition: asterisk.h:40
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: astmm.h:288
#define ast_free(a)
Definition: astmm.h:180
#define ast_realloc(p, len)
A wrapper for realloc()
Definition: astmm.h:226
#define ast_strdup(str)
A wrapper for strdup()
Definition: astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition: astmm.h:267
#define ast_calloc(num, len)
A wrapper for calloc()
Definition: astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition: astmm.h:191
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ CMP_MATCH
Definition: astobj2.h:1027
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_callback(c, flags, cb_fn, arg)
ao2_callback() is a generic function that applies cb_fn() to all objects in a container,...
Definition: astobj2.h:1693
void ao2_container_unregister(const char *name)
Unregister a container for CLI stats and integrity check.
#define ao2_cleanup(obj)
Definition: astobj2.h:1934
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_unlock(a)
Definition: astobj2.h:729
#define ao2_lock(a)
Definition: astobj2.h:717
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
int ao2_container_register(const char *name, struct ao2_container *self, ao2_prnt_obj_fn *prnt_obj)
Register a container for CLI stats and integrity check.
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_NODATA
Definition: astobj2.h:1044
@ OBJ_MULTIPLE
Definition: astobj2.h:1049
@ OBJ_UNLINK
Definition: astobj2.h:1039
@ OBJ_SEARCH_KEY
The arg parameter is a search key, but is not an object.
Definition: astobj2.h:1101
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
void() ao2_prnt_fn(void *where, const char *fmt,...)
Print output.
Definition: astobj2.h:1435
static int tmp()
Definition: bt_open.c:389
char * ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown)
Definition: callerid.c:1273
int ast_callerid_parse(char *instr, char **name, char **location)
Destructively parse inbuf into name and location (or number)
Definition: callerid.c:1162
int ast_callerid_split(const char *src, char *name, int namelen, char *num, int numlen)
Definition: callerid.c:1292
enum cc_state state
Definition: ccss.c:393
static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
static int priority
static char language[MAX_LANGUAGE]
Definition: chan_iax2.c:324
static const char type[]
Definition: chan_ooh323.c:109
static int transfer(void *data)
Definition: chan_pjsip.c:2132
charset
Definition: chan_unistim.c:336
General Asterisk PBX channel definitions.
void ast_channel_exten_set(struct ast_channel *chan, const char *value)
int ast_waitfordigit(struct ast_channel *c, int ms)
Waits for a digit.
Definition: channel.c:3194
const char * ast_channel_name(const struct ast_channel *chan)
void ast_hangup(struct ast_channel *chan)
Hang up a channel.
Definition: channel.c:2560
#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:2968
struct ast_party_redirecting * ast_channel_redirecting(struct ast_channel *chan)
int ast_channel_priority(const struct ast_channel *chan)
const char * ast_channel_uniqueid(const struct ast_channel *chan)
const char * ast_channel_context(const struct ast_channel *chan)
@ AST_ADSI_UNAVAILABLE
Definition: channel.h:891
void ast_channel_set_rawreadformat(struct ast_channel *chan, struct ast_format *format)
#define MAX_LANGUAGE
Definition: channel.h:174
int ast_check_hangup(struct ast_channel *chan)
Check to see if a channel is needing hang up.
Definition: channel.c:445
void ast_channel_set_rawwriteformat(struct ast_channel *chan, struct ast_format *format)
void ast_channel_set_readformat(struct ast_channel *chan, struct ast_format *format)
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:3004
#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:7444
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:6577
int ast_answer(struct ast_channel *chan)
Answer a channel.
Definition: channel.c:2824
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:4296
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:2969
#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:1997
ast_channel_state
ast_channel states
Definition: channelstate.h:35
@ AST_STATE_DOWN
Definition: channelstate.h:36
@ AST_STATE_UP
Definition: channelstate.h:42
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition: cli.h:45
#define CLI_SUCCESS
Definition: cli.h:44
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: clicompat.c:30
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition: clicompat.c:6
#define RESULT_SUCCESS
Definition: cli.h:40
#define ESS(x)
Definition: cli.h:59
@ CLI_HANDLER
Definition: cli.h:154
@ CLI_INIT
Definition: cli.h:152
@ CLI_GENERATE
Definition: cli.h:153
#define CLI_FAILURE
Definition: cli.h:46
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition: cli.h:265
short word
#define SENTINEL
Definition: compiler.h:87
static struct progalias aliases[]
Convenient Signal Processing routines.
@ THRESHOLD_SILENCE
Definition: dsp.h:73
int ast_dsp_get_threshold_from_settings(enum threshold which)
Get silence threshold from dsp.conf.
Definition: dsp.c:2009
char * end
Definition: eagi_proxy.c:73
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static char * config_filename
Definition: extconf.c:2120
long int flag
Definition: f2c.h:83
Generic File Format Support. Should be included by clients of the file handling routines....
off_t ast_tellstream(struct ast_filestream *fs)
Tell where we are in a stream.
Definition: file.c:1085
int ast_stopstream(struct ast_channel *c)
Stops a stream.
Definition: file.c:222
int ast_seekstream(struct ast_filestream *fs, off_t sample_offset, int whence)
Seeks into stream.
Definition: file.c:1075
int ast_filerename(const char *oldname, const char *newname, const char *fmt)
Renames a file.
Definition: file.c:1146
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:1371
int ast_streamfile(struct ast_channel *c, const char *filename, const char *preflang)
Streams a file.
Definition: file.c:1293
FILE * ast_file_mkftemp(char *template_name, mode_t mode)
same as mkstemp, but return a FILE
Definition: file.c:187
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition: file.c:1878
char * ast_format_str_reduce(char *fmts)
Definition: file.c:1894
int ast_filecopy(const char *oldname, const char *newname, const char *fmt)
Copies a file.
Definition: file.c:1151
int ast_ratestream(struct ast_filestream *fs)
Return the sample rate of the stream's format.
Definition: file.c:1090
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:1111
int ast_fileexists(const char *filename, const char *fmt, const char *preflang)
Checks for the existence of a given file.
Definition: file.c:1129
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition: file.c:1141
#define AST_DIGIT_ANY
Definition: file.h:48
int ast_waitstream(struct ast_channel *c, const char *breakon)
Waits for a stream to stop or digit to be pressed.
Definition: file.c:1840
Media Format Cache API.
struct ast_format * ast_format_gsm
Built-in cached gsm format.
Definition: format_cache.c:96
@ AST_FORMAT_CAP_FLAG_DEFAULT
Definition: format_cap.h:38
#define ast_format_cap_append(cap, format, framing)
Add format capability to capabilities structure.
Definition: format_cap.h:99
#define ast_format_cap_alloc(flags)
Allocate a new ast_format_cap structure.
Definition: format_cap.h:49
static const char name[]
Definition: format_mp3.c:68
static int exists(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
Definition: func_logic.c:157
static struct ast_threadstorage buf2
static struct ast_threadstorage buf1
static int quote(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void astman_send_listack(struct mansession *s, const struct message *m, char *msg, char *listflag)
Send ack in manager transaction to begin a list.
Definition: manager.c:2011
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:1969
void ast_manager_publish_event(const char *type, int class_type, struct ast_json *obj)
Publish an event to AMI.
Definition: manager.c:637
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:2047
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
Send ack in manager transaction.
Definition: manager.c:2001
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition: manager.c:1630
void astman_send_list_complete_end(struct mansession *s)
End the list complete event.
Definition: manager.c:2055
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:1890
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition: manager.c:7608
#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,...)
#define ERROR
Definition: hash.c:85
static ENTRY retval
Definition: hsearch.c:50
static char prefix[MAX_PREFIX]
Definition: http.c:144
const char * ext
Definition: http.c:150
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
int ast_play_and_record_full(struct ast_channel *chan, const char *playfile, const char *recordfile, int maxtime_sec, const char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence_ms, const char *path, const char *acceptdtmf, const char *canceldtmf, int skip_confirmation_sound, enum ast_record_if_exists if_exists)
Record a file based on input from a channel This function will play "auth-thankyou" upon successful r...
Definition: main/app.c:2149
int ast_app_messagecount(const char *mailbox_id, const char *folder)
Get the number of messages in a given mailbox folder.
Definition: main/app.c:645
int ast_control_streamfile(struct ast_channel *chan, const char *file, const char *fwd, const char *rev, const char *stop, const char *pause, const char *restart, int skipms, long *offsetms)
Stream a file with fast forward, pause, reverse, restart.
Definition: main/app.c:1465
@ AST_LOCK_TIMEOUT
void ast_vm_greeter_unregister(const char *module_name)
Unregister the specified voicemail greeter provider.
Definition: main/app.c:511
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
enum ast_getdata_result ast_app_getdata(struct ast_channel *c, const char *prompt, char *s, int maxlen, int timeout)
Plays a stream and gets DTMF data from a channel.
Definition: main/app.c:188
ast_vm_snapshot_sort_val
@ AST_VM_SNAPSHOT_SORT_BY_ID
@ AST_VM_SNAPSHOT_SORT_BY_TIME
int ast_play_and_prepend(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime_sec, char *fmt, int *duration, int *sound_duration, int beep, int silencethreshold, int maxsilence_ms)
Record a file based on input frm a channel. Recording is performed in 'prepend' mode which works a li...
Definition: main/app.c:2159
enum AST_LOCK_RESULT ast_lock_path(const char *path)
Lock a filesystem path.
Definition: main/app.c:2614
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
int ast_app_has_voicemail(const char *mailboxes, const char *folder)
Determine if a given mailbox has any voicemail If folder is NULL, defaults to "INBOX"....
Definition: main/app.c:582
#define VM_GREETER_MODULE_VERSION
int ast_safe_fork(int stop_reaper)
Common routine to safely fork without a chance of a signal handler firing badly in the child.
Definition: main/app.c:3207
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define VM_MODULE_VERSION
struct ast_vm_mailbox_snapshot * ast_vm_mailbox_snapshot_destroy(struct ast_vm_mailbox_snapshot *mailbox_snapshot)
destroy a snapshot
Definition: main/app.c:675
int ast_safe_system(const char *s)
Safely spawn an OS shell command while closing file descriptors.
Definition: extconf.c:829
void ast_vm_unregister(const char *module_name)
Unregister the specified voicemail provider.
Definition: main/app.c:400
#define ast_vm_register(vm_table)
See __ast_vm_register()
int ast_app_inboxcount(const char *mailboxes, int *newmsgs, int *oldmsgs)
Determine number of new/old messages in a mailbox.
Definition: main/app.c:604
int ast_play_and_wait(struct ast_channel *chan, const char *fn)
Play a stream and wait for a digit, returning the digit that was pressed.
Definition: main/app.c:1616
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
void() ast_vm_msg_play_cb(struct ast_channel *chan, const char *playfile, int duration)
Voicemail playback callback function definition.
@ AST_RECORD_IF_EXISTS_OVERWRITE
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the 'nonstandard' argument separation process for an application.
int ast_app_inboxcount2(const char *mailboxes, int *urgentmsgs, int *newmsgs, int *oldmsgs)
Determine number of urgent/new/old messages in a mailbox.
Definition: main/app.c:619
#define ast_vm_greeter_register(vm_table)
See __ast_vm_greeter_register()
struct ast_vm_mailbox_snapshot * ast_vm_mailbox_snapshot_create(const char *mailbox, const char *context, const char *folder, int descending, enum ast_vm_snapshot_sort_val sort_val, int combine_INBOX_and_OLD)
Create a snapshot of a mailbox which contains information about every msg.
Definition: main/app.c:661
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition: main/app.c:3066
int ast_unlock_path(const char *path)
Unlock a path.
Definition: main/app.c:2630
void ast_close_fds_above_n(int n)
Common routine for child processes, to close all fds prior to exec(2)
Definition: main/app.c:3202
char * strsep(char **str, const char *delims)
char * mkdtemp(char *template_s)
char * strcasestr(const char *, const char *)
Configuration File Parser.
#define ast_config_load(filename, flags)
Load a config file.
char * ast_category_browse(struct ast_config *config, const char *prev_name)
Browse categories.
Definition: extconf.c:3326
int ast_update2_realtime(const char *family,...) attribute_sentinel
Update realtime configuration.
Definition: main/config.c:3710
struct ast_config * ast_config_new(void)
Create a new base configuration structure.
Definition: extconf.c:3274
int ast_realtime_require_field(const char *family,...) attribute_sentinel
Inform realtime what fields that may be stored.
Definition: main/config.c:3564
struct ast_config * ast_load_realtime_multientry(const char *family,...) attribute_sentinel
Retrieve realtime configuration.
Definition: main/config.c:3637
int ast_config_text_file_save(const char *filename, const struct ast_config *cfg, const char *generator)
Save a config text file preserving the pre 13.2 behavior.
Definition: main/config.c:2716
void ast_category_append(struct ast_config *config, struct ast_category *category)
Appends a category to a config.
Definition: extconf.c:2833
void ast_variable_append(struct ast_category *category, struct ast_variable *variable)
Definition: extconf.c:1177
int ast_variable_update(struct ast_category *category, const char *variable, const char *value, const char *match, unsigned int object)
Update variable value within a config.
Definition: main/config.c:1542
struct ast_category * ast_category_new(const char *name, const char *in_file, int lineno)
Create a category.
Definition: extconf.c:2788
#define ast_variable_new(name, value, filename)
int ast_unload_realtime(const char *family)
Release any resources cached for a realtime family.
Definition: main/config.c:3591
#define CONFIG_STATUS_FILEUNCHANGED
#define CONFIG_STATUS_FILEINVALID
int ast_check_realtime(const char *family)
Check if realtime engine is configured for family.
Definition: main/config.c:3545
int ast_destroy_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Destroy realtime configuration.
Definition: main/config.c:3791
void ast_category_destroy(struct ast_category *cat)
Definition: extconf.c:2845
void ast_config_destroy(struct ast_config *cfg)
Destroys a config.
Definition: extconf.c:1289
const char * ast_variable_retrieve(struct ast_config *config, const char *category, const char *variable)
Definition: main/config.c:784
int ast_update_realtime(const char *family, const char *keyfield, const char *lookup,...) attribute_sentinel
Update realtime configuration.
Definition: main/config.c:3674
int ast_store_realtime(const char *family,...) attribute_sentinel
Create realtime configuration.
Definition: main/config.c:3755
const char * ast_config_option(struct ast_config *cfg, const char *cat, const char *var)
Retrieve a configuration variable within the configuration set.
Definition: main/config.c:774
struct ast_variable * ast_load_realtime(const char *family,...) attribute_sentinel
Definition: main/config.c:3521
void ast_variables_destroy(struct ast_variable *var)
Free variable list.
Definition: extconf.c:1262
struct ast_variable * ast_variable_browse(const struct ast_config *config, const char *category_name)
Definition: extconf.c:1215
struct ast_category * ast_category_get(const struct ast_config *config, const char *category_name, const char *filter)
Retrieve a category if it exists.
Definition: main/config.c:1120
@ CONFIG_FLAG_NOCACHE
@ CONFIG_FLAG_WITHCOMMENTS
@ CONFIG_FLAG_FILEUNCHANGED
#define AST_OPTION_RXGAIN
@ AST_CONTROL_PROGRESS
struct ast_frame ast_null_frame
Definition: main/frame.c:79
Support for logging to various files, console and syslog Configuration in file logger....
#define AST_LOG_WARNING
#define AST_LOG_ERROR
#define DEBUG_ATLEAST(level)
#define ast_debug(level,...)
Log a DEBUG message.
#define LOG_DEBUG
#define LOG_ERROR
#define AST_LOG_NOTICE
#define ast_verb(level,...)
#define LOG_NOTICE
#define LOG_WARNING
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition: json.c:73
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition: json.c:612
#define AST_LIST_HEAD_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:291
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
Definition: linkedlists.h:491
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
Definition: linkedlists.h:450
#define AST_LIST_INSERT_TAIL(head, elm, field)
Appends a list entry to the tail of a list.
Definition: linkedlists.h:731
#define AST_LIST_HEAD_NOLOCK_STATIC(name, type)
Defines a structure to be used to hold a list of specified type, statically initialized.
Definition: linkedlists.h:346
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
Definition: linkedlists.h:410
#define AST_LIST_TRAVERSE_SAFE_END
Closes a safe loop traversal block.
Definition: linkedlists.h:615
#define AST_LIST_INSERT_BEFORE_CURRENT(elm, field)
Inserts a list entry before the current entry during a traversal.
Definition: linkedlists.h:599
#define AST_LIST_LOCK(head)
Locks a list.
Definition: linkedlists.h:40
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
Definition: linkedlists.h:711
#define AST_LIST_TRAVERSE_SAFE_BEGIN(head, var, field)
Loops safely over (traverses) the entries in a list.
Definition: linkedlists.h:529
#define AST_LIST_REMOVE_CURRENT(field)
Removes the current entry from a list during a traversal.
Definition: linkedlists.h:557
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
Definition: linkedlists.h:833
#define AST_LIST_UNLOCK(head)
Attempts to unlock a list.
Definition: linkedlists.h:140
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:439
Custom localtime functions for multiple timezones.
struct ast_tm * ast_localtime(const struct timeval *timep, struct ast_tm *p_tm, const char *zone)
Timezone-independent version of localtime_r(3).
Definition: localtime.c:1739
int ast_strftime(char *buf, size_t len, const char *format, const struct ast_tm *tm)
Special version of strftime(3) that handles fractions of a second. Takes the same arguments as strfti...
Definition: localtime.c:2524
int ast_strftime_locale(char *buf, size_t len, const char *format, const struct ast_tm *tm, const char *locale)
Definition: localtime.c:2452
Asterisk locking-related definitions:
#define AST_PTHREADT_NULL
Definition: lock.h:66
#define ast_cond_timedwait(cond, mutex, time)
Definition: lock.h:206
#define ast_mutex_init(pmutex)
Definition: lock.h:186
#define ast_mutex_unlock(a)
Definition: lock.h:190
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:757
pthread_cond_t ast_cond_t
Definition: lock.h:178
#define ast_mutex_destroy(a)
Definition: lock.h:188
#define ast_mutex_lock(a)
Definition: lock.h:189
#define AST_MUTEX_DEFINE_STATIC(mutex)
Definition: lock.h:520
#define ast_cond_signal(cond)
Definition: lock.h:203
size_t current
Definition: main/cli.c:113
int errno
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define EVENT_FLAG_REPORTING
Definition: manager.h:84
#define EVENT_FLAG_SYSTEM
Definition: manager.h:75
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:191
#define EVENT_FLAG_CALL
Definition: manager.h:76
#define EVENT_FLAG_USER
Definition: manager.h:81
Asterisk module definitions.
@ AST_MODFLAG_DEFAULT
Definition: module.h:329
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition: module.h:557
@ AST_MODULE_SUPPORT_CORE
Definition: module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx_app.c:392
@ AST_MODULE_LOAD_SUCCESS
Definition: module.h:70
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:640
Asterisk MWI API.
#define ast_publish_mwi_state_channel(mailbox, context, new_msgs, old_msgs, channel_id)
Publish a MWI state update associated with some channel.
Definition: mwi.h:395
void ast_mwi_state_callback_all(on_mwi_state handler, void *data)
For each managed mailbox call the given handler.
Definition: mwi.c:338
void ast_mwi_state_callback_subscribed(on_mwi_state handler, void *data)
For each managed mailbox that has a subscriber call the given handler.
Definition: mwi.c:348
int ast_mwi_add_observer(struct ast_mwi_observer *observer)
Add an observer to receive MWI state related events.
Definition: mwi.c:301
int ast_delete_mwi_state_full(const char *mailbox, const char *context, struct ast_eid *eid)
Delete MWI state cached by stasis with all parameters.
Definition: mwi.c:404
void ast_mwi_remove_observer(struct ast_mwi_observer *observer)
Remove an MWI state observer.
Definition: mwi.c:307
struct ast_mwi_state * ast_mwi_subscriber_data(struct ast_mwi_subscriber *sub)
Retrieves the state data object associated with the MWI subscriber.
Definition: mwi.c:269
def info(msg)
def from_mailbox(key, val, section, pjsip, nmapped)
#define MAXHOSTNAMELEN
Definition: network.h:69
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_DATA_DIR
Definition: options.c:158
const char * ast_config_AST_SPOOL_DIR
Definition: options.c:154
Core PBX routines and definitions.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
void ast_str_substitute_variables(struct ast_str **buf, ssize_t maxlen, struct ast_channel *chan, const char *templ)
int ast_exists_extension(struct ast_channel *c, const char *context, const char *exten, int priority, const char *callerid)
Determine whether an extension exists.
Definition: pbx.c:4175
int pbx_exec(struct ast_channel *c, struct ast_app *app, const char *data)
Execute an application.
Definition: pbx_app.c:471
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
int ast_goto_if_exists(struct ast_channel *chan, const char *context, const char *exten, int priority)
Definition: pbx.c:8781
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:4190
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
struct ast_app * pbx_findapp(const char *app)
Look up an application.
Definition: ael_main.c:165
static char email[80]
Definition: pbx_dundi.c:206
static void * cleanup(void *unused)
Definition: pbx_realtime.c:124
static char intro[ADSI_MAX_INTRO][20]
Definition: res_adsi.c:65
static struct stasis_rest_handlers mailboxes
REST handler for /api-docs/mailboxes.json.
static char user[512]
struct stasis_forward * sub
Definition: res_corosync.c:240
ODBC resource manager.
int ast_odbc_prepare(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Prepares a SQL query on a statement.
Definition: res_odbc.c:454
void ast_odbc_release_obj(struct odbc_obj *obj)
Releases an ODBC object previously allocated by ast_odbc_request_obj()
Definition: res_odbc.c:804
struct ast_str * ast_odbc_print_errors(SQLSMALLINT handle_type, SQLHANDLE handle, const char *operation)
Shortcut for printing errors to logs after a failed SQL operation.
Definition: res_odbc.c:515
SQLHSTMT ast_odbc_direct_execute(struct odbc_obj *obj, SQLHSTMT(*exec_cb)(struct odbc_obj *obj, void *data), void *data)
Executes an non prepared statement and returns the resulting statement handle.
Definition: res_odbc.c:360
SQLRETURN ast_odbc_execute_sql(struct odbc_obj *obj, SQLHSTMT *stmt, const char *sql)
Execute a unprepared SQL query.
Definition: res_odbc.c:469
SQLHSTMT ast_odbc_prepare_and_execute(struct odbc_obj *obj, SQLHSTMT(*prepare_cb)(struct odbc_obj *obj, void *data), void *data)
Prepares, executes, and returns the resulting statement handle.
Definition: res_odbc.c:398
#define ast_odbc_request_obj(name, check)
Get a ODBC connection object.
Definition: res_odbc.h:120
static int debug
Global debug status.
Definition: res_xmpp.c:441
#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:8293
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:8257
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:8281
SMDI support for Asterisk.
struct ast_smdi_interface * ast_smdi_interface_find(const char *iface_name)
Find an SMDI interface with the specified name.
Definition: res_smdi.c:563
int ast_smdi_mwi_set(struct ast_smdi_interface *iface, const char *mailbox)
Set the MWI indicator for a mailbox.
Definition: res_smdi.c:309
struct ast_smdi_mwi_message * ast_smdi_mwi_message_wait_station(struct ast_smdi_interface *iface, int timeout, const char *station)
Definition: res_smdi.c:556
int ast_smdi_mwi_unset(struct ast_smdi_interface *iface, const char *mailbox)
Unset the MWI indicator for a mailbox.
Definition: res_smdi.c:314
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
Definition: stringfields.h:521
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
Definition: stringfields.h:359
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
Definition: stringfields.h:374
String manipulation functions.
int ast_str_append(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Append to a thread local dynamic string.
Definition: strings.h:1139
char * ast_str_buffer(const struct ast_str *buf)
Returns the string buffer within the ast_str buf.
Definition: strings.h:761
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition: strings.h:80
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition: utils.c:2199
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition: strings.h:87
int ast_get_time_t(const char *src, time_t *dst, time_t _default, int *consumed)
Parse a time (integer) string.
Definition: utils.c:2446
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
int ast_build_string(char **buffer, size_t *space, const char *fmt,...)
Build a string in a buffer, designed to be called repeatedly.
Definition: utils.c:2167
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"....
Definition: utils.c:2216
#define ast_str_alloca(init_len)
Definition: strings.h:848
void ast_str_reset(struct ast_str *buf)
Reset the content of a dynamic string. Useful before a series of ast_str_append.
Definition: strings.h:693
#define ast_str_create(init_len)
Create a malloc'ed dynamic length string.
Definition: strings.h:659
int ast_str_set(struct ast_str **buf, ssize_t max_len, const char *fmt,...)
Set a dynamic string using variable arguments.
Definition: strings.h:1113
size_t ast_str_strlen(const struct ast_str *buf)
Returns the current length of the string stored within buf.
Definition: strings.h:730
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
struct ast_str * ast_str_thread_get(struct ast_threadstorage *ts, size_t init_len)
Retrieve a thread locally stored dynamic string.
Definition: strings.h:909
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
ast_app: A registered application
Definition: pbx_app.c:45
Structure to describe a channel "technology", ie a channel driver See for examples:
Definition: channel.h:648
int(*const write)(struct ast_channel *chan, struct ast_frame *frame)
Write a frame, in standard format (see frame.h)
Definition: channel.h:770
struct ast_format_cap * capabilities
Definition: channel.h:652
Main Channel structure associated with a channel.
descriptor for a cli entry.
Definition: cli.h:171
char * command
Definition: cli.h:186
const char * usage
Definition: cli.h:177
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Definition: mod_format.h:101
Structure used to handle boolean flags.
Definition: utils.h:199
unsigned int flags
Definition: utils.h:200
Format capabilities structure, holds formats + preference order + etc.
Definition: format_cap.c:54
Data structure associated with a single frame of data.
Abstract JSON element (object, array, string, int, ...).
Structure for mutex and tracking information.
Definition: lock.h:135
MWI state event interface.
Definition: mwi.h:248
void(* on_subscribe)(const char *mailbox, struct ast_mwi_subscriber *sub)
Raised when MWI is being subscribed.
Definition: mwi.h:255
The structure that contains MWI state.
Definition: mwi.h:455
int urgent_msgs
Definition: mwi.h:464
int old_msgs
Definition: mwi.h:460
int new_msgs
Definition: mwi.h:459
const ast_string_field uniqueid
Definition: mwi.h:458
Caller Party information.
Definition: channel.h:420
struct ast_party_id id
Caller party ID.
Definition: channel.h:422
struct ast_party_name name
Subscriber name.
Definition: channel.h:342
struct ast_party_number number
Subscriber phone number.
Definition: channel.h:344
unsigned char valid
TRUE if the name information is valid/present.
Definition: channel.h:281
char * str
Subscriber name (Malloced)
Definition: channel.h:266
unsigned char valid
TRUE if the number information is valid/present.
Definition: channel.h:299
char * str
Subscriber phone number (Malloced)
Definition: channel.h:293
An SMDI message waiting indicator message.
Definition: smdi.h:51
char cause[SMDI_MWI_FAIL_CAUSE_LEN+1]
Definition: smdi.h:54
char fwd_st[SMDI_MAX_STATION_NUM_LEN+1]
Definition: smdi.h:53
Support for dynamic strings.
Definition: strings.h:623
A ast_taskprocessor structure is a singleton by name.
Definition: taskprocessor.c:69
Structure for variables, used for configurations and for channel variables.
Voicemail function table definition.
unsigned int module_version
The version of this function table.
const char * module_name
The name of the module that provides the voicemail functionality.
Voicemail greeter function table definition.
unsigned int module_version
The version of this function table.
const char * module_name
The name of the module that provides the voicemail greeter functionality.
struct ast_vm_mailbox_snapshot::@185 * snapshots
const ast_string_field origtime
struct ast_vm_msg_snapshot::@184 msg
const ast_string_field msg_id
Structure used for ast_copy_recording_to_vm in order to cleanly supply data needed for making the rec...
const ast_string_field recording_file
const ast_string_field call_callerchan
const ast_string_field context
const ast_string_field folder
const ast_string_field call_callerid
const ast_string_field call_context
const ast_string_field recording_ext
const ast_string_field call_extension
const ast_string_field mailbox
char mailbox[MAX_VM_MBOX_ID_LEN]
char exit[80]
char fromstring[100]
char pager[80]
char * emailbody
int passwordlocation
char * emailsubject
char callback[80]
struct ast_vm_user::@82 list
char password[80]
char uniqueid[80]
char serveremail[80]
char attachfmt[20]
double volgain
char zonetag[80]
char locale[20]
char context[MAX_VM_CONTEXT_LEN]
char language[MAX_LANGUAGE]
unsigned int flags
char dialout[80]
char fullname[80]
All configuration options for http media cache.
structure to hold extensions
char mailbox[0]
char * context
Options for leaving voicemail with the voicemail() application.
Definition: app_minivm.c:654
signed char record_gain
Definition: app_minivm.c:656
unsigned int flags
Definition: app_minivm.c:655
In case you didn't read that giant block of text above the mansession_session struct,...
Definition: manager.c:326
Number structure.
Definition: app_followme.c:154
ODBC container.
Definition: res_odbc.h:46
struct odbc_obj * next
Definition: res_odbc.h:55
SQLHDBC con
Definition: res_odbc.h:47
structure to hold users read from users.conf
list of users found in the config file
Definition: ast_expr2.c:325
int urgentmessages
char username[80]
char intro[PATH_MAX]
char curdir[PATH_MAX]
int newmessages
char context[80]
char curbox[80]
int * deleted
char fn[PATH_MAX]
int * heard
int dh_arraysize
char vmbox[PATH_MAX]
int oldmessages
struct vm_zone::@83 list
char name[80]
char timezone[80]
char msg_format[512]
int value
Definition: syslog.c:37
An API for managing task processing threads that can be shared across modules.
struct ast_taskprocessor * ast_taskprocessor_get(const char *name, enum ast_tps_options create)
Get a reference to a taskprocessor with the specified name and create the taskprocessor if necessary.
void * ast_taskprocessor_unreference(struct ast_taskprocessor *tps)
Unreference the specified taskprocessor and its reference count will decrement.
int ast_taskprocessor_push(struct ast_taskprocessor *tps, int(*task_exe)(void *datap), void *datap) attribute_warn_unused_result
Push a task into the specified taskprocessor queue and signal the taskprocessor thread.
#define AST_TASKPROCESSOR_HIGH_WATER_LEVEL
Definition: taskprocessor.h:64
int ast_taskprocessor_alert_set_levels(struct ast_taskprocessor *tps, long low_water, long high_water)
Set the high and low alert water marks of the given taskprocessor queue.
Test Framework API.
@ TEST_INIT
Definition: test.h:200
@ TEST_EXECUTE
Definition: test.h:201
#define AST_TEST_REGISTER(cb)
Definition: test.h:127
#define ast_test_status_update(a, b, c...)
Definition: test.h:129
#define AST_TEST_UNREGISTER(cb)
Definition: test.h:128
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:189
#define AST_TEST_DEFINE(hdr)
Definition: test.h:126
@ AST_TEST_PASS
Definition: test.h:195
@ AST_TEST_FAIL
Definition: test.h:196
@ AST_TEST_NOT_RUN
Definition: test.h:194
int done
Definition: test_amihooks.c:48
const char * args
static struct test_options options
static struct test_val a
static struct test_val d
static struct test_val c
#define TEST_EXTENSION
Definition: test_message.c:47
#define TEST_CONTEXT
Definition: test_message.c:46
Definitions to aid in the use of thread local storage.
#define AST_THREADSTORAGE(name)
Define a thread storage variable.
Definition: threadstorage.h:86
Time-related functions and macros.
struct timeval ast_samp2tv(unsigned int _nsamp, unsigned int _rate)
Returns a timeval corresponding to the duration of n samples at rate r. Useful to convert samples to ...
Definition: time.h:282
struct timeval ast_tvadd(struct timeval a, struct timeval b)
Returns the sum of two timevals a + b.
Definition: extconf.c:2282
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition: time.h:159
FILE * out
Definition: utils/frame.c:33
char * usage
Definition: utils/frame.c:37
Utility functions.
#define ast_test_flag(p, flag)
Definition: utils.h:63
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition: utils.h:941
int ast_file_is_readable(const char *filename)
Test that a file exists and is readable by the effective user.
Definition: utils.c:3107
#define ast_pthread_create(a, b, c, d)
Definition: utils.h:584
#define ast_set2_flag(p, value, flag)
Definition: utils.h:94
#define ast_clear_flag(p, flag)
Definition: utils.h:77
long int ast_random(void)
Definition: utils.c:2312
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2479
#define ast_set_flag(p, flag)
Definition: utils.h:70
#define ARRAY_LEN(a)
Definition: utils.h:666
#define ast_copy_flags(dest, src, flagz)
Definition: utils.h:84
int ast_base64_encode_file_path(const char *filename, FILE *outputfile, const char *endl)
Performs a base 64 encode algorithm on the contents of a File.
Definition: utils.c:702
#define AST_FLAGS_ALL
Definition: utils.h:196