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 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o">
33 <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
34 <depend>unixodbc</depend>
35 <defaultenabled>no</defaultenabled>
37 <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
38 <depend>imap_tk</depend>
40 <defaultenabled>no</defaultenabled>
47 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
57 #include <sys/types.h>
69 #include "asterisk/lock.h"
70 #include "asterisk/file.h"
71 #include "asterisk/logger.h"
72 #include "asterisk/channel.h"
73 #include "asterisk/pbx.h"
74 #include "asterisk/options.h"
75 #include "asterisk/config.h"
76 #include "asterisk/say.h"
77 #include "asterisk/module.h"
78 #include "asterisk/adsi.h"
79 #include "asterisk/app.h"
80 #include "asterisk/manager.h"
81 #include "asterisk/dsp.h"
82 #include "asterisk/localtime.h"
83 #include "asterisk/cli.h"
84 #include "asterisk/utils.h"
85 #include "asterisk/stringfields.h"
86 #include "asterisk/smdi.h"
88 #include "asterisk/res_odbc.h"
92 AST_MUTEX_DEFINE_STATIC(imaptemp_lock
);
93 static char imaptemp
[1024];
95 static char imapserver
[48];
96 static char imapport
[8];
97 static char imapflags
[128];
98 static char imapfolder
[64];
99 static char authuser
[32];
100 static char authpassword
[42];
102 static int expungeonhangup
= 1;
103 AST_MUTEX_DEFINE_STATIC(delimiter_lock
);
104 static char delimiter
= '\0';
109 static int init_mailstream (struct vm_state
*vms
, int box
);
110 static void write_file (char *filename
, char *buffer
, unsigned long len
);
111 /*static void status (MAILSTREAM *stream); */ /* No need for this. */
112 static void display_body (BODY
*body
, char *pfx
, long i
);
113 static char *get_header_by_tag(char *header
, char *tag
);
114 static void vm_imap_delete(int msgnum
, struct vm_state
*vms
);
115 static char *get_user_by_mailbox(char *mailbox
);
116 static struct vm_state
*get_vm_state_by_imapuser(char *user
, int interactive
);
117 static struct vm_state
*get_vm_state_by_mailbox(const char *mailbox
, int interactive
);
118 static void vmstate_insert(struct vm_state
*vms
);
119 static void vmstate_delete(struct vm_state
*vms
);
120 static void set_update(MAILSTREAM
* stream
);
121 static void init_vm_state(struct vm_state
*vms
);
122 static void check_msgArray(struct vm_state
*vms
);
123 static void copy_msgArray(struct vm_state
*dst
, struct vm_state
*src
);
124 static int save_body(BODY
*body
, struct vm_state
*vms
, char *section
, char *format
);
125 static int make_gsm_file(char *dest
, char *imapuser
, char *dir
, int num
);
126 static void get_mailbox_delimiter(MAILSTREAM
*stream
);
127 static void mm_parsequota (MAILSTREAM
*stream
, unsigned char *msg
, QUOTALIST
*pquota
);
128 static void imap_mailbox_name(char *spec
, struct vm_state
*vms
, int box
, int target
);
129 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
);
130 static int open_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
,int box
);
132 struct vm_state
*vms
;
133 struct vmstate
*next
;
135 AST_MUTEX_DEFINE_STATIC(vmstate_lock
);
136 static struct vmstate
*vmstates
= NULL
;
139 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
141 #define COMMAND_TIMEOUT 5000
142 /* Don't modify these here; set your umask at runtime instead */
143 #define VOICEMAIL_DIR_MODE 0777
144 #define VOICEMAIL_FILE_MODE 0666
145 #define CHUNKSIZE 65536
147 #define VOICEMAIL_CONFIG "voicemail.conf"
148 #define ASTERISK_USERNAME "asterisk"
150 /* Default mail command to mail voicemail. Change it with the
151 mailcmd= command in voicemail.conf */
152 #define SENDMAIL "/usr/sbin/sendmail -t"
154 #define INTRO "vm-intro"
157 #define MAXMSGLIMIT 9999
159 #define BASEMAXINLINE 256
160 #define BASELINELEN 72
161 #define BASEMAXINLINE 256
164 #define MAX_DATETIME_FORMAT 512
165 #define MAX_NUM_CID_CONTEXTS 10
167 #define VM_REVIEW (1 << 0)
168 #define VM_OPERATOR (1 << 1)
169 #define VM_SAYCID (1 << 2)
170 #define VM_SVMAIL (1 << 3)
171 #define VM_ENVELOPE (1 << 4)
172 #define VM_SAYDURATION (1 << 5)
173 #define VM_SKIPAFTERCMD (1 << 6)
174 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
175 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
176 #define VM_PBXSKIP (1 << 9)
177 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
178 #define VM_ATTACH (1 << 11)
179 #define VM_DELETE (1 << 12)
180 #define VM_ALLOCED (1 << 13)
181 #define VM_SEARCH (1 << 14)
182 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
183 #define ERROR_LOCK_PATH -100
187 OPT_SILENT
= (1 << 0),
188 OPT_BUSY_GREETING
= (1 << 1),
189 OPT_UNAVAIL_GREETING
= (1 << 2),
190 OPT_RECORDGAIN
= (1 << 3),
191 OPT_PREPEND_MAILBOX
= (1 << 4),
192 OPT_PRIORITY_JUMP
= (1 << 5),
193 OPT_AUTOPLAY
= (1 << 6),
197 OPT_ARG_RECORDGAIN
= 0,
198 OPT_ARG_PLAYFOLDER
= 1,
199 /* This *must* be the last value in this enum! */
200 OPT_ARG_ARRAY_SIZE
= 2,
203 AST_APP_OPTIONS(vm_app_options
, {
204 AST_APP_OPTION('s', OPT_SILENT
),
205 AST_APP_OPTION('b', OPT_BUSY_GREETING
),
206 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING
),
207 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN
, OPT_ARG_RECORDGAIN
),
208 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX
),
209 AST_APP_OPTION('j', OPT_PRIORITY_JUMP
),
210 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY
, OPT_ARG_PLAYFOLDER
),
213 static int load_config(void);
215 /*! \page vmlang Voicemail Language Syntaxes Supported
217 \par Syntaxes supported, not really language codes.
225 \arg \b pt - Portuguese
227 \arg \b no - Norwegian
230 German requires the following additional soundfile:
231 \arg \b 1F einE (feminine)
233 Spanish requires the following additional soundfile:
234 \arg \b 1M un (masculine)
236 Dutch, Portuguese & Spanish require the following additional soundfiles:
237 \arg \b vm-INBOXs singular of 'new'
238 \arg \b vm-Olds singular of 'old/heard/read'
241 \arg \b vm-INBOX nieuwe (nl)
242 \arg \b vm-Old oude (nl)
245 \arg \b vm-new-a 'new', feminine singular accusative
246 \arg \b vm-new-e 'new', feminine plural accusative
247 \arg \b vm-new-ych 'new', feminine plural genitive
248 \arg \b vm-old-a 'old', feminine singular accusative
249 \arg \b vm-old-e 'old', feminine plural accusative
250 \arg \b vm-old-ych 'old', feminine plural genitive
251 \arg \b digits/1-a 'one', not always same as 'digits/1'
252 \arg \b digits/2-ie 'two', not always same as 'digits/2'
255 \arg \b vm-nytt singular of 'new'
256 \arg \b vm-nya plural of 'new'
257 \arg \b vm-gammalt singular of 'old'
258 \arg \b vm-gamla plural of 'old'
259 \arg \b digits/ett 'one', not always same as 'digits/1'
262 \arg \b vm-ny singular of 'new'
263 \arg \b vm-nye plural of 'new'
264 \arg \b vm-gammel singular of 'old'
265 \arg \b vm-gamle plural of 'old'
273 Italian requires the following additional soundfile:
277 \arg \b vm-nuovi new plural
278 \arg \b vm-vecchio old
279 \arg \b vm-vecchi old plural
281 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
282 spelled among others when you have to change folder. For the above reasons, vm-INBOX
283 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
292 unsigned char iobuf
[BASEMAXINLINE
];
295 /*! Structure for linked list of users */
297 char context
[AST_MAX_CONTEXT
]; /*!< Voicemail context */
298 char mailbox
[AST_MAX_EXTENSION
]; /*!< Mailbox id, unique within vm context */
299 char password
[80]; /*!< Secret pin code, numbers only */
300 char fullname
[80]; /*!< Full name, for directory app */
301 char email
[80]; /*!< E-mail address */
302 char pager
[80]; /*!< E-mail address to pager (no attachment) */
303 char serveremail
[80]; /*!< From: Mail address */
304 char mailcmd
[160]; /*!< Configurable mail command */
305 char language
[MAX_LANGUAGE
]; /*!< Config: Language setting */
306 char zonetag
[80]; /*!< Time zone */
309 char uniqueid
[20]; /*!< Unique integer identifier */
311 char attachfmt
[20]; /*!< Attachment format */
312 unsigned int flags
; /*!< VM_ flags */
314 int maxmsg
; /*!< Maximum number of msgs per folder for this mailbox */
316 char imapuser
[80]; /* IMAP server login */
317 char imappassword
[80]; /* IMAP server password if authpassword not defined */
319 double volgain
; /*!< Volume gain for voicemails sent via email */
320 AST_LIST_ENTRY(ast_vm_user
) list
;
324 AST_LIST_ENTRY(vm_zone
) list
;
327 char msg_format
[512];
346 int updated
; /* decremented on each mail check until 1 -allows delay */
348 MAILSTREAM
*mailstream
;
350 char imapuser
[80]; /* IMAP server login */
352 unsigned int quota_limit
;
353 unsigned int quota_usage
;
354 struct vm_state
*persist_vms
;
357 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
);
358 static int dialout(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *num
, char *outgoing_context
);
359 static int play_record_review(struct ast_channel
*chan
, char *playfile
, char *recordfile
, int maxtime
,
360 char *fmt
, int outsidecaller
, struct ast_vm_user
*vmu
, int *duration
, const char *unlockdir
,
361 signed char record_gain
);
362 static int vm_tempgreeting(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
);
363 static int vm_play_folder_name(struct ast_channel
*chan
, char *mbox
);
364 static int notify_new_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, int msgnum
, long duration
, char *fmt
, char *cidnum
, char *cidname
);
365 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
);
366 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
367 static int __has_voicemail(const char *context
, const char *mailbox
, const char *folder
, int shortcircuit
);
369 static void apply_options(struct ast_vm_user
*vmu
, const char *options
);
372 static char odbc_database
[80];
373 static char odbc_table
[80];
374 #define RETRIEVE(a,b) retrieve_file(a,b)
375 #define DISPOSE(a,b) remove_file(a,b)
376 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
377 #define EXISTS(a,b,c,d) (message_exists(a,b))
378 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
379 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
380 #define DELETE(a,b,c) (delete_file(a,b))
383 #define RETRIEVE(a,b)
385 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
386 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
387 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
388 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
389 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
390 #define DELETE(a,b,c) (vm_delete(c))
392 #define RETRIEVE(a,b)
394 #define STORE(a,b,c,d,e,f,g,h,i)
395 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
396 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
397 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
398 #define DELETE(a,b,c) (vm_delete(c))
402 static char VM_SPOOL_DIR
[PATH_MAX
];
404 static char ext_pass_cmd
[128];
407 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
409 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
411 #define tdesc "Comedian Mail (Voicemail System)"
414 static char userscontext
[AST_MAX_EXTENSION
] = "default";
416 static char *addesc
= "Comedian Mail";
418 static char *synopsis_vm
=
419 "Leave a Voicemail message";
421 static char *descrip_vm
=
422 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
423 "application allows the calling party to leave a message for the specified\n"
424 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
425 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
426 "specified mailbox does not exist.\n"
427 " The Voicemail application will exit if any of the following DTMF digits are\n"
429 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
430 " * - Jump to the 'a' extension in the current dialplan context.\n"
431 " This application will set the following channel variable upon completion:\n"
432 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
433 " application. The possible values are:\n"
434 " SUCCESS | USEREXIT | FAILED\n\n"
436 " b - Play the 'busy' greeting to the calling party.\n"
437 " g(#) - Use the specified amount of gain when recording the voicemail\n"
438 " message. The units are whole-number decibels (dB).\n"
439 " s - Skip the playback of instructions for leaving a message to the\n"
441 " u - Play the 'unavailble greeting.\n"
442 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
445 static char *synopsis_vmain
=
446 "Check Voicemail messages";
448 static char *descrip_vmain
=
449 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
450 "calling party to check voicemail messages. A specific mailbox, and optional\n"
451 "corresponding context, may be specified. If a mailbox is not provided, the\n"
452 "calling party will be prompted to enter one. If a context is not specified,\n"
453 "the 'default' context will be used.\n\n"
455 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
456 " is entered by the caller.\n"
457 " g(#) - Use the specified amount of gain when recording a voicemail\n"
458 " message. The units are whole-number decibels (dB).\n"
459 " s - Skip checking the passcode for the mailbox.\n"
460 " a(#) - Skip folder prompt and go directly to folder specified.\n"
461 " Defaults to INBOX\n";
463 static char *synopsis_vm_box_exists
=
464 "Check to see if Voicemail mailbox exists";
466 static char *descrip_vm_box_exists
=
467 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
468 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
470 " This application will set the following channel variable upon completion:\n"
471 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
472 " MailboxExists application. Possible values include:\n"
473 " SUCCESS | FAILED\n\n"
475 " j - Jump to priority n+101 if the mailbox is found.\n";
477 static char *synopsis_vmauthenticate
=
478 "Authenticate with Voicemail passwords";
480 static char *descrip_vmauthenticate
=
481 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
482 "same way as the Authenticate application, but the passwords are taken from\n"
484 " If the mailbox is specified, only that mailbox's password will be considered\n"
485 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
486 "be set with the authenticated mailbox.\n\n"
488 " s - Skip playing the initial prompts.\n";
490 /* Leave a message */
491 static char *app
= "VoiceMail";
493 /* Check mail, control, etc */
494 static char *app2
= "VoiceMailMain";
496 static char *app3
= "MailboxExists";
497 static char *app4
= "VMAuthenticate";
499 static AST_LIST_HEAD_STATIC(users
, ast_vm_user
);
500 static AST_LIST_HEAD_STATIC(zones
, vm_zone
);
501 static int maxsilence
;
503 static int silencethreshold
= 128;
504 static char serveremail
[80];
505 static char mailcmd
[160]; /* Configurable mail cmd */
506 static char externnotify
[160];
507 static struct ast_smdi_interface
*smdi_iface
= NULL
;
508 static char vmfmts
[80];
509 static double volgain
;
510 static int vmminmessage
;
511 static int vmmaxmessage
;
514 static int maxlogins
;
516 static struct ast_flags globalflags
= {0};
518 static int saydurationminfo
;
520 static char dialcontext
[AST_MAX_CONTEXT
];
521 static char callcontext
[AST_MAX_CONTEXT
];
522 static char exitcontext
[AST_MAX_CONTEXT
];
524 static char cidinternalcontexts
[MAX_NUM_CID_CONTEXTS
][64];
527 static char *emailbody
= NULL
;
528 static char *emailsubject
= NULL
;
529 static char *pagerbody
= NULL
;
530 static char *pagersubject
= NULL
;
531 static char fromstring
[100];
532 static char pagerfromstring
[100];
533 static char emailtitle
[100];
534 static char charset
[32] = "ISO-8859-1";
536 static unsigned char adsifdn
[4] = "\x00\x00\x00\x0F";
537 static unsigned char adsisec
[4] = "\x9B\xDB\xF7\xAC";
538 static int adsiver
= 1;
539 static char emaildateformat
[32] = "%A, %B %d, %Y at %r";
542 static void populate_defaults(struct ast_vm_user
*vmu
)
544 ast_copy_flags(vmu
, (&globalflags
), AST_FLAGS_ALL
);
545 if (saydurationminfo
)
546 vmu
->saydurationm
= saydurationminfo
;
548 ast_copy_string(vmu
->callback
, callcontext
, sizeof(vmu
->callback
));
550 ast_copy_string(vmu
->dialout
, dialcontext
, sizeof(vmu
->dialout
));
552 ast_copy_string(vmu
->exit
, exitcontext
, sizeof(vmu
->exit
));
554 vmu
->maxmsg
= maxmsg
;
555 vmu
->volgain
= volgain
;
558 static void apply_option(struct ast_vm_user
*vmu
, const char *var
, const char *value
)
561 if (!strcasecmp(var
, "attach")) {
562 ast_set2_flag(vmu
, ast_true(value
), VM_ATTACH
);
563 } else if (!strcasecmp(var
, "attachfmt")) {
564 ast_copy_string(vmu
->attachfmt
, value
, sizeof(vmu
->attachfmt
));
565 } else if (!strcasecmp(var
, "serveremail")) {
566 ast_copy_string(vmu
->serveremail
, value
, sizeof(vmu
->serveremail
));
567 } else if (!strcasecmp(var
, "language")) {
568 ast_copy_string(vmu
->language
, value
, sizeof(vmu
->language
));
569 } else if (!strcasecmp(var
, "tz")) {
570 ast_copy_string(vmu
->zonetag
, value
, sizeof(vmu
->zonetag
));
572 } else if (!strcasecmp(var
, "imapuser")) {
573 ast_copy_string(vmu
->imapuser
, value
, sizeof(vmu
->imapuser
));
574 } else if (!strcasecmp(var
, "imappassword")) {
575 ast_copy_string(vmu
->imappassword
, value
, sizeof(vmu
->imappassword
));
577 } else if (!strcasecmp(var
, "delete") || !strcasecmp(var
, "deletevoicemail")) {
578 ast_set2_flag(vmu
, ast_true(value
), VM_DELETE
);
579 } else if (!strcasecmp(var
, "saycid")){
580 ast_set2_flag(vmu
, ast_true(value
), VM_SAYCID
);
581 } else if (!strcasecmp(var
,"sendvoicemail")){
582 ast_set2_flag(vmu
, ast_true(value
), VM_SVMAIL
);
583 } else if (!strcasecmp(var
, "review")){
584 ast_set2_flag(vmu
, ast_true(value
), VM_REVIEW
);
585 } else if (!strcasecmp(var
, "tempgreetwarn")){
586 ast_set2_flag(vmu
, ast_true(value
), VM_TEMPGREETWARN
);
587 } else if (!strcasecmp(var
, "operator")){
588 ast_set2_flag(vmu
, ast_true(value
), VM_OPERATOR
);
589 } else if (!strcasecmp(var
, "envelope")){
590 ast_set2_flag(vmu
, ast_true(value
), VM_ENVELOPE
);
591 } else if (!strcasecmp(var
, "sayduration")){
592 ast_set2_flag(vmu
, ast_true(value
), VM_SAYDURATION
);
593 } else if (!strcasecmp(var
, "saydurationm")){
594 if (sscanf(value
, "%d", &x
) == 1) {
595 vmu
->saydurationm
= x
;
597 ast_log(LOG_WARNING
, "Invalid min duration for say duration\n");
599 } else if (!strcasecmp(var
, "forcename")){
600 ast_set2_flag(vmu
, ast_true(value
), VM_FORCENAME
);
601 } else if (!strcasecmp(var
, "forcegreetings")){
602 ast_set2_flag(vmu
, ast_true(value
), VM_FORCEGREET
);
603 } else if (!strcasecmp(var
, "callback")) {
604 ast_copy_string(vmu
->callback
, value
, sizeof(vmu
->callback
));
605 } else if (!strcasecmp(var
, "dialout")) {
606 ast_copy_string(vmu
->dialout
, value
, sizeof(vmu
->dialout
));
607 } else if (!strcasecmp(var
, "exitcontext")) {
608 ast_copy_string(vmu
->exit
, value
, sizeof(vmu
->exit
));
609 } else if (!strcasecmp(var
, "maxmsg")) {
610 vmu
->maxmsg
= atoi(value
);
611 if (vmu
->maxmsg
<= 0) {
612 ast_log(LOG_WARNING
, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value
, MAXMSG
);
613 vmu
->maxmsg
= MAXMSG
;
614 } else if (vmu
->maxmsg
> MAXMSGLIMIT
) {
615 ast_log(LOG_WARNING
, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT
, value
);
616 vmu
->maxmsg
= MAXMSGLIMIT
;
618 } else if (!strcasecmp(var
, "volgain")) {
619 sscanf(value
, "%lf", &vmu
->volgain
);
620 } else if (!strcasecmp(var
, "options")) {
621 apply_options(vmu
, value
);
625 static int change_password_realtime(struct ast_vm_user
*vmu
, const char *password
)
628 if (!ast_strlen_zero(vmu
->uniqueid
)) {
629 res
= ast_update_realtime("voicemail", "uniqueid", vmu
->uniqueid
, "password", password
, NULL
);
631 ast_copy_string(vmu
->password
, password
, sizeof(vmu
->password
));
641 static void apply_options(struct ast_vm_user
*vmu
, const char *options
)
642 { /* Destructively Parse options and apply */
646 stringp
= ast_strdupa(options
);
647 while ((s
= strsep(&stringp
, "|"))) {
649 if ((var
= strsep(&value
, "=")) && value
) {
650 apply_option(vmu
, var
, value
);
655 static void apply_options_full(struct ast_vm_user
*retval
, struct ast_variable
*var
)
657 struct ast_variable
*tmp
;
660 if (!strcasecmp(tmp
->name
, "password")) {
661 ast_copy_string(retval
->password
, tmp
->value
, sizeof(retval
->password
));
662 } else if (!strcasecmp(tmp
->name
, "uniqueid")) {
663 ast_copy_string(retval
->uniqueid
, tmp
->value
, sizeof(retval
->uniqueid
));
664 } else if (!strcasecmp(tmp
->name
, "pager")) {
665 ast_copy_string(retval
->pager
, tmp
->value
, sizeof(retval
->pager
));
666 } else if (!strcasecmp(tmp
->name
, "email")) {
667 ast_copy_string(retval
->email
, tmp
->value
, sizeof(retval
->email
));
668 } else if (!strcasecmp(tmp
->name
, "fullname")) {
669 ast_copy_string(retval
->fullname
, tmp
->value
, sizeof(retval
->fullname
));
670 } else if (!strcasecmp(tmp
->name
, "context")) {
671 ast_copy_string(retval
->context
, tmp
->value
, sizeof(retval
->context
));
673 apply_option(retval
, tmp
->name
, tmp
->value
);
678 static struct ast_vm_user
*find_user_realtime(struct ast_vm_user
*ivm
, const char *context
, const char *mailbox
)
680 struct ast_variable
*var
;
681 struct ast_vm_user
*retval
;
683 if ((retval
= (ivm
? ivm
: ast_calloc(1, sizeof(*retval
))))) {
685 ast_set_flag(retval
, VM_ALLOCED
);
687 memset(retval
, 0, sizeof(*retval
));
689 ast_copy_string(retval
->mailbox
, mailbox
, sizeof(retval
->mailbox
));
690 populate_defaults(retval
);
691 if (!context
&& ast_test_flag((&globalflags
), VM_SEARCH
))
692 var
= ast_load_realtime("voicemail", "mailbox", mailbox
, NULL
);
694 var
= ast_load_realtime("voicemail", "mailbox", mailbox
, "context", context
, NULL
);
696 apply_options_full(retval
, var
);
697 ast_variables_destroy(var
);
707 static struct ast_vm_user
*find_user(struct ast_vm_user
*ivm
, const char *context
, const char *mailbox
)
709 /* This function could be made to generate one from a database, too */
710 struct ast_vm_user
*vmu
=NULL
, *cur
;
711 AST_LIST_LOCK(&users
);
713 if (!context
&& !ast_test_flag((&globalflags
), VM_SEARCH
))
716 AST_LIST_TRAVERSE(&users
, cur
, list
) {
717 if (ast_test_flag((&globalflags
), VM_SEARCH
) && !strcasecmp(mailbox
, cur
->mailbox
))
719 if (context
&& (!strcasecmp(context
, cur
->context
)) && (!strcasecmp(mailbox
, cur
->mailbox
)))
723 /* Make a copy, so that on a reload, we have no race */
724 if ((vmu
= (ivm
? ivm
: ast_malloc(sizeof(*vmu
))))) {
725 memcpy(vmu
, cur
, sizeof(*vmu
));
726 ast_set2_flag(vmu
, !ivm
, VM_ALLOCED
);
727 AST_LIST_NEXT(vmu
, list
) = NULL
;
730 vmu
= find_user_realtime(ivm
, context
, mailbox
);
731 AST_LIST_UNLOCK(&users
);
735 static int reset_user_pw(const char *context
, const char *mailbox
, const char *newpass
)
737 /* This function could be made to generate one from a database, too */
738 struct ast_vm_user
*cur
;
740 AST_LIST_LOCK(&users
);
741 AST_LIST_TRAVERSE(&users
, cur
, list
) {
742 if ((!context
|| !strcasecmp(context
, cur
->context
)) &&
743 (!strcasecmp(mailbox
, cur
->mailbox
)))
747 ast_copy_string(cur
->password
, newpass
, sizeof(cur
->password
));
750 AST_LIST_UNLOCK(&users
);
754 static void vm_change_password(struct ast_vm_user
*vmu
, const char *newpassword
)
756 /* There's probably a better way of doing this. */
757 /* That's why I've put the password change in a separate function. */
758 /* This could also be done with a database function */
765 char currcontext
[256] ="";
766 char tmpin
[PATH_MAX
];
767 char tmpout
[PATH_MAX
];
770 if (!change_password_realtime(vmu
, newpassword
))
773 snprintf(tmpin
, sizeof(tmpin
), "%s/voicemail.conf", ast_config_AST_CONFIG_DIR
);
774 snprintf(tmpout
, sizeof(tmpout
), "%s/voicemail.conf.new", ast_config_AST_CONFIG_DIR
);
775 configin
= fopen(tmpin
,"r");
777 configout
= fopen(tmpout
,"w+");
780 if (!configin
|| !configout
) {
784 ast_log(LOG_WARNING
, "Warning: Unable to open '%s' for reading: %s\n", tmpin
, strerror(errno
));
788 ast_log(LOG_WARNING
, "Warning: Unable to open '%s' for writing: %s\n", tmpout
, strerror(errno
));
792 while (!feof(configin
)) {
793 char *user
= NULL
, *pass
= NULL
, *rest
= NULL
, *comment
= NULL
, *tmpctx
= NULL
, *tmpctxend
= NULL
;
795 /* Read in the line */
796 if (fgets(inbuf
, sizeof(inbuf
), configin
) == NULL
)
800 /* Make a backup of it */
801 ast_copy_string(orig
, inbuf
, sizeof(orig
));
804 Read the file line by line, split each line into a comment and command section
805 only parse the command portion of the line
807 if (inbuf
[strlen(inbuf
) - 1] == '\n')
808 inbuf
[strlen(inbuf
) - 1] = '\0';
810 if ((comment
= strchr(inbuf
, ';')))
811 *comment
++ = '\0'; /* Now inbuf is terminated just before the comment */
813 if (ast_strlen_zero(inbuf
)) {
814 fprintf(configout
, "%s", orig
);
818 /* Check for a context, first '[' to first ']' */
819 if ((tmpctx
= strchr(inbuf
, '['))) {
820 tmpctxend
= strchr(tmpctx
, ']');
823 ast_copy_string(currcontext
, tmpctx
+ 1, tmpctxend
- tmpctx
);
824 fprintf(configout
, "%s", orig
);
829 /* This isn't a context line, check for MBX => PSWD... */
831 if ((pass
= strchr(user
, '='))) {
832 /* We have a line in the form of aaaaa=aaaaaa */
835 user
= ast_strip(user
);
840 pass
= ast_skip_blanks(pass
);
843 Since no whitespace allowed in fields, or more correctly white space
844 inside the fields is there for a purpose, we can just terminate pass
845 at the comma or EOL whichever comes first.
847 if ((rest
= strchr(pass
, ',')))
853 /* Compare user, pass AND context */
854 if (!ast_strlen_zero(user
) && !strcmp(user
, vmu
->mailbox
) &&
855 !ast_strlen_zero(pass
) && !strcmp(pass
, vmu
->password
) &&
856 !strcasecmp(currcontext
, vmu
->context
)) {
857 /* This is the line */
859 fprintf(configout
, "%s => %s,%s", user
, newpassword
, rest
);
861 fprintf(configout
, "%s => %s", user
, newpassword
);
863 /* If there was a comment on the line print it out */
865 fprintf(configout
, ";%s\n", comment
);
867 fprintf(configout
, "\n");
870 /* Put it back like it was */
871 fprintf(configout
, "%s", orig
);
877 stat(tmpin
, &statbuf
);
878 chmod(tmpout
, statbuf
.st_mode
);
879 chown(tmpout
, statbuf
.st_uid
, statbuf
.st_gid
);
881 rename(tmpout
, tmpin
);
882 reset_user_pw(vmu
->context
, vmu
->mailbox
, newpassword
);
883 ast_copy_string(vmu
->password
, newpassword
, sizeof(vmu
->password
));
886 static void vm_change_password_shell(struct ast_vm_user
*vmu
, char *newpassword
)
889 snprintf(buf
,255,"%s %s %s %s",ext_pass_cmd
,vmu
->context
,vmu
->mailbox
,newpassword
);
890 if (!ast_safe_system(buf
))
891 ast_copy_string(vmu
->password
, newpassword
, sizeof(vmu
->password
));
894 static int make_dir(char *dest
, int len
, const char *context
, const char *ext
, const char *folder
)
896 return snprintf(dest
, len
, "%s%s/%s/%s", VM_SPOOL_DIR
, context
, ext
, folder
);
900 static int make_gsm_file(char *dest
, char *imapuser
, char *dir
, int num
)
902 if (mkdir(dir
, 01777) && (errno
!= EEXIST
)) {
903 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dir
, strerror(errno
));
904 return sprintf(dest
, "%s/msg%04d", dir
, num
);
906 /* return sprintf(dest, "%s/s/msg%04d", dir, imapuser, num); */
907 return sprintf(dest
, "%s/msg%04d", dir
, num
);
910 static void vm_imap_delete(int msgnum
, struct vm_state
*vms
)
912 unsigned long messageNum
= 0;
915 /* find real message number based on msgnum */
916 /* this may be an index into vms->msgArray based on the msgnum. */
918 messageNum
= vms
->msgArray
[msgnum
];
919 if (messageNum
== 0) {
920 ast_log(LOG_WARNING
, "msgnum %d, mailbox message %lu is zero.\n",msgnum
,messageNum
);
924 ast_log(LOG_DEBUG
, "deleting msgnum %d, which is mailbox message %lu\n",msgnum
,messageNum
);
926 sprintf (arg
,"%lu",messageNum
);
927 mail_setflag (vms
->mailstream
,arg
,"\\DELETED");
931 static int make_file(char *dest
, int len
, char *dir
, int num
)
933 return snprintf(dest
, len
, "%s/msg%04d", dir
, num
);
936 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
937 * \param dest String. base directory.
938 * \param len Length of dest.
939 * \param context String. Ignored if is null or empty string.
940 * \param ext String. Ignored if is null or empty string.
941 * \param folder String. Ignored if is null or empty string.
942 * \return 0 on failure, 1 on success.
944 static int create_dirpath(char *dest
, int len
, const char *context
, const char *ext
, const char *folder
)
946 mode_t mode
= VOICEMAIL_DIR_MODE
;
948 if (!ast_strlen_zero(context
)) {
949 make_dir(dest
, len
, context
, "", "");
950 if (mkdir(dest
, mode
) && errno
!= EEXIST
) {
951 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dest
, strerror(errno
));
955 if (!ast_strlen_zero(ext
)) {
956 make_dir(dest
, len
, context
, ext
, "");
957 if (mkdir(dest
, mode
) && errno
!= EEXIST
) {
958 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dest
, strerror(errno
));
962 if (!ast_strlen_zero(folder
)) {
963 make_dir(dest
, len
, context
, ext
, folder
);
964 if (mkdir(dest
, mode
) && errno
!= EEXIST
) {
965 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dest
, strerror(errno
));
972 /* only return failure if ast_lock_path returns 'timeout',
973 not if the path does not exist or any other reason
975 static int vm_lock_path(const char *path
)
977 switch (ast_lock_path(path
)) {
978 case AST_LOCK_TIMEOUT
:
987 static int retrieve_file(char *dir
, int msgnum
)
994 SQLSMALLINT colcount
=0;
1001 SQLSMALLINT datatype
;
1002 SQLSMALLINT decimaldigits
;
1003 SQLSMALLINT nullable
;
1012 struct odbc_obj
*obj
;
1013 obj
= ast_odbc_request_obj(odbc_database
, 0);
1015 ast_copy_string(fmt
, vmfmts
, sizeof(fmt
));
1016 c
= strchr(fmt
, '|');
1019 if (!strcasecmp(fmt
, "wav49"))
1021 snprintf(msgnums
, sizeof(msgnums
),"%d", msgnum
);
1023 make_file(fn
, sizeof(fn
), dir
, msgnum
);
1025 ast_copy_string(fn
, dir
, sizeof(fn
));
1026 snprintf(full_fn
, sizeof(full_fn
), "%s.txt", fn
);
1027 f
= fopen(full_fn
, "w+");
1028 snprintf(full_fn
, sizeof(full_fn
), "%s.%s", fn
, fmt
);
1029 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
1030 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1031 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
1032 ast_odbc_release_obj(obj
);
1035 snprintf(sql
, sizeof(sql
), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table
);
1036 res
= SQLPrepare(stmt
, (unsigned char *)sql
, SQL_NTS
);
1037 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1038 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", sql
);
1039 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1040 ast_odbc_release_obj(obj
);
1043 SQLBindParameter(stmt
, 1, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(dir
), 0, (void *)dir
, 0, NULL
);
1044 SQLBindParameter(stmt
, 2, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(msgnums
), 0, (void *)msgnums
, 0, NULL
);
1045 res
= ast_odbc_smart_execute(obj
, stmt
);
1046 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1047 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1048 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1049 ast_odbc_release_obj(obj
);
1052 res
= SQLFetch(stmt
);
1053 if (res
== SQL_NO_DATA
) {
1054 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1055 ast_odbc_release_obj(obj
);
1058 else if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1059 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
1060 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1061 ast_odbc_release_obj(obj
);
1064 fd
= open(full_fn
, O_RDWR
| O_CREAT
| O_TRUNC
, 0770);
1066 ast_log(LOG_WARNING
, "Failed to write '%s': %s\n", full_fn
, strerror(errno
));
1067 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1068 ast_odbc_release_obj(obj
);
1071 res
= SQLNumResultCols(stmt
, &colcount
);
1072 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1073 ast_log(LOG_WARNING
, "SQL Column Count error!\n[%s]\n\n", sql
);
1074 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1075 ast_odbc_release_obj(obj
);
1079 fprintf(f
, "[message]\n");
1080 for (x
=0;x
<colcount
;x
++) {
1082 collen
= sizeof(coltitle
);
1083 res
= SQLDescribeCol(stmt
, x
+ 1, (unsigned char *)coltitle
, sizeof(coltitle
), &collen
,
1084 &datatype
, &colsize
, &decimaldigits
, &nullable
);
1085 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1086 ast_log(LOG_WARNING
, "SQL Describe Column error!\n[%s]\n\n", sql
);
1087 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1088 ast_odbc_release_obj(obj
);
1091 if (!strcasecmp(coltitle
, "recording")) {
1093 res
= SQLGetData(stmt
, x
+ 1, SQL_BINARY
, NULL
, 0, &colsize2
);
1097 lseek(fd
, fdlen
- 1, SEEK_SET
);
1098 if (write(fd
, tmp
, 1) != 1) {
1103 /* Read out in small chunks */
1104 for (offset
= 0; offset
< colsize2
; offset
+= CHUNKSIZE
) {
1105 /* +1 because SQLGetData likes null-terminating binary data */
1106 if ((fdm
= mmap(NULL
, CHUNKSIZE
+ 1, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, offset
)) == (void *)-1) {
1107 ast_log(LOG_WARNING
, "Could not mmap the output file: %s (%d)\n", strerror(errno
), errno
);
1108 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
1109 ast_odbc_release_obj(obj
);
1112 res
= SQLGetData(stmt
, x
+ 1, SQL_BINARY
, fdm
, CHUNKSIZE
+ 1, NULL
);
1114 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1115 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
1117 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
1118 ast_odbc_release_obj(obj
);
1123 truncate(full_fn
, fdlen
);
1126 res
= SQLGetData(stmt
, x
+ 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
1127 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1128 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
1129 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1130 ast_odbc_release_obj(obj
);
1133 if (strcasecmp(coltitle
, "msgnum") && strcasecmp(coltitle
, "dir") && f
)
1134 fprintf(f
, "%s=%s\n", coltitle
, rowdata
);
1137 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1138 ast_odbc_release_obj(obj
);
1140 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1149 static int remove_file(char *dir
, int msgnum
)
1156 snprintf(msgnums
, sizeof(msgnums
), "%d", msgnum
);
1157 make_file(fn
, sizeof(fn
), dir
, msgnum
);
1159 ast_copy_string(fn
, dir
, sizeof(fn
));
1160 ast_filedelete(fn
, NULL
);
1161 snprintf(full_fn
, sizeof(full_fn
), "%s.txt", fn
);
1166 static int last_message_index(struct ast_vm_user
*vmu
, char *dir
)
1174 struct odbc_obj
*obj
;
1175 obj
= ast_odbc_request_obj(odbc_database
, 0);
1177 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
1178 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1179 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
1180 ast_odbc_release_obj(obj
);
1183 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table
);
1184 res
= SQLPrepare(stmt
, (unsigned char *)sql
, SQL_NTS
);
1185 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1186 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", sql
);
1187 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1188 ast_odbc_release_obj(obj
);
1191 SQLBindParameter(stmt
, 1, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(dir
), 0, (void *)dir
, 0, NULL
);
1192 res
= ast_odbc_smart_execute(obj
, stmt
);
1193 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1194 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1195 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1196 ast_odbc_release_obj(obj
);
1199 res
= SQLFetch(stmt
);
1200 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1201 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
1202 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1203 ast_odbc_release_obj(obj
);
1206 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
1207 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1208 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
1209 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1210 ast_odbc_release_obj(obj
);
1213 if (sscanf(rowdata
, "%d", &x
) != 1)
1214 ast_log(LOG_WARNING
, "Failed to read message count!\n");
1215 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1216 ast_odbc_release_obj(obj
);
1218 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1223 static int message_exists(char *dir
, int msgnum
)
1232 struct odbc_obj
*obj
;
1233 obj
= ast_odbc_request_obj(odbc_database
, 0);
1235 snprintf(msgnums
, sizeof(msgnums
), "%d", msgnum
);
1236 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
1237 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1238 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
1239 ast_odbc_release_obj(obj
);
1242 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table
);
1243 res
= SQLPrepare(stmt
, (unsigned char *)sql
, SQL_NTS
);
1244 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1245 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", sql
);
1246 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1247 ast_odbc_release_obj(obj
);
1250 SQLBindParameter(stmt
, 1, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(dir
), 0, (void *)dir
, 0, NULL
);
1251 SQLBindParameter(stmt
, 2, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(msgnums
), 0, (void *)msgnums
, 0, NULL
);
1252 res
= ast_odbc_smart_execute(obj
, stmt
);
1253 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1254 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1255 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1256 ast_odbc_release_obj(obj
);
1259 res
= SQLFetch(stmt
);
1260 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1261 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
1262 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1263 ast_odbc_release_obj(obj
);
1266 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
1267 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1268 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
1269 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1270 ast_odbc_release_obj(obj
);
1273 if (sscanf(rowdata
, "%d", &x
) != 1)
1274 ast_log(LOG_WARNING
, "Failed to read message count!\n");
1275 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1276 ast_odbc_release_obj(obj
);
1278 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1283 static int count_messages(struct ast_vm_user
*vmu
, char *dir
)
1285 return last_message_index(vmu
, dir
) + 1;
1288 static void delete_file(char *sdir
, int smsg
)
1295 struct odbc_obj
*obj
;
1296 obj
= ast_odbc_request_obj(odbc_database
, 0);
1298 snprintf(msgnums
, sizeof(msgnums
), "%d", smsg
);
1299 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
1300 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1301 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
1302 ast_odbc_release_obj(obj
);
1305 snprintf(sql
, sizeof(sql
), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table
);
1306 res
= SQLPrepare(stmt
, (unsigned char *)sql
, SQL_NTS
);
1307 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1308 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", sql
);
1309 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1310 ast_odbc_release_obj(obj
);
1313 SQLBindParameter(stmt
, 1, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(sdir
), 0, (void *)sdir
, 0, NULL
);
1314 SQLBindParameter(stmt
, 2, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(msgnums
), 0, (void *)msgnums
, 0, NULL
);
1315 res
= ast_odbc_smart_execute(obj
, stmt
);
1316 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1317 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1318 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1319 ast_odbc_release_obj(obj
);
1322 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1323 ast_odbc_release_obj(obj
);
1325 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1330 static void copy_file(char *sdir
, int smsg
, char *ddir
, int dmsg
, char *dmailboxuser
, char *dmailboxcontext
)
1337 struct odbc_obj
*obj
;
1339 delete_file(ddir
, dmsg
);
1340 obj
= ast_odbc_request_obj(odbc_database
, 0);
1342 snprintf(msgnums
, sizeof(msgnums
), "%d", smsg
);
1343 snprintf(msgnumd
, sizeof(msgnumd
), "%d", dmsg
);
1344 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
1345 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1346 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
1347 ast_odbc_release_obj(obj
);
1350 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
);
1351 res
= SQLPrepare(stmt
, (unsigned char *)sql
, SQL_NTS
);
1352 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1353 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", sql
);
1354 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1355 ast_odbc_release_obj(obj
);
1358 SQLBindParameter(stmt
, 1, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(ddir
), 0, (void *)ddir
, 0, NULL
);
1359 SQLBindParameter(stmt
, 2, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(msgnumd
), 0, (void *)msgnumd
, 0, NULL
);
1360 SQLBindParameter(stmt
, 3, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(dmailboxuser
), 0, (void *)dmailboxuser
, 0, NULL
);
1361 SQLBindParameter(stmt
, 4, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(dmailboxcontext
), 0, (void *)dmailboxcontext
, 0, NULL
);
1362 SQLBindParameter(stmt
, 5, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(sdir
), 0, (void *)sdir
, 0, NULL
);
1363 SQLBindParameter(stmt
, 6, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(msgnums
), 0, (void *)msgnums
, 0, NULL
);
1364 res
= ast_odbc_smart_execute(obj
, stmt
);
1365 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1366 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql
);
1367 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1368 ast_odbc_release_obj(obj
);
1371 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1372 ast_odbc_release_obj(obj
);
1374 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1379 static int store_file(char *dir
, char *mailboxuser
, char *mailboxcontext
, int msgnum
)
1394 const char *context
="", *macrocontext
="", *callerid
="", *origtime
="", *duration
="";
1395 const char *category
= "";
1396 struct ast_config
*cfg
=NULL
;
1397 struct odbc_obj
*obj
;
1399 delete_file(dir
, msgnum
);
1400 obj
= ast_odbc_request_obj(odbc_database
, 0);
1402 ast_copy_string(fmt
, vmfmts
, sizeof(fmt
));
1403 c
= strchr(fmt
, '|');
1406 if (!strcasecmp(fmt
, "wav49"))
1408 snprintf(msgnums
, sizeof(msgnums
),"%d", msgnum
);
1410 make_file(fn
, sizeof(fn
), dir
, msgnum
);
1412 ast_copy_string(fn
, dir
, sizeof(fn
));
1413 snprintf(full_fn
, sizeof(full_fn
), "%s.txt", fn
);
1414 cfg
= ast_config_load(full_fn
);
1415 snprintf(full_fn
, sizeof(full_fn
), "%s.%s", fn
, fmt
);
1416 fd
= open(full_fn
, O_RDWR
);
1418 ast_log(LOG_WARNING
, "Open of sound file '%s' failed: %s\n", full_fn
, strerror(errno
));
1419 ast_odbc_release_obj(obj
);
1423 context
= ast_variable_retrieve(cfg
, "message", "context");
1424 if (!context
) context
= "";
1425 macrocontext
= ast_variable_retrieve(cfg
, "message", "macrocontext");
1426 if (!macrocontext
) macrocontext
= "";
1427 callerid
= ast_variable_retrieve(cfg
, "message", "callerid");
1428 if (!callerid
) callerid
= "";
1429 origtime
= ast_variable_retrieve(cfg
, "message", "origtime");
1430 if (!origtime
) origtime
= "";
1431 duration
= ast_variable_retrieve(cfg
, "message", "duration");
1432 if (!duration
) duration
= "";
1433 category
= ast_variable_retrieve(cfg
, "message", "category");
1434 if (!category
) category
= "";
1436 fdlen
= lseek(fd
, 0, SEEK_END
);
1437 lseek(fd
, 0, SEEK_SET
);
1438 printf("Length is %zd\n", fdlen
);
1439 fdm
= mmap(NULL
, fdlen
, PROT_READ
| PROT_WRITE
, MAP_SHARED
,fd
, 0);
1441 ast_log(LOG_WARNING
, "Memory map failed!\n");
1442 ast_odbc_release_obj(obj
);
1445 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
1446 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1447 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
1448 ast_odbc_release_obj(obj
);
1451 if (!ast_strlen_zero(category
))
1452 snprintf(sql
, sizeof(sql
), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table
);
1454 snprintf(sql
, sizeof(sql
), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table
);
1455 res
= SQLPrepare(stmt
, (unsigned char *)sql
, SQL_NTS
);
1456 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1457 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", sql
);
1458 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1459 ast_odbc_release_obj(obj
);
1462 len
= fdlen
; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1463 SQLBindParameter(stmt
, 1, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(dir
), 0, (void *)dir
, 0, NULL
);
1464 SQLBindParameter(stmt
, 2, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(msgnums
), 0, (void *)msgnums
, 0, NULL
);
1465 SQLBindParameter(stmt
, 3, SQL_PARAM_INPUT
, SQL_C_BINARY
, SQL_LONGVARBINARY
, fdlen
, 0, (void *)fdm
, fdlen
, &len
);
1466 SQLBindParameter(stmt
, 4, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(context
), 0, (void *)context
, 0, NULL
);
1467 SQLBindParameter(stmt
, 5, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(macrocontext
), 0, (void *)macrocontext
, 0, NULL
);
1468 SQLBindParameter(stmt
, 6, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(callerid
), 0, (void *)callerid
, 0, NULL
);
1469 SQLBindParameter(stmt
, 7, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(origtime
), 0, (void *)origtime
, 0, NULL
);
1470 SQLBindParameter(stmt
, 8, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(duration
), 0, (void *)duration
, 0, NULL
);
1471 SQLBindParameter(stmt
, 9, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(mailboxuser
), 0, (void *)mailboxuser
, 0, NULL
);
1472 SQLBindParameter(stmt
, 10, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(mailboxcontext
), 0, (void *)mailboxcontext
, 0, NULL
);
1473 if (!ast_strlen_zero(category
))
1474 SQLBindParameter(stmt
, 11, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(category
), 0, (void *)category
, 0, NULL
);
1475 res
= ast_odbc_smart_execute(obj
, stmt
);
1476 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1477 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1478 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1479 ast_odbc_release_obj(obj
);
1482 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1483 ast_odbc_release_obj(obj
);
1485 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1488 ast_config_destroy(cfg
);
1496 static void rename_file(char *sdir
, int smsg
, char *mailboxuser
, char *mailboxcontext
, char *ddir
, int dmsg
)
1503 struct odbc_obj
*obj
;
1505 delete_file(ddir
, dmsg
);
1506 obj
= ast_odbc_request_obj(odbc_database
, 0);
1508 snprintf(msgnums
, sizeof(msgnums
), "%d", smsg
);
1509 snprintf(msgnumd
, sizeof(msgnumd
), "%d", dmsg
);
1510 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
1511 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1512 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
1513 ast_odbc_release_obj(obj
);
1516 snprintf(sql
, sizeof(sql
), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table
);
1517 res
= SQLPrepare(stmt
, (unsigned char *)sql
, SQL_NTS
);
1518 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1519 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", sql
);
1520 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1521 ast_odbc_release_obj(obj
);
1524 SQLBindParameter(stmt
, 1, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(ddir
), 0, (void *)ddir
, 0, NULL
);
1525 SQLBindParameter(stmt
, 2, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(msgnumd
), 0, (void *)msgnumd
, 0, NULL
);
1526 SQLBindParameter(stmt
, 3, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(mailboxuser
), 0, (void *)mailboxuser
, 0, NULL
);
1527 SQLBindParameter(stmt
, 4, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(mailboxcontext
), 0, (void *)mailboxcontext
, 0, NULL
);
1528 SQLBindParameter(stmt
, 5, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(sdir
), 0, (void *)sdir
, 0, NULL
);
1529 SQLBindParameter(stmt
, 6, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(msgnums
), 0, (void *)msgnums
, 0, NULL
);
1530 res
= ast_odbc_smart_execute(obj
, stmt
);
1531 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1532 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1533 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1534 ast_odbc_release_obj(obj
);
1537 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1538 ast_odbc_release_obj(obj
);
1540 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1546 #ifndef IMAP_STORAGE
1547 static int count_messages(struct ast_vm_user
*vmu
, char *dir
)
1549 /* Find all .txt files - even if they are not in sequence from 0000 */
1553 struct dirent
*vment
= NULL
;
1555 if (vm_lock_path(dir
))
1556 return ERROR_LOCK_PATH
;
1558 if ((vmdir
= opendir(dir
))) {
1559 while ((vment
= readdir(vmdir
))) {
1560 if (strlen(vment
->d_name
) > 7 && !strncmp(vment
->d_name
+ 7, ".txt", 4))
1565 ast_unlock_path(dir
);
1570 static void rename_file(char *sfn
, char *dfn
)
1574 ast_filerename(sfn
,dfn
,NULL
);
1575 snprintf(stxt
, sizeof(stxt
), "%s.txt", sfn
);
1576 snprintf(dtxt
, sizeof(dtxt
), "%s.txt", dfn
);
1580 static int copy(char *infile
, char *outfile
)
1588 #ifdef HARDLINK_WHEN_POSSIBLE
1589 /* Hard link if possible; saves disk space & is faster */
1590 if (link(infile
, outfile
)) {
1592 if ((ifd
= open(infile
, O_RDONLY
)) < 0) {
1593 ast_log(LOG_WARNING
, "Unable to open %s in read-only mode\n", infile
);
1596 if ((ofd
= open(outfile
, O_WRONLY
| O_TRUNC
| O_CREAT
, VOICEMAIL_FILE_MODE
)) < 0) {
1597 ast_log(LOG_WARNING
, "Unable to open %s in write-only mode\n", outfile
);
1602 len
= read(ifd
, buf
, sizeof(buf
));
1604 ast_log(LOG_WARNING
, "Read failed on %s: %s\n", infile
, strerror(errno
));
1610 res
= write(ofd
, buf
, len
);
1611 if (errno
== ENOMEM
|| errno
== ENOSPC
|| res
!= len
) {
1612 ast_log(LOG_WARNING
, "Write failed on %s (%d of %d): %s\n", outfile
, res
, len
, strerror(errno
));
1622 #ifdef HARDLINK_WHEN_POSSIBLE
1624 /* Hard link succeeded */
1630 static void copy_file(char *frompath
, char *topath
)
1632 char frompath2
[256],topath2
[256];
1633 ast_filecopy(frompath
, topath
, NULL
);
1634 snprintf(frompath2
, sizeof(frompath2
), "%s.txt", frompath
);
1635 snprintf(topath2
, sizeof(topath2
), "%s.txt", topath
);
1636 copy(frompath2
, topath2
);
1640 * A negative return value indicates an error.
1642 #if (!defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
1643 static int last_message_index(struct ast_vm_user
*vmu
, char *dir
)
1648 if (vm_lock_path(dir
))
1649 return ERROR_LOCK_PATH
;
1651 for (x
= 0; x
< vmu
->maxmsg
; x
++) {
1652 make_file(fn
, sizeof(fn
), dir
, x
);
1653 if (ast_fileexists(fn
, NULL
, NULL
) < 1)
1656 ast_unlock_path(dir
);
1662 static int vm_delete(char *file
)
1667 txtsize
= (strlen(file
) + 5)*sizeof(char);
1668 txt
= alloca(txtsize
);
1669 /* Sprintf here would safe because we alloca'd exactly the right length,
1670 * but trying to eliminate all sprintf's anyhow
1672 snprintf(txt
, txtsize
, "%s.txt", file
);
1674 return ast_filedelete(file
, NULL
);
1679 static int inbuf(struct baseio
*bio
, FILE *fi
)
1686 if ((l
= fread(bio
->iobuf
,1,BASEMAXINLINE
,fi
)) <= 0) {
1700 static int inchar(struct baseio
*bio
, FILE *fi
)
1702 if (bio
->iocp
>=bio
->iolen
) {
1703 if (!inbuf(bio
, fi
))
1707 return bio
->iobuf
[bio
->iocp
++];
1710 static int ochar(struct baseio
*bio
, int c
, FILE *so
)
1712 if (bio
->linelength
>=BASELINELEN
) {
1713 if (fputs(eol
,so
)==EOF
)
1719 if (putc(((unsigned char)c
),so
)==EOF
)
1727 static int base_encode(char *filename
, FILE *so
)
1729 unsigned char dtable
[BASEMAXINLINE
];
1734 memset(&bio
, 0, sizeof(bio
));
1735 bio
.iocp
= BASEMAXINLINE
;
1737 if (!(fi
= fopen(filename
, "rb"))) {
1738 ast_log(LOG_WARNING
, "Failed to open log file: %s: %s\n", filename
, strerror(errno
));
1742 for (i
= 0;i
<9;i
++) {
1745 dtable
[26+i
]= 'a'+i
;
1746 dtable
[26+i
+9]= 'j'+i
;
1748 for (i
= 0;i
<8;i
++) {
1749 dtable
[i
+18]= 'S'+i
;
1750 dtable
[26+i
+18]= 's'+i
;
1752 for (i
= 0;i
<10;i
++) {
1753 dtable
[52+i
]= '0'+i
;
1759 unsigned char igroup
[3],ogroup
[4];
1762 igroup
[0]= igroup
[1]= igroup
[2]= 0;
1764 for (n
= 0;n
<3;n
++) {
1765 if ((c
= inchar(&bio
, fi
)) == EOF
) {
1770 igroup
[n
]= (unsigned char)c
;
1774 ogroup
[0]= dtable
[igroup
[0]>>2];
1775 ogroup
[1]= dtable
[((igroup
[0]&3)<<4)|(igroup
[1]>>4)];
1776 ogroup
[2]= dtable
[((igroup
[1]&0xF)<<2)|(igroup
[2]>>6)];
1777 ogroup
[3]= dtable
[igroup
[2]&0x3F];
1787 ochar(&bio
, ogroup
[i
], so
);
1791 if (fputs(eol
,so
)==EOF
)
1799 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
)
1802 /* Prepare variables for substition in email body and subject */
1803 pbx_builtin_setvar_helper(ast
, "VM_NAME", vmu
->fullname
);
1804 pbx_builtin_setvar_helper(ast
, "VM_DUR", dur
);
1805 snprintf(passdata
, passdatasize
, "%d", msgnum
);
1806 pbx_builtin_setvar_helper(ast
, "VM_MSGNUM", passdata
);
1807 pbx_builtin_setvar_helper(ast
, "VM_CONTEXT", context
);
1808 pbx_builtin_setvar_helper(ast
, "VM_MAILBOX", mailbox
);
1809 pbx_builtin_setvar_helper(ast
, "VM_CALLERID", ast_callerid_merge(callerid
, sizeof(callerid
), cidname
, cidnum
, "Unknown Caller"));
1810 pbx_builtin_setvar_helper(ast
, "VM_CIDNAME", (cidname
? cidname
: "an unknown caller"));
1811 pbx_builtin_setvar_helper(ast
, "VM_CIDNUM", (cidnum
? cidnum
: "an unknown caller"));
1812 pbx_builtin_setvar_helper(ast
, "VM_DATE", date
);
1813 pbx_builtin_setvar_helper(ast
, "VM_CATEGORY", category
? ast_strdupa(category
) : "no category");
1817 * fill in *tm for current time according to the proper timezone, if any.
1818 * Return tm so it can be used as a function argument.
1820 static const struct tm
*vmu_tm(const struct ast_vm_user
*vmu
, struct tm
*tm
)
1822 const struct vm_zone
*z
= NULL
;
1823 time_t t
= time(NULL
);
1825 /* Does this user have a timezone specified? */
1826 if (!ast_strlen_zero(vmu
->zonetag
)) {
1827 /* Find the zone in the list */
1828 AST_LIST_LOCK(&zones
);
1829 AST_LIST_TRAVERSE(&zones
, z
, list
) {
1830 if (!strcmp(z
->name
, vmu
->zonetag
))
1833 AST_LIST_UNLOCK(&zones
);
1835 ast_localtime(&t
, tm
, z
? z
->timezone
: NULL
);
1839 /* same as mkstemp, but return a FILE * */
1840 static FILE *vm_mkftemp(char *template)
1843 int pfd
= mkstemp(template);
1845 p
= fdopen(pfd
, "w+");
1854 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
)
1857 char host
[MAXHOSTNAMELEN
] = "";
1865 gethostname(host
, sizeof(host
) - 1);
1866 if (strchr(srcemail
, '@'))
1867 ast_copy_string(who
, srcemail
, sizeof(who
));
1869 snprintf(who
, sizeof(who
), "%s@%s", srcemail
, host
);
1871 snprintf(dur
, sizeof(dur
), "%d:%02d", duration
/ 60, duration
% 60);
1872 strftime(date
, sizeof(date
), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu
, &tm
));
1873 fprintf(p
, "Date: %s\r\n", date
);
1875 /* Set date format for voicemail mail */
1876 strftime(date
, sizeof(date
), emaildateformat
, &tm
);
1879 struct ast_channel
*ast
;
1880 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, 0))) {
1882 int vmlen
= strlen(fromstring
)*3 + 200;
1883 if ((passdata
= alloca(vmlen
))) {
1884 memset(passdata
, 0, vmlen
);
1885 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
1886 pbx_substitute_variables_helper(ast
, fromstring
, passdata
, vmlen
);
1887 fprintf(p
, "From: %s <%s>\r\n", passdata
, who
);
1889 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
1890 ast_channel_free(ast
);
1892 ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
1894 fprintf(p
, "From: Asterisk PBX <%s>\r\n", who
);
1895 fprintf(p
, "To: %s <%s>\r\n", vmu
->fullname
, vmu
->email
);
1897 struct ast_channel
*ast
;
1898 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, 0))) {
1900 int vmlen
= strlen(emailsubject
)*3 + 200;
1901 if ((passdata
= alloca(vmlen
))) {
1902 memset(passdata
, 0, vmlen
);
1903 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
1904 pbx_substitute_variables_helper(ast
, emailsubject
, passdata
, vmlen
);
1905 fprintf(p
, "Subject: %s\r\n", passdata
);
1907 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
1908 ast_channel_free(ast
);
1910 ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
1911 } else if (*emailtitle
) {
1912 fprintf(p
, emailtitle
, msgnum
+ 1, mailbox
) ;
1914 } else if (ast_test_flag((&globalflags
), VM_PBXSKIP
))
1915 fprintf(p
, "Subject: New message %d in mailbox %s\r\n", msgnum
+ 1, mailbox
);
1917 fprintf(p
, "Subject: [PBX]: New message %d in mailbox %s\r\n", msgnum
+ 1, mailbox
);
1918 fprintf(p
, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>\r\n", msgnum
, (unsigned int)ast_random(), mailbox
, getpid(), host
);
1920 /* additional information needed for IMAP searching */
1921 fprintf(p
, "X-Asterisk-VM-Message-Num: %d\r\n", msgnum
+ 1);
1922 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s\r\n", ext); */
1923 fprintf(p
, "X-Asterisk-VM-Server-Name: %s\r\n", fromstring
);
1924 fprintf(p
, "X-Asterisk-VM-Context: %s\r\n", context
);
1925 fprintf(p
, "X-Asterisk-VM-Extension: %s\r\n", mailbox
);
1926 fprintf(p
, "X-Asterisk-VM-Priority: %d\r\n", chan
->priority
);
1927 fprintf(p
, "X-Asterisk-VM-Caller-channel: %s\r\n", chan
->name
);
1928 fprintf(p
, "X-Asterisk-VM-Caller-ID-Num: %s\r\n", cidnum
);
1929 fprintf(p
, "X-Asterisk-VM-Caller-ID-Name: %s\r\n", cidname
);
1930 fprintf(p
, "X-Asterisk-VM-Duration: %d\r\n", duration
);
1931 if (!ast_strlen_zero(category
))
1932 fprintf(p
, "X-Asterisk-VM-Category: %s\r\n", category
);
1933 fprintf(p
, "X-Asterisk-VM-Orig-date: %s\r\n", date
);
1934 fprintf(p
, "X-Asterisk-VM-Orig-time: %ld\r\n", (long)time(NULL
));
1936 if (!ast_strlen_zero(cidnum
))
1937 fprintf(p
, "X-Asterisk-CallerID: %s\r\n", cidnum
);
1938 if (!ast_strlen_zero(cidname
))
1939 fprintf(p
, "X-Asterisk-CallerIDName: %s\r\n", cidname
);
1940 fprintf(p
, "MIME-Version: 1.0\r\n");
1941 if (attach_user_voicemail
) {
1942 /* Something unique. */
1943 snprintf(bound
, sizeof(bound
), "voicemail_%d%s%d%d", msgnum
, mailbox
, getpid(), (unsigned int)ast_random());
1945 fprintf(p
, "Content-Type: multipart/mixed; boundary=\"%s\"\r\n\r\n\r\n", bound
);
1947 fprintf(p
, "--%s\r\n", bound
);
1949 fprintf(p
, "Content-Type: text/plain; charset=%s\r\nContent-Transfer-Encoding: 8bit\r\n\r\n", charset
);
1951 struct ast_channel
*ast
;
1952 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, 0))) {
1954 int vmlen
= strlen(emailbody
)*3 + 200;
1955 if ((passdata
= alloca(vmlen
))) {
1956 memset(passdata
, 0, vmlen
);
1957 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
1958 pbx_substitute_variables_helper(ast
, emailbody
, passdata
, vmlen
);
1959 fprintf(p
, "%s\r\n", passdata
);
1961 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
1962 ast_channel_free(ast
);
1964 ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
1966 fprintf(p
, "Dear %s:\r\n\r\n\tJust wanted to let you know you were just left a %s long message (number %d)\r\n"
1968 "in mailbox %s from %s, on %s so you might\r\n"
1969 "want to check it when you get a chance. Thanks!\r\n\r\n\t\t\t\t--Asterisk\r\n\r\n", vmu
->fullname
,
1970 dur
, msgnum
+ 1, mailbox
, (cidname
? cidname
: (cidnum
? cidnum
: "an unknown caller")), date
);
1972 if (attach_user_voicemail
) {
1973 /* Eww. We want formats to tell us their own MIME type */
1974 char *ctype
= (!strcasecmp(format
, "ogg")) ? "application/" : "audio/x-";
1975 char tmpdir
[256], newtmp
[256];
1978 create_dirpath(tmpdir
, sizeof(tmpdir
), vmu
->context
, vmu
->mailbox
, "tmp");
1979 snprintf(newtmp
, sizeof(newtmp
), "%s/XXXXXX", tmpdir
);
1980 tmpfd
= mkstemp(newtmp
);
1981 ast_log(LOG_DEBUG
, "newtmp: %s\n", newtmp
);
1982 if (vmu
->volgain
< -.001 || vmu
->volgain
> .001) {
1983 snprintf(tmpcmd
, sizeof(tmpcmd
), "sox -v %.4f %s.%s %s.%s", vmu
->volgain
, attach
, format
, newtmp
, format
);
1984 ast_safe_system(tmpcmd
);
1986 ast_log(LOG_DEBUG
, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach
, format
, vmu
->volgain
, mailbox
);
1988 fprintf(p
, "--%s\r\n", bound
);
1989 fprintf(p
, "Content-Type: %s%s; name=\"msg%04d.%s\"\r\n", ctype
, format
, msgnum
, format
);
1990 fprintf(p
, "Content-Transfer-Encoding: base64\r\n");
1991 fprintf(p
, "Content-Description: Voicemail sound attachment.\r\n");
1992 fprintf(p
, "Content-Disposition: attachment; filename=\"msg%04d.%s\"\r\n\r\n", msgnum
, format
);
1993 snprintf(fname
, sizeof(fname
), "%s.%s", attach
, format
);
1994 base_encode(fname
, p
);
1995 /* only attach if necessary */
1996 if (imap
&& strcmp(format
, "gsm")) {
1997 fprintf(p
, "--%s\r\n", bound
);
1998 fprintf(p
, "Content-Type: audio/x-gsm; name=\"msg%04d.%s\"\r\n", msgnum
, format
);
1999 fprintf(p
, "Content-Transfer-Encoding: base64\r\n");
2000 fprintf(p
, "Content-Description: Voicemail sound attachment.\r\n");
2001 fprintf(p
, "Content-Disposition: attachment; filename=\"msg%04d.gsm\"\r\n\r\n", msgnum
);
2002 snprintf(fname
, sizeof(fname
), "%s.gsm", attach
);
2003 base_encode(fname
, p
);
2005 fprintf(p
, "\r\n\r\n--%s--\r\n.\r\n", bound
);
2011 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
)
2014 char tmp
[80] = "/tmp/astmail-XXXXXX";
2017 if (vmu
&& ast_strlen_zero(vmu
->email
)) {
2018 ast_log(LOG_WARNING
, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu
->mailbox
);
2021 if (!strcmp(format
, "wav49"))
2023 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
));
2024 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2026 if ((p
= vm_mkftemp(tmp
)) == NULL
) {
2027 ast_log(LOG_WARNING
, "Unable to launch '%s' (can't create temporary file)\n", mailcmd
);
2030 make_email_file(p
, srcemail
, vmu
, msgnum
, context
, mailbox
, cidnum
, cidname
, attach
, format
, duration
, attach_user_voicemail
, chan
, category
, 0);
2032 snprintf(tmp2
, sizeof(tmp2
), "( %s < %s ; rm -f %s ) &", mailcmd
, tmp
, tmp
);
2033 ast_safe_system(tmp2
);
2034 ast_log(LOG_DEBUG
, "Sent mail to %s with command '%s'\n", vmu
->email
, mailcmd
);
2039 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
)
2042 char host
[MAXHOSTNAMELEN
]="";
2045 char tmp
[80] = "/tmp/astmail-XXXXXX";
2050 if ((p
= vm_mkftemp(tmp
)) == NULL
) {
2051 ast_log(LOG_WARNING
, "Unable to launch '%s' (can't create temporary file)\n", mailcmd
);
2054 gethostname(host
, sizeof(host
)-1);
2055 if (strchr(srcemail
, '@'))
2056 ast_copy_string(who
, srcemail
, sizeof(who
));
2058 snprintf(who
, sizeof(who
), "%s@%s", srcemail
, host
);
2060 snprintf(dur
, sizeof(dur
), "%d:%02d", duration
/ 60, duration
% 60);
2061 strftime(date
, sizeof(date
), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu
, &tm
));
2062 fprintf(p
, "Date: %s\n", date
);
2064 if (*pagerfromstring
) {
2065 struct ast_channel
*ast
;
2066 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, 0))) {
2068 int vmlen
= strlen(fromstring
)*3 + 200;
2069 if ((passdata
= alloca(vmlen
))) {
2070 memset(passdata
, 0, vmlen
);
2071 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
2072 pbx_substitute_variables_helper(ast
, pagerfromstring
, passdata
, vmlen
);
2073 fprintf(p
, "From: %s <%s>\n", passdata
, who
);
2075 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
2076 ast_channel_free(ast
);
2077 } else ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
2079 fprintf(p
, "From: Asterisk PBX <%s>\n", who
);
2080 fprintf(p
, "To: %s\n", pager
);
2082 struct ast_channel
*ast
;
2083 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, 0))) {
2085 int vmlen
= strlen(pagersubject
) * 3 + 200;
2086 if ((passdata
= alloca(vmlen
))) {
2087 memset(passdata
, 0, vmlen
);
2088 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
2089 pbx_substitute_variables_helper(ast
, pagersubject
, passdata
, vmlen
);
2090 fprintf(p
, "Subject: %s\n\n", passdata
);
2091 } else ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
2092 ast_channel_free(ast
);
2093 } else ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
2095 fprintf(p
, "Subject: New VM\n\n");
2096 strftime(date
, sizeof(date
), "%A, %B %d, %Y at %r", &tm
);
2098 struct ast_channel
*ast
;
2099 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, 0))) {
2101 int vmlen
= strlen(pagerbody
)*3 + 200;
2102 if ((passdata
= alloca(vmlen
))) {
2103 memset(passdata
, 0, vmlen
);
2104 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
2105 pbx_substitute_variables_helper(ast
, pagerbody
, passdata
, vmlen
);
2106 fprintf(p
, "%s\n", passdata
);
2107 } else ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
2108 ast_channel_free(ast
);
2109 } else ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
2111 fprintf(p
, "New %s long msg in box %s\n"
2112 "from %s, on %s", dur
, mailbox
, (cidname
? cidname
: (cidnum
? cidnum
: "unknown")), date
);
2115 snprintf(tmp2
, sizeof(tmp2
), "( %s < %s ; rm -f %s ) &", mailcmd
, tmp
, tmp
);
2116 ast_safe_system(tmp2
);
2117 ast_log(LOG_DEBUG
, "Sent page to %s with command '%s'\n", pager
, mailcmd
);
2122 static int get_date(char *s
, int len
)
2127 localtime_r(&t
,&tm
);
2128 return strftime(s
, len
, "%a %b %e %r %Z %Y", &tm
);
2131 static int invent_message(struct ast_channel
*chan
, char *context
, char *ext
, int busy
, char *ecodes
)
2135 snprintf(fn
, sizeof(fn
), "%s%s/%s/greet", VM_SPOOL_DIR
, context
, ext
);
2137 if (ast_fileexists(fn
, NULL
, NULL
) > 0) {
2138 res
= ast_stream_and_wait(chan
, fn
, chan
->language
, ecodes
);
2144 /* Dispose just in case */
2146 res
= ast_stream_and_wait(chan
, "vm-theperson", chan
->language
, ecodes
);
2149 res
= ast_say_digit_str(chan
, ext
, ecodes
, chan
->language
);
2153 res
= ast_stream_and_wait(chan
, busy
? "vm-isonphone" : "vm-isunavail", chan
->language
, ecodes
);
2157 static void free_user(struct ast_vm_user
*vmu
)
2159 if (ast_test_flag(vmu
, VM_ALLOCED
))
2163 static void free_zone(struct vm_zone
*z
)
2168 static const char *mbox(int id
)
2170 static const char *msgs
[] = {
2182 return (id
>= 0 && id
< (sizeof(msgs
)/sizeof(msgs
[0]))) ? msgs
[id
] : "Unknown";
2186 static int inboxcount(const char *mailbox
, int *newmsgs
, int *oldmsgs
)
2194 struct odbc_obj
*obj
;
2202 /* If no mailbox, return immediately */
2203 if (ast_strlen_zero(mailbox
))
2206 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2208 context
= strchr(tmp
, '@');
2213 context
= "default";
2215 obj
= ast_odbc_request_obj(odbc_database
, 0);
2217 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
2218 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2219 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
2220 ast_odbc_release_obj(obj
);
2223 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table
, VM_SPOOL_DIR
, context
, tmp
, "INBOX");
2224 res
= SQLPrepare(stmt
, (unsigned char *)sql
, SQL_NTS
);
2225 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2226 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", sql
);
2227 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2228 ast_odbc_release_obj(obj
);
2231 res
= ast_odbc_smart_execute(obj
, stmt
);
2232 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2233 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2234 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2235 ast_odbc_release_obj(obj
);
2238 res
= SQLFetch(stmt
);
2239 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2240 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
2241 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2242 ast_odbc_release_obj(obj
);
2245 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
2246 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2247 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
2248 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2249 ast_odbc_release_obj(obj
);
2252 *newmsgs
= atoi(rowdata
);
2253 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2255 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
2256 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2257 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
2258 ast_odbc_release_obj(obj
);
2261 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table
, VM_SPOOL_DIR
, context
, tmp
, "Old");
2262 res
= SQLPrepare(stmt
, (unsigned char *)sql
, SQL_NTS
);
2263 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2264 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", sql
);
2265 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2266 ast_odbc_release_obj(obj
);
2269 res
= ast_odbc_smart_execute(obj
, stmt
);
2270 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2271 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2272 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2273 ast_odbc_release_obj(obj
);
2276 res
= SQLFetch(stmt
);
2277 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2278 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
2279 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2280 ast_odbc_release_obj(obj
);
2283 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
2284 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2285 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
2286 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2287 ast_odbc_release_obj(obj
);
2290 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2291 ast_odbc_release_obj(obj
);
2292 *oldmsgs
= atoi(rowdata
);
2295 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
2301 static int messagecount(const char *context
, const char *mailbox
, const char *folder
)
2303 struct odbc_obj
*obj
= NULL
;
2306 SQLHSTMT stmt
= NULL
;
2311 /* If no mailbox, return immediately */
2312 if (ast_strlen_zero(mailbox
))
2315 obj
= ast_odbc_request_obj(odbc_database
, 0);
2317 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
2318 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2319 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
2322 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table
, VM_SPOOL_DIR
, context
, mailbox
, folder
);
2323 res
= SQLPrepare(stmt
, (unsigned char *)sql
, SQL_NTS
);
2324 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2325 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", sql
);
2326 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
2329 res
= ast_odbc_smart_execute(obj
, stmt
);
2330 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2331 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2332 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2335 res
= SQLFetch(stmt
);
2336 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2337 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
2338 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2341 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
2342 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2343 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
2344 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2347 nummsgs
= atoi(rowdata
);
2348 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2350 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
2354 ast_odbc_release_obj(obj
);
2358 static int has_voicemail(const char *mailbox
, const char *folder
)
2360 char *context
, tmp
[256];
2361 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2362 if ((context
= strchr(tmp
, '@')))
2365 context
= "default";
2367 if (messagecount(context
, tmp
, folder
))
2373 #elif defined(IMAP_STORAGE)
2375 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
)
2377 char *myserveremail
= serveremail
;
2382 char tmp
[80] = "/tmp/astmail-XXXXXX";
2387 /* Attach only the first format */
2388 fmt
= ast_strdupa(fmt
);
2390 strsep(&stringp
, "|");
2392 if (!ast_strlen_zero(vmu
->serveremail
))
2393 myserveremail
= vmu
->serveremail
;
2395 make_file(fn
, sizeof(fn
), dir
, msgnum
);
2397 if (ast_strlen_zero(vmu
->email
))
2398 ast_copy_string(vmu
->email
, vmu
->imapuser
, sizeof(vmu
->email
));
2400 if (!strcmp(fmt
, "wav49"))
2402 if(option_debug
> 2)
2403 ast_log(LOG_DEBUG
, "Storing file '%s', format '%s'\n", fn
, fmt
);
2404 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2406 if ((p
= vm_mkftemp(tmp
)) == NULL
) {
2407 ast_log(LOG_WARNING
, "Unable to store '%s' (can't create temporary file)\n", fn
);
2410 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);
2411 /* read mail file to memory */
2414 if((buf
= ast_malloc(len
+1)) == NIL
) {
2415 ast_log(LOG_ERROR
, "Can't allocate %ld bytes to read message\n", len
+1);
2418 fread(buf
, len
, 1, p
);
2419 ((char *)buf
)[len
] = '\0';
2420 INIT(&str
, mail_string
, buf
, len
);
2421 imap_mailbox_name(mailbox
, vms
, 0, 1);
2422 if(!mail_append(vms
->mailstream
, mailbox
, &str
))
2423 ast_log(LOG_ERROR
, "Error while sending the message to %s\n", mailbox
);
2427 if(option_debug
> 2)
2428 ast_log(LOG_DEBUG
, "%s stored\n", fn
);
2434 static int inboxcount(const char *mailbox
, int *newmsgs
, int *oldmsgs
)
2439 struct ast_vm_user
*vmu
;
2440 struct vm_state
*vms_p
;
2451 if(option_debug
> 2)
2452 ast_log (LOG_DEBUG
,"Mailbox is set to %s\n",mailbox
);
2453 /* If no mailbox, return immediately */
2454 if (ast_strlen_zero(mailbox
))
2456 if (strchr(mailbox
, ',')) {
2458 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2461 while((cur
= strsep(&mb
, ", "))) {
2462 if (!ast_strlen_zero(cur
)) {
2463 if (inboxcount(cur
, newmsgs
? &tmpnew
: NULL
, oldmsgs
? &tmpold
: NULL
))
2475 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2476 context
= strchr(tmp
, '@');
2482 context
= "default";
2483 mailboxnc
= (char *)mailbox
;
2486 /* We have to get the user before we can open the stream! */
2487 /*ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2488 vmu
= find_user(NULL
, context
, mailboxnc
);
2490 ast_log (LOG_ERROR
,"Couldn't find mailbox %s in context %s\n",mailboxnc
,context
);
2493 /* No IMAP account available */
2494 if (vmu
->imapuser
[0] == '\0') {
2495 ast_log (LOG_WARNING
,"IMAP user not set for mailbox %s\n",vmu
->mailbox
);
2500 /* check if someone is accessing this box right now... */
2501 vms_p
= get_vm_state_by_imapuser(vmu
->imapuser
,1);
2503 vms_p
= get_vm_state_by_mailbox(mailboxnc
,1);
2506 if(option_debug
> 2)
2507 ast_log (LOG_DEBUG
,"Returning before search - user is logged in\n");
2508 *newmsgs
= vms_p
->newmessages
;
2509 *oldmsgs
= vms_p
->oldmessages
;
2513 /* add one if not there... */
2514 vms_p
= get_vm_state_by_imapuser(vmu
->imapuser
,0);
2516 vms_p
= get_vm_state_by_mailbox(mailboxnc
,0);
2520 if(option_debug
> 2)
2521 ast_log (LOG_DEBUG
,"Adding new vmstate for %s\n",vmu
->imapuser
);
2522 vms_p
= (struct vm_state
*)malloc(sizeof(struct vm_state
));
2523 ast_copy_string(vms_p
->imapuser
,vmu
->imapuser
, sizeof(vms_p
->imapuser
));
2524 ast_copy_string(vms_p
->username
, mailboxnc
, sizeof(vms_p
->username
)); /* save for access from interactive entry point */
2525 vms_p
->mailstream
= NIL
; /* save for access from interactive entry point */
2526 if(option_debug
> 2)
2527 ast_log (LOG_DEBUG
,"Copied %s to %s\n",vmu
->imapuser
,vms_p
->imapuser
);
2529 vms_p
->interactive
= 0;
2530 /* set mailbox to INBOX! */
2531 ast_copy_string(vms_p
->curbox
, mbox(0), sizeof(vms_p
->curbox
));
2532 init_vm_state(vms_p
);
2533 vmstate_insert(vms_p
);
2535 if (!vms_p
->mailstream
)
2536 ret
= init_mailstream(vms_p
, 0);
2537 if (!vms_p
->mailstream
) {
2538 ast_log (LOG_ERROR
,"IMAP mailstream is NULL\n");
2541 if (newmsgs
&& ret
==0 && vms_p
->updated
==1 ) {
2542 pgm
= mail_newsearchpgm ();
2543 hdr
= mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc
);
2550 vms_p
->vmArrayIndex
= 0;
2552 mail_search_full (vms_p
->mailstream
, NULL
, pgm
, NIL
);
2553 *newmsgs
= vms_p
->vmArrayIndex
;
2554 vms_p
->newmessages
= vms_p
->vmArrayIndex
;
2555 mail_free_searchpgm(&pgm
);
2557 if (oldmsgs
&& ret
==0 && vms_p
->updated
==1 ) {
2558 pgm
= mail_newsearchpgm ();
2559 hdr
= mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailboxnc
);
2566 vms_p
->vmArrayIndex
= 0;
2568 mail_search_full (vms_p
->mailstream
, NULL
, pgm
, NIL
);
2569 *oldmsgs
= vms_p
->vmArrayIndex
;
2570 vms_p
->oldmessages
= vms_p
->vmArrayIndex
;
2571 mail_free_searchpgm(&pgm
);
2573 if (vms_p
->updated
== 1) { /* changes, so we did the searches above */
2575 } else if (vms_p
->updated
> 1) { /* decrement delay count */
2577 } else { /* no changes, so don't search */
2578 mail_ping(vms_p
->mailstream
);
2579 /* Keep the old data */
2580 *newmsgs
= vms_p
->newmessages
;
2581 *oldmsgs
= vms_p
->oldmessages
;
2586 static int has_voicemail(const char *mailbox
, const char *folder
)
2588 int newmsgs
, oldmsgs
;
2590 if(inboxcount(mailbox
, &newmsgs
, &oldmsgs
))
2591 return folder
? oldmsgs
: newmsgs
;
2596 static int messagecount(const char *context
, const char *mailbox
, const char *folder
)
2598 int newmsgs
, oldmsgs
;
2601 if (ast_strlen_zero(mailbox
))
2603 sprintf(tmp
,"%s@%s", mailbox
, ast_strlen_zero(context
)? "default": context
);
2605 if(inboxcount(tmp
, &newmsgs
, &oldmsgs
))
2606 return folder
? oldmsgs
: newmsgs
;
2612 #ifndef IMAP_STORAGE
2613 /* copy message only used by file storage */
2614 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
)
2617 char fromdir
[256], todir
[256], frompath
[256], topath
[256];
2618 const char *frombox
= mbox(imbox
);
2621 ast_log(LOG_NOTICE
, "Copying message from %s@%s to %s@%s\n", vmu
->mailbox
, vmu
->context
, recip
->mailbox
, recip
->context
);
2623 create_dirpath(todir
, sizeof(todir
), recip
->context
, recip
->mailbox
, "INBOX");
2625 make_dir(fromdir
, sizeof(fromdir
), vmu
->context
, vmu
->mailbox
, frombox
);
2626 make_file(frompath
, sizeof(frompath
), fromdir
, msgnum
);
2628 if (vm_lock_path(todir
))
2629 return ERROR_LOCK_PATH
;
2633 make_file(topath
, sizeof(topath
), todir
, recipmsgnum
);
2634 if (!EXISTS(todir
, recipmsgnum
, topath
, chan
->language
))
2637 } while (recipmsgnum
< recip
->maxmsg
);
2638 if (recipmsgnum
< recip
->maxmsg
) {
2639 COPY(fromdir
, msgnum
, todir
, recipmsgnum
, recip
->mailbox
, recip
->context
, frompath
, topath
);
2641 ast_log(LOG_ERROR
, "Recipient mailbox %s@%s is full\n", recip
->mailbox
, recip
->context
);
2643 ast_unlock_path(todir
);
2644 notify_new_message(chan
, recip
, recipmsgnum
, duration
, fmt
, S_OR(chan
->cid
.cid_num
, NULL
), S_OR(chan
->cid
.cid_name
, NULL
));
2649 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2650 static int messagecount(const char *context
, const char *mailbox
, const char *folder
)
2652 return __has_voicemail(context
, mailbox
, folder
, 0);
2656 static int __has_voicemail(const char *context
, const char *mailbox
, const char *folder
, int shortcircuit
)
2664 /* If no mailbox, return immediately */
2665 if (ast_strlen_zero(mailbox
))
2668 context
= "default";
2669 snprintf(fn
, sizeof(fn
), "%s%s/%s/%s", VM_SPOOL_DIR
, context
, mailbox
, folder
);
2673 while ((de
= readdir(dir
))) {
2674 if (!strncasecmp(de
->d_name
, "msg", 3)) {
2678 } else if (!strncasecmp(de
->d_name
+ 8, "txt", 3))
2687 static int has_voicemail(const char *mailbox
, const char *folder
)
2689 char tmp
[256], *tmp2
= tmp
, *mbox
, *context
;
2690 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2691 while ((mbox
= strsep(&tmp2
, ","))) {
2692 if ((context
= strchr(mbox
, '@')))
2695 context
= "default";
2696 if (__has_voicemail(context
, mbox
, folder
, 1))
2703 static int inboxcount(const char *mailbox
, int *newmsgs
, int *oldmsgs
)
2712 /* If no mailbox, return immediately */
2713 if (ast_strlen_zero(mailbox
))
2715 if (strchr(mailbox
, ',')) {
2719 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2721 while ((cur
= strsep(&mb
, ", "))) {
2722 if (!ast_strlen_zero(cur
)) {
2723 if (inboxcount(cur
, newmsgs
? &tmpnew
: NULL
, oldmsgs
? &tmpold
: NULL
))
2735 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2736 context
= strchr(tmp
, '@');
2741 context
= "default";
2743 *newmsgs
= __has_voicemail(context
, tmp
, "INBOX", 0);
2745 *oldmsgs
= __has_voicemail(context
, tmp
, "Old", 0);
2751 static void run_externnotify(char *context
, char *extension
)
2753 char arguments
[255];
2754 char ext_context
[256] = "";
2755 int newvoicemails
= 0, oldvoicemails
= 0;
2756 struct ast_smdi_mwi_message
*mwi_msg
;
2758 if (!ast_strlen_zero(context
))
2759 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", extension
, context
);
2761 ast_copy_string(ext_context
, extension
, sizeof(ext_context
));
2763 if (!strcasecmp(externnotify
, "smdi")) {
2764 if (ast_app_has_voicemail(ext_context
, NULL
))
2765 ast_smdi_mwi_set(smdi_iface
, extension
);
2767 ast_smdi_mwi_unset(smdi_iface
, extension
);
2769 if ((mwi_msg
= ast_smdi_mwi_message_wait(smdi_iface
, SMDI_MWI_WAIT_TIMEOUT
))) {
2770 ast_log(LOG_ERROR
, "Error executing SMDI MWI change for %s on %s\n", extension
, smdi_iface
->name
);
2771 if (!strncmp(mwi_msg
->cause
, "INV", 3))
2772 ast_log(LOG_ERROR
, "Invalid MWI extension: %s\n", mwi_msg
->fwd_st
);
2773 else if (!strncmp(mwi_msg
->cause
, "BLK", 3))
2774 ast_log(LOG_WARNING
, "MWI light was already on or off for %s\n", mwi_msg
->fwd_st
);
2775 ast_log(LOG_WARNING
, "The switch reported '%s'\n", mwi_msg
->cause
);
2776 ASTOBJ_UNREF(mwi_msg
, ast_smdi_mwi_message_destroy
);
2778 ast_log(LOG_DEBUG
, "Successfully executed SMDI MWI change for %s on %s\n", extension
, smdi_iface
->name
);
2780 } else if (!ast_strlen_zero(externnotify
)) {
2781 if (inboxcount(ext_context
, &newvoicemails
, &oldvoicemails
)) {
2782 ast_log(LOG_ERROR
, "Problem in calculating number of voicemail messages available for extension %s\n", extension
);
2784 snprintf(arguments
, sizeof(arguments
), "%s %s %s %d&", externnotify
, context
, extension
, newvoicemails
);
2785 ast_log(LOG_DEBUG
, "Executing %s\n", arguments
);
2786 ast_safe_system(arguments
);
2791 struct leave_vm_options
{
2793 signed char record_gain
;
2796 static int leave_voicemail(struct ast_channel
*chan
, char *ext
, struct leave_vm_options
*options
)
2799 int newmsgs
, oldmsgs
;
2800 struct vm_state
*vms
= NULL
;
2802 char tmptxtfile
[256], txtfile
[256];
2813 char dir
[256], tmpdir
[260];
2815 char prefile
[256]="";
2816 char tempfile
[256]="";
2817 char ext_context
[256] = "";
2820 char ecodes
[16] = "#";
2821 char tmp
[256] = "", *tmpptr
;
2822 struct ast_vm_user
*vmu
;
2823 struct ast_vm_user svm
;
2824 const char *category
= NULL
;
2826 ast_copy_string(tmp
, ext
, sizeof(tmp
));
2828 context
= strchr(tmp
, '@');
2831 tmpptr
= strchr(context
, '&');
2833 tmpptr
= strchr(ext
, '&');
2839 category
= pbx_builtin_getvar_helper(chan
, "VM_CATEGORY");
2841 if(option_debug
> 2)
2842 ast_log(LOG_DEBUG
, "Before find_user\n");
2843 if (!(vmu
= find_user(&svm
, context
, ext
))) {
2844 ast_log(LOG_WARNING
, "No entry in voicemail config file for '%s'\n", ext
);
2845 if (ast_test_flag(options
, OPT_PRIORITY_JUMP
) || ast_opt_priority_jumping
)
2846 ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101);
2847 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
2851 /* Setup pre-file if appropriate */
2852 if (strcmp(vmu
->context
, "default"))
2853 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", ext
, vmu
->context
);
2855 ast_copy_string(ext_context
, vmu
->context
, sizeof(ext_context
));
2856 if (ast_test_flag(options
, OPT_BUSY_GREETING
))
2857 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/busy", VM_SPOOL_DIR
, vmu
->context
, ext
);
2858 else if (ast_test_flag(options
, OPT_UNAVAIL_GREETING
))
2859 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/unavail", VM_SPOOL_DIR
, vmu
->context
, ext
);
2860 snprintf(tempfile
, sizeof(tempfile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, ext
);
2861 RETRIEVE(tempfile
, -1);
2862 if (ast_fileexists(tempfile
, NULL
, NULL
) > 0)
2863 ast_copy_string(prefile
, tempfile
, sizeof(prefile
));
2864 DISPOSE(tempfile
, -1);
2865 /* It's easier just to try to make it than to check for its existence */
2866 create_dirpath(dir
, sizeof(dir
), vmu
->context
, ext
, "INBOX");
2867 create_dirpath(tmpdir
, sizeof(tmpdir
), vmu
->context
, ext
, "tmp");
2869 /* Check current or macro-calling context for special extensions */
2870 if (ast_test_flag(vmu
, VM_OPERATOR
)) {
2871 if (!ast_strlen_zero(vmu
->exit
)) {
2872 if (ast_exists_extension(chan
, vmu
->exit
, "o", 1, chan
->cid
.cid_num
)) {
2873 strncat(ecodes
, "0", sizeof(ecodes
) - strlen(ecodes
) - 1);
2876 } else if (ast_exists_extension(chan
, chan
->context
, "o", 1, chan
->cid
.cid_num
)) {
2877 strncat(ecodes
, "0", sizeof(ecodes
) - strlen(ecodes
) - 1);
2880 else if (!ast_strlen_zero(chan
->macrocontext
) && ast_exists_extension(chan
, chan
->macrocontext
, "o", 1, chan
->cid
.cid_num
)) {
2881 strncat(ecodes
, "0", sizeof(ecodes
) - strlen(ecodes
) - 1);
2886 if (!ast_strlen_zero(vmu
->exit
)) {
2887 if (ast_exists_extension(chan
, vmu
->exit
, "a", 1, chan
->cid
.cid_num
))
2888 strncat(ecodes
, "*", sizeof(ecodes
) - strlen(ecodes
) - 1);
2889 } else if (ast_exists_extension(chan
, chan
->context
, "a", 1, chan
->cid
.cid_num
))
2890 strncat(ecodes
, "*", sizeof(ecodes
) - strlen(ecodes
) - 1);
2891 else if (!ast_strlen_zero(chan
->macrocontext
) && ast_exists_extension(chan
, chan
->macrocontext
, "a", 1, chan
->cid
.cid_num
)) {
2892 strncat(ecodes
, "*", sizeof(ecodes
) - strlen(ecodes
) - 1);
2896 /* Play the beginning intro if desired */
2897 if (!ast_strlen_zero(prefile
)) {
2898 RETRIEVE(prefile
, -1);
2899 if (ast_fileexists(prefile
, NULL
, NULL
) > 0) {
2900 if (ast_streamfile(chan
, prefile
, chan
->language
) > -1)
2901 res
= ast_waitstream(chan
, ecodes
);
2903 ast_log(LOG_DEBUG
, "%s doesn't exist, doing what we can\n", prefile
);
2904 res
= invent_message(chan
, vmu
->context
, ext
, ast_test_flag(options
, OPT_BUSY_GREETING
), ecodes
);
2906 DISPOSE(prefile
, -1);
2908 ast_log(LOG_DEBUG
, "Hang up during prefile playback\n");
2910 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
2915 /* On a '#' we skip the instructions */
2916 ast_set_flag(options
, OPT_SILENT
);
2919 if (!res
&& !ast_test_flag(options
, OPT_SILENT
)) {
2920 res
= ast_stream_and_wait(chan
, INTRO
, chan
->language
, ecodes
);
2922 ast_set_flag(options
, OPT_SILENT
);
2927 ast_stopstream(chan
);
2928 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2929 other than the operator -- an automated attendant or mailbox login for example */
2931 chan
->exten
[0] = 'a';
2932 chan
->exten
[1] = '\0';
2933 if (!ast_strlen_zero(vmu
->exit
)) {
2934 ast_copy_string(chan
->context
, vmu
->exit
, sizeof(chan
->context
));
2935 } else if (ausemacro
&& !ast_strlen_zero(chan
->macrocontext
)) {
2936 ast_copy_string(chan
->context
, chan
->macrocontext
, sizeof(chan
->context
));
2940 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "USEREXIT");
2944 /* Check for a '0' here */
2947 if (ouseexten
|| ousemacro
) {
2948 chan
->exten
[0] = 'o';
2949 chan
->exten
[1] = '\0';
2950 if (!ast_strlen_zero(vmu
->exit
)) {
2951 ast_copy_string(chan
->context
, vmu
->exit
, sizeof(chan
->context
));
2952 } else if (ousemacro
&& !ast_strlen_zero(chan
->macrocontext
)) {
2953 ast_copy_string(chan
->context
, chan
->macrocontext
, sizeof(chan
->context
));
2955 ast_play_and_wait(chan
, "transfer");
2958 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "USEREXIT");
2964 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
2967 /* The meat of recording the message... All the announcements and beeps have been played*/
2968 ast_copy_string(fmt
, vmfmts
, sizeof(fmt
));
2969 if (!ast_strlen_zero(fmt
)) {
2973 /* Is ext a mailbox? */
2974 /* must open stream for this user to get info! */
2975 vms
= get_vm_state_by_mailbox(ext
,0);
2977 if(option_debug
> 2)
2978 ast_log(LOG_DEBUG
, "Using vm_state, interactive set to %d.\n",vms
->interactive
);
2979 newmsgs
= vms
->newmessages
++;
2980 oldmsgs
= vms
->oldmessages
;
2982 res
= inboxcount(ext
, &newmsgs
, &oldmsgs
);
2984 ast_log(LOG_NOTICE
,"Can not leave voicemail, unable to count messages\n");
2987 vms
= get_vm_state_by_mailbox(ext
,0);
2989 /* here is a big difference! We add one to it later */
2990 msgnum
= newmsgs
+ oldmsgs
;
2991 if (option_debug
> 2)
2992 ast_log(LOG_DEBUG
, "Messagecount set to %d\n",msgnum
);
2993 snprintf(fn
, sizeof(fn
), "%s/imap/msg%s%04d", VM_SPOOL_DIR
, vmu
->mailbox
, msgnum
);
2994 /* set variable for compatability */
2995 pbx_builtin_setvar_helper(chan
, "VM_MESSAGEFILE", "IMAP_STORAGE");
2997 /* Check if mailbox is full */
2998 if (vms
->quota_limit
&& vms
->quota_usage
>= vms
->quota_limit
) {
3000 ast_log(LOG_DEBUG
, "*** QUOTA EXCEEDED!! %u >= %u\n", vms
->quota_usage
, vms
->quota_limit
);
3001 ast_play_and_wait(chan
, "vm-mailboxfull");
3004 /* here is a big difference! We add one to it later */
3005 msgnum
= newmsgs
+ oldmsgs
;
3006 if (option_debug
> 2)
3007 ast_log(LOG_DEBUG
, "Messagecount set to %d\n",msgnum
);
3009 if (count_messages(vmu
, dir
) >= vmu
->maxmsg
) {
3010 res
= ast_streamfile(chan
, "vm-mailboxfull", chan
->language
);
3012 res
= ast_waitstream(chan
, "");
3013 ast_log(LOG_WARNING
, "No more messages possible\n");
3014 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3019 snprintf(tmptxtfile
, sizeof(tmptxtfile
), "%s/XXXXXX", tmpdir
);
3020 txtdes
= mkstemp(tmptxtfile
);
3022 res
= ast_streamfile(chan
, "vm-mailboxfull", chan
->language
);
3024 res
= ast_waitstream(chan
, "");
3025 ast_log(LOG_ERROR
, "Unable to create message file: %s\n", strerror(errno
));
3026 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3030 /* Now play the beep once we have the message number for our next message. */
3032 /* Unless we're *really* silent, try to send the beep */
3033 res
= ast_stream_and_wait(chan
, "beep", chan
->language
, "");
3036 /* Store information */
3037 txt
= fdopen(txtdes
, "w+");
3039 get_date(date
, sizeof(date
));
3042 "; Message Information file\n"
3061 ast_callerid_merge(callerid
, sizeof(callerid
), S_OR(chan
->cid
.cid_name
, NULL
), S_OR(chan
->cid
.cid_num
, NULL
), "Unknown"),
3062 date
, (long)time(NULL
),
3063 category
? category
: "");
3065 ast_log(LOG_WARNING
, "Error opening text file for output\n");
3066 res
= play_record_review(chan
, NULL
, tmptxtfile
, vmmaxmessage
, fmt
, 1, vmu
, &duration
, NULL
, options
->record_gain
);
3069 if (duration
< vmminmessage
) {
3070 if (option_verbose
> 2)
3071 ast_verbose( VERBOSE_PREFIX_3
"Recording was %d seconds long but needs to be at least %d - abandoning\n", duration
, vmminmessage
);
3072 ast_filedelete(tmptxtfile
, NULL
);
3075 fprintf(txt
, "duration=%d\n", duration
);
3077 if (vm_lock_path(dir
)) {
3078 ast_log(LOG_ERROR
, "Couldn't lock directory %s. Voicemail will be lost.\n", dir
);
3080 ast_filedelete(tmptxtfile
, NULL
);
3082 } else if (ast_fileexists(tmptxtfile
, NULL
, NULL
) <= 0) {
3084 ast_log(LOG_DEBUG
, "The recorded media file is gone, so we should remove the .txt file too!\n");
3086 ast_unlock_path(dir
);
3089 make_file(fn
, sizeof(fn
), dir
, msgnum
);
3090 if (!EXISTS(dir
, msgnum
, fn
, NULL
))
3095 /* assign a variable with the name of the voicemail file */
3096 #ifndef IMAP_STORAGE
3097 pbx_builtin_setvar_helper(chan
, "VM_MESSAGEFILE", fn
);
3099 pbx_builtin_setvar_helper(chan
, "VM_MESSAGEFILE", "IMAP_STORAGE");
3102 snprintf(txtfile
, sizeof(txtfile
), "%s.txt", fn
);
3103 ast_filerename(tmptxtfile
, fn
, NULL
);
3104 rename(tmptxtfile
, txtfile
);
3106 ast_unlock_path(dir
);
3107 #ifndef IMAP_STORAGE
3108 /* Are there to be more recipients of this message? */
3110 struct ast_vm_user recipu
, *recip
;
3111 char *exten
, *context
;
3113 exten
= strsep(&tmpptr
, "&");
3114 context
= strchr(exten
, '@');
3119 if ((recip
= find_user(&recipu
, context
, exten
))) {
3120 copy_message(chan
, vmu
, 0, msgnum
, duration
, recip
, fmt
);
3125 if (ast_fileexists(fn
, NULL
, NULL
)) {
3126 STORE(dir
, vmu
->mailbox
, vmu
->context
, msgnum
, chan
, vmu
, fmt
, duration
, vms
);
3127 notify_new_message(chan
, vmu
, msgnum
, duration
, fmt
, S_OR(chan
->cid
.cid_num
, NULL
), S_OR(chan
->cid
.cid_name
, NULL
));
3128 DISPOSE(dir
, msgnum
);
3138 if (duration
< vmminmessage
)
3139 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3140 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3142 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "SUCCESS");
3144 ast_log(LOG_WARNING
, "No format for saving voicemail?\n");
3151 #ifndef IMAP_STORAGE
3152 static int resequence_mailbox(struct ast_vm_user
*vmu
, char *dir
)
3154 /* we know max messages, so stop process when number is hit */
3160 if (vm_lock_path(dir
))
3161 return ERROR_LOCK_PATH
;
3163 for (x
= 0, dest
= 0; x
< vmu
->maxmsg
; x
++) {
3164 make_file(sfn
, sizeof(sfn
), dir
, x
);
3165 if (EXISTS(dir
, x
, sfn
, NULL
)) {
3168 make_file(dfn
, sizeof(dfn
), dir
, dest
);
3169 RENAME(dir
, x
, vmu
->mailbox
, vmu
->context
, dir
, dest
, sfn
, dfn
);
3175 ast_unlock_path(dir
);
3181 static int say_and_wait(struct ast_channel
*chan
, int num
, const char *language
)
3184 d
= ast_say_number(chan
, num
, AST_DIGIT_ANY
, language
, (char *) NULL
);
3188 static int save_to_folder(struct ast_vm_user
*vmu
, struct vm_state
*vms
, int msg
, int box
)
3191 /* we must use mbox(x) folder names, and copy the message there */
3197 /* if save to Old folder, just leave in INBOX */
3198 if (box
== 1) return 10;
3199 /* get the real IMAP message number for this message */
3200 sprintf(sequence
,"%ld",vms
->msgArray
[msg
]);
3201 imap_mailbox_name(dbox
, vms
, box
, 1);
3202 if(option_debug
> 2)
3203 ast_log(LOG_DEBUG
, "Copying sequence %s to mailbox %s\n",sequence
,dbox
);
3204 res
= mail_copy(vms
->mailstream
,sequence
,dbox
);
3205 if (res
== 1) return 0;
3208 char *dir
= vms
->curdir
;
3209 char *username
= vms
->username
;
3210 char *context
= vmu
->context
;
3214 const char *dbox
= mbox(box
);
3216 make_file(sfn
, sizeof(sfn
), dir
, msg
);
3217 create_dirpath(ddir
, sizeof(ddir
), context
, username
, dbox
);
3219 if (vm_lock_path(ddir
))
3220 return ERROR_LOCK_PATH
;
3222 for (x
= 0; x
< vmu
->maxmsg
; x
++) {
3223 make_file(dfn
, sizeof(dfn
), ddir
, x
);
3224 if (!EXISTS(ddir
, x
, dfn
, NULL
))
3227 if (x
>= vmu
->maxmsg
) {
3228 ast_unlock_path(ddir
);
3231 if (strcmp(sfn
, dfn
)) {
3232 COPY(dir
, msg
, ddir
, x
, username
, context
, sfn
, dfn
);
3234 ast_unlock_path(ddir
);
3239 static int adsi_logo(unsigned char *buf
)
3242 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_CENT
, 0, "Comedian Mail", "");
3243 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_CENT
, 0, "(C)2002-2006 Digium, Inc.", "");
3247 static int adsi_load_vmail(struct ast_channel
*chan
, int *useadsi
)
3249 unsigned char buf
[256];
3255 bytes
+= ast_adsi_data_mode(buf
+ bytes
);
3256 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3259 bytes
+= adsi_logo(buf
);
3260 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Downloading Scripts", "");
3262 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " .", "");
3264 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3265 bytes
+= ast_adsi_data_mode(buf
+ bytes
);
3266 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3268 if (ast_adsi_begin_download(chan
, addesc
, adsifdn
, adsisec
, adsiver
)) {
3270 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Load Cancelled.", "");
3271 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "ADSI Unavailable", "");
3272 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3273 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3274 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3281 bytes
+= ast_adsi_logo(buf
);
3282 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Downloading Scripts", "");
3283 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " ..", "");
3284 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3285 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3288 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 0, "Listen", "Listen", "1", 1);
3289 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 1, "Folder", "Folder", "2", 1);
3290 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 2, "Advanced", "Advnced", "3", 1);
3291 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 3, "Options", "Options", "0", 1);
3292 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 4, "Help", "Help", "*", 1);
3293 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 5, "Exit", "Exit", "#", 1);
3294 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
3297 /* Add another dot */
3299 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " ...", "");
3300 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3302 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3303 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3307 /* These buttons we load but don't use yet */
3308 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 6, "Previous", "Prev", "4", 1);
3309 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 8, "Repeat", "Repeat", "5", 1);
3310 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 7, "Delete", "Delete", "7", 1);
3311 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 9, "Next", "Next", "6", 1);
3312 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 10, "Save", "Save", "9", 1);
3313 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 11, "Undelete", "Restore", "7", 1);
3314 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
3317 /* Add another dot */
3319 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " ....", "");
3320 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3321 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3326 snprintf(num
, sizeof(num
), "%d", x
);
3327 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 12 + x
, mbox(x
), mbox(x
), num
, 1);
3329 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 12 + 5, "Cancel", "Cancel", "#", 1);
3330 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
3333 /* Add another dot */
3335 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " .....", "");
3336 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3337 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3340 if (ast_adsi_end_download(chan
)) {
3342 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Download Unsuccessful.", "");
3343 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "ADSI Unavailable", "");
3344 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3345 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3346 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3350 bytes
+= ast_adsi_download_disconnect(buf
+ bytes
);
3351 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3352 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
3354 ast_log(LOG_DEBUG
, "Done downloading scripts...\n");
3359 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, " ......", "");
3360 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3362 ast_log(LOG_DEBUG
, "Restarting session...\n");
3365 /* Load the session now */
3366 if (ast_adsi_load_session(chan
, adsifdn
, adsiver
, 1) == 1) {
3368 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Scripts Loaded!", "");
3370 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Load Failed!", "");
3372 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3376 static void adsi_begin(struct ast_channel
*chan
, int *useadsi
)
3379 if (!ast_adsi_available(chan
))
3381 x
= ast_adsi_load_session(chan
, adsifdn
, adsiver
, 1);
3385 if (adsi_load_vmail(chan
, useadsi
)) {
3386 ast_log(LOG_WARNING
, "Unable to upload voicemail scripts\n");
3393 static void adsi_login(struct ast_channel
*chan
)
3395 unsigned char buf
[256];
3397 unsigned char keys
[8];
3399 if (!ast_adsi_available(chan
))
3404 /* Set one key for next */
3405 keys
[3] = ADSI_KEY_APPS
+ 3;
3407 bytes
+= adsi_logo(buf
+ bytes
);
3408 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, " ", "");
3409 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, " ", "");
3410 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3411 bytes
+= ast_adsi_input_format(buf
+ bytes
, 1, ADSI_DIR_FROM_LEFT
, 0, "Mailbox: ******", "");
3412 bytes
+= ast_adsi_input_control(buf
+ bytes
, ADSI_COMM_PAGE
, 4, 1, 1, ADSI_JUST_LEFT
);
3413 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 3, "Enter", "Enter", "#", 1);
3414 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3415 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3416 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3419 static void adsi_password(struct ast_channel
*chan
)
3421 unsigned char buf
[256];
3423 unsigned char keys
[8];
3425 if (!ast_adsi_available(chan
))
3430 /* Set one key for next */
3431 keys
[3] = ADSI_KEY_APPS
+ 3;
3433 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3434 bytes
+= ast_adsi_input_format(buf
+ bytes
, 1, ADSI_DIR_FROM_LEFT
, 0, "Password: ******", "");
3435 bytes
+= ast_adsi_input_control(buf
+ bytes
, ADSI_COMM_PAGE
, 4, 0, 1, ADSI_JUST_LEFT
);
3436 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3437 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3438 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3441 static void adsi_folders(struct ast_channel
*chan
, int start
, char *label
)
3443 unsigned char buf
[256];
3445 unsigned char keys
[8];
3448 if (!ast_adsi_available(chan
))
3452 y
= ADSI_KEY_APPS
+ 12 + start
+ x
;
3453 if (y
> ADSI_KEY_APPS
+ 12 + 4)
3455 keys
[x
] = ADSI_KEY_SKT
| y
;
3457 keys
[5] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 17);
3461 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_CENT
, 0, label
, "");
3462 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_CENT
, 0, " ", "");
3463 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3464 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3465 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3467 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3470 static void adsi_message(struct ast_channel
*chan
, struct vm_state
*vms
)
3473 unsigned char buf
[256];
3474 char buf1
[256], buf2
[256];
3480 char datetime
[21]="";
3483 unsigned char keys
[8];
3487 if (!ast_adsi_available(chan
))
3490 /* Retrieve important info */
3491 snprintf(fn2
, sizeof(fn2
), "%s.txt", vms
->fn
);
3492 f
= fopen(fn2
, "r");
3495 fgets((char *)buf
, sizeof(buf
), f
);
3498 stringp
= (char *)buf
;
3499 strsep(&stringp
, "=");
3500 val
= strsep(&stringp
, "=");
3501 if (!ast_strlen_zero(val
)) {
3502 if (!strcmp((char *)buf
, "callerid"))
3503 ast_copy_string(cid
, val
, sizeof(cid
));
3504 if (!strcmp((char *)buf
, "origdate"))
3505 ast_copy_string(datetime
, val
, sizeof(datetime
));
3511 /* New meaning for keys */
3513 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 6 + x
);
3518 /* No prev key, provide "Folder" instead */
3519 keys
[0] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
3521 if (vms
->curmsg
>= vms
->lastmsg
) {
3522 /* If last message ... */
3524 /* but not only message, provide "Folder" instead */
3525 keys
[3] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
3526 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3529 /* Otherwise if only message, leave blank */
3534 if (!ast_strlen_zero(cid
)) {
3535 ast_callerid_parse(cid
, &name
, &num
);
3539 name
= "Unknown Caller";
3541 /* If deleted, show "undeleted" */
3543 if (vms
->deleted
[vms
->curmsg
])
3544 keys
[1] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 11);
3547 keys
[5] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 5);
3548 snprintf(buf1
, sizeof(buf1
), "%s%s", vms
->curbox
,
3549 strcasecmp(vms
->curbox
, "INBOX") ? " Messages" : "");
3550 snprintf(buf2
, sizeof(buf2
), "Message %d of %d", vms
->curmsg
+ 1, vms
->lastmsg
+ 1);
3552 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_LEFT
, 0, buf1
, "");
3553 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_LEFT
, 0, buf2
, "");
3554 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_LEFT
, 0, name
, "");
3555 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, datetime
, "");
3556 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3557 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3558 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3560 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3563 static void adsi_delete(struct ast_channel
*chan
, struct vm_state
*vms
)
3566 unsigned char buf
[256];
3567 unsigned char keys
[8];
3571 if (!ast_adsi_available(chan
))
3574 /* New meaning for keys */
3576 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 6 + x
);
3582 /* No prev key, provide "Folder" instead */
3583 keys
[0] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
3585 if (vms
->curmsg
>= vms
->lastmsg
) {
3586 /* If last message ... */
3588 /* but not only message, provide "Folder" instead */
3589 keys
[3] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
3591 /* Otherwise if only message, leave blank */
3596 /* If deleted, show "undeleted" */
3597 if (vms
->deleted
[vms
->curmsg
])
3598 keys
[1] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 11);
3601 keys
[5] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 5);
3602 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3603 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3605 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3608 static void adsi_status(struct ast_channel
*chan
, struct vm_state
*vms
)
3610 unsigned char buf
[256] = "";
3611 char buf1
[256] = "", buf2
[256] = "";
3613 unsigned char keys
[8];
3616 char *newm
= (vms
->newmessages
== 1) ? "message" : "messages";
3617 char *oldm
= (vms
->oldmessages
== 1) ? "message" : "messages";
3618 if (!ast_adsi_available(chan
))
3620 if (vms
->newmessages
) {
3621 snprintf(buf1
, sizeof(buf1
), "You have %d new", vms
->newmessages
);
3622 if (vms
->oldmessages
) {
3623 strncat(buf1
, " and", sizeof(buf1
) - strlen(buf1
) - 1);
3624 snprintf(buf2
, sizeof(buf2
), "%d old %s.", vms
->oldmessages
, oldm
);
3626 snprintf(buf2
, sizeof(buf2
), "%s.", newm
);
3628 } else if (vms
->oldmessages
) {
3629 snprintf(buf1
, sizeof(buf1
), "You have %d old", vms
->oldmessages
);
3630 snprintf(buf2
, sizeof(buf2
), "%s.", oldm
);
3632 strcpy(buf1
, "You have no messages.");
3636 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_LEFT
, 0, buf1
, "");
3637 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_LEFT
, 0, buf2
, "");
3638 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3641 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ x
);
3645 /* Don't let them listen if there are none */
3646 if (vms
->lastmsg
< 0)
3648 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3650 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3652 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3655 static void adsi_status2(struct ast_channel
*chan
, struct vm_state
*vms
)
3657 unsigned char buf
[256] = "";
3658 char buf1
[256] = "", buf2
[256] = "";
3660 unsigned char keys
[8];
3663 char *mess
= (vms
->lastmsg
== 0) ? "message" : "messages";
3665 if (!ast_adsi_available(chan
))
3668 /* Original command keys */
3670 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ x
);
3675 if ((vms
->lastmsg
+ 1) < 1)
3678 snprintf(buf1
, sizeof(buf1
), "%s%s has", vms
->curbox
,
3679 strcasecmp(vms
->curbox
, "INBOX") ? " folder" : "");
3681 if (vms
->lastmsg
+ 1)
3682 snprintf(buf2
, sizeof(buf2
), "%d %s.", vms
->lastmsg
+ 1, mess
);
3684 strcpy(buf2
, "no messages.");
3685 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_LEFT
, 0, buf1
, "");
3686 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_LEFT
, 0, buf2
, "");
3687 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_LEFT
, 0, "", "");
3688 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3689 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3691 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3693 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3698 static void adsi_clear(struct ast_channel *chan)
3702 if (!ast_adsi_available(chan))
3704 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3705 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3707 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3711 static void adsi_goodbye(struct ast_channel
*chan
)
3713 unsigned char buf
[256];
3716 if (!ast_adsi_available(chan
))
3718 bytes
+= adsi_logo(buf
+ bytes
);
3719 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_LEFT
, 0, " ", "");
3720 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Goodbye", "");
3721 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3722 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3724 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3727 /*--- get_folder: Folder menu ---*/
3728 /* Plays "press 1 for INBOX messages" etc
3729 Should possibly be internationalized
3731 static int get_folder(struct ast_channel
*chan
, int start
)
3736 d
= ast_play_and_wait(chan
, "vm-press"); /* "Press" */
3739 for (x
= start
; x
< 5; x
++) { /* For all folders */
3740 if ((d
= ast_say_number(chan
, x
, AST_DIGIT_ANY
, chan
->language
, (char *) NULL
)))
3742 d
= ast_play_and_wait(chan
, "vm-for"); /* "for" */
3745 snprintf(fn
, sizeof(fn
), "vm-%s", mbox(x
)); /* Folder name */
3746 d
= vm_play_folder_name(chan
, fn
);
3749 d
= ast_waitfordigit(chan
, 500);
3753 d
= ast_play_and_wait(chan
, "vm-tocancel"); /* "or pound to cancel" */
3756 d
= ast_waitfordigit(chan
, 4000);
3760 static int get_folder2(struct ast_channel
*chan
, char *fn
, int start
)
3763 res
= ast_play_and_wait(chan
, fn
); /* Folder name */
3764 while (((res
< '0') || (res
> '9')) &&
3765 (res
!= '#') && (res
>= 0)) {
3766 res
= get_folder(chan
, 0);
3771 static int vm_forwardoptions(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *curdir
, int curmsg
, char *vmfmts
,
3772 char *context
, signed char record_gain
, long *duration
, struct vm_state
*vms
)
3776 signed char zero_gain
= 0;
3778 while ((cmd
>= 0) && (cmd
!= 't') && (cmd
!= '*')) {
3783 /* prepend a message to the current message, update the metadata and return */
3785 char msgfile
[PATH_MAX
];
3786 char textfile
[PATH_MAX
];
3787 int prepend_duration
= 0;
3788 struct ast_config
*msg_cfg
;
3789 const char *duration_str
;
3791 make_file(msgfile
, sizeof(msgfile
), curdir
, curmsg
);
3792 strcpy(textfile
, msgfile
);
3793 strncat(textfile
, ".txt", sizeof(textfile
) - 1);
3796 /* if we can't read the message metadata, stop now */
3797 if (!(msg_cfg
= ast_config_load(textfile
))) {
3803 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &record_gain
, sizeof(record_gain
), 0);
3805 cmd
= ast_play_and_prepend(chan
, NULL
, msgfile
, 0, vmfmts
, &prepend_duration
, 1, silencethreshold
, maxsilence
);
3807 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &zero_gain
, sizeof(zero_gain
), 0);
3810 if ((duration_str
= ast_variable_retrieve(msg_cfg
, "message", "duration")))
3811 *duration
= atoi(duration_str
);
3813 if (prepend_duration
) {
3814 struct ast_category
*msg_cat
;
3815 /* need enough space for a maximum-length message duration */
3816 char duration_str
[12];
3818 *duration
+= prepend_duration
;
3819 msg_cat
= ast_category_get(msg_cfg
, "message");
3820 snprintf(duration_str
, 11, "%ld", *duration
);
3821 if (!ast_variable_update(msg_cat
, "duration", duration_str
, NULL
)) {
3822 config_text_file_save(textfile
, msg_cfg
, "app_voicemail");
3823 STORE(curdir
, vmu
->mailbox
, context
, curmsg
, chan
, vmu
, vmfmts
, *duration
, vms
);
3827 ast_config_destroy(msg_cfg
);
3838 cmd
= ast_play_and_wait(chan
,"vm-forwardoptions");
3839 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
3841 cmd
= ast_play_and_wait(chan
,"vm-starmain");
3842 /* "press star to return to the main menu" */
3844 cmd
= ast_waitfordigit(chan
,6000);
3851 if (cmd
== 't' || cmd
== 'S')
3856 static int notify_new_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, int msgnum
, long duration
, char *fmt
, char *cidnum
, char *cidname
)
3858 char todir
[256], fn
[256], ext_context
[256], *stringp
;
3859 int newmsgs
= 0, oldmsgs
= 0;
3860 const char *category
= pbx_builtin_getvar_helper(chan
, "VM_CATEGORY");
3862 make_dir(todir
, sizeof(todir
), vmu
->context
, vmu
->mailbox
, "INBOX");
3863 make_file(fn
, sizeof(fn
), todir
, msgnum
);
3864 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", vmu
->mailbox
, vmu
->context
);
3866 if (!ast_strlen_zero(vmu
->attachfmt
)) {
3867 if (strstr(fmt
, vmu
->attachfmt
)) {
3868 fmt
= vmu
->attachfmt
;
3870 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
);
3874 /* Attach only the first format */
3875 fmt
= ast_strdupa(fmt
);
3877 strsep(&stringp
, "|");
3879 if (!ast_strlen_zero(vmu
->email
)) {
3880 int attach_user_voicemail
= ast_test_flag((&globalflags
), VM_ATTACH
);
3881 char *myserveremail
= serveremail
;
3882 attach_user_voicemail
= ast_test_flag(vmu
, VM_ATTACH
);
3883 if (!ast_strlen_zero(vmu
->serveremail
))
3884 myserveremail
= vmu
->serveremail
;
3885 /*XXX possible imap issue, should category be NULL XXX*/
3886 sendmail(myserveremail
, vmu
, msgnum
, vmu
->context
, vmu
->mailbox
, cidnum
, cidname
, fn
, fmt
, duration
, attach_user_voicemail
, chan
, category
);
3889 if (!ast_strlen_zero(vmu
->pager
)) {
3890 char *myserveremail
= serveremail
;
3891 if (!ast_strlen_zero(vmu
->serveremail
))
3892 myserveremail
= vmu
->serveremail
;
3893 sendpage(myserveremail
, vmu
->pager
, msgnum
, vmu
->context
, vmu
->mailbox
, cidnum
, cidname
, duration
, vmu
, category
);
3896 if (ast_test_flag(vmu
, VM_DELETE
)) {
3897 DELETE(todir
, msgnum
, fn
);
3901 DELETE(todir
, msgnum
, fn
);
3903 /* Leave voicemail for someone */
3904 if (ast_app_has_voicemail(ext_context
, NULL
)) {
3905 ast_app_inboxcount(ext_context
, &newmsgs
, &oldmsgs
);
3907 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
);
3908 run_externnotify(vmu
->context
, vmu
->mailbox
);
3912 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
)
3916 char *header_content
;
3922 char username
[70]="";
3923 int res
= 0, cmd
= 0;
3924 struct ast_vm_user
*receiver
= NULL
, *vmtmp
;
3925 AST_LIST_HEAD_NOLOCK_STATIC(extensions
, ast_vm_user
);
3928 int saved_messages
= 0, found
= 0;
3929 int valid_extensions
= 0;
3933 if (vms
== NULL
) return -1;
3935 curmsg
= vms
->curmsg
;
3937 while (!res
&& !valid_extensions
) {
3938 int use_directory
= 0;
3939 if (ast_test_flag((&globalflags
), VM_DIRECFORWARD
)) {
3943 while ((cmd
>= 0) && !done
){
3960 /* Press 1 to enter an extension press 2 to use the directory */
3961 cmd
= ast_play_and_wait(chan
,"vm-forward");
3963 cmd
= ast_waitfordigit(chan
,3000);
3974 if (cmd
< 0 || cmd
== 't')
3978 if (use_directory
) {
3979 /* use app_directory */
3981 char old_context
[sizeof(chan
->context
)];
3982 char old_exten
[sizeof(chan
->exten
)];
3984 struct ast_app
* app
;
3987 app
= pbx_findapp("Directory");
3989 char vmcontext
[256];
3990 /* make backup copies */
3991 memcpy(old_context
, chan
->context
, sizeof(chan
->context
));
3992 memcpy(old_exten
, chan
->exten
, sizeof(chan
->exten
));
3993 old_priority
= chan
->priority
;
3995 /* call the the Directory, changes the channel */
3996 sprintf(vmcontext
, "%s||v", context
? context
: "default");
3997 res
= pbx_exec(chan
, app
, vmcontext
);
3999 ast_copy_string(username
, chan
->exten
, sizeof(username
));
4001 /* restore the old context, exten, and priority */
4002 memcpy(chan
->context
, old_context
, sizeof(chan
->context
));
4003 memcpy(chan
->exten
, old_exten
, sizeof(chan
->exten
));
4004 chan
->priority
= old_priority
;
4007 ast_log(LOG_WARNING
, "Could not find the Directory application, disabling directory_forward\n");
4008 ast_clear_flag((&globalflags
), VM_DIRECFORWARD
);
4011 /* Ask for an extension */
4012 res
= ast_streamfile(chan
, "vm-extension", chan
->language
); /* "extension" */
4015 if ((res
= ast_readstring(chan
, username
, sizeof(username
) - 1, 2000, 10000, "#") < 0))
4019 /* start all over if no username */
4020 if (ast_strlen_zero(username
))
4023 s
= strsep(&stringp
, "*");
4024 /* start optimistic */
4025 valid_extensions
= 1;
4027 /* Don't forward to ourselves. find_user is going to malloc since we have a NULL as first argument */
4028 if (strcmp(s
,sender
->mailbox
) && (receiver
= find_user(NULL
, context
, s
))) {
4029 AST_LIST_INSERT_HEAD(&extensions
, receiver
, list
);
4032 valid_extensions
= 0;
4035 s
= strsep(&stringp
, "*");
4037 /* break from the loop of reading the extensions */
4038 if (valid_extensions
)
4040 /* "I am sorry, that's not a valid extension. Please try again." */
4041 res
= ast_play_and_wait(chan
, "pbx-invalid");
4043 /* check if we're clear to proceed */
4044 if (AST_LIST_EMPTY(&extensions
) || !valid_extensions
)
4047 struct leave_vm_options leave_options
;
4048 char mailbox
[AST_MAX_EXTENSION
* 2 + 2];
4049 snprintf(mailbox
, sizeof(mailbox
), "%s@%s", username
, context
);
4051 /* Send VoiceMail */
4052 memset(&leave_options
, 0, sizeof(leave_options
));
4053 leave_options
.record_gain
= record_gain
;
4054 cmd
= leave_voicemail(chan
, mailbox
, &leave_options
);
4057 /* Forward VoiceMail */
4060 RETRIEVE(dir
, curmsg
);
4061 cmd
= vm_forwardoptions(chan
, sender
, dir
, curmsg
, vmfmts
, S_OR(context
, "default"), record_gain
, &duration
, vms
);
4063 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions
, vmtmp
, list
) {
4065 /* Need to get message content */
4066 if(option_debug
> 2)
4067 ast_log (LOG_DEBUG
,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms
->curmsg
, vms
->msgArray
[vms
->curmsg
]);
4068 if (vms
->msgArray
[vms
->curmsg
] == 0) {
4069 ast_log (LOG_WARNING
,"Trying to access unknown message\n");
4073 /* This will only work for new messages... */
4074 header_content
= mail_fetchheader (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
]);
4075 /* empty string means no valid header */
4076 if (ast_strlen_zero(header_content
)) {
4077 ast_log (LOG_ERROR
,"Could not fetch header for message number %ld\n",vms
->msgArray
[vms
->curmsg
]);
4080 /* Get header info needed by sendmail */
4081 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Duration:");
4083 duration
= atoi(temp
);
4087 /* Attach only the first format */
4088 fmt
= ast_strdupa(fmt
);
4091 strsep(&stringp
, "|");
4093 ast_log (LOG_ERROR
,"audio format not set. Default to WAV\n");
4096 if (!strcasecmp(fmt
, "wav49"))
4098 if(option_debug
> 2)
4099 ast_log (LOG_DEBUG
,"**** format set to %s, vmfmts set to %s\n",fmt
,vmfmts
);
4100 /* ast_copy_string(fmt, vmfmts, sizeof(fmt));*/
4101 /* if (!ast_strlen_zero(fmt)) { */
4102 snprintf(todir
, sizeof(todir
), "%s%s/%s/tmp", VM_SPOOL_DIR
, vmtmp
->context
, vmtmp
->mailbox
);
4103 make_gsm_file(vms
->fn
, vms
->imapuser
, todir
, vms
->curmsg
);
4104 if(option_debug
> 2)
4105 ast_log (LOG_DEBUG
,"Before mail_fetchstructure, message number is %ld, filename is:%s\n",vms
->msgArray
[vms
->curmsg
], vms
->fn
);
4106 /*mail_fetchstructure (mailstream, vmArray[0], &body); */
4107 mail_fetchstructure (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
], &body
);
4108 save_body(body
,vms
,"3","gsm");
4109 /* should not assume "fmt" here! */
4110 save_body(body
,vms
,"2",fmt
);
4112 STORE(todir
, vmtmp
->mailbox
, vmtmp
->context
, vms
->curmsg
, chan
, vmtmp
, fmt
, duration
, vms
);
4114 char *myserveremail
= serveremail
;
4115 if (!ast_strlen_zero(vmtmp
->serveremail
))
4116 myserveremail
= vmtmp
->serveremail
;
4117 int attach_user_voicemail
= ast_test_flag((&globalflags
), VM_ATTACH
);
4118 attach_user_voicemail
= ast_test_flag(vmtmp
, VM_ATTACH
);
4119 /* NULL category for IMAP storage */
4120 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
);
4122 copy_message(chan
, sender
, 0, curmsg
, duration
, vmtmp
, fmt
);
4125 AST_LIST_REMOVE_CURRENT(&extensions
, list
);
4130 AST_LIST_TRAVERSE_SAFE_END
;
4131 if (saved_messages
> 0) {
4132 /* give confirmation that the message was saved */
4133 /* commented out since we can't forward batches yet
4134 if (saved_messages == 1)
4135 res = ast_play_and_wait(chan, "vm-message");
4137 res = ast_play_and_wait(chan, "vm-messages");
4139 res = ast_play_and_wait(chan, "vm-saved"); */
4140 res
= ast_play_and_wait(chan
, "vm-msgsaved");
4144 return res
? res
: cmd
;
4147 static int wait_file2(struct ast_channel
*chan
, struct vm_state
*vms
, char *file
)
4150 if ((res
= ast_stream_and_wait(chan
, file
, chan
->language
, AST_DIGIT_ANY
)) < 0)
4151 ast_log(LOG_WARNING
, "Unable to play message %s\n", file
);
4155 static int wait_file(struct ast_channel
*chan
, struct vm_state
*vms
, char *file
)
4157 return ast_control_streamfile(chan
, file
, "#", "*", "1456789", "0", "2", skipms
);
4160 static int play_message_category(struct ast_channel
*chan
, const char *category
)
4164 if (!ast_strlen_zero(category
))
4165 res
= ast_play_and_wait(chan
, category
);
4168 ast_log(LOG_WARNING
, "No sound file for category '%s' was found.\n", category
);
4175 static int play_message_datetime(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, const char *origtime
, const char *filename
)
4178 struct vm_zone
*the_zone
= NULL
;
4181 if (ast_get_time_t(origtime
, &t
, 0, NULL
)) {
4182 ast_log(LOG_WARNING
, "Couldn't find origtime in %s\n", filename
);
4186 /* Does this user have a timezone specified? */
4187 if (!ast_strlen_zero(vmu
->zonetag
)) {
4188 /* Find the zone in the list */
4190 AST_LIST_LOCK(&zones
);
4191 AST_LIST_TRAVERSE(&zones
, z
, list
) {
4192 if (!strcmp(z
->name
, vmu
->zonetag
)) {
4197 AST_LIST_UNLOCK(&zones
);
4200 /* No internal variable parsing for now, so we'll comment it out for the time being */
4202 /* Set the DIFF_* variables */
4203 localtime_r(&t
, &time_now
);
4204 tv_now
= ast_tvnow();
4205 tnow
= tv_now
.tv_sec
;
4206 localtime_r(&tnow
,&time_then
);
4208 /* Day difference */
4209 if (time_now
.tm_year
== time_then
.tm_year
)
4210 snprintf(temp
,sizeof(temp
),"%d",time_now
.tm_yday
);
4212 snprintf(temp
,sizeof(temp
),"%d",(time_now
.tm_year
- time_then
.tm_year
) * 365 + (time_now
.tm_yday
- time_then
.tm_yday
));
4213 pbx_builtin_setvar_helper(chan
, "DIFF_DAY", temp
);
4215 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
4218 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, the_zone
->msg_format
, the_zone
->timezone
);
4219 else if (!strcasecmp(chan
->language
,"pl")) /* POLISH syntax */
4220 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Q HM", NULL
);
4221 else if (!strcasecmp(chan
->language
,"se")) /* SWEDISH syntax */
4222 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' dB 'digits/at' k 'and' M", NULL
);
4223 else if (!strcasecmp(chan
->language
,"no")) /* NORWEGIAN syntax */
4224 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Q 'digits/at' HM", NULL
);
4225 else if (!strcasecmp(chan
->language
,"de")) /* GERMAN syntax */
4226 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Q 'digits/at' HM", NULL
);
4227 else if (!strcasecmp(chan
->language
,"nl")) /* DUTCH syntax */
4228 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' q 'digits/nl-om' HM", NULL
);
4229 else if (!strcasecmp(chan
->language
,"it")) /* ITALIAN syntax */
4230 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
);
4231 else if (!strcasecmp(chan
->language
,"gr"))
4232 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' q H 'digits/kai' M ", NULL
);
4234 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' q 'digits/at' IMp", NULL
);
4236 pbx_builtin_setvar_helper(chan
, "DIFF_DAY", NULL
);
4243 static int play_message_callerid(struct ast_channel
*chan
, struct vm_state
*vms
, char *cid
, const char *context
, int callback
)
4247 char *callerid
, *name
;
4248 char prefile
[256]="";
4251 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
4252 /* 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 */
4253 if ((cid
== NULL
)||(context
== NULL
))
4256 /* Strip off caller ID number from name */
4257 ast_log(LOG_DEBUG
, "VM-CID: composite caller ID received: %s, context: %s\n", cid
, context
);
4258 ast_callerid_parse(cid
, &name
, &callerid
);
4259 if ((!ast_strlen_zero(callerid
)) && strcmp(callerid
, "Unknown")) {
4260 /* Check for internal contexts and only */
4261 /* say extension when the call didn't come from an internal context in the list */
4262 for (i
= 0 ; i
< MAX_NUM_CID_CONTEXTS
; i
++){
4263 ast_log(LOG_DEBUG
, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts
[i
]);
4264 if ((strcmp(cidinternalcontexts
[i
], context
) == 0))
4267 if (i
!= MAX_NUM_CID_CONTEXTS
){ /* internal context? */
4269 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/greet", VM_SPOOL_DIR
, context
, callerid
);
4270 if (!ast_strlen_zero(prefile
)) {
4271 /* See if we can find a recorded name for this person instead of their extension number */
4272 if (ast_fileexists(prefile
, NULL
, NULL
) > 0) {
4273 if (option_verbose
> 2)
4274 ast_verbose(VERBOSE_PREFIX_3
"Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid
);
4276 res
= wait_file2(chan
, vms
, "vm-from");
4277 res
= ast_stream_and_wait(chan
, prefile
, chan
->language
, "");
4279 if (option_verbose
> 2)
4280 ast_verbose(VERBOSE_PREFIX_3
"Playing envelope info: message from '%s'\n", callerid
);
4281 /* BB: Say "from extension" as one saying to sound smoother */
4283 res
= wait_file2(chan
, vms
, "vm-from-extension");
4284 res
= ast_say_digit_str(chan
, callerid
, "", chan
->language
);
4291 ast_log(LOG_DEBUG
, "VM-CID: Numeric caller id: (%s)\n",callerid
);
4292 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
4294 res
= wait_file2(chan
, vms
, "vm-from-phonenumber");
4295 res
= ast_say_digit_str(chan
, callerid
, AST_DIGIT_ANY
, chan
->language
);
4298 /* Number unknown */
4299 ast_log(LOG_DEBUG
, "VM-CID: From an unknown number\n");
4300 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
4301 res
= wait_file2(chan
, vms
, "vm-unknown-caller");
4306 static int play_message_duration(struct ast_channel
*chan
, struct vm_state
*vms
, const char *duration
, int minduration
)
4311 /* Verify that we have a duration for the message */
4312 if (duration
== NULL
)
4315 /* Convert from seconds to minutes */
4316 durations
=atoi(duration
);
4317 durationm
=(durations
/ 60);
4319 ast_log(LOG_DEBUG
, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations
, durationm
);
4321 if ((!res
) && (durationm
>= minduration
)) {
4322 res
= wait_file2(chan
, vms
, "vm-duration");
4325 if (!strcasecmp(chan
->language
, "pl")) {
4326 div_t num
= div(durationm
, 10);
4328 if (durationm
== 1) {
4329 res
= ast_play_and_wait(chan
, "digits/1z");
4330 res
= res
? res
: ast_play_and_wait(chan
, "vm-minute-ta");
4331 } else if (num
.rem
> 1 && num
.rem
< 5 && num
.quot
!= 1) {
4334 res
= ast_play_and_wait(chan
, "digits/2-ie");
4336 res
= say_and_wait(chan
, durationm
- 2 , chan
->language
);
4337 res
= res
? res
: ast_play_and_wait(chan
, "digits/2-ie");
4340 res
= say_and_wait(chan
, durationm
, chan
->language
);
4342 res
= res
? res
: ast_play_and_wait(chan
, "vm-minute-ty");
4344 res
= say_and_wait(chan
, durationm
, chan
->language
);
4345 res
= res
? res
: ast_play_and_wait(chan
, "vm-minute-t");
4347 /* DEFAULT syntax */
4349 res
= ast_say_number(chan
, durationm
, AST_DIGIT_ANY
, chan
->language
, NULL
);
4350 res
= wait_file2(chan
, vms
, "vm-minutes");
4357 static int play_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
)
4360 char *header_content
;
4371 if(option_debug
> 2)
4372 ast_log (LOG_DEBUG
,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms
->curmsg
, vms
->msgArray
[vms
->curmsg
]);
4373 if (vms
->msgArray
[vms
->curmsg
] == 0) {
4374 ast_log (LOG_WARNING
,"Trying to access unknown message\n");
4378 /* This will only work for new messages... */
4379 header_content
= mail_fetchheader (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
]);
4380 /* empty string means no valid header */
4381 if (ast_strlen_zero(header_content
)) {
4382 ast_log (LOG_ERROR
,"Could not fetch header for message number %ld\n",vms
->msgArray
[vms
->curmsg
]);
4385 snprintf(todir
, sizeof(todir
), "%s%s/%s/tmp", VM_SPOOL_DIR
, vmu
->context
, vmu
->mailbox
);
4386 make_gsm_file(vms
->fn
, vms
->imapuser
, todir
, vms
->curmsg
);
4388 mail_fetchstructure (vms
->mailstream
,vms
->msgArray
[vms
->curmsg
],&body
);
4389 save_body(body
,vms
,"3","gsm");
4391 adsi_message(chan
, vms
);
4393 res
= wait_file2(chan
, vms
, "vm-first"); /* "First" */
4394 else if (vms
->curmsg
== vms
->lastmsg
)
4395 res
= wait_file2(chan
, vms
, "vm-last"); /* "last" */
4397 res
= wait_file2(chan
, vms
, "vm-message"); /* "message" */
4398 if (vms
->curmsg
&& (vms
->curmsg
!= vms
->lastmsg
)) {
4400 res
= ast_say_number(chan
, vms
->curmsg
+ 1, AST_DIGIT_ANY
, chan
->language
, (char *) NULL
);
4404 /* Get info from headers!! */
4405 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Caller-ID-Num:");
4408 ast_copy_string(cid
, temp
, sizeof(cid
));
4412 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Context:");
4415 ast_copy_string(context
, temp
, sizeof(context
));
4419 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Orig-time:");
4422 ast_copy_string(origtime
, temp
, sizeof(origtime
));
4426 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Duration:");
4429 ast_copy_string(duration
,temp
, sizeof(duration
));
4433 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Category:");
4436 ast_copy_string(category
,temp
, sizeof(category
));
4440 /*if (!strncasecmp("macro",context,5)) Macro names in contexts are useless for our needs */
4441 /* context = ast_variable_retrieve(msg_cfg, "message","macrocontext"); */
4445 if ((!res
) && !ast_strlen_zero(category
)) {
4446 res
= play_message_category(chan
, category
);
4449 if ((!res
) && (ast_test_flag(vmu
, VM_ENVELOPE
)) && origtime
[0] != '\0')
4450 res
= play_message_datetime(chan
, vmu
, origtime
, "IMAP_STORAGE");
4451 if ((!res
) && (ast_test_flag(vmu
, VM_SAYCID
)) && cid
[0] !='\0' && context
[0] !='\0')
4452 res
= play_message_callerid(chan
, vms
, cid
, context
, 0);
4454 if ((!res
) && (ast_test_flag(vmu
, VM_SAYDURATION
)) && duration
[0] != '\0')
4455 res
= play_message_duration(chan
, vms
, duration
, vmu
->saydurationm
);
4457 /* Allow pressing '1' to skip envelope / callerid */
4461 /*ast_config_destroy(msg_cfg);*/
4465 vms
->heard
[vms
->curmsg
] = 1;
4466 res
= wait_file(chan
, vms
, vms
->fn
);
4468 DISPOSE(vms
->curdir
, vms
->curmsg
);
4469 DELETE(0, 0, vms
->fn
);
4473 static int play_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
)
4476 char filename
[256], *cid
;
4477 const char *origtime
, *context
, *category
, *duration
;
4478 struct ast_config
*msg_cfg
;
4481 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, vms
->curmsg
);
4482 adsi_message(chan
, vms
);
4484 res
= wait_file2(chan
, vms
, "vm-first"); /* "First" */
4485 else if (vms
->curmsg
== vms
->lastmsg
)
4486 res
= wait_file2(chan
, vms
, "vm-last"); /* "last" */
4489 if (!strcasecmp(chan
->language
, "pl")) {
4490 if (vms
->curmsg
&& (vms
->curmsg
!= vms
->lastmsg
)) {
4493 ten
= (vms
->curmsg
+ 1) / 10;
4494 one
= (vms
->curmsg
+ 1) % 10;
4496 if (vms
->curmsg
< 20) {
4497 snprintf(nextmsg
, sizeof(nextmsg
), "digits/n-%d", vms
->curmsg
+ 1);
4498 res
= wait_file2(chan
, vms
, nextmsg
);
4500 snprintf(nextmsg
, sizeof(nextmsg
), "digits/n-%d", ten
* 10);
4501 res
= wait_file2(chan
, vms
, nextmsg
);
4504 snprintf(nextmsg
, sizeof(nextmsg
), "digits/n-%d", one
);
4505 res
= wait_file2(chan
, vms
, nextmsg
);
4511 res
= wait_file2(chan
, vms
, "vm-message");
4513 if (!strcasecmp(chan
->language
, "se")) /* SWEDISH syntax */
4514 res
= wait_file2(chan
, vms
, "vm-meddelandet"); /* "message" */
4515 else /* DEFAULT syntax */
4516 res
= wait_file2(chan
, vms
, "vm-message");
4517 if (vms
->curmsg
&& (vms
->curmsg
!= vms
->lastmsg
)) {
4519 res
= ast_say_number(chan
, vms
->curmsg
+ 1, AST_DIGIT_ANY
, chan
->language
, NULL
);
4524 /* Retrieve info from VM attribute file */
4525 make_file(vms
->fn2
, sizeof(vms
->fn2
), vms
->curdir
, vms
->curmsg
);
4526 snprintf(filename
, sizeof(filename
), "%s.txt", vms
->fn2
);
4527 RETRIEVE(vms
->curdir
, vms
->curmsg
);
4528 msg_cfg
= ast_config_load(filename
);
4530 ast_log(LOG_WARNING
, "No message attribute file?!! (%s)\n", filename
);
4534 if (!(origtime
= ast_variable_retrieve(msg_cfg
, "message", "origtime"))) {
4535 ast_log(LOG_WARNING
, "No origtime?!\n");
4536 DISPOSE(vms
->curdir
, vms
->curmsg
);
4537 ast_config_destroy(msg_cfg
);
4541 cid
= ast_strdupa(ast_variable_retrieve(msg_cfg
, "message", "callerid"));
4542 duration
= ast_variable_retrieve(msg_cfg
, "message", "duration");
4543 category
= ast_variable_retrieve(msg_cfg
, "message", "category");
4545 context
= ast_variable_retrieve(msg_cfg
, "message", "context");
4546 if (!strncasecmp("macro",context
,5)) /* Macro names in contexts are useless for our needs */
4547 context
= ast_variable_retrieve(msg_cfg
, "message","macrocontext");
4549 res
= play_message_category(chan
, category
);
4550 if ((!res
) && (ast_test_flag(vmu
, VM_ENVELOPE
)))
4551 res
= play_message_datetime(chan
, vmu
, origtime
, filename
);
4552 if ((!res
) && (ast_test_flag(vmu
, VM_SAYCID
)))
4553 res
= play_message_callerid(chan
, vms
, cid
, context
, 0);
4554 if ((!res
) && (ast_test_flag(vmu
, VM_SAYDURATION
)))
4555 res
= play_message_duration(chan
, vms
, duration
, vmu
->saydurationm
);
4556 /* Allow pressing '1' to skip envelope / callerid */
4559 ast_config_destroy(msg_cfg
);
4562 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, vms
->curmsg
);
4563 vms
->heard
[vms
->curmsg
] = 1;
4564 res
= wait_file(chan
, vms
, vms
->fn
);
4566 DISPOSE(vms
->curdir
, vms
->curmsg
);
4572 static void imap_mailbox_name(char *spec
, struct vm_state
*vms
, int box
, int use_folder
)
4577 ast_copy_string(vms
->curbox
, mbox(0), sizeof(vms
->curbox
));
4578 sprintf(vms
->vmbox
, "vm-%s", mbox(1));
4580 ast_copy_string(vms
->curbox
, mbox(box
), sizeof(vms
->curbox
));
4581 snprintf(vms
->vmbox
, sizeof(vms
->vmbox
), "vm-%s", vms
->curbox
);
4584 if (strlen(authuser
) > 0) {
4585 snprintf(tmp
, sizeof(tmp
), "{%s:%s/imap/authuser=%s/%s/user=%s}",imapserver
,imapport
,authuser
,imapflags
,vms
->imapuser
);
4587 snprintf(tmp
, sizeof(tmp
), "{%s:%s/imap/%s/user=%s}",imapserver
,imapport
,imapflags
,vms
->imapuser
);
4589 if(box
== 0 || box
== 1)
4590 sprintf(spec
, "%s%s", tmp
, use_folder
? imapfolder
: "INBOX");
4592 sprintf(spec
, "%s%s%c%s", tmp
, imapfolder
, delimiter
, mbox(box
));
4595 static int init_mailstream(struct vm_state
*vms
, int box
)
4597 MAILSTREAM
*stream
= NIL
;
4602 ast_log (LOG_ERROR
,"vm_state is NULL!\n");
4605 if(option_debug
> 2)
4606 ast_log (LOG_DEBUG
,"vm_state user is:%s\n",vms
->imapuser
);
4607 if (vms
->mailstream
== NIL
|| !vms
->mailstream
) {
4608 ast_log (LOG_DEBUG
,"mailstream not set.\n");
4610 stream
= vms
->mailstream
;
4612 /* debug = T; user wants protocol telemetry? */
4613 debug
= NIL
; /* NO protocol telemetry? */
4615 if (delimiter
== '\0') { /* did not probe the server yet */
4617 #include "linkage.c"
4618 /* Connect to INBOX first to get folders delimiter */
4619 imap_mailbox_name(tmp
, vms
, 0, 0);
4620 stream
= mail_open (stream
, tmp
, debug
? OP_DEBUG
: NIL
);
4621 if (stream
== NIL
) {
4622 ast_log (LOG_ERROR
, "Can't connect to imap server %s\n", tmp
);
4625 get_mailbox_delimiter(stream
);
4626 /* update delimiter in imapfolder */
4627 for(cp
= imapfolder
; *cp
; cp
++)
4631 /* Now connect to the target folder */
4632 imap_mailbox_name(tmp
, vms
, box
, 1);
4633 if(option_debug
> 2)
4634 ast_log (LOG_DEBUG
,"Before mail_open, server: %s, box:%d\n", tmp
, box
);
4635 vms
->mailstream
= mail_open (stream
, tmp
, debug
? OP_DEBUG
: NIL
);
4636 if (vms
->mailstream
== NIL
) {
4643 static int open_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
, int box
)
4650 ast_copy_string(vms
->imapuser
,vmu
->imapuser
, sizeof(vms
->imapuser
));
4651 if(option_debug
> 2)
4652 ast_log(LOG_DEBUG
,"Before init_mailstream, user is %s\n",vmu
->imapuser
);
4653 ret
= init_mailstream(vms
, box
);
4654 if (ret
!= 0 || !vms
->mailstream
) {
4655 ast_log (LOG_ERROR
,"Could not initialize mailstream\n");
4659 /* Check Quota (here for now to test) */
4660 mail_parameters(NULL
, SET_QUOTA
, (void *) mm_parsequota
);
4661 imap_mailbox_name(dbox
, vms
, box
, 1);
4662 imap_getquotaroot(vms
->mailstream
, dbox
);
4664 pgm
= mail_newsearchpgm();
4666 /* Check IMAP folder for Asterisk messages only... */
4667 hdr
= mail_newsearchheader ("X-Asterisk-VM-Extension", vmu
->mailbox
);
4672 /* if box = 0, check for new, if box = 1, check for read */
4676 } else if (box
== 1) {
4681 vms
->vmArrayIndex
= 0;
4682 if(option_debug
> 2)
4683 ast_log(LOG_DEBUG
,"Before mail_search_full, user is %s\n",vmu
->imapuser
);
4684 mail_search_full (vms
->mailstream
, NULL
, pgm
, NIL
);
4687 vms
->lastmsg
= vms
->vmArrayIndex
- 1;
4689 mail_free_searchpgm(&pgm
);
4693 static int open_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
,int box
)
4696 int count_msg
, last_msg
;
4698 ast_copy_string(vms
->curbox
, mbox(box
), sizeof(vms
->curbox
));
4700 /* Rename the member vmbox HERE so that we don't try to return before
4701 * we know what's going on.
4703 snprintf(vms
->vmbox
, sizeof(vms
->vmbox
), "vm-%s", vms
->curbox
);
4705 /* Faster to make the directory than to check if it exists. */
4706 create_dirpath(vms
->curdir
, sizeof(vms
->curdir
), vmu
->context
, vms
->username
, vms
->curbox
);
4708 count_msg
= count_messages(vmu
, vms
->curdir
);
4712 vms
->lastmsg
= count_msg
- 1;
4715 The following test is needed in case sequencing gets messed up.
4716 There appears to be more than one way to mess up sequence, so
4717 we will not try to find all of the root causes--just fix it when
4721 last_msg
= last_message_index(vmu
, vms
->curdir
);
4724 else if (vms
->lastmsg
!= last_msg
)
4726 ast_log(LOG_NOTICE
, "Resequencing Mailbox: %s\n", vms
->curdir
);
4727 res
= resequence_mailbox(vmu
, vms
->curdir
);
4736 static int close_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
)
4739 #ifndef IMAP_STORAGE
4740 int res
= 0, nummsg
;
4743 if (vms
->lastmsg
<= -1)
4747 #ifndef IMAP_STORAGE
4748 /* Get the deleted messages fixed */
4749 if (vm_lock_path(vms
->curdir
))
4750 return ERROR_LOCK_PATH
;
4752 for (x
= 0; x
< vmu
->maxmsg
; x
++) {
4753 if (!vms
->deleted
[x
] && (strcasecmp(vms
->curbox
, "INBOX") || !vms
->heard
[x
])) {
4754 /* Save this message. It's not in INBOX or hasn't been heard */
4755 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, x
);
4756 if (!EXISTS(vms
->curdir
, x
, vms
->fn
, NULL
))
4759 make_file(vms
->fn2
, sizeof(vms
->fn2
), vms
->curdir
, vms
->curmsg
);
4760 if (strcmp(vms
->fn
, vms
->fn2
)) {
4761 RENAME(vms
->curdir
, x
, vmu
->mailbox
,vmu
->context
, vms
->curdir
, vms
->curmsg
, vms
->fn
, vms
->fn2
);
4763 } else if (!strcasecmp(vms
->curbox
, "INBOX") && vms
->heard
[x
] && !vms
->deleted
[x
]) {
4764 /* Move to old folder before deleting */
4765 res
= save_to_folder(vmu
, vms
, x
, 1);
4766 if (res
== ERROR_LOCK_PATH
) {
4767 /* If save failed do not delete the message */
4768 vms
->deleted
[x
] = 0;
4775 /* Delete ALL remaining messages */
4777 for (x
= vms
->curmsg
+ 1; x
<= nummsg
; x
++) {
4778 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, x
);
4779 if (EXISTS(vms
->curdir
, x
, vms
->fn
, NULL
))
4780 DELETE(vms
->curdir
, x
, vms
->fn
);
4782 ast_unlock_path(vms
->curdir
);
4784 for (x
=0;x
< vmu
->maxmsg
;x
++) {
4785 if (vms
->deleted
[x
]) {
4786 if(option_debug
> 2)
4787 ast_log(LOG_DEBUG
,"IMAP delete of %d\n",x
);
4788 IMAP_DELETE(vms
->curdir
, x
, vms
->fn
, vms
);
4795 memset(vms
->deleted
, 0, vmu
->maxmsg
* sizeof(int));
4797 memset(vms
->heard
, 0, vmu
->maxmsg
* sizeof(int));
4802 /* In Greek even though we CAN use a syntax like "friends messages"
4803 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
4804 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
4805 * syntax for the above three categories which is more elegant.
4808 static int vm_play_folder_name_gr(struct ast_channel
*chan
, char *mbox
)
4813 buf
= alloca(strlen(mbox
)+2);
4817 if (!strcasecmp(mbox
, "vm-INBOX") || !strcasecmp(mbox
, "vm-Old")){
4818 cmd
= ast_play_and_wait(chan
, buf
); /* "NEA / PALIA" */
4819 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages"); /* "messages" -> "MYNHMATA" */
4821 cmd
= ast_play_and_wait(chan
, "vm-messages"); /* "messages" -> "MYNHMATA" */
4822 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
4826 static int vm_play_folder_name_pl(struct ast_channel
*chan
, char *mbox
)
4830 if (!strcasecmp(mbox
, "vm-INBOX") || !strcasecmp(mbox
, "vm-Old")) {
4831 if (!strcasecmp(mbox
, "vm-INBOX"))
4832 cmd
= ast_play_and_wait(chan
, "vm-new-e");
4834 cmd
= ast_play_and_wait(chan
, "vm-old-e");
4835 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages");
4837 cmd
= ast_play_and_wait(chan
, "vm-messages");
4838 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
);
4842 static int vm_play_folder_name(struct ast_channel
*chan
, char *mbox
)
4846 if (!strcasecmp(chan
->language
, "it") || !strcasecmp(chan
->language
, "es") || !strcasecmp(chan
->language
, "fr") || !strcasecmp(chan
->language
, "pt")) { /* Italian, Spanish, French or Portuguese syntax */
4847 cmd
= ast_play_and_wait(chan
, "vm-messages"); /* "messages */
4848 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
);
4849 } else if (!strcasecmp(chan
->language
, "gr")){
4850 return vm_play_folder_name_gr(chan
, mbox
);
4851 } else if (!strcasecmp(chan
->language
, "pl")){
4852 return vm_play_folder_name_pl(chan
, mbox
);
4853 } else { /* Default English */
4854 cmd
= ast_play_and_wait(chan
, mbox
);
4855 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages"); /* "messages */
4860 In greek the plural for old/new is
4861 different so we need the following files
4862 We also need vm-denExeteMynhmata because
4863 this syntax is different.
4865 -> vm-Olds.wav : "Palia"
4866 -> vm-INBOXs.wav : "Nea"
4867 -> vm-denExeteMynhmata : "den exete mynhmata"
4871 static int vm_intro_gr(struct ast_channel
*chan
, struct vm_state
*vms
)
4875 if (vms
->newmessages
) {
4876 res
= ast_play_and_wait(chan
, "vm-youhave");
4878 res
= ast_say_number(chan
, vms
->newmessages
, AST_DIGIT_ANY
, chan
->language
, NULL
);
4880 if ((vms
->newmessages
== 1)) {
4881 res
= ast_play_and_wait(chan
, "vm-INBOX");
4883 res
= ast_play_and_wait(chan
, "vm-message");
4885 res
= ast_play_and_wait(chan
, "vm-INBOXs");
4887 res
= ast_play_and_wait(chan
, "vm-messages");
4890 } else if (vms
->oldmessages
){
4891 res
= ast_play_and_wait(chan
, "vm-youhave");
4893 res
= ast_say_number(chan
, vms
->oldmessages
, AST_DIGIT_ANY
, chan
->language
, NULL
);
4894 if ((vms
->oldmessages
== 1)){
4895 res
= ast_play_and_wait(chan
, "vm-Old");
4897 res
= ast_play_and_wait(chan
, "vm-message");
4899 res
= ast_play_and_wait(chan
, "vm-Olds");
4901 res
= ast_play_and_wait(chan
, "vm-messages");
4903 } else if (!vms
->oldmessages
&& !vms
->newmessages
)
4904 res
= ast_play_and_wait(chan
, "vm-denExeteMynhmata");
4908 /* Default English syntax */
4909 static int vm_intro_en(struct ast_channel
*chan
, struct vm_state
*vms
)
4913 /* Introduce messages they have */
4914 res
= ast_play_and_wait(chan
, "vm-youhave");
4916 if (vms
->newmessages
) {
4917 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
4919 res
= ast_play_and_wait(chan
, "vm-INBOX");
4920 if (vms
->oldmessages
&& !res
)
4921 res
= ast_play_and_wait(chan
, "vm-and");
4923 if ((vms
->newmessages
== 1))
4924 res
= ast_play_and_wait(chan
, "vm-message");
4926 res
= ast_play_and_wait(chan
, "vm-messages");
4930 if (!res
&& vms
->oldmessages
) {
4931 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
4933 res
= ast_play_and_wait(chan
, "vm-Old");
4935 if (vms
->oldmessages
== 1)
4936 res
= ast_play_and_wait(chan
, "vm-message");
4938 res
= ast_play_and_wait(chan
, "vm-messages");
4942 if (!vms
->oldmessages
&& !vms
->newmessages
) {
4943 res
= ast_play_and_wait(chan
, "vm-no");
4945 res
= ast_play_and_wait(chan
, "vm-messages");
4952 /* ITALIAN syntax */
4953 static int vm_intro_it(struct ast_channel
*chan
, struct vm_state
*vms
)
4955 /* Introduce messages they have */
4957 if (!vms
->oldmessages
&& !vms
->newmessages
)
4958 res
= ast_play_and_wait(chan
, "vm-no") ||
4959 ast_play_and_wait(chan
, "vm-message");
4961 res
= ast_play_and_wait(chan
, "vm-youhave");
4962 if (!res
&& vms
->newmessages
) {
4963 res
= (vms
->newmessages
== 1) ?
4964 ast_play_and_wait(chan
, "digits/un") ||
4965 ast_play_and_wait(chan
, "vm-nuovo") ||
4966 ast_play_and_wait(chan
, "vm-message") :
4967 /* 2 or more new messages */
4968 say_and_wait(chan
, vms
->newmessages
, chan
->language
) ||
4969 ast_play_and_wait(chan
, "vm-nuovi") ||
4970 ast_play_and_wait(chan
, "vm-messages");
4971 if (!res
&& vms
->oldmessages
)
4972 res
= ast_play_and_wait(chan
, "vm-and");
4974 if (!res
&& vms
->oldmessages
) {
4975 res
= (vms
->oldmessages
== 1) ?
4976 ast_play_and_wait(chan
, "digits/un") ||
4977 ast_play_and_wait(chan
, "vm-vecchio") ||
4978 ast_play_and_wait(chan
, "vm-message") :
4979 /* 2 or more old messages */
4980 say_and_wait(chan
, vms
->oldmessages
, chan
->language
) ||
4981 ast_play_and_wait(chan
, "vm-vecchi") ||
4982 ast_play_and_wait(chan
, "vm-messages");
4984 return res
? -1 : 0;
4988 static int vm_intro_pl(struct ast_channel
*chan
, struct vm_state
*vms
)
4990 /* Introduce messages they have */
4994 if (!vms
->oldmessages
&& !vms
->newmessages
) {
4995 res
= ast_play_and_wait(chan
, "vm-no");
4996 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
4999 res
= ast_play_and_wait(chan
, "vm-youhave");
5002 if (vms
->newmessages
) {
5003 num
= div(vms
->newmessages
, 10);
5004 if (vms
->newmessages
== 1) {
5005 res
= ast_play_and_wait(chan
, "digits/1-a");
5006 res
= res
? res
: ast_play_and_wait(chan
, "vm-new-a");
5007 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5008 } else if (num
.rem
> 1 && num
.rem
< 5 && num
.quot
!= 1) {
5011 res
= ast_play_and_wait(chan
, "digits/2-ie");
5013 res
= say_and_wait(chan
, vms
->newmessages
- 2 , chan
->language
);
5014 res
= res
? res
: ast_play_and_wait(chan
, "digits/2-ie");
5017 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5019 res
= res
? res
: ast_play_and_wait(chan
, "vm-new-e");
5020 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5022 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5023 res
= res
? res
: ast_play_and_wait(chan
, "vm-new-ych");
5024 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5026 if (!res
&& vms
->oldmessages
)
5027 res
= ast_play_and_wait(chan
, "vm-and");
5029 if (!res
&& vms
->oldmessages
) {
5030 num
= div(vms
->oldmessages
, 10);
5031 if (vms
->oldmessages
== 1) {
5032 res
= ast_play_and_wait(chan
, "digits/1-a");
5033 res
= res
? res
: ast_play_and_wait(chan
, "vm-old-a");
5034 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5035 } else if (num
.rem
> 1 && num
.rem
< 5 && num
.quot
!= 1) {
5038 res
= ast_play_and_wait(chan
, "digits/2-ie");
5040 res
= say_and_wait(chan
, vms
->oldmessages
- 2 , chan
->language
);
5041 res
= res
? res
: ast_play_and_wait(chan
, "digits/2-ie");
5044 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5046 res
= res
? res
: ast_play_and_wait(chan
, "vm-old-e");
5047 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5049 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5050 res
= res
? res
: ast_play_and_wait(chan
, "vm-old-ych");
5051 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5058 /* SWEDISH syntax */
5059 static int vm_intro_se(struct ast_channel
*chan
, struct vm_state
*vms
)
5061 /* Introduce messages they have */
5064 res
= ast_play_and_wait(chan
, "vm-youhave");
5068 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5069 res
= ast_play_and_wait(chan
, "vm-no");
5070 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5074 if (vms
->newmessages
) {
5075 if ((vms
->newmessages
== 1)) {
5076 res
= ast_play_and_wait(chan
, "digits/ett");
5077 res
= res
? res
: ast_play_and_wait(chan
, "vm-nytt");
5078 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5080 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5081 res
= res
? res
: ast_play_and_wait(chan
, "vm-nya");
5082 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5084 if (!res
&& vms
->oldmessages
)
5085 res
= ast_play_and_wait(chan
, "vm-and");
5087 if (!res
&& vms
->oldmessages
) {
5088 if (vms
->oldmessages
== 1) {
5089 res
= ast_play_and_wait(chan
, "digits/ett");
5090 res
= res
? res
: ast_play_and_wait(chan
, "vm-gammalt");
5091 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5093 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5094 res
= res
? res
: ast_play_and_wait(chan
, "vm-gamla");
5095 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5102 /* NORWEGIAN syntax */
5103 static int vm_intro_no(struct ast_channel
*chan
,struct vm_state
*vms
)
5105 /* Introduce messages they have */
5108 res
= ast_play_and_wait(chan
, "vm-youhave");
5112 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5113 res
= ast_play_and_wait(chan
, "vm-no");
5114 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5118 if (vms
->newmessages
) {
5119 if ((vms
->newmessages
== 1)) {
5120 res
= ast_play_and_wait(chan
, "digits/1");
5121 res
= res
? res
: ast_play_and_wait(chan
, "vm-ny");
5122 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5124 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5125 res
= res
? res
: ast_play_and_wait(chan
, "vm-nye");
5126 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5128 if (!res
&& vms
->oldmessages
)
5129 res
= ast_play_and_wait(chan
, "vm-and");
5131 if (!res
&& vms
->oldmessages
) {
5132 if (vms
->oldmessages
== 1) {
5133 res
= ast_play_and_wait(chan
, "digits/1");
5134 res
= res
? res
: ast_play_and_wait(chan
, "vm-gamel");
5135 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5137 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5138 res
= res
? res
: ast_play_and_wait(chan
, "vm-gamle");
5139 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5147 static int vm_intro_de(struct ast_channel
*chan
,struct vm_state
*vms
)
5149 /* Introduce messages they have */
5151 res
= ast_play_and_wait(chan
, "vm-youhave");
5153 if (vms
->newmessages
) {
5154 if ((vms
->newmessages
== 1))
5155 res
= ast_play_and_wait(chan
, "digits/1F");
5157 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5159 res
= ast_play_and_wait(chan
, "vm-INBOX");
5160 if (vms
->oldmessages
&& !res
)
5161 res
= ast_play_and_wait(chan
, "vm-and");
5163 if ((vms
->newmessages
== 1))
5164 res
= ast_play_and_wait(chan
, "vm-message");
5166 res
= ast_play_and_wait(chan
, "vm-messages");
5170 if (!res
&& vms
->oldmessages
) {
5171 if (vms
->oldmessages
== 1)
5172 res
= ast_play_and_wait(chan
, "digits/1F");
5174 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5176 res
= ast_play_and_wait(chan
, "vm-Old");
5178 if (vms
->oldmessages
== 1)
5179 res
= ast_play_and_wait(chan
, "vm-message");
5181 res
= ast_play_and_wait(chan
, "vm-messages");
5185 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5186 res
= ast_play_and_wait(chan
, "vm-no");
5188 res
= ast_play_and_wait(chan
, "vm-messages");
5195 /* SPANISH syntax */
5196 static int vm_intro_es(struct ast_channel
*chan
,struct vm_state
*vms
)
5198 /* Introduce messages they have */
5200 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5201 res
= ast_play_and_wait(chan
, "vm-youhaveno");
5203 res
= ast_play_and_wait(chan
, "vm-messages");
5205 res
= ast_play_and_wait(chan
, "vm-youhave");
5208 if (vms
->newmessages
) {
5210 if ((vms
->newmessages
== 1)) {
5211 res
= ast_play_and_wait(chan
, "digits/1M");
5213 res
= ast_play_and_wait(chan
, "vm-message");
5215 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5217 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5219 res
= ast_play_and_wait(chan
, "vm-messages");
5221 res
= ast_play_and_wait(chan
, "vm-INBOX");
5224 if (vms
->oldmessages
&& !res
)
5225 res
= ast_play_and_wait(chan
, "vm-and");
5227 if (vms
->oldmessages
) {
5229 if (vms
->oldmessages
== 1) {
5230 res
= ast_play_and_wait(chan
, "digits/1M");
5232 res
= ast_play_and_wait(chan
, "vm-message");
5234 res
= ast_play_and_wait(chan
, "vm-Olds");
5236 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5238 res
= ast_play_and_wait(chan
, "vm-messages");
5240 res
= ast_play_and_wait(chan
, "vm-Old");
5249 static int vm_intro_fr(struct ast_channel
*chan
,struct vm_state
*vms
)
5251 /* Introduce messages they have */
5253 res
= ast_play_and_wait(chan
, "vm-youhave");
5255 if (vms
->newmessages
) {
5256 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5258 res
= ast_play_and_wait(chan
, "vm-INBOX");
5259 if (vms
->oldmessages
&& !res
)
5260 res
= ast_play_and_wait(chan
, "vm-and");
5262 if ((vms
->newmessages
== 1))
5263 res
= ast_play_and_wait(chan
, "vm-message");
5265 res
= ast_play_and_wait(chan
, "vm-messages");
5269 if (!res
&& vms
->oldmessages
) {
5270 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5272 if (vms
->oldmessages
== 1)
5273 res
= ast_play_and_wait(chan
, "vm-message");
5275 res
= ast_play_and_wait(chan
, "vm-messages");
5278 res
= ast_play_and_wait(chan
, "vm-Old");
5281 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5282 res
= ast_play_and_wait(chan
, "vm-no");
5284 res
= ast_play_and_wait(chan
, "vm-messages");
5292 static int vm_intro_nl(struct ast_channel
*chan
,struct vm_state
*vms
)
5294 /* Introduce messages they have */
5296 res
= ast_play_and_wait(chan
, "vm-youhave");
5298 if (vms
->newmessages
) {
5299 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5301 if (vms
->newmessages
== 1)
5302 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5304 res
= ast_play_and_wait(chan
, "vm-INBOX");
5306 if (vms
->oldmessages
&& !res
)
5307 res
= ast_play_and_wait(chan
, "vm-and");
5309 if ((vms
->newmessages
== 1))
5310 res
= ast_play_and_wait(chan
, "vm-message");
5312 res
= ast_play_and_wait(chan
, "vm-messages");
5316 if (!res
&& vms
->oldmessages
) {
5317 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5319 if (vms
->oldmessages
== 1)
5320 res
= ast_play_and_wait(chan
, "vm-Olds");
5322 res
= ast_play_and_wait(chan
, "vm-Old");
5325 if (vms
->oldmessages
== 1)
5326 res
= ast_play_and_wait(chan
, "vm-message");
5328 res
= ast_play_and_wait(chan
, "vm-messages");
5332 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5333 res
= ast_play_and_wait(chan
, "vm-no");
5335 res
= ast_play_and_wait(chan
, "vm-messages");
5342 /* PORTUGUESE syntax */
5343 static int vm_intro_pt(struct ast_channel
*chan
,struct vm_state
*vms
)
5345 /* Introduce messages they have */
5347 res
= ast_play_and_wait(chan
, "vm-youhave");
5349 if (vms
->newmessages
) {
5350 res
= ast_say_number(chan
, vms
->newmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
5352 if ((vms
->newmessages
== 1)) {
5353 res
= ast_play_and_wait(chan
, "vm-message");
5355 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5357 res
= ast_play_and_wait(chan
, "vm-messages");
5359 res
= ast_play_and_wait(chan
, "vm-INBOX");
5362 if (vms
->oldmessages
&& !res
)
5363 res
= ast_play_and_wait(chan
, "vm-and");
5365 if (!res
&& vms
->oldmessages
) {
5366 res
= ast_say_number(chan
, vms
->oldmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
5368 if (vms
->oldmessages
== 1) {
5369 res
= ast_play_and_wait(chan
, "vm-message");
5371 res
= ast_play_and_wait(chan
, "vm-Olds");
5373 res
= ast_play_and_wait(chan
, "vm-messages");
5375 res
= ast_play_and_wait(chan
, "vm-Old");
5380 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5381 res
= ast_play_and_wait(chan
, "vm-no");
5383 res
= ast_play_and_wait(chan
, "vm-messages");
5392 /* in czech there must be declension of word new and message
5393 * czech : english : czech : english
5394 * --------------------------------------------------------
5395 * vm-youhave : you have
5396 * vm-novou : one new : vm-zpravu : message
5397 * vm-nove : 2-4 new : vm-zpravy : messages
5398 * vm-novych : 5-infinite new : vm-zprav : messages
5399 * vm-starou : one old
5400 * vm-stare : 2-4 old
5401 * vm-starych : 5-infinite old
5402 * jednu : one - falling 4.
5403 * vm-no : no ( no messages )
5406 static int vm_intro_cz(struct ast_channel
*chan
,struct vm_state
*vms
)
5409 res
= ast_play_and_wait(chan
, "vm-youhave");
5411 if (vms
->newmessages
) {
5412 if (vms
->newmessages
== 1) {
5413 res
= ast_play_and_wait(chan
, "digits/jednu");
5415 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5418 if ((vms
->newmessages
== 1))
5419 res
= ast_play_and_wait(chan
, "vm-novou");
5420 if ((vms
->newmessages
) > 1 && (vms
->newmessages
< 5))
5421 res
= ast_play_and_wait(chan
, "vm-nove");
5422 if (vms
->newmessages
> 4)
5423 res
= ast_play_and_wait(chan
, "vm-novych");
5425 if (vms
->oldmessages
&& !res
)
5426 res
= ast_play_and_wait(chan
, "vm-and");
5428 if ((vms
->newmessages
== 1))
5429 res
= ast_play_and_wait(chan
, "vm-zpravu");
5430 if ((vms
->newmessages
) > 1 && (vms
->newmessages
< 5))
5431 res
= ast_play_and_wait(chan
, "vm-zpravy");
5432 if (vms
->newmessages
> 4)
5433 res
= ast_play_and_wait(chan
, "vm-zprav");
5436 if (!res
&& vms
->oldmessages
) {
5437 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5439 if ((vms
->oldmessages
== 1))
5440 res
= ast_play_and_wait(chan
, "vm-starou");
5441 if ((vms
->oldmessages
) > 1 && (vms
->oldmessages
< 5))
5442 res
= ast_play_and_wait(chan
, "vm-stare");
5443 if (vms
->oldmessages
> 4)
5444 res
= ast_play_and_wait(chan
, "vm-starych");
5447 if ((vms
->oldmessages
== 1))
5448 res
= ast_play_and_wait(chan
, "vm-zpravu");
5449 if ((vms
->oldmessages
) > 1 && (vms
->oldmessages
< 5))
5450 res
= ast_play_and_wait(chan
, "vm-zpravy");
5451 if (vms
->oldmessages
> 4)
5452 res
= ast_play_and_wait(chan
, "vm-zprav");
5456 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5457 res
= ast_play_and_wait(chan
, "vm-no");
5459 res
= ast_play_and_wait(chan
, "vm-zpravy");
5466 static int get_lastdigits(int num
)
5469 return (num
< 20) ? num
: num
% 10;
5472 static int vm_intro_ru(struct ast_channel
*chan
,struct vm_state
*vms
)
5478 res
= ast_play_and_wait(chan
, "vm-youhave");
5479 if (!res
&& vms
->newmessages
) {
5480 lastnum
= get_lastdigits(vms
->newmessages
);
5481 dcnum
= vms
->newmessages
- lastnum
;
5483 res
= say_and_wait(chan
, dcnum
, chan
->language
);
5484 if (!res
&& lastnum
) {
5486 res
= ast_play_and_wait(chan
, "digits/ru/odno");
5488 res
= say_and_wait(chan
, lastnum
, chan
->language
);
5492 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-novoe" : "vm-novyh");
5494 if (!res
&& vms
->oldmessages
)
5495 res
= ast_play_and_wait(chan
, "vm-and");
5498 if (!res
&& vms
->oldmessages
) {
5499 lastnum
= get_lastdigits(vms
->oldmessages
);
5500 dcnum
= vms
->newmessages
- lastnum
;
5502 res
= say_and_wait(chan
, dcnum
, chan
->language
);
5503 if (!res
&& lastnum
) {
5505 res
= ast_play_and_wait(chan
, "digits/ru/odno");
5507 res
= say_and_wait(chan
, lastnum
, chan
->language
);
5511 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-staroe" : "vm-staryh");
5514 if (!res
&& !vms
->newmessages
&& !vms
->oldmessages
) {
5516 res
= ast_play_and_wait(chan
, "vm-no");
5522 res
= ast_play_and_wait(chan
, "vm-soobshenie");
5527 res
= ast_play_and_wait(chan
, "vm-soobsheniya");
5530 res
= ast_play_and_wait(chan
, "vm-soobsheniy");
5539 static int vm_intro(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
)
5543 /* Notify the user that the temp greeting is set and give them the option to remove it */
5544 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
5545 if (ast_test_flag(vmu
, VM_TEMPGREETWARN
)) {
5546 if (ast_fileexists(prefile
, NULL
, NULL
) > 0)
5547 ast_play_and_wait(chan
, "vm-tempgreetactive");
5550 /* Play voicemail intro - syntax is different for different languages */
5551 if (!strcasecmp(chan
->language
, "de")) { /* GERMAN syntax */
5552 return vm_intro_de(chan
, vms
);
5553 } else if (!strcasecmp(chan
->language
, "es")) { /* SPANISH syntax */
5554 return vm_intro_es(chan
, vms
);
5555 } else if (!strcasecmp(chan
->language
, "it")) { /* ITALIAN syntax */
5556 return vm_intro_it(chan
, vms
);
5557 } else if (!strcasecmp(chan
->language
, "fr")) { /* FRENCH syntax */
5558 return vm_intro_fr(chan
, vms
);
5559 } else if (!strcasecmp(chan
->language
, "nl")) { /* DUTCH syntax */
5560 return vm_intro_nl(chan
, vms
);
5561 } else if (!strcasecmp(chan
->language
, "pt")) { /* PORTUGUESE syntax */
5562 return vm_intro_pt(chan
, vms
);
5563 } else if (!strcasecmp(chan
->language
, "cz")) { /* CZECH syntax */
5564 return vm_intro_cz(chan
, vms
);
5565 } else if (!strcasecmp(chan
->language
, "gr")) { /* GREEK syntax */
5566 return vm_intro_gr(chan
, vms
);
5567 } else if (!strcasecmp(chan
->language
, "pl")) { /* POLISH syntax */
5568 return vm_intro_pl(chan
, vms
);
5569 } else if (!strcasecmp(chan
->language
, "se")) { /* SWEDISH syntax */
5570 return vm_intro_se(chan
, vms
);
5571 } else if (!strcasecmp(chan
->language
, "no")) { /* NORWEGIAN syntax */
5572 return vm_intro_no(chan
, vms
);
5573 } else if (!strcasecmp(chan
->language
, "ru")) { /* RUSSIAN syntax */
5574 return vm_intro_ru(chan
, vms
);
5575 } else { /* Default to ENGLISH */
5576 return vm_intro_en(chan
, vms
);
5580 static int vm_instructions(struct ast_channel
*chan
, struct vm_state
*vms
, int skipadvanced
)
5583 /* Play instructions and wait for new command */
5585 if (vms
->starting
) {
5586 if (vms
->lastmsg
> -1) {
5587 res
= ast_play_and_wait(chan
, "vm-onefor");
5589 res
= vm_play_folder_name(chan
, vms
->vmbox
);
5592 res
= ast_play_and_wait(chan
, "vm-opts");
5595 res
= ast_play_and_wait(chan
, "vm-prev");
5596 if (!res
&& !skipadvanced
)
5597 res
= ast_play_and_wait(chan
, "vm-advopts");
5599 res
= ast_play_and_wait(chan
, "vm-repeat");
5600 if (!res
&& (vms
->curmsg
!= vms
->lastmsg
))
5601 res
= ast_play_and_wait(chan
, "vm-next");
5603 if (!vms
->deleted
[vms
->curmsg
])
5604 res
= ast_play_and_wait(chan
, "vm-delete");
5606 res
= ast_play_and_wait(chan
, "vm-undelete");
5608 res
= ast_play_and_wait(chan
, "vm-toforward");
5610 res
= ast_play_and_wait(chan
, "vm-savemessage");
5614 res
= ast_play_and_wait(chan
, "vm-helpexit");
5616 res
= ast_waitfordigit(chan
, 6000);
5619 if (vms
->repeats
> 2) {
5627 static int vm_newuser(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
)
5632 char newpassword
[80] = "";
5633 char newpassword2
[80] = "";
5634 char prefile
[256]="";
5635 unsigned char buf
[256];
5638 if (ast_adsi_available(chan
)) {
5639 bytes
+= adsi_logo(buf
+ bytes
);
5640 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "New User Setup", "");
5641 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Not Done", "");
5642 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
5643 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
5644 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
5647 /* First, have the user change their password
5648 so they won't get here again */
5650 newpassword
[1] = '\0';
5651 newpassword
[0] = cmd
= ast_play_and_wait(chan
,"vm-newpassword");
5653 newpassword
[0] = '\0';
5654 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5656 cmd
= ast_readstring(chan
,newpassword
+ strlen(newpassword
),sizeof(newpassword
)-1,2000,10000,"#");
5657 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5659 newpassword2
[1] = '\0';
5660 newpassword2
[0] = cmd
= ast_play_and_wait(chan
,"vm-reenterpassword");
5662 newpassword2
[0] = '\0';
5663 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5665 cmd
= ast_readstring(chan
,newpassword2
+ strlen(newpassword2
),sizeof(newpassword2
)-1,2000,10000,"#");
5666 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5668 if (!strcmp(newpassword
, newpassword2
))
5670 ast_log(LOG_NOTICE
,"Password mismatch for user %s (%s != %s)\n", vms
->username
, newpassword
, newpassword2
);
5671 cmd
= ast_play_and_wait(chan
, "vm-mismatch");
5675 if (ast_strlen_zero(ext_pass_cmd
))
5676 vm_change_password(vmu
,newpassword
);
5678 vm_change_password_shell(vmu
,newpassword
);
5679 ast_log(LOG_DEBUG
,"User %s set password to %s of length %d\n",vms
->username
,newpassword
,(int)strlen(newpassword
));
5680 cmd
= ast_play_and_wait(chan
,"vm-passchanged");
5682 /* If forcename is set, have the user record their name */
5683 if (ast_test_flag(vmu
, VM_FORCENAME
)) {
5684 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/greet", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
5685 cmd
= play_record_review(chan
,"vm-rec-name",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
);
5686 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5690 /* If forcegreetings is set, have the user record their greetings */
5691 if (ast_test_flag(vmu
, VM_FORCEGREET
)) {
5692 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/unavail", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
5693 cmd
= play_record_review(chan
,"vm-rec-unv",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
);
5694 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5696 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/busy", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
5697 cmd
= play_record_review(chan
,"vm-rec-busy",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
);
5698 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5705 static int vm_options(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
)
5710 char newpassword
[80] = "";
5711 char newpassword2
[80] = "";
5712 char prefile
[256]="";
5713 unsigned char buf
[256];
5716 if (ast_adsi_available(chan
))
5718 bytes
+= adsi_logo(buf
+ bytes
);
5719 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Options Menu", "");
5720 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Not Done", "");
5721 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
5722 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
5723 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
5725 while ((cmd
>= 0) && (cmd
!= 't')) {
5730 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/unavail", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
5731 cmd
= play_record_review(chan
,"vm-rec-unv",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
);
5734 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/busy", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
5735 cmd
= play_record_review(chan
,"vm-rec-busy",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
);
5738 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/greet", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
5739 cmd
= play_record_review(chan
,"vm-rec-name",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
);
5742 cmd
= vm_tempgreeting(chan
, vmu
, vms
, fmtc
, record_gain
);
5745 if (vmu
->password
[0] == '-') {
5746 cmd
= ast_play_and_wait(chan
, "vm-no");
5749 newpassword
[1] = '\0';
5750 newpassword
[0] = cmd
= ast_play_and_wait(chan
,"vm-newpassword");
5752 newpassword
[0] = '\0';
5756 if ((cmd
= ast_readstring(chan
,newpassword
+ strlen(newpassword
),sizeof(newpassword
)-1,2000,10000,"#")) < 0) {
5760 newpassword2
[1] = '\0';
5761 newpassword2
[0] = cmd
= ast_play_and_wait(chan
,"vm-reenterpassword");
5763 newpassword2
[0] = '\0';
5768 if ((cmd
= ast_readstring(chan
,newpassword2
+ strlen(newpassword2
),sizeof(newpassword2
)-1,2000,10000,"#"))) {
5772 if (strcmp(newpassword
, newpassword2
)) {
5773 ast_log(LOG_NOTICE
,"Password mismatch for user %s (%s != %s)\n", vms
->username
, newpassword
, newpassword2
);
5774 cmd
= ast_play_and_wait(chan
, "vm-mismatch");
5777 if (ast_strlen_zero(ext_pass_cmd
))
5778 vm_change_password(vmu
,newpassword
);
5780 vm_change_password_shell(vmu
,newpassword
);
5781 ast_log(LOG_DEBUG
,"User %s set password to %s of length %d\n",vms
->username
,newpassword
,(int)strlen(newpassword
));
5782 cmd
= ast_play_and_wait(chan
,"vm-passchanged");
5788 cmd
= ast_play_and_wait(chan
,"vm-options");
5790 cmd
= ast_waitfordigit(chan
,6000);
5802 static int vm_tempgreeting(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
)
5807 char prefile
[256]="";
5808 unsigned char buf
[256];
5811 if (ast_adsi_available(chan
)) {
5812 bytes
+= adsi_logo(buf
+ bytes
);
5813 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Temp Greeting Menu", "");
5814 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Not Done", "");
5815 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
5816 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
5817 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
5819 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
5820 while (cmd
>= 0 && cmd
!= 't') {
5823 RETRIEVE(prefile
, -1);
5824 if (ast_fileexists(prefile
, NULL
, NULL
) <= 0) {
5825 play_record_review(chan
, "vm-rec-temp", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
);
5830 cmd
= play_record_review(chan
, "vm-rec-temp", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
);
5833 DELETE(prefile
, -1, prefile
);
5834 ast_play_and_wait(chan
, "vm-tempremoved");
5841 cmd
= ast_play_and_wait(chan
,
5842 ast_fileexists(prefile
, NULL
, NULL
) > 0 ? /* XXX always true ? */
5843 "vm-tempgreeting2" : "vm-tempgreeting");
5845 cmd
= ast_waitfordigit(chan
,6000);
5852 DISPOSE(prefile
, -1);
5861 static int vm_browse_messages_gr(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
5865 if (vms
->lastmsg
> -1) {
5866 cmd
= play_message(chan
, vmu
, vms
);
5868 cmd
= ast_play_and_wait(chan
, "vm-youhaveno");
5869 if (!strcasecmp(vms
->vmbox
, "vm-INBOX") ||!strcasecmp(vms
->vmbox
, "vm-Old")){
5871 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%ss", vms
->curbox
);
5872 cmd
= ast_play_and_wait(chan
, vms
->fn
);
5875 cmd
= ast_play_and_wait(chan
, "vm-messages");
5878 cmd
= ast_play_and_wait(chan
, "vm-messages");
5880 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
5881 cmd
= ast_play_and_wait(chan
, vms
->fn
);
5888 /* Default English syntax */
5889 static int vm_browse_messages_en(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
5893 if (vms
->lastmsg
> -1) {
5894 cmd
= play_message(chan
, vmu
, vms
);
5896 cmd
= ast_play_and_wait(chan
, "vm-youhave");
5898 cmd
= ast_play_and_wait(chan
, "vm-no");
5900 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
5901 cmd
= ast_play_and_wait(chan
, vms
->fn
);
5904 cmd
= ast_play_and_wait(chan
, "vm-messages");
5909 /* ITALIAN syntax */
5910 static int vm_browse_messages_it(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
5914 if (vms
->lastmsg
> -1) {
5915 cmd
= play_message(chan
, vmu
, vms
);
5917 cmd
= ast_play_and_wait(chan
, "vm-no");
5919 cmd
= ast_play_and_wait(chan
, "vm-message");
5921 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
5922 cmd
= ast_play_and_wait(chan
, vms
->fn
);
5928 /* SPANISH syntax */
5929 static int vm_browse_messages_es(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
5933 if (vms
->lastmsg
> -1) {
5934 cmd
= play_message(chan
, vmu
, vms
);
5936 cmd
= ast_play_and_wait(chan
, "vm-youhaveno");
5938 cmd
= ast_play_and_wait(chan
, "vm-messages");
5940 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
5941 cmd
= ast_play_and_wait(chan
, vms
->fn
);
5947 /* PORTUGUESE syntax */
5948 static int vm_browse_messages_pt(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
5952 if (vms
->lastmsg
> -1) {
5953 cmd
= play_message(chan
, vmu
, vms
);
5955 cmd
= ast_play_and_wait(chan
, "vm-no");
5957 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
5958 cmd
= ast_play_and_wait(chan
, vms
->fn
);
5961 cmd
= ast_play_and_wait(chan
, "vm-messages");
5966 static int vm_browse_messages(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
5968 if (!strcasecmp(chan
->language
, "es")) { /* SPANISH */
5969 return vm_browse_messages_es(chan
, vms
, vmu
);
5970 } else if (!strcasecmp(chan
->language
, "it")) { /* ITALIAN */
5971 return vm_browse_messages_it(chan
, vms
, vmu
);
5972 } else if (!strcasecmp(chan
->language
, "pt")) { /* PORTUGUESE */
5973 return vm_browse_messages_pt(chan
, vms
, vmu
);
5974 } else if (!strcasecmp(chan
->language
, "gr")){
5975 return vm_browse_messages_gr(chan
, vms
, vmu
); /* GREEK */
5976 } else { /* Default to English syntax */
5977 return vm_browse_messages_en(chan
, vms
, vmu
);
5981 static int vm_authenticate(struct ast_channel
*chan
, char *mailbox
, int mailbox_size
,
5982 struct ast_vm_user
*res_vmu
, const char *context
, const char *prefix
,
5983 int skipuser
, int maxlogins
, int silent
)
5985 int useadsi
=0, valid
=0, logretries
=0;
5986 char password
[AST_MAX_EXTENSION
]="", *passptr
;
5987 struct ast_vm_user vmus
, *vmu
= NULL
;
5989 /* If ADSI is supported, setup login screen */
5990 adsi_begin(chan
, &useadsi
);
5991 if (!skipuser
&& useadsi
)
5993 if (!silent
&& !skipuser
&& ast_streamfile(chan
, "vm-login", chan
->language
)) {
5994 ast_log(LOG_WARNING
, "Couldn't stream login file\n");
5998 /* Authenticate them and get their mailbox/password */
6000 while (!valid
&& (logretries
< maxlogins
)) {
6001 /* Prompt for, and read in the username */
6002 if (!skipuser
&& ast_readstring(chan
, mailbox
, mailbox_size
- 1, 2000, 10000, "#") < 0) {
6003 ast_log(LOG_WARNING
, "Couldn't read username\n");
6006 if (ast_strlen_zero(mailbox
)) {
6007 if (chan
->cid
.cid_num
) {
6008 ast_copy_string(mailbox
, chan
->cid
.cid_num
, mailbox_size
);
6010 if (option_verbose
> 2)
6011 ast_verbose(VERBOSE_PREFIX_3
"Username not entered\n");
6016 adsi_password(chan
);
6018 if (!ast_strlen_zero(prefix
)) {
6019 char fullusername
[80] = "";
6020 ast_copy_string(fullusername
, prefix
, sizeof(fullusername
));
6021 strncat(fullusername
, mailbox
, sizeof(fullusername
) - 1 - strlen(fullusername
));
6022 ast_copy_string(mailbox
, fullusername
, mailbox_size
);
6025 ast_log(LOG_DEBUG
, "Before find user for mailbox %s\n",mailbox
);
6026 vmu
= find_user(&vmus
, context
, mailbox
);
6027 if (vmu
&& (vmu
->password
[0] == '\0' || (vmu
->password
[0] == '-' && vmu
->password
[1] == '\0'))) {
6028 /* saved password is blank, so don't bother asking */
6031 if (ast_streamfile(chan
, "vm-password", chan
->language
)) {
6032 ast_log(LOG_WARNING
, "Unable to stream password file\n");
6035 if (ast_readstring(chan
, password
, sizeof(password
) - 1, 2000, 10000, "#") < 0) {
6036 ast_log(LOG_WARNING
, "Unable to read password\n");
6042 passptr
= vmu
->password
;
6043 if (passptr
[0] == '-') passptr
++;
6045 if (vmu
&& !strcmp(passptr
, password
))
6048 if (option_verbose
> 2)
6049 ast_verbose( VERBOSE_PREFIX_3
"Incorrect password '%s' for user '%s' (context = %s)\n", password
, mailbox
, context
? context
: "default");
6050 if (!ast_strlen_zero(prefix
))
6055 if (skipuser
|| logretries
>= maxlogins
) {
6056 if (ast_streamfile(chan
, "vm-incorrect", chan
->language
)) {
6057 ast_log(LOG_WARNING
, "Unable to stream incorrect message\n");
6063 if (ast_streamfile(chan
, "vm-incorrect-mailbox", chan
->language
)) {
6064 ast_log(LOG_WARNING
, "Unable to stream incorrect mailbox message\n");
6068 if (ast_waitstream(chan
, "")) /* Channel is hung up */
6072 if (!valid
&& (logretries
>= maxlogins
)) {
6073 ast_stopstream(chan
);
6074 ast_play_and_wait(chan
, "vm-goodbye");
6077 if (vmu
&& !skipuser
) {
6078 memcpy(res_vmu
, vmu
, sizeof(struct ast_vm_user
));
6083 static int vm_execmain(struct ast_channel
*chan
, void *data
)
6085 /* XXX This is, admittedly, some pretty horrendus code. For some
6086 reason it just seemed a lot easier to do with GOTO's. I feel
6087 like I'm back in my GWBASIC days. XXX */
6091 struct ast_module_user
*u
;
6092 char prefixstr
[80] ="";
6093 char ext_context
[256]="";
6097 struct vm_state vms
;
6098 struct ast_vm_user
*vmu
= NULL
, vmus
;
6101 struct ast_flags flags
= { 0 };
6102 signed char record_gain
= 0;
6104 int play_folder
= 0;
6108 u
= ast_module_user_add(chan
);
6110 /* Add the vm_state to the active list and keep it active */
6111 memset(&vms
, 0, sizeof(vms
));
6114 memset(&vmus
, 0, sizeof(vmus
));
6116 if (chan
->_state
!= AST_STATE_UP
) {
6117 ast_log(LOG_DEBUG
, "Before ast_answer\n");
6121 if (!ast_strlen_zero(data
)) {
6122 char *opts
[OPT_ARG_ARRAY_SIZE
];
6124 AST_DECLARE_APP_ARGS(args
,
6129 parse
= ast_strdupa(data
);
6131 AST_STANDARD_APP_ARGS(args
, parse
);
6133 if (args
.argc
== 2) {
6134 if (ast_app_parse_options(vm_app_options
, &flags
, opts
, args
.argv1
)) {
6135 ast_module_user_remove(u
);
6138 if (ast_test_flag(&flags
, OPT_RECORDGAIN
)) {
6140 if (opts
[OPT_ARG_RECORDGAIN
]) {
6141 if (sscanf(opts
[OPT_ARG_RECORDGAIN
], "%d", &gain
) != 1) {
6142 ast_log(LOG_WARNING
, "Invalid value '%s' provided for record gain option\n", opts
[OPT_ARG_RECORDGAIN
]);
6143 ast_module_user_remove(u
);
6146 record_gain
= (signed char) gain
;
6149 ast_log(LOG_WARNING
, "Invalid Gain level set with option g\n");
6152 if (ast_test_flag(&flags
, OPT_AUTOPLAY
) ) {
6154 if (opts
[OPT_ARG_PLAYFOLDER
]) {
6155 if (sscanf(opts
[OPT_ARG_PLAYFOLDER
], "%d", &play_folder
) != 1) {
6156 ast_log(LOG_WARNING
, "Invalid value '%s' provided for folder autoplay option\n", opts
[OPT_ARG_PLAYFOLDER
]);
6159 ast_log(LOG_WARNING
, "Invalid folder set with option a\n");
6161 if ( play_folder
> 9 || play_folder
< 0) {
6162 ast_log(LOG_WARNING
, "Invalid value '%d' provided for folder autoplay option\n", play_folder
);
6167 /* old style options parsing */
6168 while (*(args
.argv0
)) {
6169 if (*(args
.argv0
) == 's')
6170 ast_set_flag(&flags
, OPT_SILENT
);
6171 else if (*(args
.argv0
) == 'p')
6172 ast_set_flag(&flags
, OPT_PREPEND_MAILBOX
);
6180 valid
= ast_test_flag(&flags
, OPT_SILENT
);
6182 if ((context
= strchr(args
.argv0
, '@')))
6185 if (ast_test_flag(&flags
, OPT_PREPEND_MAILBOX
))
6186 ast_copy_string(prefixstr
, args
.argv0
, sizeof(prefixstr
));
6188 ast_copy_string(vms
.username
, args
.argv0
, sizeof(vms
.username
));
6190 if (!ast_strlen_zero(vms
.username
) && (vmu
= find_user(&vmus
, context
,vms
.username
)))
6197 res
= vm_authenticate(chan
, vms
.username
, sizeof(vms
.username
), &vmus
, context
, prefixstr
, skipuser
, maxlogins
, 0);
6199 ast_log(LOG_DEBUG
, "After vm_authenticate\n");
6208 /* If ADSI is supported, setup login screen */
6209 adsi_begin(chan
, &useadsi
);
6212 vms
.interactive
= 1;
6214 vmstate_insert(&vms
);
6215 init_vm_state(&vms
);
6220 if (!(vms
.deleted
= ast_calloc(vmu
->maxmsg
, sizeof(int)))) {
6221 /* TODO: Handle memory allocation failure */
6223 if (!(vms
.heard
= ast_calloc(vmu
->maxmsg
, sizeof(int)))) {
6224 /* TODO: Handle memory allocation failure */
6227 /* Set language from config to override channel language */
6228 if (!ast_strlen_zero(vmu
->language
))
6229 ast_string_field_set(chan
, language
, vmu
->language
);
6230 #ifndef IMAP_STORAGE
6231 create_dirpath(vms
.curdir
, sizeof(vms
.curdir
), vmu
->context
, vms
.username
, "");
6233 /* Retrieve old and new message counts */
6234 ast_log(LOG_DEBUG
, "Before open_mailbox\n");
6235 res
= open_mailbox(&vms
, vmu
, 1);
6236 if (res
== ERROR_LOCK_PATH
)
6238 vms
.oldmessages
= vms
.lastmsg
+ 1;
6239 ast_log(LOG_DEBUG
, "Number of old messages: %d\n",vms
.oldmessages
);
6240 /* Start in INBOX */
6241 res
= open_mailbox(&vms
, vmu
, 0);
6242 if (res
== ERROR_LOCK_PATH
)
6244 vms
.newmessages
= vms
.lastmsg
+ 1;
6245 ast_log(LOG_DEBUG
, "Number of new messages: %d\n",vms
.newmessages
);
6247 /* Select proper mailbox FIRST!! */
6249 res
= open_mailbox(&vms
, vmu
, play_folder
);
6250 if (res
== ERROR_LOCK_PATH
)
6253 /* If there are no new messages, inform the user and hangup */
6254 if (vms
.lastmsg
== -1) {
6255 cmd
= vm_browse_messages(chan
, &vms
, vmu
);
6260 if (!vms
.newmessages
&& vms
.oldmessages
) {
6261 /* If we only have old messages start here */
6262 res
= open_mailbox(&vms
, vmu
, 1);
6263 if (res
== ERROR_LOCK_PATH
)
6269 adsi_status(chan
, &vms
);
6272 /* Check to see if this is a new user */
6273 if (!strcasecmp(vmu
->mailbox
, vmu
->password
) &&
6274 (ast_test_flag(vmu
, VM_FORCENAME
| VM_FORCEGREET
))) {
6275 if (ast_play_and_wait(chan
, "vm-newuser") == -1)
6276 ast_log(LOG_WARNING
, "Couldn't stream new user file\n");
6277 cmd
= vm_newuser(chan
, vmu
, &vms
, vmfmts
, record_gain
);
6278 if ((cmd
== 't') || (cmd
== '#')) {
6282 } else if (cmd
< 0) {
6289 if(option_debug
> 2)
6290 ast_log(LOG_DEBUG
, "Checking quotas: comparing %u to %u\n",vms
.quota_usage
,vms
.quota_limit
);
6291 if (vms
.quota_limit
&& vms
.quota_usage
>= vms
.quota_limit
) {
6292 ast_log(LOG_DEBUG
, "*** QUOTA EXCEEDED!!\n");
6293 cmd
= ast_play_and_wait(chan
, "vm-mailboxfull");
6299 cmd
= vm_intro(chan
, vmu
, &vms
);
6304 while ((cmd
> -1) && (cmd
!= 't') && (cmd
!= '#')) {
6311 cmd
= vm_browse_messages(chan
, &vms
, vmu
);
6313 case '2': /* Change folders */
6315 adsi_folders(chan
, 0, "Change to folder...");
6316 cmd
= get_folder2(chan
, "vm-changeto", 0);
6319 } else if (cmd
> 0) {
6321 res
= close_mailbox(&vms
, vmu
);
6322 if (res
== ERROR_LOCK_PATH
)
6324 res
= open_mailbox(&vms
, vmu
, cmd
);
6325 if (res
== ERROR_LOCK_PATH
)
6330 adsi_status2(chan
, &vms
);
6333 cmd
= vm_play_folder_name(chan
, vms
.vmbox
);
6337 case '3': /* Advanced options */
6340 while ((cmd
> -1) && (cmd
!= 't') && (cmd
!= '#')) {
6342 case '1': /* Reply */
6343 if (vms
.lastmsg
> -1 && !vms
.starting
) {
6344 cmd
= advanced_options(chan
, vmu
, &vms
, vms
.curmsg
, 1, record_gain
);
6345 if (cmd
== ERROR_LOCK_PATH
) {
6350 cmd
= ast_play_and_wait(chan
, "vm-sorry");
6353 case '2': /* Callback */
6354 if (option_verbose
> 2 && !vms
.starting
)
6355 ast_verbose( VERBOSE_PREFIX_3
"Callback Requested\n");
6356 if (!ast_strlen_zero(vmu
->callback
) && vms
.lastmsg
> -1 && !vms
.starting
) {
6357 cmd
= advanced_options(chan
, vmu
, &vms
, vms
.curmsg
, 2, record_gain
);
6361 } else if (cmd
== ERROR_LOCK_PATH
) {
6367 cmd
= ast_play_and_wait(chan
, "vm-sorry");
6370 case '3': /* Envelope */
6371 if (vms
.lastmsg
> -1 && !vms
.starting
) {
6372 cmd
= advanced_options(chan
, vmu
, &vms
, vms
.curmsg
, 3, record_gain
);
6373 if (cmd
== ERROR_LOCK_PATH
) {
6378 cmd
= ast_play_and_wait(chan
, "vm-sorry");
6381 case '4': /* Dialout */
6382 if (!ast_strlen_zero(vmu
->dialout
)) {
6383 cmd
= dialout(chan
, vmu
, NULL
, vmu
->dialout
);
6390 cmd
= ast_play_and_wait(chan
, "vm-sorry");
6394 case '5': /* Leave VoiceMail */
6395 if (ast_test_flag(vmu
, VM_SVMAIL
)) {
6396 cmd
= forward_message(chan
, context
, &vms
, vmu
, vmfmts
, 1, record_gain
);
6397 if (cmd
== ERROR_LOCK_PATH
) {
6402 cmd
= ast_play_and_wait(chan
,"vm-sorry");
6406 case '*': /* Return to main menu */
6412 if (!vms
.starting
) {
6413 cmd
= ast_play_and_wait(chan
, "vm-toreply");
6415 if (!ast_strlen_zero(vmu
->callback
) && !vms
.starting
&& !cmd
) {
6416 cmd
= ast_play_and_wait(chan
, "vm-tocallback");
6418 if (!cmd
&& !vms
.starting
) {
6419 cmd
= ast_play_and_wait(chan
, "vm-tohearenv");
6421 if (!ast_strlen_zero(vmu
->dialout
) && !cmd
) {
6422 cmd
= ast_play_and_wait(chan
, "vm-tomakecall");
6424 if (ast_test_flag(vmu
, VM_SVMAIL
) && !cmd
)
6425 cmd
=ast_play_and_wait(chan
, "vm-leavemsg");
6427 cmd
= ast_play_and_wait(chan
, "vm-starmain");
6429 cmd
= ast_waitfordigit(chan
,6000);
6432 if (vms
.repeats
> 3)
6444 cmd
= play_message(chan
, vmu
, &vms
);
6446 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6450 if (vms
.curmsg
< vms
.lastmsg
) {
6452 cmd
= play_message(chan
, vmu
, &vms
);
6454 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6458 vms
.deleted
[vms
.curmsg
] = !vms
.deleted
[vms
.curmsg
];
6460 adsi_delete(chan
, &vms
);
6461 if (vms
.deleted
[vms
.curmsg
])
6462 cmd
= ast_play_and_wait(chan
, "vm-deleted");
6464 cmd
= ast_play_and_wait(chan
, "vm-undeleted");
6465 if (ast_test_flag((&globalflags
), VM_SKIPAFTERCMD
)) {
6466 if (vms
.curmsg
< vms
.lastmsg
) {
6468 cmd
= play_message(chan
, vmu
, &vms
);
6470 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6479 if (vms
.lastmsg
> -1) {
6480 cmd
= forward_message(chan
, context
, &vms
, vmu
, vmfmts
, 0, record_gain
);
6481 if (cmd
== ERROR_LOCK_PATH
) {
6486 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6490 adsi_folders(chan
, 1, "Save to folder...");
6491 cmd
= get_folder2(chan
, "vm-savefolder", 1);
6492 box
= 0; /* Shut up compiler */
6496 } else if (cmd
> 0) {
6497 box
= cmd
= cmd
- '0';
6498 cmd
= save_to_folder(vmu
, &vms
, vms
.curmsg
, cmd
);
6499 if (cmd
== ERROR_LOCK_PATH
) {
6503 } else if (cmd
== 10) {
6507 vms
.deleted
[vms
.curmsg
] = 1;
6509 vms
.deleted
[vms
.curmsg
] = 0;
6510 vms
.heard
[vms
.curmsg
] = 0;
6513 make_file(vms
.fn
, sizeof(vms
.fn
), vms
.curdir
, vms
.curmsg
);
6515 adsi_message(chan
, &vms
);
6516 snprintf(vms
.fn
, sizeof(vms
.fn
), "vm-%s", mbox(box
));
6518 cmd
= ast_play_and_wait(chan
, "vm-message");
6520 cmd
= say_and_wait(chan
, vms
.curmsg
+ 1, chan
->language
);
6522 cmd
= ast_play_and_wait(chan
, "vm-savedto");
6524 cmd
= vm_play_folder_name(chan
, vms
.fn
);
6526 cmd
= ast_play_and_wait(chan
, "vm-mailboxfull");
6528 if (ast_test_flag((&globalflags
), VM_SKIPAFTERCMD
)) {
6529 if (vms
.curmsg
< vms
.lastmsg
) {
6531 cmd
= play_message(chan
, vmu
, &vms
);
6533 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6538 if (!vms
.starting
) {
6539 cmd
= ast_play_and_wait(chan
, "vm-onefor");
6541 cmd
= vm_play_folder_name(chan
, vms
.vmbox
);
6543 cmd
= ast_play_and_wait(chan
, "vm-opts");
6545 cmd
= vm_instructions(chan
, &vms
, 1);
6550 cmd
= vm_options(chan
, vmu
, &vms
, vmfmts
, record_gain
);
6552 adsi_status(chan
, &vms
);
6554 default: /* Nothing */
6555 cmd
= vm_instructions(chan
, &vms
, 0);
6559 if ((cmd
== 't') || (cmd
== '#')) {
6569 ast_stopstream(chan
);
6573 res
= ast_play_and_wait(chan
, "vm-dialout");
6575 res
= ast_play_and_wait(chan
, "vm-goodbye");
6580 ast_adsi_unload_session(chan
);
6583 close_mailbox(&vms
, vmu
);
6585 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", vms
.username
, vmu
->context
);
6586 manager_event(EVENT_FLAG_CALL
, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context
, has_voicemail(ext_context
, NULL
));
6587 run_externnotify(vmu
->context
, vmu
->mailbox
);
6590 /* expunge message - use UID Expunge if supported on IMAP server*/
6591 if(option_debug
> 2)
6592 ast_log(LOG_DEBUG
, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted
,expungeonhangup
);
6593 if (vmu
&& deleted
== 1 && expungeonhangup
== 1) {
6594 #ifdef HAVE_IMAP_TK2006
6595 if (LEVELUIDPLUS (vms
.mailstream
)) {
6596 mail_expunge_full(vms
.mailstream
,NIL
,EX_UID
);
6599 mail_expunge(vms
.mailstream
);
6601 /* before we delete the state, we should copy pertinent info
6602 * back to the persistent model */
6603 vmstate_delete(&vms
);
6611 ast_module_user_remove(u
);
6616 static int vm_exec(struct ast_channel
*chan
, void *data
)
6619 struct ast_module_user
*u
;
6621 struct leave_vm_options leave_options
;
6622 struct ast_flags flags
= { 0 };
6623 static int deprecate_warning
= 0;
6624 char *opts
[OPT_ARG_ARRAY_SIZE
];
6625 AST_DECLARE_APP_ARGS(args
,
6630 u
= ast_module_user_add(chan
);
6632 memset(&leave_options
, 0, sizeof(leave_options
));
6634 if (chan
->_state
!= AST_STATE_UP
)
6637 if (!ast_strlen_zero(data
)) {
6638 tmp
= ast_strdupa(data
);
6639 AST_STANDARD_APP_ARGS(args
, tmp
);
6640 if (args
.argc
== 2) {
6641 if (ast_app_parse_options(vm_app_options
, &flags
, opts
, args
.argv1
)) {
6642 ast_module_user_remove(u
);
6645 ast_copy_flags(&leave_options
, &flags
, OPT_SILENT
| OPT_BUSY_GREETING
| OPT_UNAVAIL_GREETING
| OPT_PRIORITY_JUMP
);
6646 if (ast_test_flag(&flags
, OPT_RECORDGAIN
)) {
6649 if (sscanf(opts
[OPT_ARG_RECORDGAIN
], "%d", &gain
) != 1) {
6650 ast_log(LOG_WARNING
, "Invalid value '%s' provided for record gain option\n", opts
[OPT_ARG_RECORDGAIN
]);
6651 ast_module_user_remove(u
);
6654 leave_options
.record_gain
= (signed char) gain
;
6658 /* old style options parsing */
6660 char *orig_argv0
= args
.argv0
;
6661 while (*(args
.argv0
)) {
6662 if (*(args
.argv0
) == 's') {
6664 ast_set_flag(&leave_options
, OPT_SILENT
);
6665 } else if (*(args
.argv0
) == 'b') {
6667 ast_set_flag(&leave_options
, OPT_BUSY_GREETING
);
6668 } else if (*(args
.argv0
) == 'u') {
6670 ast_set_flag(&leave_options
, OPT_UNAVAIL_GREETING
);
6671 } else if (*(args
.argv0
) == 'j') {
6673 ast_set_flag(&leave_options
, OPT_PRIORITY_JUMP
);
6678 if (!deprecate_warning
&& old
) {
6679 deprecate_warning
= 1;
6680 ast_log(LOG_WARNING
, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0
);
6681 ast_log(LOG_WARNING
, "Please move all leading options to the second argument.\n");
6686 res
= ast_app_getdata(chan
, "vm-whichbox", tmp
, sizeof(tmp
) - 1, 0);
6688 ast_module_user_remove(u
);
6691 if (ast_strlen_zero(tmp
)) {
6692 ast_module_user_remove(u
);
6695 args
.argv0
= ast_strdupa(tmp
);
6698 res
= leave_voicemail(chan
, args
.argv0
, &leave_options
);
6700 if (res
== ERROR_LOCK_PATH
) {
6701 ast_log(LOG_ERROR
, "Could not leave voicemail. The path is already locked.\n");
6702 /*Send the call to n+101 priority, where n is the current priority*/
6703 if (ast_test_flag(&leave_options
, OPT_PRIORITY_JUMP
) || ast_opt_priority_jumping
)
6704 if (ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101))
6705 ast_log(LOG_WARNING
, "Extension %s, priority %d doesn't exist.\n", chan
->exten
, chan
->priority
+ 101);
6706 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
6710 ast_module_user_remove(u
);
6715 static struct ast_vm_user
*find_or_create(char *context
, char *mbox
)
6717 struct ast_vm_user
*vmu
;
6718 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
6719 if (ast_test_flag((&globalflags
), VM_SEARCH
) && !strcasecmp(mbox
, vmu
->mailbox
))
6721 if (context
&& (!strcasecmp(context
, vmu
->context
)) && (!strcasecmp(mbox
, vmu
->mailbox
)))
6726 if ((vmu
= ast_calloc(1, sizeof(*vmu
)))) {
6727 ast_copy_string(vmu
->context
, context
, sizeof(vmu
->context
));
6728 ast_copy_string(vmu
->mailbox
, mbox
, sizeof(vmu
->mailbox
));
6729 AST_LIST_INSERT_TAIL(&users
, vmu
, list
);
6735 static int append_mailbox(char *context
, char *mbox
, char *data
)
6737 /* Assumes lock is already held */
6741 struct ast_vm_user
*vmu
;
6743 tmp
= ast_strdupa(data
);
6745 if ((vmu
= find_or_create(context
, mbox
))) {
6746 populate_defaults(vmu
);
6749 if ((s
= strsep(&stringp
, ",")))
6750 ast_copy_string(vmu
->password
, s
, sizeof(vmu
->password
));
6751 if (stringp
&& (s
= strsep(&stringp
, ",")))
6752 ast_copy_string(vmu
->fullname
, s
, sizeof(vmu
->fullname
));
6753 if (stringp
&& (s
= strsep(&stringp
, ",")))
6754 ast_copy_string(vmu
->email
, s
, sizeof(vmu
->email
));
6755 if (stringp
&& (s
= strsep(&stringp
, ",")))
6756 ast_copy_string(vmu
->pager
, s
, sizeof(vmu
->pager
));
6757 if (stringp
&& (s
= strsep(&stringp
, ",")))
6758 apply_options(vmu
, s
);
6763 static int vm_box_exists(struct ast_channel
*chan
, void *data
)
6765 struct ast_module_user
*u
;
6766 struct ast_vm_user svm
;
6767 char *context
, *box
;
6768 int priority_jump
= 0;
6769 AST_DECLARE_APP_ARGS(args
,
6771 AST_APP_ARG(options
);
6774 if (ast_strlen_zero(data
)) {
6775 ast_log(LOG_ERROR
, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
6779 u
= ast_module_user_add(chan
);
6781 box
= ast_strdupa(data
);
6783 AST_STANDARD_APP_ARGS(args
, box
);
6786 if (strchr(args
.options
, 'j'))
6790 if ((context
= strchr(args
.mbox
, '@'))) {
6795 if (find_user(&svm
, context
, args
.mbox
)) {
6796 pbx_builtin_setvar_helper(chan
, "VMBOXEXISTSSTATUS", "SUCCESS");
6797 if (priority_jump
|| ast_opt_priority_jumping
)
6798 if (ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101))
6799 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);
6801 pbx_builtin_setvar_helper(chan
, "VMBOXEXISTSSTATUS", "FAILED");
6802 ast_module_user_remove(u
);
6806 static int vmauthenticate(struct ast_channel
*chan
, void *data
)
6808 struct ast_module_user
*u
;
6809 char *s
= data
, *user
=NULL
, *context
=NULL
, mailbox
[AST_MAX_EXTENSION
] = "";
6810 struct ast_vm_user vmus
;
6811 char *options
= NULL
;
6812 int silent
= 0, skipuser
= 0;
6815 u
= ast_module_user_add(chan
);
6819 user
= strsep(&s
, "|");
6820 options
= strsep(&s
, "|");
6823 user
= strsep(&s
, "@");
6824 context
= strsep(&s
, "");
6825 if (!ast_strlen_zero(user
))
6827 ast_copy_string(mailbox
, user
, sizeof(mailbox
));
6832 silent
= (strchr(options
, 's')) != NULL
;
6835 if (!vm_authenticate(chan
, mailbox
, sizeof(mailbox
), &vmus
, context
, NULL
, skipuser
, 3, silent
)) {
6836 pbx_builtin_setvar_helper(chan
, "AUTH_MAILBOX", mailbox
);
6837 pbx_builtin_setvar_helper(chan
, "AUTH_CONTEXT", vmus
.context
);
6838 ast_play_and_wait(chan
, "auth-thankyou");
6842 ast_module_user_remove(u
);
6846 static char voicemail_show_users_help
[] =
6847 "Usage: voicemail show users [for <context>]\n"
6848 " Lists all mailboxes currently set up\n";
6850 static char voicemail_show_zones_help
[] =
6851 "Usage: voicemail show zones\n"
6852 " Lists zone message formats\n";
6854 static int handle_voicemail_show_users(int fd
, int argc
, char *argv
[])
6856 struct ast_vm_user
*vmu
;
6857 char *output_format
= "%-10s %-5s %-25s %-10s %6s\n";
6859 if ((argc
< 3) || (argc
> 5) || (argc
== 4)) return RESULT_SHOWUSAGE
;
6860 else if ((argc
== 5) && strcmp(argv
[3],"for")) return RESULT_SHOWUSAGE
;
6862 AST_LIST_LOCK(&users
);
6863 if (!AST_LIST_EMPTY(&users
)) {
6865 ast_cli(fd
, output_format
, "Context", "Mbox", "User", "Zone", "NewMsg");
6868 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
6869 if (!strcmp(argv
[4],vmu
->context
))
6873 ast_cli(fd
, output_format
, "Context", "Mbox", "User", "Zone", "NewMsg");
6875 ast_cli(fd
, "No such voicemail context \"%s\"\n", argv
[4]);
6876 AST_LIST_UNLOCK(&users
);
6877 return RESULT_FAILURE
;
6880 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
6881 int newmsgs
= 0, oldmsgs
= 0;
6882 char count
[12], tmp
[256] = "";
6884 if ((argc
== 3) || ((argc
== 5) && !strcmp(argv
[4],vmu
->context
))) {
6885 snprintf(tmp
, sizeof(tmp
), "%s@%s", vmu
->mailbox
, ast_strlen_zero(vmu
->context
) ? "default" : vmu
->context
);
6886 inboxcount(tmp
, &newmsgs
, &oldmsgs
);
6887 snprintf(count
,sizeof(count
),"%d",newmsgs
);
6888 ast_cli(fd
, output_format
, vmu
->context
, vmu
->mailbox
, vmu
->fullname
, vmu
->zonetag
, count
);
6892 ast_cli(fd
, "There are no voicemail users currently defined\n");
6893 AST_LIST_UNLOCK(&users
);
6894 return RESULT_FAILURE
;
6896 AST_LIST_UNLOCK(&users
);
6897 return RESULT_SUCCESS
;
6900 static int handle_voicemail_show_zones(int fd
, int argc
, char *argv
[])
6902 struct vm_zone
*zone
;
6903 char *output_format
= "%-15s %-20s %-45s\n";
6904 int res
= RESULT_SUCCESS
;
6907 return RESULT_SHOWUSAGE
;
6909 AST_LIST_LOCK(&zones
);
6910 if (!AST_LIST_EMPTY(&zones
)) {
6911 ast_cli(fd
, output_format
, "Zone", "Timezone", "Message Format");
6912 AST_LIST_TRAVERSE(&zones
, zone
, list
) {
6913 ast_cli(fd
, output_format
, zone
->name
, zone
->timezone
, zone
->msg_format
);
6916 ast_cli(fd
, "There are no voicemail zones currently defined\n");
6917 res
= RESULT_FAILURE
;
6919 AST_LIST_UNLOCK(&zones
);
6924 static char *complete_voicemail_show_users(const char *line
, const char *word
, int pos
, int state
)
6928 struct ast_vm_user
*vmu
;
6929 const char *context
= "";
6931 /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
6935 return (state
== 0) ? ast_strdup("for") : NULL
;
6936 wordlen
= strlen(word
);
6937 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
6938 if (!strncasecmp(word
, vmu
->context
, wordlen
)) {
6939 if (context
&& strcmp(context
, vmu
->context
) && ++which
> state
)
6940 return ast_strdup(vmu
->context
);
6941 /* ignore repeated contexts ? */
6942 context
= vmu
->context
;
6948 static struct ast_cli_entry cli_show_voicemail_users_deprecated
= {
6949 { "show", "voicemail", "users", NULL
},
6950 handle_voicemail_show_users
, NULL
,
6951 NULL
, complete_voicemail_show_users
};
6953 static struct ast_cli_entry cli_show_voicemail_zones_deprecated
= {
6954 { "show", "voicemail", "zones", NULL
},
6955 handle_voicemail_show_zones
, NULL
,
6958 static struct ast_cli_entry cli_voicemail
[] = {
6959 { { "voicemail", "show", "users", NULL
},
6960 handle_voicemail_show_users
, "List defined voicemail boxes",
6961 voicemail_show_users_help
, complete_voicemail_show_users
, &cli_show_voicemail_users_deprecated
},
6963 { { "voicemail", "show", "zones", NULL
},
6964 handle_voicemail_show_zones
, "List zone message formats",
6965 voicemail_show_zones_help
, NULL
, &cli_show_voicemail_zones_deprecated
},
6968 static int load_config(void)
6970 struct ast_vm_user
*cur
;
6971 struct vm_zone
*zcur
;
6972 struct ast_config
*cfg
, *ucfg
;
6974 struct ast_variable
*var
;
6975 const char *notifystr
= NULL
;
6976 const char *smdistr
= NULL
;
6977 const char *astattach
;
6978 const char *astsearch
;
6979 const char *astsaycid
;
6980 const char *send_voicemail
;
6982 const char *imap_server
;
6983 const char *imap_port
;
6984 const char *imap_flags
;
6985 const char *imap_folder
;
6986 const char *auth_user
;
6987 const char *auth_password
;
6988 const char *expunge_on_hangup
;
6990 const char *astcallop
;
6991 const char *astreview
;
6992 const char *asttempgreetwarn
;
6993 const char *astskipcmd
;
6994 const char *asthearenv
;
6995 const char *astsaydurationinfo
;
6996 const char *astsaydurationminfo
;
6997 const char *silencestr
;
6998 const char *maxmsgstr
;
6999 const char *astdirfwd
;
7000 const char *thresholdstr
;
7002 const char *astemail
;
7003 const char *ucontext
;
7004 const char *astmailcmd
= SENDMAIL
;
7005 const char *astforcename
;
7006 const char *astforcegreet
;
7009 const char *dialoutcxt
= NULL
;
7010 const char *callbackcxt
= NULL
;
7011 const char *exitcxt
= NULL
;
7013 const char *emaildateformatstr
;
7014 const char *volgainstr
;
7018 cfg
= ast_config_load(VOICEMAIL_CONFIG
);
7020 AST_LIST_LOCK(&users
);
7021 while ((cur
= AST_LIST_REMOVE_HEAD(&users
, list
))) {
7022 ast_set_flag(cur
, VM_ALLOCED
);
7026 AST_LIST_LOCK(&zones
);
7027 while ((zcur
= AST_LIST_REMOVE_HEAD(&zones
, list
)))
7029 AST_LIST_UNLOCK(&zones
);
7031 memset(ext_pass_cmd
, 0, sizeof(ext_pass_cmd
));
7034 /* General settings */
7036 if (!(ucontext
= ast_variable_retrieve(cfg
, "general", "userscontext")))
7037 ucontext
= "default";
7038 ast_copy_string(userscontext
, ucontext
, sizeof(userscontext
));
7039 /* Attach voice message to mail message ? */
7040 if (!(astattach
= ast_variable_retrieve(cfg
, "general", "attach")))
7042 ast_set2_flag((&globalflags
), ast_true(astattach
), VM_ATTACH
);
7044 if (!(astsearch
= ast_variable_retrieve(cfg
, "general", "searchcontexts")))
7046 ast_set2_flag((&globalflags
), ast_true(astsearch
), VM_SEARCH
);
7049 if ((volgainstr
= ast_variable_retrieve(cfg
, "general", "volgain")))
7050 sscanf(volgainstr
, "%lf", &volgain
);
7053 strcpy(odbc_database
, "asterisk");
7054 if ((thresholdstr
= ast_variable_retrieve(cfg
, "general", "odbcstorage"))) {
7055 ast_copy_string(odbc_database
, thresholdstr
, sizeof(odbc_database
));
7057 strcpy(odbc_table
, "voicemessages");
7058 if ((thresholdstr
= ast_variable_retrieve(cfg
, "general", "odbctable"))) {
7059 ast_copy_string(odbc_table
, thresholdstr
, sizeof(odbc_table
));
7063 strcpy(mailcmd
, SENDMAIL
);
7064 if ((astmailcmd
= ast_variable_retrieve(cfg
, "general", "mailcmd")))
7065 ast_copy_string(mailcmd
, astmailcmd
, sizeof(mailcmd
)); /* User setting */
7068 if ((silencestr
= ast_variable_retrieve(cfg
, "general", "maxsilence"))) {
7069 maxsilence
= atoi(silencestr
);
7074 if (!(maxmsgstr
= ast_variable_retrieve(cfg
, "general", "maxmsg"))) {
7077 maxmsg
= atoi(maxmsgstr
);
7079 ast_log(LOG_WARNING
, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr
, MAXMSG
);
7081 } else if (maxmsg
> MAXMSGLIMIT
) {
7082 ast_log(LOG_WARNING
, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT
, maxmsgstr
);
7083 maxmsg
= MAXMSGLIMIT
;
7087 /* Load date format config for voicemail mail */
7088 if ((emaildateformatstr
= ast_variable_retrieve(cfg
, "general", "emaildateformat"))) {
7089 ast_copy_string(emaildateformat
, emaildateformatstr
, sizeof(emaildateformat
));
7092 /* External password changing command */
7093 if ((extpc
= ast_variable_retrieve(cfg
, "general", "externpass"))) {
7094 ast_copy_string(ext_pass_cmd
,extpc
,sizeof(ext_pass_cmd
));
7097 /* IMAP server address */
7098 if ((imap_server
= ast_variable_retrieve(cfg
, "general", "imapserver"))) {
7099 ast_copy_string(imapserver
, imap_server
, sizeof(imapserver
));
7101 ast_copy_string(imapserver
,"localhost", sizeof(imapserver
));
7103 /* IMAP server port */
7104 if ((imap_port
= ast_variable_retrieve(cfg
, "general", "imapport"))) {
7105 ast_copy_string(imapport
, imap_port
, sizeof(imapport
));
7107 ast_copy_string(imapport
,"143", sizeof(imapport
));
7109 /* IMAP server flags */
7110 if ((imap_flags
= ast_variable_retrieve(cfg
, "general", "imapflags"))) {
7111 ast_copy_string(imapflags
, imap_flags
, sizeof(imapflags
));
7113 /* IMAP server master username */
7114 if ((auth_user
= ast_variable_retrieve(cfg
, "general", "authuser"))) {
7115 ast_copy_string(authuser
, auth_user
, sizeof(authuser
));
7117 /* IMAP server master password */
7118 if ((auth_password
= ast_variable_retrieve(cfg
, "general", "authpassword"))) {
7119 ast_copy_string(authpassword
, auth_password
, sizeof(authpassword
));
7121 /* Expunge on exit */
7122 if ((expunge_on_hangup
= ast_variable_retrieve(cfg
, "general", "expungeonhangup"))) {
7123 if(ast_false(expunge_on_hangup
))
7124 expungeonhangup
= 0;
7126 expungeonhangup
= 1;
7128 expungeonhangup
= 1;
7130 /* IMAP voicemail folder */
7131 if ((imap_folder
= ast_variable_retrieve(cfg
, "general", "imapfolder"))) {
7132 ast_copy_string(imapfolder
, imap_folder
, sizeof(imapfolder
));
7134 ast_copy_string(imapfolder
,"INBOX", sizeof(imapfolder
));
7137 /* External voicemail notify application */
7139 if ((notifystr
= ast_variable_retrieve(cfg
, "general", "externnotify"))) {
7140 ast_copy_string(externnotify
, notifystr
, sizeof(externnotify
));
7141 ast_log(LOG_DEBUG
, "found externnotify: %s\n", externnotify
);
7142 if (!strcasecmp(externnotify
, "smdi")) {
7143 ast_log(LOG_DEBUG
, "Using SMDI for external voicemail notification\n");
7144 if ((smdistr
= ast_variable_retrieve(cfg
, "general", "smdiport"))) {
7145 smdi_iface
= ast_smdi_interface_find(smdistr
);
7147 ast_log(LOG_DEBUG
, "No SMDI interface set, trying default (/dev/ttyS0)\n");
7148 smdi_iface
= ast_smdi_interface_find("/dev/ttyS0");
7152 ast_log(LOG_ERROR
, "No valid SMDI interface specfied, disabling external voicemail notification\n");
7153 externnotify
[0] = '\0';
7155 ast_log(LOG_DEBUG
, "Using SMDI port %s\n", smdi_iface
->name
);
7159 externnotify
[0] = '\0';
7162 /* Silence treshold */
7163 silencethreshold
= 256;
7164 if ((thresholdstr
= ast_variable_retrieve(cfg
, "general", "silencethreshold")))
7165 silencethreshold
= atoi(thresholdstr
);
7167 if (!(astemail
= ast_variable_retrieve(cfg
, "general", "serveremail")))
7168 astemail
= ASTERISK_USERNAME
;
7169 ast_copy_string(serveremail
, astemail
, sizeof(serveremail
));
7172 if ((s
= ast_variable_retrieve(cfg
, "general", "maxmessage"))) {
7173 if (sscanf(s
, "%d", &x
) == 1) {
7176 ast_log(LOG_WARNING
, "Invalid max message time length\n");
7181 if ((s
= ast_variable_retrieve(cfg
, "general", "minmessage"))) {
7182 if (sscanf(s
, "%d", &x
) == 1) {
7184 if (maxsilence
<= vmminmessage
)
7185 ast_log(LOG_WARNING
, "maxsilence should be less than minmessage or you may get empty messages\n");
7187 ast_log(LOG_WARNING
, "Invalid min message time length\n");
7190 fmt
= ast_variable_retrieve(cfg
, "general", "format");
7193 ast_copy_string(vmfmts
, fmt
, sizeof(vmfmts
));
7196 if ((s
= ast_variable_retrieve(cfg
, "general", "maxgreet"))) {
7197 if (sscanf(s
, "%d", &x
) == 1) {
7200 ast_log(LOG_WARNING
, "Invalid max message greeting length\n");
7204 if ((s
= ast_variable_retrieve(cfg
, "general", "skipms"))) {
7205 if (sscanf(s
, "%d", &x
) == 1) {
7208 ast_log(LOG_WARNING
, "Invalid skipms value\n");
7213 if ((s
= ast_variable_retrieve(cfg
, "general", "maxlogins"))) {
7214 if (sscanf(s
, "%d", &x
) == 1) {
7217 ast_log(LOG_WARNING
, "Invalid max failed login attempts\n");
7221 /* Force new user to record name ? */
7222 if (!(astforcename
= ast_variable_retrieve(cfg
, "general", "forcename")))
7223 astforcename
= "no";
7224 ast_set2_flag((&globalflags
), ast_true(astforcename
), VM_FORCENAME
);
7226 /* Force new user to record greetings ? */
7227 if (!(astforcegreet
= ast_variable_retrieve(cfg
, "general", "forcegreetings")))
7228 astforcegreet
= "no";
7229 ast_set2_flag((&globalflags
), ast_true(astforcegreet
), VM_FORCEGREET
);
7231 if ((s
= ast_variable_retrieve(cfg
, "general", "cidinternalcontexts"))){
7232 ast_log(LOG_DEBUG
,"VM_CID Internal context string: %s\n",s
);
7233 stringp
= ast_strdupa(s
);
7234 for (x
= 0 ; x
< MAX_NUM_CID_CONTEXTS
; x
++){
7235 if (!ast_strlen_zero(stringp
)) {
7236 q
= strsep(&stringp
,",");
7237 while ((*q
== ' ')||(*q
== '\t')) /* Eat white space between contexts */
7239 ast_copy_string(cidinternalcontexts
[x
], q
, sizeof(cidinternalcontexts
[x
]));
7240 ast_log(LOG_DEBUG
,"VM_CID Internal context %d: %s\n", x
, cidinternalcontexts
[x
]);
7242 cidinternalcontexts
[x
][0] = '\0';
7246 if (!(astreview
= ast_variable_retrieve(cfg
, "general", "review"))){
7247 ast_log(LOG_DEBUG
,"VM Review Option disabled globally\n");
7250 ast_set2_flag((&globalflags
), ast_true(astreview
), VM_REVIEW
);
7252 /*Temperary greeting reminder */
7253 if (!(asttempgreetwarn
= ast_variable_retrieve(cfg
, "general", "tempgreetwarn"))) {
7254 ast_log(LOG_DEBUG
, "VM Temperary Greeting Reminder Option disabled globally\n");
7255 asttempgreetwarn
= "no";
7257 ast_log(LOG_DEBUG
, "VM Temperary Greeting Reminder Option enabled globally\n");
7259 ast_set2_flag((&globalflags
), ast_true(asttempgreetwarn
), VM_TEMPGREETWARN
);
7261 if (!(astcallop
= ast_variable_retrieve(cfg
, "general", "operator"))){
7262 ast_log(LOG_DEBUG
,"VM Operator break disabled globally\n");
7265 ast_set2_flag((&globalflags
), ast_true(astcallop
), VM_OPERATOR
);
7267 if (!(astsaycid
= ast_variable_retrieve(cfg
, "general", "saycid"))) {
7268 ast_log(LOG_DEBUG
,"VM CID Info before msg disabled globally\n");
7271 ast_set2_flag((&globalflags
), ast_true(astsaycid
), VM_SAYCID
);
7273 if (!(send_voicemail
= ast_variable_retrieve(cfg
,"general", "sendvoicemail"))){
7274 ast_log(LOG_DEBUG
,"Send Voicemail msg disabled globally\n");
7275 send_voicemail
= "no";
7277 ast_set2_flag((&globalflags
), ast_true(send_voicemail
), VM_SVMAIL
);
7279 if (!(asthearenv
= ast_variable_retrieve(cfg
, "general", "envelope"))) {
7280 ast_log(LOG_DEBUG
,"ENVELOPE before msg enabled globally\n");
7283 ast_set2_flag((&globalflags
), ast_true(asthearenv
), VM_ENVELOPE
);
7285 if (!(astsaydurationinfo
= ast_variable_retrieve(cfg
, "general", "sayduration"))) {
7286 ast_log(LOG_DEBUG
,"Duration info before msg enabled globally\n");
7287 astsaydurationinfo
= "yes";
7289 ast_set2_flag((&globalflags
), ast_true(astsaydurationinfo
), VM_SAYDURATION
);
7291 saydurationminfo
= 2;
7292 if ((astsaydurationminfo
= ast_variable_retrieve(cfg
, "general", "saydurationm"))) {
7293 if (sscanf(astsaydurationminfo
, "%d", &x
) == 1) {
7294 saydurationminfo
= x
;
7296 ast_log(LOG_WARNING
, "Invalid min duration for say duration\n");
7300 if (!(astskipcmd
= ast_variable_retrieve(cfg
, "general", "nextaftercmd"))) {
7301 ast_log(LOG_DEBUG
,"We are not going to skip to the next msg after save/delete\n");
7304 ast_set2_flag((&globalflags
), ast_true(astskipcmd
), VM_SKIPAFTERCMD
);
7306 if ((dialoutcxt
= ast_variable_retrieve(cfg
, "general", "dialout"))) {
7307 ast_copy_string(dialcontext
, dialoutcxt
, sizeof(dialcontext
));
7308 ast_log(LOG_DEBUG
, "found dialout context: %s\n", dialcontext
);
7310 dialcontext
[0] = '\0';
7313 if ((callbackcxt
= ast_variable_retrieve(cfg
, "general", "callback"))) {
7314 ast_copy_string(callcontext
, callbackcxt
, sizeof(callcontext
));
7315 ast_log(LOG_DEBUG
, "found callback context: %s\n", callcontext
);
7317 callcontext
[0] = '\0';
7320 if ((exitcxt
= ast_variable_retrieve(cfg
, "general", "exitcontext"))) {
7321 ast_copy_string(exitcontext
, exitcxt
, sizeof(exitcontext
));
7322 ast_log(LOG_DEBUG
, "found operator context: %s\n", exitcontext
);
7324 exitcontext
[0] = '\0';
7327 if (!(astdirfwd
= ast_variable_retrieve(cfg
, "general", "usedirectory")))
7329 ast_set2_flag((&globalflags
), ast_true(astdirfwd
), VM_DIRECFORWARD
);
7330 if ((ucfg
= ast_config_load("users.conf"))) {
7331 for (cat
= ast_category_browse(ucfg
, NULL
); cat
; cat
= ast_category_browse(ucfg
, cat
)) {
7332 if (!ast_true(ast_config_option(ucfg
, cat
, "hasvoicemail")))
7334 if ((cur
= find_or_create(userscontext
, cat
))) {
7335 populate_defaults(cur
);
7336 apply_options_full(cur
, ast_variable_browse(ucfg
, cat
));
7337 ast_copy_string(cur
->context
, userscontext
, sizeof(cur
->context
));
7340 ast_config_destroy(ucfg
);
7342 cat
= ast_category_browse(cfg
, NULL
);
7344 if (strcasecmp(cat
, "general")) {
7345 var
= ast_variable_browse(cfg
, cat
);
7346 if (strcasecmp(cat
, "zonemessages")) {
7347 /* Process mailboxes in this context */
7349 append_mailbox(cat
, var
->name
, var
->value
);
7353 /* Timezones in this context */
7356 if ((z
= ast_malloc(sizeof(*z
)))) {
7357 char *msg_format
, *timezone
;
7358 msg_format
= ast_strdupa(var
->value
);
7359 timezone
= strsep(&msg_format
, "|");
7361 ast_copy_string(z
->name
, var
->name
, sizeof(z
->name
));
7362 ast_copy_string(z
->timezone
, timezone
, sizeof(z
->timezone
));
7363 ast_copy_string(z
->msg_format
, msg_format
, sizeof(z
->msg_format
));
7364 AST_LIST_LOCK(&zones
);
7365 AST_LIST_INSERT_HEAD(&zones
, z
, list
);
7366 AST_LIST_UNLOCK(&zones
);
7368 ast_log(LOG_WARNING
, "Invalid timezone definition at line %d\n", var
->lineno
);
7373 AST_LIST_UNLOCK(&users
);
7374 ast_config_destroy(cfg
);
7381 cat
= ast_category_browse(cfg
, cat
);
7383 memset(fromstring
,0,sizeof(fromstring
));
7384 memset(pagerfromstring
,0,sizeof(pagerfromstring
));
7385 memset(emailtitle
,0,sizeof(emailtitle
));
7386 strcpy(charset
, "ISO-8859-1");
7393 emailsubject
= NULL
;
7401 pagersubject
= NULL
;
7403 if ((s
= ast_variable_retrieve(cfg
, "general", "pbxskip")))
7404 ast_set2_flag((&globalflags
), ast_true(s
), VM_PBXSKIP
);
7405 if ((s
= ast_variable_retrieve(cfg
, "general", "fromstring")))
7406 ast_copy_string(fromstring
,s
,sizeof(fromstring
));
7407 if ((s
= ast_variable_retrieve(cfg
, "general", "pagerfromstring")))
7408 ast_copy_string(pagerfromstring
,s
,sizeof(pagerfromstring
));
7409 if ((s
= ast_variable_retrieve(cfg
, "general", "charset")))
7410 ast_copy_string(charset
,s
,sizeof(charset
));
7411 if ((s
= ast_variable_retrieve(cfg
, "general", "adsifdn"))) {
7412 sscanf(s
, "%2x%2x%2x%2x", &tmpadsi
[0], &tmpadsi
[1], &tmpadsi
[2], &tmpadsi
[3]);
7413 for (x
= 0; x
< 4; x
++) {
7414 memcpy(&adsifdn
[x
], &tmpadsi
[x
], 1);
7417 if ((s
= ast_variable_retrieve(cfg
, "general", "adsisec"))) {
7418 sscanf(s
, "%2x%2x%2x%2x", &tmpadsi
[0], &tmpadsi
[1], &tmpadsi
[2], &tmpadsi
[3]);
7419 for (x
= 0; x
< 4; x
++) {
7420 memcpy(&adsisec
[x
], &tmpadsi
[x
], 1);
7423 if ((s
= ast_variable_retrieve(cfg
, "general", "adsiver")))
7427 if ((s
= ast_variable_retrieve(cfg
, "general", "emailtitle"))) {
7428 ast_log(LOG_NOTICE
, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
7429 ast_copy_string(emailtitle
,s
,sizeof(emailtitle
));
7431 if ((s
= ast_variable_retrieve(cfg
, "general", "emailsubject")))
7432 emailsubject
= ast_strdup(s
);
7433 if ((s
= ast_variable_retrieve(cfg
, "general", "emailbody"))) {
7434 char *tmpread
, *tmpwrite
;
7435 emailbody
= ast_strdup(s
);
7437 /* substitute strings \t and \n into the appropriate characters */
7438 tmpread
= tmpwrite
= emailbody
;
7439 while ((tmpwrite
= strchr(tmpread
,'\\'))) {
7440 switch (tmpwrite
[1]) {
7442 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7446 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7450 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7454 ast_log(LOG_NOTICE
, "Substitution routine does not support this character: %c\n", tmpwrite
[1]);
7456 tmpread
= tmpwrite
+ 1;
7459 if ((s
= ast_variable_retrieve(cfg
, "general", "pagersubject")))
7460 pagersubject
= ast_strdup(s
);
7461 if ((s
= ast_variable_retrieve(cfg
, "general", "pagerbody"))) {
7462 char *tmpread
, *tmpwrite
;
7463 pagerbody
= ast_strdup(s
);
7465 /* substitute strings \t and \n into the appropriate characters */
7466 tmpread
= tmpwrite
= pagerbody
;
7467 while ((tmpwrite
= strchr(tmpread
, '\\'))) {
7468 switch (tmpwrite
[1]) {
7470 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7474 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7478 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7482 ast_log(LOG_NOTICE
, "Substitution routine does not support this character: %c\n", tmpwrite
[1]);
7484 tmpread
= tmpwrite
+ 1;
7487 AST_LIST_UNLOCK(&users
);
7488 ast_config_destroy(cfg
);
7491 AST_LIST_UNLOCK(&users
);
7492 ast_log(LOG_WARNING
, "Failed to load configuration file. Module not activated.\n");
7497 static int reload(void)
7499 return(load_config());
7502 static int unload_module(void)
7506 res
= ast_unregister_application(app
);
7507 res
|= ast_unregister_application(app2
);
7508 res
|= ast_unregister_application(app3
);
7509 res
|= ast_unregister_application(app4
);
7510 ast_cli_unregister_multiple(cli_voicemail
, sizeof(cli_voicemail
) / sizeof(struct ast_cli_entry
));
7511 ast_uninstall_vm_functions();
7513 ast_module_user_hangup_all();
7518 static int load_module(void)
7521 res
= ast_register_application(app
, vm_exec
, synopsis_vm
, descrip_vm
);
7522 res
|= ast_register_application(app2
, vm_execmain
, synopsis_vmain
, descrip_vmain
);
7523 res
|= ast_register_application(app3
, vm_box_exists
, synopsis_vm_box_exists
, descrip_vm_box_exists
);
7524 res
|= ast_register_application(app4
, vmauthenticate
, synopsis_vmauthenticate
, descrip_vmauthenticate
);
7528 if ((res
=load_config())) {
7532 ast_cli_register_multiple(cli_voicemail
, sizeof(cli_voicemail
) / sizeof(struct ast_cli_entry
));
7534 /* compute the location of the voicemail spool directory */
7535 snprintf(VM_SPOOL_DIR
, sizeof(VM_SPOOL_DIR
), "%s/voicemail/", ast_config_AST_SPOOL_DIR
);
7537 ast_install_vm_functions(has_voicemail
, inboxcount
, messagecount
);
7542 static int dialout(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *num
, char *outgoing_context
)
7545 char destination
[80] = "";
7549 if (option_verbose
> 2)
7550 ast_verbose( VERBOSE_PREFIX_3
"Destination number will be entered manually\n");
7551 while (retries
< 3 && cmd
!= 't') {
7552 destination
[1] = '\0';
7553 destination
[0] = cmd
= ast_play_and_wait(chan
,"vm-enter-num-to-call");
7555 destination
[0] = cmd
= ast_play_and_wait(chan
, "vm-then-pound");
7557 destination
[0] = cmd
= ast_play_and_wait(chan
, "vm-star-cancel");
7559 cmd
= ast_waitfordigit(chan
, 6000);
7561 destination
[0] = cmd
;
7570 if (option_verbose
> 2)
7571 ast_verbose( VERBOSE_PREFIX_3
"User hit '*' to cancel outgoing call\n");
7574 if ((cmd
= ast_readstring(chan
,destination
+ strlen(destination
),sizeof(destination
)-1,6000,10000,"#")) < 0)
7585 if (option_verbose
> 2)
7586 ast_verbose( VERBOSE_PREFIX_3
"Destination number is CID number '%s'\n", num
);
7587 ast_copy_string(destination
, num
, sizeof(destination
));
7590 if (!ast_strlen_zero(destination
)) {
7591 if (destination
[strlen(destination
) -1 ] == '*')
7593 if (option_verbose
> 2)
7594 ast_verbose( VERBOSE_PREFIX_3
"Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination
, outgoing_context
, chan
->context
);
7595 ast_copy_string(chan
->exten
, destination
, sizeof(chan
->exten
));
7596 ast_copy_string(chan
->context
, outgoing_context
, sizeof(chan
->context
));
7603 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
)
7607 char origtimeS
[256],cidS
[256],contextS
[256];
7608 char *header_content
,*temp
;
7611 struct ast_config
*msg_cfg
= NULL
;
7612 const char *origtime
, *context
;
7613 char *cid
, *name
, *num
;
7619 /* get the message info!! */
7620 if(option_debug
> 2)
7621 ast_log (LOG_DEBUG
,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms
->curmsg
, vms
->msgArray
[vms
->curmsg
]);
7622 if (vms
->msgArray
[vms
->curmsg
] == 0) {
7623 ast_log (LOG_WARNING
,"Trying to access unknown message\n");
7627 /* This will only work for new messages... */
7628 header_content
= mail_fetchheader (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
]);
7629 /* empty string means no valid header */
7630 if (ast_strlen_zero(header_content
)) {
7631 ast_log (LOG_ERROR
,"Could not fetch header for message number %ld\n",vms
->msgArray
[vms
->curmsg
]);
7635 /* Get info from headers!! */
7636 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Caller-ID-Num:");
7639 ast_copy_string(cidS
,temp
, sizeof(cidS
));
7644 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Context:");
7647 ast_copy_string(contextS
,temp
, sizeof(contextS
));
7651 context
= &contextS
[0];
7652 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Orig-time:");
7655 ast_copy_string(origtimeS
,temp
, sizeof(origtimeS
));
7657 origtimeS
[0] = '\0';
7659 origtime
= &origtimeS
[0];
7661 ast_copy_string(filename
, "IMAP_STORAGE", sizeof(filename
));
7663 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, msg
);
7665 /* Retrieve info from VM attribute file */
7667 make_file(vms
->fn2
, sizeof(vms
->fn2
), vms
->curdir
, vms
->curmsg
);
7668 snprintf(filename
,sizeof(filename
), "%s.txt", vms
->fn2
);
7669 RETRIEVE(vms
->curdir
, vms
->curmsg
);
7670 msg_cfg
= ast_config_load(filename
);
7671 DISPOSE(vms
->curdir
, vms
->curmsg
);
7673 ast_log(LOG_WARNING
, "No message attribute file?!! (%s)\n", filename
);
7677 if (!(origtime
= ast_variable_retrieve(msg_cfg
, "message", "origtime"))) {
7678 ast_config_destroy(msg_cfg
);
7682 cid
= ast_strdupa(ast_variable_retrieve(msg_cfg
, "message", "callerid"));
7684 context
= ast_variable_retrieve(msg_cfg
, "message", "context");
7685 if (!strncasecmp("macro",context
,5)) /* Macro names in contexts are useless for our needs */
7686 context
= ast_variable_retrieve(msg_cfg
, "message","macrocontext");
7691 res
= play_message_datetime(chan
, vmu
, origtime
, filename
);
7693 res
= play_message_callerid(chan
, vms
, cid
, context
, 0);
7698 case 2: /* Call back */
7700 if (ast_strlen_zero(cid
))
7703 ast_callerid_parse(cid
, &name
, &num
);
7704 while ((res
> -1) && (res
!= 't')) {
7708 /* Dial the CID number */
7709 res
= dialout(chan
, vmu
, num
, vmu
->callback
);
7711 ast_config_destroy(msg_cfg
);
7720 /* Want to enter a different number, can only do this if there's a dialout context for this user */
7721 if (!ast_strlen_zero(vmu
->dialout
)) {
7722 res
= dialout(chan
, vmu
, NULL
, vmu
->dialout
);
7724 ast_config_destroy(msg_cfg
);
7728 if (option_verbose
> 2)
7729 ast_verbose( VERBOSE_PREFIX_3
"Caller can not specify callback number - no dialout context available\n");
7730 res
= ast_play_and_wait(chan
, "vm-sorry");
7732 ast_config_destroy(msg_cfg
);
7746 res
= ast_play_and_wait(chan
, "vm-sorry");
7751 if (option_verbose
> 2)
7752 ast_verbose( VERBOSE_PREFIX_3
"Confirm CID number '%s' is number to use for callback\n", num
);
7753 res
= ast_play_and_wait(chan
, "vm-num-i-have");
7755 res
= play_message_callerid(chan
, vms
, num
, vmu
->context
, 1);
7757 res
= ast_play_and_wait(chan
, "vm-tocallnum");
7758 /* Only prompt for a caller-specified number if there is a dialout context specified */
7759 if (!ast_strlen_zero(vmu
->dialout
)) {
7761 res
= ast_play_and_wait(chan
, "vm-calldiffnum");
7764 res
= ast_play_and_wait(chan
, "vm-nonumber");
7765 if (!ast_strlen_zero(vmu
->dialout
)) {
7767 res
= ast_play_and_wait(chan
, "vm-toenternumber");
7771 res
= ast_play_and_wait(chan
, "vm-star-cancel");
7773 res
= ast_waitfordigit(chan
, 6000);
7784 else if (res
== '*')
7790 /* Send reply directly to sender */
7791 if (ast_strlen_zero(cid
))
7794 ast_callerid_parse(cid
, &name
, &num
);
7796 if (option_verbose
> 2)
7797 ast_verbose(VERBOSE_PREFIX_3
"No CID number available, no reply sent\n");
7799 res
= ast_play_and_wait(chan
, "vm-nonumber");
7800 ast_config_destroy(msg_cfg
);
7803 if (find_user(NULL
, vmu
->context
, num
)) {
7804 struct leave_vm_options leave_options
;
7805 char mailbox
[AST_MAX_EXTENSION
* 2 + 2];
7806 snprintf(mailbox
, sizeof(mailbox
), "%s@%s", num
, vmu
->context
);
7808 if (option_verbose
> 2)
7809 ast_verbose(VERBOSE_PREFIX_3
"Leaving voicemail for '%s' in context '%s'\n", num
, vmu
->context
);
7811 memset(&leave_options
, 0, sizeof(leave_options
));
7812 leave_options
.record_gain
= record_gain
;
7813 res
= leave_voicemail(chan
, mailbox
, &leave_options
);
7816 ast_config_destroy(msg_cfg
);
7819 /* Sender has no mailbox, can't reply */
7820 if (option_verbose
> 2)
7821 ast_verbose( VERBOSE_PREFIX_3
"No mailbox number '%s' in context '%s', no reply sent\n", num
, vmu
->context
);
7822 ast_play_and_wait(chan
, "vm-nobox");
7824 ast_config_destroy(msg_cfg
);
7833 #ifndef IMAP_STORAGE
7834 ast_config_destroy(msg_cfg
);
7837 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, msg
);
7838 vms
->heard
[msg
] = 1;
7839 res
= wait_file(chan
, vms
, vms
->fn
);
7845 static int play_record_review(struct ast_channel
*chan
, char *playfile
, char *recordfile
, int maxtime
, char *fmt
,
7846 int outsidecaller
, struct ast_vm_user
*vmu
, int *duration
, const char *unlockdir
,
7847 signed char record_gain
)
7849 /* Record message & let caller review or re-record it, or set options if applicable */
7852 int max_attempts
= 3;
7855 int message_exists
= 0;
7856 signed char zero_gain
= 0;
7857 char *acceptdtmf
= "#";
7858 char *canceldtmf
= "";
7860 /* Note that urgent and private are for flagging messages as such in the future */
7862 /* barf if no pointer passed to store duration in */
7863 if (duration
== NULL
) {
7864 ast_log(LOG_WARNING
, "Error play_record_review called without duration pointer\n");
7868 cmd
= '3'; /* Want to start by recording */
7870 while ((cmd
>= 0) && (cmd
!= 't')) {
7873 if (!message_exists
) {
7874 /* In this case, 1 is to record a message */
7878 /* Otherwise 1 is to save the existing message */
7879 if (option_verbose
> 2)
7880 ast_verbose(VERBOSE_PREFIX_3
"Saving message as is\n");
7881 ast_stream_and_wait(chan
, "vm-msgsaved", chan
->language
, "");
7882 STORE(recordfile
, vmu
->mailbox
, vmu
->context
, -1, chan
, vmu
, fmt
, duration
, vms
);
7883 DISPOSE(recordfile
, -1);
7889 if (option_verbose
> 2)
7890 ast_verbose(VERBOSE_PREFIX_3
"Reviewing the message\n");
7891 cmd
= ast_stream_and_wait(chan
, recordfile
, chan
->language
, AST_DIGIT_ANY
);
7896 if (recorded
== 1) {
7897 if (option_verbose
> 2)
7898 ast_verbose(VERBOSE_PREFIX_3
"Re-recording the message\n");
7900 if (option_verbose
> 2)
7901 ast_verbose(VERBOSE_PREFIX_3
"Recording the message\n");
7903 if (recorded
&& outsidecaller
) {
7904 cmd
= ast_play_and_wait(chan
, INTRO
);
7905 cmd
= ast_play_and_wait(chan
, "beep");
7908 /* 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 */
7910 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &record_gain
, sizeof(record_gain
), 0);
7911 if (ast_test_flag(vmu
, VM_OPERATOR
))
7913 cmd
= ast_play_and_record_full(chan
, playfile
, recordfile
, maxtime
, fmt
, duration
, silencethreshold
, maxsilence
, unlockdir
, acceptdtmf
, canceldtmf
);
7915 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &zero_gain
, sizeof(zero_gain
), 0);
7917 /* User has hung up, no options to give */
7922 } else if (cmd
== '*') {
7926 else if (vmu
->review
&& (*duration
< 5)) {
7927 /* Message is too short */
7928 if (option_verbose
> 2)
7929 ast_verbose(VERBOSE_PREFIX_3
"Message too short\n");
7930 cmd
= ast_play_and_wait(chan
, "vm-tooshort");
7931 cmd
= vm_delete(recordfile
);
7934 else if (vmu
->review
&& (cmd
== 2 && *duration
< (maxsilence
+ 3))) {
7935 /* Message is all silence */
7936 if (option_verbose
> 2)
7937 ast_verbose(VERBOSE_PREFIX_3
"Nothing recorded\n");
7938 cmd
= vm_delete(recordfile
);
7939 cmd
= ast_play_and_wait(chan
, "vm-nothingrecorded");
7941 cmd
= ast_play_and_wait(chan
, "vm-speakup");
7946 /* If all is well, a message exists */
7959 cmd
= ast_play_and_wait(chan
, "vm-sorry");
7962 /* XXX Commented out for the moment because of the dangers of deleting
7963 a message while recording (can put the message numbers out of sync) */
7965 /* Cancel recording, delete message, offer to take another message*/
7966 cmd
= ast_play_and_wait(chan
, "vm-deleted");
7967 cmd
= vm_delete(recordfile
);
7968 if (outsidecaller
) {
7969 res
= vm_exec(chan
, NULL
);
7976 if (!ast_test_flag(vmu
, VM_OPERATOR
)) {
7977 cmd
= ast_play_and_wait(chan
, "vm-sorry");
7980 if (message_exists
|| recorded
) {
7981 cmd
= ast_play_and_wait(chan
, "vm-saveoper");
7983 cmd
= ast_waitfordigit(chan
, 3000);
7985 ast_play_and_wait(chan
, "vm-msgsaved");
7988 ast_play_and_wait(chan
, "vm-deleted");
7989 DELETE(recordfile
, -1, recordfile
);
7995 /* If the caller is an ouside caller, and the review option is enabled,
7996 allow them to review the message, but let the owner of the box review
7998 if (outsidecaller
&& !ast_test_flag(vmu
, VM_REVIEW
))
8000 if (message_exists
) {
8001 cmd
= ast_play_and_wait(chan
, "vm-review");
8004 cmd
= ast_play_and_wait(chan
, "vm-torerecord");
8006 cmd
= ast_waitfordigit(chan
, 600);
8009 if (!cmd
&& outsidecaller
&& ast_test_flag(vmu
, VM_OPERATOR
)) {
8010 cmd
= ast_play_and_wait(chan
, "vm-reachoper");
8012 cmd
= ast_waitfordigit(chan
, 600);
8016 cmd
= ast_play_and_wait(chan
, "vm-tocancelmsg");
8019 cmd
= ast_waitfordigit(chan
, 6000);
8023 if (attempts
> max_attempts
) {
8029 ast_play_and_wait(chan
, "vm-goodbye");
8037 static void write_file(char *filename
, char *buffer
, unsigned long len
)
8041 output
= fopen (filename
, "w");
8042 fwrite (buffer
, len
, 1, output
);
8046 void mm_searched(MAILSTREAM
*stream
, unsigned long number
)
8048 struct vm_state
*vms
;
8051 mailbox
= stream
->mailbox
;
8052 user
= get_user_by_mailbox(mailbox
);
8054 vms
= get_vm_state_by_imapuser(user
,2);
8056 if(option_debug
> 2)
8057 ast_log (LOG_DEBUG
, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number
,vms
->vmArrayIndex
,vms
->interactive
);
8058 vms
->msgArray
[vms
->vmArrayIndex
++] = number
;
8060 ast_log (LOG_ERROR
, "No state found.\n");
8066 * Accepts: BODY structure pointer
8070 static void display_body(BODY
*body
, char *pfx
, long i
)
8072 char tmp
[MAILTMPLEN
];
8075 PART
*part
; /* multipart doesn't have a row to itself */
8076 if (body
->type
== TYPEMULTIPART
) {
8077 /* if not first time, extend prefix */
8079 sprintf (tmp
, "%s%ld.", pfx
, ++i
);
8082 for (i
= 0, part
= body
->nested
.part
; part
; part
= part
->next
)
8083 display_body (&part
->body
, tmp
, i
++);
8084 } else { /* non-multipart, output oneline descriptor */
8086 pfx
= ""; /* dummy prefix if top level */
8087 sprintf (s
, " %s%ld %s", pfx
, ++i
, body_types
[body
->type
]);
8089 sprintf (s
+= strlen (s
), "/%s", body
->subtype
);
8090 if (body
->description
)
8091 sprintf (s
+= strlen (s
), " (%s)", body
->description
);
8092 if ((par
= body
->parameter
))
8094 sprintf (s
+= strlen (s
), ";%s=%s", par
->attribute
, par
->value
);
8095 while ((par
= par
->next
));
8097 sprintf (s
+= strlen (s
), ", id = %s", body
->id
);
8098 switch (body
->type
) { /* bytes or lines depending upon body type */
8099 case TYPEMESSAGE
: /* encapsulated message */
8100 case TYPETEXT
: /* plain text */
8101 sprintf (s
+= strlen (s
), " (%lu lines)", body
->size
.lines
);
8104 sprintf (s
+= strlen (s
), " (%lu bytes)", body
->size
.bytes
);
8107 /* ast_log (LOG_NOTICE,tmp); output this line */
8108 /* encapsulated message? */
8109 if ((body
->type
== TYPEMESSAGE
) && !strcmp (body
->subtype
, "RFC822") && (body
= body
->nested
.msg
->body
)) {
8110 if (body
->type
== TYPEMULTIPART
)
8111 display_body (body
, pfx
, i
- 1);
8112 else { /* build encapsulation prefix */
8113 sprintf (tmp
, "%s%ld.", pfx
, i
);
8114 display_body (body
, tmp
, (long) 0);
8120 #if 0 /*No need for this. */
8122 * Accepts: MAIL stream
8124 static void status(MAILSTREAM
*stream
)
8127 char *s
, date
[MAILTMPLEN
];
8129 AUTHENTICATOR
*auth
;
8131 ast_log (LOG_NOTICE
,"%s\n",date
);
8133 if (stream
->mailbox
)
8134 ast_log (LOG_NOTICE
," %s mailbox: %s, %lu messages, %lu recent\n",
8135 stream
->dtb
->name
, stream
->mailbox
, stream
->nmsgs
,stream
->recent
);
8137 ast_log (LOG_NOTICE
,"No mailbox is open on this stream\n");
8138 if (stream
->user_flags
[0]) {
8139 ast_log (LOG_NOTICE
,"Keywords: %s\n", stream
->user_flags
[0]);
8140 for (i
= 1; i
< NUSERFLAGS
&& stream
->user_flags
[i
]; ++i
)
8141 ast_log (LOG_NOTICE
," %s\n", stream
->user_flags
[i
]);
8143 if (!strcmp (stream
->dtb
->name
, "imap")) {
8144 if (LEVELIMAP4rev1 (stream
))
8145 s
= "IMAP4rev1 (RFC 3501)";
8146 else if (LEVEL1730 (stream
))
8147 s
= "IMAP4 (RFC 1730)";
8148 else if (LEVELIMAP2bis (stream
))
8150 else if (LEVEL1176 (stream
))
8151 s
= "IMAP2 (RFC 1176)";
8153 s
= "IMAP2 (RFC 1064)";
8154 ast_log (LOG_NOTICE
,"%s server %s\n", s
, imap_host (stream
));
8155 if (LEVELIMAP4 (stream
)) {
8156 if ((i
= (imap_cap(stream
)->auth
))) {
8158 ast_log (LOG_NOTICE
,"Mutually-supported SASL mechanisms:\n");
8159 while ((auth
= mail_lookup_auth (find_rightmost_bit (&i
) + 1))) {
8160 ast_log (LOG_NOTICE
," %s\n", auth
->name
);
8161 if (!strcmp (auth
->name
, "PLAIN"))
8162 s
= "\n [LOGIN will not be listed here if PLAIN is supported]\n";
8164 ast_log (LOG_NOTICE
,s
);
8166 ast_log (LOG_NOTICE
,"Supported standard extensions:\n");
8167 if (LEVELACL (stream
))
8168 ast_log (LOG_NOTICE
," Access Control lists (RFC 2086)\n");
8169 if (LEVELQUOTA (stream
))
8170 ast_log (LOG_NOTICE
," Quotas (RFC 2087)\n");
8171 if (LEVELLITERALPLUS (stream
))
8172 ast_log (LOG_NOTICE
," Non-synchronizing literals (RFC 2088)\n");
8173 if (LEVELIDLE (stream
))
8174 ast_log (LOG_NOTICE
," IDLE unsolicited update (RFC 2177)\n");
8175 if (LEVELMBX_REF (stream
))
8176 ast_log (LOG_NOTICE
," Mailbox referrals (RFC 2193)\n");
8177 if (LEVELLOG_REF (stream
))
8178 ast_log (LOG_NOTICE
," Login referrals (RFC 2221)\n");
8179 if (LEVELANONYMOUS (stream
))
8180 ast_log (LOG_NOTICE
," Anonymous access (RFC 2245)\n");
8181 if (LEVELNAMESPACE (stream
))
8182 ast_log (LOG_NOTICE
," Multiple namespaces (RFC 2342)\n");
8183 if (LEVELUIDPLUS (stream
))
8184 ast_log (LOG_NOTICE
," Extended UID behavior (RFC 2359)\n");
8185 if (LEVELSTARTTLS (stream
))
8186 ast_log (LOG_NOTICE
," Transport Layer Security (RFC 2595)\n");
8187 if (LEVELLOGINDISABLED (stream
))
8188 ast_log (LOG_NOTICE
," LOGIN command disabled (RFC 2595)\n");
8189 if (LEVELID (stream
))
8190 ast_log (LOG_NOTICE
," Implementation identity negotiation (RFC 2971)\n");
8191 if (LEVELCHILDREN (stream
))
8192 ast_log (LOG_NOTICE
," LIST children announcement (RFC 3348)\n");
8193 if (LEVELMULTIAPPEND (stream
))
8194 ast_log (LOG_NOTICE
," Atomic multiple APPEND (RFC 3502)\n");
8195 if (LEVELBINARY (stream
))
8196 ast_log (LOG_NOTICE
," Binary body content (RFC 3516)\n");
8197 ast_log (LOG_NOTICE
,"Supported draft extensions:\n");
8198 if (LEVELUNSELECT (stream
))
8199 ast_log (LOG_NOTICE
," Mailbox unselect\n");
8200 if (LEVELSASLIR (stream
))
8201 ast_log (LOG_NOTICE
," SASL initial client response\n");
8202 if (LEVELSORT (stream
))
8203 ast_log (LOG_NOTICE
," Server-based sorting\n");
8204 if (LEVELTHREAD (stream
)) {
8205 ast_log (LOG_NOTICE
," Server-based threading:\n");
8206 for (thr
= imap_cap(stream
)->threader
; thr
; thr
= thr
->next
)
8207 ast_log (LOG_NOTICE
," %s\n", thr
->name
);
8209 if (LEVELSCAN (stream
))
8210 ast_log (LOG_NOTICE
," Mailbox text scan\n");
8211 if ((i
= imap_cap(stream
)->extlevel
)) {
8212 ast_log (LOG_NOTICE
,"Supported BODYSTRUCTURE extensions:\n");
8215 ast_log (LOG_NOTICE
," location\n");
8217 ast_log (LOG_NOTICE
," language\n");
8219 ast_log (LOG_NOTICE
," disposition\n");
8221 ast_log (LOG_NOTICE
," MD5\n");
8225 ast_log (LOG_NOTICE
,"\n");
8231 /* Interfaces to C-client */
8233 void mm_exists(MAILSTREAM
* stream
, unsigned long number
)
8235 /* mail_ping will callback here if new mail! */
8236 if(option_debug
> 3)
8237 ast_log (LOG_DEBUG
, "Entering EXISTS callback for message %ld\n", number
);
8238 if (number
== 0) return;
8243 void mm_expunged(MAILSTREAM
* stream
, unsigned long number
)
8245 /* mail_ping will callback here if expunged mail! */
8246 if(option_debug
> 3)
8247 ast_log (LOG_DEBUG
, "Entering EXPUNGE callback for message %ld\n", number
);
8248 if (number
== 0) return;
8253 void mm_flags(MAILSTREAM
* stream
, unsigned long number
)
8255 /* mail_ping will callback here if read mail! */
8256 if(option_debug
> 3)
8257 ast_log (LOG_DEBUG
, "Entering FLAGS callback for message %ld\n", number
);
8258 if (number
== 0) return;
8263 void mm_notify(MAILSTREAM
* stream
, char *string
, long errflg
)
8265 mm_log (string
, errflg
);
8269 void mm_list(MAILSTREAM
* stream
, int delim
, char *mailbox
, long attributes
)
8271 if (delimiter
== '\0') {
8272 ast_mutex_lock(&delimiter_lock
);
8274 ast_mutex_unlock(&delimiter_lock
);
8276 if (option_debug
> 4) {
8277 ast_log(LOG_DEBUG
, "Delimiter set to %c and mailbox %s\n",delim
, mailbox
);
8278 if (attributes
& LATT_NOINFERIORS
)
8279 ast_log(LOG_DEBUG
, "no inferiors\n");
8280 if (attributes
& LATT_NOSELECT
)
8281 ast_log(LOG_DEBUG
, "no select\n");
8282 if (attributes
& LATT_MARKED
)
8283 ast_log(LOG_DEBUG
, "marked\n");
8284 if (attributes
& LATT_UNMARKED
)
8285 ast_log(LOG_DEBUG
, "unmarked\n");
8290 void mm_lsub(MAILSTREAM
* stream
, int delimiter
, char *mailbox
, long attributes
)
8292 if (option_debug
> 4) {
8293 ast_log(LOG_DEBUG
, "Delimiter set to %c and mailbox %s\n",delimiter
, mailbox
);
8294 if (attributes
& LATT_NOINFERIORS
)
8295 ast_log(LOG_DEBUG
, "no inferiors\n");
8296 if (attributes
& LATT_NOSELECT
)
8297 ast_log(LOG_DEBUG
, "no select\n");
8298 if (attributes
& LATT_MARKED
)
8299 ast_log(LOG_DEBUG
, "marked\n");
8300 if (attributes
& LATT_UNMARKED
)
8301 ast_log(LOG_DEBUG
, "unmarked\n");
8306 void mm_status(MAILSTREAM
* stream
, char *mailbox
, MAILSTATUS
* status
)
8308 ast_log (LOG_NOTICE
," Mailbox %s", mailbox
);
8309 if (status
->flags
& SA_MESSAGES
)
8310 ast_log (LOG_NOTICE
,", %lu messages", status
->messages
);
8311 if (status
->flags
& SA_RECENT
)
8312 ast_log (LOG_NOTICE
,", %lu recent", status
->recent
);
8313 if (status
->flags
& SA_UNSEEN
)
8314 ast_log (LOG_NOTICE
,", %lu unseen", status
->unseen
);
8315 if (status
->flags
& SA_UIDVALIDITY
)
8316 ast_log (LOG_NOTICE
,", %lu UID validity", status
->uidvalidity
);
8317 if (status
->flags
& SA_UIDNEXT
)
8318 ast_log (LOG_NOTICE
,", %lu next UID", status
->uidnext
);
8319 ast_log (LOG_NOTICE
,"\n");
8323 void mm_log(char *string
, long errflg
)
8325 switch ((short) errflg
) {
8328 ast_log(LOG_DEBUG
,"IMAP Info: %s\n", string
);
8332 ast_log (LOG_WARNING
,"IMAP Warning: %s\n", string
);
8335 ast_log (LOG_ERROR
,"IMAP Error: %s\n", string
);
8341 void mm_dlog(char *string
)
8343 ast_log (LOG_NOTICE
, "%s\n", string
);
8347 void mm_login(NETMBX
* mb
, char *user
, char *pwd
, long trial
)
8349 struct ast_vm_user
*vmu
;
8351 if(option_debug
> 3)
8352 ast_log(LOG_DEBUG
, "Entering callback mm_login\n");
8354 ast_copy_string(user
, mb
->user
, MAILTMPLEN
);
8356 /* We should only do this when necessary */
8357 if (!ast_strlen_zero(authpassword
)) {
8358 ast_copy_string(pwd
, authpassword
, MAILTMPLEN
);
8360 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
8361 if(!strcasecmp(mb
->user
, vmu
->imapuser
)) {
8362 ast_copy_string(pwd
, vmu
->imappassword
, MAILTMPLEN
);
8370 void mm_critical(MAILSTREAM
* stream
)
8375 void mm_nocritical(MAILSTREAM
* stream
)
8380 long mm_diskerror(MAILSTREAM
* stream
, long errcode
, long serious
)
8382 kill (getpid (), SIGSTOP
);
8387 void mm_fatal(char *string
)
8389 ast_log(LOG_ERROR
,"IMAP access FATAL error: %s\n", string
);
8392 /* C-client callback to handle quota */
8393 static void mm_parsequota(MAILSTREAM
*stream
, unsigned char *msg
, QUOTALIST
*pquota
)
8395 struct vm_state
*vms
;
8398 unsigned long usage
= 0;
8399 unsigned long limit
= 0;
8402 usage
= pquota
->usage
;
8403 limit
= pquota
->limit
;
8404 pquota
= pquota
->next
;
8407 mailbox
= stream
->mailbox
;
8408 user
= get_user_by_mailbox(mailbox
);
8409 vms
= get_vm_state_by_imapuser(user
,2);
8411 if(option_debug
> 2)
8412 ast_log (LOG_DEBUG
, "User %s usage is %lu, limit is %lu\n",user
,usage
,limit
);
8413 vms
->quota_usage
= usage
;
8414 vms
->quota_limit
= limit
;
8416 ast_log (LOG_ERROR
, "No state found.\n");
8420 static char *get_header_by_tag(char *header
, char *tag
)
8426 if (!header
|| !tag
)
8429 taglen
= strlen(tag
) + 1;
8433 start
= strstr(header
, tag
);
8437 ast_mutex_lock(&imaptemp_lock
);
8438 ast_copy_string(imaptemp
, start
+taglen
, sizeof(imaptemp
));
8439 ast_mutex_unlock(&imaptemp_lock
);
8440 eol_pnt
= strchr(imaptemp
,'\n');
8445 static char *get_user_by_mailbox(char *mailbox
)
8447 char *start
, *quote
;
8453 start
= strstr(mailbox
,"user=");
8457 ast_mutex_lock(&imaptemp_lock
);
8458 ast_copy_string(imaptemp
, start
+5, sizeof(imaptemp
));
8459 ast_mutex_unlock(&imaptemp_lock
);
8461 quote
= strchr(imaptemp
,'\"');
8462 if (!quote
) { /* if username is not in quotes */
8463 eol_pnt
= strchr(imaptemp
,'/');
8465 eol_pnt
= strchr(imaptemp
,'}');
8470 eol_pnt
= strchr(imaptemp
+1,'\"');
8476 static struct vm_state
*get_vm_state_by_imapuser(char *user
, int interactive
)
8478 struct vmstate
*vlist
= NULL
;
8483 if (vlist
->vms
->imapuser
) {
8484 if (!strcmp(vlist
->vms
->imapuser
,user
)) {
8485 if (interactive
== 2) {
8487 } else if (vlist
->vms
->interactive
== interactive
) {
8492 if(option_debug
> 2)
8493 ast_log(LOG_DEBUG
, " error: imapuser is NULL for %s\n",user
);
8496 if(option_debug
> 2)
8497 ast_log(LOG_DEBUG
, " error: vms is NULL for %s\n",user
);
8499 vlist
= vlist
->next
;
8501 if(option_debug
> 2)
8502 ast_log(LOG_DEBUG
, "%s not found in vmstates\n",user
);
8506 static struct vm_state
*get_vm_state_by_mailbox(const char *mailbox
, int interactive
)
8508 struct vmstate
*vlist
= NULL
;
8511 if(option_debug
> 2)
8512 ast_log(LOG_DEBUG
, "Mailbox set to %s\n",mailbox
);
8515 if (vlist
->vms
->username
) {
8516 if(option_debug
> 2)
8517 ast_log(LOG_DEBUG
, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox
,interactive
,vlist
->vms
->username
,vlist
->vms
->interactive
);
8518 if (!strcmp(vlist
->vms
->username
,mailbox
) && vlist
->vms
->interactive
== interactive
) {
8519 if(option_debug
> 2)
8520 ast_log(LOG_DEBUG
, " Found it!\n");
8524 if(option_debug
> 2)
8525 ast_log(LOG_DEBUG
, " error: username is NULL for %s\n",mailbox
);
8528 if(option_debug
> 2)
8529 ast_log(LOG_DEBUG
, " error: vms is NULL for %s\n",mailbox
);
8531 vlist
= vlist
->next
;
8533 if(option_debug
> 2)
8534 ast_log(LOG_DEBUG
, "%s not found in vmstates\n",mailbox
);
8538 static void vmstate_insert(struct vm_state
*vms
)
8541 struct vm_state
*altvms
;
8543 /* If interactive, it probably already exists, and we should
8544 use the one we already have since it is more up to date.
8545 We can compare the username to find the duplicate */
8546 if (vms
->interactive
== 1) {
8547 altvms
= get_vm_state_by_mailbox(vms
->username
,0);
8549 if(option_debug
> 2)
8550 ast_log(LOG_DEBUG
, "Duplicate mailbox %s, copying message info...\n",vms
->username
);
8551 vms
->newmessages
= altvms
->newmessages
;
8552 vms
->oldmessages
= altvms
->oldmessages
;
8553 if(option_debug
> 2)
8554 ast_log(LOG_DEBUG
, "check_msgArray before memcpy\n");
8555 check_msgArray(vms
);
8556 /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
8557 copy_msgArray(vms
, altvms
);
8558 if(option_debug
> 2)
8559 ast_log(LOG_DEBUG
, "check_msgArray after memcpy\n");
8560 check_msgArray(vms
);
8561 vms
->vmArrayIndex
= altvms
->vmArrayIndex
;
8562 vms
->lastmsg
= altvms
->lastmsg
;
8563 vms
->curmsg
= altvms
->curmsg
;
8564 /* get a pointer to the persistent store */
8565 vms
->persist_vms
= altvms
;
8566 /* Reuse the mailstream? */
8567 vms
->mailstream
= altvms
->mailstream
;
8568 /* vms->mailstream = NIL; */
8572 v
= (struct vmstate
*)malloc(sizeof(struct vmstate
));
8574 ast_log(LOG_ERROR
, "Out of memory\n");
8576 if(option_debug
> 2)
8577 ast_log(LOG_DEBUG
, "Inserting vm_state for user:%s, mailbox %s\n",vms
->imapuser
,vms
->username
);
8578 ast_mutex_lock(&vmstate_lock
);
8582 ast_mutex_unlock(&vmstate_lock
);
8585 static void vmstate_delete(struct vm_state
*vms
)
8587 struct vmstate
*vc
, *vf
= NULL
, *vl
= NULL
;
8588 struct vm_state
*altvms
;
8590 /* If interactive, we should copy pertainent info
8591 back to the persistent state (to make update immediate) */
8592 if (vms
->interactive
== 1) {
8593 altvms
= vms
->persist_vms
;
8595 if(option_debug
> 2)
8596 ast_log(LOG_DEBUG
, "Duplicate mailbox %s, copying message info...\n",vms
->username
);
8597 altvms
->newmessages
= vms
->newmessages
;
8598 altvms
->oldmessages
= vms
->oldmessages
;
8599 altvms
->updated
= 2;
8603 ast_mutex_lock(&vmstate_lock
);
8605 if(option_debug
> 2)
8606 ast_log(LOG_DEBUG
, "Removing vm_state for user:%s, mailbox %s\n",vms
->imapuser
,vms
->username
);
8608 if (vc
->vms
== vms
) {
8611 vl
->next
= vc
->next
;
8613 vmstates
= vc
->next
;
8620 ast_log(LOG_ERROR
, "No vmstate found for user:%s, mailbox %s\n",vms
->imapuser
,vms
->username
);
8624 ast_mutex_unlock(&vmstate_lock
);
8627 static void set_update(MAILSTREAM
* stream
)
8629 struct vm_state
*vms
;
8633 mailbox
= stream
->mailbox
;
8634 user
= get_user_by_mailbox(mailbox
);
8635 vms
= get_vm_state_by_imapuser(user
, 0);
8637 if(option_debug
> 2)
8638 ast_log (LOG_DEBUG
, "User %s mailbox set for update.\n",user
);
8639 vms
->updated
= 2; /* set updated flag since mailbox changed */
8641 if(option_debug
> 2)
8642 ast_log (LOG_WARNING
, "User %s mailbox not found for update.\n",user
);
8646 static void init_vm_state(struct vm_state
*vms
)
8649 vms
->vmArrayIndex
= 0;
8650 for (x
= 0; x
< 256; x
++) {
8651 vms
->msgArray
[x
] = 0;
8655 static void check_msgArray(struct vm_state
*vms
)
8658 for (x
= 0; x
<256; x
++) {
8659 if (vms
->msgArray
[x
]!=0) {
8661 ast_log (LOG_DEBUG
, "Item %d set to %ld\n",x
,vms
->msgArray
[x
]);
8666 static void copy_msgArray(struct vm_state
*dst
, struct vm_state
*src
)
8669 for (x
= 0; x
<256; x
++) {
8670 dst
->msgArray
[x
] = src
->msgArray
[x
];
8674 static int save_body(BODY
*body
, struct vm_state
*vms
, char *section
, char *format
)
8679 unsigned long newlen
;
8682 if (!body
|| body
== NIL
)
8684 display_body (body
, NIL
, (long) 0);
8685 body_content
= mail_fetchbody (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
], section
, &len
);
8686 if (body_content
!= NIL
) {
8687 sprintf(filename
,"%s.%s", vms
->fn
, format
);
8688 /* ast_log (LOG_DEBUG,body_content); */
8689 body_decoded
= rfc822_base64 ((unsigned char *)body_content
, len
, &newlen
);
8690 write_file (filename
, (char *) body_decoded
, newlen
);
8695 /* get delimiter via mm_list callback */
8696 static void get_mailbox_delimiter(MAILSTREAM
*stream
) {
8698 sprintf(tmp
, "{%s}", imapserver
);
8699 mail_list(stream
, tmp
, "*");
8702 #endif /* IMAP_STORAGE */
8704 /* This is a workaround so that menuselect displays a proper description
8705 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
8708 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, tdesc
,
8709 .load
= load_module
,
8710 .unload
= unload_module
,