2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
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.
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.
21 * \brief Comedian Mail - Voicemail System
23 * \author Mark Spencer <markster@digium.com>
27 * \ingroup applications
28 * \note This module requires res_adsi to load.
32 <depend>res_adsi</depend>
33 <depend>res_smdi</depend>
37 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_directory.o">
38 <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
39 <depend>unixodbc</depend>
41 <conflict>IMAP_STORAGE</conflict>
42 <defaultenabled>no</defaultenabled>
44 <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
45 <depend>imap_tk</depend>
46 <conflict>ODBC_STORAGE</conflict>
48 <defaultenabled>no</defaultenabled>
55 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
65 #include <sys/types.h>
73 #ifdef USE_SYSTEM_IMAP
74 #include <imap/c-client.h>
75 #include <imap/imap4r1.h>
76 #include <imap/linkage.h>
77 #elif defined (USE_SYSTEM_CCLIENT)
78 #include <c-client/c-client.h>
79 #include <c-client/imap4r1.h>
80 #include <c-client/linkage.h>
87 #include "asterisk/lock.h"
88 #include "asterisk/file.h"
89 #include "asterisk/logger.h"
90 #include "asterisk/channel.h"
91 #include "asterisk/pbx.h"
92 #include "asterisk/options.h"
93 #include "asterisk/config.h"
94 #include "asterisk/say.h"
95 #include "asterisk/module.h"
96 #include "asterisk/adsi.h"
97 #include "asterisk/app.h"
98 #include "asterisk/manager.h"
99 #include "asterisk/dsp.h"
100 #include "asterisk/localtime.h"
101 #include "asterisk/cli.h"
102 #include "asterisk/utils.h"
103 #include "asterisk/stringfields.h"
104 #include "asterisk/smdi.h"
106 #include "asterisk/res_odbc.h"
110 AST_MUTEX_DEFINE_STATIC(imaptemp_lock
);
111 static char imaptemp
[1024];
112 static char imapserver
[48];
113 static char imapport
[8];
114 static char imapflags
[128];
115 static char imapfolder
[64];
116 static char authuser
[32];
117 static char authpassword
[42];
119 static int expungeonhangup
= 1;
120 static char delimiter
= '\0';
121 static const long DEFAULT_IMAP_TCP_TIMEOUT
= 60L;
126 static int init_mailstream (struct vm_state
*vms
, int box
);
127 static void write_file (char *filename
, char *buffer
, unsigned long len
);
128 /*static void status (MAILSTREAM *stream); */ /* No need for this. */
129 static char *get_header_by_tag(char *header
, char *tag
);
130 static void vm_imap_delete(int msgnum
, struct ast_vm_user
*vmu
);
131 static char *get_user_by_mailbox(char *mailbox
);
132 static struct vm_state
*get_vm_state_by_imapuser(char *user
, int interactive
);
133 static struct vm_state
*get_vm_state_by_mailbox(const char *mailbox
, const char *context
, int interactive
);
134 static struct vm_state
*create_vm_state_from_user(struct ast_vm_user
*vmu
);
135 static void vmstate_insert(struct vm_state
*vms
);
136 static void vmstate_delete(struct vm_state
*vms
);
137 static void set_update(MAILSTREAM
* stream
);
138 static void init_vm_state(struct vm_state
*vms
);
139 static void copy_msgArray(struct vm_state
*dst
, struct vm_state
*src
);
140 static int save_body(BODY
*body
, struct vm_state
*vms
, char *section
, char *format
);
141 static void get_mailbox_delimiter(MAILSTREAM
*stream
);
142 static void mm_parsequota (MAILSTREAM
*stream
, unsigned char *msg
, QUOTALIST
*pquota
);
143 static void imap_mailbox_name(char *spec
, size_t len
, struct vm_state
*vms
, int box
, int target
);
144 static int imap_store_file(char *dir
, char *mailboxuser
, char *mailboxcontext
, int msgnum
, struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *fmt
, int duration
, struct vm_state
*vms
);
145 static void check_quota(struct vm_state
*vms
, char *mailbox
);
146 static int open_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
,int box
);
148 struct vm_state
*vms
;
149 struct vmstate
*next
;
151 AST_MUTEX_DEFINE_STATIC(vmstate_lock
);
152 static struct vmstate
*vmstates
= NULL
;
155 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
157 #define COMMAND_TIMEOUT 5000
158 /* Don't modify these here; set your umask at runtime instead */
159 #define VOICEMAIL_DIR_MODE 0777
160 #define VOICEMAIL_FILE_MODE 0666
161 #define CHUNKSIZE 65536
163 #define VOICEMAIL_CONFIG "voicemail.conf"
164 #define ASTERISK_USERNAME "asterisk"
166 /* Default mail command to mail voicemail. Change it with the
167 mailcmd= command in voicemail.conf */
168 #define SENDMAIL "/usr/sbin/sendmail -t"
170 #define INTRO "vm-intro"
174 #define MAXMSGLIMIT 9999
176 #define MAXMSGLIMIT 255
179 #define BASEMAXINLINE 256
180 #define BASELINELEN 72
181 #define BASEMAXINLINE 256
184 #define MAX_DATETIME_FORMAT 512
185 #define MAX_NUM_CID_CONTEXTS 10
187 #define VM_REVIEW (1 << 0)
188 #define VM_OPERATOR (1 << 1)
189 #define VM_SAYCID (1 << 2)
190 #define VM_SVMAIL (1 << 3)
191 #define VM_ENVELOPE (1 << 4)
192 #define VM_SAYDURATION (1 << 5)
193 #define VM_SKIPAFTERCMD (1 << 6)
194 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
195 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
196 #define VM_PBXSKIP (1 << 9)
197 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
198 #define VM_ATTACH (1 << 11)
199 #define VM_DELETE (1 << 12)
200 #define VM_ALLOCED (1 << 13)
201 #define VM_SEARCH (1 << 14)
202 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
203 #define ERROR_LOCK_PATH -100
204 #define ERROR_MAILBOX_FULL -200
208 OPT_SILENT
= (1 << 0),
209 OPT_BUSY_GREETING
= (1 << 1),
210 OPT_UNAVAIL_GREETING
= (1 << 2),
211 OPT_RECORDGAIN
= (1 << 3),
212 OPT_PREPEND_MAILBOX
= (1 << 4),
213 OPT_PRIORITY_JUMP
= (1 << 5),
214 OPT_AUTOPLAY
= (1 << 6),
218 OPT_ARG_RECORDGAIN
= 0,
219 OPT_ARG_PLAYFOLDER
= 1,
220 /* This *must* be the last value in this enum! */
221 OPT_ARG_ARRAY_SIZE
= 2,
224 AST_APP_OPTIONS(vm_app_options
, {
225 AST_APP_OPTION('s', OPT_SILENT
),
226 AST_APP_OPTION('b', OPT_BUSY_GREETING
),
227 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING
),
228 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN
, OPT_ARG_RECORDGAIN
),
229 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX
),
230 AST_APP_OPTION('j', OPT_PRIORITY_JUMP
),
231 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY
, OPT_ARG_PLAYFOLDER
),
234 static int load_config(void);
236 /*! \page vmlang Voicemail Language Syntaxes Supported
238 \par Syntaxes supported, not really language codes.
246 \arg \b pt - Portuguese
247 \arg \b pt_BR - Portuguese (Brazil)
249 \arg \b no - Norwegian
251 \arg \b ua - Ukrainian
254 German requires the following additional soundfile:
255 \arg \b 1F einE (feminine)
257 Spanish requires the following additional soundfile:
258 \arg \b 1M un (masculine)
260 Dutch, Portuguese & Spanish require the following additional soundfiles:
261 \arg \b vm-INBOXs singular of 'new'
262 \arg \b vm-Olds singular of 'old/heard/read'
265 \arg \b vm-INBOX nieuwe (nl)
266 \arg \b vm-Old oude (nl)
269 \arg \b vm-new-a 'new', feminine singular accusative
270 \arg \b vm-new-e 'new', feminine plural accusative
271 \arg \b vm-new-ych 'new', feminine plural genitive
272 \arg \b vm-old-a 'old', feminine singular accusative
273 \arg \b vm-old-e 'old', feminine plural accusative
274 \arg \b vm-old-ych 'old', feminine plural genitive
275 \arg \b digits/1-a 'one', not always same as 'digits/1'
276 \arg \b digits/2-ie 'two', not always same as 'digits/2'
279 \arg \b vm-nytt singular of 'new'
280 \arg \b vm-nya plural of 'new'
281 \arg \b vm-gammalt singular of 'old'
282 \arg \b vm-gamla plural of 'old'
283 \arg \b digits/ett 'one', not always same as 'digits/1'
286 \arg \b vm-ny singular of 'new'
287 \arg \b vm-nye plural of 'new'
288 \arg \b vm-gammel singular of 'old'
289 \arg \b vm-gamle plural of 'old'
297 Ukrainian requires the following additional soundfile:
298 \arg \b vm-nove 'nove'
299 \arg \b vm-stare 'stare'
300 \arg \b digits/ua/1e 'odne'
302 Italian requires the following additional soundfile:
306 \arg \b vm-nuovi new plural
307 \arg \b vm-vecchio old
308 \arg \b vm-vecchi old plural
311 \arg \b vm-INBOX1 '1 new message'
312 \arg \b vm-OLD1 '1 old message'
313 \arg \b vm-shtei 'shtei'
314 \arg \b vm-nomessages 'you have no new messages'
316 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
317 spelled among others when you have to change folder. For the above reasons, vm-INBOX
318 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
327 unsigned char iobuf
[BASEMAXINLINE
];
330 /*! Structure for linked list of users */
332 char context
[AST_MAX_CONTEXT
]; /*!< Voicemail context */
333 char mailbox
[AST_MAX_EXTENSION
]; /*!< Mailbox id, unique within vm context */
334 char password
[80]; /*!< Secret pin code, numbers only */
335 char fullname
[80]; /*!< Full name, for directory app */
336 char email
[80]; /*!< E-mail address */
337 char pager
[80]; /*!< E-mail address to pager (no attachment) */
338 char serveremail
[80]; /*!< From: Mail address */
339 char mailcmd
[160]; /*!< Configurable mail command */
340 char language
[MAX_LANGUAGE
]; /*!< Config: Language setting */
341 char zonetag
[80]; /*!< Time zone */
344 char uniqueid
[80]; /*!< Unique integer identifier */
346 char attachfmt
[20]; /*!< Attachment format */
347 unsigned int flags
; /*!< VM_ flags */
349 int maxmsg
; /*!< Maximum number of msgs per folder for this mailbox */
351 char imapuser
[80]; /* IMAP server login */
352 char imappassword
[80]; /* IMAP server password if authpassword not defined */
354 double volgain
; /*!< Volume gain for voicemails sent via email */
355 AST_LIST_ENTRY(ast_vm_user
) list
;
359 AST_LIST_ENTRY(vm_zone
) list
;
362 char msg_format
[512];
369 char curdir
[PATH_MAX
];
370 char vmbox
[PATH_MAX
];
383 int updated
; /* decremented on each mail check until 1 -allows delay */
385 MAILSTREAM
*mailstream
;
387 char imapuser
[80]; /* IMAP server login */
389 unsigned int quota_limit
;
390 unsigned int quota_usage
;
391 struct vm_state
*persist_vms
;
394 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
);
395 static int dialout(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *num
, char *outgoing_context
);
396 static int play_record_review(struct ast_channel
*chan
, char *playfile
, char *recordfile
, int maxtime
,
397 char *fmt
, int outsidecaller
, struct ast_vm_user
*vmu
, int *duration
, const char *unlockdir
,
398 signed char record_gain
, struct vm_state
*vms
);
399 static int vm_tempgreeting(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
);
400 static int vm_play_folder_name(struct ast_channel
*chan
, char *mbox
);
401 static int notify_new_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, int msgnum
, long duration
, char *fmt
, char *cidnum
, char *cidname
);
402 static void make_email_file(FILE *p
, char *srcemail
, struct ast_vm_user
*vmu
, int msgnum
, char *context
, char *mailbox
, char *cidnum
, char *cidname
, char *attach
, char *format
, int duration
, int attach_user_voicemail
, struct ast_channel
*chan
, const char *category
, int imap
);
403 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
404 static int __has_voicemail(const char *context
, const char *mailbox
, const char *folder
, int shortcircuit
);
406 static void apply_options(struct ast_vm_user
*vmu
, const char *options
);
409 static char odbc_database
[80];
410 static char odbc_table
[80];
411 #define RETRIEVE(a,b,c) retrieve_file(a,b)
412 #define DISPOSE(a,b) remove_file(a,b)
413 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
414 #define EXISTS(a,b,c,d) (message_exists(a,b))
415 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
416 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
417 #define DELETE(a,b,c,d) (delete_file(a,b))
420 #define RETRIEVE(a,b,c) imap_retrieve_file(a,b,c)
421 #define DISPOSE(a,b) remove_file(a,b)
422 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
423 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
424 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
425 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
426 #define DELETE(a,b,c,d) (vm_imap_delete(b,d))
428 #define RETRIEVE(a,b,c)
430 #define STORE(a,b,c,d,e,f,g,h,i)
431 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
432 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
433 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
434 #define DELETE(a,b,c,d) (vm_delete(c))
438 static char VM_SPOOL_DIR
[PATH_MAX
];
440 static char ext_pass_cmd
[128];
445 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
447 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
449 #define tdesc "Comedian Mail (Voicemail System)"
452 static char userscontext
[AST_MAX_EXTENSION
] = "default";
454 static char *addesc
= "Comedian Mail";
456 static char *synopsis_vm
=
457 "Leave a Voicemail message";
459 static char *descrip_vm
=
460 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
461 "application allows the calling party to leave a message for the specified\n"
462 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
463 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
464 "specified mailbox does not exist.\n"
465 " The Voicemail application will exit if any of the following DTMF digits are\n"
467 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
468 " * - Jump to the 'a' extension in the current dialplan context.\n"
469 " This application will set the following channel variable upon completion:\n"
470 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
471 " application. The possible values are:\n"
472 " SUCCESS | USEREXIT | FAILED\n\n"
474 " b - Play the 'busy' greeting to the calling party.\n"
475 " g(#) - Use the specified amount of gain when recording the voicemail\n"
476 " message. The units are whole-number decibels (dB).\n"
477 " Only works on supported technologies, which is Zap only.\n"
478 " s - Skip the playback of instructions for leaving a message to the\n"
480 " u - Play the 'unavailable' greeting.\n"
481 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
484 static char *synopsis_vmain
=
485 "Check Voicemail messages";
487 static char *descrip_vmain
=
488 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
489 "calling party to check voicemail messages. A specific mailbox, and optional\n"
490 "corresponding context, may be specified. If a mailbox is not provided, the\n"
491 "calling party will be prompted to enter one. If a context is not specified,\n"
492 "the 'default' context will be used.\n\n"
494 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
495 " is entered by the caller.\n"
496 " g(#) - Use the specified amount of gain when recording a voicemail\n"
497 " message. The units are whole-number decibels (dB).\n"
498 " s - Skip checking the passcode for the mailbox.\n"
499 " a(#) - Skip folder prompt and go directly to folder specified.\n"
500 " Defaults to INBOX\n";
502 static char *synopsis_vm_box_exists
=
503 "Check to see if Voicemail mailbox exists";
505 static char *descrip_vm_box_exists
=
506 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
507 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
509 " This application will set the following channel variable upon completion:\n"
510 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
511 " MailboxExists application. Possible values include:\n"
512 " SUCCESS | FAILED\n\n"
514 " j - Jump to priority n+101 if the mailbox is found.\n";
516 static char *synopsis_vmauthenticate
=
517 "Authenticate with Voicemail passwords";
519 static char *descrip_vmauthenticate
=
520 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
521 "same way as the Authenticate application, but the passwords are taken from\n"
523 " If the mailbox is specified, only that mailbox's password will be considered\n"
524 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
525 "be set with the authenticated mailbox.\n\n"
527 " s - Skip playing the initial prompts.\n";
529 /* Leave a message */
530 static char *app
= "VoiceMail";
532 /* Check mail, control, etc */
533 static char *app2
= "VoiceMailMain";
535 static char *app3
= "MailboxExists";
536 static char *app4
= "VMAuthenticate";
538 static AST_LIST_HEAD_STATIC(users
, ast_vm_user
);
539 static AST_LIST_HEAD_STATIC(zones
, vm_zone
);
540 static int maxsilence
;
542 static int silencethreshold
= 128;
543 static char serveremail
[80];
544 static char mailcmd
[160]; /* Configurable mail cmd */
545 static char externnotify
[160];
546 static struct ast_smdi_interface
*smdi_iface
= NULL
;
547 static char vmfmts
[80];
548 static double volgain
;
549 static int vmminmessage
;
550 static int vmmaxmessage
;
553 static int maxlogins
;
555 static struct ast_flags globalflags
= {0};
557 static int saydurationminfo
;
559 static char dialcontext
[AST_MAX_CONTEXT
];
560 static char callcontext
[AST_MAX_CONTEXT
];
561 static char exitcontext
[AST_MAX_CONTEXT
];
563 static char cidinternalcontexts
[MAX_NUM_CID_CONTEXTS
][64];
566 static char *emailbody
= NULL
;
567 static char *emailsubject
= NULL
;
568 static char *pagerbody
= NULL
;
569 static char *pagersubject
= NULL
;
570 static char fromstring
[100];
571 static char pagerfromstring
[100];
572 static char emailtitle
[100];
573 static char charset
[32] = "ISO-8859-1";
575 static unsigned char adsifdn
[4] = "\x00\x00\x00\x0F";
576 static unsigned char adsisec
[4] = "\x9B\xDB\xF7\xAC";
577 static int adsiver
= 1;
578 static char emaildateformat
[32] = "%A, %B %d, %Y at %r";
581 static char *strip_control(const char *input
, char *buf
, size_t buflen
)
584 for (; *input
; input
++) {
589 if (bufptr
== buf
+ buflen
- 1) {
597 static void populate_defaults(struct ast_vm_user
*vmu
)
599 ast_copy_flags(vmu
, (&globalflags
), AST_FLAGS_ALL
);
600 if (saydurationminfo
)
601 vmu
->saydurationm
= saydurationminfo
;
602 ast_copy_string(vmu
->callback
, callcontext
, sizeof(vmu
->callback
));
603 ast_copy_string(vmu
->dialout
, dialcontext
, sizeof(vmu
->dialout
));
604 ast_copy_string(vmu
->exit
, exitcontext
, sizeof(vmu
->exit
));
606 vmu
->maxmsg
= maxmsg
;
607 vmu
->volgain
= volgain
;
610 static void apply_option(struct ast_vm_user
*vmu
, const char *var
, const char *value
)
613 if (!strcasecmp(var
, "attach")) {
614 ast_set2_flag(vmu
, ast_true(value
), VM_ATTACH
);
615 } else if (!strcasecmp(var
, "attachfmt")) {
616 ast_copy_string(vmu
->attachfmt
, value
, sizeof(vmu
->attachfmt
));
617 } else if (!strcasecmp(var
, "serveremail")) {
618 ast_copy_string(vmu
->serveremail
, value
, sizeof(vmu
->serveremail
));
619 } else if (!strcasecmp(var
, "language")) {
620 ast_copy_string(vmu
->language
, value
, sizeof(vmu
->language
));
621 } else if (!strcasecmp(var
, "tz")) {
622 ast_copy_string(vmu
->zonetag
, value
, sizeof(vmu
->zonetag
));
624 } else if (!strcasecmp(var
, "imapuser")) {
625 ast_copy_string(vmu
->imapuser
, value
, sizeof(vmu
->imapuser
));
626 } else if (!strcasecmp(var
, "imappassword") || !strcasecmp(var
, "imapsecret")) {
627 ast_copy_string(vmu
->imappassword
, value
, sizeof(vmu
->imappassword
));
629 } else if (!strcasecmp(var
, "delete") || !strcasecmp(var
, "deletevoicemail")) {
630 ast_set2_flag(vmu
, ast_true(value
), VM_DELETE
);
631 } else if (!strcasecmp(var
, "saycid")){
632 ast_set2_flag(vmu
, ast_true(value
), VM_SAYCID
);
633 } else if (!strcasecmp(var
,"sendvoicemail")){
634 ast_set2_flag(vmu
, ast_true(value
), VM_SVMAIL
);
635 } else if (!strcasecmp(var
, "review")){
636 ast_set2_flag(vmu
, ast_true(value
), VM_REVIEW
);
637 } else if (!strcasecmp(var
, "tempgreetwarn")){
638 ast_set2_flag(vmu
, ast_true(value
), VM_TEMPGREETWARN
);
639 } else if (!strcasecmp(var
, "operator")){
640 ast_set2_flag(vmu
, ast_true(value
), VM_OPERATOR
);
641 } else if (!strcasecmp(var
, "envelope")){
642 ast_set2_flag(vmu
, ast_true(value
), VM_ENVELOPE
);
643 } else if (!strcasecmp(var
, "sayduration")){
644 ast_set2_flag(vmu
, ast_true(value
), VM_SAYDURATION
);
645 } else if (!strcasecmp(var
, "saydurationm")){
646 if (sscanf(value
, "%d", &x
) == 1) {
647 vmu
->saydurationm
= x
;
649 ast_log(LOG_WARNING
, "Invalid min duration for say duration\n");
651 } else if (!strcasecmp(var
, "forcename")){
652 ast_set2_flag(vmu
, ast_true(value
), VM_FORCENAME
);
653 } else if (!strcasecmp(var
, "forcegreetings")){
654 ast_set2_flag(vmu
, ast_true(value
), VM_FORCEGREET
);
655 } else if (!strcasecmp(var
, "callback")) {
656 ast_copy_string(vmu
->callback
, value
, sizeof(vmu
->callback
));
657 } else if (!strcasecmp(var
, "dialout")) {
658 ast_copy_string(vmu
->dialout
, value
, sizeof(vmu
->dialout
));
659 } else if (!strcasecmp(var
, "exitcontext")) {
660 ast_copy_string(vmu
->exit
, value
, sizeof(vmu
->exit
));
661 } else if (!strcasecmp(var
, "maxmsg")) {
662 vmu
->maxmsg
= atoi(value
);
663 if (vmu
->maxmsg
<= 0) {
664 ast_log(LOG_WARNING
, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value
, MAXMSG
);
665 vmu
->maxmsg
= MAXMSG
;
666 } else if (vmu
->maxmsg
> MAXMSGLIMIT
) {
667 ast_log(LOG_WARNING
, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT
, value
);
668 vmu
->maxmsg
= MAXMSGLIMIT
;
670 } else if (!strcasecmp(var
, "volgain")) {
671 sscanf(value
, "%lf", &vmu
->volgain
);
672 } else if (!strcasecmp(var
, "options")) {
673 apply_options(vmu
, value
);
677 static int change_password_realtime(struct ast_vm_user
*vmu
, const char *password
)
680 if (!ast_strlen_zero(vmu
->uniqueid
)) {
681 res
= ast_update_realtime("voicemail", "uniqueid", vmu
->uniqueid
, "password", password
, NULL
);
683 ast_copy_string(vmu
->password
, password
, sizeof(vmu
->password
));
693 static void apply_options(struct ast_vm_user
*vmu
, const char *options
)
694 { /* Destructively Parse options and apply */
698 stringp
= ast_strdupa(options
);
699 while ((s
= strsep(&stringp
, "|"))) {
701 if ((var
= strsep(&value
, "=")) && value
) {
702 apply_option(vmu
, var
, value
);
707 static void apply_options_full(struct ast_vm_user
*retval
, struct ast_variable
*var
)
709 struct ast_variable
*tmp
;
712 if (!strcasecmp(tmp
->name
, "vmsecret")) {
713 ast_copy_string(retval
->password
, tmp
->value
, sizeof(retval
->password
));
714 } else if (!strcasecmp(tmp
->name
, "secret") || !strcasecmp(tmp
->name
, "password")) { /* don't overwrite vmsecret if it exists */
715 if (ast_strlen_zero(retval
->password
))
716 ast_copy_string(retval
->password
, tmp
->value
, sizeof(retval
->password
));
717 } else if (!strcasecmp(tmp
->name
, "uniqueid")) {
718 ast_copy_string(retval
->uniqueid
, tmp
->value
, sizeof(retval
->uniqueid
));
719 } else if (!strcasecmp(tmp
->name
, "pager")) {
720 ast_copy_string(retval
->pager
, tmp
->value
, sizeof(retval
->pager
));
721 } else if (!strcasecmp(tmp
->name
, "email")) {
722 ast_copy_string(retval
->email
, tmp
->value
, sizeof(retval
->email
));
723 } else if (!strcasecmp(tmp
->name
, "fullname")) {
724 ast_copy_string(retval
->fullname
, tmp
->value
, sizeof(retval
->fullname
));
725 } else if (!strcasecmp(tmp
->name
, "context")) {
726 ast_copy_string(retval
->context
, tmp
->value
, sizeof(retval
->context
));
728 } else if (!strcasecmp(tmp
->name
, "imapuser")) {
729 ast_copy_string(retval
->imapuser
, tmp
->value
, sizeof(retval
->imapuser
));
730 } else if (!strcasecmp(tmp
->name
, "imappassword") || !strcasecmp(tmp
->name
, "imapsecret")) {
731 ast_copy_string(retval
->imappassword
, tmp
->value
, sizeof(retval
->imappassword
));
734 apply_option(retval
, tmp
->name
, tmp
->value
);
739 static struct ast_vm_user
*find_user_realtime(struct ast_vm_user
*ivm
, const char *context
, const char *mailbox
)
741 struct ast_variable
*var
;
742 struct ast_vm_user
*retval
;
744 if ((retval
= (ivm
? ivm
: ast_calloc(1, sizeof(*retval
))))) {
746 ast_set_flag(retval
, VM_ALLOCED
);
748 memset(retval
, 0, sizeof(*retval
));
750 ast_copy_string(retval
->mailbox
, mailbox
, sizeof(retval
->mailbox
));
751 populate_defaults(retval
);
752 if (!context
&& ast_test_flag((&globalflags
), VM_SEARCH
))
753 var
= ast_load_realtime("voicemail", "mailbox", mailbox
, NULL
);
755 var
= ast_load_realtime("voicemail", "mailbox", mailbox
, "context", context
, NULL
);
757 apply_options_full(retval
, var
);
758 ast_variables_destroy(var
);
768 static struct ast_vm_user
*find_user(struct ast_vm_user
*ivm
, const char *context
, const char *mailbox
)
770 /* This function could be made to generate one from a database, too */
771 struct ast_vm_user
*vmu
=NULL
, *cur
;
772 AST_LIST_LOCK(&users
);
774 if (!context
&& !ast_test_flag((&globalflags
), VM_SEARCH
))
777 AST_LIST_TRAVERSE(&users
, cur
, list
) {
778 if (ast_test_flag((&globalflags
), VM_SEARCH
) && !strcasecmp(mailbox
, cur
->mailbox
))
780 if (context
&& (!strcasecmp(context
, cur
->context
)) && (!strcasecmp(mailbox
, cur
->mailbox
)))
784 /* Make a copy, so that on a reload, we have no race */
785 if ((vmu
= (ivm
? ivm
: ast_malloc(sizeof(*vmu
))))) {
786 memcpy(vmu
, cur
, sizeof(*vmu
));
787 ast_set2_flag(vmu
, !ivm
, VM_ALLOCED
);
788 AST_LIST_NEXT(vmu
, list
) = NULL
;
791 vmu
= find_user_realtime(ivm
, context
, mailbox
);
792 AST_LIST_UNLOCK(&users
);
796 static int reset_user_pw(const char *context
, const char *mailbox
, const char *newpass
)
798 /* This function could be made to generate one from a database, too */
799 struct ast_vm_user
*cur
;
801 AST_LIST_LOCK(&users
);
802 AST_LIST_TRAVERSE(&users
, cur
, list
) {
803 if ((!context
|| !strcasecmp(context
, cur
->context
)) &&
804 (!strcasecmp(mailbox
, cur
->mailbox
)))
808 ast_copy_string(cur
->password
, newpass
, sizeof(cur
->password
));
811 AST_LIST_UNLOCK(&users
);
815 static void vm_change_password(struct ast_vm_user
*vmu
, const char *newpassword
)
817 struct ast_config
*cfg
=NULL
;
818 struct ast_variable
*var
=NULL
;
819 struct ast_category
*cat
=NULL
;
820 char *category
=NULL
, *value
=NULL
, *new=NULL
;
821 const char *tmp
=NULL
;
823 if (!change_password_realtime(vmu
, newpassword
))
826 /* check voicemail.conf */
827 if ((cfg
= ast_config_load_with_comments(VOICEMAIL_CONFIG
))) {
828 while ((category
= ast_category_browse(cfg
, category
))) {
829 if (!strcasecmp(category
, vmu
->context
)) {
830 tmp
= ast_variable_retrieve(cfg
, category
, vmu
->mailbox
);
832 ast_log(LOG_WARNING
, "We could not find the mailbox.\n");
835 value
= strstr(tmp
,",");
837 ast_log(LOG_WARNING
, "variable has bad format.\n");
840 new = alloca((strlen(value
)+strlen(newpassword
)+1));
841 sprintf(new,"%s%s", newpassword
, value
);
842 if (!(cat
= ast_category_get(cfg
, category
))) {
843 ast_log(LOG_WARNING
, "Failed to get category structure.\n");
846 ast_variable_update(cat
, vmu
->mailbox
, new, NULL
, 0);
849 /* save the results */
850 reset_user_pw(vmu
->context
, vmu
->mailbox
, newpassword
);
851 ast_copy_string(vmu
->password
, newpassword
, sizeof(vmu
->password
));
852 config_text_file_save(VOICEMAIL_CONFIG
, cfg
, "AppVoicemail");
856 /* check users.conf and update the password stored for the mailbox*/
857 /* if no vmsecret entry exists create one. */
858 if ((cfg
= ast_config_load_with_comments("users.conf"))) {
859 if (option_debug
> 3)
860 ast_log(LOG_DEBUG
, "we are looking for %s\n", vmu
->mailbox
);
861 while ((category
= ast_category_browse(cfg
, category
))) {
862 if (option_debug
> 3)
863 ast_log(LOG_DEBUG
, "users.conf: %s\n", category
);
864 if (!strcasecmp(category
, vmu
->mailbox
)) {
865 if (!(tmp
= ast_variable_retrieve(cfg
, category
, "vmsecret"))) {
866 if (option_debug
> 3)
867 ast_log(LOG_DEBUG
, "looks like we need to make vmsecret!\n");
868 var
= ast_variable_new("vmsecret", newpassword
);
870 new = alloca(strlen(newpassword
)+1);
871 sprintf(new, "%s", newpassword
);
872 if (!(cat
= ast_category_get(cfg
, category
))) {
873 if (option_debug
> 3)
874 ast_log(LOG_DEBUG
, "failed to get category!\n");
878 ast_variable_update(cat
, "vmsecret", new, NULL
, 0);
880 ast_variable_append(cat
, var
);
883 /* save the results and clean things up */
884 reset_user_pw(vmu
->context
, vmu
->mailbox
, newpassword
);
885 ast_copy_string(vmu
->password
, newpassword
, sizeof(vmu
->password
));
886 config_text_file_save("users.conf", cfg
, "AppVoicemail");
890 static void vm_change_password_shell(struct ast_vm_user
*vmu
, char *newpassword
)
893 snprintf(buf
,255,"%s %s %s %s",ext_pass_cmd
,vmu
->context
,vmu
->mailbox
,newpassword
);
894 if (!ast_safe_system(buf
)) {
895 ast_copy_string(vmu
->password
, newpassword
, sizeof(vmu
->password
));
896 /* Reset the password in memory, too */
897 reset_user_pw(vmu
->context
, vmu
->mailbox
, newpassword
);
901 static int make_dir(char *dest
, int len
, const char *context
, const char *ext
, const char *folder
)
903 return snprintf(dest
, len
, "%s%s/%s/%s", VM_SPOOL_DIR
, context
, ext
, folder
);
906 static int make_file(char *dest
, const int len
, const char *dir
, const int num
)
908 return snprintf(dest
, len
, "%s/msg%04d", dir
, num
);
911 /* same as mkstemp, but return a FILE * */
912 static FILE *vm_mkftemp(char *template)
915 int pfd
= mkstemp(template);
916 chmod(template, VOICEMAIL_FILE_MODE
& ~my_umask
);
918 p
= fdopen(pfd
, "w+");
927 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
928 * \param dest String. base directory.
929 * \param len Length of dest.
930 * \param context String. Ignored if is null or empty string.
931 * \param ext String. Ignored if is null or empty string.
932 * \param folder String. Ignored if is null or empty string.
933 * \return -1 on failure, 0 on success.
935 static int create_dirpath(char *dest
, int len
, const char *context
, const char *ext
, const char *folder
)
937 mode_t mode
= VOICEMAIL_DIR_MODE
;
939 if (!ast_strlen_zero(context
)) {
940 make_dir(dest
, len
, context
, "", "");
941 if (mkdir(dest
, mode
) && errno
!= EEXIST
) {
942 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dest
, strerror(errno
));
946 if (!ast_strlen_zero(ext
)) {
947 make_dir(dest
, len
, context
, ext
, "");
948 if (mkdir(dest
, mode
) && errno
!= EEXIST
) {
949 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dest
, strerror(errno
));
953 if (!ast_strlen_zero(folder
)) {
954 make_dir(dest
, len
, context
, ext
, folder
);
955 if (mkdir(dest
, mode
) && errno
!= EEXIST
) {
956 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dest
, strerror(errno
));
963 static char *mbox(int id
)
965 static char *msgs
[] = {
977 return (id
>= 0 && id
< (sizeof(msgs
)/sizeof(msgs
[0]))) ? msgs
[id
] : "tmp";
980 static void free_user(struct ast_vm_user
*vmu
)
982 if (ast_test_flag(vmu
, VM_ALLOCED
))
986 /* All IMAP-specific functions should go in this block. This
987 * keeps them from being spread out all over the code */
989 static void vm_imap_delete(int msgnum
, struct ast_vm_user
*vmu
)
992 struct vm_state
*vms
;
993 unsigned long messageNum
;
995 /* Greetings aren't stored in IMAP, so we can't delete them there */
1000 if (!(vms
= get_vm_state_by_mailbox(vmu
->mailbox
, vmu
->context
, 1)) && !(vms
= get_vm_state_by_mailbox(vmu
->mailbox
, vmu
->context
, 0))) {
1001 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
);
1005 /* find real message number based on msgnum */
1006 /* this may be an index into vms->msgArray based on the msgnum. */
1007 messageNum
= vms
->msgArray
[msgnum
];
1008 if (messageNum
== 0) {
1009 ast_log(LOG_WARNING
, "msgnum %d, mailbox message %lu is zero.\n",msgnum
,messageNum
);
1012 if (option_debug
> 2)
1013 ast_log(LOG_DEBUG
, "deleting msgnum %d, which is mailbox message %lu\n",msgnum
,messageNum
);
1014 /* delete message */
1015 snprintf (arg
, sizeof(arg
), "%lu",messageNum
);
1016 mail_setflag (vms
->mailstream
,arg
,"\\DELETED");
1019 static int imap_retrieve_file(const char *dir
, const int msgnum
, const struct ast_vm_user
*vmu
)
1022 char *header_content
;
1023 char *attachedfilefmt
;
1024 const char *cid_num
;
1025 const char *cid_name
;
1026 const char *duration
;
1027 const char *context
;
1028 const char *category
;
1029 const char *origtime
;
1030 struct vm_state
*vms
;
1031 char text_file
[PATH_MAX
];
1032 FILE *text_file_ptr
;
1034 /* Greetings are not stored on the IMAP server, so we should not
1035 * attempt to retrieve them.
1041 /* Before anything can happen, we need a vm_state so that we can
1042 * actually access the imap server through the vms->mailstream
1044 if(!(vms
= get_vm_state_by_mailbox(vmu
->mailbox
, vmu
->context
, 1)) && !(vms
= get_vm_state_by_mailbox(vmu
->mailbox
, vmu
->context
, 0))) {
1045 /* This should not happen. If it does, then I guess we'd
1046 * need to create the vm_state, extract which mailbox to
1047 * open, and then set up the msgArray so that the correct
1048 * IMAP message could be accessed. If I have seen correctly
1049 * though, the vms should be obtainable from the vmstates list
1050 * and should have its msgArray properly set up.
1052 ast_log(LOG_ERROR
, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu
->mailbox
);
1055 make_file(vms
->fn
, sizeof(vms
->fn
), dir
, msgnum
);
1057 /* Don't try to retrieve a message from IMAP if it already is on the file system */
1058 if (ast_fileexists(vms
->fn
, NULL
, NULL
) > 0) {
1062 if (option_debug
> 2)
1063 ast_log (LOG_DEBUG
,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum
, vms
->msgArray
[msgnum
]);
1064 if (vms
->msgArray
[msgnum
] == 0) {
1065 ast_log (LOG_WARNING
,"Trying to access unknown message\n");
1069 /* This will only work for new messages... */
1070 header_content
= mail_fetchheader (vms
->mailstream
, vms
->msgArray
[msgnum
]);
1071 /* empty string means no valid header */
1072 if (ast_strlen_zero(header_content
)) {
1073 ast_log (LOG_ERROR
,"Could not fetch header for message number %ld\n",vms
->msgArray
[msgnum
]);
1077 mail_fetchstructure (vms
->mailstream
,vms
->msgArray
[msgnum
],&body
);
1079 /* We have the body, now we extract the file name of the first attachment. */
1080 if (body
->nested
.part
&& body
->nested
.part
->next
&& body
->nested
.part
->next
->body
.parameter
->value
) {
1081 attachedfilefmt
= ast_strdupa(body
->nested
.part
->next
->body
.parameter
->value
);
1083 ast_log(LOG_ERROR
, "There is no file attached to this IMAP message.\n");
1087 /* Find the format of the attached file */
1089 strsep(&attachedfilefmt
, ".");
1090 if (!attachedfilefmt
) {
1091 ast_log(LOG_ERROR
, "File format could not be obtained from IMAP message attachment\n");
1095 save_body(body
, vms
, "2", attachedfilefmt
);
1097 /* Get info from headers!! */
1098 snprintf(text_file
, sizeof(text_file
), "%s.%s", vms
->fn
, "txt");
1100 if (!(text_file_ptr
= fopen(text_file
, "w"))) {
1101 ast_log(LOG_WARNING
, "Unable to open/create file %s: %s\n", text_file
, strerror(errno
));
1104 fprintf(text_file_ptr
, "%s\n", "[message]");
1106 cid_num
= get_header_by_tag(header_content
, "X-Asterisk-VM-Caller-ID-Num:");
1107 cid_name
= get_header_by_tag(header_content
, "X-Asterisk-VM-Caller-ID-Name:");
1108 fprintf(text_file_ptr
, "callerid=\"%s\" <%s>\n", S_OR(cid_name
, ""), S_OR(cid_num
, ""));
1109 context
= get_header_by_tag(header_content
, "X-Asterisk-VM-Context:");
1110 fprintf(text_file_ptr
, "context=%s\n", S_OR(context
, ""));
1111 origtime
= get_header_by_tag(header_content
, "X-Asterisk-VM-Orig-time:");
1112 fprintf(text_file_ptr
, "origtime=%s\n", S_OR(origtime
, ""));
1113 duration
= get_header_by_tag(header_content
, "X-Asterisk-VM-Duration:");
1114 fprintf(text_file_ptr
, "duration=%s\n", S_OR(origtime
, ""));
1115 category
= get_header_by_tag(header_content
, "X-Asterisk-VM-Category:");
1116 fprintf(text_file_ptr
, "category=%s\n", S_OR(category
, ""));
1118 fclose(text_file_ptr
);
1122 static int folder_int(const char *folder
)
1124 /*assume a NULL folder means INBOX*/
1127 if (!strcasecmp(folder
, "INBOX"))
1129 else if (!strcasecmp(folder
, "Old"))
1131 else if (!strcasecmp(folder
, "Work"))
1133 else if (!strcasecmp(folder
, "Family"))
1135 else if (!strcasecmp(folder
, "Friends"))
1137 else if (!strcasecmp(folder
, "Cust1"))
1139 else if (!strcasecmp(folder
, "Cust2"))
1141 else if (!strcasecmp(folder
, "Cust3"))
1143 else if (!strcasecmp(folder
, "Cust4"))
1145 else if (!strcasecmp(folder
, "Cust5"))
1147 else /*assume they meant INBOX if folder is not found otherwise*/
1151 static int messagecount(const char *context
, const char *mailbox
, const char *folder
)
1156 struct ast_vm_user
*vmu
, vmus
;
1157 struct vm_state
*vms_p
;
1159 int fold
= folder_int(folder
);
1161 if (ast_strlen_zero(mailbox
))
1164 /* We have to get the user before we can open the stream! */
1165 /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
1166 vmu
= find_user(&vmus
, context
, mailbox
);
1168 ast_log (LOG_ERROR
,"Couldn't find mailbox %s in context %s\n",mailbox
,context
);
1171 /* No IMAP account available */
1172 if (vmu
->imapuser
[0] == '\0') {
1173 ast_log (LOG_WARNING
,"IMAP user not set for mailbox %s\n",vmu
->mailbox
);
1178 /* check if someone is accessing this box right now... */
1179 vms_p
= get_vm_state_by_imapuser(vmu
->imapuser
,1);
1181 vms_p
= get_vm_state_by_mailbox(mailbox
, context
, 1);
1184 if (option_debug
> 2)
1185 ast_log (LOG_DEBUG
,"Returning before search - user is logged in\n");
1186 if (fold
== 0) {/*INBOX*/
1187 return vms_p
->newmessages
;
1189 if (fold
== 1) {/*Old messages*/
1190 return vms_p
->oldmessages
;
1194 /* add one if not there... */
1195 vms_p
= get_vm_state_by_imapuser(vmu
->imapuser
,0);
1197 vms_p
= get_vm_state_by_mailbox(mailbox
, context
, 0);
1201 if (!(vms_p
= create_vm_state_from_user(vmu
))) {
1202 ast_log(LOG_WARNING
, "Unable to allocate space for new vm_state!\n");
1206 ret
= init_mailstream(vms_p
, fold
);
1207 if (!vms_p
->mailstream
) {
1208 ast_log (LOG_ERROR
,"IMAP mailstream is NULL\n");
1212 pgm
= mail_newsearchpgm ();
1213 hdr
= mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox
);
1219 /* In the special case where fold is 1 (old messages) we have to do things a bit
1220 * differently. Old messages are stored in the INBOX but are marked as "seen"
1229 vms_p
->vmArrayIndex
= 0;
1230 mail_search_full (vms_p
->mailstream
, NULL
, pgm
, NIL
);
1232 vms_p
->newmessages
= vms_p
->vmArrayIndex
;
1234 vms_p
->oldmessages
= vms_p
->vmArrayIndex
;
1235 /*Freeing the searchpgm also frees the searchhdr*/
1236 mail_free_searchpgm(&pgm
);
1238 return vms_p
->vmArrayIndex
;
1240 mail_ping(vms_p
->mailstream
);
1245 static int imap_store_file(char *dir
, char *mailboxuser
, char *mailboxcontext
, int msgnum
, struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *fmt
, int duration
, struct vm_state
*vms
)
1247 char *myserveremail
= serveremail
;
1252 char tmp
[80] = "/tmp/astmail-XXXXXX";
1258 /*Greetings are not retrieved from IMAP, so there is no reason to attempt storing them there either*/
1262 /* Attach only the first format */
1263 fmt
= ast_strdupa(fmt
);
1265 strsep(&stringp
, "|");
1267 if (!ast_strlen_zero(vmu
->serveremail
))
1268 myserveremail
= vmu
->serveremail
;
1270 make_file(fn
, sizeof(fn
), dir
, msgnum
);
1272 if (ast_strlen_zero(vmu
->email
)) {
1273 /*we need the vmu->email to be set when we call make_email_file, but if we keep it set,
1274 * a duplicate e-mail will be created. So at the end of this function, we will revert back to an empty
1275 * string if tempcopy is 1
1277 ast_copy_string(vmu
->email
, vmu
->imapuser
, sizeof(vmu
->email
));
1281 if (!strcmp(fmt
, "wav49"))
1283 if (option_debug
> 2)
1284 ast_log(LOG_DEBUG
, "Storing file '%s', format '%s'\n", fn
, fmt
);
1285 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1287 if ((p
= vm_mkftemp(tmp
)) == NULL
) {
1288 ast_log(LOG_WARNING
, "Unable to store '%s' (can't create temporary file)\n", fn
);
1290 *(vmu
->email
) = '\0';
1293 make_email_file(p
, myserveremail
, vmu
, msgnum
, vmu
->context
, vmu
->mailbox
, S_OR(chan
->cid
.cid_num
, NULL
), S_OR(chan
->cid
.cid_name
, NULL
), fn
, fmt
, duration
, 1, chan
, NULL
, 1);
1294 /* read mail file to memory */
1297 if ((buf
= ast_malloc(len
+1)) == NIL
) {
1298 ast_log(LOG_ERROR
, "Can't allocate %ld bytes to read message\n", len
+1);
1302 fread(buf
, len
, 1, p
);
1303 ((char *)buf
)[len
] = '\0';
1304 INIT(&str
, mail_string
, buf
, len
);
1305 init_mailstream(vms
, 0);
1306 imap_mailbox_name(mailbox
, sizeof(mailbox
), vms
, 0, 1);
1307 if (!mail_append(vms
->mailstream
, mailbox
, &str
))
1308 ast_log(LOG_ERROR
, "Error while sending the message to %s\n", mailbox
);
1312 if (option_debug
> 2)
1313 ast_log(LOG_DEBUG
, "%s stored\n", fn
);
1314 /* Using messagecount to populate the last place in the msgArray
1315 * is less than optimal, but it's the only way given the current setup
1317 messagecount(vmu
->context
, vmu
->mailbox
, "INBOX");
1320 *(vmu
->email
) = '\0';
1325 static int inboxcount(const char *mailbox_context
, int *newmsgs
, int *oldmsgs
)
1327 char tmp
[PATH_MAX
] = "";
1337 if (option_debug
> 2)
1338 ast_log (LOG_DEBUG
,"Mailbox is set to %s\n",mailbox_context
);
1339 /* If no mailbox, return immediately */
1340 if (ast_strlen_zero(mailbox_context
))
1343 ast_copy_string(tmp
, mailbox_context
, sizeof(tmp
));
1344 context
= strchr(tmp
, '@');
1345 if (strchr(mailbox_context
, ',')) {
1347 ast_copy_string(tmp
, mailbox_context
, sizeof(tmp
));
1349 while ((cur
= strsep(&mb
, ", "))) {
1350 if (!ast_strlen_zero(cur
)) {
1351 if (inboxcount(cur
, newmsgs
? &tmpnew
: NULL
, oldmsgs
? &tmpold
: NULL
))
1368 context
= "default";
1369 mailboxnc
= (char *)mailbox_context
;
1372 if ((*newmsgs
= messagecount(context
, mailboxnc
, "INBOX")) < 0)
1376 if ((*oldmsgs
= messagecount(context
, mailboxnc
, "Old")) < 0)
1383 static int has_voicemail(const char *mailbox
, const char *folder
)
1385 char tmp
[256], *tmp2
, *mbox
, *context
;
1386 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
1388 if (strchr(tmp2
, ',')) {
1389 while ((mbox
= strsep(&tmp2
, ","))) {
1390 if (!ast_strlen_zero(mbox
)) {
1391 if (has_voicemail(mbox
, folder
))
1396 if ((context
= strchr(tmp
, '@')))
1399 context
= "default";
1400 return messagecount(context
, tmp
, folder
) ? 1 : 0;
1403 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
)
1405 struct vm_state
*sendvms
= NULL
, *destvms
= NULL
;
1406 char messagestring
[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
1407 if (msgnum
>= recip
->maxmsg
) {
1408 ast_log(LOG_WARNING
, "Unable to copy mail, mailbox %s is full\n", recip
->mailbox
);
1411 if (!(sendvms
= get_vm_state_by_imapuser(vmu
->imapuser
, 0))) {
1412 ast_log(LOG_ERROR
, "Couldn't get vm_state for originator's mailbox!!\n");
1415 if (!(destvms
= get_vm_state_by_imapuser(recip
->imapuser
, 0))) {
1416 ast_log(LOG_ERROR
, "Couldn't get vm_state for destination mailbox!\n");
1419 snprintf(messagestring
, sizeof(messagestring
), "%ld", sendvms
->msgArray
[msgnum
]);
1420 if ((mail_copy(sendvms
->mailstream
, messagestring
, (char *) mbox(imbox
)) == T
))
1422 ast_log(LOG_WARNING
, "Unable to copy message from mailbox %s to mailbox %s\n", vmu
->mailbox
, recip
->mailbox
);
1426 static void imap_mailbox_name(char *spec
, size_t len
, struct vm_state
*vms
, int box
, int use_folder
)
1428 char tmp
[256], *t
= tmp
;
1429 size_t left
= sizeof(tmp
);
1432 ast_copy_string(vms
->curbox
, mbox(0), sizeof(vms
->curbox
));
1433 snprintf(vms
->vmbox
, sizeof(vms
->vmbox
), "vm-%s", mbox(1));
1435 ast_copy_string(vms
->curbox
, mbox(box
), sizeof(vms
->curbox
));
1436 snprintf(vms
->vmbox
, sizeof(vms
->vmbox
), "vm-%s", vms
->curbox
);
1439 /* Build up server information */
1440 ast_build_string(&t
, &left
, "{%s:%s/imap", imapserver
, imapport
);
1442 /* Add authentication user if present */
1443 if (!ast_strlen_zero(authuser
))
1444 ast_build_string(&t
, &left
, "/authuser=%s", authuser
);
1446 /* Add flags if present */
1447 if (!ast_strlen_zero(imapflags
))
1448 ast_build_string(&t
, &left
, "/%s", imapflags
);
1450 /* End with username */
1451 ast_build_string(&t
, &left
, "/user=%s}", vms
->imapuser
);
1453 if (box
== 0 || box
== 1)
1454 snprintf(spec
, len
, "%s%s", tmp
, use_folder
? imapfolder
: "INBOX");
1456 snprintf(spec
, len
, "%s%s%c%s", tmp
, imapfolder
, delimiter
, mbox(box
));
1459 static int init_mailstream(struct vm_state
*vms
, int box
)
1461 MAILSTREAM
*stream
= NIL
;
1466 ast_log (LOG_ERROR
,"vm_state is NULL!\n");
1469 if (option_debug
> 2)
1470 ast_log (LOG_DEBUG
,"vm_state user is:%s\n",vms
->imapuser
);
1471 if (vms
->mailstream
== NIL
|| !vms
->mailstream
) {
1473 ast_log (LOG_DEBUG
,"mailstream not set.\n");
1475 stream
= vms
->mailstream
;
1477 /* debug = T; user wants protocol telemetry? */
1478 debug
= NIL
; /* NO protocol telemetry? */
1480 if (delimiter
== '\0') { /* did not probe the server yet */
1482 #ifdef USE_SYSTEM_IMAP
1483 #include <imap/linkage.c>
1484 #elif defined(USE_SYSTEM_CCLIENT)
1485 #include <c-client/linkage.c>
1487 #include "linkage.c"
1489 /* Connect to INBOX first to get folders delimiter */
1490 imap_mailbox_name(tmp
, sizeof(tmp
), vms
, 0, 1);
1491 ast_mutex_lock(&vms
->lock
);
1492 stream
= mail_open (stream
, tmp
, debug
? OP_DEBUG
: NIL
);
1493 ast_mutex_unlock(&vms
->lock
);
1494 if (stream
== NIL
) {
1495 ast_log (LOG_ERROR
, "Can't connect to imap server %s\n", tmp
);
1498 get_mailbox_delimiter(stream
);
1499 /* update delimiter in imapfolder */
1500 for (cp
= imapfolder
; *cp
; cp
++)
1504 /* Now connect to the target folder */
1505 imap_mailbox_name(tmp
, sizeof(tmp
), vms
, box
, 1);
1506 if (option_debug
> 2)
1507 ast_log (LOG_DEBUG
,"Before mail_open, server: %s, box:%d\n", tmp
, box
);
1508 ast_mutex_lock(&vms
->lock
);
1509 vms
->mailstream
= mail_open (stream
, tmp
, debug
? OP_DEBUG
: NIL
);
1510 ast_mutex_unlock(&vms
->lock
);
1511 if (vms
->mailstream
== NIL
) {
1518 static int open_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
, int box
)
1524 ast_copy_string(vms
->imapuser
,vmu
->imapuser
, sizeof(vms
->imapuser
));
1525 if (option_debug
> 2)
1526 ast_log(LOG_DEBUG
,"Before init_mailstream, user is %s\n",vmu
->imapuser
);
1527 ret
= init_mailstream(vms
, box
);
1528 if (ret
!= 0 || !vms
->mailstream
) {
1529 ast_log (LOG_ERROR
,"Could not initialize mailstream\n");
1533 create_dirpath(vms
->curdir
, sizeof(vms
->curdir
), vmu
->context
, vms
->username
, vms
->curbox
);
1537 if (option_debug
> 2)
1538 ast_log(LOG_DEBUG
, "Mailbox name set to: %s, about to check quotas\n", mbox(box
));
1539 check_quota(vms
,(char *)mbox(box
));
1542 pgm
= mail_newsearchpgm();
1544 /* Check IMAP folder for Asterisk messages only... */
1545 hdr
= mail_newsearchheader ("X-Asterisk-VM-Extension", vmu
->mailbox
);
1550 /* if box = 0, check for new, if box = 1, check for read */
1554 } else if (box
== 1) {
1559 vms
->vmArrayIndex
= 0;
1560 if (option_debug
> 2)
1561 ast_log(LOG_DEBUG
,"Before mail_search_full, user is %s\n",vmu
->imapuser
);
1562 mail_search_full (vms
->mailstream
, NULL
, pgm
, NIL
);
1564 vms
->lastmsg
= vms
->vmArrayIndex
- 1;
1566 mail_free_searchpgm(&pgm
);
1570 static void write_file(char *filename
, char *buffer
, unsigned long len
)
1574 output
= fopen (filename
, "w");
1575 fwrite (buffer
, len
, 1, output
);
1579 void mm_searched(MAILSTREAM
*stream
, unsigned long number
)
1581 struct vm_state
*vms
;
1584 mailbox
= stream
->mailbox
;
1585 user
= get_user_by_mailbox(mailbox
);
1586 vms
= get_vm_state_by_imapuser(user
,2);
1588 if (option_debug
> 2)
1589 ast_log (LOG_DEBUG
, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number
,vms
->vmArrayIndex
,vms
->interactive
);
1590 vms
->msgArray
[vms
->vmArrayIndex
++] = number
;
1592 ast_log (LOG_ERROR
, "No state found.\n");
1596 static struct ast_vm_user
*find_user_realtime_imapuser(const char *imapuser
)
1598 struct ast_variable
*var
;
1599 struct ast_vm_user
*vmu
;
1601 vmu
= ast_calloc(1, sizeof *vmu
);
1604 ast_set_flag(vmu
, VM_ALLOCED
);
1605 populate_defaults(vmu
);
1607 var
= ast_load_realtime("voicemail", "imapuser", imapuser
, NULL
);
1609 apply_options_full(vmu
, var
);
1610 ast_variables_destroy(var
);
1618 /* Interfaces to C-client */
1620 void mm_exists(MAILSTREAM
* stream
, unsigned long number
)
1622 /* mail_ping will callback here if new mail! */
1623 if (option_debug
> 3)
1624 ast_log (LOG_DEBUG
, "Entering EXISTS callback for message %ld\n", number
);
1625 if (number
== 0) return;
1630 void mm_expunged(MAILSTREAM
* stream
, unsigned long number
)
1632 /* mail_ping will callback here if expunged mail! */
1633 if (option_debug
> 3)
1634 ast_log (LOG_DEBUG
, "Entering EXPUNGE callback for message %ld\n", number
);
1635 if (number
== 0) return;
1640 void mm_flags(MAILSTREAM
* stream
, unsigned long number
)
1642 /* mail_ping will callback here if read mail! */
1643 if (option_debug
> 3)
1644 ast_log (LOG_DEBUG
, "Entering FLAGS callback for message %ld\n", number
);
1645 if (number
== 0) return;
1650 void mm_notify(MAILSTREAM
* stream
, char *string
, long errflg
)
1652 mm_log (string
, errflg
);
1656 void mm_list(MAILSTREAM
* stream
, int delim
, char *mailbox
, long attributes
)
1658 if (delimiter
== '\0') {
1661 if (option_debug
> 4) {
1662 ast_log(LOG_DEBUG
, "Delimiter set to %c and mailbox %s\n",delim
, mailbox
);
1663 if (attributes
& LATT_NOINFERIORS
)
1664 ast_log(LOG_DEBUG
, "no inferiors\n");
1665 if (attributes
& LATT_NOSELECT
)
1666 ast_log(LOG_DEBUG
, "no select\n");
1667 if (attributes
& LATT_MARKED
)
1668 ast_log(LOG_DEBUG
, "marked\n");
1669 if (attributes
& LATT_UNMARKED
)
1670 ast_log(LOG_DEBUG
, "unmarked\n");
1675 void mm_lsub(MAILSTREAM
* stream
, int delimiter
, char *mailbox
, long attributes
)
1677 if (option_debug
> 4) {
1678 ast_log(LOG_DEBUG
, "Delimiter set to %c and mailbox %s\n",delimiter
, mailbox
);
1679 if (attributes
& LATT_NOINFERIORS
)
1680 ast_log(LOG_DEBUG
, "no inferiors\n");
1681 if (attributes
& LATT_NOSELECT
)
1682 ast_log(LOG_DEBUG
, "no select\n");
1683 if (attributes
& LATT_MARKED
)
1684 ast_log(LOG_DEBUG
, "marked\n");
1685 if (attributes
& LATT_UNMARKED
)
1686 ast_log(LOG_DEBUG
, "unmarked\n");
1691 void mm_status(MAILSTREAM
* stream
, char *mailbox
, MAILSTATUS
* status
)
1693 ast_log (LOG_NOTICE
," Mailbox %s", mailbox
);
1694 if (status
->flags
& SA_MESSAGES
)
1695 ast_log (LOG_NOTICE
,", %lu messages", status
->messages
);
1696 if (status
->flags
& SA_RECENT
)
1697 ast_log (LOG_NOTICE
,", %lu recent", status
->recent
);
1698 if (status
->flags
& SA_UNSEEN
)
1699 ast_log (LOG_NOTICE
,", %lu unseen", status
->unseen
);
1700 if (status
->flags
& SA_UIDVALIDITY
)
1701 ast_log (LOG_NOTICE
,", %lu UID validity", status
->uidvalidity
);
1702 if (status
->flags
& SA_UIDNEXT
)
1703 ast_log (LOG_NOTICE
,", %lu next UID", status
->uidnext
);
1704 ast_log (LOG_NOTICE
,"\n");
1708 void mm_log(char *string
, long errflg
)
1710 switch ((short) errflg
) {
1713 ast_log(LOG_DEBUG
,"IMAP Info: %s\n", string
);
1717 ast_log (LOG_WARNING
,"IMAP Warning: %s\n", string
);
1720 ast_log (LOG_ERROR
,"IMAP Error: %s\n", string
);
1726 void mm_dlog(char *string
)
1728 ast_log (LOG_NOTICE
, "%s\n", string
);
1732 void mm_login(NETMBX
* mb
, char *user
, char *pwd
, long trial
)
1734 struct ast_vm_user
*vmu
;
1736 if (option_debug
> 3)
1737 ast_log(LOG_DEBUG
, "Entering callback mm_login\n");
1739 ast_copy_string(user
, mb
->user
, MAILTMPLEN
);
1741 /* We should only do this when necessary */
1742 if (!ast_strlen_zero(authpassword
)) {
1743 ast_copy_string(pwd
, authpassword
, MAILTMPLEN
);
1745 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
1746 if (!strcasecmp(mb
->user
, vmu
->imapuser
)) {
1747 ast_copy_string(pwd
, vmu
->imappassword
, MAILTMPLEN
);
1752 if ((vmu
= find_user_realtime_imapuser(mb
->user
))) {
1753 ast_copy_string(pwd
, vmu
->imappassword
, MAILTMPLEN
);
1761 void mm_critical(MAILSTREAM
* stream
)
1766 void mm_nocritical(MAILSTREAM
* stream
)
1771 long mm_diskerror(MAILSTREAM
* stream
, long errcode
, long serious
)
1773 kill (getpid (), SIGSTOP
);
1778 void mm_fatal(char *string
)
1780 ast_log(LOG_ERROR
,"IMAP access FATAL error: %s\n", string
);
1783 /* C-client callback to handle quota */
1784 static void mm_parsequota(MAILSTREAM
*stream
, unsigned char *msg
, QUOTALIST
*pquota
)
1786 struct vm_state
*vms
;
1789 unsigned long usage
= 0;
1790 unsigned long limit
= 0;
1793 usage
= pquota
->usage
;
1794 limit
= pquota
->limit
;
1795 pquota
= pquota
->next
;
1798 mailbox
= stream
->mailbox
;
1799 user
= get_user_by_mailbox(mailbox
);
1800 vms
= get_vm_state_by_imapuser(user
,2);
1802 if (option_debug
> 2)
1803 ast_log (LOG_DEBUG
, "User %s usage is %lu, limit is %lu\n",user
,usage
,limit
);
1804 vms
->quota_usage
= usage
;
1805 vms
->quota_limit
= limit
;
1807 ast_log (LOG_ERROR
, "No state found.\n");
1811 static char *get_header_by_tag(char *header
, char *tag
)
1817 if (!header
|| !tag
)
1820 taglen
= strlen(tag
) + 1;
1824 start
= strstr(header
, tag
);
1828 ast_mutex_lock(&imaptemp_lock
);
1829 ast_copy_string(imaptemp
, start
+taglen
, sizeof(imaptemp
));
1830 ast_mutex_unlock(&imaptemp_lock
);
1831 if ((eol_pnt
= strchr(imaptemp
,'\r')) || (eol_pnt
= strchr(imaptemp
,'\n')))
1836 static char *get_user_by_mailbox(char *mailbox
)
1838 char *start
, *quote
;
1844 start
= strstr(mailbox
,"/user=");
1848 ast_mutex_lock(&imaptemp_lock
);
1849 ast_copy_string(imaptemp
, start
+6, sizeof(imaptemp
));
1850 ast_mutex_unlock(&imaptemp_lock
);
1852 quote
= strchr(imaptemp
,'\"');
1853 if (!quote
) { /* if username is not in quotes */
1854 eol_pnt
= strchr(imaptemp
,'/');
1856 eol_pnt
= strchr(imaptemp
,'}');
1861 eol_pnt
= strchr(imaptemp
+1,'\"');
1867 static struct vm_state
*create_vm_state_from_user(struct ast_vm_user
*vmu
)
1869 struct vm_state
*vms_p
;
1871 if (option_debug
> 4)
1872 ast_log(LOG_DEBUG
,"Adding new vmstate for %s\n",vmu
->imapuser
);
1873 if (!(vms_p
= ast_calloc(1, sizeof(*vms_p
))))
1875 ast_copy_string(vms_p
->imapuser
,vmu
->imapuser
, sizeof(vms_p
->imapuser
));
1876 ast_copy_string(vms_p
->username
, vmu
->mailbox
, sizeof(vms_p
->username
)); /* save for access from interactive entry point */
1877 ast_copy_string(vms_p
->context
, vmu
->context
, sizeof(vms_p
->context
));
1878 vms_p
->mailstream
= NIL
; /* save for access from interactive entry point */
1879 if (option_debug
> 4)
1880 ast_log(LOG_DEBUG
,"Copied %s to %s\n",vmu
->imapuser
,vms_p
->imapuser
);
1882 /* set mailbox to INBOX! */
1883 ast_copy_string(vms_p
->curbox
, mbox(0), sizeof(vms_p
->curbox
));
1884 init_vm_state(vms_p
);
1885 vmstate_insert(vms_p
);
1889 static struct vm_state
*get_vm_state_by_imapuser(char *user
, int interactive
)
1891 struct vmstate
*vlist
= NULL
;
1893 ast_mutex_lock(&vmstate_lock
);
1897 if (vlist
->vms
->imapuser
) {
1898 if (!strcmp(vlist
->vms
->imapuser
,user
)) {
1899 if (interactive
== 2) {
1900 ast_mutex_unlock(&vmstate_lock
);
1902 } else if (vlist
->vms
->interactive
== interactive
) {
1903 ast_mutex_unlock(&vmstate_lock
);
1908 if (option_debug
> 2)
1909 ast_log(LOG_DEBUG
, " error: imapuser is NULL for %s\n",user
);
1912 if (option_debug
> 2)
1913 ast_log(LOG_DEBUG
, " error: vms is NULL for %s\n",user
);
1915 vlist
= vlist
->next
;
1917 ast_mutex_unlock(&vmstate_lock
);
1918 if (option_debug
> 2)
1919 ast_log(LOG_DEBUG
, "%s not found in vmstates\n",user
);
1923 static struct vm_state
*get_vm_state_by_mailbox(const char *mailbox
, const char *context
, int interactive
)
1925 struct vmstate
*vlist
= NULL
;
1926 const char *local_context
= S_OR(context
, "default");
1928 ast_mutex_lock(&vmstate_lock
);
1930 if (option_debug
> 2)
1931 ast_log(LOG_DEBUG
, "Mailbox set to %s\n",mailbox
);
1934 if (vlist
->vms
->username
&& vlist
->vms
->context
) {
1935 if (option_debug
> 2)
1936 ast_log(LOG_DEBUG
, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox
,interactive
,vlist
->vms
->username
,vlist
->vms
->interactive
);
1937 if (!strcmp(vlist
->vms
->username
,mailbox
) && !(strcmp(vlist
->vms
->context
, local_context
)) && vlist
->vms
->interactive
== interactive
) {
1938 if (option_debug
> 2)
1939 ast_log(LOG_DEBUG
, " Found it!\n");
1940 ast_mutex_unlock(&vmstate_lock
);
1944 if (option_debug
> 2)
1945 ast_log(LOG_DEBUG
, " error: username or context is NULL for %s\n",mailbox
);
1948 if (option_debug
> 2)
1949 ast_log(LOG_DEBUG
, " error: vms is NULL for %s\n",mailbox
);
1951 vlist
= vlist
->next
;
1953 ast_mutex_unlock(&vmstate_lock
);
1954 if (option_debug
> 2)
1955 ast_log(LOG_DEBUG
, "%s not found in vmstates\n",mailbox
);
1959 static void vmstate_insert(struct vm_state
*vms
)
1962 struct vm_state
*altvms
;
1964 /* If interactive, it probably already exists, and we should
1965 use the one we already have since it is more up to date.
1966 We can compare the username to find the duplicate */
1967 if (vms
->interactive
== 1) {
1968 altvms
= get_vm_state_by_mailbox(vms
->username
, vms
->context
, 0);
1970 if (option_debug
> 2)
1971 ast_log(LOG_DEBUG
, "Duplicate mailbox %s, copying message info...\n",vms
->username
);
1972 vms
->newmessages
= altvms
->newmessages
;
1973 vms
->oldmessages
= altvms
->oldmessages
;
1974 /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
1975 copy_msgArray(vms
, altvms
);
1976 vms
->vmArrayIndex
= altvms
->vmArrayIndex
;
1977 vms
->lastmsg
= altvms
->lastmsg
;
1978 vms
->curmsg
= altvms
->curmsg
;
1979 /* get a pointer to the persistent store */
1980 vms
->persist_vms
= altvms
;
1981 /* Reuse the mailstream? */
1982 vms
->mailstream
= altvms
->mailstream
;
1983 /* vms->mailstream = NIL; */
1987 v
= (struct vmstate
*)malloc(sizeof(struct vmstate
));
1989 ast_log(LOG_ERROR
, "Out of memory\n");
1991 if (option_debug
> 2)
1992 ast_log(LOG_DEBUG
, "Inserting vm_state for user:%s, mailbox %s\n",vms
->imapuser
,vms
->username
);
1993 ast_mutex_lock(&vmstate_lock
);
1997 ast_mutex_unlock(&vmstate_lock
);
2000 static void vmstate_delete(struct vm_state
*vms
)
2002 struct vmstate
*vc
, *vf
= NULL
, *vl
= NULL
;
2003 struct vm_state
*altvms
;
2005 /* If interactive, we should copy pertainent info
2006 back to the persistent state (to make update immediate) */
2007 if (vms
->interactive
== 1) {
2008 altvms
= vms
->persist_vms
;
2010 if (option_debug
> 2)
2011 ast_log(LOG_DEBUG
, "Duplicate mailbox %s, copying message info...\n",vms
->username
);
2012 altvms
->newmessages
= vms
->newmessages
;
2013 altvms
->oldmessages
= vms
->oldmessages
;
2014 altvms
->updated
= 1;
2018 ast_mutex_lock(&vmstate_lock
);
2020 if (option_debug
> 2)
2021 ast_log(LOG_DEBUG
, "Removing vm_state for user:%s, mailbox %s\n",vms
->imapuser
,vms
->username
);
2023 if (vc
->vms
== vms
) {
2026 vl
->next
= vc
->next
;
2028 vmstates
= vc
->next
;
2035 ast_log(LOG_ERROR
, "No vmstate found for user:%s, mailbox %s\n",vms
->imapuser
,vms
->username
);
2037 ast_mutex_destroy(&vf
->vms
->lock
);
2040 ast_mutex_unlock(&vmstate_lock
);
2043 static void set_update(MAILSTREAM
* stream
)
2045 struct vm_state
*vms
;
2049 mailbox
= stream
->mailbox
;
2050 user
= get_user_by_mailbox(mailbox
);
2051 vms
= get_vm_state_by_imapuser(user
, 0);
2053 if (option_debug
> 2)
2054 ast_log (LOG_DEBUG
, "User %s mailbox set for update.\n",user
);
2055 vms
->updated
= 1; /* set updated flag since mailbox changed */
2057 if (option_debug
> 2)
2058 ast_log (LOG_WARNING
, "User %s mailbox not found for update.\n",user
);
2062 static void init_vm_state(struct vm_state
*vms
)
2065 vms
->vmArrayIndex
= 0;
2066 for (x
= 0; x
< 256; x
++) {
2067 vms
->msgArray
[x
] = 0;
2069 ast_mutex_init(&vms
->lock
);
2072 static void copy_msgArray(struct vm_state
*dst
, struct vm_state
*src
)
2075 for (x
= 0; x
<256; x
++) {
2076 dst
->msgArray
[x
] = src
->msgArray
[x
];
2080 static int save_body(BODY
*body
, struct vm_state
*vms
, char *section
, char *format
)
2085 unsigned long newlen
;
2088 if (!body
|| body
== NIL
)
2090 body_content
= mail_fetchbody (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
], section
, &len
);
2091 if (body_content
!= NIL
) {
2092 snprintf(filename
, sizeof(filename
), "%s.%s", vms
->fn
, format
);
2093 /* ast_log (LOG_DEBUG,body_content); */
2094 body_decoded
= rfc822_base64 ((unsigned char *)body_content
, len
, &newlen
);
2095 write_file (filename
, (char *) body_decoded
, newlen
);
2100 /* get delimiter via mm_list callback */
2101 static void get_mailbox_delimiter(MAILSTREAM
*stream
) {
2103 snprintf(tmp
, sizeof(tmp
), "{%s}", imapserver
);
2104 mail_list(stream
, tmp
, "*");
2107 /* Check Quota for user */
2108 static void check_quota(struct vm_state
*vms
, char *mailbox
) {
2109 mail_parameters(NULL
, SET_QUOTA
, (void *) mm_parsequota
);
2110 if (option_debug
> 2)
2111 ast_log(LOG_DEBUG
, "Mailbox name set to: %s, about to check quotas\n", mailbox
);
2112 if (vms
&& vms
->mailstream
!= NULL
) {
2113 imap_getquotaroot(vms
->mailstream
, mailbox
);
2115 ast_log(LOG_WARNING
,"Mailstream not available for mailbox: %s\n",mailbox
);
2120 /* only return failure if ast_lock_path returns 'timeout',
2121 not if the path does not exist or any other reason
2123 static int vm_lock_path(const char *path
)
2125 switch (ast_lock_path(path
)) {
2126 case AST_LOCK_TIMEOUT
:
2135 struct generic_prepare_struct
{
2141 static SQLHSTMT
generic_prepare(struct odbc_obj
*obj
, void *data
)
2143 struct generic_prepare_struct
*gps
= data
;
2147 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
2148 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2149 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
2152 res
= SQLPrepare(stmt
, (unsigned char *)gps
->sql
, SQL_NTS
);
2153 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2154 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", gps
->sql
);
2155 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
2158 for (i
= 0; i
< gps
->argc
; i
++)
2159 SQLBindParameter(stmt
, i
+ 1, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(gps
->argv
[i
]), 0, gps
->argv
[i
], 0, NULL
);
2164 static int retrieve_file(char *dir
, int msgnum
)
2170 void *fdm
= MAP_FAILED
;
2171 SQLSMALLINT colcount
=0;
2178 SQLSMALLINT datatype
;
2179 SQLSMALLINT decimaldigits
;
2180 SQLSMALLINT nullable
;
2186 char full_fn
[PATH_MAX
];
2188 char *argv
[] = { dir
, msgnums
};
2189 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 2, .argv
= argv
};
2191 struct odbc_obj
*obj
;
2192 obj
= ast_odbc_request_obj(odbc_database
, 0);
2194 ast_copy_string(fmt
, vmfmts
, sizeof(fmt
));
2195 c
= strchr(fmt
, '|');
2198 if (!strcasecmp(fmt
, "wav49"))
2200 snprintf(msgnums
, sizeof(msgnums
),"%d", msgnum
);
2202 make_file(fn
, sizeof(fn
), dir
, msgnum
);
2204 ast_copy_string(fn
, dir
, sizeof(fn
));
2205 snprintf(full_fn
, sizeof(full_fn
), "%s.txt", fn
);
2207 if (!(f
= fopen(full_fn
, "w+"))) {
2208 ast_log(LOG_WARNING
, "Failed to open/create '%s'\n", full_fn
);
2212 snprintf(full_fn
, sizeof(full_fn
), "%s.%s", fn
, fmt
);
2213 snprintf(sql
, sizeof(sql
), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table
);
2214 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
2216 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2217 ast_odbc_release_obj(obj
);
2220 res
= SQLFetch(stmt
);
2221 if (res
== SQL_NO_DATA
) {
2222 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2223 ast_odbc_release_obj(obj
);
2226 else if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2227 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
2228 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2229 ast_odbc_release_obj(obj
);
2232 fd
= open(full_fn
, O_RDWR
| O_CREAT
| O_TRUNC
, 0770);
2234 ast_log(LOG_WARNING
, "Failed to write '%s': %s\n", full_fn
, strerror(errno
));
2235 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2236 ast_odbc_release_obj(obj
);
2239 res
= SQLNumResultCols(stmt
, &colcount
);
2240 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2241 ast_log(LOG_WARNING
, "SQL Column Count error!\n[%s]\n\n", sql
);
2242 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2243 ast_odbc_release_obj(obj
);
2247 fprintf(f
, "[message]\n");
2248 for (x
=0;x
<colcount
;x
++) {
2250 collen
= sizeof(coltitle
);
2251 res
= SQLDescribeCol(stmt
, x
+ 1, (unsigned char *)coltitle
, sizeof(coltitle
), &collen
,
2252 &datatype
, &colsize
, &decimaldigits
, &nullable
);
2253 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2254 ast_log(LOG_WARNING
, "SQL Describe Column error!\n[%s]\n\n", sql
);
2255 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2256 ast_odbc_release_obj(obj
);
2259 if (!strcasecmp(coltitle
, "recording")) {
2261 res
= SQLGetData(stmt
, x
+ 1, SQL_BINARY
, rowdata
, 0, &colsize2
);
2265 lseek(fd
, fdlen
- 1, SEEK_SET
);
2266 if (write(fd
, tmp
, 1) != 1) {
2271 /* Read out in small chunks */
2272 for (offset
= 0; offset
< colsize2
; offset
+= CHUNKSIZE
) {
2273 if ((fdm
= mmap(NULL
, CHUNKSIZE
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, offset
)) == MAP_FAILED
) {
2274 ast_log(LOG_WARNING
, "Could not mmap the output file: %s (%d)\n", strerror(errno
), errno
);
2275 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
2276 ast_odbc_release_obj(obj
);
2279 res
= SQLGetData(stmt
, x
+ 1, SQL_BINARY
, fdm
, CHUNKSIZE
, NULL
);
2280 munmap(fdm
, CHUNKSIZE
);
2281 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2282 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
2284 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
2285 ast_odbc_release_obj(obj
);
2290 truncate(full_fn
, fdlen
);
2294 res
= SQLGetData(stmt
, x
+ 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), &ind
);
2295 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2296 SQLINTEGER nativeerror
= 0;
2297 SQLSMALLINT diagbytes
= 0;
2298 unsigned char state
[10], diagnostic
[256];
2299 SQLGetDiagRec(SQL_HANDLE_STMT
, stmt
, 1, state
, &nativeerror
, diagnostic
, sizeof(diagnostic
), &diagbytes
);
2300 ast_log(LOG_WARNING
, "SQL Get Data error: %s: %s!\n[%s]\n\n", state
, diagnostic
, sql
);
2301 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2302 ast_odbc_release_obj(obj
);
2305 if (strcasecmp(coltitle
, "msgnum") && strcasecmp(coltitle
, "dir") && f
)
2306 fprintf(f
, "%s=%s\n", coltitle
, rowdata
);
2309 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2310 ast_odbc_release_obj(obj
);
2312 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
2321 static int last_message_index(struct ast_vm_user
*vmu
, char *dir
)
2328 char *argv
[] = { dir
};
2329 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 1, .argv
= argv
};
2331 struct odbc_obj
*obj
;
2332 obj
= ast_odbc_request_obj(odbc_database
, 0);
2334 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table
);
2335 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
2337 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2338 ast_odbc_release_obj(obj
);
2341 res
= SQLFetch(stmt
);
2342 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2343 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
2344 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2345 ast_odbc_release_obj(obj
);
2348 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
2349 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2350 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
2351 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2352 ast_odbc_release_obj(obj
);
2355 if (sscanf(rowdata
, "%d", &x
) != 1)
2356 ast_log(LOG_WARNING
, "Failed to read message count!\n");
2357 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2358 ast_odbc_release_obj(obj
);
2360 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
2365 static int message_exists(char *dir
, int msgnum
)
2373 char *argv
[] = { dir
, msgnums
};
2374 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 2, .argv
= argv
};
2376 struct odbc_obj
*obj
;
2377 obj
= ast_odbc_request_obj(odbc_database
, 0);
2379 snprintf(msgnums
, sizeof(msgnums
), "%d", msgnum
);
2380 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table
);
2381 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
2383 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2384 ast_odbc_release_obj(obj
);
2387 res
= SQLFetch(stmt
);
2388 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2389 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
2390 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2391 ast_odbc_release_obj(obj
);
2394 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
2395 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2396 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
2397 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2398 ast_odbc_release_obj(obj
);
2401 if (sscanf(rowdata
, "%d", &x
) != 1)
2402 ast_log(LOG_WARNING
, "Failed to read message count!\n");
2403 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2404 ast_odbc_release_obj(obj
);
2406 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
2411 static int count_messages(struct ast_vm_user
*vmu
, char *dir
)
2413 return last_message_index(vmu
, dir
) + 1;
2416 static void delete_file(char *sdir
, int smsg
)
2421 char *argv
[] = { sdir
, msgnums
};
2422 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 2, .argv
= argv
};
2424 struct odbc_obj
*obj
;
2425 obj
= ast_odbc_request_obj(odbc_database
, 0);
2427 snprintf(msgnums
, sizeof(msgnums
), "%d", smsg
);
2428 snprintf(sql
, sizeof(sql
), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table
);
2429 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
2431 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2433 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2434 ast_odbc_release_obj(obj
);
2436 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
2440 static void copy_file(char *sdir
, int smsg
, char *ddir
, int dmsg
, char *dmailboxuser
, char *dmailboxcontext
)
2446 struct odbc_obj
*obj
;
2447 char *argv
[] = { ddir
, msgnumd
, dmailboxuser
, dmailboxcontext
, sdir
, msgnums
};
2448 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 6, .argv
= argv
};
2450 delete_file(ddir
, dmsg
);
2451 obj
= ast_odbc_request_obj(odbc_database
, 0);
2453 snprintf(msgnums
, sizeof(msgnums
), "%d", smsg
);
2454 snprintf(msgnumd
, sizeof(msgnumd
), "%d", dmsg
);
2455 snprintf(sql
, sizeof(sql
), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table
,odbc_table
);
2456 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
2458 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql
);
2460 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
2461 ast_odbc_release_obj(obj
);
2463 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
2467 struct insert_cb_struct
{
2471 size_t recordinglen
;
2473 const char *context
;
2474 const char *macrocontext
;
2475 const char *callerid
;
2476 const char *origtime
;
2477 const char *duration
;
2479 char *mailboxcontext
;
2480 const char *category
;
2484 static SQLHSTMT
insert_cb(struct odbc_obj
*obj
, void *vd
)
2486 struct insert_cb_struct
*d
= vd
;
2490 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
2491 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2492 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
2496 res
= SQLPrepare(stmt
, (unsigned char *)d
->sql
, SQL_NTS
);
2497 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2498 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", d
->sql
);
2499 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
2503 SQLBindParameter(stmt
, 1, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(d
->dir
), 0, (void *)d
->dir
, 0, NULL
);
2504 SQLBindParameter(stmt
, 2, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(d
->msgnum
), 0, (void *)d
->msgnum
, 0, NULL
);
2505 SQLBindParameter(stmt
, 3, SQL_PARAM_INPUT
, SQL_C_BINARY
, SQL_LONGVARBINARY
, d
->recordinglen
, 0, (void *)d
->recording
, 0, &d
->indlen
);
2506 SQLBindParameter(stmt
, 4, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(d
->context
), 0, (void *)d
->context
, 0, NULL
);
2507 SQLBindParameter(stmt
, 5, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(d
->macrocontext
), 0, (void *)d
->macrocontext
, 0, NULL
);
2508 SQLBindParameter(stmt
, 6, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(d
->callerid
), 0, (void *)d
->callerid
, 0, NULL
);
2509 SQLBindParameter(stmt
, 7, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(d
->origtime
), 0, (void *)d
->origtime
, 0, NULL
);
2510 SQLBindParameter(stmt
, 8, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(d
->duration
), 0, (void *)d
->duration
, 0, NULL
);
2511 SQLBindParameter(stmt
, 9, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(d
->mailboxuser
), 0, (void *)d
->mailboxuser
, 0, NULL
);
2512 SQLBindParameter(stmt
, 10, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(d
->mailboxcontext
), 0, (void *)d
->mailboxcontext
, 0, NULL
);
2513 if (!ast_strlen_zero(d
->category
)) {
2514 SQLBindParameter(stmt
, 11, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(d
->category
), 0, (void *)d
->category
, 0, NULL
);
2520 static int store_file(char *dir
, char *mailboxuser
, char *mailboxcontext
, int msgnum
)
2524 void *fdm
= MAP_FAILED
;
2530 char full_fn
[PATH_MAX
];
2533 struct insert_cb_struct d
= {
2541 .mailboxuser
= mailboxuser
,
2542 .mailboxcontext
= mailboxcontext
,
2546 struct ast_config
*cfg
=NULL
;
2547 struct odbc_obj
*obj
;
2549 delete_file(dir
, msgnum
);
2550 obj
= ast_odbc_request_obj(odbc_database
, 0);
2552 ast_copy_string(fmt
, vmfmts
, sizeof(fmt
));
2553 c
= strchr(fmt
, '|');
2556 if (!strcasecmp(fmt
, "wav49"))
2558 snprintf(msgnums
, sizeof(msgnums
),"%d", msgnum
);
2560 make_file(fn
, sizeof(fn
), dir
, msgnum
);
2562 ast_copy_string(fn
, dir
, sizeof(fn
));
2563 snprintf(full_fn
, sizeof(full_fn
), "%s.txt", fn
);
2564 cfg
= ast_config_load(full_fn
);
2565 snprintf(full_fn
, sizeof(full_fn
), "%s.%s", fn
, fmt
);
2566 fd
= open(full_fn
, O_RDWR
);
2568 ast_log(LOG_WARNING
, "Open of sound file '%s' failed: %s\n", full_fn
, strerror(errno
));
2569 ast_odbc_release_obj(obj
);
2573 d
.context
= ast_variable_retrieve(cfg
, "message", "context");
2574 if (!d
.context
) d
.context
= "";
2575 d
.macrocontext
= ast_variable_retrieve(cfg
, "message", "macrocontext");
2576 if (!d
.macrocontext
) d
.macrocontext
= "";
2577 d
.callerid
= ast_variable_retrieve(cfg
, "message", "callerid");
2578 if (!d
.callerid
) d
.callerid
= "";
2579 d
.origtime
= ast_variable_retrieve(cfg
, "message", "origtime");
2580 if (!d
.origtime
) d
.origtime
= "";
2581 d
.duration
= ast_variable_retrieve(cfg
, "message", "duration");
2582 if (!d
.duration
) d
.duration
= "";
2583 d
.category
= ast_variable_retrieve(cfg
, "message", "category");
2584 if (!d
.category
) d
.category
= "";
2586 fdlen
= lseek(fd
, 0, SEEK_END
);
2587 lseek(fd
, 0, SEEK_SET
);
2588 printf("Length is %zd\n", fdlen
);
2589 fdm
= mmap(NULL
, fdlen
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, 0);
2590 if (fdm
== MAP_FAILED
) {
2591 ast_log(LOG_WARNING
, "Memory map failed!\n");
2592 ast_odbc_release_obj(obj
);
2596 d
.recordinglen
= d
.indlen
= fdlen
; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
2597 if (!ast_strlen_zero(d
.category
))
2598 snprintf(sql
, sizeof(sql
), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table
);
2600 snprintf(sql
, sizeof(sql
), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?, ? , ?,?,?,?,?,?,?)",odbc_table
);
2601 stmt
= ast_odbc_prepare_and_execute(obj
, insert_cb
, &d
);
2603 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
2605 ast_odbc_release_obj(obj
);
2607 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
2610 ast_config_destroy(cfg
);
2611 if (fdm
!= MAP_FAILED
)
2618 static void rename_file(char *sdir
, int smsg
, char *mailboxuser
, char *mailboxcontext
, char *ddir
, int dmsg
)
2624 struct odbc_obj
*obj
;
2625 char *argv
[] = { ddir
, msgnumd
, mailboxuser
, mailboxcontext
, sdir
, msgnums
};
2626 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 6, .argv
= argv
};
2628 delete_file(ddir
, dmsg
);
2629 obj
= ast_odbc_request_obj(odbc_database
, 0);
2631 snprintf(msgnums
, sizeof(msgnums
), "%d", smsg
);
2632 snprintf(msgnumd
, sizeof(msgnumd
), "%d", dmsg
);
2633 snprintf(sql
, sizeof(sql
), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table
);
2634 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
2636 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2638 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
2639 ast_odbc_release_obj(obj
);
2641 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
2646 #ifndef IMAP_STORAGE
2647 static int count_messages(struct ast_vm_user
*vmu
, char *dir
)
2649 /* Find all .txt files - even if they are not in sequence from 0000 */
2653 struct dirent
*vment
= NULL
;
2655 if (vm_lock_path(dir
))
2656 return ERROR_LOCK_PATH
;
2658 if ((vmdir
= opendir(dir
))) {
2659 while ((vment
= readdir(vmdir
))) {
2660 if (strlen(vment
->d_name
) > 7 && !strncmp(vment
->d_name
+ 7, ".txt", 4))
2665 ast_unlock_path(dir
);
2670 static void rename_file(char *sfn
, char *dfn
)
2672 char stxt
[PATH_MAX
];
2673 char dtxt
[PATH_MAX
];
2674 ast_filerename(sfn
,dfn
,NULL
);
2675 snprintf(stxt
, sizeof(stxt
), "%s.txt", sfn
);
2676 snprintf(dtxt
, sizeof(dtxt
), "%s.txt", dfn
);
2682 * A negative return value indicates an error.
2684 #if (!defined(IMAP_STORAGE) && !defined(ODBC_STORAGE))
2685 static int last_message_index(struct ast_vm_user
*vmu
, char *dir
)
2690 if (vm_lock_path(dir
))
2691 return ERROR_LOCK_PATH
;
2693 for (x
= 0; x
< vmu
->maxmsg
; x
++) {
2694 make_file(fn
, sizeof(fn
), dir
, x
);
2695 if (ast_fileexists(fn
, NULL
, NULL
) < 1)
2698 ast_unlock_path(dir
);
2704 #if (defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2705 static int remove_file(char *dir
, int msgnum
)
2708 char full_fn
[PATH_MAX
];
2712 snprintf(msgnums
, sizeof(msgnums
), "%d", msgnum
);
2713 make_file(fn
, sizeof(fn
), dir
, msgnum
);
2715 #ifndef IMAP_STORAGE
2716 ast_copy_string(fn
, dir
, sizeof(fn
));
2718 /*IMAP stores greetings locally so it should not
2719 * try to dispose of them
2724 ast_filedelete(fn
, NULL
);
2725 snprintf(full_fn
, sizeof(full_fn
), "%s.txt", fn
);
2731 static int copy(char *infile
, char *outfile
)
2739 #ifdef HARDLINK_WHEN_POSSIBLE
2740 /* Hard link if possible; saves disk space & is faster */
2741 if (link(infile
, outfile
)) {
2743 if ((ifd
= open(infile
, O_RDONLY
)) < 0) {
2744 ast_log(LOG_WARNING
, "Unable to open %s in read-only mode: %s\n", infile
, strerror(errno
));
2747 if ((ofd
= open(outfile
, O_WRONLY
| O_TRUNC
| O_CREAT
, VOICEMAIL_FILE_MODE
)) < 0) {
2748 ast_log(LOG_WARNING
, "Unable to open %s in write-only mode: %s\n", outfile
, strerror(errno
));
2753 len
= read(ifd
, buf
, sizeof(buf
));
2755 ast_log(LOG_WARNING
, "Read failed on %s: %s\n", infile
, strerror(errno
));
2761 res
= write(ofd
, buf
, len
);
2762 if (errno
== ENOMEM
|| errno
== ENOSPC
|| res
!= len
) {
2763 ast_log(LOG_WARNING
, "Write failed on %s (%d of %d): %s\n", outfile
, res
, len
, strerror(errno
));
2773 #ifdef HARDLINK_WHEN_POSSIBLE
2775 /* Hard link succeeded */
2781 static void copy_plain_file(char *frompath
, char *topath
)
2783 char frompath2
[PATH_MAX
], topath2
[PATH_MAX
];
2784 ast_filecopy(frompath
, topath
, NULL
);
2785 snprintf(frompath2
, sizeof(frompath2
), "%s.txt", frompath
);
2786 snprintf(topath2
, sizeof(topath2
), "%s.txt", topath
);
2787 copy(frompath2
, topath2
);
2790 static int vm_delete(char *file
)
2795 txtsize
= (strlen(file
) + 5)*sizeof(char);
2796 txt
= alloca(txtsize
);
2797 /* Sprintf here would safe because we alloca'd exactly the right length,
2798 * but trying to eliminate all sprintf's anyhow
2800 snprintf(txt
, txtsize
, "%s.txt", file
);
2802 return ast_filedelete(file
, NULL
);
2805 static int inbuf(struct baseio
*bio
, FILE *fi
)
2812 if ((l
= fread(bio
->iobuf
,1,BASEMAXINLINE
,fi
)) <= 0) {
2826 static int inchar(struct baseio
*bio
, FILE *fi
)
2828 if (bio
->iocp
>=bio
->iolen
) {
2829 if (!inbuf(bio
, fi
))
2833 return bio
->iobuf
[bio
->iocp
++];
2836 static int ochar(struct baseio
*bio
, int c
, FILE *so
)
2838 if (bio
->linelength
>=BASELINELEN
) {
2839 if (fputs(eol
,so
)==EOF
)
2845 if (putc(((unsigned char)c
),so
)==EOF
)
2853 static int base_encode(char *filename
, FILE *so
)
2855 unsigned char dtable
[BASEMAXINLINE
];
2860 memset(&bio
, 0, sizeof(bio
));
2861 bio
.iocp
= BASEMAXINLINE
;
2863 if (!(fi
= fopen(filename
, "rb"))) {
2864 ast_log(LOG_WARNING
, "Failed to open file: %s: %s\n", filename
, strerror(errno
));
2868 for (i
= 0;i
<9;i
++) {
2871 dtable
[26+i
]= 'a'+i
;
2872 dtable
[26+i
+9]= 'j'+i
;
2874 for (i
= 0;i
<8;i
++) {
2875 dtable
[i
+18]= 'S'+i
;
2876 dtable
[26+i
+18]= 's'+i
;
2878 for (i
= 0;i
<10;i
++) {
2879 dtable
[52+i
]= '0'+i
;
2885 unsigned char igroup
[3],ogroup
[4];
2888 igroup
[0]= igroup
[1]= igroup
[2]= 0;
2890 for (n
= 0;n
<3;n
++) {
2891 if ((c
= inchar(&bio
, fi
)) == EOF
) {
2896 igroup
[n
]= (unsigned char)c
;
2900 ogroup
[0]= dtable
[igroup
[0]>>2];
2901 ogroup
[1]= dtable
[((igroup
[0]&3)<<4)|(igroup
[1]>>4)];
2902 ogroup
[2]= dtable
[((igroup
[1]&0xF)<<2)|(igroup
[2]>>6)];
2903 ogroup
[3]= dtable
[igroup
[2]&0x3F];
2913 ochar(&bio
, ogroup
[i
], so
);
2919 if (fputs(eol
,so
)==EOF
)
2925 static void prep_email_sub_vars(struct ast_channel
*ast
, struct ast_vm_user
*vmu
, int msgnum
, char *context
, char *mailbox
, char *cidnum
, char *cidname
, char *dur
, char *date
, char *passdata
, size_t passdatasize
, const char *category
)
2928 /* Prepare variables for substition in email body and subject */
2929 pbx_builtin_setvar_helper(ast
, "VM_NAME", vmu
->fullname
);
2930 pbx_builtin_setvar_helper(ast
, "VM_DUR", dur
);
2931 snprintf(passdata
, passdatasize
, "%d", msgnum
);
2932 pbx_builtin_setvar_helper(ast
, "VM_MSGNUM", passdata
);
2933 pbx_builtin_setvar_helper(ast
, "VM_CONTEXT", context
);
2934 pbx_builtin_setvar_helper(ast
, "VM_MAILBOX", mailbox
);
2935 pbx_builtin_setvar_helper(ast
, "VM_CALLERID", (!ast_strlen_zero(cidname
) || !ast_strlen_zero(cidnum
)) ?
2936 ast_callerid_merge(callerid
, sizeof(callerid
), cidname
, cidnum
, NULL
) : "an unknown caller");
2937 pbx_builtin_setvar_helper(ast
, "VM_CIDNAME", (!ast_strlen_zero(cidname
) ? cidname
: "an unknown caller"));
2938 pbx_builtin_setvar_helper(ast
, "VM_CIDNUM", (!ast_strlen_zero(cidnum
) ? cidnum
: "an unknown caller"));
2939 pbx_builtin_setvar_helper(ast
, "VM_DATE", date
);
2940 pbx_builtin_setvar_helper(ast
, "VM_CATEGORY", category
? ast_strdupa(category
) : "no category");
2943 static char *quote(const char *from
, char *to
, size_t len
)
2947 for (; ptr
< to
+ len
- 1; from
++) {
2950 else if (*from
== '\0')
2954 if (ptr
< to
+ len
- 1)
2960 * fill in *tm for current time according to the proper timezone, if any.
2961 * Return tm so it can be used as a function argument.
2963 static const struct tm
*vmu_tm(const struct ast_vm_user
*vmu
, struct tm
*tm
)
2965 const struct vm_zone
*z
= NULL
;
2966 time_t t
= time(NULL
);
2968 /* Does this user have a timezone specified? */
2969 if (!ast_strlen_zero(vmu
->zonetag
)) {
2970 /* Find the zone in the list */
2971 AST_LIST_LOCK(&zones
);
2972 AST_LIST_TRAVERSE(&zones
, z
, list
) {
2973 if (!strcmp(z
->name
, vmu
->zonetag
))
2976 AST_LIST_UNLOCK(&zones
);
2978 ast_localtime(&t
, tm
, z
? z
->timezone
: NULL
);
2982 static void make_email_file(FILE *p
, char *srcemail
, struct ast_vm_user
*vmu
, int msgnum
, char *context
, char *mailbox
, char *cidnum
, char *cidname
, char *attach
, char *format
, int duration
, int attach_user_voicemail
, struct ast_channel
*chan
, const char *category
, int imap
)
2985 char host
[MAXHOSTNAMELEN
] = "";
2991 char enc_cidnum
[256] = "", enc_cidname
[256] = "";
2994 size_t len_passdata
;
3002 strip_control(cidnum
, enc_cidnum
, sizeof(enc_cidnum
));
3005 strip_control(cidname
, enc_cidname
, sizeof(enc_cidname
));
3007 gethostname(host
, sizeof(host
) - 1);
3008 if (strchr(srcemail
, '@'))
3009 ast_copy_string(who
, srcemail
, sizeof(who
));
3011 snprintf(who
, sizeof(who
), "%s@%s", srcemail
, host
);
3013 snprintf(dur
, sizeof(dur
), "%d:%02d", duration
/ 60, duration
% 60);
3014 strftime(date
, sizeof(date
), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu
, &tm
));
3015 fprintf(p
, "Date: %s" ENDL
, date
);
3017 /* Set date format for voicemail mail */
3018 strftime(date
, sizeof(date
), emaildateformat
, &tm
);
3021 struct ast_channel
*ast
;
3022 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
3024 int vmlen
= strlen(fromstring
)*3 + 200;
3025 if ((passdata
= alloca(vmlen
))) {
3026 memset(passdata
, 0, vmlen
);
3027 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, enc_cidnum
, enc_cidname
, dur
, date
, passdata
, vmlen
, category
);
3028 pbx_substitute_variables_helper(ast
, fromstring
, passdata
, vmlen
);
3029 len_passdata
= strlen(passdata
) * 2 + 3;
3030 passdata2
= alloca(len_passdata
);
3031 fprintf(p
, "From: %s <%s>" ENDL
, quote(passdata
, passdata2
, len_passdata
), who
);
3033 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
3034 ast_channel_free(ast
);
3036 ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
3038 fprintf(p
, "From: Asterisk PBX <%s>" ENDL
, who
);
3039 len_passdata
= strlen(vmu
->fullname
) * 2 + 3;
3040 passdata2
= alloca(len_passdata
);
3041 fprintf(p
, "To: %s <%s>" ENDL
, quote(vmu
->fullname
, passdata2
, len_passdata
), vmu
->email
);
3043 struct ast_channel
*ast
;
3044 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
3046 int vmlen
= strlen(emailsubject
)*3 + 200;
3047 if ((passdata
= alloca(vmlen
))) {
3048 memset(passdata
, 0, vmlen
);
3049 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
3050 pbx_substitute_variables_helper(ast
, emailsubject
, passdata
, vmlen
);
3051 fprintf(p
, "Subject: %s" ENDL
, passdata
);
3053 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
3055 ast_channel_free(ast
);
3057 ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
3059 } else if (*emailtitle
) {
3060 fprintf(p
, emailtitle
, msgnum
+ 1, mailbox
) ;
3062 } else if (ast_test_flag((&globalflags
), VM_PBXSKIP
)) {
3063 fprintf(p
, "Subject: New message %d in mailbox %s" ENDL
, msgnum
+ 1, mailbox
);
3065 fprintf(p
, "Subject: [PBX]: New message %d in mailbox %s" ENDL
, msgnum
+ 1, mailbox
);
3067 fprintf(p
, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL
, msgnum
+ 1, (unsigned int)ast_random(), mailbox
, (int)getpid(), host
);
3069 /* additional information needed for IMAP searching */
3070 fprintf(p
, "X-Asterisk-VM-Message-Num: %d" ENDL
, msgnum
+ 1);
3071 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
3072 fprintf(p
, "X-Asterisk-VM-Server-Name: %s" ENDL
, fromstring
);
3073 fprintf(p
, "X-Asterisk-VM-Context: %s" ENDL
, context
);
3074 fprintf(p
, "X-Asterisk-VM-Extension: %s" ENDL
, mailbox
);
3075 fprintf(p
, "X-Asterisk-VM-Priority: %d" ENDL
, chan
->priority
);
3076 fprintf(p
, "X-Asterisk-VM-Caller-channel: %s" ENDL
, chan
->name
);
3077 fprintf(p
, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL
, enc_cidnum
);
3078 fprintf(p
, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL
, enc_cidname
);
3079 fprintf(p
, "X-Asterisk-VM-Duration: %d" ENDL
, duration
);
3080 if (!ast_strlen_zero(category
)) {
3081 fprintf(p
, "X-Asterisk-VM-Category: %s" ENDL
, category
);
3083 fprintf(p
, "X-Asterisk-VM-Orig-date: %s" ENDL
, date
);
3084 fprintf(p
, "X-Asterisk-VM-Orig-time: %ld" ENDL
, (long)time(NULL
));
3086 if (!ast_strlen_zero(cidnum
)) {
3087 fprintf(p
, "X-Asterisk-CallerID: %s" ENDL
, enc_cidnum
);
3089 if (!ast_strlen_zero(cidname
)) {
3090 fprintf(p
, "X-Asterisk-CallerIDName: %s" ENDL
, enc_cidname
);
3092 fprintf(p
, "MIME-Version: 1.0" ENDL
);
3093 if (attach_user_voicemail
) {
3094 /* Something unique. */
3095 snprintf(bound
, sizeof(bound
), "----voicemail_%d%s%d%d", msgnum
+ 1, mailbox
, (int)getpid(), (unsigned int)ast_random());
3097 fprintf(p
, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL
, bound
);
3098 fprintf(p
, ENDL ENDL
"This is a multi-part message in MIME format." ENDL ENDL
);
3099 fprintf(p
, "--%s" ENDL
, bound
);
3101 fprintf(p
, "Content-Type: text/plain; charset=%s" ENDL
"Content-Transfer-Encoding: 8bit" ENDL ENDL
, charset
);
3103 struct ast_channel
*ast
;
3104 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
3106 int vmlen
= strlen(emailbody
)*3 + 200;
3107 if ((passdata
= alloca(vmlen
))) {
3108 memset(passdata
, 0, vmlen
);
3109 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
3110 pbx_substitute_variables_helper(ast
, emailbody
, passdata
, vmlen
);
3111 fprintf(p
, "%s" ENDL
, passdata
);
3113 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
3114 ast_channel_free(ast
);
3116 ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
3118 fprintf(p
, "Dear %s:" ENDL ENDL
"\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
3120 "in mailbox %s from %s, on %s so you might" ENDL
3121 "want to check it when you get a chance. Thanks!" ENDL ENDL
"\t\t\t\t--Asterisk" ENDL ENDL
, vmu
->fullname
,
3122 dur
, msgnum
+ 1, mailbox
, (cidname
? cidname
: (cidnum
? cidnum
: "an unknown caller")), date
);
3124 if (attach_user_voicemail
) {
3125 /* Eww. We want formats to tell us their own MIME type */
3126 char *ctype
= (!strcasecmp(format
, "ogg")) ? "application/" : "audio/x-";
3127 char tmpdir
[256], newtmp
[256];
3130 if (vmu
->volgain
< -.001 || vmu
->volgain
> .001) {
3131 create_dirpath(tmpdir
, sizeof(tmpdir
), vmu
->context
, vmu
->mailbox
, "tmp");
3132 snprintf(newtmp
, sizeof(newtmp
), "%s/XXXXXX", tmpdir
);
3133 tmpfd
= mkstemp(newtmp
);
3134 chmod(newtmp
, VOICEMAIL_FILE_MODE
& ~my_umask
);
3135 if (option_debug
> 2)
3136 ast_log(LOG_DEBUG
, "newtmp: %s\n", newtmp
);
3139 snprintf(tmpcmd
, sizeof(tmpcmd
), "sox -v %.4f %s.%s %s.%s", vmu
->volgain
, attach
, format
, newtmp
, format
);
3140 if ((soxstatus
= ast_safe_system(tmpcmd
)) == 0) {
3142 if (option_debug
> 2) {
3143 ast_log(LOG_DEBUG
, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach
, format
, vmu
->volgain
, mailbox
);
3146 ast_log(LOG_WARNING
, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach
, format
,
3147 soxstatus
== 1 ? "Problem with command line options" : "An error occurred during file processing");
3148 ast_log(LOG_WARNING
, "Voicemail attachment will have no volume gain.\n");
3152 fprintf(p
, "--%s" ENDL
, bound
);
3153 fprintf(p
, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL
, ctype
, format
, msgnum
+ 1, format
);
3154 fprintf(p
, "Content-Transfer-Encoding: base64" ENDL
);
3155 fprintf(p
, "Content-Description: Voicemail sound attachment." ENDL
);
3156 fprintf(p
, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL
, msgnum
+ 1, format
);
3157 snprintf(fname
, sizeof(fname
), "%s.%s", attach
, format
);
3158 base_encode(fname
, p
);
3159 fprintf(p
, ENDL
"--%s--" ENDL
"." ENDL
, bound
);
3168 static int sendmail(char *srcemail
, struct ast_vm_user
*vmu
, int msgnum
, char *context
, char *mailbox
, char *cidnum
, char *cidname
, char *attach
, char *format
, int duration
, int attach_user_voicemail
, struct ast_channel
*chan
, const char *category
)
3171 char tmp
[80] = "/tmp/astmail-XXXXXX";
3174 if (vmu
&& ast_strlen_zero(vmu
->email
)) {
3175 ast_log(LOG_WARNING
, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu
->mailbox
);
3178 if (!strcmp(format
, "wav49"))
3180 if (option_debug
> 2)
3181 ast_log(LOG_DEBUG
, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach
, format
, attach_user_voicemail
, ast_test_flag((&globalflags
), VM_ATTACH
));
3182 /* Make a temporary file instead of piping directly to sendmail, in case the mail
3184 if ((p
= vm_mkftemp(tmp
)) == NULL
) {
3185 ast_log(LOG_WARNING
, "Unable to launch '%s' (can't create temporary file)\n", mailcmd
);
3188 make_email_file(p
, srcemail
, vmu
, msgnum
, context
, mailbox
, cidnum
, cidname
, attach
, format
, duration
, attach_user_voicemail
, chan
, category
, 0);
3190 snprintf(tmp2
, sizeof(tmp2
), "( %s < %s ; rm -f %s ) &", mailcmd
, tmp
, tmp
);
3191 ast_safe_system(tmp2
);
3192 if (option_debug
> 2)
3193 ast_log(LOG_DEBUG
, "Sent mail to %s with command '%s'\n", vmu
->email
, mailcmd
);
3198 static int sendpage(char *srcemail
, char *pager
, int msgnum
, char *context
, char *mailbox
, char *cidnum
, char *cidname
, int duration
, struct ast_vm_user
*vmu
, const char *category
)
3201 char host
[MAXHOSTNAMELEN
] = "";
3204 char tmp
[80] = "/tmp/astmail-XXXXXX";
3205 char tmp2
[PATH_MAX
];
3209 if ((p
= vm_mkftemp(tmp
)) == NULL
) {
3210 ast_log(LOG_WARNING
, "Unable to launch '%s' (can't create temporary file)\n", mailcmd
);
3213 gethostname(host
, sizeof(host
)-1);
3214 if (strchr(srcemail
, '@'))
3215 ast_copy_string(who
, srcemail
, sizeof(who
));
3217 snprintf(who
, sizeof(who
), "%s@%s", srcemail
, host
);
3219 snprintf(dur
, sizeof(dur
), "%d:%02d", duration
/ 60, duration
% 60);
3220 strftime(date
, sizeof(date
), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu
, &tm
));
3221 fprintf(p
, "Date: %s\n", date
);
3223 if (*pagerfromstring
) {
3224 struct ast_channel
*ast
;
3225 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
3227 int vmlen
= strlen(fromstring
)*3 + 200;
3228 if ((passdata
= alloca(vmlen
))) {
3229 memset(passdata
, 0, vmlen
);
3230 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
3231 pbx_substitute_variables_helper(ast
, pagerfromstring
, passdata
, vmlen
);
3232 fprintf(p
, "From: %s <%s>\n", passdata
, who
);
3234 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
3235 ast_channel_free(ast
);
3236 } else ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
3238 fprintf(p
, "From: Asterisk PBX <%s>\n", who
);
3239 fprintf(p
, "To: %s\n", pager
);
3241 struct ast_channel
*ast
;
3242 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
3244 int vmlen
= strlen(pagersubject
) * 3 + 200;
3245 if ((passdata
= alloca(vmlen
))) {
3246 memset(passdata
, 0, vmlen
);
3247 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
3248 pbx_substitute_variables_helper(ast
, pagersubject
, passdata
, vmlen
);
3249 fprintf(p
, "Subject: %s\n\n", passdata
);
3250 } else ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
3251 ast_channel_free(ast
);
3252 } else ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
3254 fprintf(p
, "Subject: New VM\n\n");
3255 strftime(date
, sizeof(date
), "%A, %B %d, %Y at %r", &tm
);
3257 struct ast_channel
*ast
;
3258 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
3260 int vmlen
= strlen(pagerbody
)*3 + 200;
3261 if ((passdata
= alloca(vmlen
))) {
3262 memset(passdata
, 0, vmlen
);
3263 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
3264 pbx_substitute_variables_helper(ast
, pagerbody
, passdata
, vmlen
);
3265 fprintf(p
, "%s\n", passdata
);
3266 } else ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
3267 ast_channel_free(ast
);
3268 } else ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
3270 fprintf(p
, "New %s long msg in box %s\n"
3271 "from %s, on %s", dur
, mailbox
, (cidname
? cidname
: (cidnum
? cidnum
: "unknown")), date
);
3274 snprintf(tmp2
, sizeof(tmp2
), "( %s < %s ; rm -f %s ) &", mailcmd
, tmp
, tmp
);
3275 ast_safe_system(tmp2
);
3276 if (option_debug
> 2)
3277 ast_log(LOG_DEBUG
, "Sent page to %s with command '%s'\n", pager
, mailcmd
);
3282 static int get_date(char *s
, int len
)
3289 ast_localtime(&t
, &tm
, NULL
);
3291 return strftime(s
, len
, "%a %b %e %r %Z %Y", &tm
);
3294 static int play_greeting(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *filename
, char *ecodes
)
3301 RETRIEVE(filename
, -1, vmu
);
3302 if (ast_fileexists(filename
, NULL
, NULL
) > 0) {
3303 res
= ast_streamfile(chan
, filename
, chan
->language
);
3305 res
= ast_waitstream(chan
, ecodes
);
3307 if (success
== -1) {
3308 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
3310 ast_log(LOG_DEBUG
, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
3311 store_file(filename
, vmu
->mailbox
, vmu
->context
, -1);
3315 DISPOSE(filename
, -1);
3320 static int invent_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *ext
, int busy
, char *ecodes
)
3324 char dest
[PATH_MAX
];
3326 snprintf(fn
, sizeof(fn
), "%s%s/%s/greet", VM_SPOOL_DIR
, vmu
->context
, ext
);
3328 if ((res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, ext
, "greet"))) {
3329 ast_log(LOG_WARNING
, "Failed to make directory(%s)\n", fn
);
3333 res
= play_greeting(chan
, vmu
, fn
, ecodes
);
3335 /* File did not exist */
3336 res
= ast_stream_and_wait(chan
, "vm-theperson", chan
->language
, ecodes
);
3339 res
= ast_say_digit_str(chan
, ext
, ecodes
, chan
->language
);
3345 res
= ast_stream_and_wait(chan
, busy
? "vm-isonphone" : "vm-isunavail", chan
->language
, ecodes
);
3349 static void free_zone(struct vm_zone
*z
)
3355 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
3356 static int inboxcount(const char *mailbox
, int *newmsgs
, int *oldmsgs
)
3363 char tmp
[PATH_MAX
] = "";
3364 struct odbc_obj
*obj
;
3366 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 0 };
3373 /* If no mailbox, return immediately */
3374 if (ast_strlen_zero(mailbox
))
3377 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
3379 context
= strchr(tmp
, '@');
3384 context
= "default";
3386 obj
= ast_odbc_request_obj(odbc_database
, 0);
3388 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table
, VM_SPOOL_DIR
, context
, tmp
, "INBOX");
3389 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
3391 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
3392 ast_odbc_release_obj(obj
);
3395 res
= SQLFetch(stmt
);
3396 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
3397 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
3398 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
3399 ast_odbc_release_obj(obj
);
3402 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
3403 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
3404 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
3405 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
3406 ast_odbc_release_obj(obj
);
3409 *newmsgs
= atoi(rowdata
);
3410 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
3412 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table
, VM_SPOOL_DIR
, context
, tmp
, "Old");
3413 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
3415 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
3416 ast_odbc_release_obj(obj
);
3419 res
= SQLFetch(stmt
);
3420 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
3421 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
3422 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
3423 ast_odbc_release_obj(obj
);
3426 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
3427 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
3428 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
3429 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
3430 ast_odbc_release_obj(obj
);
3433 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
3434 ast_odbc_release_obj(obj
);
3435 *oldmsgs
= atoi(rowdata
);
3438 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
3444 static int messagecount(const char *context
, const char *mailbox
, const char *folder
)
3446 struct odbc_obj
*obj
= NULL
;
3449 SQLHSTMT stmt
= NULL
;
3452 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 0 };
3455 /* If no mailbox, return immediately */
3456 if (ast_strlen_zero(mailbox
))
3459 obj
= ast_odbc_request_obj(odbc_database
, 0);
3461 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table
, VM_SPOOL_DIR
, context
, mailbox
, folder
);
3462 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
3464 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
3467 res
= SQLFetch(stmt
);
3468 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
3469 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
3470 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
3473 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
3474 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
3475 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
3476 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
3479 nummsgs
= atoi(rowdata
);
3480 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
3482 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
3486 ast_odbc_release_obj(obj
);
3490 static int has_voicemail(const char *mailbox
, const char *folder
)
3492 char tmp
[256], *tmp2
= tmp
, *mbox
, *context
;
3493 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
3494 while ((context
= mbox
= strsep(&tmp2
, ","))) {
3495 strsep(&context
, "@");
3496 if (ast_strlen_zero(context
))
3497 context
= "default";
3498 if (messagecount(context
, mbox
, folder
))
3504 #ifndef IMAP_STORAGE
3505 /* copy message only used by file storage */
3506 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
)
3508 char fromdir
[PATH_MAX
], todir
[PATH_MAX
], frompath
[PATH_MAX
], topath
[PATH_MAX
];
3509 const char *frombox
= mbox(imbox
);
3512 ast_log(LOG_NOTICE
, "Copying message from %s@%s to %s@%s\n", vmu
->mailbox
, vmu
->context
, recip
->mailbox
, recip
->context
);
3514 create_dirpath(todir
, sizeof(todir
), recip
->context
, recip
->mailbox
, "INBOX");
3517 make_dir(fromdir
, sizeof(fromdir
), vmu
->context
, vmu
->mailbox
, frombox
);
3519 ast_copy_string(fromdir
, dir
, sizeof(fromdir
));
3521 make_file(frompath
, sizeof(frompath
), fromdir
, msgnum
);
3523 if (vm_lock_path(todir
))
3524 return ERROR_LOCK_PATH
;
3528 make_file(topath
, sizeof(topath
), todir
, recipmsgnum
);
3529 if (!EXISTS(todir
, recipmsgnum
, topath
, chan
->language
))
3532 } while (recipmsgnum
< recip
->maxmsg
);
3533 if (recipmsgnum
< recip
->maxmsg
) {
3534 if (EXISTS(fromdir
, msgnum
, frompath
, chan
->language
)) {
3535 COPY(fromdir
, msgnum
, todir
, recipmsgnum
, recip
->mailbox
, recip
->context
, frompath
, topath
);
3537 /* For ODBC storage, if the file we want to copy isn't yet in the database, then the SQL
3538 * copy will fail. Instead, we need to create a local copy, store it, and delete the local
3539 * copy. We don't have to #ifdef this because if file storage reaches this point, there's a
3540 * much worse problem happening and IMAP storage doesn't call this function
3542 copy_plain_file(frompath
, topath
);
3543 STORE(todir
, recip
->mailbox
, recip
->context
, recipmsgnum
, chan
, recip
, fmt
, duration
, NULL
);
3547 ast_log(LOG_ERROR
, "Recipient mailbox %s@%s is full\n", recip
->mailbox
, recip
->context
);
3549 ast_unlock_path(todir
);
3550 notify_new_message(chan
, recip
, recipmsgnum
, duration
, fmt
, S_OR(chan
->cid
.cid_num
, NULL
), S_OR(chan
->cid
.cid_name
, NULL
));
3555 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
3556 static int messagecount(const char *context
, const char *mailbox
, const char *folder
)
3558 return __has_voicemail(context
, mailbox
, folder
, 0);
3562 static int __has_voicemail(const char *context
, const char *mailbox
, const char *folder
, int shortcircuit
)
3570 /* If no mailbox, return immediately */
3571 if (ast_strlen_zero(mailbox
))
3574 context
= "default";
3575 snprintf(fn
, sizeof(fn
), "%s%s/%s/%s", VM_SPOOL_DIR
, context
, mailbox
, folder
);
3579 while ((de
= readdir(dir
))) {
3580 if (!strncasecmp(de
->d_name
, "msg", 3)) {
3584 } else if (!strncasecmp(de
->d_name
+ 8, "txt", 3))
3593 static int has_voicemail(const char *mailbox
, const char *folder
)
3595 char tmp
[256], *tmp2
= tmp
, *mbox
, *context
;
3596 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
3597 while ((mbox
= strsep(&tmp2
, ","))) {
3598 if ((context
= strchr(mbox
, '@')))
3601 context
= "default";
3602 if (__has_voicemail(context
, mbox
, folder
, 1))
3609 static int inboxcount(const char *mailbox
, int *newmsgs
, int *oldmsgs
)
3618 /* If no mailbox, return immediately */
3619 if (ast_strlen_zero(mailbox
))
3621 if (strchr(mailbox
, ',')) {
3625 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
3627 while ((cur
= strsep(&mb
, ", "))) {
3628 if (!ast_strlen_zero(cur
)) {
3629 if (inboxcount(cur
, newmsgs
? &tmpnew
: NULL
, oldmsgs
? &tmpold
: NULL
))
3641 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
3642 context
= strchr(tmp
, '@');
3647 context
= "default";
3649 *newmsgs
= __has_voicemail(context
, tmp
, "INBOX", 0);
3651 *oldmsgs
= __has_voicemail(context
, tmp
, "Old", 0);
3657 static void run_externnotify(char *context
, char *extension
)
3659 char arguments
[255];
3660 char ext_context
[256] = "";
3661 int newvoicemails
= 0, oldvoicemails
= 0;
3662 struct ast_smdi_mwi_message
*mwi_msg
;
3664 if (!ast_strlen_zero(context
))
3665 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", extension
, context
);
3667 ast_copy_string(ext_context
, extension
, sizeof(ext_context
));
3669 if (!strcasecmp(externnotify
, "smdi")) {
3670 if (ast_app_has_voicemail(ext_context
, NULL
))
3671 ast_smdi_mwi_set(smdi_iface
, extension
);
3673 ast_smdi_mwi_unset(smdi_iface
, extension
);
3675 if ((mwi_msg
= ast_smdi_mwi_message_wait_station(smdi_iface
, SMDI_MWI_WAIT_TIMEOUT
, extension
))) {
3676 ast_log(LOG_ERROR
, "Error executing SMDI MWI change for %s\n", extension
);
3677 if (!strncmp(mwi_msg
->cause
, "INV", 3))
3678 ast_log(LOG_ERROR
, "Invalid MWI extension: %s\n", mwi_msg
->fwd_st
);
3679 else if (!strncmp(mwi_msg
->cause
, "BLK", 3))
3680 ast_log(LOG_WARNING
, "MWI light was already on or off for %s\n", mwi_msg
->fwd_st
);
3681 ast_log(LOG_WARNING
, "The switch reported '%s'\n", mwi_msg
->cause
);
3682 ASTOBJ_UNREF(mwi_msg
, ast_smdi_mwi_message_destroy
);
3685 ast_log(LOG_DEBUG
, "Successfully executed SMDI MWI change for %s\n", extension
);
3687 } else if (!ast_strlen_zero(externnotify
)) {
3688 if (inboxcount(ext_context
, &newvoicemails
, &oldvoicemails
)) {
3689 ast_log(LOG_ERROR
, "Problem in calculating number of voicemail messages available for extension %s\n", extension
);
3691 snprintf(arguments
, sizeof(arguments
), "%s %s %s %d&", externnotify
, context
, extension
, newvoicemails
);
3693 ast_log(LOG_DEBUG
, "Executing %s\n", arguments
);
3694 ast_safe_system(arguments
);
3699 struct leave_vm_options
{
3701 signed char record_gain
;
3704 static int leave_voicemail(struct ast_channel
*chan
, char *ext
, struct leave_vm_options
*options
)
3707 int newmsgs
, oldmsgs
;
3709 struct vm_state
*vms
= NULL
;
3710 char txtfile
[PATH_MAX
], tmptxtfile
[PATH_MAX
];
3721 char dir
[PATH_MAX
], tmpdir
[PATH_MAX
];
3722 char dest
[PATH_MAX
];
3724 char prefile
[PATH_MAX
] = "";
3725 char tempfile
[PATH_MAX
] = "";
3726 char ext_context
[256] = "";
3729 char ecodes
[16] = "#";
3730 char tmp
[1024] = "", *tmpptr
;
3731 struct ast_vm_user
*vmu
;
3732 struct ast_vm_user svm
;
3733 const char *category
= NULL
;
3735 ast_copy_string(tmp
, ext
, sizeof(tmp
));
3737 context
= strchr(tmp
, '@');
3740 tmpptr
= strchr(context
, '&');
3742 tmpptr
= strchr(ext
, '&');
3748 category
= pbx_builtin_getvar_helper(chan
, "VM_CATEGORY");
3750 if (option_debug
> 2)
3751 ast_log(LOG_DEBUG
, "Before find_user\n");
3752 if (!(vmu
= find_user(&svm
, context
, ext
))) {
3753 ast_log(LOG_WARNING
, "No entry in voicemail config file for '%s'\n", ext
);
3754 if (ast_test_flag(options
, OPT_PRIORITY_JUMP
) || ast_opt_priority_jumping
)
3755 ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101);
3756 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3759 /* Setup pre-file if appropriate */
3760 if (strcmp(vmu
->context
, "default"))
3761 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", ext
, vmu
->context
);
3763 ast_copy_string(ext_context
, vmu
->mailbox
, sizeof(ext_context
));
3764 if (ast_test_flag(options
, OPT_BUSY_GREETING
)) {
3765 res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, ext
, "busy");
3766 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/busy", VM_SPOOL_DIR
, vmu
->context
, ext
);
3767 } else if (ast_test_flag(options
, OPT_UNAVAIL_GREETING
)) {
3768 res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, ext
, "unavail");
3769 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/unavail", VM_SPOOL_DIR
, vmu
->context
, ext
);
3771 snprintf(tempfile
, sizeof(tempfile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, ext
);
3772 if ((res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, ext
, "temp"))) {
3773 ast_log(LOG_WARNING
, "Failed to make directory (%s)\n", tempfile
);
3776 RETRIEVE(tempfile
, -1, vmu
);
3777 if (ast_fileexists(tempfile
, NULL
, NULL
) > 0)
3778 ast_copy_string(prefile
, tempfile
, sizeof(prefile
));
3779 DISPOSE(tempfile
, -1);
3780 /* It's easier just to try to make it than to check for its existence */
3781 create_dirpath(dir
, sizeof(dir
), vmu
->context
, ext
, "INBOX");
3782 create_dirpath(tmpdir
, sizeof(tmpdir
), vmu
->context
, ext
, "tmp");
3784 /* Check current or macro-calling context for special extensions */
3785 if (ast_test_flag(vmu
, VM_OPERATOR
)) {
3786 if (!ast_strlen_zero(vmu
->exit
)) {
3787 if (ast_exists_extension(chan
, vmu
->exit
, "o", 1, chan
->cid
.cid_num
)) {
3788 strncat(ecodes
, "0", sizeof(ecodes
) - strlen(ecodes
) - 1);
3791 } else if (ast_exists_extension(chan
, chan
->context
, "o", 1, chan
->cid
.cid_num
)) {
3792 strncat(ecodes
, "0", sizeof(ecodes
) - strlen(ecodes
) - 1);
3795 else if (!ast_strlen_zero(chan
->macrocontext
) && ast_exists_extension(chan
, chan
->macrocontext
, "o", 1, chan
->cid
.cid_num
)) {
3796 strncat(ecodes
, "0", sizeof(ecodes
) - strlen(ecodes
) - 1);
3801 if (!ast_strlen_zero(vmu
->exit
)) {
3802 if (ast_exists_extension(chan
, vmu
->exit
, "a", 1, chan
->cid
.cid_num
))
3803 strncat(ecodes
, "*", sizeof(ecodes
) - strlen(ecodes
) - 1);
3804 } else if (ast_exists_extension(chan
, chan
->context
, "a", 1, chan
->cid
.cid_num
))
3805 strncat(ecodes
, "*", sizeof(ecodes
) - strlen(ecodes
) - 1);
3806 else if (!ast_strlen_zero(chan
->macrocontext
) && ast_exists_extension(chan
, chan
->macrocontext
, "a", 1, chan
->cid
.cid_num
)) {
3807 strncat(ecodes
, "*", sizeof(ecodes
) - strlen(ecodes
) - 1);
3811 /* Play the beginning intro if desired */
3812 if (!ast_strlen_zero(prefile
)) {
3813 res
= play_greeting(chan
, vmu
, prefile
, ecodes
);
3815 /* The file did not exist */
3817 ast_log(LOG_DEBUG
, "%s doesn't exist, doing what we can\n", prefile
);
3818 res
= invent_message(chan
, vmu
, ext
, ast_test_flag(options
, OPT_BUSY_GREETING
), ecodes
);
3822 ast_log(LOG_DEBUG
, "Hang up during prefile playback\n");
3824 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3829 /* On a '#' we skip the instructions */
3830 ast_set_flag(options
, OPT_SILENT
);
3833 if (!res
&& !ast_test_flag(options
, OPT_SILENT
)) {
3834 res
= ast_stream_and_wait(chan
, INTRO
, chan
->language
, ecodes
);
3836 ast_set_flag(options
, OPT_SILENT
);
3841 ast_stopstream(chan
);
3842 /* Check for a '*' here in case the caller wants to escape from voicemail to something
3843 other than the operator -- an automated attendant or mailbox login for example */
3845 chan
->exten
[0] = 'a';
3846 chan
->exten
[1] = '\0';
3847 if (!ast_strlen_zero(vmu
->exit
)) {
3848 ast_copy_string(chan
->context
, vmu
->exit
, sizeof(chan
->context
));
3849 } else if (ausemacro
&& !ast_strlen_zero(chan
->macrocontext
)) {
3850 ast_copy_string(chan
->context
, chan
->macrocontext
, sizeof(chan
->context
));
3854 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "USEREXIT");
3858 /* Check for a '0' here */
3861 if (ouseexten
|| ousemacro
) {
3862 chan
->exten
[0] = 'o';
3863 chan
->exten
[1] = '\0';
3864 if (!ast_strlen_zero(vmu
->exit
)) {
3865 ast_copy_string(chan
->context
, vmu
->exit
, sizeof(chan
->context
));
3866 } else if (ousemacro
&& !ast_strlen_zero(chan
->macrocontext
)) {
3867 ast_copy_string(chan
->context
, chan
->macrocontext
, sizeof(chan
->context
));
3869 ast_play_and_wait(chan
, "transfer");
3872 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "USEREXIT");
3878 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3881 /* The meat of recording the message... All the announcements and beeps have been played*/
3882 ast_copy_string(fmt
, vmfmts
, sizeof(fmt
));
3883 if (!ast_strlen_zero(fmt
)) {
3887 /* Is ext a mailbox? */
3888 /* must open stream for this user to get info! */
3889 res
= inboxcount(ext_context
, &newmsgs
, &oldmsgs
);
3891 ast_log(LOG_NOTICE
,"Can not leave voicemail, unable to count messages\n");
3894 if (!(vms
= get_vm_state_by_mailbox(ext
, context
, 0))) {
3895 /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
3897 if (!(vms
= create_vm_state_from_user(vmu
))) {
3898 ast_log(LOG_ERROR
, "Couldn't allocate necessary space\n");
3903 /* here is a big difference! We add one to it later */
3904 msgnum
= newmsgs
+ oldmsgs
;
3905 if (option_debug
> 2)
3906 ast_log(LOG_DEBUG
, "Messagecount set to %d\n",msgnum
);
3907 snprintf(fn
, sizeof(fn
), "%s/imap/msg%s%04d", VM_SPOOL_DIR
, vmu
->mailbox
, msgnum
);
3908 /* set variable for compatability */
3909 pbx_builtin_setvar_helper(chan
, "VM_MESSAGEFILE", "IMAP_STORAGE");
3911 /* Check if mailbox is full */
3912 check_quota(vms
, imapfolder
);
3913 if (vms
->quota_limit
&& vms
->quota_usage
>= vms
->quota_limit
) {
3915 ast_log(LOG_DEBUG
, "*** QUOTA EXCEEDED!! %u >= %u\n", vms
->quota_usage
, vms
->quota_limit
);
3916 ast_play_and_wait(chan
, "vm-mailboxfull");
3919 if (option_debug
> 2)
3920 ast_log(LOG_DEBUG
, "Checking message number quota - mailbox has %d messages, maximum is set to %d\n",msgnum
,vmu
->maxmsg
);
3921 if (msgnum
>= vmu
->maxmsg
) {
3922 res
= ast_streamfile(chan
, "vm-mailboxfull", chan
->language
);
3924 res
= ast_waitstream(chan
, "");
3925 ast_log(LOG_WARNING
, "No more messages possible\n");
3926 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3930 /* Check if we have exceeded maxmsg */
3931 if (msgnum
>= vmu
->maxmsg
) {
3932 ast_log(LOG_WARNING
, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum
, vmu
->maxmsg
);
3933 ast_play_and_wait(chan
, "vm-mailboxfull");
3937 if (count_messages(vmu
, dir
) >= vmu
->maxmsg
) {
3938 res
= ast_streamfile(chan
, "vm-mailboxfull", chan
->language
);
3940 res
= ast_waitstream(chan
, "");
3941 ast_log(LOG_WARNING
, "No more messages possible\n");
3942 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3947 snprintf(tmptxtfile
, sizeof(tmptxtfile
), "%s/XXXXXX", tmpdir
);
3948 txtdes
= mkstemp(tmptxtfile
);
3949 chmod(tmptxtfile
, VOICEMAIL_FILE_MODE
& ~my_umask
);
3951 res
= ast_streamfile(chan
, "vm-mailboxfull", chan
->language
);
3953 res
= ast_waitstream(chan
, "");
3954 ast_log(LOG_ERROR
, "Unable to create message file: %s\n", strerror(errno
));
3955 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3959 /* Now play the beep once we have the message number for our next message. */
3961 /* Unless we're *really* silent, try to send the beep */
3962 res
= ast_stream_and_wait(chan
, "beep", chan
->language
, "");
3965 /* Store information */
3966 txt
= fdopen(txtdes
, "w+");
3968 get_date(date
, sizeof(date
));
3971 "; Message Information file\n"
3990 ast_callerid_merge(callerid
, sizeof(callerid
), S_OR(chan
->cid
.cid_name
, NULL
), S_OR(chan
->cid
.cid_num
, NULL
), "Unknown"),
3991 date
, (long)time(NULL
),
3992 category
? category
: "");
3994 ast_log(LOG_WARNING
, "Error opening text file for output\n");
3995 res
= play_record_review(chan
, NULL
, tmptxtfile
, vmmaxmessage
, fmt
, 1, vmu
, &duration
, NULL
, options
->record_gain
, vms
);
3998 if (duration
< vmminmessage
) {
4000 if (option_verbose
> 2)
4001 ast_verbose( VERBOSE_PREFIX_3
"Recording was %d seconds long but needs to be at least %d - abandoning\n", duration
, vmminmessage
);
4002 ast_filedelete(tmptxtfile
, NULL
);
4005 fprintf(txt
, "duration=%d\n", duration
);
4007 if (vm_lock_path(dir
)) {
4008 ast_log(LOG_ERROR
, "Couldn't lock directory %s. Voicemail will be lost.\n", dir
);
4010 ast_filedelete(tmptxtfile
, NULL
);
4012 } else if (ast_fileexists(tmptxtfile
, NULL
, NULL
) <= 0) {
4014 ast_log(LOG_DEBUG
, "The recorded media file is gone, so we should remove the .txt file too!\n");
4016 ast_unlock_path(dir
);
4019 make_file(fn
, sizeof(fn
), dir
, msgnum
);
4020 if (!EXISTS(dir
, msgnum
, fn
, NULL
))
4025 /* assign a variable with the name of the voicemail file */
4026 #ifndef IMAP_STORAGE
4027 pbx_builtin_setvar_helper(chan
, "VM_MESSAGEFILE", fn
);
4029 pbx_builtin_setvar_helper(chan
, "VM_MESSAGEFILE", "IMAP_STORAGE");
4032 snprintf(txtfile
, sizeof(txtfile
), "%s.txt", fn
);
4033 ast_filerename(tmptxtfile
, fn
, NULL
);
4034 rename(tmptxtfile
, txtfile
);
4036 ast_unlock_path(dir
);
4037 /* We must store the file first, before copying the message, because
4038 * ODBC storage does the entire copy with SQL.
4040 if (ast_fileexists(fn
, NULL
, NULL
) > 0) {
4041 STORE(dir
, vmu
->mailbox
, vmu
->context
, msgnum
, chan
, vmu
, fmt
, duration
, vms
);
4044 /* Are there to be more recipients of this message? */
4046 struct ast_vm_user recipu
, *recip
;
4047 char *exten
, *context
;
4049 exten
= strsep(&tmpptr
, "&");
4050 context
= strchr(exten
, '@');
4055 if ((recip
= find_user(&recipu
, context
, exten
))) {
4056 copy_message(chan
, vmu
, 0, msgnum
, duration
, recip
, fmt
, dir
);
4060 /* Notification and disposal needs to happen after the copy, though. */
4061 if (ast_fileexists(fn
, NULL
, NULL
)) {
4062 notify_new_message(chan
, vmu
, msgnum
, duration
, fmt
, S_OR(chan
->cid
.cid_num
, NULL
), S_OR(chan
->cid
.cid_name
, NULL
));
4063 DISPOSE(dir
, msgnum
);
4073 if (duration
< vmminmessage
)
4074 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
4075 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
4077 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "SUCCESS");
4079 ast_log(LOG_WARNING
, "No format for saving voicemail?\n");
4086 #ifndef IMAP_STORAGE
4087 static int resequence_mailbox(struct ast_vm_user
*vmu
, char *dir
)
4089 /* we know max messages, so stop process when number is hit */
4095 if (vm_lock_path(dir
))
4096 return ERROR_LOCK_PATH
;
4098 for (x
= 0, dest
= 0; x
< vmu
->maxmsg
; x
++) {
4099 make_file(sfn
, sizeof(sfn
), dir
, x
);
4100 if (EXISTS(dir
, x
, sfn
, NULL
)) {
4103 make_file(dfn
, sizeof(dfn
), dir
, dest
);
4104 RENAME(dir
, x
, vmu
->mailbox
, vmu
->context
, dir
, dest
, sfn
, dfn
);
4110 ast_unlock_path(dir
);
4116 static int say_and_wait(struct ast_channel
*chan
, int num
, const char *language
)
4119 d
= ast_say_number(chan
, num
, AST_DIGIT_ANY
, language
, (char *) NULL
);
4123 static int save_to_folder(struct ast_vm_user
*vmu
, struct vm_state
*vms
, int msg
, int box
)
4126 /* we must use mbox(x) folder names, and copy the message there */
4129 /* get the real IMAP message number for this message */
4130 snprintf(sequence
, sizeof(sequence
), "%ld", vms
->msgArray
[msg
]);
4131 if (option_debug
> 2)
4132 ast_log(LOG_DEBUG
, "Copying sequence %s to mailbox %s\n",sequence
,(char *) mbox(box
));
4134 mail_setflag(vms
->mailstream
, sequence
, "\\Seen");
4135 } else if (box
== 0) {
4136 mail_clearflag(vms
->mailstream
, sequence
, "\\Seen");
4138 if (!strcasecmp(mbox(0), vms
->curbox
) && (box
== 0 || box
== 1))
4141 return !mail_copy(vms
->mailstream
,sequence
,(char *) mbox(box
));
4143 char *dir
= vms
->curdir
;
4144 char *username
= vms
->username
;
4145 char *context
= vmu
->context
;
4148 char ddir
[PATH_MAX
];
4149 const char *dbox
= mbox(box
);
4151 make_file(sfn
, sizeof(sfn
), dir
, msg
);
4152 create_dirpath(ddir
, sizeof(ddir
), context
, username
, dbox
);
4154 if (vm_lock_path(ddir
))
4155 return ERROR_LOCK_PATH
;
4157 for (x
= 0; x
< vmu
->maxmsg
; x
++) {
4158 make_file(dfn
, sizeof(dfn
), ddir
, x
);
4159 if (!EXISTS(ddir
, x
, dfn
, NULL
))
4162 if (x
>= vmu
->maxmsg
) {
4163 ast_unlock_path(ddir
);
4164 return ERROR_MAILBOX_FULL
;
4166 if (strcmp(sfn
, dfn
)) {
4167 COPY(dir
, msg
, ddir
, x
, username
, context
, sfn
, dfn
);
4169 ast_unlock_path(ddir
);
4174 static int adsi_logo(unsigned char *buf
)
4177 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_CENT
, 0, "Comedian Mail", "");
4178 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_CENT
, 0, "(C)2002-2006 Digium, Inc.", "");
4182 static int adsi_load_vmail(struct ast_channel
*chan
, int *useadsi
)
4184 unsigned char buf
[256];
4190 bytes
+= ast_adsi_data_mode(buf
+ bytes
);
4191 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4194 bytes
+= adsi_logo(buf
);
4195 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Downloading Scripts", "");
4197 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " .", "");
4199 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4200 bytes
+= ast_adsi_data_mode(buf
+ bytes
);
4201 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4203 if (ast_adsi_begin_download(chan
, addesc
, adsifdn
, adsisec
, adsiver
)) {
4205 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Load Cancelled.", "");
4206 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "ADSI Unavailable", "");
4207 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4208 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
4209 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4216 bytes
+= ast_adsi_logo(buf
);
4217 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Downloading Scripts", "");
4218 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " ..", "");
4219 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4220 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4223 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 0, "Listen", "Listen", "1", 1);
4224 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 1, "Folder", "Folder", "2", 1);
4225 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 2, "Advanced", "Advnced", "3", 1);
4226 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 3, "Options", "Options", "0", 1);
4227 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 4, "Help", "Help", "*", 1);
4228 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 5, "Exit", "Exit", "#", 1);
4229 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
4232 /* Add another dot */
4234 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " ...", "");
4235 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
4237 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4238 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4242 /* These buttons we load but don't use yet */
4243 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 6, "Previous", "Prev", "4", 1);
4244 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 8, "Repeat", "Repeat", "5", 1);
4245 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 7, "Delete", "Delete", "7", 1);
4246 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 9, "Next", "Next", "6", 1);
4247 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 10, "Save", "Save", "9", 1);
4248 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 11, "Undelete", "Restore", "7", 1);
4249 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
4252 /* Add another dot */
4254 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " ....", "");
4255 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4256 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4261 snprintf(num
, sizeof(num
), "%d", x
);
4262 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 12 + x
, mbox(x
), mbox(x
), num
, 1);
4264 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 12 + 5, "Cancel", "Cancel", "#", 1);
4265 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
4268 /* Add another dot */
4270 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " .....", "");
4271 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4272 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4275 if (ast_adsi_end_download(chan
)) {
4277 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Download Unsuccessful.", "");
4278 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "ADSI Unavailable", "");
4279 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4280 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
4281 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4285 bytes
+= ast_adsi_download_disconnect(buf
+ bytes
);
4286 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
4287 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
4290 ast_log(LOG_DEBUG
, "Done downloading scripts...\n");
4295 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, " ......", "");
4296 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4299 ast_log(LOG_DEBUG
, "Restarting session...\n");
4302 /* Load the session now */
4303 if (ast_adsi_load_session(chan
, adsifdn
, adsiver
, 1) == 1) {
4305 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Scripts Loaded!", "");
4307 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Load Failed!", "");
4309 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4313 static void adsi_begin(struct ast_channel
*chan
, int *useadsi
)
4316 if (!ast_adsi_available(chan
))
4318 x
= ast_adsi_load_session(chan
, adsifdn
, adsiver
, 1);
4322 if (adsi_load_vmail(chan
, useadsi
)) {
4323 ast_log(LOG_WARNING
, "Unable to upload voicemail scripts\n");
4330 static void adsi_login(struct ast_channel
*chan
)
4332 unsigned char buf
[256];
4334 unsigned char keys
[8];
4336 if (!ast_adsi_available(chan
))
4341 /* Set one key for next */
4342 keys
[3] = ADSI_KEY_APPS
+ 3;
4344 bytes
+= adsi_logo(buf
+ bytes
);
4345 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, " ", "");
4346 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, " ", "");
4347 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4348 bytes
+= ast_adsi_input_format(buf
+ bytes
, 1, ADSI_DIR_FROM_LEFT
, 0, "Mailbox: ******", "");
4349 bytes
+= ast_adsi_input_control(buf
+ bytes
, ADSI_COMM_PAGE
, 4, 1, 1, ADSI_JUST_LEFT
);
4350 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 3, "Enter", "Enter", "#", 1);
4351 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
4352 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
4353 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4356 static void adsi_password(struct ast_channel
*chan
)
4358 unsigned char buf
[256];
4360 unsigned char keys
[8];
4362 if (!ast_adsi_available(chan
))
4367 /* Set one key for next */
4368 keys
[3] = ADSI_KEY_APPS
+ 3;
4370 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4371 bytes
+= ast_adsi_input_format(buf
+ bytes
, 1, ADSI_DIR_FROM_LEFT
, 0, "Password: ******", "");
4372 bytes
+= ast_adsi_input_control(buf
+ bytes
, ADSI_COMM_PAGE
, 4, 0, 1, ADSI_JUST_LEFT
);
4373 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
4374 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
4375 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4378 static void adsi_folders(struct ast_channel
*chan
, int start
, char *label
)
4380 unsigned char buf
[256];
4382 unsigned char keys
[8];
4385 if (!ast_adsi_available(chan
))
4389 y
= ADSI_KEY_APPS
+ 12 + start
+ x
;
4390 if (y
> ADSI_KEY_APPS
+ 12 + 4)
4392 keys
[x
] = ADSI_KEY_SKT
| y
;
4394 keys
[5] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 17);
4398 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_CENT
, 0, label
, "");
4399 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_CENT
, 0, " ", "");
4400 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4401 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
4402 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
4404 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4407 static void adsi_message(struct ast_channel
*chan
, struct vm_state
*vms
)
4410 unsigned char buf
[256];
4411 char buf1
[256], buf2
[256];
4417 char datetime
[21]="";
4420 unsigned char keys
[8];
4424 if (!ast_adsi_available(chan
))
4427 /* Retrieve important info */
4428 snprintf(fn2
, sizeof(fn2
), "%s.txt", vms
->fn
);
4429 f
= fopen(fn2
, "r");
4432 fgets((char *)buf
, sizeof(buf
), f
);
4435 stringp
= (char *)buf
;
4436 strsep(&stringp
, "=");
4437 val
= strsep(&stringp
, "=");
4438 if (!ast_strlen_zero(val
)) {
4439 if (!strcmp((char *)buf
, "callerid"))
4440 ast_copy_string(cid
, val
, sizeof(cid
));
4441 if (!strcmp((char *)buf
, "origdate"))
4442 ast_copy_string(datetime
, val
, sizeof(datetime
));
4448 /* New meaning for keys */
4450 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 6 + x
);
4455 /* No prev key, provide "Folder" instead */
4456 keys
[0] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
4458 if (vms
->curmsg
>= vms
->lastmsg
) {
4459 /* If last message ... */
4461 /* but not only message, provide "Folder" instead */
4462 keys
[3] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
4463 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
4466 /* Otherwise if only message, leave blank */
4471 if (!ast_strlen_zero(cid
)) {
4472 ast_callerid_parse(cid
, &name
, &num
);
4476 name
= "Unknown Caller";
4478 /* If deleted, show "undeleted" */
4480 if (vms
->deleted
[vms
->curmsg
])
4481 keys
[1] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 11);
4484 keys
[5] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 5);
4485 snprintf(buf1
, sizeof(buf1
), "%s%s", vms
->curbox
,
4486 strcasecmp(vms
->curbox
, "INBOX") ? " Messages" : "");
4487 snprintf(buf2
, sizeof(buf2
), "Message %d of %d", vms
->curmsg
+ 1, vms
->lastmsg
+ 1);
4489 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_LEFT
, 0, buf1
, "");
4490 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_LEFT
, 0, buf2
, "");
4491 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_LEFT
, 0, name
, "");
4492 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, datetime
, "");
4493 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4494 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
4495 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
4497 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4500 static void adsi_delete(struct ast_channel
*chan
, struct vm_state
*vms
)
4503 unsigned char buf
[256];
4504 unsigned char keys
[8];
4508 if (!ast_adsi_available(chan
))
4511 /* New meaning for keys */
4513 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 6 + x
);
4519 /* No prev key, provide "Folder" instead */
4520 keys
[0] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
4522 if (vms
->curmsg
>= vms
->lastmsg
) {
4523 /* If last message ... */
4525 /* but not only message, provide "Folder" instead */
4526 keys
[3] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
4528 /* Otherwise if only message, leave blank */
4533 /* If deleted, show "undeleted" */
4534 if (vms
->deleted
[vms
->curmsg
])
4535 keys
[1] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 11);
4538 keys
[5] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 5);
4539 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
4540 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
4542 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4545 static void adsi_status(struct ast_channel
*chan
, struct vm_state
*vms
)
4547 unsigned char buf
[256] = "";
4548 char buf1
[256] = "", buf2
[256] = "";
4550 unsigned char keys
[8];
4553 char *newm
= (vms
->newmessages
== 1) ? "message" : "messages";
4554 char *oldm
= (vms
->oldmessages
== 1) ? "message" : "messages";
4555 if (!ast_adsi_available(chan
))
4557 if (vms
->newmessages
) {
4558 snprintf(buf1
, sizeof(buf1
), "You have %d new", vms
->newmessages
);
4559 if (vms
->oldmessages
) {
4560 strncat(buf1
, " and", sizeof(buf1
) - strlen(buf1
) - 1);
4561 snprintf(buf2
, sizeof(buf2
), "%d old %s.", vms
->oldmessages
, oldm
);
4563 snprintf(buf2
, sizeof(buf2
), "%s.", newm
);
4565 } else if (vms
->oldmessages
) {
4566 snprintf(buf1
, sizeof(buf1
), "You have %d old", vms
->oldmessages
);
4567 snprintf(buf2
, sizeof(buf2
), "%s.", oldm
);
4569 strcpy(buf1
, "You have no messages.");
4573 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_LEFT
, 0, buf1
, "");
4574 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_LEFT
, 0, buf2
, "");
4575 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4578 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ x
);
4582 /* Don't let them listen if there are none */
4583 if (vms
->lastmsg
< 0)
4585 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
4587 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
4589 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4592 static void adsi_status2(struct ast_channel
*chan
, struct vm_state
*vms
)
4594 unsigned char buf
[256] = "";
4595 char buf1
[256] = "", buf2
[256] = "";
4597 unsigned char keys
[8];
4600 char *mess
= (vms
->lastmsg
== 0) ? "message" : "messages";
4602 if (!ast_adsi_available(chan
))
4605 /* Original command keys */
4607 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ x
);
4612 if ((vms
->lastmsg
+ 1) < 1)
4615 snprintf(buf1
, sizeof(buf1
), "%s%s has", vms
->curbox
,
4616 strcasecmp(vms
->curbox
, "INBOX") ? " folder" : "");
4618 if (vms
->lastmsg
+ 1)
4619 snprintf(buf2
, sizeof(buf2
), "%d %s.", vms
->lastmsg
+ 1, mess
);
4621 strcpy(buf2
, "no messages.");
4622 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_LEFT
, 0, buf1
, "");
4623 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_LEFT
, 0, buf2
, "");
4624 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_LEFT
, 0, "", "");
4625 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4626 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
4628 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
4630 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4635 static void adsi_clear(struct ast_channel *chan)
4639 if (!ast_adsi_available(chan))
4641 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4642 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4644 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4648 static void adsi_goodbye(struct ast_channel
*chan
)
4650 unsigned char buf
[256];
4653 if (!ast_adsi_available(chan
))
4655 bytes
+= adsi_logo(buf
+ bytes
);
4656 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_LEFT
, 0, " ", "");
4657 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Goodbye", "");
4658 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
4659 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
4661 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
4664 /*--- get_folder: Folder menu ---*/
4665 /* Plays "press 1 for INBOX messages" etc
4666 Should possibly be internationalized
4668 static int get_folder(struct ast_channel
*chan
, int start
)
4673 d
= ast_play_and_wait(chan
, "vm-press"); /* "Press" */
4676 for (x
= start
; x
< 5; x
++) { /* For all folders */
4677 if ((d
= ast_say_number(chan
, x
, AST_DIGIT_ANY
, chan
->language
, (char *) NULL
)))
4679 d
= ast_play_and_wait(chan
, "vm-for"); /* "for" */
4682 snprintf(fn
, sizeof(fn
), "vm-%s", mbox(x
)); /* Folder name */
4683 d
= vm_play_folder_name(chan
, fn
);
4686 d
= ast_waitfordigit(chan
, 500);
4690 d
= ast_play_and_wait(chan
, "vm-tocancel"); /* "or pound to cancel" */
4693 d
= ast_waitfordigit(chan
, 4000);
4697 static int get_folder2(struct ast_channel
*chan
, char *fn
, int start
)
4700 res
= ast_play_and_wait(chan
, fn
); /* Folder name */
4701 while (((res
< '0') || (res
> '9')) &&
4702 (res
!= '#') && (res
>= 0)) {
4703 res
= get_folder(chan
, 0);
4708 static int vm_forwardoptions(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *curdir
, int curmsg
, char *vmfmts
,
4709 char *context
, signed char record_gain
, long *duration
, struct vm_state
*vms
)
4712 int retries
= 0, prepend_duration
= 0, already_recorded
= 0;
4713 signed char zero_gain
= 0;
4714 struct ast_config
*msg_cfg
;
4715 const char *duration_str
;
4716 char msgfile
[PATH_MAX
], backup
[PATH_MAX
];
4717 char textfile
[PATH_MAX
];
4719 /* Must always populate duration correctly */
4720 make_file(msgfile
, sizeof(msgfile
), curdir
, curmsg
);
4721 strcpy(textfile
, msgfile
);
4722 strcpy(backup
, msgfile
);
4723 strncat(textfile
, ".txt", sizeof(textfile
) - strlen(textfile
) - 1);
4724 strncat(backup
, "-bak", sizeof(backup
) - strlen(backup
) - 1);
4726 if (!(msg_cfg
= ast_config_load(textfile
))) {
4731 if ((duration_str
= ast_variable_retrieve(msg_cfg
, "message", "duration")))
4732 *duration
= atoi(duration_str
);
4734 while ((cmd
>= 0) && (cmd
!= 't') && (cmd
!= '*')) {
4739 /* prepend a message to the current message, update the metadata and return */
4741 prepend_duration
= 0;
4743 /* if we can't read the message metadata, stop now */
4749 /* Back up the original file, so we can retry the prepend */
4750 if (already_recorded
)
4751 ast_filecopy(backup
, msgfile
, NULL
);
4753 ast_filecopy(msgfile
, backup
, NULL
);
4754 already_recorded
= 1;
4757 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &record_gain
, sizeof(record_gain
), 0);
4759 cmd
= ast_play_and_prepend(chan
, NULL
, msgfile
, 0, vmfmts
, &prepend_duration
, 1, silencethreshold
, maxsilence
);
4761 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &zero_gain
, sizeof(zero_gain
), 0);
4763 if (prepend_duration
) {
4764 struct ast_category
*msg_cat
;
4765 /* need enough space for a maximum-length message duration */
4766 char duration_str
[12];
4768 prepend_duration
+= *duration
;
4769 msg_cat
= ast_category_get(msg_cfg
, "message");
4770 snprintf(duration_str
, 11, "%d", prepend_duration
);
4771 if (!ast_variable_update(msg_cat
, "duration", duration_str
, NULL
, 0)) {
4772 config_text_file_save(textfile
, msg_cfg
, "app_voicemail");
4785 cmd
= ast_play_and_wait(chan
,"vm-forwardoptions");
4786 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
4788 cmd
= ast_play_and_wait(chan
,"vm-starmain");
4789 /* "press star to return to the main menu" */
4791 cmd
= ast_waitfordigit(chan
,6000);
4799 ast_config_destroy(msg_cfg
);
4800 if (already_recorded
)
4801 ast_filedelete(backup
, NULL
);
4802 if (prepend_duration
)
4803 *duration
= prepend_duration
;
4805 if (cmd
== 't' || cmd
== 'S')
4810 static int notify_new_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, int msgnum
, long duration
, char *fmt
, char *cidnum
, char *cidname
)
4812 char todir
[PATH_MAX
], fn
[PATH_MAX
], ext_context
[PATH_MAX
], *stringp
;
4813 int newmsgs
= 0, oldmsgs
= 0;
4814 const char *category
= pbx_builtin_getvar_helper(chan
, "VM_CATEGORY");
4816 make_dir(todir
, sizeof(todir
), vmu
->context
, vmu
->mailbox
, "INBOX");
4817 make_file(fn
, sizeof(fn
), todir
, msgnum
);
4818 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", vmu
->mailbox
, vmu
->context
);
4820 if (!ast_strlen_zero(vmu
->attachfmt
)) {
4821 if (strstr(fmt
, vmu
->attachfmt
)) {
4822 fmt
= vmu
->attachfmt
;
4824 ast_log(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
);
4828 /* Attach only the first format */
4829 fmt
= ast_strdupa(fmt
);
4831 strsep(&stringp
, "|");
4833 if (!ast_strlen_zero(vmu
->email
)) {
4834 int attach_user_voicemail
= ast_test_flag((&globalflags
), VM_ATTACH
);
4835 char *myserveremail
= serveremail
;
4836 attach_user_voicemail
= ast_test_flag(vmu
, VM_ATTACH
);
4837 if (!ast_strlen_zero(vmu
->serveremail
))
4838 myserveremail
= vmu
->serveremail
;
4840 if (attach_user_voicemail
)
4841 RETRIEVE(todir
, msgnum
, vmu
);
4843 /*XXX possible imap issue, should category be NULL XXX*/
4844 sendmail(myserveremail
, vmu
, msgnum
, vmu
->context
, vmu
->mailbox
, cidnum
, cidname
, fn
, fmt
, duration
, attach_user_voicemail
, chan
, category
);
4846 if (attach_user_voicemail
)
4847 DISPOSE(todir
, msgnum
);
4850 if (!ast_strlen_zero(vmu
->pager
)) {
4851 char *myserveremail
= serveremail
;
4852 if (!ast_strlen_zero(vmu
->serveremail
))
4853 myserveremail
= vmu
->serveremail
;
4854 sendpage(myserveremail
, vmu
->pager
, msgnum
, vmu
->context
, vmu
->mailbox
, cidnum
, cidname
, duration
, vmu
, category
);
4857 if (ast_test_flag(vmu
, VM_DELETE
)) {
4858 DELETE(todir
, msgnum
, fn
, vmu
);
4861 /* Leave voicemail for someone */
4862 if (ast_app_has_voicemail(ext_context
, NULL
)) {
4863 ast_app_inboxcount(ext_context
, &newmsgs
, &oldmsgs
);
4865 manager_event(EVENT_FLAG_CALL
, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu
->mailbox
, vmu
->context
, ast_app_has_voicemail(ext_context
, NULL
), newmsgs
, oldmsgs
);
4866 run_externnotify(vmu
->context
, vmu
->mailbox
);
4870 static int forward_message(struct ast_channel
*chan
, char *context
, struct vm_state
*vms
, struct ast_vm_user
*sender
, char *fmt
, int flag
, signed char record_gain
)
4874 struct vm_state
*dstvms
;
4876 char username
[70]="";
4877 int res
= 0, cmd
= 0;
4878 struct ast_vm_user
*receiver
= NULL
, *vmtmp
;
4879 AST_LIST_HEAD_NOLOCK_STATIC(extensions
, ast_vm_user
);
4882 int saved_messages
= 0, found
= 0;
4883 int valid_extensions
= 0;
4887 if (vms
== NULL
) return -1;
4889 curmsg
= vms
->curmsg
;
4891 while (!res
&& !valid_extensions
) {
4892 int use_directory
= 0;
4893 if (ast_test_flag((&globalflags
), VM_DIRECFORWARD
)) {
4897 while ((cmd
>= 0) && !done
){
4914 /* Press 1 to enter an extension press 2 to use the directory */
4915 cmd
= ast_play_and_wait(chan
,"vm-forward");
4917 cmd
= ast_waitfordigit(chan
,3000);
4928 if (cmd
< 0 || cmd
== 't')
4932 if (use_directory
) {
4933 /* use app_directory */
4935 char old_context
[sizeof(chan
->context
)];
4936 char old_exten
[sizeof(chan
->exten
)];
4938 struct ast_app
* app
;
4941 app
= pbx_findapp("Directory");
4943 char vmcontext
[256];
4944 /* make backup copies */
4945 memcpy(old_context
, chan
->context
, sizeof(chan
->context
));
4946 memcpy(old_exten
, chan
->exten
, sizeof(chan
->exten
));
4947 old_priority
= chan
->priority
;
4949 /* call the the Directory, changes the channel */
4950 snprintf(vmcontext
, sizeof(vmcontext
), "%s||v", context
? context
: "default");
4951 res
= pbx_exec(chan
, app
, vmcontext
);
4953 ast_copy_string(username
, chan
->exten
, sizeof(username
));
4955 /* restore the old context, exten, and priority */
4956 memcpy(chan
->context
, old_context
, sizeof(chan
->context
));
4957 memcpy(chan
->exten
, old_exten
, sizeof(chan
->exten
));
4958 chan
->priority
= old_priority
;
4961 ast_log(LOG_WARNING
, "Could not find the Directory application, disabling directory_forward\n");
4962 ast_clear_flag((&globalflags
), VM_DIRECFORWARD
);
4965 /* Ask for an extension */
4966 res
= ast_streamfile(chan
, "vm-extension", chan
->language
); /* "extension" */
4969 if ((res
= ast_readstring(chan
, username
, sizeof(username
) - 1, 2000, 10000, "#") < 0))
4973 /* start all over if no username */
4974 if (ast_strlen_zero(username
))
4977 s
= strsep(&stringp
, "*");
4978 /* start optimistic */
4979 valid_extensions
= 1;
4981 /* Don't forward to ourselves but allow leaving a message for ourselves (flag == 1). find_user is going to malloc since we have a NULL as first argument */
4982 if ((flag
== 1 || strcmp(s
,sender
->mailbox
)) && (receiver
= find_user(NULL
, context
, s
))) {
4983 AST_LIST_INSERT_HEAD(&extensions
, receiver
, list
);
4986 valid_extensions
= 0;
4989 s
= strsep(&stringp
, "*");
4991 /* break from the loop of reading the extensions */
4992 if (valid_extensions
)
4994 /* "I am sorry, that's not a valid extension. Please try again." */
4995 res
= ast_play_and_wait(chan
, "pbx-invalid");
4997 /* check if we're clear to proceed */
4998 if (AST_LIST_EMPTY(&extensions
) || !valid_extensions
)
5001 struct leave_vm_options leave_options
;
5002 char mailbox
[AST_MAX_EXTENSION
* 2 + 2];
5003 /* Make sure that context doesn't get set as a literal "(null)" (or else find_user won't find it) */
5005 snprintf(mailbox
, sizeof(mailbox
), "%s@%s", username
, context
);
5007 ast_copy_string(mailbox
, username
, sizeof(mailbox
));
5009 /* Send VoiceMail */
5010 memset(&leave_options
, 0, sizeof(leave_options
));
5011 leave_options
.record_gain
= record_gain
;
5012 cmd
= leave_voicemail(chan
, mailbox
, &leave_options
);
5014 /* Forward VoiceMail */
5016 char origmsgfile
[PATH_MAX
], msgfile
[PATH_MAX
];
5017 struct vm_state vmstmp
;
5019 memcpy(&vmstmp
, vms
, sizeof(vmstmp
));
5021 make_file(origmsgfile
, sizeof(origmsgfile
), dir
, curmsg
);
5022 create_dirpath(vmstmp
.curdir
, sizeof(vmstmp
.curdir
), sender
->context
, vmstmp
.username
, "tmp");
5023 make_file(msgfile
, sizeof(msgfile
), vmstmp
.curdir
, curmsg
);
5025 RETRIEVE(dir
, curmsg
, sender
);
5027 /* Alter a surrogate file, only */
5028 copy_plain_file(origmsgfile
, msgfile
);
5030 cmd
= vm_forwardoptions(chan
, sender
, vmstmp
.curdir
, curmsg
, vmfmts
, S_OR(context
, "default"), record_gain
, &duration
, &vmstmp
);
5032 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions
, vmtmp
, list
) {
5034 char *myserveremail
;
5035 int attach_user_voicemail
;
5036 /* get destination mailbox */
5037 dstvms
= get_vm_state_by_mailbox(vmtmp
->mailbox
, vmtmp
->context
, 0);
5039 dstvms
= create_vm_state_from_user(vmtmp
);
5042 init_mailstream(dstvms
, 0);
5043 if (!dstvms
->mailstream
) {
5044 ast_log (LOG_ERROR
,"IMAP mailstream for %s is NULL\n",vmtmp
->mailbox
);
5046 STORE(vmstmp
.curdir
, vmtmp
->mailbox
, vmtmp
->context
, dstvms
->curmsg
, chan
, vmtmp
, fmt
, duration
, dstvms
);
5047 run_externnotify(vmtmp
->context
, vmtmp
->mailbox
);
5050 ast_log (LOG_ERROR
,"Could not find state information for mailbox %s\n",vmtmp
->mailbox
);
5052 myserveremail
= serveremail
;
5053 if (!ast_strlen_zero(vmtmp
->serveremail
))
5054 myserveremail
= vmtmp
->serveremail
;
5055 attach_user_voicemail
= ast_test_flag(vmtmp
, VM_ATTACH
);
5056 /* NULL category for IMAP storage */
5057 sendmail(myserveremail
, vmtmp
, todircount
, vmtmp
->context
, vmtmp
->mailbox
, S_OR(chan
->cid
.cid_num
, NULL
), S_OR(chan
->cid
.cid_name
, NULL
), vms
->fn
, fmt
, duration
, attach_user_voicemail
, chan
, NULL
);
5059 copy_message(chan
, sender
, -1, curmsg
, duration
, vmtmp
, fmt
, vmstmp
.curdir
);
5062 AST_LIST_REMOVE_CURRENT(&extensions
, list
);
5067 AST_LIST_TRAVERSE_SAFE_END
;
5068 if (saved_messages
> 0) {
5069 /* give confirmation that the message was saved */
5070 /* commented out since we can't forward batches yet
5071 if (saved_messages == 1)
5072 res = ast_play_and_wait(chan, "vm-message");
5074 res = ast_play_and_wait(chan, "vm-messages");
5076 res = ast_play_and_wait(chan, "vm-saved"); */
5077 res
= ast_play_and_wait(chan
, "vm-msgsaved");
5081 /* Remove surrogate file */
5083 DISPOSE(dir
, curmsg
);
5086 /* If anything failed above, we still have this list to free */
5087 while ((vmtmp
= AST_LIST_REMOVE_HEAD(&extensions
, list
)))
5089 return res
? res
: cmd
;
5092 static int wait_file2(struct ast_channel
*chan
, struct vm_state
*vms
, char *file
)
5095 if ((res
= ast_stream_and_wait(chan
, file
, chan
->language
, AST_DIGIT_ANY
)) < 0)
5096 ast_log(LOG_WARNING
, "Unable to play message %s\n", file
);
5100 static int wait_file(struct ast_channel
*chan
, struct vm_state
*vms
, char *file
)
5102 return ast_control_streamfile(chan
, file
, "#", "*", "1456789", "0", "2", skipms
);
5105 static int play_message_category(struct ast_channel
*chan
, const char *category
)
5109 if (!ast_strlen_zero(category
))
5110 res
= ast_play_and_wait(chan
, category
);
5113 ast_log(LOG_WARNING
, "No sound file for category '%s' was found.\n", category
);
5120 static int play_message_datetime(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, const char *origtime
, const char *filename
)
5123 struct vm_zone
*the_zone
= NULL
;
5126 if (ast_get_time_t(origtime
, &t
, 0, NULL
)) {
5127 ast_log(LOG_WARNING
, "Couldn't find origtime in %s\n", filename
);
5131 /* Does this user have a timezone specified? */
5132 if (!ast_strlen_zero(vmu
->zonetag
)) {
5133 /* Find the zone in the list */
5135 AST_LIST_LOCK(&zones
);
5136 AST_LIST_TRAVERSE(&zones
, z
, list
) {
5137 if (!strcmp(z
->name
, vmu
->zonetag
)) {
5142 AST_LIST_UNLOCK(&zones
);
5145 /* No internal variable parsing for now, so we'll comment it out for the time being */
5147 /* Set the DIFF_* variables */
5148 ast_localtime(&t
, &time_now
, NULL
);
5149 tv_now
= ast_tvnow();
5150 tnow
= tv_now
.tv_sec
;
5151 ast_localtime(&tnow
, &time_then
, NULL
);
5153 /* Day difference */
5154 if (time_now
.tm_year
== time_then
.tm_year
)
5155 snprintf(temp
,sizeof(temp
),"%d",time_now
.tm_yday
);
5157 snprintf(temp
,sizeof(temp
),"%d",(time_now
.tm_year
- time_then
.tm_year
) * 365 + (time_now
.tm_yday
- time_then
.tm_yday
));
5158 pbx_builtin_setvar_helper(chan
, "DIFF_DAY", temp
);
5160 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
5163 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, the_zone
->msg_format
, the_zone
->timezone
);
5164 else if (!strcasecmp(chan
->language
,"pl")) /* POLISH syntax */
5165 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Q HM", NULL
);
5166 else if (!strcasecmp(chan
->language
,"se")) /* SWEDISH syntax */
5167 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' dB 'digits/at' k 'and' M", NULL
);
5168 else if (!strcasecmp(chan
->language
,"no")) /* NORWEGIAN syntax */
5169 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Q 'digits/at' HM", NULL
);
5170 else if (!strcasecmp(chan
->language
,"de")) /* GERMAN syntax */
5171 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Q 'digits/at' HM", NULL
);
5172 else if (!strcasecmp(chan
->language
,"nl")) /* DUTCH syntax */
5173 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' q 'digits/nl-om' HM", NULL
);
5174 else if (!strcasecmp(chan
->language
,"it")) /* ITALIAN syntax */
5175 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL
);
5176 else if (!strcasecmp(chan
->language
,"gr"))
5177 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' q H 'digits/kai' M ", NULL
);
5178 else if (!strcasecmp(chan
->language
,"pt_BR"))
5179 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL
);
5180 else if (!strcasecmp(chan
->language
,"he"))
5181 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Ad 'at2' kM", NULL
);
5183 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' q 'digits/at' IMp", NULL
);
5185 pbx_builtin_setvar_helper(chan
, "DIFF_DAY", NULL
);
5192 static int play_message_callerid(struct ast_channel
*chan
, struct vm_state
*vms
, char *cid
, const char *context
, int callback
)
5196 char *callerid
, *name
;
5197 char prefile
[PATH_MAX
] = "";
5200 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
5201 /* BB: Still need to change this so that if this function is called by the message envelope (and someone is explicitly requesting to hear the CID), it does not check to see if CID is enabled in the config file */
5202 if ((cid
== NULL
)||(context
== NULL
))
5205 /* Strip off caller ID number from name */
5206 if (option_debug
> 2)
5207 ast_log(LOG_DEBUG
, "VM-CID: composite caller ID received: %s, context: %s\n", cid
, context
);
5208 ast_callerid_parse(cid
, &name
, &callerid
);
5209 if ((!ast_strlen_zero(callerid
)) && strcmp(callerid
, "Unknown")) {
5210 /* Check for internal contexts and only */
5211 /* say extension when the call didn't come from an internal context in the list */
5212 for (i
= 0 ; i
< MAX_NUM_CID_CONTEXTS
; i
++){
5213 if (option_debug
> 2)
5214 ast_log(LOG_DEBUG
, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts
[i
]);
5215 if ((strcmp(cidinternalcontexts
[i
], context
) == 0))
5218 if (i
!= MAX_NUM_CID_CONTEXTS
){ /* internal context? */
5220 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/greet", VM_SPOOL_DIR
, context
, callerid
);
5221 if (!ast_strlen_zero(prefile
)) {
5222 /* See if we can find a recorded name for this person instead of their extension number */
5223 if (ast_fileexists(prefile
, NULL
, NULL
) > 0) {
5224 if (option_verbose
> 2)
5225 ast_verbose(VERBOSE_PREFIX_3
"Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid
);
5227 res
= wait_file2(chan
, vms
, "vm-from");
5228 res
= ast_stream_and_wait(chan
, prefile
, chan
->language
, "");
5230 if (option_verbose
> 2)
5231 ast_verbose(VERBOSE_PREFIX_3
"Playing envelope info: message from '%s'\n", callerid
);
5232 /* BB: Say "from extension" as one saying to sound smoother */
5234 res
= wait_file2(chan
, vms
, "vm-from-extension");
5235 res
= ast_say_digit_str(chan
, callerid
, "", chan
->language
);
5242 if (option_debug
> 2)
5243 ast_log(LOG_DEBUG
, "VM-CID: Numeric caller id: (%s)\n",callerid
);
5244 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
5246 res
= wait_file2(chan
, vms
, "vm-from-phonenumber");
5247 res
= ast_say_digit_str(chan
, callerid
, AST_DIGIT_ANY
, chan
->language
);
5250 /* Number unknown */
5252 ast_log(LOG_DEBUG
, "VM-CID: From an unknown number\n");
5253 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
5254 res
= wait_file2(chan
, vms
, "vm-unknown-caller");
5259 static int play_message_duration(struct ast_channel
*chan
, struct vm_state
*vms
, const char *duration
, int minduration
)
5264 /* Verify that we have a duration for the message */
5265 if (duration
== NULL
)
5268 /* Convert from seconds to minutes */
5269 durations
=atoi(duration
);
5270 durationm
=(durations
/ 60);
5272 if (option_debug
> 2)
5273 ast_log(LOG_DEBUG
, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations
, durationm
);
5275 if ((!res
) && (durationm
>= minduration
)) {
5276 res
= wait_file2(chan
, vms
, "vm-duration");
5279 if (!strcasecmp(chan
->language
, "pl")) {
5280 div_t num
= div(durationm
, 10);
5282 if (durationm
== 1) {
5283 res
= ast_play_and_wait(chan
, "digits/1z");
5284 res
= res
? res
: ast_play_and_wait(chan
, "vm-minute-ta");
5285 } else if (num
.rem
> 1 && num
.rem
< 5 && num
.quot
!= 1) {
5288 res
= ast_play_and_wait(chan
, "digits/2-ie");
5290 res
= say_and_wait(chan
, durationm
- 2 , chan
->language
);
5291 res
= res
? res
: ast_play_and_wait(chan
, "digits/2-ie");
5294 res
= say_and_wait(chan
, durationm
, chan
->language
);
5296 res
= res
? res
: ast_play_and_wait(chan
, "vm-minute-ty");
5298 res
= say_and_wait(chan
, durationm
, chan
->language
);
5299 res
= res
? res
: ast_play_and_wait(chan
, "vm-minute-t");
5301 /* DEFAULT syntax */
5303 res
= ast_say_number(chan
, durationm
, AST_DIGIT_ANY
, chan
->language
, NULL
);
5304 res
= wait_file2(chan
, vms
, "vm-minutes");
5310 static int play_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
)
5313 char filename
[256], *cid
;
5314 const char *origtime
, *context
, *category
, *duration
;
5315 struct ast_config
*msg_cfg
;
5318 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, vms
->curmsg
);
5319 adsi_message(chan
, vms
);
5320 if (!strcasecmp(chan
->language
, "he")) { /* HEBREW FORMAT */
5322 * The syntax in hebrew for counting the number of message is up side down
5323 * in comparison to english.
5326 res
= wait_file2(chan
, vms
, "vm-message");
5327 res
= wait_file2(chan
, vms
, "vm-first"); /* "First" */
5328 } else if (vms
->curmsg
== vms
->lastmsg
) {
5329 res
= wait_file2(chan
, vms
, "vm-message");
5330 res
= wait_file2(chan
, vms
, "vm-last"); /* "last" */
5332 res
= wait_file2(chan
, vms
, "vm-message"); /* "message" */
5333 if (vms
->curmsg
&& (vms
->curmsg
!= vms
->lastmsg
)) {
5334 ast_log(LOG_DEBUG
, "curmsg: %d\n", vms
->curmsg
);
5335 ast_log(LOG_DEBUG
, "lagmsg: %d\n", vms
->lastmsg
);
5337 res
= ast_say_number(chan
, vms
->curmsg
+ 1, AST_DIGIT_ANY
, chan
->language
, "f");
5343 res
= wait_file2(chan
, vms
, "vm-first"); /* "First" */
5344 else if (vms
->curmsg
== vms
->lastmsg
)
5345 res
= wait_file2(chan
, vms
, "vm-last"); /* "last" */
5349 if (!strcasecmp(chan
->language
, "pl")) {
5350 if (vms
->curmsg
&& (vms
->curmsg
!= vms
->lastmsg
)) {
5353 ten
= (vms
->curmsg
+ 1) / 10;
5354 one
= (vms
->curmsg
+ 1) % 10;
5356 if (vms
->curmsg
< 20) {
5357 snprintf(nextmsg
, sizeof(nextmsg
), "digits/n-%d", vms
->curmsg
+ 1);
5358 res
= wait_file2(chan
, vms
, nextmsg
);
5360 snprintf(nextmsg
, sizeof(nextmsg
), "digits/n-%d", ten
* 10);
5361 res
= wait_file2(chan
, vms
, nextmsg
);
5364 snprintf(nextmsg
, sizeof(nextmsg
), "digits/n-%d", one
);
5365 res
= wait_file2(chan
, vms
, nextmsg
);
5371 res
= wait_file2(chan
, vms
, "vm-message");
5373 if (!strcasecmp(chan
->language
, "se")) /* SWEDISH syntax */
5374 res
= wait_file2(chan
, vms
, "vm-meddelandet"); /* "message" */
5375 else /* DEFAULT syntax */
5376 res
= wait_file2(chan
, vms
, "vm-message");
5377 if (vms
->curmsg
&& (vms
->curmsg
!= vms
->lastmsg
)) {
5379 res
= ast_say_number(chan
, vms
->curmsg
+ 1, AST_DIGIT_ANY
, chan
->language
, NULL
);
5384 /* Retrieve info from VM attribute file */
5385 make_file(vms
->fn2
, sizeof(vms
->fn2
), vms
->curdir
, vms
->curmsg
);
5386 snprintf(filename
, sizeof(filename
), "%s.txt", vms
->fn2
);
5387 RETRIEVE(vms
->curdir
, vms
->curmsg
, vmu
);
5388 msg_cfg
= ast_config_load(filename
);
5390 ast_log(LOG_WARNING
, "No message attribute file?!! (%s)\n", filename
);
5394 if (!(origtime
= ast_variable_retrieve(msg_cfg
, "message", "origtime"))) {
5395 ast_log(LOG_WARNING
, "No origtime?!\n");
5396 DISPOSE(vms
->curdir
, vms
->curmsg
);
5397 ast_config_destroy(msg_cfg
);
5401 cid
= ast_strdupa(ast_variable_retrieve(msg_cfg
, "message", "callerid"));
5402 duration
= ast_variable_retrieve(msg_cfg
, "message", "duration");
5403 category
= ast_variable_retrieve(msg_cfg
, "message", "category");
5405 context
= ast_variable_retrieve(msg_cfg
, "message", "context");
5406 if (!strncasecmp("macro",context
,5)) /* Macro names in contexts are useless for our needs */
5407 context
= ast_variable_retrieve(msg_cfg
, "message","macrocontext");
5409 res
= play_message_category(chan
, category
);
5410 if ((!res
) && (ast_test_flag(vmu
, VM_ENVELOPE
)))
5411 res
= play_message_datetime(chan
, vmu
, origtime
, filename
);
5412 if ((!res
) && (ast_test_flag(vmu
, VM_SAYCID
)))
5413 res
= play_message_callerid(chan
, vms
, cid
, context
, 0);
5414 if ((!res
) && (ast_test_flag(vmu
, VM_SAYDURATION
)))
5415 res
= play_message_duration(chan
, vms
, duration
, vmu
->saydurationm
);
5416 /* Allow pressing '1' to skip envelope / callerid */
5419 ast_config_destroy(msg_cfg
);
5422 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, vms
->curmsg
);
5423 vms
->heard
[vms
->curmsg
] = 1;
5424 if ((res
= wait_file(chan
, vms
, vms
->fn
)) < 0) {
5425 ast_log(LOG_WARNING
, "Playback of message %s failed\n", vms
->fn
);
5429 DISPOSE(vms
->curdir
, vms
->curmsg
);
5433 #ifndef IMAP_STORAGE
5434 static int open_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
,int box
)
5437 int count_msg
, last_msg
;
5439 ast_copy_string(vms
->curbox
, mbox(box
), sizeof(vms
->curbox
));
5441 /* Rename the member vmbox HERE so that we don't try to return before
5442 * we know what's going on.
5444 snprintf(vms
->vmbox
, sizeof(vms
->vmbox
), "vm-%s", vms
->curbox
);
5446 /* Faster to make the directory than to check if it exists. */
5447 create_dirpath(vms
->curdir
, sizeof(vms
->curdir
), vmu
->context
, vms
->username
, vms
->curbox
);
5449 count_msg
= count_messages(vmu
, vms
->curdir
);
5453 vms
->lastmsg
= count_msg
- 1;
5456 The following test is needed in case sequencing gets messed up.
5457 There appears to be more than one way to mess up sequence, so
5458 we will not try to find all of the root causes--just fix it when
5462 last_msg
= last_message_index(vmu
, vms
->curdir
);
5465 else if (vms
->lastmsg
!= last_msg
)
5467 ast_log(LOG_NOTICE
, "Resequencing Mailbox: %s\n", vms
->curdir
);
5468 res
= resequence_mailbox(vmu
, vms
->curdir
);
5477 static int close_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
)
5480 #ifndef IMAP_STORAGE
5481 int res
= 0, nummsg
;
5484 if (vms
->lastmsg
<= -1)
5488 #ifndef IMAP_STORAGE
5489 /* Get the deleted messages fixed */
5490 if (vm_lock_path(vms
->curdir
))
5491 return ERROR_LOCK_PATH
;
5493 for (x
= 0; x
< vmu
->maxmsg
; x
++) {
5494 if (!vms
->deleted
[x
] && (strcasecmp(vms
->curbox
, "INBOX") || !vms
->heard
[x
])) {
5495 /* Save this message. It's not in INBOX or hasn't been heard */
5496 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, x
);
5497 if (!EXISTS(vms
->curdir
, x
, vms
->fn
, NULL
))
5500 make_file(vms
->fn2
, sizeof(vms
->fn2
), vms
->curdir
, vms
->curmsg
);
5501 if (strcmp(vms
->fn
, vms
->fn2
)) {
5502 RENAME(vms
->curdir
, x
, vmu
->mailbox
,vmu
->context
, vms
->curdir
, vms
->curmsg
, vms
->fn
, vms
->fn2
);
5504 } else if (!strcasecmp(vms
->curbox
, "INBOX") && vms
->heard
[x
] && !vms
->deleted
[x
]) {
5505 /* Move to old folder before deleting */
5506 res
= save_to_folder(vmu
, vms
, x
, 1);
5507 if (res
== ERROR_LOCK_PATH
|| res
== ERROR_MAILBOX_FULL
) {
5508 /* If save failed do not delete the message */
5509 ast_log(LOG_WARNING
, "Save failed. Not moving message: %s.\n", res
== ERROR_LOCK_PATH
? "unable to lock path" : "destination folder full");
5510 vms
->deleted
[x
] = 0;
5517 /* Delete ALL remaining messages */
5519 for (x
= vms
->curmsg
+ 1; x
<= nummsg
; x
++) {
5520 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, x
);
5521 if (EXISTS(vms
->curdir
, x
, vms
->fn
, NULL
))
5522 DELETE(vms
->curdir
, x
, vms
->fn
, vmu
);
5524 ast_unlock_path(vms
->curdir
);
5527 for (x
=0;x
< vmu
->maxmsg
;x
++) {
5528 if (vms
->deleted
[x
]) {
5529 if (option_debug
> 2)
5530 ast_log(LOG_DEBUG
,"IMAP delete of %d\n",x
);
5531 DELETE(vms
->curdir
, x
, vms
->fn
, vmu
);
5539 memset(vms
->deleted
, 0, vmu
->maxmsg
* sizeof(int));
5541 memset(vms
->heard
, 0, vmu
->maxmsg
* sizeof(int));
5546 /* In Greek even though we CAN use a syntax like "friends messages"
5547 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
5548 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
5549 * syntax for the above three categories which is more elegant.
5552 static int vm_play_folder_name_gr(struct ast_channel
*chan
, char *mbox
)
5557 buf
= alloca(strlen(mbox
)+2);
5561 if (!strcasecmp(mbox
, "vm-INBOX") || !strcasecmp(mbox
, "vm-Old")){
5562 cmd
= ast_play_and_wait(chan
, buf
); /* "NEA / PALIA" */
5563 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages"); /* "messages" -> "MYNHMATA" */
5565 cmd
= ast_play_and_wait(chan
, "vm-messages"); /* "messages" -> "MYNHMATA" */
5566 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
5570 static int vm_play_folder_name_pl(struct ast_channel
*chan
, char *mbox
)
5574 if (!strcasecmp(mbox
, "vm-INBOX") || !strcasecmp(mbox
, "vm-Old")) {
5575 if (!strcasecmp(mbox
, "vm-INBOX"))
5576 cmd
= ast_play_and_wait(chan
, "vm-new-e");
5578 cmd
= ast_play_and_wait(chan
, "vm-old-e");
5579 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages");
5581 cmd
= ast_play_and_wait(chan
, "vm-messages");
5582 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
);
5586 static int vm_play_folder_name_ua(struct ast_channel
*chan
, char *mbox
)
5590 if (!strcasecmp(mbox
, "vm-Family") || !strcasecmp(mbox
, "vm-Friends") || !strcasecmp(mbox
, "vm-Work")){
5591 cmd
= ast_play_and_wait(chan
, "vm-messages");
5592 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
);
5594 cmd
= ast_play_and_wait(chan
, mbox
);
5595 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages");
5599 static int vm_play_folder_name(struct ast_channel
*chan
, char *mbox
)
5603 if (!strcasecmp(chan
->language
, "it") || !strcasecmp(chan
->language
, "es") || !strcasecmp(chan
->language
, "pt") || !strcasecmp(chan
->language
, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
5604 cmd
= ast_play_and_wait(chan
, "vm-messages"); /* "messages */
5605 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
);
5606 } else if (!strcasecmp(chan
->language
, "gr")){
5607 return vm_play_folder_name_gr(chan
, mbox
);
5608 } else if (!strcasecmp(chan
->language
, "pl")){
5609 return vm_play_folder_name_pl(chan
, mbox
);
5610 } else if (!strcasecmp(chan
->language
, "ua")){ /* Ukrainian syntax */
5611 return vm_play_folder_name_ua(chan
, mbox
);
5612 } else if (!strcasecmp(chan
->language
, "he")){ /* Hebrew syntax */
5613 cmd
= ast_play_and_wait(chan
, mbox
);
5615 } else { /* Default English */
5616 cmd
= ast_play_and_wait(chan
, mbox
);
5617 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages"); /* "messages */
5622 In greek the plural for old/new is
5623 different so we need the following files
5624 We also need vm-denExeteMynhmata because
5625 this syntax is different.
5627 -> vm-Olds.wav : "Palia"
5628 -> vm-INBOXs.wav : "Nea"
5629 -> vm-denExeteMynhmata : "den exete mynhmata"
5633 static int vm_intro_gr(struct ast_channel
*chan
, struct vm_state
*vms
)
5637 if (vms
->newmessages
) {
5638 res
= ast_play_and_wait(chan
, "vm-youhave");
5640 res
= ast_say_number(chan
, vms
->newmessages
, AST_DIGIT_ANY
, chan
->language
, NULL
);
5642 if ((vms
->newmessages
== 1)) {
5643 res
= ast_play_and_wait(chan
, "vm-INBOX");
5645 res
= ast_play_and_wait(chan
, "vm-message");
5647 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5649 res
= ast_play_and_wait(chan
, "vm-messages");
5652 } else if (vms
->oldmessages
){
5653 res
= ast_play_and_wait(chan
, "vm-youhave");
5655 res
= ast_say_number(chan
, vms
->oldmessages
, AST_DIGIT_ANY
, chan
->language
, NULL
);
5656 if ((vms
->oldmessages
== 1)){
5657 res
= ast_play_and_wait(chan
, "vm-Old");
5659 res
= ast_play_and_wait(chan
, "vm-message");
5661 res
= ast_play_and_wait(chan
, "vm-Olds");
5663 res
= ast_play_and_wait(chan
, "vm-messages");
5665 } else if (!vms
->oldmessages
&& !vms
->newmessages
)
5666 res
= ast_play_and_wait(chan
, "vm-denExeteMynhmata");
5670 /* Default English syntax */
5671 static int vm_intro_en(struct ast_channel
*chan
, struct vm_state
*vms
)
5675 /* Introduce messages they have */
5676 res
= ast_play_and_wait(chan
, "vm-youhave");
5678 if (vms
->newmessages
) {
5679 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5681 res
= ast_play_and_wait(chan
, "vm-INBOX");
5682 if (vms
->oldmessages
&& !res
)
5683 res
= ast_play_and_wait(chan
, "vm-and");
5685 if ((vms
->newmessages
== 1))
5686 res
= ast_play_and_wait(chan
, "vm-message");
5688 res
= ast_play_and_wait(chan
, "vm-messages");
5692 if (!res
&& vms
->oldmessages
) {
5693 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5695 res
= ast_play_and_wait(chan
, "vm-Old");
5697 if (vms
->oldmessages
== 1)
5698 res
= ast_play_and_wait(chan
, "vm-message");
5700 res
= ast_play_and_wait(chan
, "vm-messages");
5704 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5705 res
= ast_play_and_wait(chan
, "vm-no");
5707 res
= ast_play_and_wait(chan
, "vm-messages");
5714 /* Default Hebrew syntax */
5715 static int vm_intro_he(struct ast_channel
*chan
, struct vm_state
*vms
)
5719 /* Introduce messages they have */
5721 if ((vms
->newmessages
) || (vms
->oldmessages
)) {
5722 res
= ast_play_and_wait(chan
, "vm-youhave");
5725 * The word "shtei" refers to the number 2 in hebrew when performing a count
5726 * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
5727 * an element, this is one of them.
5729 if (vms
->newmessages
) {
5731 if (vms
->newmessages
== 1) {
5732 res
= ast_play_and_wait(chan
, "vm-INBOX1");
5734 if (vms
->newmessages
== 2) {
5735 res
= ast_play_and_wait(chan
, "vm-shtei");
5737 res
= ast_say_number(chan
, vms
->newmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
5739 res
= ast_play_and_wait(chan
, "vm-INBOX");
5742 if (vms
->oldmessages
&& !res
) {
5743 res
= ast_play_and_wait(chan
, "vm-and");
5744 if (vms
->oldmessages
== 1) {
5745 res
= ast_play_and_wait(chan
, "vm-Old1");
5747 if (vms
->oldmessages
== 2) {
5748 res
= ast_play_and_wait(chan
, "vm-shtei");
5750 res
= ast_say_number(chan
, vms
->oldmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
5752 res
= ast_play_and_wait(chan
, "vm-Old");
5756 if (!res
&& vms
->oldmessages
&& !vms
->newmessages
) {
5758 if (vms
->oldmessages
== 1) {
5759 res
= ast_play_and_wait(chan
, "vm-Old1");
5761 if (vms
->oldmessages
== 2) {
5762 res
= ast_play_and_wait(chan
, "vm-shtei");
5764 res
= ast_say_number(chan
, vms
->oldmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
5766 res
= ast_play_and_wait(chan
, "vm-Old");
5771 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5773 res
= ast_play_and_wait(chan
, "vm-nomessages");
5782 /* ITALIAN syntax */
5783 static int vm_intro_it(struct ast_channel
*chan
, struct vm_state
*vms
)
5785 /* Introduce messages they have */
5787 if (!vms
->oldmessages
&& !vms
->newmessages
)
5788 res
= ast_play_and_wait(chan
, "vm-no") ||
5789 ast_play_and_wait(chan
, "vm-message");
5791 res
= ast_play_and_wait(chan
, "vm-youhave");
5792 if (!res
&& vms
->newmessages
) {
5793 res
= (vms
->newmessages
== 1) ?
5794 ast_play_and_wait(chan
, "digits/un") ||
5795 ast_play_and_wait(chan
, "vm-nuovo") ||
5796 ast_play_and_wait(chan
, "vm-message") :
5797 /* 2 or more new messages */
5798 say_and_wait(chan
, vms
->newmessages
, chan
->language
) ||
5799 ast_play_and_wait(chan
, "vm-nuovi") ||
5800 ast_play_and_wait(chan
, "vm-messages");
5801 if (!res
&& vms
->oldmessages
)
5802 res
= ast_play_and_wait(chan
, "vm-and");
5804 if (!res
&& vms
->oldmessages
) {
5805 res
= (vms
->oldmessages
== 1) ?
5806 ast_play_and_wait(chan
, "digits/un") ||
5807 ast_play_and_wait(chan
, "vm-vecchio") ||
5808 ast_play_and_wait(chan
, "vm-message") :
5809 /* 2 or more old messages */
5810 say_and_wait(chan
, vms
->oldmessages
, chan
->language
) ||
5811 ast_play_and_wait(chan
, "vm-vecchi") ||
5812 ast_play_and_wait(chan
, "vm-messages");
5818 static int vm_intro_pl(struct ast_channel
*chan
, struct vm_state
*vms
)
5820 /* Introduce messages they have */
5824 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5825 res
= ast_play_and_wait(chan
, "vm-no");
5826 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5829 res
= ast_play_and_wait(chan
, "vm-youhave");
5832 if (vms
->newmessages
) {
5833 num
= div(vms
->newmessages
, 10);
5834 if (vms
->newmessages
== 1) {
5835 res
= ast_play_and_wait(chan
, "digits/1-a");
5836 res
= res
? res
: ast_play_and_wait(chan
, "vm-new-a");
5837 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5838 } else if (num
.rem
> 1 && num
.rem
< 5 && num
.quot
!= 1) {
5841 res
= ast_play_and_wait(chan
, "digits/2-ie");
5843 res
= say_and_wait(chan
, vms
->newmessages
- 2 , chan
->language
);
5844 res
= res
? res
: ast_play_and_wait(chan
, "digits/2-ie");
5847 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5849 res
= res
? res
: ast_play_and_wait(chan
, "vm-new-e");
5850 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5852 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5853 res
= res
? res
: ast_play_and_wait(chan
, "vm-new-ych");
5854 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5856 if (!res
&& vms
->oldmessages
)
5857 res
= ast_play_and_wait(chan
, "vm-and");
5859 if (!res
&& vms
->oldmessages
) {
5860 num
= div(vms
->oldmessages
, 10);
5861 if (vms
->oldmessages
== 1) {
5862 res
= ast_play_and_wait(chan
, "digits/1-a");
5863 res
= res
? res
: ast_play_and_wait(chan
, "vm-old-a");
5864 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5865 } else if (num
.rem
> 1 && num
.rem
< 5 && num
.quot
!= 1) {
5868 res
= ast_play_and_wait(chan
, "digits/2-ie");
5870 res
= say_and_wait(chan
, vms
->oldmessages
- 2 , chan
->language
);
5871 res
= res
? res
: ast_play_and_wait(chan
, "digits/2-ie");
5874 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5876 res
= res
? res
: ast_play_and_wait(chan
, "vm-old-e");
5877 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5879 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5880 res
= res
? res
: ast_play_and_wait(chan
, "vm-old-ych");
5881 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5888 /* SWEDISH syntax */
5889 static int vm_intro_se(struct ast_channel
*chan
, struct vm_state
*vms
)
5891 /* Introduce messages they have */
5894 res
= ast_play_and_wait(chan
, "vm-youhave");
5898 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5899 res
= ast_play_and_wait(chan
, "vm-no");
5900 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5904 if (vms
->newmessages
) {
5905 if ((vms
->newmessages
== 1)) {
5906 res
= ast_play_and_wait(chan
, "digits/ett");
5907 res
= res
? res
: ast_play_and_wait(chan
, "vm-nytt");
5908 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5910 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5911 res
= res
? res
: ast_play_and_wait(chan
, "vm-nya");
5912 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5914 if (!res
&& vms
->oldmessages
)
5915 res
= ast_play_and_wait(chan
, "vm-and");
5917 if (!res
&& vms
->oldmessages
) {
5918 if (vms
->oldmessages
== 1) {
5919 res
= ast_play_and_wait(chan
, "digits/ett");
5920 res
= res
? res
: ast_play_and_wait(chan
, "vm-gammalt");
5921 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5923 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5924 res
= res
? res
: ast_play_and_wait(chan
, "vm-gamla");
5925 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5932 /* NORWEGIAN syntax */
5933 static int vm_intro_no(struct ast_channel
*chan
,struct vm_state
*vms
)
5935 /* Introduce messages they have */
5938 res
= ast_play_and_wait(chan
, "vm-youhave");
5942 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5943 res
= ast_play_and_wait(chan
, "vm-no");
5944 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5948 if (vms
->newmessages
) {
5949 if ((vms
->newmessages
== 1)) {
5950 res
= ast_play_and_wait(chan
, "digits/1");
5951 res
= res
? res
: ast_play_and_wait(chan
, "vm-ny");
5952 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5954 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5955 res
= res
? res
: ast_play_and_wait(chan
, "vm-nye");
5956 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5958 if (!res
&& vms
->oldmessages
)
5959 res
= ast_play_and_wait(chan
, "vm-and");
5961 if (!res
&& vms
->oldmessages
) {
5962 if (vms
->oldmessages
== 1) {
5963 res
= ast_play_and_wait(chan
, "digits/1");
5964 res
= res
? res
: ast_play_and_wait(chan
, "vm-gamel");
5965 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5967 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5968 res
= res
? res
: ast_play_and_wait(chan
, "vm-gamle");
5969 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5977 static int vm_intro_de(struct ast_channel
*chan
,struct vm_state
*vms
)
5979 /* Introduce messages they have */
5981 res
= ast_play_and_wait(chan
, "vm-youhave");
5983 if (vms
->newmessages
) {
5984 if ((vms
->newmessages
== 1))
5985 res
= ast_play_and_wait(chan
, "digits/1F");
5987 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5989 res
= ast_play_and_wait(chan
, "vm-INBOX");
5990 if (vms
->oldmessages
&& !res
)
5991 res
= ast_play_and_wait(chan
, "vm-and");
5993 if ((vms
->newmessages
== 1))
5994 res
= ast_play_and_wait(chan
, "vm-message");
5996 res
= ast_play_and_wait(chan
, "vm-messages");
6000 if (!res
&& vms
->oldmessages
) {
6001 if (vms
->oldmessages
== 1)
6002 res
= ast_play_and_wait(chan
, "digits/1F");
6004 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
6006 res
= ast_play_and_wait(chan
, "vm-Old");
6008 if (vms
->oldmessages
== 1)
6009 res
= ast_play_and_wait(chan
, "vm-message");
6011 res
= ast_play_and_wait(chan
, "vm-messages");
6015 if (!vms
->oldmessages
&& !vms
->newmessages
) {
6016 res
= ast_play_and_wait(chan
, "vm-no");
6018 res
= ast_play_and_wait(chan
, "vm-messages");
6025 /* SPANISH syntax */
6026 static int vm_intro_es(struct ast_channel
*chan
,struct vm_state
*vms
)
6028 /* Introduce messages they have */
6030 if (!vms
->oldmessages
&& !vms
->newmessages
) {
6031 res
= ast_play_and_wait(chan
, "vm-youhaveno");
6033 res
= ast_play_and_wait(chan
, "vm-messages");
6035 res
= ast_play_and_wait(chan
, "vm-youhave");
6038 if (vms
->newmessages
) {
6040 if ((vms
->newmessages
== 1)) {
6041 res
= ast_play_and_wait(chan
, "digits/1M");
6043 res
= ast_play_and_wait(chan
, "vm-message");
6045 res
= ast_play_and_wait(chan
, "vm-INBOXs");
6047 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
6049 res
= ast_play_and_wait(chan
, "vm-messages");
6051 res
= ast_play_and_wait(chan
, "vm-INBOX");
6054 if (vms
->oldmessages
&& !res
)
6055 res
= ast_play_and_wait(chan
, "vm-and");
6057 if (vms
->oldmessages
) {
6059 if (vms
->oldmessages
== 1) {
6060 res
= ast_play_and_wait(chan
, "digits/1M");
6062 res
= ast_play_and_wait(chan
, "vm-message");
6064 res
= ast_play_and_wait(chan
, "vm-Olds");
6066 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
6068 res
= ast_play_and_wait(chan
, "vm-messages");
6070 res
= ast_play_and_wait(chan
, "vm-Old");
6078 /* BRAZILIAN PORTUGUESE syntax */
6079 static int vm_intro_pt_BR(struct ast_channel
*chan
,struct vm_state
*vms
) {
6080 /* Introduce messages they have */
6082 if (!vms
->oldmessages
&& !vms
->newmessages
) {
6083 res
= ast_play_and_wait(chan
, "vm-nomessages");
6087 res
= ast_play_and_wait(chan
, "vm-youhave");
6089 if (vms
->newmessages
) {
6091 res
= ast_say_number(chan
, vms
->newmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
6092 if ((vms
->newmessages
== 1)) {
6094 res
= ast_play_and_wait(chan
, "vm-message");
6096 res
= ast_play_and_wait(chan
, "vm-INBOXs");
6100 res
= ast_play_and_wait(chan
, "vm-messages");
6102 res
= ast_play_and_wait(chan
, "vm-INBOX");
6104 if (vms
->oldmessages
&& !res
)
6105 res
= ast_play_and_wait(chan
, "vm-and");
6107 if (vms
->oldmessages
) {
6109 res
= ast_say_number(chan
, vms
->oldmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
6110 if (vms
->oldmessages
== 1) {
6112 res
= ast_play_and_wait(chan
, "vm-message");
6114 res
= ast_play_and_wait(chan
, "vm-Olds");
6118 res
= ast_play_and_wait(chan
, "vm-messages");
6120 res
= ast_play_and_wait(chan
, "vm-Old");
6127 static int vm_intro_fr(struct ast_channel
*chan
,struct vm_state
*vms
)
6129 /* Introduce messages they have */
6131 res
= ast_play_and_wait(chan
, "vm-youhave");
6133 if (vms
->newmessages
) {
6134 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
6136 res
= ast_play_and_wait(chan
, "vm-INBOX");
6137 if (vms
->oldmessages
&& !res
)
6138 res
= ast_play_and_wait(chan
, "vm-and");
6140 if ((vms
->newmessages
== 1))
6141 res
= ast_play_and_wait(chan
, "vm-message");
6143 res
= ast_play_and_wait(chan
, "vm-messages");
6147 if (!res
&& vms
->oldmessages
) {
6148 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
6150 res
= ast_play_and_wait(chan
, "vm-Old");
6152 if (vms
->oldmessages
== 1)
6153 res
= ast_play_and_wait(chan
, "vm-message");
6155 res
= ast_play_and_wait(chan
, "vm-messages");
6159 if (!vms
->oldmessages
&& !vms
->newmessages
) {
6160 res
= ast_play_and_wait(chan
, "vm-no");
6162 res
= ast_play_and_wait(chan
, "vm-messages");
6170 static int vm_intro_nl(struct ast_channel
*chan
,struct vm_state
*vms
)
6172 /* Introduce messages they have */
6174 res
= ast_play_and_wait(chan
, "vm-youhave");
6176 if (vms
->newmessages
) {
6177 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
6179 if (vms
->newmessages
== 1)
6180 res
= ast_play_and_wait(chan
, "vm-INBOXs");
6182 res
= ast_play_and_wait(chan
, "vm-INBOX");
6184 if (vms
->oldmessages
&& !res
)
6185 res
= ast_play_and_wait(chan
, "vm-and");
6187 if ((vms
->newmessages
== 1))
6188 res
= ast_play_and_wait(chan
, "vm-message");
6190 res
= ast_play_and_wait(chan
, "vm-messages");
6194 if (!res
&& vms
->oldmessages
) {
6195 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
6197 if (vms
->oldmessages
== 1)
6198 res
= ast_play_and_wait(chan
, "vm-Olds");
6200 res
= ast_play_and_wait(chan
, "vm-Old");
6203 if (vms
->oldmessages
== 1)
6204 res
= ast_play_and_wait(chan
, "vm-message");
6206 res
= ast_play_and_wait(chan
, "vm-messages");
6210 if (!vms
->oldmessages
&& !vms
->newmessages
) {
6211 res
= ast_play_and_wait(chan
, "vm-no");
6213 res
= ast_play_and_wait(chan
, "vm-messages");
6220 /* PORTUGUESE syntax */
6221 static int vm_intro_pt(struct ast_channel
*chan
,struct vm_state
*vms
)
6223 /* Introduce messages they have */
6225 res
= ast_play_and_wait(chan
, "vm-youhave");
6227 if (vms
->newmessages
) {
6228 res
= ast_say_number(chan
, vms
->newmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
6230 if ((vms
->newmessages
== 1)) {
6231 res
= ast_play_and_wait(chan
, "vm-message");
6233 res
= ast_play_and_wait(chan
, "vm-INBOXs");
6235 res
= ast_play_and_wait(chan
, "vm-messages");
6237 res
= ast_play_and_wait(chan
, "vm-INBOX");
6240 if (vms
->oldmessages
&& !res
)
6241 res
= ast_play_and_wait(chan
, "vm-and");
6243 if (!res
&& vms
->oldmessages
) {
6244 res
= ast_say_number(chan
, vms
->oldmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
6246 if (vms
->oldmessages
== 1) {
6247 res
= ast_play_and_wait(chan
, "vm-message");
6249 res
= ast_play_and_wait(chan
, "vm-Olds");
6251 res
= ast_play_and_wait(chan
, "vm-messages");
6253 res
= ast_play_and_wait(chan
, "vm-Old");
6258 if (!vms
->oldmessages
&& !vms
->newmessages
) {
6259 res
= ast_play_and_wait(chan
, "vm-no");
6261 res
= ast_play_and_wait(chan
, "vm-messages");
6270 /* in czech there must be declension of word new and message
6271 * czech : english : czech : english
6272 * --------------------------------------------------------
6273 * vm-youhave : you have
6274 * vm-novou : one new : vm-zpravu : message
6275 * vm-nove : 2-4 new : vm-zpravy : messages
6276 * vm-novych : 5-infinite new : vm-zprav : messages
6277 * vm-starou : one old
6278 * vm-stare : 2-4 old
6279 * vm-starych : 5-infinite old
6280 * jednu : one - falling 4.
6281 * vm-no : no ( no messages )
6284 static int vm_intro_cz(struct ast_channel
*chan
,struct vm_state
*vms
)
6287 res
= ast_play_and_wait(chan
, "vm-youhave");
6289 if (vms
->newmessages
) {
6290 if (vms
->newmessages
== 1) {
6291 res
= ast_play_and_wait(chan
, "digits/jednu");
6293 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
6296 if ((vms
->newmessages
== 1))
6297 res
= ast_play_and_wait(chan
, "vm-novou");
6298 if ((vms
->newmessages
) > 1 && (vms
->newmessages
< 5))
6299 res
= ast_play_and_wait(chan
, "vm-nove");
6300 if (vms
->newmessages
> 4)
6301 res
= ast_play_and_wait(chan
, "vm-novych");
6303 if (vms
->oldmessages
&& !res
)
6304 res
= ast_play_and_wait(chan
, "vm-and");
6306 if ((vms
->newmessages
== 1))
6307 res
= ast_play_and_wait(chan
, "vm-zpravu");
6308 if ((vms
->newmessages
) > 1 && (vms
->newmessages
< 5))
6309 res
= ast_play_and_wait(chan
, "vm-zpravy");
6310 if (vms
->newmessages
> 4)
6311 res
= ast_play_and_wait(chan
, "vm-zprav");
6314 if (!res
&& vms
->oldmessages
) {
6315 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
6317 if ((vms
->oldmessages
== 1))
6318 res
= ast_play_and_wait(chan
, "vm-starou");
6319 if ((vms
->oldmessages
) > 1 && (vms
->oldmessages
< 5))
6320 res
= ast_play_and_wait(chan
, "vm-stare");
6321 if (vms
->oldmessages
> 4)
6322 res
= ast_play_and_wait(chan
, "vm-starych");
6325 if ((vms
->oldmessages
== 1))
6326 res
= ast_play_and_wait(chan
, "vm-zpravu");
6327 if ((vms
->oldmessages
) > 1 && (vms
->oldmessages
< 5))
6328 res
= ast_play_and_wait(chan
, "vm-zpravy");
6329 if (vms
->oldmessages
> 4)
6330 res
= ast_play_and_wait(chan
, "vm-zprav");
6334 if (!vms
->oldmessages
&& !vms
->newmessages
) {
6335 res
= ast_play_and_wait(chan
, "vm-no");
6337 res
= ast_play_and_wait(chan
, "vm-zpravy");
6344 static int get_lastdigits(int num
)
6347 return (num
< 20) ? num
: num
% 10;
6350 static int vm_intro_ru(struct ast_channel
*chan
,struct vm_state
*vms
)
6356 res
= ast_play_and_wait(chan
, "vm-youhave");
6357 if (!res
&& vms
->newmessages
) {
6358 lastnum
= get_lastdigits(vms
->newmessages
);
6359 dcnum
= vms
->newmessages
- lastnum
;
6361 res
= say_and_wait(chan
, dcnum
, chan
->language
);
6362 if (!res
&& lastnum
) {
6364 res
= ast_play_and_wait(chan
, "digits/odno");
6366 res
= say_and_wait(chan
, lastnum
, chan
->language
);
6370 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-novoe" : "vm-novyh");
6372 if (!res
&& vms
->oldmessages
)
6373 res
= ast_play_and_wait(chan
, "vm-and");
6376 if (!res
&& vms
->oldmessages
) {
6377 lastnum
= get_lastdigits(vms
->oldmessages
);
6378 dcnum
= vms
->oldmessages
- lastnum
;
6380 res
= say_and_wait(chan
, dcnum
, chan
->language
);
6381 if (!res
&& lastnum
) {
6383 res
= ast_play_and_wait(chan
, "digits/odno");
6385 res
= say_and_wait(chan
, lastnum
, chan
->language
);
6389 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-staroe" : "vm-staryh");
6392 if (!res
&& !vms
->newmessages
&& !vms
->oldmessages
) {
6394 res
= ast_play_and_wait(chan
, "vm-no");
6400 res
= ast_play_and_wait(chan
, "vm-soobshenie");
6405 res
= ast_play_and_wait(chan
, "vm-soobsheniya");
6408 res
= ast_play_and_wait(chan
, "vm-soobsheniy");
6416 /* UKRAINIAN syntax */
6417 /* in ukrainian the syntax is different so we need the following files
6418 * --------------------------------------------------------
6419 * /digits/ua/1e 'odne'
6424 static int vm_intro_ua(struct ast_channel
*chan
,struct vm_state
*vms
)
6430 res
= ast_play_and_wait(chan
, "vm-youhave");
6431 if (!res
&& vms
->newmessages
) {
6432 lastnum
= get_lastdigits(vms
->newmessages
);
6433 dcnum
= vms
->newmessages
- lastnum
;
6435 res
= say_and_wait(chan
, dcnum
, chan
->language
);
6436 if (!res
&& lastnum
) {
6438 res
= ast_play_and_wait(chan
, "digits/ua/1e");
6440 res
= say_and_wait(chan
, lastnum
, chan
->language
);
6444 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-nove" : "vm-INBOX");
6446 if (!res
&& vms
->oldmessages
)
6447 res
= ast_play_and_wait(chan
, "vm-and");
6450 if (!res
&& vms
->oldmessages
) {
6451 lastnum
= get_lastdigits(vms
->oldmessages
);
6452 dcnum
= vms
->oldmessages
- lastnum
;
6454 res
= say_and_wait(chan
, dcnum
, chan
->language
);
6455 if (!res
&& lastnum
) {
6457 res
= ast_play_and_wait(chan
, "digits/ua/1e");
6459 res
= say_and_wait(chan
, lastnum
, chan
->language
);
6463 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-stare" : "vm-Old");
6466 if (!res
&& !vms
->newmessages
&& !vms
->oldmessages
) {
6468 res
= ast_play_and_wait(chan
, "vm-no");
6477 res
= ast_play_and_wait(chan
, "vm-message");
6480 res
= ast_play_and_wait(chan
, "vm-messages");
6489 static int vm_intro(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
)
6493 /* Notify the user that the temp greeting is set and give them the option to remove it */
6494 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6495 if (ast_test_flag(vmu
, VM_TEMPGREETWARN
)) {
6496 RETRIEVE(prefile
, -1, vmu
);
6497 if (ast_fileexists(prefile
, NULL
, NULL
) > 0)
6498 ast_play_and_wait(chan
, "vm-tempgreetactive");
6499 DISPOSE(prefile
, -1);
6502 /* Play voicemail intro - syntax is different for different languages */
6503 if (!strcasecmp(chan
->language
, "de")) { /* GERMAN syntax */
6504 return vm_intro_de(chan
, vms
);
6505 } else if (!strcasecmp(chan
->language
, "es")) { /* SPANISH syntax */
6506 return vm_intro_es(chan
, vms
);
6507 } else if (!strcasecmp(chan
->language
, "it")) { /* ITALIAN syntax */
6508 return vm_intro_it(chan
, vms
);
6509 } else if (!strcasecmp(chan
->language
, "fr")) { /* FRENCH syntax */
6510 return vm_intro_fr(chan
, vms
);
6511 } else if (!strcasecmp(chan
->language
, "nl")) { /* DUTCH syntax */
6512 return vm_intro_nl(chan
, vms
);
6513 } else if (!strcasecmp(chan
->language
, "pt")) { /* PORTUGUESE syntax */
6514 return vm_intro_pt(chan
, vms
);
6515 } else if (!strcasecmp(chan
->language
, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
6516 return vm_intro_pt_BR(chan
, vms
);
6517 } else if (!strcasecmp(chan
->language
, "cz")) { /* CZECH syntax */
6518 return vm_intro_cz(chan
, vms
);
6519 } else if (!strcasecmp(chan
->language
, "gr")) { /* GREEK syntax */
6520 return vm_intro_gr(chan
, vms
);
6521 } else if (!strcasecmp(chan
->language
, "pl")) { /* POLISH syntax */
6522 return vm_intro_pl(chan
, vms
);
6523 } else if (!strcasecmp(chan
->language
, "se")) { /* SWEDISH syntax */
6524 return vm_intro_se(chan
, vms
);
6525 } else if (!strcasecmp(chan
->language
, "no")) { /* NORWEGIAN syntax */
6526 return vm_intro_no(chan
, vms
);
6527 } else if (!strcasecmp(chan
->language
, "ru")) { /* RUSSIAN syntax */
6528 return vm_intro_ru(chan
, vms
);
6529 } else if (!strcasecmp(chan
->language
, "ua")) { /* UKRAINIAN syntax */
6530 return vm_intro_ua(chan
, vms
);
6531 } else if (!strcasecmp(chan
->language
, "he")) { /* HEBREW syntax */
6532 return vm_intro_he(chan
, vms
);
6533 } else { /* Default to ENGLISH */
6534 return vm_intro_en(chan
, vms
);
6538 static int vm_instructions(struct ast_channel
*chan
, struct vm_state
*vms
, int skipadvanced
)
6541 /* Play instructions and wait for new command */
6543 if (vms
->starting
) {
6544 if (vms
->lastmsg
> -1) {
6545 res
= ast_play_and_wait(chan
, "vm-onefor");
6546 if (!strcasecmp(chan
->language
, "he"))
6547 res
= ast_play_and_wait(chan
, "vm-for");
6549 res
= vm_play_folder_name(chan
, vms
->vmbox
);
6552 res
= ast_play_and_wait(chan
, "vm-opts");
6555 res
= ast_play_and_wait(chan
, "vm-prev");
6556 if (!res
&& !skipadvanced
)
6557 res
= ast_play_and_wait(chan
, "vm-advopts");
6559 res
= ast_play_and_wait(chan
, "vm-repeat");
6560 if (!res
&& (vms
->curmsg
!= vms
->lastmsg
))
6561 res
= ast_play_and_wait(chan
, "vm-next");
6563 if (!vms
->deleted
[vms
->curmsg
])
6564 res
= ast_play_and_wait(chan
, "vm-delete");
6566 res
= ast_play_and_wait(chan
, "vm-undelete");
6568 res
= ast_play_and_wait(chan
, "vm-toforward");
6570 res
= ast_play_and_wait(chan
, "vm-savemessage");
6574 res
= ast_play_and_wait(chan
, "vm-helpexit");
6576 res
= ast_waitfordigit(chan
, 6000);
6579 if (vms
->repeats
> 2) {
6587 static int vm_newuser(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
)
6592 char newpassword
[80] = "";
6593 char newpassword2
[80] = "";
6594 char prefile
[PATH_MAX
] = "";
6595 unsigned char buf
[256];
6598 if (ast_adsi_available(chan
)) {
6599 bytes
+= adsi_logo(buf
+ bytes
);
6600 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "New User Setup", "");
6601 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Not Done", "");
6602 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
6603 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
6604 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
6607 /* First, have the user change their password
6608 so they won't get here again */
6610 newpassword
[1] = '\0';
6611 newpassword
[0] = cmd
= ast_play_and_wait(chan
,"vm-newpassword");
6613 newpassword
[0] = '\0';
6614 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
6616 cmd
= ast_readstring(chan
,newpassword
+ strlen(newpassword
),sizeof(newpassword
)-1,2000,10000,"#");
6617 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
6619 newpassword2
[1] = '\0';
6620 newpassword2
[0] = cmd
= ast_play_and_wait(chan
,"vm-reenterpassword");
6622 newpassword2
[0] = '\0';
6623 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
6625 cmd
= ast_readstring(chan
,newpassword2
+ strlen(newpassword2
),sizeof(newpassword2
)-1,2000,10000,"#");
6626 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
6628 if (!strcmp(newpassword
, newpassword2
))
6630 ast_log(LOG_NOTICE
,"Password mismatch for user %s (%s != %s)\n", vms
->username
, newpassword
, newpassword2
);
6631 cmd
= ast_play_and_wait(chan
, "vm-mismatch");
6635 if (ast_strlen_zero(ext_pass_cmd
))
6636 vm_change_password(vmu
,newpassword
);
6638 vm_change_password_shell(vmu
,newpassword
);
6639 if (option_debug
> 2)
6640 ast_log(LOG_DEBUG
,"User %s set password to %s of length %d\n",vms
->username
,newpassword
,(int)strlen(newpassword
));
6641 cmd
= ast_play_and_wait(chan
,"vm-passchanged");
6643 /* If forcename is set, have the user record their name */
6644 if (ast_test_flag(vmu
, VM_FORCENAME
)) {
6645 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/greet", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6646 if (ast_fileexists(prefile
, NULL
, NULL
) < 1) {
6647 cmd
= play_record_review(chan
, "vm-rec-name", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6648 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
6653 /* If forcegreetings is set, have the user record their greetings */
6654 if (ast_test_flag(vmu
, VM_FORCEGREET
)) {
6655 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/unavail", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6656 if (ast_fileexists(prefile
, NULL
, NULL
) < 1) {
6657 cmd
= play_record_review(chan
, "vm-rec-unv", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6658 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
6662 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/busy", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6663 if (ast_fileexists(prefile
, NULL
, NULL
) < 1) {
6664 cmd
= play_record_review(chan
, "vm-rec-busy", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6665 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
6673 static int vm_options(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
)
6678 char newpassword
[80] = "";
6679 char newpassword2
[80] = "";
6680 char prefile
[PATH_MAX
] = "";
6681 unsigned char buf
[256];
6684 if (ast_adsi_available(chan
))
6686 bytes
+= adsi_logo(buf
+ bytes
);
6687 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Options Menu", "");
6688 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Not Done", "");
6689 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
6690 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
6691 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
6693 while ((cmd
>= 0) && (cmd
!= 't')) {
6698 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/unavail", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6699 cmd
= play_record_review(chan
,"vm-rec-unv",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6702 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/busy", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6703 cmd
= play_record_review(chan
,"vm-rec-busy",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6706 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/greet", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6707 cmd
= play_record_review(chan
,"vm-rec-name",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6710 cmd
= vm_tempgreeting(chan
, vmu
, vms
, fmtc
, record_gain
);
6713 if (vmu
->password
[0] == '-') {
6714 cmd
= ast_play_and_wait(chan
, "vm-no");
6717 newpassword
[1] = '\0';
6718 newpassword
[0] = cmd
= ast_play_and_wait(chan
,"vm-newpassword");
6720 newpassword
[0] = '\0';
6724 if ((cmd
= ast_readstring(chan
,newpassword
+ strlen(newpassword
),sizeof(newpassword
)-1,2000,10000,"#")) < 0) {
6728 newpassword2
[1] = '\0';
6729 newpassword2
[0] = cmd
= ast_play_and_wait(chan
,"vm-reenterpassword");
6731 newpassword2
[0] = '\0';
6736 if ((cmd
= ast_readstring(chan
,newpassword2
+ strlen(newpassword2
),sizeof(newpassword2
)-1,2000,10000,"#"))) {
6740 if (strcmp(newpassword
, newpassword2
)) {
6741 ast_log(LOG_NOTICE
,"Password mismatch for user %s (%s != %s)\n", vms
->username
, newpassword
, newpassword2
);
6742 cmd
= ast_play_and_wait(chan
, "vm-mismatch");
6745 if (ast_strlen_zero(ext_pass_cmd
))
6746 vm_change_password(vmu
,newpassword
);
6748 vm_change_password_shell(vmu
,newpassword
);
6749 if (option_debug
> 2)
6750 ast_log(LOG_DEBUG
,"User %s set password to %s of length %d\n",vms
->username
,newpassword
,(int)strlen(newpassword
));
6751 cmd
= ast_play_and_wait(chan
,"vm-passchanged");
6758 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6759 RETRIEVE(prefile
, -1, vmu
);
6760 if (ast_fileexists(prefile
, NULL
, NULL
))
6761 cmd
= ast_play_and_wait(chan
, "vm-tmpexists");
6762 DISPOSE(prefile
, -1);
6764 cmd
= ast_play_and_wait(chan
, "vm-options");
6766 cmd
= ast_waitfordigit(chan
,6000);
6778 static int vm_tempgreeting(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
)
6784 char prefile
[PATH_MAX
] = "";
6785 unsigned char buf
[256];
6786 char dest
[PATH_MAX
];
6789 if (ast_adsi_available(chan
)) {
6790 bytes
+= adsi_logo(buf
+ bytes
);
6791 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Temp Greeting Menu", "");
6792 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Not Done", "");
6793 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
6794 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
6795 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
6798 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6799 if ((res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, vms
->username
, "temp"))) {
6800 ast_log(LOG_WARNING
, "Failed to create directory (%s).\n", prefile
);
6803 while ((cmd
>= 0) && (cmd
!= 't')) {
6806 RETRIEVE(prefile
, -1, vmu
);
6807 if (ast_fileexists(prefile
, NULL
, NULL
) <= 0) {
6808 play_record_review(chan
, "vm-rec-temp", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6813 cmd
= play_record_review(chan
, "vm-rec-temp", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6816 DELETE(prefile
, -1, prefile
, vmu
);
6817 ast_play_and_wait(chan
, "vm-tempremoved");
6824 cmd
= ast_play_and_wait(chan
,
6825 ast_fileexists(prefile
, NULL
, NULL
) > 0 ? /* XXX always true ? */
6826 "vm-tempgreeting2" : "vm-tempgreeting");
6828 cmd
= ast_waitfordigit(chan
,6000);
6835 DISPOSE(prefile
, -1);
6844 static int vm_browse_messages_gr(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6848 if (vms
->lastmsg
> -1) {
6849 cmd
= play_message(chan
, vmu
, vms
);
6851 cmd
= ast_play_and_wait(chan
, "vm-youhaveno");
6852 if (!strcasecmp(vms
->vmbox
, "vm-INBOX") ||!strcasecmp(vms
->vmbox
, "vm-Old")){
6854 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%ss", vms
->curbox
);
6855 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6858 cmd
= ast_play_and_wait(chan
, "vm-messages");
6861 cmd
= ast_play_and_wait(chan
, "vm-messages");
6863 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6864 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6871 /* Default English syntax */
6872 static int vm_browse_messages_en(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6876 if (vms
->lastmsg
> -1) {
6877 cmd
= play_message(chan
, vmu
, vms
);
6879 cmd
= ast_play_and_wait(chan
, "vm-youhave");
6881 cmd
= ast_play_and_wait(chan
, "vm-no");
6883 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6884 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6887 cmd
= ast_play_and_wait(chan
, "vm-messages");
6893 static int vm_browse_messages_he(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6897 if (vms
->lastmsg
> -1) {
6898 cmd
= play_message(chan
, vmu
, vms
);
6900 if (!strcasecmp(vms
->fn
, "INBOX")) {
6901 cmd
= ast_play_and_wait(chan
, "vm-nonewmessages");
6903 cmd
= ast_play_and_wait(chan
, "vm-nomessages");
6910 /* ITALIAN syntax */
6911 static int vm_browse_messages_it(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6915 if (vms
->lastmsg
> -1) {
6916 cmd
= play_message(chan
, vmu
, vms
);
6918 cmd
= ast_play_and_wait(chan
, "vm-no");
6920 cmd
= ast_play_and_wait(chan
, "vm-message");
6922 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6923 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6929 /* SPANISH syntax */
6930 static int vm_browse_messages_es(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6934 if (vms
->lastmsg
> -1) {
6935 cmd
= play_message(chan
, vmu
, vms
);
6937 cmd
= ast_play_and_wait(chan
, "vm-youhaveno");
6939 cmd
= ast_play_and_wait(chan
, "vm-messages");
6941 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6942 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6948 /* PORTUGUESE syntax */
6949 static int vm_browse_messages_pt(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6953 if (vms
->lastmsg
> -1) {
6954 cmd
= play_message(chan
, vmu
, vms
);
6956 cmd
= ast_play_and_wait(chan
, "vm-no");
6958 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6959 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6962 cmd
= ast_play_and_wait(chan
, "vm-messages");
6967 static int vm_browse_messages(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6969 if (!strcasecmp(chan
->language
, "es")) { /* SPANISH */
6970 return vm_browse_messages_es(chan
, vms
, vmu
);
6971 } else if (!strcasecmp(chan
->language
, "it")) { /* ITALIAN */
6972 return vm_browse_messages_it(chan
, vms
, vmu
);
6973 } else if (!strcasecmp(chan
->language
, "pt") || !strcasecmp(chan
->language
, "pt_BR")) { /* PORTUGUESE */
6974 return vm_browse_messages_pt(chan
, vms
, vmu
);
6975 } else if (!strcasecmp(chan
->language
, "gr")){
6976 return vm_browse_messages_gr(chan
, vms
, vmu
); /* GREEK */
6977 } else if (!strcasecmp(chan
->language
, "he")) {
6978 return vm_browse_messages_he(chan
, vms
, vmu
); /* HEBREW */
6979 } else { /* Default to English syntax */
6980 return vm_browse_messages_en(chan
, vms
, vmu
);
6984 static int vm_authenticate(struct ast_channel
*chan
, char *mailbox
, int mailbox_size
,
6985 struct ast_vm_user
*res_vmu
, const char *context
, const char *prefix
,
6986 int skipuser
, int maxlogins
, int silent
)
6988 int useadsi
=0, valid
=0, logretries
=0;
6989 char password
[AST_MAX_EXTENSION
]="", *passptr
;
6990 struct ast_vm_user vmus
, *vmu
= NULL
;
6992 /* If ADSI is supported, setup login screen */
6993 adsi_begin(chan
, &useadsi
);
6994 if (!skipuser
&& useadsi
)
6996 if (!silent
&& !skipuser
&& ast_streamfile(chan
, "vm-login", chan
->language
)) {
6997 ast_log(LOG_WARNING
, "Couldn't stream login file\n");
7001 /* Authenticate them and get their mailbox/password */
7003 while (!valid
&& (logretries
< maxlogins
)) {
7004 /* Prompt for, and read in the username */
7005 if (!skipuser
&& ast_readstring(chan
, mailbox
, mailbox_size
- 1, 2000, 10000, "#") < 0) {
7006 ast_log(LOG_WARNING
, "Couldn't read username\n");
7009 if (ast_strlen_zero(mailbox
)) {
7010 if (chan
->cid
.cid_num
) {
7011 ast_copy_string(mailbox
, chan
->cid
.cid_num
, mailbox_size
);
7013 if (option_verbose
> 2)
7014 ast_verbose(VERBOSE_PREFIX_3
"Username not entered\n");
7019 adsi_password(chan
);
7021 if (!ast_strlen_zero(prefix
)) {
7022 char fullusername
[80] = "";
7023 ast_copy_string(fullusername
, prefix
, sizeof(fullusername
));
7024 strncat(fullusername
, mailbox
, sizeof(fullusername
) - 1 - strlen(fullusername
));
7025 ast_copy_string(mailbox
, fullusername
, mailbox_size
);
7029 ast_log(LOG_DEBUG
, "Before find user for mailbox %s\n",mailbox
);
7030 vmu
= find_user(&vmus
, context
, mailbox
);
7031 if (vmu
&& (vmu
->password
[0] == '\0' || (vmu
->password
[0] == '-' && vmu
->password
[1] == '\0'))) {
7032 /* saved password is blank, so don't bother asking */
7035 if (ast_streamfile(chan
, "vm-password", chan
->language
)) {
7036 ast_log(LOG_WARNING
, "Unable to stream password file\n");
7039 if (ast_readstring(chan
, password
, sizeof(password
) - 1, 2000, 10000, "#") < 0) {
7040 ast_log(LOG_WARNING
, "Unable to read password\n");
7046 passptr
= vmu
->password
;
7047 if (passptr
[0] == '-') passptr
++;
7049 if (vmu
&& !strcmp(passptr
, password
))
7052 if (option_verbose
> 2)
7053 ast_verbose( VERBOSE_PREFIX_3
"Incorrect password '%s' for user '%s' (context = %s)\n", password
, mailbox
, context
? context
: "default");
7054 if (!ast_strlen_zero(prefix
))
7059 if (skipuser
|| logretries
>= maxlogins
) {
7060 if (ast_streamfile(chan
, "vm-incorrect", chan
->language
)) {
7061 ast_log(LOG_WARNING
, "Unable to stream incorrect message\n");
7067 if (ast_streamfile(chan
, "vm-incorrect-mailbox", chan
->language
)) {
7068 ast_log(LOG_WARNING
, "Unable to stream incorrect mailbox message\n");
7072 if (ast_waitstream(chan
, "")) /* Channel is hung up */
7076 if (!valid
&& (logretries
>= maxlogins
)) {
7077 ast_stopstream(chan
);
7078 ast_play_and_wait(chan
, "vm-goodbye");
7081 if (vmu
&& !skipuser
) {
7082 memcpy(res_vmu
, vmu
, sizeof(struct ast_vm_user
));
7087 static int vm_execmain(struct ast_channel
*chan
, void *data
)
7089 /* XXX This is, admittedly, some pretty horrendus code. For some
7090 reason it just seemed a lot easier to do with GOTO's. I feel
7091 like I'm back in my GWBASIC days. XXX */
7095 struct ast_module_user
*u
;
7096 char prefixstr
[80] ="";
7097 char ext_context
[256]="";
7101 struct vm_state vms
;
7102 struct ast_vm_user
*vmu
= NULL
, vmus
;
7105 struct ast_flags flags
= { 0 };
7106 signed char record_gain
= 0;
7108 int play_folder
= 0;
7112 u
= ast_module_user_add(chan
);
7114 /* Add the vm_state to the active list and keep it active */
7115 memset(&vms
, 0, sizeof(vms
));
7118 memset(&vmus
, 0, sizeof(vmus
));
7120 if (chan
->_state
!= AST_STATE_UP
) {
7122 ast_log(LOG_DEBUG
, "Before ast_answer\n");
7126 if (!ast_strlen_zero(data
)) {
7127 char *opts
[OPT_ARG_ARRAY_SIZE
];
7129 AST_DECLARE_APP_ARGS(args
,
7134 parse
= ast_strdupa(data
);
7136 AST_STANDARD_APP_ARGS(args
, parse
);
7138 if (args
.argc
== 2) {
7139 if (ast_app_parse_options(vm_app_options
, &flags
, opts
, args
.argv1
)) {
7140 ast_module_user_remove(u
);
7143 if (ast_test_flag(&flags
, OPT_RECORDGAIN
)) {
7145 if (!ast_strlen_zero(opts
[OPT_ARG_RECORDGAIN
])) {
7146 if (sscanf(opts
[OPT_ARG_RECORDGAIN
], "%d", &gain
) != 1) {
7147 ast_log(LOG_WARNING
, "Invalid value '%s' provided for record gain option\n", opts
[OPT_ARG_RECORDGAIN
]);
7148 ast_module_user_remove(u
);
7151 record_gain
= (signed char) gain
;
7154 ast_log(LOG_WARNING
, "Invalid Gain level set with option g\n");
7157 if (ast_test_flag(&flags
, OPT_AUTOPLAY
) ) {
7159 if (opts
[OPT_ARG_PLAYFOLDER
]) {
7160 if (sscanf(opts
[OPT_ARG_PLAYFOLDER
], "%d", &play_folder
) != 1) {
7161 ast_log(LOG_WARNING
, "Invalid value '%s' provided for folder autoplay option\n", opts
[OPT_ARG_PLAYFOLDER
]);
7164 ast_log(LOG_WARNING
, "Invalid folder set with option a\n");
7166 if ( play_folder
> 9 || play_folder
< 0) {
7167 ast_log(LOG_WARNING
, "Invalid value '%d' provided for folder autoplay option\n", play_folder
);
7172 /* old style options parsing */
7173 while (*(args
.argv0
)) {
7174 if (*(args
.argv0
) == 's')
7175 ast_set_flag(&flags
, OPT_SILENT
);
7176 else if (*(args
.argv0
) == 'p')
7177 ast_set_flag(&flags
, OPT_PREPEND_MAILBOX
);
7185 valid
= ast_test_flag(&flags
, OPT_SILENT
);
7187 if ((context
= strchr(args
.argv0
, '@')))
7190 if (ast_test_flag(&flags
, OPT_PREPEND_MAILBOX
))
7191 ast_copy_string(prefixstr
, args
.argv0
, sizeof(prefixstr
));
7193 ast_copy_string(vms
.username
, args
.argv0
, sizeof(vms
.username
));
7195 if (!ast_strlen_zero(vms
.username
) && (vmu
= find_user(&vmus
, context
,vms
.username
)))
7202 res
= vm_authenticate(chan
, vms
.username
, sizeof(vms
.username
), &vmus
, context
, prefixstr
, skipuser
, maxlogins
, 0);
7205 ast_log(LOG_DEBUG
, "After vm_authenticate\n");
7214 /* If ADSI is supported, setup login screen */
7215 adsi_begin(chan
, &useadsi
);
7218 vms
.interactive
= 1;
7220 ast_copy_string(vms
.context
, vmu
->context
, sizeof(vms
.context
));
7221 vmstate_insert(&vms
);
7222 init_vm_state(&vms
);
7227 if (!(vms
.deleted
= ast_calloc(vmu
->maxmsg
, sizeof(int)))) {
7228 /* TODO: Handle memory allocation failure */
7230 if (!(vms
.heard
= ast_calloc(vmu
->maxmsg
, sizeof(int)))) {
7231 /* TODO: Handle memory allocation failure */
7234 /* Set language from config to override channel language */
7235 if (!ast_strlen_zero(vmu
->language
))
7236 ast_string_field_set(chan
, language
, vmu
->language
);
7237 create_dirpath(vms
.curdir
, sizeof(vms
.curdir
), vmu
->context
, vms
.username
, "");
7238 /* Retrieve old and new message counts */
7240 ast_log(LOG_DEBUG
, "Before open_mailbox\n");
7241 res
= open_mailbox(&vms
, vmu
, 1);
7242 if (res
== ERROR_LOCK_PATH
)
7244 vms
.oldmessages
= vms
.lastmsg
+ 1;
7245 if (option_debug
> 2)
7246 ast_log(LOG_DEBUG
, "Number of old messages: %d\n",vms
.oldmessages
);
7247 /* Start in INBOX */
7248 res
= open_mailbox(&vms
, vmu
, 0);
7249 if (res
== ERROR_LOCK_PATH
)
7251 vms
.newmessages
= vms
.lastmsg
+ 1;
7252 if (option_debug
> 2)
7253 ast_log(LOG_DEBUG
, "Number of new messages: %d\n",vms
.newmessages
);
7255 /* Select proper mailbox FIRST!! */
7257 res
= open_mailbox(&vms
, vmu
, play_folder
);
7258 if (res
== ERROR_LOCK_PATH
)
7261 /* If there are no new messages, inform the user and hangup */
7262 if (vms
.lastmsg
== -1) {
7263 cmd
= vm_browse_messages(chan
, &vms
, vmu
);
7268 if (!vms
.newmessages
&& vms
.oldmessages
) {
7269 /* If we only have old messages start here */
7270 res
= open_mailbox(&vms
, vmu
, 1);
7272 if (res
== ERROR_LOCK_PATH
)
7278 adsi_status(chan
, &vms
);
7281 /* Check to see if this is a new user */
7282 if (!strcasecmp(vmu
->mailbox
, vmu
->password
) &&
7283 (ast_test_flag(vmu
, VM_FORCENAME
| VM_FORCEGREET
))) {
7284 if (ast_play_and_wait(chan
, "vm-newuser") == -1)
7285 ast_log(LOG_WARNING
, "Couldn't stream new user file\n");
7286 cmd
= vm_newuser(chan
, vmu
, &vms
, vmfmts
, record_gain
);
7287 if ((cmd
== 't') || (cmd
== '#')) {
7291 } else if (cmd
< 0) {
7298 if (option_debug
> 2)
7299 ast_log(LOG_DEBUG
, "Checking quotas: comparing %u to %u\n",vms
.quota_usage
,vms
.quota_limit
);
7300 if (vms
.quota_limit
&& vms
.quota_usage
>= vms
.quota_limit
) {
7302 ast_log(LOG_DEBUG
, "*** QUOTA EXCEEDED!!\n");
7303 cmd
= ast_play_and_wait(chan
, "vm-mailboxfull");
7305 if (option_debug
> 2)
7306 ast_log(LOG_DEBUG
, "Checking quotas: User has %d messages and limit is %d.\n",(vms
.newmessages
+ vms
.oldmessages
),vmu
->maxmsg
);
7307 if ((vms
.newmessages
+ vms
.oldmessages
) >= vmu
->maxmsg
) {
7308 ast_log(LOG_WARNING
, "No more messages possible. User has %d messages and limit is %d.\n",(vms
.newmessages
+ vms
.oldmessages
),vmu
->maxmsg
);
7309 cmd
= ast_play_and_wait(chan
, "vm-mailboxfull");
7315 cmd
= vm_intro(chan
, vmu
, &vms
);
7320 while ((cmd
> -1) && (cmd
!= 't') && (cmd
!= '#')) {
7327 cmd
= vm_browse_messages(chan
, &vms
, vmu
);
7329 case '2': /* Change folders */
7331 adsi_folders(chan
, 0, "Change to folder...");
7332 cmd
= get_folder2(chan
, "vm-changeto", 0);
7335 } else if (cmd
> 0) {
7337 res
= close_mailbox(&vms
, vmu
);
7338 if (res
== ERROR_LOCK_PATH
)
7340 res
= open_mailbox(&vms
, vmu
, cmd
);
7341 if (res
== ERROR_LOCK_PATH
)
7347 adsi_status2(chan
, &vms
);
7350 cmd
= vm_play_folder_name(chan
, vms
.vmbox
);
7354 case '3': /* Advanced options */
7357 while ((cmd
> -1) && (cmd
!= 't') && (cmd
!= '#')) {
7359 case '1': /* Reply */
7360 if (vms
.lastmsg
> -1 && !vms
.starting
) {
7361 cmd
= advanced_options(chan
, vmu
, &vms
, vms
.curmsg
, 1, record_gain
);
7362 if (cmd
== ERROR_LOCK_PATH
) {
7367 cmd
= ast_play_and_wait(chan
, "vm-sorry");
7370 case '2': /* Callback */
7371 if (option_verbose
> 2 && !vms
.starting
)
7372 ast_verbose( VERBOSE_PREFIX_3
"Callback Requested\n");
7373 if (!ast_strlen_zero(vmu
->callback
) && vms
.lastmsg
> -1 && !vms
.starting
) {
7374 cmd
= advanced_options(chan
, vmu
, &vms
, vms
.curmsg
, 2, record_gain
);
7378 } else if (cmd
== ERROR_LOCK_PATH
) {
7384 cmd
= ast_play_and_wait(chan
, "vm-sorry");
7387 case '3': /* Envelope */
7388 if (vms
.lastmsg
> -1 && !vms
.starting
) {
7389 cmd
= advanced_options(chan
, vmu
, &vms
, vms
.curmsg
, 3, record_gain
);
7390 if (cmd
== ERROR_LOCK_PATH
) {
7395 cmd
= ast_play_and_wait(chan
, "vm-sorry");
7398 case '4': /* Dialout */
7399 if (!ast_strlen_zero(vmu
->dialout
)) {
7400 cmd
= dialout(chan
, vmu
, NULL
, vmu
->dialout
);
7407 cmd
= ast_play_and_wait(chan
, "vm-sorry");
7411 case '5': /* Leave VoiceMail */
7412 if (ast_test_flag(vmu
, VM_SVMAIL
)) {
7413 cmd
= forward_message(chan
, context
, &vms
, vmu
, vmfmts
, 1, record_gain
);
7414 if (cmd
== ERROR_LOCK_PATH
) {
7416 ast_log(LOG_WARNING
, "forward_message failed to lock path.\n");
7420 cmd
= ast_play_and_wait(chan
,"vm-sorry");
7424 case '*': /* Return to main menu */
7430 if (!vms
.starting
) {
7431 cmd
= ast_play_and_wait(chan
, "vm-toreply");
7433 if (!ast_strlen_zero(vmu
->callback
) && !vms
.starting
&& !cmd
) {
7434 cmd
= ast_play_and_wait(chan
, "vm-tocallback");
7436 if (!cmd
&& !vms
.starting
) {
7437 cmd
= ast_play_and_wait(chan
, "vm-tohearenv");
7439 if (!ast_strlen_zero(vmu
->dialout
) && !cmd
) {
7440 cmd
= ast_play_and_wait(chan
, "vm-tomakecall");
7442 if (ast_test_flag(vmu
, VM_SVMAIL
) && !cmd
)
7443 cmd
=ast_play_and_wait(chan
, "vm-leavemsg");
7445 cmd
= ast_play_and_wait(chan
, "vm-starmain");
7447 cmd
= ast_waitfordigit(chan
,6000);
7450 if (vms
.repeats
> 3)
7460 if (vms
.curmsg
> 0) {
7462 cmd
= play_message(chan
, vmu
, &vms
);
7464 cmd
= ast_play_and_wait(chan
, "vm-nomore");
7468 if (vms
.curmsg
< vms
.lastmsg
) {
7470 cmd
= play_message(chan
, vmu
, &vms
);
7472 cmd
= ast_play_and_wait(chan
, "vm-nomore");
7476 if (vms
.curmsg
>= 0 && vms
.curmsg
<= vms
.lastmsg
) {
7477 vms
.deleted
[vms
.curmsg
] = !vms
.deleted
[vms
.curmsg
];
7479 adsi_delete(chan
, &vms
);
7480 if (vms
.deleted
[vms
.curmsg
]) {
7481 if (play_folder
== 0)
7483 else if (play_folder
== 1)
7485 cmd
= ast_play_and_wait(chan
, "vm-deleted");
7488 if (play_folder
== 0)
7490 else if (play_folder
== 1)
7492 cmd
= ast_play_and_wait(chan
, "vm-undeleted");
7494 if (ast_test_flag((&globalflags
), VM_SKIPAFTERCMD
)) {
7495 if (vms
.curmsg
< vms
.lastmsg
) {
7497 cmd
= play_message(chan
, vmu
, &vms
);
7499 cmd
= ast_play_and_wait(chan
, "vm-nomore");
7502 } else /* Delete not valid if we haven't selected a message */
7510 if (vms
.lastmsg
> -1) {
7511 cmd
= forward_message(chan
, context
, &vms
, vmu
, vmfmts
, 0, record_gain
);
7512 if (cmd
== ERROR_LOCK_PATH
) {
7517 cmd
= ast_play_and_wait(chan
, "vm-nomore");
7520 if (vms
.curmsg
< 0 || vms
.curmsg
> vms
.lastmsg
) {
7521 /* No message selected */
7526 adsi_folders(chan
, 1, "Save to folder...");
7527 cmd
= get_folder2(chan
, "vm-savefolder", 1);
7528 box
= 0; /* Shut up compiler */
7532 } else if (cmd
> 0) {
7533 box
= cmd
= cmd
- '0';
7534 cmd
= save_to_folder(vmu
, &vms
, vms
.curmsg
, cmd
);
7535 if (cmd
== ERROR_LOCK_PATH
) {
7538 #ifndef IMAP_STORAGE
7540 vms
.deleted
[vms
.curmsg
] = 1;
7543 vms
.deleted
[vms
.curmsg
] = 0;
7544 vms
.heard
[vms
.curmsg
] = 0;
7547 make_file(vms
.fn
, sizeof(vms
.fn
), vms
.curdir
, vms
.curmsg
);
7549 adsi_message(chan
, &vms
);
7550 snprintf(vms
.fn
, sizeof(vms
.fn
), "vm-%s", mbox(box
));
7552 cmd
= ast_play_and_wait(chan
, "vm-message");
7554 cmd
= say_and_wait(chan
, vms
.curmsg
+ 1, chan
->language
);
7556 cmd
= ast_play_and_wait(chan
, "vm-savedto");
7558 cmd
= vm_play_folder_name(chan
, vms
.fn
);
7560 cmd
= ast_play_and_wait(chan
, "vm-mailboxfull");
7562 if (ast_test_flag((&globalflags
), VM_SKIPAFTERCMD
)) {
7563 if (vms
.curmsg
< vms
.lastmsg
) {
7565 cmd
= play_message(chan
, vmu
, &vms
);
7567 cmd
= ast_play_and_wait(chan
, "vm-nomore");
7572 if (!vms
.starting
) {
7573 cmd
= ast_play_and_wait(chan
, "vm-onefor");
7574 if (!strcasecmp(chan
->language
, "he"))
7575 cmd
= ast_play_and_wait(chan
, "vm-for");
7577 cmd
= vm_play_folder_name(chan
, vms
.vmbox
);
7579 cmd
= ast_play_and_wait(chan
, "vm-opts");
7581 cmd
= vm_instructions(chan
, &vms
, 1);
7586 cmd
= vm_options(chan
, vmu
, &vms
, vmfmts
, record_gain
);
7588 adsi_status(chan
, &vms
);
7590 default: /* Nothing */
7591 cmd
= vm_instructions(chan
, &vms
, 0);
7595 if ((cmd
== 't') || (cmd
== '#')) {
7605 ast_stopstream(chan
);
7609 res
= ast_play_and_wait(chan
, "vm-dialout");
7611 res
= ast_play_and_wait(chan
, "vm-goodbye");
7616 ast_adsi_unload_session(chan
);
7619 close_mailbox(&vms
, vmu
);
7621 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", vms
.username
, vmu
->context
);
7622 manager_event(EVENT_FLAG_CALL
, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context
, has_voicemail(ext_context
, NULL
));
7623 run_externnotify(vmu
->context
, vmu
->mailbox
);
7626 /* expunge message - use UID Expunge if supported on IMAP server*/
7627 if (option_debug
> 2)
7628 ast_log(LOG_DEBUG
, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted
,expungeonhangup
);
7629 if (vmu
&& deleted
== 1 && expungeonhangup
== 1) {
7630 #ifdef HAVE_IMAP_TK2006
7631 if (LEVELUIDPLUS (vms
.mailstream
)) {
7632 mail_expunge_full(vms
.mailstream
,NIL
,EX_UID
);
7635 mail_expunge(vms
.mailstream
);
7637 /* before we delete the state, we should copy pertinent info
7638 * back to the persistent model */
7639 vmstate_delete(&vms
);
7647 ast_module_user_remove(u
);
7652 static int vm_exec(struct ast_channel
*chan
, void *data
)
7655 struct ast_module_user
*u
;
7657 struct leave_vm_options leave_options
;
7658 struct ast_flags flags
= { 0 };
7659 static int deprecate_warning
= 0;
7660 char *opts
[OPT_ARG_ARRAY_SIZE
];
7661 AST_DECLARE_APP_ARGS(args
,
7666 u
= ast_module_user_add(chan
);
7668 memset(&leave_options
, 0, sizeof(leave_options
));
7670 if (chan
->_state
!= AST_STATE_UP
)
7673 if (!ast_strlen_zero(data
)) {
7674 tmp
= ast_strdupa(data
);
7675 AST_STANDARD_APP_ARGS(args
, tmp
);
7676 if (args
.argc
== 2) {
7677 if (ast_app_parse_options(vm_app_options
, &flags
, opts
, args
.argv1
)) {
7678 ast_module_user_remove(u
);
7681 ast_copy_flags(&leave_options
, &flags
, OPT_SILENT
| OPT_BUSY_GREETING
| OPT_UNAVAIL_GREETING
| OPT_PRIORITY_JUMP
);
7682 if (ast_test_flag(&flags
, OPT_RECORDGAIN
)) {
7685 if (sscanf(opts
[OPT_ARG_RECORDGAIN
], "%d", &gain
) != 1) {
7686 ast_log(LOG_WARNING
, "Invalid value '%s' provided for record gain option\n", opts
[OPT_ARG_RECORDGAIN
]);
7687 ast_module_user_remove(u
);
7690 leave_options
.record_gain
= (signed char) gain
;
7694 /* old style options parsing */
7696 char *orig_argv0
= args
.argv0
;
7697 while (*(args
.argv0
)) {
7698 if (*(args
.argv0
) == 's') {
7700 ast_set_flag(&leave_options
, OPT_SILENT
);
7701 } else if (*(args
.argv0
) == 'b') {
7703 ast_set_flag(&leave_options
, OPT_BUSY_GREETING
);
7704 } else if (*(args
.argv0
) == 'u') {
7706 ast_set_flag(&leave_options
, OPT_UNAVAIL_GREETING
);
7707 } else if (*(args
.argv0
) == 'j') {
7709 ast_set_flag(&leave_options
, OPT_PRIORITY_JUMP
);
7714 if (!deprecate_warning
&& old
) {
7715 deprecate_warning
= 1;
7716 ast_log(LOG_WARNING
, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0
);
7717 ast_log(LOG_WARNING
, "Please move all leading options to the second argument.\n");
7722 res
= ast_app_getdata(chan
, "vm-whichbox", tmp
, sizeof(tmp
) - 1, 0);
7724 ast_module_user_remove(u
);
7727 if (ast_strlen_zero(tmp
)) {
7728 ast_module_user_remove(u
);
7731 args
.argv0
= ast_strdupa(tmp
);
7734 res
= leave_voicemail(chan
, args
.argv0
, &leave_options
);
7736 if (res
== ERROR_LOCK_PATH
) {
7737 ast_log(LOG_ERROR
, "Could not leave voicemail. The path is already locked.\n");
7738 /*Send the call to n+101 priority, where n is the current priority*/
7739 if (ast_test_flag(&leave_options
, OPT_PRIORITY_JUMP
) || ast_opt_priority_jumping
)
7740 if (ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101))
7741 ast_log(LOG_WARNING
, "Extension %s, priority %d doesn't exist.\n", chan
->exten
, chan
->priority
+ 101);
7742 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
7746 ast_module_user_remove(u
);
7751 static struct ast_vm_user
*find_or_create(char *context
, char *mbox
)
7753 struct ast_vm_user
*vmu
;
7754 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
7755 if (ast_test_flag((&globalflags
), VM_SEARCH
) && !strcasecmp(mbox
, vmu
->mailbox
))
7757 if (context
&& (!strcasecmp(context
, vmu
->context
)) && (!strcasecmp(mbox
, vmu
->mailbox
)))
7762 if ((vmu
= ast_calloc(1, sizeof(*vmu
)))) {
7763 ast_copy_string(vmu
->context
, context
, sizeof(vmu
->context
));
7764 ast_copy_string(vmu
->mailbox
, mbox
, sizeof(vmu
->mailbox
));
7765 AST_LIST_INSERT_TAIL(&users
, vmu
, list
);
7771 static int append_mailbox(char *context
, char *mbox
, char *data
)
7773 /* Assumes lock is already held */
7777 struct ast_vm_user
*vmu
;
7779 tmp
= ast_strdupa(data
);
7781 if ((vmu
= find_or_create(context
, mbox
))) {
7782 populate_defaults(vmu
);
7785 if ((s
= strsep(&stringp
, ",")))
7786 ast_copy_string(vmu
->password
, s
, sizeof(vmu
->password
));
7787 if (stringp
&& (s
= strsep(&stringp
, ",")))
7788 ast_copy_string(vmu
->fullname
, s
, sizeof(vmu
->fullname
));
7789 if (stringp
&& (s
= strsep(&stringp
, ",")))
7790 ast_copy_string(vmu
->email
, s
, sizeof(vmu
->email
));
7791 if (stringp
&& (s
= strsep(&stringp
, ",")))
7792 ast_copy_string(vmu
->pager
, s
, sizeof(vmu
->pager
));
7793 if (stringp
&& (s
= strsep(&stringp
, ",")))
7794 apply_options(vmu
, s
);
7799 static int vm_box_exists(struct ast_channel
*chan
, void *data
)
7801 struct ast_module_user
*u
;
7802 struct ast_vm_user svm
;
7803 char *context
, *box
;
7804 int priority_jump
= 0;
7805 AST_DECLARE_APP_ARGS(args
,
7807 AST_APP_ARG(options
);
7810 if (ast_strlen_zero(data
)) {
7811 ast_log(LOG_ERROR
, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
7815 u
= ast_module_user_add(chan
);
7817 box
= ast_strdupa(data
);
7819 AST_STANDARD_APP_ARGS(args
, box
);
7822 if (strchr(args
.options
, 'j'))
7826 if ((context
= strchr(args
.mbox
, '@'))) {
7831 if (find_user(&svm
, context
, args
.mbox
)) {
7832 pbx_builtin_setvar_helper(chan
, "VMBOXEXISTSSTATUS", "SUCCESS");
7833 if (priority_jump
|| ast_opt_priority_jumping
)
7834 if (ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101))
7835 ast_log(LOG_WARNING
, "VM box %s@%s exists, but extension %s, priority %d doesn't exist\n", box
, context
, chan
->exten
, chan
->priority
+ 101);
7837 pbx_builtin_setvar_helper(chan
, "VMBOXEXISTSSTATUS", "FAILED");
7838 ast_module_user_remove(u
);
7842 static int vmauthenticate(struct ast_channel
*chan
, void *data
)
7844 struct ast_module_user
*u
;
7845 char *s
= data
, *user
=NULL
, *context
=NULL
, mailbox
[AST_MAX_EXTENSION
] = "";
7846 struct ast_vm_user vmus
;
7847 char *options
= NULL
;
7848 int silent
= 0, skipuser
= 0;
7851 u
= ast_module_user_add(chan
);
7855 user
= strsep(&s
, "|");
7856 options
= strsep(&s
, "|");
7859 user
= strsep(&s
, "@");
7860 context
= strsep(&s
, "");
7861 if (!ast_strlen_zero(user
))
7863 ast_copy_string(mailbox
, user
, sizeof(mailbox
));
7868 silent
= (strchr(options
, 's')) != NULL
;
7871 if (!vm_authenticate(chan
, mailbox
, sizeof(mailbox
), &vmus
, context
, NULL
, skipuser
, 3, silent
)) {
7872 pbx_builtin_setvar_helper(chan
, "AUTH_MAILBOX", mailbox
);
7873 pbx_builtin_setvar_helper(chan
, "AUTH_CONTEXT", vmus
.context
);
7874 ast_play_and_wait(chan
, "auth-thankyou");
7878 ast_module_user_remove(u
);
7882 static char voicemail_show_users_help
[] =
7883 "Usage: voicemail show users [for <context>]\n"
7884 " Lists all mailboxes currently set up\n";
7886 static char voicemail_show_zones_help
[] =
7887 "Usage: voicemail show zones\n"
7888 " Lists zone message formats\n";
7890 static int handle_voicemail_show_users(int fd
, int argc
, char *argv
[])
7892 struct ast_vm_user
*vmu
;
7893 char *output_format
= "%-10s %-5s %-25s %-10s %6s\n";
7895 if ((argc
< 3) || (argc
> 5) || (argc
== 4)) return RESULT_SHOWUSAGE
;
7896 else if ((argc
== 5) && strcmp(argv
[3],"for")) return RESULT_SHOWUSAGE
;
7898 AST_LIST_LOCK(&users
);
7899 if (!AST_LIST_EMPTY(&users
)) {
7901 ast_cli(fd
, output_format
, "Context", "Mbox", "User", "Zone", "NewMsg");
7904 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
7905 if (!strcmp(argv
[4],vmu
->context
))
7909 ast_cli(fd
, output_format
, "Context", "Mbox", "User", "Zone", "NewMsg");
7911 ast_cli(fd
, "No such voicemail context \"%s\"\n", argv
[4]);
7912 AST_LIST_UNLOCK(&users
);
7913 return RESULT_FAILURE
;
7916 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
7917 int newmsgs
= 0, oldmsgs
= 0;
7918 char count
[12], tmp
[256] = "";
7920 if ((argc
== 3) || ((argc
== 5) && !strcmp(argv
[4],vmu
->context
))) {
7921 snprintf(tmp
, sizeof(tmp
), "%s@%s", vmu
->mailbox
, ast_strlen_zero(vmu
->context
) ? "default" : vmu
->context
);
7922 inboxcount(tmp
, &newmsgs
, &oldmsgs
);
7923 snprintf(count
,sizeof(count
),"%d",newmsgs
);
7924 ast_cli(fd
, output_format
, vmu
->context
, vmu
->mailbox
, vmu
->fullname
, vmu
->zonetag
, count
);
7928 ast_cli(fd
, "There are no voicemail users currently defined\n");
7929 AST_LIST_UNLOCK(&users
);
7930 return RESULT_FAILURE
;
7932 AST_LIST_UNLOCK(&users
);
7933 return RESULT_SUCCESS
;
7936 static int handle_voicemail_show_zones(int fd
, int argc
, char *argv
[])
7938 struct vm_zone
*zone
;
7939 char *output_format
= "%-15s %-20s %-45s\n";
7940 int res
= RESULT_SUCCESS
;
7943 return RESULT_SHOWUSAGE
;
7945 AST_LIST_LOCK(&zones
);
7946 if (!AST_LIST_EMPTY(&zones
)) {
7947 ast_cli(fd
, output_format
, "Zone", "Timezone", "Message Format");
7948 AST_LIST_TRAVERSE(&zones
, zone
, list
) {
7949 ast_cli(fd
, output_format
, zone
->name
, zone
->timezone
, zone
->msg_format
);
7952 ast_cli(fd
, "There are no voicemail zones currently defined\n");
7953 res
= RESULT_FAILURE
;
7955 AST_LIST_UNLOCK(&zones
);
7960 static char *complete_voicemail_show_users(const char *line
, const char *word
, int pos
, int state
)
7964 struct ast_vm_user
*vmu
;
7965 const char *context
= "";
7967 /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
7971 return (state
== 0) ? ast_strdup("for") : NULL
;
7972 wordlen
= strlen(word
);
7973 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
7974 if (!strncasecmp(word
, vmu
->context
, wordlen
)) {
7975 if (context
&& strcmp(context
, vmu
->context
) && ++which
> state
)
7976 return ast_strdup(vmu
->context
);
7977 /* ignore repeated contexts ? */
7978 context
= vmu
->context
;
7984 static struct ast_cli_entry cli_show_voicemail_users_deprecated
= {
7985 { "show", "voicemail", "users", NULL
},
7986 handle_voicemail_show_users
, NULL
,
7987 NULL
, complete_voicemail_show_users
};
7989 static struct ast_cli_entry cli_show_voicemail_zones_deprecated
= {
7990 { "show", "voicemail", "zones", NULL
},
7991 handle_voicemail_show_zones
, NULL
,
7994 static struct ast_cli_entry cli_voicemail
[] = {
7995 { { "voicemail", "show", "users", NULL
},
7996 handle_voicemail_show_users
, "List defined voicemail boxes",
7997 voicemail_show_users_help
, complete_voicemail_show_users
, &cli_show_voicemail_users_deprecated
},
7999 { { "voicemail", "show", "zones", NULL
},
8000 handle_voicemail_show_zones
, "List zone message formats",
8001 voicemail_show_zones_help
, NULL
, &cli_show_voicemail_zones_deprecated
},
8004 static void free_vm_users(void)
8006 struct ast_vm_user
*cur
;
8007 struct vm_zone
*zcur
;
8009 AST_LIST_LOCK(&users
);
8010 while ((cur
= AST_LIST_REMOVE_HEAD(&users
, list
))) {
8011 ast_set_flag(cur
, VM_ALLOCED
);
8014 AST_LIST_UNLOCK(&users
);
8016 AST_LIST_LOCK(&zones
);
8017 while ((zcur
= AST_LIST_REMOVE_HEAD(&zones
, list
))) {
8020 AST_LIST_UNLOCK(&zones
);
8023 static int load_config(void)
8025 struct ast_vm_user
*cur
;
8026 struct ast_config
*cfg
, *ucfg
;
8028 struct ast_variable
*var
;
8029 const char *notifystr
= NULL
;
8030 const char *smdistr
= NULL
;
8031 const char *astattach
;
8032 const char *astsearch
;
8033 const char *astsaycid
;
8034 const char *send_voicemail
;
8036 const char *imap_server
;
8037 const char *imap_port
;
8038 const char *imap_flags
;
8039 const char *imap_folder
;
8040 const char *auth_user
;
8041 const char *auth_password
;
8042 const char *expunge_on_hangup
;
8043 const char *imap_timeout
;
8045 const char *astcallop
;
8046 const char *astreview
;
8047 const char *asttempgreetwarn
;
8048 const char *astskipcmd
;
8049 const char *asthearenv
;
8050 const char *astsaydurationinfo
;
8051 const char *astsaydurationminfo
;
8052 const char *silencestr
;
8053 const char *maxmsgstr
;
8054 const char *astdirfwd
;
8055 const char *thresholdstr
;
8057 const char *astemail
;
8058 const char *ucontext
;
8059 const char *astmailcmd
= SENDMAIL
;
8060 const char *astforcename
;
8061 const char *astforcegreet
;
8064 const char *dialoutcxt
= NULL
;
8065 const char *callbackcxt
= NULL
;
8066 const char *exitcxt
= NULL
;
8068 const char *emaildateformatstr
;
8069 const char *volgainstr
;
8073 cfg
= ast_config_load(VOICEMAIL_CONFIG
);
8077 AST_LIST_LOCK(&users
);
8079 memset(ext_pass_cmd
, 0, sizeof(ext_pass_cmd
));
8082 /* General settings */
8084 if (!(ucontext
= ast_variable_retrieve(cfg
, "general", "userscontext")))
8085 ucontext
= "default";
8086 ast_copy_string(userscontext
, ucontext
, sizeof(userscontext
));
8087 /* Attach voice message to mail message ? */
8088 if (!(astattach
= ast_variable_retrieve(cfg
, "general", "attach")))
8090 ast_set2_flag((&globalflags
), ast_true(astattach
), VM_ATTACH
);
8092 if (!(astsearch
= ast_variable_retrieve(cfg
, "general", "searchcontexts")))
8094 ast_set2_flag((&globalflags
), ast_true(astsearch
), VM_SEARCH
);
8097 if ((volgainstr
= ast_variable_retrieve(cfg
, "general", "volgain")))
8098 sscanf(volgainstr
, "%lf", &volgain
);
8101 strcpy(odbc_database
, "asterisk");
8102 if ((thresholdstr
= ast_variable_retrieve(cfg
, "general", "odbcstorage"))) {
8103 ast_copy_string(odbc_database
, thresholdstr
, sizeof(odbc_database
));
8105 strcpy(odbc_table
, "voicemessages");
8106 if ((thresholdstr
= ast_variable_retrieve(cfg
, "general", "odbctable"))) {
8107 ast_copy_string(odbc_table
, thresholdstr
, sizeof(odbc_table
));
8111 strcpy(mailcmd
, SENDMAIL
);
8112 if ((astmailcmd
= ast_variable_retrieve(cfg
, "general", "mailcmd")))
8113 ast_copy_string(mailcmd
, astmailcmd
, sizeof(mailcmd
)); /* User setting */
8116 if ((silencestr
= ast_variable_retrieve(cfg
, "general", "maxsilence"))) {
8117 maxsilence
= atoi(silencestr
);
8122 if (!(maxmsgstr
= ast_variable_retrieve(cfg
, "general", "maxmsg"))) {
8125 maxmsg
= atoi(maxmsgstr
);
8127 ast_log(LOG_WARNING
, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr
, MAXMSG
);
8129 } else if (maxmsg
> MAXMSGLIMIT
) {
8130 ast_log(LOG_WARNING
, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT
, maxmsgstr
);
8131 maxmsg
= MAXMSGLIMIT
;
8135 /* Load date format config for voicemail mail */
8136 if ((emaildateformatstr
= ast_variable_retrieve(cfg
, "general", "emaildateformat"))) {
8137 ast_copy_string(emaildateformat
, emaildateformatstr
, sizeof(emaildateformat
));
8140 /* External password changing command */
8141 if ((extpc
= ast_variable_retrieve(cfg
, "general", "externpass"))) {
8142 ast_copy_string(ext_pass_cmd
,extpc
,sizeof(ext_pass_cmd
));
8145 /* IMAP server address */
8146 if ((imap_server
= ast_variable_retrieve(cfg
, "general", "imapserver"))) {
8147 ast_copy_string(imapserver
, imap_server
, sizeof(imapserver
));
8149 ast_copy_string(imapserver
,"localhost", sizeof(imapserver
));
8151 /* IMAP server port */
8152 if ((imap_port
= ast_variable_retrieve(cfg
, "general", "imapport"))) {
8153 ast_copy_string(imapport
, imap_port
, sizeof(imapport
));
8155 ast_copy_string(imapport
,"143", sizeof(imapport
));
8157 /* IMAP server flags */
8158 if ((imap_flags
= ast_variable_retrieve(cfg
, "general", "imapflags"))) {
8159 ast_copy_string(imapflags
, imap_flags
, sizeof(imapflags
));
8161 /* IMAP server master username */
8162 if ((auth_user
= ast_variable_retrieve(cfg
, "general", "authuser"))) {
8163 ast_copy_string(authuser
, auth_user
, sizeof(authuser
));
8165 /* IMAP server master password */
8166 if ((auth_password
= ast_variable_retrieve(cfg
, "general", "authpassword"))) {
8167 ast_copy_string(authpassword
, auth_password
, sizeof(authpassword
));
8169 /* Expunge on exit */
8170 if ((expunge_on_hangup
= ast_variable_retrieve(cfg
, "general", "expungeonhangup"))) {
8171 if (ast_false(expunge_on_hangup
))
8172 expungeonhangup
= 0;
8174 expungeonhangup
= 1;
8176 expungeonhangup
= 1;
8178 /* IMAP voicemail folder */
8179 if ((imap_folder
= ast_variable_retrieve(cfg
, "general", "imapfolder"))) {
8180 ast_copy_string(imapfolder
, imap_folder
, sizeof(imapfolder
));
8182 ast_copy_string(imapfolder
,"INBOX", sizeof(imapfolder
));
8185 /* There is some very unorthodox casting done here. This is due
8186 * to the way c-client handles the argument passed in. It expects a
8187 * void pointer and casts the pointer directly to a long without
8188 * first dereferencing it. */
8190 if ((imap_timeout
= ast_variable_retrieve(cfg
, "general", "imapreadtimeout"))) {
8191 mail_parameters(NIL
, SET_READTIMEOUT
, (void *) (atol(imap_timeout
)));
8193 mail_parameters(NIL
, SET_READTIMEOUT
, (void *) DEFAULT_IMAP_TCP_TIMEOUT
);
8196 if ((imap_timeout
= ast_variable_retrieve(cfg
, "general", "imapwritetimeout"))) {
8197 mail_parameters(NIL
, SET_WRITETIMEOUT
, (void *) (atol(imap_timeout
)));
8199 mail_parameters(NIL
, SET_WRITETIMEOUT
, (void *) DEFAULT_IMAP_TCP_TIMEOUT
);
8202 if ((imap_timeout
= ast_variable_retrieve(cfg
, "general", "imapopentimeout"))) {
8203 mail_parameters(NIL
, SET_OPENTIMEOUT
, (void *) (atol(imap_timeout
)));
8205 mail_parameters(NIL
, SET_OPENTIMEOUT
, (void *) DEFAULT_IMAP_TCP_TIMEOUT
);
8208 if ((imap_timeout
= ast_variable_retrieve(cfg
, "general", "imapclosetimeout"))) {
8209 mail_parameters(NIL
, SET_CLOSETIMEOUT
, (void *) (atol(imap_timeout
)));
8211 mail_parameters(NIL
, SET_CLOSETIMEOUT
, (void *) DEFAULT_IMAP_TCP_TIMEOUT
);
8215 /* External voicemail notify application */
8217 if ((notifystr
= ast_variable_retrieve(cfg
, "general", "externnotify"))) {
8218 ast_copy_string(externnotify
, notifystr
, sizeof(externnotify
));
8219 if (option_debug
> 2)
8220 ast_log(LOG_DEBUG
, "found externnotify: %s\n", externnotify
);
8221 if (!strcasecmp(externnotify
, "smdi")) {
8223 ast_log(LOG_DEBUG
, "Using SMDI for external voicemail notification\n");
8224 if ((smdistr
= ast_variable_retrieve(cfg
, "general", "smdiport"))) {
8225 smdi_iface
= ast_smdi_interface_find(smdistr
);
8228 ast_log(LOG_DEBUG
, "No SMDI interface set, trying default (/dev/ttyS0)\n");
8229 smdi_iface
= ast_smdi_interface_find("/dev/ttyS0");
8233 ast_log(LOG_ERROR
, "No valid SMDI interface specfied, disabling external voicemail notification\n");
8234 externnotify
[0] = '\0';
8238 externnotify
[0] = '\0';
8241 /* Silence treshold */
8242 silencethreshold
= 256;
8243 if ((thresholdstr
= ast_variable_retrieve(cfg
, "general", "silencethreshold")))
8244 silencethreshold
= atoi(thresholdstr
);
8246 if (!(astemail
= ast_variable_retrieve(cfg
, "general", "serveremail")))
8247 astemail
= ASTERISK_USERNAME
;
8248 ast_copy_string(serveremail
, astemail
, sizeof(serveremail
));
8251 if ((s
= ast_variable_retrieve(cfg
, "general", "maxmessage"))) {
8252 if (sscanf(s
, "%d", &x
) == 1) {
8255 ast_log(LOG_WARNING
, "Invalid max message time length\n");
8260 if ((s
= ast_variable_retrieve(cfg
, "general", "minmessage"))) {
8261 if (sscanf(s
, "%d", &x
) == 1) {
8263 if (maxsilence
<= vmminmessage
)
8264 ast_log(LOG_WARNING
, "maxsilence should be less than minmessage or you may get empty messages\n");
8266 ast_log(LOG_WARNING
, "Invalid min message time length\n");
8269 fmt
= ast_variable_retrieve(cfg
, "general", "format");
8272 ast_copy_string(vmfmts
, fmt
, sizeof(vmfmts
));
8275 if ((s
= ast_variable_retrieve(cfg
, "general", "maxgreet"))) {
8276 if (sscanf(s
, "%d", &x
) == 1) {
8279 ast_log(LOG_WARNING
, "Invalid max message greeting length\n");
8283 if ((s
= ast_variable_retrieve(cfg
, "general", "skipms"))) {
8284 if (sscanf(s
, "%d", &x
) == 1) {
8287 ast_log(LOG_WARNING
, "Invalid skipms value\n");
8292 if ((s
= ast_variable_retrieve(cfg
, "general", "maxlogins"))) {
8293 if (sscanf(s
, "%d", &x
) == 1) {
8296 ast_log(LOG_WARNING
, "Invalid max failed login attempts\n");
8300 /* Force new user to record name ? */
8301 if (!(astforcename
= ast_variable_retrieve(cfg
, "general", "forcename")))
8302 astforcename
= "no";
8303 ast_set2_flag((&globalflags
), ast_true(astforcename
), VM_FORCENAME
);
8305 /* Force new user to record greetings ? */
8306 if (!(astforcegreet
= ast_variable_retrieve(cfg
, "general", "forcegreetings")))
8307 astforcegreet
= "no";
8308 ast_set2_flag((&globalflags
), ast_true(astforcegreet
), VM_FORCEGREET
);
8310 if ((s
= ast_variable_retrieve(cfg
, "general", "cidinternalcontexts"))){
8311 if (option_debug
> 2)
8312 ast_log(LOG_DEBUG
,"VM_CID Internal context string: %s\n",s
);
8313 stringp
= ast_strdupa(s
);
8314 for (x
= 0 ; x
< MAX_NUM_CID_CONTEXTS
; x
++){
8315 if (!ast_strlen_zero(stringp
)) {
8316 q
= strsep(&stringp
,",");
8317 while ((*q
== ' ')||(*q
== '\t')) /* Eat white space between contexts */
8319 ast_copy_string(cidinternalcontexts
[x
], q
, sizeof(cidinternalcontexts
[x
]));
8320 if (option_debug
> 2)
8321 ast_log(LOG_DEBUG
,"VM_CID Internal context %d: %s\n", x
, cidinternalcontexts
[x
]);
8323 cidinternalcontexts
[x
][0] = '\0';
8327 if (!(astreview
= ast_variable_retrieve(cfg
, "general", "review"))){
8329 ast_log(LOG_DEBUG
,"VM Review Option disabled globally\n");
8332 ast_set2_flag((&globalflags
), ast_true(astreview
), VM_REVIEW
);
8334 /*Temperary greeting reminder */
8335 if (!(asttempgreetwarn
= ast_variable_retrieve(cfg
, "general", "tempgreetwarn"))) {
8337 ast_log(LOG_DEBUG
, "VM Temperary Greeting Reminder Option disabled globally\n");
8338 asttempgreetwarn
= "no";
8341 ast_log(LOG_DEBUG
, "VM Temperary Greeting Reminder Option enabled globally\n");
8343 ast_set2_flag((&globalflags
), ast_true(asttempgreetwarn
), VM_TEMPGREETWARN
);
8345 if (!(astcallop
= ast_variable_retrieve(cfg
, "general", "operator"))){
8347 ast_log(LOG_DEBUG
,"VM Operator break disabled globally\n");
8350 ast_set2_flag((&globalflags
), ast_true(astcallop
), VM_OPERATOR
);
8352 if (!(astsaycid
= ast_variable_retrieve(cfg
, "general", "saycid"))) {
8354 ast_log(LOG_DEBUG
,"VM CID Info before msg disabled globally\n");
8357 ast_set2_flag((&globalflags
), ast_true(astsaycid
), VM_SAYCID
);
8359 if (!(send_voicemail
= ast_variable_retrieve(cfg
,"general", "sendvoicemail"))){
8361 ast_log(LOG_DEBUG
,"Send Voicemail msg disabled globally\n");
8362 send_voicemail
= "no";
8364 ast_set2_flag((&globalflags
), ast_true(send_voicemail
), VM_SVMAIL
);
8366 if (!(asthearenv
= ast_variable_retrieve(cfg
, "general", "envelope"))) {
8368 ast_log(LOG_DEBUG
,"ENVELOPE before msg enabled globally\n");
8371 ast_set2_flag((&globalflags
), ast_true(asthearenv
), VM_ENVELOPE
);
8373 if (!(astsaydurationinfo
= ast_variable_retrieve(cfg
, "general", "sayduration"))) {
8375 ast_log(LOG_DEBUG
,"Duration info before msg enabled globally\n");
8376 astsaydurationinfo
= "yes";
8378 ast_set2_flag((&globalflags
), ast_true(astsaydurationinfo
), VM_SAYDURATION
);
8380 saydurationminfo
= 2;
8381 if ((astsaydurationminfo
= ast_variable_retrieve(cfg
, "general", "saydurationm"))) {
8382 if (sscanf(astsaydurationminfo
, "%d", &x
) == 1) {
8383 saydurationminfo
= x
;
8385 ast_log(LOG_WARNING
, "Invalid min duration for say duration\n");
8389 if (!(astskipcmd
= ast_variable_retrieve(cfg
, "general", "nextaftercmd"))) {
8391 ast_log(LOG_DEBUG
,"We are not going to skip to the next msg after save/delete\n");
8394 ast_set2_flag((&globalflags
), ast_true(astskipcmd
), VM_SKIPAFTERCMD
);
8396 if ((dialoutcxt
= ast_variable_retrieve(cfg
, "general", "dialout"))) {
8397 ast_copy_string(dialcontext
, dialoutcxt
, sizeof(dialcontext
));
8399 ast_log(LOG_DEBUG
, "found dialout context: %s\n", dialcontext
);
8401 dialcontext
[0] = '\0';
8404 if ((callbackcxt
= ast_variable_retrieve(cfg
, "general", "callback"))) {
8405 ast_copy_string(callcontext
, callbackcxt
, sizeof(callcontext
));
8407 ast_log(LOG_DEBUG
, "found callback context: %s\n", callcontext
);
8409 callcontext
[0] = '\0';
8412 if ((exitcxt
= ast_variable_retrieve(cfg
, "general", "exitcontext"))) {
8413 ast_copy_string(exitcontext
, exitcxt
, sizeof(exitcontext
));
8415 ast_log(LOG_DEBUG
, "found operator context: %s\n", exitcontext
);
8417 exitcontext
[0] = '\0';
8420 if (!(astdirfwd
= ast_variable_retrieve(cfg
, "general", "usedirectory")))
8422 ast_set2_flag((&globalflags
), ast_true(astdirfwd
), VM_DIRECFORWARD
);
8423 if ((ucfg
= ast_config_load("users.conf"))) {
8424 for (cat
= ast_category_browse(ucfg
, NULL
); cat
; cat
= ast_category_browse(ucfg
, cat
)) {
8425 if (!ast_true(ast_config_option(ucfg
, cat
, "hasvoicemail")))
8427 if ((cur
= find_or_create(userscontext
, cat
))) {
8428 populate_defaults(cur
);
8429 apply_options_full(cur
, ast_variable_browse(ucfg
, cat
));
8430 ast_copy_string(cur
->context
, userscontext
, sizeof(cur
->context
));
8433 ast_config_destroy(ucfg
);
8435 cat
= ast_category_browse(cfg
, NULL
);
8437 if (strcasecmp(cat
, "general")) {
8438 var
= ast_variable_browse(cfg
, cat
);
8439 if (strcasecmp(cat
, "zonemessages")) {
8440 /* Process mailboxes in this context */
8442 append_mailbox(cat
, var
->name
, var
->value
);
8446 /* Timezones in this context */
8449 if ((z
= ast_malloc(sizeof(*z
)))) {
8450 char *msg_format
, *timezone
;
8451 msg_format
= ast_strdupa(var
->value
);
8452 timezone
= strsep(&msg_format
, "|");
8454 ast_copy_string(z
->name
, var
->name
, sizeof(z
->name
));
8455 ast_copy_string(z
->timezone
, timezone
, sizeof(z
->timezone
));
8456 ast_copy_string(z
->msg_format
, msg_format
, sizeof(z
->msg_format
));
8457 AST_LIST_LOCK(&zones
);
8458 AST_LIST_INSERT_HEAD(&zones
, z
, list
);
8459 AST_LIST_UNLOCK(&zones
);
8461 ast_log(LOG_WARNING
, "Invalid timezone definition at line %d\n", var
->lineno
);
8466 AST_LIST_UNLOCK(&users
);
8467 ast_config_destroy(cfg
);
8474 cat
= ast_category_browse(cfg
, cat
);
8476 memset(fromstring
,0,sizeof(fromstring
));
8477 memset(pagerfromstring
,0,sizeof(pagerfromstring
));
8478 memset(emailtitle
,0,sizeof(emailtitle
));
8479 strcpy(charset
, "ISO-8859-1");
8486 emailsubject
= NULL
;
8494 pagersubject
= NULL
;
8496 if ((s
= ast_variable_retrieve(cfg
, "general", "pbxskip")))
8497 ast_set2_flag((&globalflags
), ast_true(s
), VM_PBXSKIP
);
8498 if ((s
= ast_variable_retrieve(cfg
, "general", "fromstring")))
8499 ast_copy_string(fromstring
,s
,sizeof(fromstring
));
8500 if ((s
= ast_variable_retrieve(cfg
, "general", "pagerfromstring")))
8501 ast_copy_string(pagerfromstring
,s
,sizeof(pagerfromstring
));
8502 if ((s
= ast_variable_retrieve(cfg
, "general", "charset")))
8503 ast_copy_string(charset
,s
,sizeof(charset
));
8504 if ((s
= ast_variable_retrieve(cfg
, "general", "adsifdn"))) {
8505 sscanf(s
, "%2x%2x%2x%2x", &tmpadsi
[0], &tmpadsi
[1], &tmpadsi
[2], &tmpadsi
[3]);
8506 for (x
= 0; x
< 4; x
++) {
8507 memcpy(&adsifdn
[x
], &tmpadsi
[x
], 1);
8510 if ((s
= ast_variable_retrieve(cfg
, "general", "adsisec"))) {
8511 sscanf(s
, "%2x%2x%2x%2x", &tmpadsi
[0], &tmpadsi
[1], &tmpadsi
[2], &tmpadsi
[3]);
8512 for (x
= 0; x
< 4; x
++) {
8513 memcpy(&adsisec
[x
], &tmpadsi
[x
], 1);
8516 if ((s
= ast_variable_retrieve(cfg
, "general", "adsiver")))
8520 if ((s
= ast_variable_retrieve(cfg
, "general", "emailtitle"))) {
8521 ast_log(LOG_NOTICE
, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
8522 ast_copy_string(emailtitle
,s
,sizeof(emailtitle
));
8524 if ((s
= ast_variable_retrieve(cfg
, "general", "emailsubject")))
8525 emailsubject
= ast_strdup(s
);
8526 if ((s
= ast_variable_retrieve(cfg
, "general", "emailbody"))) {
8527 char *tmpread
, *tmpwrite
;
8528 emailbody
= ast_strdup(s
);
8530 /* substitute strings \t and \n into the appropriate characters */
8531 tmpread
= tmpwrite
= emailbody
;
8532 while ((tmpwrite
= strchr(tmpread
,'\\'))) {
8533 switch (tmpwrite
[1]) {
8535 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
8539 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
8543 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
8547 ast_log(LOG_NOTICE
, "Substitution routine does not support this character: %c\n", tmpwrite
[1]);
8549 tmpread
= tmpwrite
+ 1;
8552 if ((s
= ast_variable_retrieve(cfg
, "general", "pagersubject")))
8553 pagersubject
= ast_strdup(s
);
8554 if ((s
= ast_variable_retrieve(cfg
, "general", "pagerbody"))) {
8555 char *tmpread
, *tmpwrite
;
8556 pagerbody
= ast_strdup(s
);
8558 /* substitute strings \t and \n into the appropriate characters */
8559 tmpread
= tmpwrite
= pagerbody
;
8560 while ((tmpwrite
= strchr(tmpread
, '\\'))) {
8561 switch (tmpwrite
[1]) {
8563 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
8567 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
8571 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
8575 ast_log(LOG_NOTICE
, "Substitution routine does not support this character: %c\n", tmpwrite
[1]);
8577 tmpread
= tmpwrite
+ 1;
8580 AST_LIST_UNLOCK(&users
);
8581 ast_config_destroy(cfg
);
8584 AST_LIST_UNLOCK(&users
);
8585 ast_log(LOG_WARNING
, "Failed to load configuration file.\n");
8590 static int reload(void)
8592 return(load_config());
8595 static int unload_module(void)
8599 res
= ast_unregister_application(app
);
8600 res
|= ast_unregister_application(app2
);
8601 res
|= ast_unregister_application(app3
);
8602 res
|= ast_unregister_application(app4
);
8603 ast_cli_unregister_multiple(cli_voicemail
, sizeof(cli_voicemail
) / sizeof(struct ast_cli_entry
));
8604 ast_uninstall_vm_functions();
8606 ast_module_user_hangup_all();
8611 static int load_module(void)
8614 char *adsi_loaded
= ast_module_helper("", "res_adsi.so", 0, 0, 0, 0);
8617 /* If embedded, res_adsi may be known as "res_adsi" not "res_adsi.so" */
8618 adsi_loaded
= ast_module_helper("", "res_adsi", 0, 0, 0, 0);
8619 ast_free(adsi_loaded
);
8621 ast_log(LOG_ERROR
, "app_voicemail.so depends upon res_adsi.so\n");
8622 return AST_MODULE_LOAD_DECLINE
;
8626 my_umask
= umask(0);
8628 res
= ast_register_application(app
, vm_exec
, synopsis_vm
, descrip_vm
);
8629 res
|= ast_register_application(app2
, vm_execmain
, synopsis_vmain
, descrip_vmain
);
8630 res
|= ast_register_application(app3
, vm_box_exists
, synopsis_vm_box_exists
, descrip_vm_box_exists
);
8631 res
|= ast_register_application(app4
, vmauthenticate
, synopsis_vmauthenticate
, descrip_vmauthenticate
);
8635 if ((res
=load_config())) {
8639 ast_cli_register_multiple(cli_voicemail
, sizeof(cli_voicemail
) / sizeof(struct ast_cli_entry
));
8641 /* compute the location of the voicemail spool directory */
8642 snprintf(VM_SPOOL_DIR
, sizeof(VM_SPOOL_DIR
), "%s/voicemail/", ast_config_AST_SPOOL_DIR
);
8644 ast_install_vm_functions(has_voicemail
, inboxcount
, messagecount
);
8649 static int dialout(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *num
, char *outgoing_context
)
8652 char destination
[80] = "";
8656 if (option_verbose
> 2)
8657 ast_verbose( VERBOSE_PREFIX_3
"Destination number will be entered manually\n");
8658 while (retries
< 3 && cmd
!= 't') {
8659 destination
[1] = '\0';
8660 destination
[0] = cmd
= ast_play_and_wait(chan
,"vm-enter-num-to-call");
8662 destination
[0] = cmd
= ast_play_and_wait(chan
, "vm-then-pound");
8664 destination
[0] = cmd
= ast_play_and_wait(chan
, "vm-star-cancel");
8666 cmd
= ast_waitfordigit(chan
, 6000);
8668 destination
[0] = cmd
;
8677 if (option_verbose
> 2)
8678 ast_verbose( VERBOSE_PREFIX_3
"User hit '*' to cancel outgoing call\n");
8681 if ((cmd
= ast_readstring(chan
,destination
+ strlen(destination
),sizeof(destination
)-1,6000,10000,"#")) < 0)
8692 if (option_verbose
> 2)
8693 ast_verbose( VERBOSE_PREFIX_3
"Destination number is CID number '%s'\n", num
);
8694 ast_copy_string(destination
, num
, sizeof(destination
));
8697 if (!ast_strlen_zero(destination
)) {
8698 if (destination
[strlen(destination
) -1 ] == '*')
8700 if (option_verbose
> 2)
8701 ast_verbose( VERBOSE_PREFIX_3
"Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination
, outgoing_context
, chan
->context
);
8702 ast_copy_string(chan
->exten
, destination
, sizeof(chan
->exten
));
8703 ast_copy_string(chan
->context
, outgoing_context
, sizeof(chan
->context
));
8710 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
)
8713 char filename
[PATH_MAX
];
8714 struct ast_config
*msg_cfg
= NULL
;
8715 const char *origtime
, *context
;
8716 char *cid
, *name
, *num
;
8720 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, msg
);
8722 /* Retrieve info from VM attribute file */
8723 make_file(vms
->fn2
, sizeof(vms
->fn2
), vms
->curdir
, vms
->curmsg
);
8724 snprintf(filename
,sizeof(filename
), "%s.txt", vms
->fn2
);
8725 RETRIEVE(vms
->curdir
, vms
->curmsg
, vmu
);
8726 msg_cfg
= ast_config_load(filename
);
8727 DISPOSE(vms
->curdir
, vms
->curmsg
);
8729 ast_log(LOG_WARNING
, "No message attribute file?!! (%s)\n", filename
);
8733 if (!(origtime
= ast_variable_retrieve(msg_cfg
, "message", "origtime"))) {
8734 ast_config_destroy(msg_cfg
);
8738 cid
= ast_strdupa(ast_variable_retrieve(msg_cfg
, "message", "callerid"));
8740 context
= ast_variable_retrieve(msg_cfg
, "message", "context");
8741 if (!strncasecmp("macro",context
,5)) /* Macro names in contexts are useless for our needs */
8742 context
= ast_variable_retrieve(msg_cfg
, "message","macrocontext");
8746 res
= play_message_datetime(chan
, vmu
, origtime
, filename
);
8748 res
= play_message_callerid(chan
, vms
, cid
, context
, 0);
8753 case 2: /* Call back */
8755 if (ast_strlen_zero(cid
))
8758 ast_callerid_parse(cid
, &name
, &num
);
8759 while ((res
> -1) && (res
!= 't')) {
8763 /* Dial the CID number */
8764 res
= dialout(chan
, vmu
, num
, vmu
->callback
);
8766 ast_config_destroy(msg_cfg
);
8775 /* Want to enter a different number, can only do this if there's a dialout context for this user */
8776 if (!ast_strlen_zero(vmu
->dialout
)) {
8777 res
= dialout(chan
, vmu
, NULL
, vmu
->dialout
);
8779 ast_config_destroy(msg_cfg
);
8783 if (option_verbose
> 2)
8784 ast_verbose( VERBOSE_PREFIX_3
"Caller can not specify callback number - no dialout context available\n");
8785 res
= ast_play_and_wait(chan
, "vm-sorry");
8787 ast_config_destroy(msg_cfg
);
8801 res
= ast_play_and_wait(chan
, "vm-sorry");
8806 if (option_verbose
> 2)
8807 ast_verbose( VERBOSE_PREFIX_3
"Confirm CID number '%s' is number to use for callback\n", num
);
8808 res
= ast_play_and_wait(chan
, "vm-num-i-have");
8810 res
= play_message_callerid(chan
, vms
, num
, vmu
->context
, 1);
8812 res
= ast_play_and_wait(chan
, "vm-tocallnum");
8813 /* Only prompt for a caller-specified number if there is a dialout context specified */
8814 if (!ast_strlen_zero(vmu
->dialout
)) {
8816 res
= ast_play_and_wait(chan
, "vm-calldiffnum");
8819 res
= ast_play_and_wait(chan
, "vm-nonumber");
8820 if (!ast_strlen_zero(vmu
->dialout
)) {
8822 res
= ast_play_and_wait(chan
, "vm-toenternumber");
8826 res
= ast_play_and_wait(chan
, "vm-star-cancel");
8828 res
= ast_waitfordigit(chan
, 6000);
8839 else if (res
== '*')
8845 /* Send reply directly to sender */
8846 if (ast_strlen_zero(cid
))
8849 ast_callerid_parse(cid
, &name
, &num
);
8851 if (option_verbose
> 2)
8852 ast_verbose(VERBOSE_PREFIX_3
"No CID number available, no reply sent\n");
8854 res
= ast_play_and_wait(chan
, "vm-nonumber");
8855 ast_config_destroy(msg_cfg
);
8858 struct ast_vm_user vmu2
;
8859 if (find_user(&vmu2
, vmu
->context
, num
)) {
8860 struct leave_vm_options leave_options
;
8861 char mailbox
[AST_MAX_EXTENSION
* 2 + 2];
8862 snprintf(mailbox
, sizeof(mailbox
), "%s@%s", num
, vmu
->context
);
8864 if (option_verbose
> 2)
8865 ast_verbose(VERBOSE_PREFIX_3
"Leaving voicemail for '%s' in context '%s'\n", num
, vmu
->context
);
8867 memset(&leave_options
, 0, sizeof(leave_options
));
8868 leave_options
.record_gain
= record_gain
;
8869 res
= leave_voicemail(chan
, mailbox
, &leave_options
);
8872 ast_config_destroy(msg_cfg
);
8875 /* Sender has no mailbox, can't reply */
8876 if (option_verbose
> 2)
8877 ast_verbose( VERBOSE_PREFIX_3
"No mailbox number '%s' in context '%s', no reply sent\n", num
, vmu
->context
);
8878 ast_play_and_wait(chan
, "vm-nobox");
8880 ast_config_destroy(msg_cfg
);
8889 #ifndef IMAP_STORAGE
8890 ast_config_destroy(msg_cfg
);
8893 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, msg
);
8894 vms
->heard
[msg
] = 1;
8895 res
= wait_file(chan
, vms
, vms
->fn
);
8901 static int play_record_review(struct ast_channel
*chan
, char *playfile
, char *recordfile
, int maxtime
, char *fmt
,
8902 int outsidecaller
, struct ast_vm_user
*vmu
, int *duration
, const char *unlockdir
,
8903 signed char record_gain
, struct vm_state
*vms
)
8905 /* Record message & let caller review or re-record it, or set options if applicable */
8908 int max_attempts
= 3;
8911 int message_exists
= 0;
8912 signed char zero_gain
= 0;
8913 char tempfile
[PATH_MAX
];
8914 char *acceptdtmf
= "#";
8915 char *canceldtmf
= "";
8917 /* Note that urgent and private are for flagging messages as such in the future */
8919 /* barf if no pointer passed to store duration in */
8920 if (duration
== NULL
) {
8921 ast_log(LOG_WARNING
, "Error play_record_review called without duration pointer\n");
8926 snprintf(tempfile
, sizeof(tempfile
), "%s.tmp", recordfile
);
8928 ast_copy_string(tempfile
, recordfile
, sizeof(tempfile
));
8930 cmd
= '3'; /* Want to start by recording */
8932 while ((cmd
>= 0) && (cmd
!= 't')) {
8935 if (!message_exists
) {
8936 /* In this case, 1 is to record a message */
8940 /* Otherwise 1 is to save the existing message */
8941 if (option_verbose
> 2)
8942 ast_verbose(VERBOSE_PREFIX_3
"Saving message as is\n");
8944 ast_filerename(tempfile
, recordfile
, NULL
);
8945 ast_stream_and_wait(chan
, "vm-msgsaved", chan
->language
, "");
8946 if (!outsidecaller
) {
8947 STORE(recordfile
, vmu
->mailbox
, vmu
->context
, -1, chan
, vmu
, fmt
, *duration
, vms
);
8948 DISPOSE(recordfile
, -1);
8955 if (option_verbose
> 2)
8956 ast_verbose(VERBOSE_PREFIX_3
"Reviewing the message\n");
8957 cmd
= ast_stream_and_wait(chan
, tempfile
, chan
->language
, AST_DIGIT_ANY
);
8962 if (recorded
== 1) {
8963 if (option_verbose
> 2)
8964 ast_verbose(VERBOSE_PREFIX_3
"Re-recording the message\n");
8966 if (option_verbose
> 2)
8967 ast_verbose(VERBOSE_PREFIX_3
"Recording the message\n");
8969 if (recorded
&& outsidecaller
) {
8970 cmd
= ast_play_and_wait(chan
, INTRO
);
8971 cmd
= ast_play_and_wait(chan
, "beep");
8974 /* 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 */
8976 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &record_gain
, sizeof(record_gain
), 0);
8977 if (ast_test_flag(vmu
, VM_OPERATOR
))
8979 cmd
= ast_play_and_record_full(chan
, playfile
, tempfile
, maxtime
, fmt
, duration
, silencethreshold
, maxsilence
, unlockdir
, acceptdtmf
, canceldtmf
);
8981 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &zero_gain
, sizeof(zero_gain
), 0);
8983 /* User has hung up, no options to give */
8984 if (!outsidecaller
) {
8985 /* user was recording a greeting and they hung up, so let's delete the recording. */
8986 ast_filedelete(tempfile
, NULL
);
8992 } else if (cmd
== '*') {
8996 else if (vmu
->review
&& (*duration
< 5)) {
8997 /* Message is too short */
8998 if (option_verbose
> 2)
8999 ast_verbose(VERBOSE_PREFIX_3
"Message too short\n");
9000 cmd
= ast_play_and_wait(chan
, "vm-tooshort");
9001 cmd
= ast_filedelete(tempfile
, NULL
);
9004 else if (vmu
->review
&& (cmd
== 2 && *duration
< (maxsilence
+ 3))) {
9005 /* Message is all silence */
9006 if (option_verbose
> 2)
9007 ast_verbose(VERBOSE_PREFIX_3
"Nothing recorded\n");
9008 cmd
= ast_filedelete(tempfile
, NULL
);
9009 cmd
= ast_play_and_wait(chan
, "vm-nothingrecorded");
9011 cmd
= ast_play_and_wait(chan
, "vm-speakup");
9016 /* If all is well, a message exists */
9029 cmd
= ast_play_and_wait(chan
, "vm-sorry");
9032 /* XXX Commented out for the moment because of the dangers of deleting
9033 a message while recording (can put the message numbers out of sync) */
9035 /* Cancel recording, delete message, offer to take another message*/
9036 cmd
= ast_play_and_wait(chan
, "vm-deleted");
9037 cmd
= ast_filedelete(tempfile
, NULL
);
9038 if (outsidecaller
) {
9039 res
= vm_exec(chan
, NULL
);
9046 if (!ast_test_flag(vmu
, VM_OPERATOR
)) {
9047 cmd
= ast_play_and_wait(chan
, "vm-sorry");
9050 if (message_exists
|| recorded
) {
9051 cmd
= ast_play_and_wait(chan
, "vm-saveoper");
9053 cmd
= ast_waitfordigit(chan
, 3000);
9055 ast_play_and_wait(chan
, "vm-msgsaved");
9058 ast_play_and_wait(chan
, "vm-deleted");
9059 DELETE(recordfile
, -1, recordfile
, vmu
);
9065 /* If the caller is an ouside caller, and the review option is enabled,
9066 allow them to review the message, but let the owner of the box review
9068 if (outsidecaller
&& !ast_test_flag(vmu
, VM_REVIEW
))
9070 if (message_exists
) {
9071 cmd
= ast_play_and_wait(chan
, "vm-review");
9074 cmd
= ast_play_and_wait(chan
, "vm-torerecord");
9076 cmd
= ast_waitfordigit(chan
, 600);
9079 if (!cmd
&& outsidecaller
&& ast_test_flag(vmu
, VM_OPERATOR
)) {
9080 cmd
= ast_play_and_wait(chan
, "vm-reachoper");
9082 cmd
= ast_waitfordigit(chan
, 600);
9086 cmd
= ast_play_and_wait(chan
, "vm-tocancelmsg");
9089 cmd
= ast_waitfordigit(chan
, 6000);
9093 if (attempts
> max_attempts
) {
9099 ast_play_and_wait(chan
, "vm-goodbye");
9105 /* This is a workaround so that menuselect displays a proper description
9106 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
9109 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, tdesc
,
9110 .load
= load_module
,
9111 .unload
= unload_module
,