forked from asterisk/asterisk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapp_voicemail.c
17262 lines (15552 loc) · 545 KB
/
app_voicemail.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*
* Asterisk -- An open source telephony toolkit.
*
* Copyright (C) 1999 - 2006, Digium, Inc.
*
* Mark Spencer <[email protected]>
*
* See http://www.asterisk.org for more information about
* the Asterisk project. Please do not directly contact
* any of the maintainers of this project for assistance;
* the project provides a web site, mailing lists and IRC
* channels for your use.
*
* This program is free software, distributed under the terms of
* the GNU General Public License Version 2. See the LICENSE file
* at the top of the source tree.
*/
/*!
* \file
* \author Mark Spencer <[email protected]>
* \brief Comedian Mail - Voicemail System
*
* unixODBC (http://www.unixodbc.org/)
* A source distribution of University of Washington's IMAP c-client
* (http://www.washington.edu/imap/)
*
* \par See also
* \arg \ref voicemail.conf "Config_voicemail"
* \note For information about voicemail IMAP storage, https://wiki.asterisk.org/wiki/display/AST/IMAP+Voicemail+Storage
* \ingroup applications
* \todo This module requires res_adsi to load. This needs to be optional
* during compilation.
*
* \todo This file is now almost impossible to work with, due to all \#ifdefs.
* Feels like the database code before realtime. Someone - please come up
* with a plan to clean this up.
*/
/*! \li \ref app_voicemail.c uses configuration file \ref voicemail.conf
* \addtogroup configuration_file Configuration Files
*/
/*!
* \page voicemail.conf voicemail.conf
* \verbinclude voicemail.conf.sample
*/
#include "asterisk.h"
#ifdef IMAP_STORAGE
#include <ctype.h>
#include <signal.h>
#include <pwd.h>
#ifdef USE_SYSTEM_IMAP
#include <imap/c-client.h>
#include <imap/imap4r1.h>
#include <imap/linkage.h>
#elif defined (USE_SYSTEM_CCLIENT)
#include <c-client/c-client.h>
#include <c-client/imap4r1.h>
#include <c-client/linkage.h>
#else
#include "c-client.h"
#include "imap4r1.h"
#include "linkage.h"
#endif
#endif
#include "asterisk/paths.h" /* use ast_config_AST_SPOOL_DIR */
#include <sys/time.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <time.h>
#include <dirent.h>
#if defined(__FreeBSD__) || defined(__OpenBSD__)
#include <sys/wait.h>
#endif
#include "asterisk/logger.h"
#include "asterisk/lock.h"
#include "asterisk/file.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/config.h"
#include "asterisk/say.h"
#include "asterisk/module.h"
#include "asterisk/adsi.h"
#include "asterisk/app.h"
#include "asterisk/mwi.h"
#include "asterisk/manager.h"
#include "asterisk/dsp.h"
#include "asterisk/localtime.h"
#include "asterisk/cli.h"
#include "asterisk/utils.h"
#include "asterisk/stringfields.h"
#include "asterisk/strings.h"
#include "asterisk/smdi.h"
#include "asterisk/astobj2.h"
#include "asterisk/taskprocessor.h"
#include "asterisk/test.h"
#include "asterisk/format_cache.h"
#ifdef ODBC_STORAGE
#include "asterisk/res_odbc.h"
#endif
#ifdef IMAP_STORAGE
#include "asterisk/threadstorage.h"
#endif
/*** DOCUMENTATION
<application name="VoiceMail" language="en_US">
<synopsis>
Leave a Voicemail message.
</synopsis>
<syntax>
<parameter name="mailboxs" argsep="&" required="true">
<argument name="mailbox1" argsep="@" required="true">
<argument name="mailbox" required="true" />
<argument name="context" />
</argument>
<argument name="mailbox2" argsep="@" multiple="true">
<argument name="mailbox" required="true" />
<argument name="context" />
</argument>
</parameter>
<parameter name="options">
<optionlist>
<option name="b">
<para>Play the <literal>busy</literal> greeting to the calling party.</para>
</option>
<option name="d">
<argument name="c" />
<para>Accept digits for a new extension in context <replaceable>c</replaceable>,
if played during the greeting. Context defaults to the current context.</para>
</option>
<option name="e">
<para>Play greetings as early media -- only answer the channel just
before accepting the voice message.</para>
</option>
<option name="g">
<argument name="#" required="true" />
<para>Use the specified amount of gain when recording the voicemail
message. The units are whole-number decibels (dB). Only works on supported
technologies, which is DAHDI only.</para>
</option>
<option name="s">
<para>Skip the playback of instructions for leaving a message to the
calling party.</para>
</option>
<option name="S">
<para>Skip the playback of instructions for leaving a message to the
calling party, but only if a greeting has been recorded by the
mailbox user.</para>
</option>
<option name="t">
<argument name="x" required="false" />
<para>Play a custom beep tone to the caller instead of the default one.
If this option is used but no file is specified, the beep is suppressed.</para>
</option>
<option name="u">
<para>Play the <literal>unavailable</literal> greeting.</para>
</option>
<option name="U">
<para>Mark message as <literal>URGENT</literal>.</para>
</option>
<option name="P">
<para>Mark message as <literal>PRIORITY</literal>.</para>
</option>
</optionlist>
</parameter>
</syntax>
<description>
<para>This application allows the calling party to leave a message for the specified
list of mailboxes. When multiple mailboxes are specified, the greeting will be taken from
the first mailbox specified. Dialplan execution will stop if the specified mailbox does not
exist.</para>
<para>The Voicemail application will exit if any of the following DTMF digits are received:</para>
<enumlist>
<enum name="0">
<para>Jump to the <literal>o</literal> extension in the current dialplan context.</para>
</enum>
<enum name="*">
<para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
</enum>
</enumlist>
<para>This application will set the following channel variable upon completion:</para>
<variablelist>
<variable name="VMSTATUS">
<para>This indicates the status of the execution of the VoiceMail application.</para>
<value name="SUCCESS" />
<value name="USEREXIT" />
<value name="FAILED" />
</variable>
</variablelist>
</description>
<see-also>
<ref type="application">VoiceMailMain</ref>
</see-also>
</application>
<application name="VoiceMailMain" language="en_US">
<synopsis>
Check Voicemail messages.
</synopsis>
<syntax>
<parameter name="mailbox" required="true" argsep="@">
<argument name="mailbox" />
<argument name="context" />
</parameter>
<parameter name="options">
<optionlist>
<option name="p">
<para>Consider the <replaceable>mailbox</replaceable> parameter as a prefix to
the mailbox that is entered by the caller.</para>
</option>
<option name="g">
<argument name="#" required="true" />
<para>Use the specified amount of gain when recording a voicemail message.
The units are whole-number decibels (dB).</para>
</option>
<option name="r">
<para>"Read only". Prevent user from deleting any messages.</para>
<para>This applies only to specific executions of VoiceMailMain, NOT the mailbox itself.</para>
</option>
<option name="s">
<para>Skip checking the passcode for the mailbox.</para>
</option>
<option name="a">
<argument name="folder" required="true" />
<para>Skip folder prompt and go directly to <replaceable>folder</replaceable> specified.
Defaults to <literal>INBOX</literal> (or <literal>0</literal>).</para>
<enumlist>
<enum name="0"><para>INBOX</para></enum>
<enum name="1"><para>Old</para></enum>
<enum name="2"><para>Work</para></enum>
<enum name="3"><para>Family</para></enum>
<enum name="4"><para>Friends</para></enum>
<enum name="5"><para>Cust1</para></enum>
<enum name="6"><para>Cust2</para></enum>
<enum name="7"><para>Cust3</para></enum>
<enum name="8"><para>Cust4</para></enum>
<enum name="9"><para>Cust5</para></enum>
</enumlist>
</option>
</optionlist>
</parameter>
</syntax>
<description>
<para>This application allows the calling party to check voicemail messages. A specific
<replaceable>mailbox</replaceable>, and optional corresponding <replaceable>context</replaceable>,
may be specified. If a <replaceable>mailbox</replaceable> is not provided, the calling party will
be prompted to enter one. If a <replaceable>context</replaceable> is not specified, the
<literal>default</literal> context will be used.</para>
<para>The VoiceMailMain application will exit if the following DTMF digit is entered as Mailbox
or Password, and the extension exists:</para>
<enumlist>
<enum name="*">
<para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
</enum>
</enumlist>
</description>
<see-also>
<ref type="application">VoiceMail</ref>
</see-also>
</application>
<application name="VMAuthenticate" language="en_US">
<synopsis>
Authenticate with Voicemail passwords.
</synopsis>
<syntax>
<parameter name="mailbox" required="true" argsep="@">
<argument name="mailbox" />
<argument name="context" />
</parameter>
<parameter name="options">
<optionlist>
<option name="s">
<para>Skip playing the initial prompts.</para>
</option>
</optionlist>
</parameter>
</syntax>
<description>
<para>This application behaves the same way as the Authenticate application, but the passwords
are taken from <filename>voicemail.conf</filename>. If the <replaceable>mailbox</replaceable> is
specified, only that mailbox's password will be considered valid. If the <replaceable>mailbox</replaceable>
is not specified, the channel variable <variable>AUTH_MAILBOX</variable> will be set with the authenticated
mailbox.</para>
<para>The VMAuthenticate application will exit if the following DTMF digit is entered as Mailbox
or Password, and the extension exists:</para>
<enumlist>
<enum name="*">
<para>Jump to the <literal>a</literal> extension in the current dialplan context.</para>
</enum>
</enumlist>
</description>
</application>
<application name="VoiceMailPlayMsg" language="en_US">
<synopsis>
Play a single voice mail msg from a mailbox by msg id.
</synopsis>
<syntax>
<parameter name="mailbox" required="true" argsep="@">
<argument name="mailbox" />
<argument name="context" />
</parameter>
<parameter name="msg_id" required="true">
<para>The msg id of the msg to play back. </para>
</parameter>
</syntax>
<description>
<para>This application sets the following channel variable upon completion:</para>
<variablelist>
<variable name="VOICEMAIL_PLAYBACKSTATUS">
<para>The status of the playback attempt as a text string.</para>
<value name="SUCCESS"/>
<value name="FAILED"/>
</variable>
</variablelist>
</description>
</application>
<application name="VMSayName" language="en_US">
<synopsis>
Play the name of a voicemail user
</synopsis>
<syntax>
<parameter name="mailbox" required="true" argsep="@">
<argument name="mailbox" />
<argument name="context" />
</parameter>
</syntax>
<description>
<para>This application will say the recorded name of the voicemail user specified as the
argument to this application. If no context is provided, <literal>default</literal> is assumed.</para>
<para>Similar to the Background() application, playback of the recorded
name can be interrupted by entering an extension, which will be searched
for in the current context.</para>
</description>
</application>
<function name="VM_INFO" language="en_US">
<synopsis>
Returns the selected attribute from a mailbox.
</synopsis>
<syntax argsep=",">
<parameter name="mailbox" argsep="@" required="true">
<argument name="mailbox" required="true" />
<argument name="context" />
</parameter>
<parameter name="attribute" required="true">
<optionlist>
<option name="count">
<para>Count of messages in specified <replaceable>folder</replaceable>.
If <replaceable>folder</replaceable> is not specified, defaults to <literal>INBOX</literal>.</para>
</option>
<option name="email">
<para>E-mail address associated with the mailbox.</para>
</option>
<option name="exists">
<para>Returns a boolean of whether the corresponding <replaceable>mailbox</replaceable> exists.</para>
</option>
<option name="fullname">
<para>Full name associated with the mailbox.</para>
</option>
<option name="language">
<para>Mailbox language if overridden, otherwise the language of the channel.</para>
</option>
<option name="locale">
<para>Mailbox locale if overridden, otherwise global locale.</para>
</option>
<option name="pager">
<para>Pager e-mail address associated with the mailbox.</para>
</option>
<option name="password">
<para>Mailbox access password.</para>
</option>
<option name="tz">
<para>Mailbox timezone if overridden, otherwise global timezone</para>
</option>
</optionlist>
</parameter>
<parameter name="folder" required="false">
<para>If not specified, <literal>INBOX</literal> is assumed.</para>
</parameter>
</syntax>
<description>
<para>Returns the selected attribute from the specified <replaceable>mailbox</replaceable>.
If <replaceable>context</replaceable> is not specified, defaults to the <literal>default</literal>
context. Where the <replaceable>folder</replaceable> can be specified, common folders
include <literal>INBOX</literal>, <literal>Old</literal>, <literal>Work</literal>,
<literal>Family</literal> and <literal>Friends</literal>.</para>
</description>
</function>
<manager name="VoicemailUsersList" language="en_US">
<synopsis>
List All Voicemail User Information.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
</syntax>
<description>
</description>
</manager>
<manager name="VoicemailUserStatus" language="en_US">
<synopsis>
Show the status of given voicemail user's info.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
<parameter name="Context" required="true">
<para>The context you want to check.</para>
</parameter>
<parameter name="Mailbox" required="true">
<para>The mailbox you want to check.</para>
</parameter>
</syntax>
<description>
<para>Retrieves the status of the given voicemail user.</para>
</description>
</manager>
<manager name="VoicemailRefresh" language="en_US">
<synopsis>
Tell Asterisk to poll mailboxes for a change
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
<parameter name="Context" />
<parameter name="Mailbox" />
</syntax>
<description>
<para>Normally, MWI indicators are only sent when Asterisk itself
changes a mailbox. With external programs that modify the content
of a mailbox from outside the application, an option exists called
<literal>pollmailboxes</literal> that will cause voicemail to
continually scan all mailboxes on a system for changes. This can
cause a large amount of load on a system. This command allows
external applications to signal when a particular mailbox has
changed, thus permitting external applications to modify mailboxes
and MWI to work without introducing considerable CPU load.</para>
<para>If <replaceable>Context</replaceable> is not specified, all
mailboxes on the system will be polled for changes. If
<replaceable>Context</replaceable> is specified, but
<replaceable>Mailbox</replaceable> is omitted, then all mailboxes
within <replaceable>Context</replaceable> will be polled.
Otherwise, only a single mailbox will be polled for changes.</para>
</description>
</manager>
<manager name="VoicemailBoxSummary" language="en_US">
<synopsis>
Show the mailbox contents of given voicemail user.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
<parameter name="Context" required="true">
<para>The context you want to check.</para>
</parameter>
<parameter name="Mailbox" required="true">
<para>The mailbox you want to check.</para>
</parameter>
</syntax>
<description>
<para>Retrieves the contents of the given voicemail user's mailbox.</para>
</description>
</manager>
<manager name="VoicemailMove" language="en_US">
<synopsis>
Move Voicemail between mailbox folders of given user.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
<parameter name="Context" required="true">
<para>The context of the Voicemail you want to move.</para>
</parameter>
<parameter name="Mailbox" required="true">
<para>The mailbox of the Voicemail you want to move.</para>
</parameter>
<parameter name="Folder" required="true">
<para>The Folder containing the Voicemail you want to move.</para>
</parameter>
<parameter name="ID" required="true">
<para>The ID of the Voicemail you want to move.</para>
</parameter>
<parameter name="ToFolder" required="true">
<para>The Folder you want to move the Voicemail to.</para>
</parameter>
</syntax>
<description>
<para>Move a given Voicemail between Folders within a user's Mailbox.</para>
</description>
</manager>
<manager name="VoicemailRemove" language="en_US">
<synopsis>
Remove Voicemail from mailbox folder.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
<parameter name="Context" required="true">
<para>The context of the Voicemail you want to remove.</para>
</parameter>
<parameter name="Mailbox" required="true">
<para>The mailbox of the Voicemail you want to remove.</para>
</parameter>
<parameter name="Folder" required="true">
<para>The Folder containing the Voicemail you want to remove.</para>
</parameter>
<parameter name="ID" required="true">
<para>The ID of the Voicemail you want to remove.</para>
</parameter>
</syntax>
<description>
<para>Remove a given Voicemail from a user's Mailbox Folder.</para>
</description>
</manager>
<manager name="VoicemailForward" language="en_US">
<synopsis>
Forward Voicemail from one mailbox folder to another between given users.
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
<parameter name="Context" required="true">
<para>The context of the Voicemail you want to move.</para>
</parameter>
<parameter name="Mailbox" required="true">
<para>The mailbox of the Voicemail you want to move.</para>
</parameter>
<parameter name="Folder" required="true">
<para>The Folder containing the Voicemail you want to move.</para>
</parameter>
<parameter name="ID" required="true">
<para>The ID of the Voicemail you want to move.</para>
</parameter>
<parameter name="ToContext" required="true">
<para>The context you want to move the Voicemail to.</para>
</parameter>
<parameter name="ToMailbox" required="true">
<para>The mailbox you want to move the Voicemail to.</para>
</parameter>
<parameter name="ToFolder" required="true">
<para>The Folder you want to move the Voicemail to.</para>
</parameter>
</syntax>
<description>
<para>Forward a given Voicemail from a user's Mailbox Folder to
another user's Mailbox Folder. Can be used to copy between
Folders within a mailbox by specifying the to context and user
as the same as the from.</para>
</description>
</manager>
***/
#ifdef IMAP_STORAGE
static char imapserver[48] = "localhost";
static char imapport[8] = "143";
static char imapflags[128];
static char imapfolder[64] = "INBOX";
static char imapparentfolder[64];
static char greetingfolder[64] = "INBOX";
static char authuser[32];
static char authpassword[42];
static int imapversion = 1;
static int expungeonhangup = 1;
static int imapgreetings;
static int imap_poll_logout;
static char delimiter;
/* mail_open cannot be protected on a stream basis */
ast_mutex_t mail_open_lock;
struct vm_state;
struct ast_vm_user;
AST_THREADSTORAGE(ts_vmstate);
/* Forward declarations for IMAP */
static int init_mailstream(struct vm_state *vms, int box);
static void write_file(char *filename, char *buffer, unsigned long len);
static char *get_header_by_tag(char *header, char *tag, char *buf, size_t len);
static void vm_imap_delete(char *file, int msgnum, struct ast_vm_user *vmu);
static char *get_user_by_mailbox(char *mailbox, char *buf, size_t len);
static struct vm_state *get_vm_state_by_imapuser(const char *user, int interactive);
static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
static void vmstate_insert(struct vm_state *vms);
static void vmstate_delete(struct vm_state *vms);
static void set_update(MAILSTREAM * stream);
static void init_vm_state(struct vm_state *vms);
static int save_body(BODY *body, struct vm_state *vms, char *section, char *format, int is_intro);
static void get_mailbox_delimiter(struct vm_state *vms, MAILSTREAM *stream);
static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
static 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);
static 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);
static void update_messages_by_imapuser(const char *user, unsigned long number);
static int vm_delete(char *file);
static int imap_remove_file (char *dir, int msgnum);
static int imap_retrieve_file (const char *dir, const int msgnum, const char *mailbox, const char *context);
static int imap_delete_old_greeting (char *dir, struct vm_state *vms);
static void check_quota(struct vm_state *vms, char *mailbox);
static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box);
static void imap_logout(const char *mailbox_id);
struct vmstate {
struct vm_state *vms;
AST_LIST_ENTRY(vmstate) list;
};
static AST_LIST_HEAD_STATIC(vmstates, vmstate);
#endif
#define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
#define COMMAND_TIMEOUT 5000
/* Don't modify these here; set your umask at runtime instead */
#define VOICEMAIL_DIR_MODE 0777
#define VOICEMAIL_FILE_MODE 0666
#define CHUNKSIZE 65536
#define VOICEMAIL_CONFIG "voicemail.conf"
#define ASTERISK_USERNAME "asterisk"
/* Define fast-forward, pause, restart, and reverse keys
* while listening to a voicemail message - these are
* strings, not characters */
#define DEFAULT_LISTEN_CONTROL_FORWARD_KEY "#"
#define DEFAULT_LISTEN_CONTROL_REVERSE_KEY "*"
#define DEFAULT_LISTEN_CONTROL_PAUSE_KEY "0"
#define DEFAULT_LISTEN_CONTROL_RESTART_KEY "2"
#define DEFAULT_LISTEN_CONTROL_STOP_KEY "13456789"
#define VALID_DTMF "1234567890*#" /* Yes ABCD are valid dtmf but what phones have those? */
/* Default mail command to mail voicemail. Change it with the
* mailcmd= command in voicemail.conf */
#define SENDMAIL "/usr/sbin/sendmail -t"
#define INTRO "vm-intro"
#define MAX_MAIL_BODY_CONTENT_SIZE 134217728L // 128 Mbyte
#define MAXMSG 100
#define MAXMSGLIMIT 9999
#define MINPASSWORD 0 /*!< Default minimum mailbox password length */
#ifdef IMAP_STORAGE
#define ENDL "\r\n"
#else
#define ENDL "\n"
#endif
#define MAX_DATETIME_FORMAT 512
#define MAX_NUM_CID_CONTEXTS 10
#define VM_REVIEW (1 << 0) /*!< After recording, permit the caller to review the recording before saving */
#define VM_OPERATOR (1 << 1) /*!< Allow 0 to be pressed to go to 'o' extension */
#define VM_SAYCID (1 << 2) /*!< Repeat the CallerID info during envelope playback */
#define VM_SVMAIL (1 << 3) /*!< Allow the user to compose a new VM from within VoicemailMain */
#define VM_ENVELOPE (1 << 4) /*!< Play the envelope information (who-from, time received, etc.) */
#define VM_SAYDURATION (1 << 5) /*!< Play the length of the message during envelope playback */
#define VM_SKIPAFTERCMD (1 << 6) /*!< After deletion, assume caller wants to go to the next message */
#define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
#define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
#define VM_PBXSKIP (1 << 9) /*!< Skip the [PBX] preamble in the Subject line of emails */
#define VM_DIRECTFORWARD (1 << 10) /*!< Permit caller to use the Directory app for selecting to which mailbox to forward a VM */
#define VM_ATTACH (1 << 11) /*!< Attach message to voicemail notifications? */
#define VM_DELETE (1 << 12) /*!< Delete message after sending notification */
#define VM_ALLOCED (1 << 13) /*!< Structure was malloc'ed, instead of placed in a return (usually static) buffer */
#define VM_SEARCH (1 << 14) /*!< Search all contexts for a matching mailbox */
#define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
#define VM_MOVEHEARD (1 << 16) /*!< Move a "heard" message to Old after listening to it */
#define VM_MESSAGEWRAP (1 << 17) /*!< Wrap around from the last message to the first, and vice-versa */
#define VM_FWDURGAUTO (1 << 18) /*!< Autoset of Urgent flag on forwarded Urgent messages set globally */
#define VM_EMAIL_EXT_RECS (1 << 19) /*!< Send voicemail emails when an external recording is added to a mailbox */
#define ERROR_LOCK_PATH -100
#define ERROR_MAX_MSGS -101
#define OPERATOR_EXIT 300
enum vm_box {
NEW_FOLDER = 0,
OLD_FOLDER = 1,
WORK_FOLDER = 2,
FAMILY_FOLDER = 3,
FRIENDS_FOLDER = 4,
GREETINGS_FOLDER = -1
};
enum vm_option_flags {
OPT_SILENT = (1 << 0),
OPT_BUSY_GREETING = (1 << 1),
OPT_UNAVAIL_GREETING = (1 << 2),
OPT_RECORDGAIN = (1 << 3),
OPT_PREPEND_MAILBOX = (1 << 4),
OPT_AUTOPLAY = (1 << 6),
OPT_DTMFEXIT = (1 << 7),
OPT_MESSAGE_Urgent = (1 << 8),
OPT_MESSAGE_PRIORITY = (1 << 9),
OPT_EARLYM_GREETING = (1 << 10),
OPT_BEEP = (1 << 11),
OPT_SILENT_IF_GREET = (1 << 12),
OPT_READONLY = (1 << 13),
};
enum vm_option_args {
OPT_ARG_RECORDGAIN = 0,
OPT_ARG_PLAYFOLDER = 1,
OPT_ARG_DTMFEXIT = 2,
OPT_ARG_BEEP_TONE = 3,
/* This *must* be the last value in this enum! */
OPT_ARG_ARRAY_SIZE = 4,
};
enum vm_passwordlocation {
OPT_PWLOC_VOICEMAILCONF = 0,
OPT_PWLOC_SPOOLDIR = 1,
OPT_PWLOC_USERSCONF = 2,
};
AST_APP_OPTIONS(vm_app_options, {
AST_APP_OPTION('s', OPT_SILENT),
AST_APP_OPTION('S', OPT_SILENT_IF_GREET),
AST_APP_OPTION('b', OPT_BUSY_GREETING),
AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
AST_APP_OPTION_ARG('d', OPT_DTMFEXIT, OPT_ARG_DTMFEXIT),
AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
AST_APP_OPTION('U', OPT_MESSAGE_Urgent),
AST_APP_OPTION('P', OPT_MESSAGE_PRIORITY),
AST_APP_OPTION('e', OPT_EARLYM_GREETING),
AST_APP_OPTION_ARG('t', OPT_BEEP, OPT_ARG_BEEP_TONE),
AST_APP_OPTION('r', OPT_READONLY),
});
static const char * const mailbox_folders[] = {
#ifdef IMAP_STORAGE
imapfolder,
#else
"INBOX",
#endif
"Old",
"Work",
"Family",
"Friends",
"Cust1",
"Cust2",
"Cust3",
"Cust4",
"Cust5",
"Deleted",
"Urgent",
};
static int load_config(int reload);
#ifdef TEST_FRAMEWORK
static int load_config_from_memory(int reload, struct ast_config *cfg, struct ast_config *ucfg);
#endif
static int actual_load_config(int reload, struct ast_config *cfg, struct ast_config *ucfg);
/*! \page vmlang Voicemail Language Syntaxes Supported
\par Syntaxes supported, not really language codes.
\arg \b en - English
\arg \b de - German
\arg \b es - Spanish
\arg \b fr - French
\arg \b it - Italian
\arg \b nl - Dutch
\arg \b pt - Portuguese
\arg \b pt_BR - Portuguese (Brazil)
\arg \b gr - Greek
\arg \b no - Norwegian
\arg \b se - Swedish
\arg \b tw - Chinese (Taiwan)
\arg \b ua - Ukrainian
German requires the following additional soundfile:
\arg \b 1F einE (feminine)
Spanish requires the following additional soundfile:
\arg \b 1M un (masculine)
Dutch, Portuguese & Spanish require the following additional soundfiles:
\arg \b vm-INBOXs singular of 'new'
\arg \b vm-Olds singular of 'old/heard/read'
NB these are plural:
\arg \b vm-INBOX nieuwe (nl)
\arg \b vm-Old oude (nl)
Polish uses:
\arg \b vm-new-a 'new', feminine singular accusative
\arg \b vm-new-e 'new', feminine plural accusative
\arg \b vm-new-ych 'new', feminine plural genitive
\arg \b vm-old-a 'old', feminine singular accusative
\arg \b vm-old-e 'old', feminine plural accusative
\arg \b vm-old-ych 'old', feminine plural genitive
\arg \b digits/1-a 'one', not always same as 'digits/1'
\arg \b digits/2-ie 'two', not always same as 'digits/2'
Swedish uses:
\arg \b vm-nytt singular of 'new'
\arg \b vm-nya plural of 'new'
\arg \b vm-gammalt singular of 'old'
\arg \b vm-gamla plural of 'old'
\arg \b digits/ett 'one', not always same as 'digits/1'
Norwegian uses:
\arg \b vm-ny singular of 'new'
\arg \b vm-nye plural of 'new'
\arg \b vm-gammel singular of 'old'
\arg \b vm-gamle plural of 'old'
Dutch also uses:
\arg \b nl-om 'at'?
Spanish also uses:
\arg \b vm-youhaveno
Italian requires the following additional soundfile:
For vm_intro_it:
\arg \b vm-nuovo new
\arg \b vm-nuovi new plural
\arg \b vm-vecchio old
\arg \b vm-vecchi old plural
Japanese requires the following additional soundfile:
\arg \b jp-arimasu there is
\arg \b jp-arimasen there is not
\arg \b jp-oshitekudasai please press
\arg \b jp-ni article ni
\arg \b jp-ga article ga
\arg \b jp-wa article wa
\arg \b jp-wo article wo
Chinese (Taiwan) requires the following additional soundfile:
\arg \b vm-tong A class-word for call (tong1)
\arg \b vm-ri A class-word for day (ri4)
\arg \b vm-you You (ni3)
\arg \b vm-haveno Have no (mei2 you3)
\arg \b vm-have Have (you3)
\arg \b vm-listen To listen (yao4 ting1)
\note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
spelled among others when you have to change folder. For the above reasons, vm-INBOX
and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
*/
#define MAX_VM_MBOX_ID_LEN (AST_MAX_EXTENSION)
#define MAX_VM_CONTEXT_LEN (AST_MAX_CONTEXT)
/* MAX_VM_MAILBOX_LEN allows enough room for the '@' and NULL terminator */
#define MAX_VM_MAILBOX_LEN (MAX_VM_MBOX_ID_LEN + MAX_VM_CONTEXT_LEN)
/*! Structure for linked list of users
* Use ast_vm_user_destroy() to free one of these structures. */
struct ast_vm_user {
char context[MAX_VM_CONTEXT_LEN];/*!< Voicemail context */
char mailbox[MAX_VM_MBOX_ID_LEN];/*!< Mailbox id, unique within vm context */
char password[80]; /*!< Secret pin code, numbers only */
char fullname[80]; /*!< Full name, for directory app */
char *email; /*!< E-mail address */
char *emailsubject; /*!< E-mail subject */
char *emailbody; /*!< E-mail body */
char pager[80]; /*!< E-mail address to pager (no attachment) */
char serveremail[80]; /*!< From: Mail address */
char fromstring[100]; /*!< From: Username */
char language[MAX_LANGUAGE]; /*!< Config: Language setting */
char zonetag[80]; /*!< Time zone */
char locale[20]; /*!< The locale (for presentation of date/time) */
char callback[80];
char dialout[80];
char uniqueid[80]; /*!< Unique integer identifier */
char exit[80];
char attachfmt[20]; /*!< Attachment format */
unsigned int flags; /*!< VM_ flags */
int saydurationm;
int minsecs; /*!< Minimum number of seconds per message for this mailbox */
int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
int maxdeletedmsg; /*!< Maximum number of deleted msgs saved for this mailbox */
int maxsecs; /*!< Maximum number of seconds per message for this mailbox */
int passwordlocation; /*!< Storage location of the password */
#ifdef IMAP_STORAGE
char imapserver[48]; /*!< IMAP server address */
char imapport[8]; /*!< IMAP server port */
char imapflags[128]; /*!< IMAP optional flags */
char imapuser[80]; /*!< IMAP server login */
char imappassword[80]; /*!< IMAP server password if authpassword not defined */
char imapfolder[64]; /*!< IMAP voicemail folder */
char imapvmshareid[80]; /*!< Shared mailbox ID to use rather than the dialed one */
int imapversion; /*!< If configuration changes, use the new values */
#endif
double volgain; /*!< Volume gain for voicemails sent via email */
AST_LIST_ENTRY(ast_vm_user) list;
};
/*! Voicemail time zones */
struct vm_zone {
AST_LIST_ENTRY(vm_zone) list;
char name[80];
char timezone[80];
char msg_format[512];
};
#define VMSTATE_MAX_MSG_ARRAY 256
/*! Voicemail mailbox state */
struct vm_state {
char curbox[80];
char username[80];
char context[80];
char curdir[PATH_MAX];
char vmbox[PATH_MAX];
char fn[PATH_MAX];
char intro[PATH_MAX];
int *deleted;
int *heard;
int dh_arraysize; /* used for deleted / heard allocation */
int curmsg;
int lastmsg;
int newmessages;
int oldmessages;
int urgentmessages;
int starting;
int repeats;
#ifdef IMAP_STORAGE
ast_mutex_t lock;
int updated; /*!< decremented on each mail check until 1 -allows delay */
long *msgArray;
unsigned msg_array_max;
MAILSTREAM *mailstream;
int vmArrayIndex;
char imapuser[80]; /*!< IMAP server login */
char imapfolder[64]; /*!< IMAP voicemail folder */
char imapserver[48]; /*!< IMAP server address */
char imapport[8]; /*!< IMAP server port */
char imapflags[128]; /*!< IMAP optional flags */
int imapversion;
int interactive;
char introfn[PATH_MAX]; /*!< Name of prepended file */
unsigned int quota_limit;
unsigned int quota_usage;
struct vm_state *persist_vms;
#endif
};
#ifdef ODBC_STORAGE
static char odbc_database[80] = "asterisk";
static char odbc_table[80] = "voicemessages";
#define RETRIEVE(a,b,c,d) retrieve_file(a,b)
#define DISPOSE(a,b) remove_file(a,b)
#define STORE(a,b,c,d,e,f,g,h,i,j,k) store_file(a,b,c,d)
#define EXISTS(a,b,c,d) (message_exists(a,b))
#define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
#define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
#define DELETE(a,b,c,d) (delete_file(a,b))
#define UPDATE_MSG_ID(a, b, c, d, e, f) (odbc_update_msg_id((a), (b), (c)))
#else
#ifdef IMAP_STORAGE
#define DISPOSE(a,b) (imap_remove_file(a,b))
#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))
#define RETRIEVE(a,b,c,d) imap_retrieve_file(a,b,c,d)
#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
#define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
#define DELETE(a,b,c,d) (vm_imap_delete(a,b,d))
#define UPDATE_MSG_ID(a, b, c, d, e, f) (vm_imap_update_msg_id((a), (b), (c), (d), (e), (f)))
#else
#define RETRIEVE(a,b,c,d)
#define DISPOSE(a,b)
#define STORE(a,b,c,d,e,f,g,h,i,j,k)
#define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
#define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
#define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
#define DELETE(a,b,c,d) (vm_delete(c))
#define UPDATE_MSG_ID(a, b, c, d, e, f)
#endif
#endif
static char VM_SPOOL_DIR[PATH_MAX];
static char ext_pass_cmd[128];
static char ext_pass_check_cmd[128];
static int my_umask;
#define PWDCHANGE_INTERNAL (1 << 1)
#define PWDCHANGE_EXTERNAL (1 << 2)
static int pwdchange = PWDCHANGE_INTERNAL;
#ifdef ODBC_STORAGE
#define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
#else
# ifdef IMAP_STORAGE
# define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
# else
# define tdesc "Comedian Mail (Voicemail System)"
# endif