2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 1999 - 2006, Digium, Inc.
6 * Mark Spencer <markster@digium.com>
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
21 * \brief Comedian Mail - Voicemail System
23 * \author Mark Spencer <markster@digium.com>
27 * \ingroup applications
28 * \note This module requires res_adsi to load.
32 <depend>res_adsi</depend>
33 <depend>res_smdi</depend>
37 <category name="MENUSELECT_OPTS_app_voicemail" displayname="Voicemail Build Options" positive_output="yes" remove_on_change="apps/app_voicemail.o apps/app_directory.o">
38 <member name="ODBC_STORAGE" displayname="Storage of Voicemail using ODBC">
39 <depend>unixodbc</depend>
41 <conflict>IMAP_STORAGE</conflict>
42 <defaultenabled>no</defaultenabled>
44 <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
45 <depend>imap_tk</depend>
46 <conflict>ODBC_STORAGE</conflict>
48 <defaultenabled>no</defaultenabled>
55 ASTERISK_FILE_VERSION(__FILE__
, "$Revision$")
65 #include <sys/types.h>
73 #ifdef USE_SYSTEM_IMAP
74 #include <imap/c-client.h>
75 #include <imap/imap4r1.h>
76 #include <imap/linkage.h>
77 #elif defined (USE_SYSTEM_CCLIENT)
78 #include <c-client/c-client.h>
79 #include <c-client/imap4r1.h>
80 #include <c-client/linkage.h>
87 #include "asterisk/lock.h"
88 #include "asterisk/file.h"
89 #include "asterisk/logger.h"
90 #include "asterisk/channel.h"
91 #include "asterisk/pbx.h"
92 #include "asterisk/options.h"
93 #include "asterisk/config.h"
94 #include "asterisk/say.h"
95 #include "asterisk/module.h"
96 #include "asterisk/adsi.h"
97 #include "asterisk/app.h"
98 #include "asterisk/manager.h"
99 #include "asterisk/dsp.h"
100 #include "asterisk/localtime.h"
101 #include "asterisk/cli.h"
102 #include "asterisk/utils.h"
103 #include "asterisk/stringfields.h"
104 #include "asterisk/smdi.h"
106 #include "asterisk/res_odbc.h"
110 AST_MUTEX_DEFINE_STATIC(imaptemp_lock
);
111 static char imaptemp
[1024];
113 static char imapserver
[48];
114 static char imapport
[8];
115 static char imapflags
[128];
116 static char imapfolder
[64];
117 static char authuser
[32];
118 static char authpassword
[42];
120 static int expungeonhangup
= 1;
121 static char delimiter
= '\0';
126 static int init_mailstream (struct vm_state
*vms
, int box
);
127 static void write_file (char *filename
, char *buffer
, unsigned long len
);
128 /*static void status (MAILSTREAM *stream); */ /* No need for this. */
129 static char *get_header_by_tag(char *header
, char *tag
);
130 static void vm_imap_delete(int msgnum
, struct vm_state
*vms
);
131 static char *get_user_by_mailbox(char *mailbox
);
132 static struct vm_state
*get_vm_state_by_imapuser(char *user
, int interactive
);
133 static struct vm_state
*get_vm_state_by_mailbox(const char *mailbox
, int interactive
);
134 static void vmstate_insert(struct vm_state
*vms
);
135 static void vmstate_delete(struct vm_state
*vms
);
136 static void set_update(MAILSTREAM
* stream
);
137 static void init_vm_state(struct vm_state
*vms
);
138 static void check_msgArray(struct vm_state
*vms
);
139 static void copy_msgArray(struct vm_state
*dst
, struct vm_state
*src
);
140 static int save_body(BODY
*body
, struct vm_state
*vms
, char *section
, char *format
);
141 static int make_gsm_file(char *dest
, size_t len
, char *imapuser
, char *dir
, int num
);
142 static void get_mailbox_delimiter(MAILSTREAM
*stream
);
143 static void mm_parsequota (MAILSTREAM
*stream
, unsigned char *msg
, QUOTALIST
*pquota
);
144 static void imap_mailbox_name(char *spec
, size_t len
, struct vm_state
*vms
, int box
, int target
);
145 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
);
146 static void check_quota(struct vm_state
*vms
, char *mailbox
);
147 static int open_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
,int box
);
149 struct vm_state
*vms
;
150 struct vmstate
*next
;
152 AST_MUTEX_DEFINE_STATIC(vmstate_lock
);
153 static struct vmstate
*vmstates
= NULL
;
156 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
158 #define COMMAND_TIMEOUT 5000
159 /* Don't modify these here; set your umask at runtime instead */
160 #define VOICEMAIL_DIR_MODE 0777
161 #define VOICEMAIL_FILE_MODE 0666
162 #define CHUNKSIZE 65536
164 #define VOICEMAIL_CONFIG "voicemail.conf"
165 #define ASTERISK_USERNAME "asterisk"
167 /* Default mail command to mail voicemail. Change it with the
168 mailcmd= command in voicemail.conf */
169 #define SENDMAIL "/usr/sbin/sendmail -t"
171 #define INTRO "vm-intro"
175 #define MAXMSGLIMIT 9999
177 #define MAXMSGLIMIT 255
180 #define BASEMAXINLINE 256
181 #define BASELINELEN 72
182 #define BASEMAXINLINE 256
185 #define MAX_DATETIME_FORMAT 512
186 #define MAX_NUM_CID_CONTEXTS 10
188 #define VM_REVIEW (1 << 0)
189 #define VM_OPERATOR (1 << 1)
190 #define VM_SAYCID (1 << 2)
191 #define VM_SVMAIL (1 << 3)
192 #define VM_ENVELOPE (1 << 4)
193 #define VM_SAYDURATION (1 << 5)
194 #define VM_SKIPAFTERCMD (1 << 6)
195 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
196 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
197 #define VM_PBXSKIP (1 << 9)
198 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
199 #define VM_ATTACH (1 << 11)
200 #define VM_DELETE (1 << 12)
201 #define VM_ALLOCED (1 << 13)
202 #define VM_SEARCH (1 << 14)
203 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
204 #define ERROR_LOCK_PATH -100
205 #define ERROR_MAILBOX_FULL -200
209 OPT_SILENT
= (1 << 0),
210 OPT_BUSY_GREETING
= (1 << 1),
211 OPT_UNAVAIL_GREETING
= (1 << 2),
212 OPT_RECORDGAIN
= (1 << 3),
213 OPT_PREPEND_MAILBOX
= (1 << 4),
214 OPT_PRIORITY_JUMP
= (1 << 5),
215 OPT_AUTOPLAY
= (1 << 6),
219 OPT_ARG_RECORDGAIN
= 0,
220 OPT_ARG_PLAYFOLDER
= 1,
221 /* This *must* be the last value in this enum! */
222 OPT_ARG_ARRAY_SIZE
= 2,
225 AST_APP_OPTIONS(vm_app_options
, {
226 AST_APP_OPTION('s', OPT_SILENT
),
227 AST_APP_OPTION('b', OPT_BUSY_GREETING
),
228 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING
),
229 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN
, OPT_ARG_RECORDGAIN
),
230 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX
),
231 AST_APP_OPTION('j', OPT_PRIORITY_JUMP
),
232 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY
, OPT_ARG_PLAYFOLDER
),
235 static int load_config(void);
237 /*! \page vmlang Voicemail Language Syntaxes Supported
239 \par Syntaxes supported, not really language codes.
247 \arg \b pt - Portuguese
248 \arg \b pt_BR - Portuguese (Brazil)
250 \arg \b no - Norwegian
252 \arg \b ua - Ukrainian
254 German requires the following additional soundfile:
255 \arg \b 1F einE (feminine)
257 Spanish requires the following additional soundfile:
258 \arg \b 1M un (masculine)
260 Dutch, Portuguese & Spanish require the following additional soundfiles:
261 \arg \b vm-INBOXs singular of 'new'
262 \arg \b vm-Olds singular of 'old/heard/read'
265 \arg \b vm-INBOX nieuwe (nl)
266 \arg \b vm-Old oude (nl)
269 \arg \b vm-new-a 'new', feminine singular accusative
270 \arg \b vm-new-e 'new', feminine plural accusative
271 \arg \b vm-new-ych 'new', feminine plural genitive
272 \arg \b vm-old-a 'old', feminine singular accusative
273 \arg \b vm-old-e 'old', feminine plural accusative
274 \arg \b vm-old-ych 'old', feminine plural genitive
275 \arg \b digits/1-a 'one', not always same as 'digits/1'
276 \arg \b digits/2-ie 'two', not always same as 'digits/2'
279 \arg \b vm-nytt singular of 'new'
280 \arg \b vm-nya plural of 'new'
281 \arg \b vm-gammalt singular of 'old'
282 \arg \b vm-gamla plural of 'old'
283 \arg \b digits/ett 'one', not always same as 'digits/1'
286 \arg \b vm-ny singular of 'new'
287 \arg \b vm-nye plural of 'new'
288 \arg \b vm-gammel singular of 'old'
289 \arg \b vm-gamle plural of 'old'
297 Ukrainian requires the following additional soundfile:
298 \arg \b vm-nove 'nove'
299 \arg \b vm-stare 'stare'
300 \arg \b digits/ua/1e 'odne'
302 Italian requires the following additional soundfile:
306 \arg \b vm-nuovi new plural
307 \arg \b vm-vecchio old
308 \arg \b vm-vecchi old plural
310 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
311 spelled among others when you have to change folder. For the above reasons, vm-INBOX
312 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
321 unsigned char iobuf
[BASEMAXINLINE
];
324 /*! Structure for linked list of users */
326 char context
[AST_MAX_CONTEXT
]; /*!< Voicemail context */
327 char mailbox
[AST_MAX_EXTENSION
]; /*!< Mailbox id, unique within vm context */
328 char password
[80]; /*!< Secret pin code, numbers only */
329 char fullname
[80]; /*!< Full name, for directory app */
330 char email
[80]; /*!< E-mail address */
331 char pager
[80]; /*!< E-mail address to pager (no attachment) */
332 char serveremail
[80]; /*!< From: Mail address */
333 char mailcmd
[160]; /*!< Configurable mail command */
334 char language
[MAX_LANGUAGE
]; /*!< Config: Language setting */
335 char zonetag
[80]; /*!< Time zone */
338 char uniqueid
[80]; /*!< Unique integer identifier */
340 char attachfmt
[20]; /*!< Attachment format */
341 unsigned int flags
; /*!< VM_ flags */
343 int maxmsg
; /*!< Maximum number of msgs per folder for this mailbox */
345 char imapuser
[80]; /* IMAP server login */
346 char imappassword
[80]; /* IMAP server password if authpassword not defined */
348 double volgain
; /*!< Volume gain for voicemails sent via email */
349 AST_LIST_ENTRY(ast_vm_user
) list
;
353 AST_LIST_ENTRY(vm_zone
) list
;
356 char msg_format
[512];
362 char curdir
[PATH_MAX
];
363 char vmbox
[PATH_MAX
];
376 int updated
; /* decremented on each mail check until 1 -allows delay */
378 MAILSTREAM
*mailstream
;
380 char imapuser
[80]; /* IMAP server login */
382 unsigned int quota_limit
;
383 unsigned int quota_usage
;
384 struct vm_state
*persist_vms
;
387 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
);
388 static int dialout(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *num
, char *outgoing_context
);
389 static int play_record_review(struct ast_channel
*chan
, char *playfile
, char *recordfile
, int maxtime
,
390 char *fmt
, int outsidecaller
, struct ast_vm_user
*vmu
, int *duration
, const char *unlockdir
,
391 signed char record_gain
, struct vm_state
*vms
);
392 static int vm_tempgreeting(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
);
393 static int vm_play_folder_name(struct ast_channel
*chan
, char *mbox
);
394 static int notify_new_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, int msgnum
, long duration
, char *fmt
, char *cidnum
, char *cidname
);
395 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
);
396 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
397 static int __has_voicemail(const char *context
, const char *mailbox
, const char *folder
, int shortcircuit
);
399 static void apply_options(struct ast_vm_user
*vmu
, const char *options
);
402 static char odbc_database
[80];
403 static char odbc_table
[80];
404 #define RETRIEVE(a,b) retrieve_file(a,b)
405 #define DISPOSE(a,b) remove_file(a,b)
406 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
407 #define EXISTS(a,b,c,d) (message_exists(a,b))
408 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
409 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
410 #define DELETE(a,b,c) (delete_file(a,b))
413 #define RETRIEVE(a,b)
415 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
416 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
417 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
418 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
419 #define IMAP_DELETE(a,b,c,d) (vm_imap_delete(b,d))
420 #define DELETE(a,b,c) (vm_delete(c))
422 #define RETRIEVE(a,b)
424 #define STORE(a,b,c,d,e,f,g,h,i)
425 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
426 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
427 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
428 #define DELETE(a,b,c) (vm_delete(c))
432 static char VM_SPOOL_DIR
[PATH_MAX
];
434 static char ext_pass_cmd
[128];
439 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
441 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
443 #define tdesc "Comedian Mail (Voicemail System)"
446 static char userscontext
[AST_MAX_EXTENSION
] = "default";
448 static char *addesc
= "Comedian Mail";
450 static char *synopsis_vm
=
451 "Leave a Voicemail message";
453 static char *descrip_vm
=
454 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
455 "application allows the calling party to leave a message for the specified\n"
456 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
457 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
458 "specified mailbox does not exist.\n"
459 " The Voicemail application will exit if any of the following DTMF digits are\n"
461 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
462 " * - Jump to the 'a' extension in the current dialplan context.\n"
463 " This application will set the following channel variable upon completion:\n"
464 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
465 " application. The possible values are:\n"
466 " SUCCESS | USEREXIT | FAILED\n\n"
468 " b - Play the 'busy' greeting to the calling party.\n"
469 " g(#) - Use the specified amount of gain when recording the voicemail\n"
470 " message. The units are whole-number decibels (dB).\n"
471 " Only works on supported technologies, which is Zap only.\n"
472 " s - Skip the playback of instructions for leaving a message to the\n"
474 " u - Play the 'unavailable' greeting.\n"
475 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
478 static char *synopsis_vmain
=
479 "Check Voicemail messages";
481 static char *descrip_vmain
=
482 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
483 "calling party to check voicemail messages. A specific mailbox, and optional\n"
484 "corresponding context, may be specified. If a mailbox is not provided, the\n"
485 "calling party will be prompted to enter one. If a context is not specified,\n"
486 "the 'default' context will be used.\n\n"
488 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
489 " is entered by the caller.\n"
490 " g(#) - Use the specified amount of gain when recording a voicemail\n"
491 " message. The units are whole-number decibels (dB).\n"
492 " s - Skip checking the passcode for the mailbox.\n"
493 " a(#) - Skip folder prompt and go directly to folder specified.\n"
494 " Defaults to INBOX\n";
496 static char *synopsis_vm_box_exists
=
497 "Check to see if Voicemail mailbox exists";
499 static char *descrip_vm_box_exists
=
500 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
501 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
503 " This application will set the following channel variable upon completion:\n"
504 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
505 " MailboxExists application. Possible values include:\n"
506 " SUCCESS | FAILED\n\n"
508 " j - Jump to priority n+101 if the mailbox is found.\n";
510 static char *synopsis_vmauthenticate
=
511 "Authenticate with Voicemail passwords";
513 static char *descrip_vmauthenticate
=
514 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
515 "same way as the Authenticate application, but the passwords are taken from\n"
517 " If the mailbox is specified, only that mailbox's password will be considered\n"
518 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
519 "be set with the authenticated mailbox.\n\n"
521 " s - Skip playing the initial prompts.\n";
523 /* Leave a message */
524 static char *app
= "VoiceMail";
526 /* Check mail, control, etc */
527 static char *app2
= "VoiceMailMain";
529 static char *app3
= "MailboxExists";
530 static char *app4
= "VMAuthenticate";
532 static AST_LIST_HEAD_STATIC(users
, ast_vm_user
);
533 static AST_LIST_HEAD_STATIC(zones
, vm_zone
);
534 static int maxsilence
;
536 static int silencethreshold
= 128;
537 static char serveremail
[80];
538 static char mailcmd
[160]; /* Configurable mail cmd */
539 static char externnotify
[160];
540 static struct ast_smdi_interface
*smdi_iface
= NULL
;
541 static char vmfmts
[80];
542 static double volgain
;
543 static int vmminmessage
;
544 static int vmmaxmessage
;
547 static int maxlogins
;
549 static struct ast_flags globalflags
= {0};
551 static int saydurationminfo
;
553 static char dialcontext
[AST_MAX_CONTEXT
];
554 static char callcontext
[AST_MAX_CONTEXT
];
555 static char exitcontext
[AST_MAX_CONTEXT
];
557 static char cidinternalcontexts
[MAX_NUM_CID_CONTEXTS
][64];
560 static char *emailbody
= NULL
;
561 static char *emailsubject
= NULL
;
562 static char *pagerbody
= NULL
;
563 static char *pagersubject
= NULL
;
564 static char fromstring
[100];
565 static char pagerfromstring
[100];
566 static char emailtitle
[100];
567 static char charset
[32] = "ISO-8859-1";
569 static unsigned char adsifdn
[4] = "\x00\x00\x00\x0F";
570 static unsigned char adsisec
[4] = "\x9B\xDB\xF7\xAC";
571 static int adsiver
= 1;
572 static char emaildateformat
[32] = "%A, %B %d, %Y at %r";
575 static void populate_defaults(struct ast_vm_user
*vmu
)
577 ast_copy_flags(vmu
, (&globalflags
), AST_FLAGS_ALL
);
578 if (saydurationminfo
)
579 vmu
->saydurationm
= saydurationminfo
;
580 ast_copy_string(vmu
->callback
, callcontext
, sizeof(vmu
->callback
));
581 ast_copy_string(vmu
->dialout
, dialcontext
, sizeof(vmu
->dialout
));
582 ast_copy_string(vmu
->exit
, exitcontext
, sizeof(vmu
->exit
));
584 vmu
->maxmsg
= maxmsg
;
585 vmu
->volgain
= volgain
;
588 static void apply_option(struct ast_vm_user
*vmu
, const char *var
, const char *value
)
591 if (!strcasecmp(var
, "attach")) {
592 ast_set2_flag(vmu
, ast_true(value
), VM_ATTACH
);
593 } else if (!strcasecmp(var
, "attachfmt")) {
594 ast_copy_string(vmu
->attachfmt
, value
, sizeof(vmu
->attachfmt
));
595 } else if (!strcasecmp(var
, "serveremail")) {
596 ast_copy_string(vmu
->serveremail
, value
, sizeof(vmu
->serveremail
));
597 } else if (!strcasecmp(var
, "language")) {
598 ast_copy_string(vmu
->language
, value
, sizeof(vmu
->language
));
599 } else if (!strcasecmp(var
, "tz")) {
600 ast_copy_string(vmu
->zonetag
, value
, sizeof(vmu
->zonetag
));
602 } else if (!strcasecmp(var
, "imapuser")) {
603 ast_copy_string(vmu
->imapuser
, value
, sizeof(vmu
->imapuser
));
604 } else if (!strcasecmp(var
, "imappassword")) {
605 ast_copy_string(vmu
->imappassword
, value
, sizeof(vmu
->imappassword
));
607 } else if (!strcasecmp(var
, "delete") || !strcasecmp(var
, "deletevoicemail")) {
608 ast_set2_flag(vmu
, ast_true(value
), VM_DELETE
);
609 } else if (!strcasecmp(var
, "saycid")){
610 ast_set2_flag(vmu
, ast_true(value
), VM_SAYCID
);
611 } else if (!strcasecmp(var
,"sendvoicemail")){
612 ast_set2_flag(vmu
, ast_true(value
), VM_SVMAIL
);
613 } else if (!strcasecmp(var
, "review")){
614 ast_set2_flag(vmu
, ast_true(value
), VM_REVIEW
);
615 } else if (!strcasecmp(var
, "tempgreetwarn")){
616 ast_set2_flag(vmu
, ast_true(value
), VM_TEMPGREETWARN
);
617 } else if (!strcasecmp(var
, "operator")){
618 ast_set2_flag(vmu
, ast_true(value
), VM_OPERATOR
);
619 } else if (!strcasecmp(var
, "envelope")){
620 ast_set2_flag(vmu
, ast_true(value
), VM_ENVELOPE
);
621 } else if (!strcasecmp(var
, "sayduration")){
622 ast_set2_flag(vmu
, ast_true(value
), VM_SAYDURATION
);
623 } else if (!strcasecmp(var
, "saydurationm")){
624 if (sscanf(value
, "%d", &x
) == 1) {
625 vmu
->saydurationm
= x
;
627 ast_log(LOG_WARNING
, "Invalid min duration for say duration\n");
629 } else if (!strcasecmp(var
, "forcename")){
630 ast_set2_flag(vmu
, ast_true(value
), VM_FORCENAME
);
631 } else if (!strcasecmp(var
, "forcegreetings")){
632 ast_set2_flag(vmu
, ast_true(value
), VM_FORCEGREET
);
633 } else if (!strcasecmp(var
, "callback")) {
634 ast_copy_string(vmu
->callback
, value
, sizeof(vmu
->callback
));
635 } else if (!strcasecmp(var
, "dialout")) {
636 ast_copy_string(vmu
->dialout
, value
, sizeof(vmu
->dialout
));
637 } else if (!strcasecmp(var
, "exitcontext")) {
638 ast_copy_string(vmu
->exit
, value
, sizeof(vmu
->exit
));
639 } else if (!strcasecmp(var
, "maxmsg")) {
640 vmu
->maxmsg
= atoi(value
);
641 if (vmu
->maxmsg
<= 0) {
642 ast_log(LOG_WARNING
, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value
, MAXMSG
);
643 vmu
->maxmsg
= MAXMSG
;
644 } else if (vmu
->maxmsg
> MAXMSGLIMIT
) {
645 ast_log(LOG_WARNING
, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT
, value
);
646 vmu
->maxmsg
= MAXMSGLIMIT
;
648 } else if (!strcasecmp(var
, "volgain")) {
649 sscanf(value
, "%lf", &vmu
->volgain
);
650 } else if (!strcasecmp(var
, "options")) {
651 apply_options(vmu
, value
);
655 static int change_password_realtime(struct ast_vm_user
*vmu
, const char *password
)
658 if (!ast_strlen_zero(vmu
->uniqueid
)) {
659 res
= ast_update_realtime("voicemail", "uniqueid", vmu
->uniqueid
, "password", password
, NULL
);
661 ast_copy_string(vmu
->password
, password
, sizeof(vmu
->password
));
671 static void apply_options(struct ast_vm_user
*vmu
, const char *options
)
672 { /* Destructively Parse options and apply */
676 stringp
= ast_strdupa(options
);
677 while ((s
= strsep(&stringp
, "|"))) {
679 if ((var
= strsep(&value
, "=")) && value
) {
680 apply_option(vmu
, var
, value
);
685 static void apply_options_full(struct ast_vm_user
*retval
, struct ast_variable
*var
)
687 struct ast_variable
*tmp
;
690 if (!strcasecmp(tmp
->name
, "vmsecret")) {
691 ast_copy_string(retval
->password
, tmp
->value
, sizeof(retval
->password
));
692 } else if (!strcasecmp(tmp
->name
, "secret") || !strcasecmp(tmp
->name
, "password")) { /* don't overwrite vmsecret if it exists */
693 if (ast_strlen_zero(retval
->password
))
694 ast_copy_string(retval
->password
, tmp
->value
, sizeof(retval
->password
));
695 } else if (!strcasecmp(tmp
->name
, "uniqueid")) {
696 ast_copy_string(retval
->uniqueid
, tmp
->value
, sizeof(retval
->uniqueid
));
697 } else if (!strcasecmp(tmp
->name
, "pager")) {
698 ast_copy_string(retval
->pager
, tmp
->value
, sizeof(retval
->pager
));
699 } else if (!strcasecmp(tmp
->name
, "email")) {
700 ast_copy_string(retval
->email
, tmp
->value
, sizeof(retval
->email
));
701 } else if (!strcasecmp(tmp
->name
, "fullname")) {
702 ast_copy_string(retval
->fullname
, tmp
->value
, sizeof(retval
->fullname
));
703 } else if (!strcasecmp(tmp
->name
, "context")) {
704 ast_copy_string(retval
->context
, tmp
->value
, sizeof(retval
->context
));
706 } else if (!strcasecmp(tmp
->name
, "imapuser")) {
707 ast_copy_string(retval
->imapuser
, tmp
->value
, sizeof(retval
->imapuser
));
708 } else if (!strcasecmp(tmp
->name
, "imappassword")) {
709 ast_copy_string(retval
->imappassword
, tmp
->value
, sizeof(retval
->imappassword
));
712 apply_option(retval
, tmp
->name
, tmp
->value
);
717 static struct ast_vm_user
*find_user_realtime(struct ast_vm_user
*ivm
, const char *context
, const char *mailbox
)
719 struct ast_variable
*var
;
720 struct ast_vm_user
*retval
;
722 if ((retval
= (ivm
? ivm
: ast_calloc(1, sizeof(*retval
))))) {
724 ast_set_flag(retval
, VM_ALLOCED
);
726 memset(retval
, 0, sizeof(*retval
));
728 ast_copy_string(retval
->mailbox
, mailbox
, sizeof(retval
->mailbox
));
729 populate_defaults(retval
);
730 if (!context
&& ast_test_flag((&globalflags
), VM_SEARCH
))
731 var
= ast_load_realtime("voicemail", "mailbox", mailbox
, NULL
);
733 var
= ast_load_realtime("voicemail", "mailbox", mailbox
, "context", context
, NULL
);
735 apply_options_full(retval
, var
);
736 ast_variables_destroy(var
);
746 static struct ast_vm_user
*find_user(struct ast_vm_user
*ivm
, const char *context
, const char *mailbox
)
748 /* This function could be made to generate one from a database, too */
749 struct ast_vm_user
*vmu
=NULL
, *cur
;
750 AST_LIST_LOCK(&users
);
752 if (!context
&& !ast_test_flag((&globalflags
), VM_SEARCH
))
755 AST_LIST_TRAVERSE(&users
, cur
, list
) {
756 if (ast_test_flag((&globalflags
), VM_SEARCH
) && !strcasecmp(mailbox
, cur
->mailbox
))
758 if (context
&& (!strcasecmp(context
, cur
->context
)) && (!strcasecmp(mailbox
, cur
->mailbox
)))
762 /* Make a copy, so that on a reload, we have no race */
763 if ((vmu
= (ivm
? ivm
: ast_malloc(sizeof(*vmu
))))) {
764 memcpy(vmu
, cur
, sizeof(*vmu
));
765 ast_set2_flag(vmu
, !ivm
, VM_ALLOCED
);
766 AST_LIST_NEXT(vmu
, list
) = NULL
;
769 vmu
= find_user_realtime(ivm
, context
, mailbox
);
770 AST_LIST_UNLOCK(&users
);
774 static int reset_user_pw(const char *context
, const char *mailbox
, const char *newpass
)
776 /* This function could be made to generate one from a database, too */
777 struct ast_vm_user
*cur
;
779 AST_LIST_LOCK(&users
);
780 AST_LIST_TRAVERSE(&users
, cur
, list
) {
781 if ((!context
|| !strcasecmp(context
, cur
->context
)) &&
782 (!strcasecmp(mailbox
, cur
->mailbox
)))
786 ast_copy_string(cur
->password
, newpass
, sizeof(cur
->password
));
789 AST_LIST_UNLOCK(&users
);
793 static void vm_change_password(struct ast_vm_user
*vmu
, const char *newpassword
)
795 struct ast_config
*cfg
=NULL
;
796 struct ast_variable
*var
=NULL
;
797 struct ast_category
*cat
=NULL
;
798 char *category
=NULL
, *value
=NULL
, *new=NULL
;
799 const char *tmp
=NULL
;
801 if (!change_password_realtime(vmu
, newpassword
))
804 /* check voicemail.conf */
805 if ((cfg
= ast_config_load_with_comments(VOICEMAIL_CONFIG
))) {
806 while ((category
= ast_category_browse(cfg
, category
))) {
807 if (!strcasecmp(category
, vmu
->context
)) {
808 tmp
= ast_variable_retrieve(cfg
, category
, vmu
->mailbox
);
810 ast_log(LOG_WARNING
, "We could not find the mailbox.\n");
813 value
= strstr(tmp
,",");
815 ast_log(LOG_WARNING
, "variable has bad format.\n");
818 new = alloca((strlen(value
)+strlen(newpassword
)+1));
819 sprintf(new,"%s%s", newpassword
, value
);
820 if (!(cat
= ast_category_get(cfg
, category
))) {
821 ast_log(LOG_WARNING
, "Failed to get category structure.\n");
824 ast_variable_update(cat
, vmu
->mailbox
, new, NULL
, 0);
827 /* save the results */
828 reset_user_pw(vmu
->context
, vmu
->mailbox
, newpassword
);
829 ast_copy_string(vmu
->password
, newpassword
, sizeof(vmu
->password
));
830 config_text_file_save(VOICEMAIL_CONFIG
, cfg
, "AppVoicemail");
834 /* check users.conf and update the password stored for the mailbox*/
835 /* if no vmsecret entry exists create one. */
836 if ((cfg
= ast_config_load_with_comments("users.conf"))) {
837 if (option_debug
> 3)
838 ast_log(LOG_DEBUG
, "we are looking for %s\n", vmu
->mailbox
);
839 while ((category
= ast_category_browse(cfg
, category
))) {
840 if (option_debug
> 3)
841 ast_log(LOG_DEBUG
, "users.conf: %s\n", category
);
842 if (!strcasecmp(category
, vmu
->mailbox
)) {
843 if (!(tmp
= ast_variable_retrieve(cfg
, category
, "vmsecret"))) {
844 if (option_debug
> 3)
845 ast_log(LOG_DEBUG
, "looks like we need to make vmsecret!\n");
846 var
= ast_variable_new("vmsecret", newpassword
);
848 new = alloca(strlen(newpassword
)+1);
849 sprintf(new, "%s", newpassword
);
850 if (!(cat
= ast_category_get(cfg
, category
))) {
851 if (option_debug
> 3)
852 ast_log(LOG_DEBUG
, "failed to get category!\n");
856 ast_variable_update(cat
, "vmsecret", new, NULL
, 0);
858 ast_variable_append(cat
, var
);
861 /* save the results and clean things up */
862 reset_user_pw(vmu
->context
, vmu
->mailbox
, newpassword
);
863 ast_copy_string(vmu
->password
, newpassword
, sizeof(vmu
->password
));
864 config_text_file_save("users.conf", cfg
, "AppVoicemail");
868 static void vm_change_password_shell(struct ast_vm_user
*vmu
, char *newpassword
)
871 snprintf(buf
,255,"%s %s %s %s",ext_pass_cmd
,vmu
->context
,vmu
->mailbox
,newpassword
);
872 if (!ast_safe_system(buf
)) {
873 ast_copy_string(vmu
->password
, newpassword
, sizeof(vmu
->password
));
874 /* Reset the password in memory, too */
875 reset_user_pw(vmu
->context
, vmu
->mailbox
, newpassword
);
879 static int make_dir(char *dest
, int len
, const char *context
, const char *ext
, const char *folder
)
881 return snprintf(dest
, len
, "%s%s/%s/%s", VM_SPOOL_DIR
, context
, ext
, folder
);
885 static int make_gsm_file(char *dest
, size_t len
, char *imapuser
, char *dir
, int num
)
887 if (mkdir(dir
, 01777) && (errno
!= EEXIST
)) {
888 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dir
, strerror(errno
));
889 return snprintf(dest
, len
, "%s/msg%04d", dir
, num
);
891 return snprintf(dest
, len
, "%s/msg%04d", dir
, num
);
894 static void vm_imap_delete(int msgnum
, struct vm_state
*vms
)
896 unsigned long messageNum
= 0;
899 /* find real message number based on msgnum */
900 /* this may be an index into vms->msgArray based on the msgnum. */
902 messageNum
= vms
->msgArray
[msgnum
];
903 if (messageNum
== 0) {
904 ast_log(LOG_WARNING
, "msgnum %d, mailbox message %lu is zero.\n",msgnum
,messageNum
);
907 if (option_debug
> 2)
908 ast_log(LOG_DEBUG
, "deleting msgnum %d, which is mailbox message %lu\n",msgnum
,messageNum
);
910 snprintf (arg
, sizeof(arg
), "%lu",messageNum
);
911 mail_setflag (vms
->mailstream
,arg
,"\\DELETED");
915 static int make_file(char *dest
, int len
, char *dir
, int num
)
917 return snprintf(dest
, len
, "%s/msg%04d", dir
, num
);
920 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
921 * \param dest String. base directory.
922 * \param len Length of dest.
923 * \param context String. Ignored if is null or empty string.
924 * \param ext String. Ignored if is null or empty string.
925 * \param folder String. Ignored if is null or empty string.
926 * \return -1 on failure, 0 on success.
928 static int create_dirpath(char *dest
, int len
, const char *context
, const char *ext
, const char *folder
)
930 mode_t mode
= VOICEMAIL_DIR_MODE
;
932 if (!ast_strlen_zero(context
)) {
933 make_dir(dest
, len
, context
, "", "");
934 if (mkdir(dest
, mode
) && errno
!= EEXIST
) {
935 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dest
, strerror(errno
));
939 if (!ast_strlen_zero(ext
)) {
940 make_dir(dest
, len
, context
, ext
, "");
941 if (mkdir(dest
, mode
) && errno
!= EEXIST
) {
942 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dest
, strerror(errno
));
946 if (!ast_strlen_zero(folder
)) {
947 make_dir(dest
, len
, context
, ext
, folder
);
948 if (mkdir(dest
, mode
) && errno
!= EEXIST
) {
949 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dest
, strerror(errno
));
956 /* only return failure if ast_lock_path returns 'timeout',
957 not if the path does not exist or any other reason
959 static int vm_lock_path(const char *path
)
961 switch (ast_lock_path(path
)) {
962 case AST_LOCK_TIMEOUT
:
971 struct generic_prepare_struct
{
977 static SQLHSTMT
generic_prepare(struct odbc_obj
*obj
, void *data
)
979 struct generic_prepare_struct
*gps
= data
;
983 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
984 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
985 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
988 res
= SQLPrepare(stmt
, (unsigned char *)gps
->sql
, SQL_NTS
);
989 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
990 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", gps
->sql
);
991 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
994 for (i
= 0; i
< gps
->argc
; i
++)
995 SQLBindParameter(stmt
, i
+ 1, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(gps
->argv
[i
]), 0, gps
->argv
[i
], 0, NULL
);
1000 static int retrieve_file(char *dir
, int msgnum
)
1006 void *fdm
= MAP_FAILED
;
1007 SQLSMALLINT colcount
=0;
1014 SQLSMALLINT datatype
;
1015 SQLSMALLINT decimaldigits
;
1016 SQLSMALLINT nullable
;
1022 char full_fn
[PATH_MAX
];
1024 char *argv
[] = { dir
, msgnums
};
1025 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 2, .argv
= argv
};
1027 struct odbc_obj
*obj
;
1028 obj
= ast_odbc_request_obj(odbc_database
, 0);
1030 ast_copy_string(fmt
, vmfmts
, sizeof(fmt
));
1031 c
= strchr(fmt
, '|');
1034 if (!strcasecmp(fmt
, "wav49"))
1036 snprintf(msgnums
, sizeof(msgnums
),"%d", msgnum
);
1038 make_file(fn
, sizeof(fn
), dir
, msgnum
);
1040 ast_copy_string(fn
, dir
, sizeof(fn
));
1041 snprintf(full_fn
, sizeof(full_fn
), "%s.txt", fn
);
1043 if (!(f
= fopen(full_fn
, "w+"))) {
1044 ast_log(LOG_WARNING
, "Failed to open/create '%s'\n", full_fn
);
1048 snprintf(full_fn
, sizeof(full_fn
), "%s.%s", fn
, fmt
);
1049 snprintf(sql
, sizeof(sql
), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table
);
1050 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
1052 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1053 ast_odbc_release_obj(obj
);
1056 res
= SQLFetch(stmt
);
1057 if (res
== SQL_NO_DATA
) {
1058 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1059 ast_odbc_release_obj(obj
);
1062 else if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1063 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
1064 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1065 ast_odbc_release_obj(obj
);
1068 fd
= open(full_fn
, O_RDWR
| O_CREAT
| O_TRUNC
, 0770);
1070 ast_log(LOG_WARNING
, "Failed to write '%s': %s\n", full_fn
, strerror(errno
));
1071 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1072 ast_odbc_release_obj(obj
);
1075 res
= SQLNumResultCols(stmt
, &colcount
);
1076 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1077 ast_log(LOG_WARNING
, "SQL Column Count error!\n[%s]\n\n", sql
);
1078 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1079 ast_odbc_release_obj(obj
);
1083 fprintf(f
, "[message]\n");
1084 for (x
=0;x
<colcount
;x
++) {
1086 collen
= sizeof(coltitle
);
1087 res
= SQLDescribeCol(stmt
, x
+ 1, (unsigned char *)coltitle
, sizeof(coltitle
), &collen
,
1088 &datatype
, &colsize
, &decimaldigits
, &nullable
);
1089 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1090 ast_log(LOG_WARNING
, "SQL Describe Column error!\n[%s]\n\n", sql
);
1091 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1092 ast_odbc_release_obj(obj
);
1095 if (!strcasecmp(coltitle
, "recording")) {
1097 res
= SQLGetData(stmt
, x
+ 1, SQL_BINARY
, rowdata
, 0, &colsize2
);
1101 lseek(fd
, fdlen
- 1, SEEK_SET
);
1102 if (write(fd
, tmp
, 1) != 1) {
1107 /* Read out in small chunks */
1108 for (offset
= 0; offset
< colsize2
; offset
+= CHUNKSIZE
) {
1109 if ((fdm
= mmap(NULL
, CHUNKSIZE
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, offset
)) == MAP_FAILED
) {
1110 ast_log(LOG_WARNING
, "Could not mmap the output file: %s (%d)\n", strerror(errno
), errno
);
1111 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
1112 ast_odbc_release_obj(obj
);
1115 res
= SQLGetData(stmt
, x
+ 1, SQL_BINARY
, fdm
, CHUNKSIZE
, NULL
);
1116 munmap(fdm
, CHUNKSIZE
);
1117 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1118 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
1120 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
1121 ast_odbc_release_obj(obj
);
1126 truncate(full_fn
, fdlen
);
1129 res
= SQLGetData(stmt
, x
+ 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
1130 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1131 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
1132 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1133 ast_odbc_release_obj(obj
);
1136 if (strcasecmp(coltitle
, "msgnum") && strcasecmp(coltitle
, "dir") && f
)
1137 fprintf(f
, "%s=%s\n", coltitle
, rowdata
);
1140 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1141 ast_odbc_release_obj(obj
);
1143 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1152 static int remove_file(char *dir
, int msgnum
)
1155 char full_fn
[PATH_MAX
];
1159 snprintf(msgnums
, sizeof(msgnums
), "%d", msgnum
);
1160 make_file(fn
, sizeof(fn
), dir
, msgnum
);
1162 ast_copy_string(fn
, dir
, sizeof(fn
));
1163 ast_filedelete(fn
, NULL
);
1164 snprintf(full_fn
, sizeof(full_fn
), "%s.txt", fn
);
1169 static int last_message_index(struct ast_vm_user
*vmu
, char *dir
)
1176 char *argv
[] = { dir
};
1177 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 1, .argv
= argv
};
1179 struct odbc_obj
*obj
;
1180 obj
= ast_odbc_request_obj(odbc_database
, 0);
1182 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table
);
1183 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
1185 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1186 ast_odbc_release_obj(obj
);
1189 res
= SQLFetch(stmt
);
1190 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1191 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
1192 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1193 ast_odbc_release_obj(obj
);
1196 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
1197 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1198 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
1199 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1200 ast_odbc_release_obj(obj
);
1203 if (sscanf(rowdata
, "%d", &x
) != 1)
1204 ast_log(LOG_WARNING
, "Failed to read message count!\n");
1205 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1206 ast_odbc_release_obj(obj
);
1208 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1213 static int message_exists(char *dir
, int msgnum
)
1221 char *argv
[] = { dir
, msgnums
};
1222 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 2, .argv
= argv
};
1224 struct odbc_obj
*obj
;
1225 obj
= ast_odbc_request_obj(odbc_database
, 0);
1227 snprintf(msgnums
, sizeof(msgnums
), "%d", msgnum
);
1228 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table
);
1229 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
1231 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1232 ast_odbc_release_obj(obj
);
1235 res
= SQLFetch(stmt
);
1236 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1237 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
1238 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1239 ast_odbc_release_obj(obj
);
1242 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
1243 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1244 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
1245 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1246 ast_odbc_release_obj(obj
);
1249 if (sscanf(rowdata
, "%d", &x
) != 1)
1250 ast_log(LOG_WARNING
, "Failed to read message count!\n");
1251 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1252 ast_odbc_release_obj(obj
);
1254 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1259 static int count_messages(struct ast_vm_user
*vmu
, char *dir
)
1261 return last_message_index(vmu
, dir
) + 1;
1264 static void delete_file(char *sdir
, int smsg
)
1269 char *argv
[] = { sdir
, msgnums
};
1270 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 2, .argv
= argv
};
1272 struct odbc_obj
*obj
;
1273 obj
= ast_odbc_request_obj(odbc_database
, 0);
1275 snprintf(msgnums
, sizeof(msgnums
), "%d", smsg
);
1276 snprintf(sql
, sizeof(sql
), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table
);
1277 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
1279 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1281 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1282 ast_odbc_release_obj(obj
);
1284 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1288 static void copy_file(char *sdir
, int smsg
, char *ddir
, int dmsg
, char *dmailboxuser
, char *dmailboxcontext
)
1294 struct odbc_obj
*obj
;
1295 char *argv
[] = { ddir
, msgnumd
, dmailboxuser
, dmailboxcontext
, sdir
, msgnums
};
1296 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 6, .argv
= argv
};
1298 delete_file(ddir
, dmsg
);
1299 obj
= ast_odbc_request_obj(odbc_database
, 0);
1301 snprintf(msgnums
, sizeof(msgnums
), "%d", smsg
);
1302 snprintf(msgnumd
, sizeof(msgnumd
), "%d", dmsg
);
1303 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
);
1304 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
1306 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql
);
1308 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
1309 ast_odbc_release_obj(obj
);
1311 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1315 static int store_file(char *dir
, char *mailboxuser
, char *mailboxcontext
, int msgnum
)
1320 void *fdm
= MAP_FAILED
;
1327 char full_fn
[PATH_MAX
];
1330 const char *context
="", *macrocontext
="", *callerid
="", *origtime
="", *duration
="";
1331 const char *category
= "";
1332 struct ast_config
*cfg
=NULL
;
1333 struct odbc_obj
*obj
;
1335 delete_file(dir
, msgnum
);
1336 obj
= ast_odbc_request_obj(odbc_database
, 0);
1338 ast_copy_string(fmt
, vmfmts
, sizeof(fmt
));
1339 c
= strchr(fmt
, '|');
1342 if (!strcasecmp(fmt
, "wav49"))
1344 snprintf(msgnums
, sizeof(msgnums
),"%d", msgnum
);
1346 make_file(fn
, sizeof(fn
), dir
, msgnum
);
1348 ast_copy_string(fn
, dir
, sizeof(fn
));
1349 snprintf(full_fn
, sizeof(full_fn
), "%s.txt", fn
);
1350 cfg
= ast_config_load(full_fn
);
1351 snprintf(full_fn
, sizeof(full_fn
), "%s.%s", fn
, fmt
);
1352 fd
= open(full_fn
, O_RDWR
);
1354 ast_log(LOG_WARNING
, "Open of sound file '%s' failed: %s\n", full_fn
, strerror(errno
));
1355 ast_odbc_release_obj(obj
);
1359 context
= ast_variable_retrieve(cfg
, "message", "context");
1360 if (!context
) context
= "";
1361 macrocontext
= ast_variable_retrieve(cfg
, "message", "macrocontext");
1362 if (!macrocontext
) macrocontext
= "";
1363 callerid
= ast_variable_retrieve(cfg
, "message", "callerid");
1364 if (!callerid
) callerid
= "";
1365 origtime
= ast_variable_retrieve(cfg
, "message", "origtime");
1366 if (!origtime
) origtime
= "";
1367 duration
= ast_variable_retrieve(cfg
, "message", "duration");
1368 if (!duration
) duration
= "";
1369 category
= ast_variable_retrieve(cfg
, "message", "category");
1370 if (!category
) category
= "";
1372 fdlen
= lseek(fd
, 0, SEEK_END
);
1373 lseek(fd
, 0, SEEK_SET
);
1374 printf("Length is %zd\n", fdlen
);
1375 fdm
= mmap(NULL
, fdlen
, PROT_READ
| PROT_WRITE
, MAP_SHARED
,fd
, 0);
1376 if (fdm
== MAP_FAILED
) {
1377 ast_log(LOG_WARNING
, "Memory map failed!\n");
1378 ast_odbc_release_obj(obj
);
1381 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
1382 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1383 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
1384 ast_odbc_release_obj(obj
);
1387 if (!ast_strlen_zero(category
))
1388 snprintf(sql
, sizeof(sql
), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table
);
1390 snprintf(sql
, sizeof(sql
), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table
);
1391 res
= SQLPrepare(stmt
, (unsigned char *)sql
, SQL_NTS
);
1392 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1393 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", sql
);
1394 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1395 ast_odbc_release_obj(obj
);
1398 len
= fdlen
; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1399 SQLBindParameter(stmt
, 1, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(dir
), 0, (void *)dir
, 0, NULL
);
1400 SQLBindParameter(stmt
, 2, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(msgnums
), 0, (void *)msgnums
, 0, NULL
);
1401 SQLBindParameter(stmt
, 3, SQL_PARAM_INPUT
, SQL_C_BINARY
, SQL_LONGVARBINARY
, fdlen
, 0, (void *)fdm
, fdlen
, &len
);
1402 SQLBindParameter(stmt
, 4, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(context
), 0, (void *)context
, 0, NULL
);
1403 SQLBindParameter(stmt
, 5, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(macrocontext
), 0, (void *)macrocontext
, 0, NULL
);
1404 SQLBindParameter(stmt
, 6, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(callerid
), 0, (void *)callerid
, 0, NULL
);
1405 SQLBindParameter(stmt
, 7, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(origtime
), 0, (void *)origtime
, 0, NULL
);
1406 SQLBindParameter(stmt
, 8, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(duration
), 0, (void *)duration
, 0, NULL
);
1407 SQLBindParameter(stmt
, 9, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(mailboxuser
), 0, (void *)mailboxuser
, 0, NULL
);
1408 SQLBindParameter(stmt
, 10, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(mailboxcontext
), 0, (void *)mailboxcontext
, 0, NULL
);
1409 if (!ast_strlen_zero(category
))
1410 SQLBindParameter(stmt
, 11, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(category
), 0, (void *)category
, 0, NULL
);
1411 res
= ast_odbc_smart_execute(obj
, stmt
);
1412 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1413 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1414 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1415 ast_odbc_release_obj(obj
);
1418 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1419 ast_odbc_release_obj(obj
);
1421 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1424 ast_config_destroy(cfg
);
1425 if (fdm
!= MAP_FAILED
)
1432 static void rename_file(char *sdir
, int smsg
, char *mailboxuser
, char *mailboxcontext
, char *ddir
, int dmsg
)
1438 struct odbc_obj
*obj
;
1439 char *argv
[] = { ddir
, msgnumd
, mailboxuser
, mailboxcontext
, sdir
, msgnums
};
1440 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 6, .argv
= argv
};
1442 delete_file(ddir
, dmsg
);
1443 obj
= ast_odbc_request_obj(odbc_database
, 0);
1445 snprintf(msgnums
, sizeof(msgnums
), "%d", smsg
);
1446 snprintf(msgnumd
, sizeof(msgnumd
), "%d", dmsg
);
1447 snprintf(sql
, sizeof(sql
), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table
);
1448 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
1450 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1452 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
1453 ast_odbc_release_obj(obj
);
1455 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1460 #ifndef IMAP_STORAGE
1461 static int count_messages(struct ast_vm_user
*vmu
, char *dir
)
1463 /* Find all .txt files - even if they are not in sequence from 0000 */
1467 struct dirent
*vment
= NULL
;
1469 if (vm_lock_path(dir
))
1470 return ERROR_LOCK_PATH
;
1472 if ((vmdir
= opendir(dir
))) {
1473 while ((vment
= readdir(vmdir
))) {
1474 if (strlen(vment
->d_name
) > 7 && !strncmp(vment
->d_name
+ 7, ".txt", 4))
1479 ast_unlock_path(dir
);
1484 static void rename_file(char *sfn
, char *dfn
)
1486 char stxt
[PATH_MAX
];
1487 char dtxt
[PATH_MAX
];
1488 ast_filerename(sfn
,dfn
,NULL
);
1489 snprintf(stxt
, sizeof(stxt
), "%s.txt", sfn
);
1490 snprintf(dtxt
, sizeof(dtxt
), "%s.txt", dfn
);
1496 * A negative return value indicates an error.
1498 #if (!defined(IMAP_STORAGE) && !defined(ODBC_STORAGE))
1499 static int last_message_index(struct ast_vm_user
*vmu
, char *dir
)
1504 if (vm_lock_path(dir
))
1505 return ERROR_LOCK_PATH
;
1507 for (x
= 0; x
< vmu
->maxmsg
; x
++) {
1508 make_file(fn
, sizeof(fn
), dir
, x
);
1509 if (ast_fileexists(fn
, NULL
, NULL
) < 1)
1512 ast_unlock_path(dir
);
1519 static int copy(char *infile
, char *outfile
)
1527 #ifdef HARDLINK_WHEN_POSSIBLE
1528 /* Hard link if possible; saves disk space & is faster */
1529 if (link(infile
, outfile
)) {
1531 if ((ifd
= open(infile
, O_RDONLY
)) < 0) {
1532 ast_log(LOG_WARNING
, "Unable to open %s in read-only mode\n", infile
);
1535 if ((ofd
= open(outfile
, O_WRONLY
| O_TRUNC
| O_CREAT
, VOICEMAIL_FILE_MODE
)) < 0) {
1536 ast_log(LOG_WARNING
, "Unable to open %s in write-only mode\n", outfile
);
1541 len
= read(ifd
, buf
, sizeof(buf
));
1543 ast_log(LOG_WARNING
, "Read failed on %s: %s\n", infile
, strerror(errno
));
1549 res
= write(ofd
, buf
, len
);
1550 if (errno
== ENOMEM
|| errno
== ENOSPC
|| res
!= len
) {
1551 ast_log(LOG_WARNING
, "Write failed on %s (%d of %d): %s\n", outfile
, res
, len
, strerror(errno
));
1561 #ifdef HARDLINK_WHEN_POSSIBLE
1563 /* Hard link succeeded */
1569 static void copy_plain_file(char *frompath
, char *topath
)
1571 char frompath2
[PATH_MAX
], topath2
[PATH_MAX
];
1572 ast_filecopy(frompath
, topath
, NULL
);
1573 snprintf(frompath2
, sizeof(frompath2
), "%s.txt", frompath
);
1574 snprintf(topath2
, sizeof(topath2
), "%s.txt", topath
);
1575 copy(frompath2
, topath2
);
1578 static int vm_delete(char *file
)
1583 txtsize
= (strlen(file
) + 5)*sizeof(char);
1584 txt
= alloca(txtsize
);
1585 /* Sprintf here would safe because we alloca'd exactly the right length,
1586 * but trying to eliminate all sprintf's anyhow
1588 snprintf(txt
, txtsize
, "%s.txt", file
);
1590 return ast_filedelete(file
, NULL
);
1593 static int inbuf(struct baseio
*bio
, FILE *fi
)
1600 if ((l
= fread(bio
->iobuf
,1,BASEMAXINLINE
,fi
)) <= 0) {
1614 static int inchar(struct baseio
*bio
, FILE *fi
)
1616 if (bio
->iocp
>=bio
->iolen
) {
1617 if (!inbuf(bio
, fi
))
1621 return bio
->iobuf
[bio
->iocp
++];
1624 static int ochar(struct baseio
*bio
, int c
, FILE *so
)
1626 if (bio
->linelength
>=BASELINELEN
) {
1627 if (fputs(eol
,so
)==EOF
)
1633 if (putc(((unsigned char)c
),so
)==EOF
)
1641 static int base_encode(char *filename
, FILE *so
)
1643 unsigned char dtable
[BASEMAXINLINE
];
1648 memset(&bio
, 0, sizeof(bio
));
1649 bio
.iocp
= BASEMAXINLINE
;
1651 if (!(fi
= fopen(filename
, "rb"))) {
1652 ast_log(LOG_WARNING
, "Failed to open file: %s: %s\n", filename
, strerror(errno
));
1656 for (i
= 0;i
<9;i
++) {
1659 dtable
[26+i
]= 'a'+i
;
1660 dtable
[26+i
+9]= 'j'+i
;
1662 for (i
= 0;i
<8;i
++) {
1663 dtable
[i
+18]= 'S'+i
;
1664 dtable
[26+i
+18]= 's'+i
;
1666 for (i
= 0;i
<10;i
++) {
1667 dtable
[52+i
]= '0'+i
;
1673 unsigned char igroup
[3],ogroup
[4];
1676 igroup
[0]= igroup
[1]= igroup
[2]= 0;
1678 for (n
= 0;n
<3;n
++) {
1679 if ((c
= inchar(&bio
, fi
)) == EOF
) {
1684 igroup
[n
]= (unsigned char)c
;
1688 ogroup
[0]= dtable
[igroup
[0]>>2];
1689 ogroup
[1]= dtable
[((igroup
[0]&3)<<4)|(igroup
[1]>>4)];
1690 ogroup
[2]= dtable
[((igroup
[1]&0xF)<<2)|(igroup
[2]>>6)];
1691 ogroup
[3]= dtable
[igroup
[2]&0x3F];
1701 ochar(&bio
, ogroup
[i
], so
);
1707 if (fputs(eol
,so
)==EOF
)
1713 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
)
1716 /* Prepare variables for substition in email body and subject */
1717 pbx_builtin_setvar_helper(ast
, "VM_NAME", vmu
->fullname
);
1718 pbx_builtin_setvar_helper(ast
, "VM_DUR", dur
);
1719 snprintf(passdata
, passdatasize
, "%d", msgnum
);
1720 pbx_builtin_setvar_helper(ast
, "VM_MSGNUM", passdata
);
1721 pbx_builtin_setvar_helper(ast
, "VM_CONTEXT", context
);
1722 pbx_builtin_setvar_helper(ast
, "VM_MAILBOX", mailbox
);
1723 pbx_builtin_setvar_helper(ast
, "VM_CALLERID", ast_callerid_merge(callerid
, sizeof(callerid
), cidname
, cidnum
, "Unknown Caller"));
1724 pbx_builtin_setvar_helper(ast
, "VM_CIDNAME", (cidname
? cidname
: "an unknown caller"));
1725 pbx_builtin_setvar_helper(ast
, "VM_CIDNUM", (cidnum
? cidnum
: "an unknown caller"));
1726 pbx_builtin_setvar_helper(ast
, "VM_DATE", date
);
1727 pbx_builtin_setvar_helper(ast
, "VM_CATEGORY", category
? ast_strdupa(category
) : "no category");
1730 static char *quote(const char *from
, char *to
, size_t len
)
1734 for (; ptr
< to
+ len
- 1; from
++) {
1737 else if (*from
== '\0')
1741 if (ptr
< to
+ len
- 1)
1747 * fill in *tm for current time according to the proper timezone, if any.
1748 * Return tm so it can be used as a function argument.
1750 static const struct tm
*vmu_tm(const struct ast_vm_user
*vmu
, struct tm
*tm
)
1752 const struct vm_zone
*z
= NULL
;
1753 time_t t
= time(NULL
);
1755 /* Does this user have a timezone specified? */
1756 if (!ast_strlen_zero(vmu
->zonetag
)) {
1757 /* Find the zone in the list */
1758 AST_LIST_LOCK(&zones
);
1759 AST_LIST_TRAVERSE(&zones
, z
, list
) {
1760 if (!strcmp(z
->name
, vmu
->zonetag
))
1763 AST_LIST_UNLOCK(&zones
);
1765 ast_localtime(&t
, tm
, z
? z
->timezone
: NULL
);
1769 /* same as mkstemp, but return a FILE * */
1770 static FILE *vm_mkftemp(char *template)
1773 int pfd
= mkstemp(template);
1774 chmod(template, VOICEMAIL_FILE_MODE
& ~my_umask
);
1776 p
= fdopen(pfd
, "w+");
1785 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
)
1788 char host
[MAXHOSTNAMELEN
] = "";
1796 size_t len_passdata
;
1803 gethostname(host
, sizeof(host
) - 1);
1804 if (strchr(srcemail
, '@'))
1805 ast_copy_string(who
, srcemail
, sizeof(who
));
1807 snprintf(who
, sizeof(who
), "%s@%s", srcemail
, host
);
1809 snprintf(dur
, sizeof(dur
), "%d:%02d", duration
/ 60, duration
% 60);
1810 strftime(date
, sizeof(date
), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu
, &tm
));
1811 fprintf(p
, "Date: %s" ENDL
, date
);
1813 /* Set date format for voicemail mail */
1814 strftime(date
, sizeof(date
), emaildateformat
, &tm
);
1817 struct ast_channel
*ast
;
1818 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
1820 int vmlen
= strlen(fromstring
)*3 + 200;
1821 if ((passdata
= alloca(vmlen
))) {
1822 memset(passdata
, 0, vmlen
);
1823 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
1824 pbx_substitute_variables_helper(ast
, fromstring
, passdata
, vmlen
);
1825 len_passdata
= strlen(passdata
) * 2 + 3;
1826 passdata2
= alloca(len_passdata
);
1827 fprintf(p
, "From: %s <%s>" ENDL
, quote(passdata
, passdata2
, len_passdata
), who
);
1829 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
1830 ast_channel_free(ast
);
1832 ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
1834 fprintf(p
, "From: Asterisk PBX <%s>" ENDL
, who
);
1835 len_passdata
= strlen(vmu
->fullname
) * 2 + 3;
1836 passdata2
= alloca(len_passdata
);
1837 fprintf(p
, "To: %s <%s>" ENDL
, quote(vmu
->fullname
, passdata2
, len_passdata
), vmu
->email
);
1839 struct ast_channel
*ast
;
1840 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
1842 int vmlen
= strlen(emailsubject
)*3 + 200;
1843 if ((passdata
= alloca(vmlen
))) {
1844 memset(passdata
, 0, vmlen
);
1845 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
1846 pbx_substitute_variables_helper(ast
, emailsubject
, passdata
, vmlen
);
1847 fprintf(p
, "Subject: %s" ENDL
, passdata
);
1849 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
1850 ast_channel_free(ast
);
1852 ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
1853 } else if (*emailtitle
) {
1854 fprintf(p
, emailtitle
, msgnum
+ 1, mailbox
) ;
1856 } else if (ast_test_flag((&globalflags
), VM_PBXSKIP
))
1857 fprintf(p
, "Subject: New message %d in mailbox %s" ENDL
, msgnum
+ 1, mailbox
);
1859 fprintf(p
, "Subject: [PBX]: New message %d in mailbox %s" ENDL
, msgnum
+ 1, mailbox
);
1860 fprintf(p
, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL
, msgnum
+ 1, (unsigned int)ast_random(), mailbox
, (int)getpid(), host
);
1862 /* additional information needed for IMAP searching */
1863 fprintf(p
, "X-Asterisk-VM-Message-Num: %d" ENDL
, msgnum
+ 1);
1864 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
1865 fprintf(p
, "X-Asterisk-VM-Server-Name: %s" ENDL
, fromstring
);
1866 fprintf(p
, "X-Asterisk-VM-Context: %s" ENDL
, context
);
1867 fprintf(p
, "X-Asterisk-VM-Extension: %s" ENDL
, mailbox
);
1868 fprintf(p
, "X-Asterisk-VM-Priority: %d" ENDL
, chan
->priority
);
1869 fprintf(p
, "X-Asterisk-VM-Caller-channel: %s" ENDL
, chan
->name
);
1870 fprintf(p
, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL
, cidnum
);
1871 fprintf(p
, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL
, cidname
);
1872 fprintf(p
, "X-Asterisk-VM-Duration: %d" ENDL
, duration
);
1873 if (!ast_strlen_zero(category
))
1874 fprintf(p
, "X-Asterisk-VM-Category: %s" ENDL
, category
);
1875 fprintf(p
, "X-Asterisk-VM-Orig-date: %s" ENDL
, date
);
1876 fprintf(p
, "X-Asterisk-VM-Orig-time: %ld" ENDL
, (long)time(NULL
));
1878 if (!ast_strlen_zero(cidnum
))
1879 fprintf(p
, "X-Asterisk-CallerID: %s" ENDL
, cidnum
);
1880 if (!ast_strlen_zero(cidname
))
1881 fprintf(p
, "X-Asterisk-CallerIDName: %s" ENDL
, cidname
);
1882 fprintf(p
, "MIME-Version: 1.0" ENDL
);
1883 if (attach_user_voicemail
) {
1884 /* Something unique. */
1885 snprintf(bound
, sizeof(bound
), "----voicemail_%d%s%d%d", msgnum
+ 1, mailbox
, (int)getpid(), (unsigned int)ast_random());
1887 fprintf(p
, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL
, bound
);
1888 fprintf(p
, ENDL ENDL
"This is a multi-part message in MIME format." ENDL ENDL
);
1889 fprintf(p
, "--%s" ENDL
, bound
);
1891 fprintf(p
, "Content-Type: text/plain; charset=%s" ENDL
"Content-Transfer-Encoding: 8bit" ENDL ENDL
, charset
);
1893 struct ast_channel
*ast
;
1894 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
1896 int vmlen
= strlen(emailbody
)*3 + 200;
1897 if ((passdata
= alloca(vmlen
))) {
1898 memset(passdata
, 0, vmlen
);
1899 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
1900 pbx_substitute_variables_helper(ast
, emailbody
, passdata
, vmlen
);
1901 fprintf(p
, "%s" ENDL
, passdata
);
1903 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
1904 ast_channel_free(ast
);
1906 ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
1908 fprintf(p
, "Dear %s:" ENDL ENDL
"\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
1910 "in mailbox %s from %s, on %s so you might" ENDL
1911 "want to check it when you get a chance. Thanks!" ENDL ENDL
"\t\t\t\t--Asterisk" ENDL ENDL
, vmu
->fullname
,
1912 dur
, msgnum
+ 1, mailbox
, (cidname
? cidname
: (cidnum
? cidnum
: "an unknown caller")), date
);
1914 if (attach_user_voicemail
) {
1915 /* Eww. We want formats to tell us their own MIME type */
1916 char *ctype
= (!strcasecmp(format
, "ogg")) ? "application/" : "audio/x-";
1917 char tmpdir
[256], newtmp
[256];
1920 if (vmu
->volgain
< -.001 || vmu
->volgain
> .001) {
1921 create_dirpath(tmpdir
, sizeof(tmpdir
), vmu
->context
, vmu
->mailbox
, "tmp");
1922 snprintf(newtmp
, sizeof(newtmp
), "%s/XXXXXX", tmpdir
);
1923 tmpfd
= mkstemp(newtmp
);
1924 chmod(newtmp
, VOICEMAIL_FILE_MODE
& ~my_umask
);
1925 if (option_debug
> 2)
1926 ast_log(LOG_DEBUG
, "newtmp: %s\n", newtmp
);
1928 snprintf(tmpcmd
, sizeof(tmpcmd
), "sox -v %.4f %s.%s %s.%s", vmu
->volgain
, attach
, format
, newtmp
, format
);
1929 ast_safe_system(tmpcmd
);
1931 if (option_debug
> 2)
1932 ast_log(LOG_DEBUG
, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach
, format
, vmu
->volgain
, mailbox
);
1935 fprintf(p
, "--%s" ENDL
, bound
);
1936 fprintf(p
, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL
, ctype
, format
, msgnum
+ 1, format
);
1937 fprintf(p
, "Content-Transfer-Encoding: base64" ENDL
);
1938 fprintf(p
, "Content-Description: Voicemail sound attachment." ENDL
);
1939 fprintf(p
, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL
, msgnum
+ 1, format
);
1940 snprintf(fname
, sizeof(fname
), "%s.%s", attach
, format
);
1941 base_encode(fname
, p
);
1942 fprintf(p
, ENDL
"--%s--" ENDL
"." ENDL
, bound
);
1951 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
)
1954 char tmp
[80] = "/tmp/astmail-XXXXXX";
1957 if (vmu
&& ast_strlen_zero(vmu
->email
)) {
1958 ast_log(LOG_WARNING
, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu
->mailbox
);
1961 if (!strcmp(format
, "wav49"))
1963 if (option_debug
> 2)
1964 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
));
1965 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1967 if ((p
= vm_mkftemp(tmp
)) == NULL
) {
1968 ast_log(LOG_WARNING
, "Unable to launch '%s' (can't create temporary file)\n", mailcmd
);
1971 make_email_file(p
, srcemail
, vmu
, msgnum
, context
, mailbox
, cidnum
, cidname
, attach
, format
, duration
, attach_user_voicemail
, chan
, category
, 0);
1973 snprintf(tmp2
, sizeof(tmp2
), "( %s < %s ; rm -f %s ) &", mailcmd
, tmp
, tmp
);
1974 ast_safe_system(tmp2
);
1975 if (option_debug
> 2)
1976 ast_log(LOG_DEBUG
, "Sent mail to %s with command '%s'\n", vmu
->email
, mailcmd
);
1981 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
)
1984 char host
[MAXHOSTNAMELEN
] = "";
1987 char tmp
[80] = "/tmp/astmail-XXXXXX";
1988 char tmp2
[PATH_MAX
];
1992 if ((p
= vm_mkftemp(tmp
)) == NULL
) {
1993 ast_log(LOG_WARNING
, "Unable to launch '%s' (can't create temporary file)\n", mailcmd
);
1996 gethostname(host
, sizeof(host
)-1);
1997 if (strchr(srcemail
, '@'))
1998 ast_copy_string(who
, srcemail
, sizeof(who
));
2000 snprintf(who
, sizeof(who
), "%s@%s", srcemail
, host
);
2002 snprintf(dur
, sizeof(dur
), "%d:%02d", duration
/ 60, duration
% 60);
2003 strftime(date
, sizeof(date
), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu
, &tm
));
2004 fprintf(p
, "Date: %s\n", date
);
2006 if (*pagerfromstring
) {
2007 struct ast_channel
*ast
;
2008 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
2010 int vmlen
= strlen(fromstring
)*3 + 200;
2011 if ((passdata
= alloca(vmlen
))) {
2012 memset(passdata
, 0, vmlen
);
2013 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
2014 pbx_substitute_variables_helper(ast
, pagerfromstring
, passdata
, vmlen
);
2015 fprintf(p
, "From: %s <%s>\n", passdata
, who
);
2017 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
2018 ast_channel_free(ast
);
2019 } else ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
2021 fprintf(p
, "From: Asterisk PBX <%s>\n", who
);
2022 fprintf(p
, "To: %s\n", pager
);
2024 struct ast_channel
*ast
;
2025 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
2027 int vmlen
= strlen(pagersubject
) * 3 + 200;
2028 if ((passdata
= alloca(vmlen
))) {
2029 memset(passdata
, 0, vmlen
);
2030 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
2031 pbx_substitute_variables_helper(ast
, pagersubject
, passdata
, vmlen
);
2032 fprintf(p
, "Subject: %s\n\n", passdata
);
2033 } else ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
2034 ast_channel_free(ast
);
2035 } else ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
2037 fprintf(p
, "Subject: New VM\n\n");
2038 strftime(date
, sizeof(date
), "%A, %B %d, %Y at %r", &tm
);
2040 struct ast_channel
*ast
;
2041 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
2043 int vmlen
= strlen(pagerbody
)*3 + 200;
2044 if ((passdata
= alloca(vmlen
))) {
2045 memset(passdata
, 0, vmlen
);
2046 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
2047 pbx_substitute_variables_helper(ast
, pagerbody
, passdata
, vmlen
);
2048 fprintf(p
, "%s\n", passdata
);
2049 } else ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
2050 ast_channel_free(ast
);
2051 } else ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
2053 fprintf(p
, "New %s long msg in box %s\n"
2054 "from %s, on %s", dur
, mailbox
, (cidname
? cidname
: (cidnum
? cidnum
: "unknown")), date
);
2057 snprintf(tmp2
, sizeof(tmp2
), "( %s < %s ; rm -f %s ) &", mailcmd
, tmp
, tmp
);
2058 ast_safe_system(tmp2
);
2059 if (option_debug
> 2)
2060 ast_log(LOG_DEBUG
, "Sent page to %s with command '%s'\n", pager
, mailcmd
);
2065 static int get_date(char *s
, int len
)
2072 ast_localtime(&t
, &tm
, NULL
);
2074 return strftime(s
, len
, "%a %b %e %r %Z %Y", &tm
);
2077 static int play_greeting(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *filename
, char *ecodes
)
2084 RETRIEVE(filename
, -1);
2085 if (ast_fileexists(filename
, NULL
, NULL
) > 0) {
2086 res
= ast_streamfile(chan
, filename
, chan
->language
);
2088 res
= ast_waitstream(chan
, ecodes
);
2090 if (success
== -1) {
2091 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
2093 ast_log(LOG_DEBUG
, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
2094 store_file(filename
, vmu
->mailbox
, vmu
->context
, -1);
2098 DISPOSE(filename
, -1);
2103 static int invent_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *ext
, int busy
, char *ecodes
)
2107 char dest
[PATH_MAX
];
2109 snprintf(fn
, sizeof(fn
), "%s%s/%s/greet", VM_SPOOL_DIR
, vmu
->context
, ext
);
2111 if ((res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, ext
, "greet"))) {
2112 ast_log(LOG_WARNING
, "Failed to make directory(%s)\n", fn
);
2116 res
= play_greeting(chan
, vmu
, fn
, ecodes
);
2118 /* File did not exist */
2119 res
= ast_stream_and_wait(chan
, "vm-theperson", chan
->language
, ecodes
);
2122 res
= ast_say_digit_str(chan
, ext
, ecodes
, chan
->language
);
2128 res
= ast_stream_and_wait(chan
, busy
? "vm-isonphone" : "vm-isunavail", chan
->language
, ecodes
);
2132 static void free_user(struct ast_vm_user
*vmu
)
2134 if (ast_test_flag(vmu
, VM_ALLOCED
))
2138 static void free_zone(struct vm_zone
*z
)
2143 static const char *mbox(int id
)
2145 static const char *msgs
[] = {
2157 return (id
>= 0 && id
< (sizeof(msgs
)/sizeof(msgs
[0]))) ? msgs
[id
] : "tmp";
2160 static int folder_int(const char *folder
)
2162 /*assume a NULL folder means INBOX*/
2165 if (!strcasecmp(folder
, "INBOX"))
2167 else if (!strcasecmp(folder
, "Old"))
2169 else if (!strcasecmp(folder
, "Work"))
2171 else if (!strcasecmp(folder
, "Family"))
2173 else if (!strcasecmp(folder
, "Friends"))
2175 else if (!strcasecmp(folder
, "Cust1"))
2177 else if (!strcasecmp(folder
, "Cust2"))
2179 else if (!strcasecmp(folder
, "Cust3"))
2181 else if (!strcasecmp(folder
, "Cust4"))
2183 else if (!strcasecmp(folder
, "Cust5"))
2185 else /*assume they meant INBOX if folder is not found otherwise*/
2191 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2192 static int inboxcount(const char *mailbox
, int *newmsgs
, int *oldmsgs
)
2199 char tmp
[PATH_MAX
] = "";
2200 struct odbc_obj
*obj
;
2202 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 0 };
2209 /* If no mailbox, return immediately */
2210 if (ast_strlen_zero(mailbox
))
2213 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2215 context
= strchr(tmp
, '@');
2220 context
= "default";
2222 obj
= ast_odbc_request_obj(odbc_database
, 0);
2224 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table
, VM_SPOOL_DIR
, context
, tmp
, "INBOX");
2225 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
2227 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2228 ast_odbc_release_obj(obj
);
2231 res
= SQLFetch(stmt
);
2232 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2233 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
2234 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2235 ast_odbc_release_obj(obj
);
2238 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
2239 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2240 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
2241 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2242 ast_odbc_release_obj(obj
);
2245 *newmsgs
= atoi(rowdata
);
2246 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2248 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table
, VM_SPOOL_DIR
, context
, tmp
, "Old");
2249 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
2251 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2252 ast_odbc_release_obj(obj
);
2255 res
= SQLFetch(stmt
);
2256 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2257 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
2258 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2259 ast_odbc_release_obj(obj
);
2262 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
2263 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2264 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
2265 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2266 ast_odbc_release_obj(obj
);
2269 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2270 ast_odbc_release_obj(obj
);
2271 *oldmsgs
= atoi(rowdata
);
2274 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
2280 static int messagecount(const char *context
, const char *mailbox
, const char *folder
)
2282 struct odbc_obj
*obj
= NULL
;
2285 SQLHSTMT stmt
= NULL
;
2288 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 0 };
2291 /* If no mailbox, return immediately */
2292 if (ast_strlen_zero(mailbox
))
2295 obj
= ast_odbc_request_obj(odbc_database
, 0);
2297 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table
, VM_SPOOL_DIR
, context
, mailbox
, folder
);
2298 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
2300 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2303 res
= SQLFetch(stmt
);
2304 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2305 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
2306 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2309 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
2310 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2311 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
2312 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2315 nummsgs
= atoi(rowdata
);
2316 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2318 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
2322 ast_odbc_release_obj(obj
);
2326 static int has_voicemail(const char *mailbox
, const char *folder
)
2328 char tmp
[256], *tmp2
= tmp
, *mbox
, *context
;
2329 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2330 while ((context
= mbox
= strsep(&tmp2
, ","))) {
2331 strsep(&context
, "@");
2332 if (ast_strlen_zero(context
))
2333 context
= "default";
2334 if (messagecount(context
, mbox
, folder
))
2340 #elif defined(IMAP_STORAGE)
2342 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
)
2344 char *myserveremail
= serveremail
;
2349 char tmp
[80] = "/tmp/astmail-XXXXXX";
2355 /*Greetings are not retrieved from IMAP, so there is no reason to attempt storing them there either*/
2359 /* Attach only the first format */
2360 fmt
= ast_strdupa(fmt
);
2362 strsep(&stringp
, "|");
2364 if (!ast_strlen_zero(vmu
->serveremail
))
2365 myserveremail
= vmu
->serveremail
;
2367 make_file(fn
, sizeof(fn
), dir
, msgnum
);
2369 if (ast_strlen_zero(vmu
->email
)) {
2370 /*we need the vmu->email to be set when we call make_email_file, but if we keep it set,
2371 * a duplicate e-mail will be created. So at the end of this function, we will revert back to an empty
2372 * string if tempcopy is 1
2374 ast_copy_string(vmu
->email
, vmu
->imapuser
, sizeof(vmu
->email
));
2378 if (!strcmp(fmt
, "wav49"))
2380 if (option_debug
> 2)
2381 ast_log(LOG_DEBUG
, "Storing file '%s', format '%s'\n", fn
, fmt
);
2382 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2384 if ((p
= vm_mkftemp(tmp
)) == NULL
) {
2385 ast_log(LOG_WARNING
, "Unable to store '%s' (can't create temporary file)\n", fn
);
2387 *(vmu
->email
) = '\0';
2390 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);
2391 /* read mail file to memory */
2394 if ((buf
= ast_malloc(len
+1)) == NIL
) {
2395 ast_log(LOG_ERROR
, "Can't allocate %ld bytes to read message\n", len
+1);
2399 fread(buf
, len
, 1, p
);
2400 ((char *)buf
)[len
] = '\0';
2401 INIT(&str
, mail_string
, buf
, len
);
2402 init_mailstream(vms
, 0);
2403 imap_mailbox_name(mailbox
, sizeof(mailbox
), vms
, 0, 1);
2404 if (!mail_append(vms
->mailstream
, mailbox
, &str
))
2405 ast_log(LOG_ERROR
, "Error while sending the message to %s\n", mailbox
);
2409 if (option_debug
> 2)
2410 ast_log(LOG_DEBUG
, "%s stored\n", fn
);
2413 *(vmu
->email
) = '\0';
2418 static int messagecount(const char *context
, const char *mailbox
, const char *folder
)
2423 struct ast_vm_user
*vmu
, vmus
;
2424 struct vm_state
*vms_p
;
2426 int fold
= folder_int(folder
);
2428 if (ast_strlen_zero(mailbox
))
2431 /* We have to get the user before we can open the stream! */
2432 /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2433 vmu
= find_user(&vmus
, context
, mailbox
);
2435 ast_log (LOG_ERROR
,"Couldn't find mailbox %s in context %s\n",mailbox
,context
);
2438 /* No IMAP account available */
2439 if (vmu
->imapuser
[0] == '\0') {
2440 ast_log (LOG_WARNING
,"IMAP user not set for mailbox %s\n",vmu
->mailbox
);
2445 /* check if someone is accessing this box right now... */
2446 vms_p
= get_vm_state_by_imapuser(vmu
->imapuser
,1);
2448 vms_p
= get_vm_state_by_mailbox(mailbox
,1);
2451 if (option_debug
> 2)
2452 ast_log (LOG_DEBUG
,"Returning before search - user is logged in\n");
2453 if (fold
== 0) {/*INBOX*/
2454 return vms_p
->newmessages
;
2456 if (fold
== 1) {/*Old messages*/
2457 return vms_p
->oldmessages
;
2461 /* add one if not there... */
2462 vms_p
= get_vm_state_by_imapuser(vmu
->imapuser
,0);
2464 vms_p
= get_vm_state_by_mailbox(mailbox
,0);
2468 if (option_debug
> 2)
2469 ast_log (LOG_DEBUG
,"Adding new vmstate for %s\n",vmu
->imapuser
);
2470 if (!(vms_p
= ast_calloc(1, sizeof(*vms_p
)))) {
2473 ast_copy_string(vms_p
->imapuser
,vmu
->imapuser
, sizeof(vms_p
->imapuser
));
2474 ast_copy_string(vms_p
->username
, mailbox
, sizeof(vms_p
->username
)); /* save for access from interactive entry point */
2475 vms_p
->mailstream
= NIL
; /* save for access from interactive entry point */
2476 if (option_debug
> 2)
2477 ast_log (LOG_DEBUG
,"Copied %s to %s\n",vmu
->imapuser
,vms_p
->imapuser
);
2479 /* set mailbox to INBOX! */
2480 ast_copy_string(vms_p
->curbox
, mbox(fold
), sizeof(vms_p
->curbox
));
2481 init_vm_state(vms_p
);
2482 vmstate_insert(vms_p
);
2484 ret
= init_mailstream(vms_p
, fold
);
2485 if (!vms_p
->mailstream
) {
2486 ast_log (LOG_ERROR
,"IMAP mailstream is NULL\n");
2490 pgm
= mail_newsearchpgm ();
2491 hdr
= mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox
);
2497 /* In the special case where fold is 1 (old messages) we have to do things a bit
2498 * differently. Old messages are stored in the INBOX but are marked as "seen"
2507 vms_p
->vmArrayIndex
= 0;
2508 mail_search_full (vms_p
->mailstream
, NULL
, pgm
, NIL
);
2510 vms_p
->newmessages
= vms_p
->vmArrayIndex
;
2512 vms_p
->oldmessages
= vms_p
->vmArrayIndex
;
2513 /*Freeing the searchpgm also frees the searchhdr*/
2514 mail_free_searchpgm(&pgm
);
2516 return vms_p
->vmArrayIndex
;
2518 mail_ping(vms_p
->mailstream
);
2522 static int inboxcount(const char *mailbox_context
, int *newmsgs
, int *oldmsgs
)
2524 char tmp
[PATH_MAX
] = "";
2534 if (option_debug
> 2)
2535 ast_log (LOG_DEBUG
,"Mailbox is set to %s\n",mailbox_context
);
2536 /* If no mailbox, return immediately */
2537 if (ast_strlen_zero(mailbox_context
))
2540 ast_copy_string(tmp
, mailbox_context
, sizeof(tmp
));
2541 context
= strchr(tmp
, '@');
2542 if (strchr(mailbox_context
, ',')) {
2544 ast_copy_string(tmp
, mailbox_context
, sizeof(tmp
));
2546 while ((cur
= strsep(&mb
, ", "))) {
2547 if (!ast_strlen_zero(cur
)) {
2548 if (inboxcount(cur
, newmsgs
? &tmpnew
: NULL
, oldmsgs
? &tmpold
: NULL
))
2565 context
= "default";
2566 mailboxnc
= (char *)mailbox_context
;
2569 if ((*newmsgs
= messagecount(context
, mailboxnc
, "INBOX")) < 0)
2573 if ((*oldmsgs
= messagecount(context
, mailboxnc
, "Old")) < 0)
2580 static int has_voicemail(const char *mailbox
, const char *folder
)
2582 char tmp
[256], *tmp2
, *mbox
, *context
;
2583 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2585 if (strchr(tmp2
, ',')) {
2586 while ((mbox
= strsep(&tmp2
, ","))) {
2587 if (!ast_strlen_zero(mbox
)) {
2588 if (has_voicemail(mbox
, folder
))
2593 if ((context
= strchr(tmp
, '@')))
2596 context
= "default";
2597 return messagecount(context
, tmp
, folder
) ? 1 : 0;
2600 static int copy_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, int imbox
, int msgnum
, long duration
, struct ast_vm_user
*recip
, char *fmt
, char *dir
)
2602 struct vm_state
*sendvms
= NULL
, *destvms
= NULL
;
2603 char messagestring
[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2604 if (msgnum
>= recip
->maxmsg
) {
2605 ast_log(LOG_WARNING
, "Unable to copy mail, mailbox %s is full\n", recip
->mailbox
);
2608 if (!(sendvms
= get_vm_state_by_imapuser(vmu
->imapuser
, 0))) {
2609 ast_log(LOG_ERROR
, "Couldn't get vm_state for originator's mailbox!!\n");
2612 if (!(destvms
= get_vm_state_by_imapuser(recip
->imapuser
, 0))) {
2613 ast_log(LOG_ERROR
, "Couldn't get vm_state for destination mailbox!\n");
2616 snprintf(messagestring
, sizeof(messagestring
), "%ld", sendvms
->msgArray
[msgnum
]);
2617 if ((mail_copy(sendvms
->mailstream
, messagestring
, (char *) mbox(imbox
)) == T
))
2619 ast_log(LOG_WARNING
, "Unable to copy message from mailbox %s to mailbox %s\n", vmu
->mailbox
, recip
->mailbox
);
2624 #ifndef IMAP_STORAGE
2625 /* copy message only used by file storage */
2626 static int copy_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, int imbox
, int msgnum
, long duration
, struct ast_vm_user
*recip
, char *fmt
, char *dir
)
2628 char fromdir
[PATH_MAX
], todir
[PATH_MAX
], frompath
[PATH_MAX
], topath
[PATH_MAX
];
2629 const char *frombox
= mbox(imbox
);
2632 ast_log(LOG_NOTICE
, "Copying message from %s@%s to %s@%s\n", vmu
->mailbox
, vmu
->context
, recip
->mailbox
, recip
->context
);
2634 create_dirpath(todir
, sizeof(todir
), recip
->context
, recip
->mailbox
, "INBOX");
2637 make_dir(fromdir
, sizeof(fromdir
), vmu
->context
, vmu
->mailbox
, frombox
);
2639 ast_copy_string(fromdir
, dir
, sizeof(fromdir
));
2641 make_file(frompath
, sizeof(frompath
), fromdir
, msgnum
);
2643 if (vm_lock_path(todir
))
2644 return ERROR_LOCK_PATH
;
2648 make_file(topath
, sizeof(topath
), todir
, recipmsgnum
);
2649 if (!EXISTS(todir
, recipmsgnum
, topath
, chan
->language
))
2652 } while (recipmsgnum
< recip
->maxmsg
);
2653 if (recipmsgnum
< recip
->maxmsg
) {
2654 COPY(fromdir
, msgnum
, todir
, recipmsgnum
, recip
->mailbox
, recip
->context
, frompath
, topath
);
2656 ast_log(LOG_ERROR
, "Recipient mailbox %s@%s is full\n", recip
->mailbox
, recip
->context
);
2658 ast_unlock_path(todir
);
2659 notify_new_message(chan
, recip
, recipmsgnum
, duration
, fmt
, S_OR(chan
->cid
.cid_num
, NULL
), S_OR(chan
->cid
.cid_name
, NULL
));
2664 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2665 static int messagecount(const char *context
, const char *mailbox
, const char *folder
)
2667 return __has_voicemail(context
, mailbox
, folder
, 0);
2671 static int __has_voicemail(const char *context
, const char *mailbox
, const char *folder
, int shortcircuit
)
2679 /* If no mailbox, return immediately */
2680 if (ast_strlen_zero(mailbox
))
2683 context
= "default";
2684 snprintf(fn
, sizeof(fn
), "%s%s/%s/%s", VM_SPOOL_DIR
, context
, mailbox
, folder
);
2688 while ((de
= readdir(dir
))) {
2689 if (!strncasecmp(de
->d_name
, "msg", 3)) {
2693 } else if (!strncasecmp(de
->d_name
+ 8, "txt", 3))
2702 static int has_voicemail(const char *mailbox
, const char *folder
)
2704 char tmp
[256], *tmp2
= tmp
, *mbox
, *context
;
2705 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2706 while ((mbox
= strsep(&tmp2
, ","))) {
2707 if ((context
= strchr(mbox
, '@')))
2710 context
= "default";
2711 if (__has_voicemail(context
, mbox
, folder
, 1))
2718 static int inboxcount(const char *mailbox
, int *newmsgs
, int *oldmsgs
)
2727 /* If no mailbox, return immediately */
2728 if (ast_strlen_zero(mailbox
))
2730 if (strchr(mailbox
, ',')) {
2734 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2736 while ((cur
= strsep(&mb
, ", "))) {
2737 if (!ast_strlen_zero(cur
)) {
2738 if (inboxcount(cur
, newmsgs
? &tmpnew
: NULL
, oldmsgs
? &tmpold
: NULL
))
2750 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2751 context
= strchr(tmp
, '@');
2756 context
= "default";
2758 *newmsgs
= __has_voicemail(context
, tmp
, "INBOX", 0);
2760 *oldmsgs
= __has_voicemail(context
, tmp
, "Old", 0);
2766 static void run_externnotify(char *context
, char *extension
)
2768 char arguments
[255];
2769 char ext_context
[256] = "";
2770 int newvoicemails
= 0, oldvoicemails
= 0;
2771 struct ast_smdi_mwi_message
*mwi_msg
;
2773 if (!ast_strlen_zero(context
))
2774 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", extension
, context
);
2776 ast_copy_string(ext_context
, extension
, sizeof(ext_context
));
2778 if (!strcasecmp(externnotify
, "smdi")) {
2779 if (ast_app_has_voicemail(ext_context
, NULL
))
2780 ast_smdi_mwi_set(smdi_iface
, extension
);
2782 ast_smdi_mwi_unset(smdi_iface
, extension
);
2784 if ((mwi_msg
= ast_smdi_mwi_message_wait_station(smdi_iface
, SMDI_MWI_WAIT_TIMEOUT
, extension
))) {
2785 ast_log(LOG_ERROR
, "Error executing SMDI MWI change for %s\n", extension
);
2786 if (!strncmp(mwi_msg
->cause
, "INV", 3))
2787 ast_log(LOG_ERROR
, "Invalid MWI extension: %s\n", mwi_msg
->fwd_st
);
2788 else if (!strncmp(mwi_msg
->cause
, "BLK", 3))
2789 ast_log(LOG_WARNING
, "MWI light was already on or off for %s\n", mwi_msg
->fwd_st
);
2790 ast_log(LOG_WARNING
, "The switch reported '%s'\n", mwi_msg
->cause
);
2791 ASTOBJ_UNREF(mwi_msg
, ast_smdi_mwi_message_destroy
);
2794 ast_log(LOG_DEBUG
, "Successfully executed SMDI MWI change for %s\n", extension
);
2796 } else if (!ast_strlen_zero(externnotify
)) {
2797 if (inboxcount(ext_context
, &newvoicemails
, &oldvoicemails
)) {
2798 ast_log(LOG_ERROR
, "Problem in calculating number of voicemail messages available for extension %s\n", extension
);
2800 snprintf(arguments
, sizeof(arguments
), "%s %s %s %d&", externnotify
, context
, extension
, newvoicemails
);
2802 ast_log(LOG_DEBUG
, "Executing %s\n", arguments
);
2803 ast_safe_system(arguments
);
2808 struct leave_vm_options
{
2810 signed char record_gain
;
2813 static int leave_voicemail(struct ast_channel
*chan
, char *ext
, struct leave_vm_options
*options
)
2816 int newmsgs
, oldmsgs
;
2817 struct vm_state
*vms
= NULL
;
2819 char txtfile
[PATH_MAX
], tmptxtfile
[PATH_MAX
];
2830 char dir
[PATH_MAX
], tmpdir
[PATH_MAX
];
2831 char dest
[PATH_MAX
];
2833 char prefile
[PATH_MAX
] = "";
2834 char tempfile
[PATH_MAX
] = "";
2835 char ext_context
[256] = "";
2838 char ecodes
[16] = "#";
2839 char tmp
[1024] = "", *tmpptr
;
2840 struct ast_vm_user
*vmu
;
2841 struct ast_vm_user svm
;
2842 const char *category
= NULL
;
2844 ast_copy_string(tmp
, ext
, sizeof(tmp
));
2846 context
= strchr(tmp
, '@');
2849 tmpptr
= strchr(context
, '&');
2851 tmpptr
= strchr(ext
, '&');
2857 category
= pbx_builtin_getvar_helper(chan
, "VM_CATEGORY");
2859 if (option_debug
> 2)
2860 ast_log(LOG_DEBUG
, "Before find_user\n");
2861 if (!(vmu
= find_user(&svm
, context
, ext
))) {
2862 ast_log(LOG_WARNING
, "No entry in voicemail config file for '%s'\n", ext
);
2863 if (ast_test_flag(options
, OPT_PRIORITY_JUMP
) || ast_opt_priority_jumping
)
2864 ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101);
2865 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
2868 /* Setup pre-file if appropriate */
2869 if (strcmp(vmu
->context
, "default"))
2870 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", ext
, vmu
->context
);
2872 ast_copy_string(ext_context
, vmu
->mailbox
, sizeof(ext_context
));
2873 if (ast_test_flag(options
, OPT_BUSY_GREETING
)) {
2874 res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, ext
, "busy");
2875 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/busy", VM_SPOOL_DIR
, vmu
->context
, ext
);
2876 } else if (ast_test_flag(options
, OPT_UNAVAIL_GREETING
)) {
2877 res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, ext
, "unavail");
2878 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/unavail", VM_SPOOL_DIR
, vmu
->context
, ext
);
2880 snprintf(tempfile
, sizeof(tempfile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, ext
);
2881 if ((res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, ext
, "temp"))) {
2882 ast_log(LOG_WARNING
, "Failed to make directory (%s)\n", tempfile
);
2885 RETRIEVE(tempfile
, -1);
2886 if (ast_fileexists(tempfile
, NULL
, NULL
) > 0)
2887 ast_copy_string(prefile
, tempfile
, sizeof(prefile
));
2888 DISPOSE(tempfile
, -1);
2889 /* It's easier just to try to make it than to check for its existence */
2890 create_dirpath(dir
, sizeof(dir
), vmu
->context
, ext
, "INBOX");
2891 create_dirpath(tmpdir
, sizeof(tmpdir
), vmu
->context
, ext
, "tmp");
2893 /* Check current or macro-calling context for special extensions */
2894 if (ast_test_flag(vmu
, VM_OPERATOR
)) {
2895 if (!ast_strlen_zero(vmu
->exit
)) {
2896 if (ast_exists_extension(chan
, vmu
->exit
, "o", 1, chan
->cid
.cid_num
)) {
2897 strncat(ecodes
, "0", sizeof(ecodes
) - strlen(ecodes
) - 1);
2900 } else if (ast_exists_extension(chan
, chan
->context
, "o", 1, chan
->cid
.cid_num
)) {
2901 strncat(ecodes
, "0", sizeof(ecodes
) - strlen(ecodes
) - 1);
2904 else if (!ast_strlen_zero(chan
->macrocontext
) && ast_exists_extension(chan
, chan
->macrocontext
, "o", 1, chan
->cid
.cid_num
)) {
2905 strncat(ecodes
, "0", sizeof(ecodes
) - strlen(ecodes
) - 1);
2910 if (!ast_strlen_zero(vmu
->exit
)) {
2911 if (ast_exists_extension(chan
, vmu
->exit
, "a", 1, chan
->cid
.cid_num
))
2912 strncat(ecodes
, "*", sizeof(ecodes
) - strlen(ecodes
) - 1);
2913 } else if (ast_exists_extension(chan
, chan
->context
, "a", 1, chan
->cid
.cid_num
))
2914 strncat(ecodes
, "*", sizeof(ecodes
) - strlen(ecodes
) - 1);
2915 else if (!ast_strlen_zero(chan
->macrocontext
) && ast_exists_extension(chan
, chan
->macrocontext
, "a", 1, chan
->cid
.cid_num
)) {
2916 strncat(ecodes
, "*", sizeof(ecodes
) - strlen(ecodes
) - 1);
2920 /* Play the beginning intro if desired */
2921 if (!ast_strlen_zero(prefile
)) {
2922 res
= play_greeting(chan
, vmu
, prefile
, ecodes
);
2924 /* The file did not exist */
2926 ast_log(LOG_DEBUG
, "%s doesn't exist, doing what we can\n", prefile
);
2927 res
= invent_message(chan
, vmu
, ext
, ast_test_flag(options
, OPT_BUSY_GREETING
), ecodes
);
2931 ast_log(LOG_DEBUG
, "Hang up during prefile playback\n");
2933 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
2938 /* On a '#' we skip the instructions */
2939 ast_set_flag(options
, OPT_SILENT
);
2942 if (!res
&& !ast_test_flag(options
, OPT_SILENT
)) {
2943 res
= ast_stream_and_wait(chan
, INTRO
, chan
->language
, ecodes
);
2945 ast_set_flag(options
, OPT_SILENT
);
2950 ast_stopstream(chan
);
2951 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2952 other than the operator -- an automated attendant or mailbox login for example */
2954 chan
->exten
[0] = 'a';
2955 chan
->exten
[1] = '\0';
2956 if (!ast_strlen_zero(vmu
->exit
)) {
2957 ast_copy_string(chan
->context
, vmu
->exit
, sizeof(chan
->context
));
2958 } else if (ausemacro
&& !ast_strlen_zero(chan
->macrocontext
)) {
2959 ast_copy_string(chan
->context
, chan
->macrocontext
, sizeof(chan
->context
));
2963 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "USEREXIT");
2967 /* Check for a '0' here */
2970 if (ouseexten
|| ousemacro
) {
2971 chan
->exten
[0] = 'o';
2972 chan
->exten
[1] = '\0';
2973 if (!ast_strlen_zero(vmu
->exit
)) {
2974 ast_copy_string(chan
->context
, vmu
->exit
, sizeof(chan
->context
));
2975 } else if (ousemacro
&& !ast_strlen_zero(chan
->macrocontext
)) {
2976 ast_copy_string(chan
->context
, chan
->macrocontext
, sizeof(chan
->context
));
2978 ast_play_and_wait(chan
, "transfer");
2981 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "USEREXIT");
2987 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
2990 /* The meat of recording the message... All the announcements and beeps have been played*/
2991 ast_copy_string(fmt
, vmfmts
, sizeof(fmt
));
2992 if (!ast_strlen_zero(fmt
)) {
2996 /* Is ext a mailbox? */
2997 /* must open stream for this user to get info! */
2998 res
= inboxcount(ext_context
, &newmsgs
, &oldmsgs
);
3000 ast_log(LOG_NOTICE
,"Can not leave voicemail, unable to count messages\n");
3003 if (!(vms
= get_vm_state_by_mailbox(ext
,0))) {
3004 /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
3006 if (!(vms
= ast_calloc(1, sizeof(*vms
)))) {
3007 ast_log(LOG_ERROR
, "Couldn't allocate necessary space\n");
3010 ast_copy_string(vms
->imapuser
, vmu
->imapuser
, sizeof(vms
->imapuser
));
3011 ast_copy_string(vms
->username
, ext
, sizeof(vms
->username
));
3012 vms
->mailstream
= NIL
;
3013 if (option_debug
> 2)
3014 ast_log(LOG_DEBUG
, "Copied %s to %s\n", vmu
->imapuser
, vms
->imapuser
);
3016 ast_copy_string(vms
->curbox
, mbox(0), sizeof(vms
->curbox
));
3018 vmstate_insert(vms
);
3019 vms
= get_vm_state_by_mailbox(ext
,0);
3022 /* here is a big difference! We add one to it later */
3023 msgnum
= newmsgs
+ oldmsgs
;
3024 if (option_debug
> 2)
3025 ast_log(LOG_DEBUG
, "Messagecount set to %d\n",msgnum
);
3026 snprintf(fn
, sizeof(fn
), "%s/imap/msg%s%04d", VM_SPOOL_DIR
, vmu
->mailbox
, msgnum
);
3027 /* set variable for compatability */
3028 pbx_builtin_setvar_helper(chan
, "VM_MESSAGEFILE", "IMAP_STORAGE");
3030 /* Check if mailbox is full */
3031 check_quota(vms
, imapfolder
);
3032 if (vms
->quota_limit
&& vms
->quota_usage
>= vms
->quota_limit
) {
3034 ast_log(LOG_DEBUG
, "*** QUOTA EXCEEDED!! %u >= %u\n", vms
->quota_usage
, vms
->quota_limit
);
3035 ast_play_and_wait(chan
, "vm-mailboxfull");
3038 if (option_debug
> 2)
3039 ast_log(LOG_DEBUG
, "Checking message number quota - mailbox has %d messages, maximum is set to %d\n",msgnum
,vmu
->maxmsg
);
3040 if (msgnum
>= vmu
->maxmsg
) {
3041 res
= ast_streamfile(chan
, "vm-mailboxfull", chan
->language
);
3043 res
= ast_waitstream(chan
, "");
3044 ast_log(LOG_WARNING
, "No more messages possible\n");
3045 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3049 /* Check if we have exceeded maxmsg */
3050 if (msgnum
>= vmu
->maxmsg
) {
3051 ast_log(LOG_WARNING
, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum
, vmu
->maxmsg
);
3052 ast_play_and_wait(chan
, "vm-mailboxfull");
3055 /* here is a big difference! We add one to it later */
3056 if (option_debug
> 2)
3057 ast_log(LOG_DEBUG
, "Messagecount set to %d\n",msgnum
);
3059 if (count_messages(vmu
, dir
) >= vmu
->maxmsg
) {
3060 res
= ast_streamfile(chan
, "vm-mailboxfull", chan
->language
);
3062 res
= ast_waitstream(chan
, "");
3063 ast_log(LOG_WARNING
, "No more messages possible\n");
3064 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3069 snprintf(tmptxtfile
, sizeof(tmptxtfile
), "%s/XXXXXX", tmpdir
);
3070 txtdes
= mkstemp(tmptxtfile
);
3071 chmod(tmptxtfile
, VOICEMAIL_FILE_MODE
& ~my_umask
);
3073 res
= ast_streamfile(chan
, "vm-mailboxfull", chan
->language
);
3075 res
= ast_waitstream(chan
, "");
3076 ast_log(LOG_ERROR
, "Unable to create message file: %s\n", strerror(errno
));
3077 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3081 /* Now play the beep once we have the message number for our next message. */
3083 /* Unless we're *really* silent, try to send the beep */
3084 res
= ast_stream_and_wait(chan
, "beep", chan
->language
, "");
3087 /* Store information */
3088 txt
= fdopen(txtdes
, "w+");
3090 get_date(date
, sizeof(date
));
3093 "; Message Information file\n"
3112 ast_callerid_merge(callerid
, sizeof(callerid
), S_OR(chan
->cid
.cid_name
, NULL
), S_OR(chan
->cid
.cid_num
, NULL
), "Unknown"),
3113 date
, (long)time(NULL
),
3114 category
? category
: "");
3116 ast_log(LOG_WARNING
, "Error opening text file for output\n");
3118 res
= play_record_review(chan
, NULL
, tmptxtfile
, vmmaxmessage
, fmt
, 1, vmu
, &duration
, NULL
, options
->record_gain
, vms
);
3120 res
= play_record_review(chan
, NULL
, tmptxtfile
, vmmaxmessage
, fmt
, 1, vmu
, &duration
, NULL
, options
->record_gain
, NULL
);
3124 if (duration
< vmminmessage
) {
3126 if (option_verbose
> 2)
3127 ast_verbose( VERBOSE_PREFIX_3
"Recording was %d seconds long but needs to be at least %d - abandoning\n", duration
, vmminmessage
);
3128 ast_filedelete(tmptxtfile
, NULL
);
3131 fprintf(txt
, "duration=%d\n", duration
);
3133 if (vm_lock_path(dir
)) {
3134 ast_log(LOG_ERROR
, "Couldn't lock directory %s. Voicemail will be lost.\n", dir
);
3136 ast_filedelete(tmptxtfile
, NULL
);
3138 } else if (ast_fileexists(tmptxtfile
, NULL
, NULL
) <= 0) {
3140 ast_log(LOG_DEBUG
, "The recorded media file is gone, so we should remove the .txt file too!\n");
3142 ast_unlock_path(dir
);
3145 make_file(fn
, sizeof(fn
), dir
, msgnum
);
3146 if (!EXISTS(dir
, msgnum
, fn
, NULL
))
3151 /* assign a variable with the name of the voicemail file */
3152 #ifndef IMAP_STORAGE
3153 pbx_builtin_setvar_helper(chan
, "VM_MESSAGEFILE", fn
);
3155 pbx_builtin_setvar_helper(chan
, "VM_MESSAGEFILE", "IMAP_STORAGE");
3158 snprintf(txtfile
, sizeof(txtfile
), "%s.txt", fn
);
3159 ast_filerename(tmptxtfile
, fn
, NULL
);
3160 rename(tmptxtfile
, txtfile
);
3162 ast_unlock_path(dir
);
3163 /* We must store the file first, before copying the message, because
3164 * ODBC storage does the entire copy with SQL.
3166 if (ast_fileexists(fn
, NULL
, NULL
) > 0) {
3167 STORE(dir
, vmu
->mailbox
, vmu
->context
, msgnum
, chan
, vmu
, fmt
, duration
, vms
);
3170 /* Are there to be more recipients of this message? */
3172 struct ast_vm_user recipu
, *recip
;
3173 char *exten
, *context
;
3175 exten
= strsep(&tmpptr
, "&");
3176 context
= strchr(exten
, '@');
3181 if ((recip
= find_user(&recipu
, context
, exten
))) {
3182 copy_message(chan
, vmu
, 0, msgnum
, duration
, recip
, fmt
, dir
);
3186 /* Notification and disposal needs to happen after the copy, though. */
3187 if (ast_fileexists(fn
, NULL
, NULL
)) {
3188 notify_new_message(chan
, vmu
, msgnum
, duration
, fmt
, S_OR(chan
->cid
.cid_num
, NULL
), S_OR(chan
->cid
.cid_name
, NULL
));
3189 DISPOSE(dir
, msgnum
);
3199 if (duration
< vmminmessage
)
3200 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3201 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3203 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "SUCCESS");
3205 ast_log(LOG_WARNING
, "No format for saving voicemail?\n");
3212 #ifndef IMAP_STORAGE
3213 static int resequence_mailbox(struct ast_vm_user
*vmu
, char *dir
)
3215 /* we know max messages, so stop process when number is hit */
3221 if (vm_lock_path(dir
))
3222 return ERROR_LOCK_PATH
;
3224 for (x
= 0, dest
= 0; x
< vmu
->maxmsg
; x
++) {
3225 make_file(sfn
, sizeof(sfn
), dir
, x
);
3226 if (EXISTS(dir
, x
, sfn
, NULL
)) {
3229 make_file(dfn
, sizeof(dfn
), dir
, dest
);
3230 RENAME(dir
, x
, vmu
->mailbox
, vmu
->context
, dir
, dest
, sfn
, dfn
);
3236 ast_unlock_path(dir
);
3242 static int say_and_wait(struct ast_channel
*chan
, int num
, const char *language
)
3245 d
= ast_say_number(chan
, num
, AST_DIGIT_ANY
, language
, (char *) NULL
);
3249 static int save_to_folder(struct ast_vm_user
*vmu
, struct vm_state
*vms
, int msg
, int box
)
3252 /* we must use mbox(x) folder names, and copy the message there */
3257 /* if save to Old folder, just leave in INBOX */
3258 if (box
== 1) return 10;
3259 /* get the real IMAP message number for this message */
3260 snprintf(sequence
, sizeof(sequence
), "%ld", vms
->msgArray
[msg
]);
3261 if (option_debug
> 2)
3262 ast_log(LOG_DEBUG
, "Copying sequence %s to mailbox %s\n",sequence
,(char *) mbox(box
));
3263 res
= mail_copy(vms
->mailstream
,sequence
,(char *) mbox(box
));
3264 if (res
== 1) return 0;
3267 char *dir
= vms
->curdir
;
3268 char *username
= vms
->username
;
3269 char *context
= vmu
->context
;
3272 char ddir
[PATH_MAX
];
3273 const char *dbox
= mbox(box
);
3275 make_file(sfn
, sizeof(sfn
), dir
, msg
);
3276 create_dirpath(ddir
, sizeof(ddir
), context
, username
, dbox
);
3278 if (vm_lock_path(ddir
))
3279 return ERROR_LOCK_PATH
;
3281 for (x
= 0; x
< vmu
->maxmsg
; x
++) {
3282 make_file(dfn
, sizeof(dfn
), ddir
, x
);
3283 if (!EXISTS(ddir
, x
, dfn
, NULL
))
3286 if (x
>= vmu
->maxmsg
) {
3287 ast_unlock_path(ddir
);
3288 return ERROR_MAILBOX_FULL
;
3290 if (strcmp(sfn
, dfn
)) {
3291 COPY(dir
, msg
, ddir
, x
, username
, context
, sfn
, dfn
);
3293 ast_unlock_path(ddir
);
3298 static int adsi_logo(unsigned char *buf
)
3301 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_CENT
, 0, "Comedian Mail", "");
3302 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_CENT
, 0, "(C)2002-2006 Digium, Inc.", "");
3306 static int adsi_load_vmail(struct ast_channel
*chan
, int *useadsi
)
3308 unsigned char buf
[256];
3314 bytes
+= ast_adsi_data_mode(buf
+ bytes
);
3315 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3318 bytes
+= adsi_logo(buf
);
3319 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Downloading Scripts", "");
3321 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " .", "");
3323 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3324 bytes
+= ast_adsi_data_mode(buf
+ bytes
);
3325 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3327 if (ast_adsi_begin_download(chan
, addesc
, adsifdn
, adsisec
, adsiver
)) {
3329 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Load Cancelled.", "");
3330 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "ADSI Unavailable", "");
3331 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3332 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3333 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3340 bytes
+= ast_adsi_logo(buf
);
3341 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Downloading Scripts", "");
3342 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " ..", "");
3343 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3344 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3347 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 0, "Listen", "Listen", "1", 1);
3348 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 1, "Folder", "Folder", "2", 1);
3349 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 2, "Advanced", "Advnced", "3", 1);
3350 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 3, "Options", "Options", "0", 1);
3351 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 4, "Help", "Help", "*", 1);
3352 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 5, "Exit", "Exit", "#", 1);
3353 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
3356 /* Add another dot */
3358 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " ...", "");
3359 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3361 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3362 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3366 /* These buttons we load but don't use yet */
3367 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 6, "Previous", "Prev", "4", 1);
3368 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 8, "Repeat", "Repeat", "5", 1);
3369 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 7, "Delete", "Delete", "7", 1);
3370 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 9, "Next", "Next", "6", 1);
3371 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 10, "Save", "Save", "9", 1);
3372 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 11, "Undelete", "Restore", "7", 1);
3373 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
3376 /* Add another dot */
3378 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " ....", "");
3379 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3380 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3385 snprintf(num
, sizeof(num
), "%d", x
);
3386 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 12 + x
, mbox(x
), mbox(x
), num
, 1);
3388 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 12 + 5, "Cancel", "Cancel", "#", 1);
3389 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
3392 /* Add another dot */
3394 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " .....", "");
3395 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3396 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3399 if (ast_adsi_end_download(chan
)) {
3401 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Download Unsuccessful.", "");
3402 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "ADSI Unavailable", "");
3403 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3404 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3405 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3409 bytes
+= ast_adsi_download_disconnect(buf
+ bytes
);
3410 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3411 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
3414 ast_log(LOG_DEBUG
, "Done downloading scripts...\n");
3419 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, " ......", "");
3420 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3423 ast_log(LOG_DEBUG
, "Restarting session...\n");
3426 /* Load the session now */
3427 if (ast_adsi_load_session(chan
, adsifdn
, adsiver
, 1) == 1) {
3429 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Scripts Loaded!", "");
3431 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Load Failed!", "");
3433 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3437 static void adsi_begin(struct ast_channel
*chan
, int *useadsi
)
3440 if (!ast_adsi_available(chan
))
3442 x
= ast_adsi_load_session(chan
, adsifdn
, adsiver
, 1);
3446 if (adsi_load_vmail(chan
, useadsi
)) {
3447 ast_log(LOG_WARNING
, "Unable to upload voicemail scripts\n");
3454 static void adsi_login(struct ast_channel
*chan
)
3456 unsigned char buf
[256];
3458 unsigned char keys
[8];
3460 if (!ast_adsi_available(chan
))
3465 /* Set one key for next */
3466 keys
[3] = ADSI_KEY_APPS
+ 3;
3468 bytes
+= adsi_logo(buf
+ bytes
);
3469 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, " ", "");
3470 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, " ", "");
3471 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3472 bytes
+= ast_adsi_input_format(buf
+ bytes
, 1, ADSI_DIR_FROM_LEFT
, 0, "Mailbox: ******", "");
3473 bytes
+= ast_adsi_input_control(buf
+ bytes
, ADSI_COMM_PAGE
, 4, 1, 1, ADSI_JUST_LEFT
);
3474 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 3, "Enter", "Enter", "#", 1);
3475 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3476 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3477 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3480 static void adsi_password(struct ast_channel
*chan
)
3482 unsigned char buf
[256];
3484 unsigned char keys
[8];
3486 if (!ast_adsi_available(chan
))
3491 /* Set one key for next */
3492 keys
[3] = ADSI_KEY_APPS
+ 3;
3494 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3495 bytes
+= ast_adsi_input_format(buf
+ bytes
, 1, ADSI_DIR_FROM_LEFT
, 0, "Password: ******", "");
3496 bytes
+= ast_adsi_input_control(buf
+ bytes
, ADSI_COMM_PAGE
, 4, 0, 1, ADSI_JUST_LEFT
);
3497 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3498 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3499 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3502 static void adsi_folders(struct ast_channel
*chan
, int start
, char *label
)
3504 unsigned char buf
[256];
3506 unsigned char keys
[8];
3509 if (!ast_adsi_available(chan
))
3513 y
= ADSI_KEY_APPS
+ 12 + start
+ x
;
3514 if (y
> ADSI_KEY_APPS
+ 12 + 4)
3516 keys
[x
] = ADSI_KEY_SKT
| y
;
3518 keys
[5] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 17);
3522 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_CENT
, 0, label
, "");
3523 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_CENT
, 0, " ", "");
3524 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3525 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3526 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3528 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3531 static void adsi_message(struct ast_channel
*chan
, struct vm_state
*vms
)
3534 unsigned char buf
[256];
3535 char buf1
[256], buf2
[256];
3541 char datetime
[21]="";
3544 unsigned char keys
[8];
3548 if (!ast_adsi_available(chan
))
3551 /* Retrieve important info */
3552 snprintf(fn2
, sizeof(fn2
), "%s.txt", vms
->fn
);
3553 f
= fopen(fn2
, "r");
3556 fgets((char *)buf
, sizeof(buf
), f
);
3559 stringp
= (char *)buf
;
3560 strsep(&stringp
, "=");
3561 val
= strsep(&stringp
, "=");
3562 if (!ast_strlen_zero(val
)) {
3563 if (!strcmp((char *)buf
, "callerid"))
3564 ast_copy_string(cid
, val
, sizeof(cid
));
3565 if (!strcmp((char *)buf
, "origdate"))
3566 ast_copy_string(datetime
, val
, sizeof(datetime
));
3572 /* New meaning for keys */
3574 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 6 + x
);
3579 /* No prev key, provide "Folder" instead */
3580 keys
[0] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
3582 if (vms
->curmsg
>= vms
->lastmsg
) {
3583 /* If last message ... */
3585 /* but not only message, provide "Folder" instead */
3586 keys
[3] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
3587 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3590 /* Otherwise if only message, leave blank */
3595 if (!ast_strlen_zero(cid
)) {
3596 ast_callerid_parse(cid
, &name
, &num
);
3600 name
= "Unknown Caller";
3602 /* If deleted, show "undeleted" */
3604 if (vms
->deleted
[vms
->curmsg
])
3605 keys
[1] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 11);
3608 keys
[5] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 5);
3609 snprintf(buf1
, sizeof(buf1
), "%s%s", vms
->curbox
,
3610 strcasecmp(vms
->curbox
, "INBOX") ? " Messages" : "");
3611 snprintf(buf2
, sizeof(buf2
), "Message %d of %d", vms
->curmsg
+ 1, vms
->lastmsg
+ 1);
3613 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_LEFT
, 0, buf1
, "");
3614 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_LEFT
, 0, buf2
, "");
3615 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_LEFT
, 0, name
, "");
3616 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, datetime
, "");
3617 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3618 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3619 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3621 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3624 static void adsi_delete(struct ast_channel
*chan
, struct vm_state
*vms
)
3627 unsigned char buf
[256];
3628 unsigned char keys
[8];
3632 if (!ast_adsi_available(chan
))
3635 /* New meaning for keys */
3637 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 6 + x
);
3643 /* No prev key, provide "Folder" instead */
3644 keys
[0] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
3646 if (vms
->curmsg
>= vms
->lastmsg
) {
3647 /* If last message ... */
3649 /* but not only message, provide "Folder" instead */
3650 keys
[3] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
3652 /* Otherwise if only message, leave blank */
3657 /* If deleted, show "undeleted" */
3658 if (vms
->deleted
[vms
->curmsg
])
3659 keys
[1] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 11);
3662 keys
[5] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 5);
3663 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3664 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3666 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3669 static void adsi_status(struct ast_channel
*chan
, struct vm_state
*vms
)
3671 unsigned char buf
[256] = "";
3672 char buf1
[256] = "", buf2
[256] = "";
3674 unsigned char keys
[8];
3677 char *newm
= (vms
->newmessages
== 1) ? "message" : "messages";
3678 char *oldm
= (vms
->oldmessages
== 1) ? "message" : "messages";
3679 if (!ast_adsi_available(chan
))
3681 if (vms
->newmessages
) {
3682 snprintf(buf1
, sizeof(buf1
), "You have %d new", vms
->newmessages
);
3683 if (vms
->oldmessages
) {
3684 strncat(buf1
, " and", sizeof(buf1
) - strlen(buf1
) - 1);
3685 snprintf(buf2
, sizeof(buf2
), "%d old %s.", vms
->oldmessages
, oldm
);
3687 snprintf(buf2
, sizeof(buf2
), "%s.", newm
);
3689 } else if (vms
->oldmessages
) {
3690 snprintf(buf1
, sizeof(buf1
), "You have %d old", vms
->oldmessages
);
3691 snprintf(buf2
, sizeof(buf2
), "%s.", oldm
);
3693 strcpy(buf1
, "You have no messages.");
3697 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_LEFT
, 0, buf1
, "");
3698 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_LEFT
, 0, buf2
, "");
3699 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3702 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ x
);
3706 /* Don't let them listen if there are none */
3707 if (vms
->lastmsg
< 0)
3709 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3711 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3713 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3716 static void adsi_status2(struct ast_channel
*chan
, struct vm_state
*vms
)
3718 unsigned char buf
[256] = "";
3719 char buf1
[256] = "", buf2
[256] = "";
3721 unsigned char keys
[8];
3724 char *mess
= (vms
->lastmsg
== 0) ? "message" : "messages";
3726 if (!ast_adsi_available(chan
))
3729 /* Original command keys */
3731 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ x
);
3736 if ((vms
->lastmsg
+ 1) < 1)
3739 snprintf(buf1
, sizeof(buf1
), "%s%s has", vms
->curbox
,
3740 strcasecmp(vms
->curbox
, "INBOX") ? " folder" : "");
3742 if (vms
->lastmsg
+ 1)
3743 snprintf(buf2
, sizeof(buf2
), "%d %s.", vms
->lastmsg
+ 1, mess
);
3745 strcpy(buf2
, "no messages.");
3746 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_LEFT
, 0, buf1
, "");
3747 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_LEFT
, 0, buf2
, "");
3748 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_LEFT
, 0, "", "");
3749 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3750 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3752 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3754 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3759 static void adsi_clear(struct ast_channel *chan)
3763 if (!ast_adsi_available(chan))
3765 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3766 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3768 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3772 static void adsi_goodbye(struct ast_channel
*chan
)
3774 unsigned char buf
[256];
3777 if (!ast_adsi_available(chan
))
3779 bytes
+= adsi_logo(buf
+ bytes
);
3780 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_LEFT
, 0, " ", "");
3781 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Goodbye", "");
3782 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3783 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3785 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3788 /*--- get_folder: Folder menu ---*/
3789 /* Plays "press 1 for INBOX messages" etc
3790 Should possibly be internationalized
3792 static int get_folder(struct ast_channel
*chan
, int start
)
3797 d
= ast_play_and_wait(chan
, "vm-press"); /* "Press" */
3800 for (x
= start
; x
< 5; x
++) { /* For all folders */
3801 if ((d
= ast_say_number(chan
, x
, AST_DIGIT_ANY
, chan
->language
, (char *) NULL
)))
3803 d
= ast_play_and_wait(chan
, "vm-for"); /* "for" */
3806 snprintf(fn
, sizeof(fn
), "vm-%s", mbox(x
)); /* Folder name */
3807 d
= vm_play_folder_name(chan
, fn
);
3810 d
= ast_waitfordigit(chan
, 500);
3814 d
= ast_play_and_wait(chan
, "vm-tocancel"); /* "or pound to cancel" */
3817 d
= ast_waitfordigit(chan
, 4000);
3821 static int get_folder2(struct ast_channel
*chan
, char *fn
, int start
)
3824 res
= ast_play_and_wait(chan
, fn
); /* Folder name */
3825 while (((res
< '0') || (res
> '9')) &&
3826 (res
!= '#') && (res
>= 0)) {
3827 res
= get_folder(chan
, 0);
3832 static int vm_forwardoptions(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *curdir
, int curmsg
, char *vmfmts
,
3833 char *context
, signed char record_gain
, long *duration
, struct vm_state
*vms
)
3836 int retries
= 0, prepend_duration
= 0, already_recorded
= 0;
3837 signed char zero_gain
= 0;
3838 struct ast_config
*msg_cfg
;
3839 const char *duration_str
;
3840 char msgfile
[PATH_MAX
], backup
[PATH_MAX
];
3841 char textfile
[PATH_MAX
];
3843 /* Must always populate duration correctly */
3844 make_file(msgfile
, sizeof(msgfile
), curdir
, curmsg
);
3845 strcpy(textfile
, msgfile
);
3846 strcpy(backup
, msgfile
);
3847 strncat(textfile
, ".txt", sizeof(textfile
) - strlen(textfile
) - 1);
3848 strncat(backup
, "-bak", sizeof(backup
) - strlen(backup
) - 1);
3850 if (!(msg_cfg
= ast_config_load(textfile
))) {
3855 if ((duration_str
= ast_variable_retrieve(msg_cfg
, "message", "duration")))
3856 *duration
= atoi(duration_str
);
3858 while ((cmd
>= 0) && (cmd
!= 't') && (cmd
!= '*')) {
3863 /* prepend a message to the current message, update the metadata and return */
3865 prepend_duration
= 0;
3867 /* if we can't read the message metadata, stop now */
3873 /* Back up the original file, so we can retry the prepend */
3874 if (already_recorded
)
3875 ast_filecopy(backup
, msgfile
, NULL
);
3877 ast_filecopy(msgfile
, backup
, NULL
);
3878 already_recorded
= 1;
3881 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &record_gain
, sizeof(record_gain
), 0);
3883 cmd
= ast_play_and_prepend(chan
, NULL
, msgfile
, 0, vmfmts
, &prepend_duration
, 1, silencethreshold
, maxsilence
);
3885 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &zero_gain
, sizeof(zero_gain
), 0);
3887 if (prepend_duration
) {
3888 struct ast_category
*msg_cat
;
3889 /* need enough space for a maximum-length message duration */
3890 char duration_str
[12];
3892 prepend_duration
+= *duration
;
3893 msg_cat
= ast_category_get(msg_cfg
, "message");
3894 snprintf(duration_str
, 11, "%d", prepend_duration
);
3895 if (!ast_variable_update(msg_cat
, "duration", duration_str
, NULL
, 0)) {
3896 config_text_file_save(textfile
, msg_cfg
, "app_voicemail");
3897 STORE(curdir
, vmu
->mailbox
, context
, curmsg
, chan
, vmu
, vmfmts
, prepend_duration
, vms
);
3910 cmd
= ast_play_and_wait(chan
,"vm-forwardoptions");
3911 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
3913 cmd
= ast_play_and_wait(chan
,"vm-starmain");
3914 /* "press star to return to the main menu" */
3916 cmd
= ast_waitfordigit(chan
,6000);
3924 ast_config_destroy(msg_cfg
);
3925 if (already_recorded
)
3926 ast_filedelete(backup
, NULL
);
3927 if (prepend_duration
)
3928 *duration
= prepend_duration
;
3930 if (cmd
== 't' || cmd
== 'S')
3935 static int notify_new_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, int msgnum
, long duration
, char *fmt
, char *cidnum
, char *cidname
)
3937 char todir
[PATH_MAX
], fn
[PATH_MAX
], ext_context
[PATH_MAX
], *stringp
;
3938 int newmsgs
= 0, oldmsgs
= 0;
3939 const char *category
= pbx_builtin_getvar_helper(chan
, "VM_CATEGORY");
3941 make_dir(todir
, sizeof(todir
), vmu
->context
, vmu
->mailbox
, "INBOX");
3942 make_file(fn
, sizeof(fn
), todir
, msgnum
);
3943 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", vmu
->mailbox
, vmu
->context
);
3945 if (!ast_strlen_zero(vmu
->attachfmt
)) {
3946 if (strstr(fmt
, vmu
->attachfmt
)) {
3947 fmt
= vmu
->attachfmt
;
3949 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
);
3953 /* Attach only the first format */
3954 fmt
= ast_strdupa(fmt
);
3956 strsep(&stringp
, "|");
3958 if (!ast_strlen_zero(vmu
->email
)) {
3959 int attach_user_voicemail
= ast_test_flag((&globalflags
), VM_ATTACH
);
3960 char *myserveremail
= serveremail
;
3961 attach_user_voicemail
= ast_test_flag(vmu
, VM_ATTACH
);
3962 if (!ast_strlen_zero(vmu
->serveremail
))
3963 myserveremail
= vmu
->serveremail
;
3965 if (attach_user_voicemail
)
3966 RETRIEVE(todir
, msgnum
);
3968 /*XXX possible imap issue, should category be NULL XXX*/
3969 sendmail(myserveremail
, vmu
, msgnum
, vmu
->context
, vmu
->mailbox
, cidnum
, cidname
, fn
, fmt
, duration
, attach_user_voicemail
, chan
, category
);
3971 if (attach_user_voicemail
)
3972 DISPOSE(todir
, msgnum
);
3975 if (!ast_strlen_zero(vmu
->pager
)) {
3976 char *myserveremail
= serveremail
;
3977 if (!ast_strlen_zero(vmu
->serveremail
))
3978 myserveremail
= vmu
->serveremail
;
3979 sendpage(myserveremail
, vmu
->pager
, msgnum
, vmu
->context
, vmu
->mailbox
, cidnum
, cidname
, duration
, vmu
, category
);
3982 if (ast_test_flag(vmu
, VM_DELETE
)) {
3983 DELETE(todir
, msgnum
, fn
);
3987 DELETE(todir
, msgnum
, fn
);
3989 /* Leave voicemail for someone */
3990 if (ast_app_has_voicemail(ext_context
, NULL
)) {
3991 ast_app_inboxcount(ext_context
, &newmsgs
, &oldmsgs
);
3993 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
);
3994 run_externnotify(vmu
->context
, vmu
->mailbox
);
3998 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
)
4002 char *header_content
;
4006 struct vm_state
*dstvms
;
4008 char username
[70]="";
4009 int res
= 0, cmd
= 0;
4010 struct ast_vm_user
*receiver
= NULL
, *vmtmp
;
4011 AST_LIST_HEAD_NOLOCK_STATIC(extensions
, ast_vm_user
);
4014 int saved_messages
= 0, found
= 0;
4015 int valid_extensions
= 0;
4019 if (vms
== NULL
) return -1;
4021 curmsg
= vms
->curmsg
;
4023 while (!res
&& !valid_extensions
) {
4024 int use_directory
= 0;
4025 if (ast_test_flag((&globalflags
), VM_DIRECFORWARD
)) {
4029 while ((cmd
>= 0) && !done
){
4046 /* Press 1 to enter an extension press 2 to use the directory */
4047 cmd
= ast_play_and_wait(chan
,"vm-forward");
4049 cmd
= ast_waitfordigit(chan
,3000);
4060 if (cmd
< 0 || cmd
== 't')
4064 if (use_directory
) {
4065 /* use app_directory */
4067 char old_context
[sizeof(chan
->context
)];
4068 char old_exten
[sizeof(chan
->exten
)];
4070 struct ast_app
* app
;
4073 app
= pbx_findapp("Directory");
4075 char vmcontext
[256];
4076 /* make backup copies */
4077 memcpy(old_context
, chan
->context
, sizeof(chan
->context
));
4078 memcpy(old_exten
, chan
->exten
, sizeof(chan
->exten
));
4079 old_priority
= chan
->priority
;
4081 /* call the the Directory, changes the channel */
4082 snprintf(vmcontext
, sizeof(vmcontext
), "%s||v", context
? context
: "default");
4083 res
= pbx_exec(chan
, app
, vmcontext
);
4085 ast_copy_string(username
, chan
->exten
, sizeof(username
));
4087 /* restore the old context, exten, and priority */
4088 memcpy(chan
->context
, old_context
, sizeof(chan
->context
));
4089 memcpy(chan
->exten
, old_exten
, sizeof(chan
->exten
));
4090 chan
->priority
= old_priority
;
4093 ast_log(LOG_WARNING
, "Could not find the Directory application, disabling directory_forward\n");
4094 ast_clear_flag((&globalflags
), VM_DIRECFORWARD
);
4097 /* Ask for an extension */
4098 res
= ast_streamfile(chan
, "vm-extension", chan
->language
); /* "extension" */
4101 if ((res
= ast_readstring(chan
, username
, sizeof(username
) - 1, 2000, 10000, "#") < 0))
4105 /* start all over if no username */
4106 if (ast_strlen_zero(username
))
4109 s
= strsep(&stringp
, "*");
4110 /* start optimistic */
4111 valid_extensions
= 1;
4113 /* Don't forward to ourselves but allow leaving a message for ourselves (flag == 1). find_user is going to malloc since we have a NULL as first argument */
4114 if ((flag
== 1 || strcmp(s
,sender
->mailbox
)) && (receiver
= find_user(NULL
, context
, s
))) {
4115 AST_LIST_INSERT_HEAD(&extensions
, receiver
, list
);
4118 valid_extensions
= 0;
4121 s
= strsep(&stringp
, "*");
4123 /* break from the loop of reading the extensions */
4124 if (valid_extensions
)
4126 /* "I am sorry, that's not a valid extension. Please try again." */
4127 res
= ast_play_and_wait(chan
, "pbx-invalid");
4129 /* check if we're clear to proceed */
4130 if (AST_LIST_EMPTY(&extensions
) || !valid_extensions
)
4133 struct leave_vm_options leave_options
;
4134 char mailbox
[AST_MAX_EXTENSION
* 2 + 2];
4135 /* Make sure that context doesn't get set as a literal "(null)" (or else find_user won't find it) */
4137 snprintf(mailbox
, sizeof(mailbox
), "%s@%s", username
, context
);
4139 ast_copy_string(mailbox
, username
, sizeof(mailbox
));
4141 /* Send VoiceMail */
4142 memset(&leave_options
, 0, sizeof(leave_options
));
4143 leave_options
.record_gain
= record_gain
;
4144 cmd
= leave_voicemail(chan
, mailbox
, &leave_options
);
4146 /* Forward VoiceMail */
4148 char origmsgfile
[PATH_MAX
], msgfile
[PATH_MAX
];
4149 struct vm_state vmstmp
;
4151 memcpy(&vmstmp
, vms
, sizeof(vmstmp
));
4153 make_file(origmsgfile
, sizeof(origmsgfile
), dir
, curmsg
);
4154 create_dirpath(vmstmp
.curdir
, sizeof(vmstmp
.curdir
), sender
->context
, vmstmp
.username
, "tmp");
4155 make_file(msgfile
, sizeof(msgfile
), vmstmp
.curdir
, curmsg
);
4157 RETRIEVE(dir
, curmsg
);
4159 /* Alter a surrogate file, only */
4160 copy_plain_file(origmsgfile
, msgfile
);
4162 cmd
= vm_forwardoptions(chan
, sender
, vmstmp
.curdir
, curmsg
, vmfmts
, S_OR(context
, "default"), record_gain
, &duration
, &vmstmp
);
4164 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions
, vmtmp
, list
) {
4166 char *myserveremail
;
4167 int attach_user_voicemail
;
4168 /* Need to get message content */
4169 if (option_debug
> 2)
4170 ast_log (LOG_DEBUG
, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", vms
->curmsg
, vms
->msgArray
[vms
->curmsg
]);
4171 if (vms
->msgArray
[vms
->curmsg
] == 0) {
4172 ast_log (LOG_WARNING
,"Trying to access unknown message\n");
4176 /* This will only work for new messages... */
4177 header_content
= mail_fetchheader (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
]);
4178 /* empty string means no valid header */
4179 if (ast_strlen_zero(header_content
)) {
4180 ast_log (LOG_ERROR
,"Could not fetch header for message number %ld\n",vms
->msgArray
[vms
->curmsg
]);
4183 /* Get header info needed by sendmail */
4184 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Duration:");
4186 duration
= atoi(temp
);
4190 /* Attach only the first format */
4191 fmt
= ast_strdupa(fmt
);
4194 strsep(&stringp
, "|");
4196 ast_log (LOG_ERROR
,"audio format not set. Default to WAV\n");
4199 if (!strcasecmp(fmt
, "wav49"))
4201 if (option_debug
> 2)
4202 ast_log (LOG_DEBUG
,"**** format set to %s, vmfmts set to %s\n",fmt
,vmfmts
);
4203 /* ast_copy_string(fmt, vmfmts, sizeof(fmt));*/
4204 /* if (!ast_strlen_zero(fmt)) { */
4205 snprintf(todir
, sizeof(todir
), "%s%s/%s/tmp", VM_SPOOL_DIR
, vmtmp
->context
, vmtmp
->mailbox
);
4206 make_gsm_file(vms
->fn
, sizeof(vms
->fn
), vms
->imapuser
, todir
, vms
->curmsg
);
4207 if (option_debug
> 2)
4208 ast_log (LOG_DEBUG
,"Before mail_fetchstructure, message number is %ld, filename is:%s\n",vms
->msgArray
[vms
->curmsg
], vms
->fn
);
4209 /*mail_fetchstructure (mailstream, vmArray[0], &body); */
4210 mail_fetchstructure (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
], &body
);
4211 save_body(body
,vms
,"3","gsm");
4212 /* should not assume "fmt" here! */
4213 save_body(body
,vms
,"2",fmt
);
4215 /* get destination mailbox */
4216 dstvms
= get_vm_state_by_mailbox(vmtmp
->mailbox
,0);
4218 init_mailstream(dstvms
, 0);
4219 if (!dstvms
->mailstream
) {
4220 ast_log (LOG_ERROR
,"IMAP mailstream for %s is NULL\n",vmtmp
->mailbox
);
4222 STORE(todir
, vmtmp
->mailbox
, vmtmp
->context
, dstvms
->curmsg
, chan
, vmtmp
, fmt
, duration
, dstvms
);
4223 run_externnotify(vmtmp
->context
, vmtmp
->mailbox
);
4226 ast_log (LOG_ERROR
,"Could not find state information for mailbox %s\n",vmtmp
->mailbox
);
4229 myserveremail
= serveremail
;
4230 if (!ast_strlen_zero(vmtmp
->serveremail
))
4231 myserveremail
= vmtmp
->serveremail
;
4232 attach_user_voicemail
= ast_test_flag((&globalflags
), VM_ATTACH
);
4233 attach_user_voicemail
= ast_test_flag(vmtmp
, VM_ATTACH
);
4234 /* NULL category for IMAP storage */
4235 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
);
4237 copy_message(chan
, sender
, -1, curmsg
, duration
, vmtmp
, fmt
, vmstmp
.curdir
);
4240 AST_LIST_REMOVE_CURRENT(&extensions
, list
);
4245 AST_LIST_TRAVERSE_SAFE_END
;
4246 if (saved_messages
> 0) {
4247 /* give confirmation that the message was saved */
4248 /* commented out since we can't forward batches yet
4249 if (saved_messages == 1)
4250 res = ast_play_and_wait(chan, "vm-message");
4252 res = ast_play_and_wait(chan, "vm-messages");
4254 res = ast_play_and_wait(chan, "vm-saved"); */
4255 res
= ast_play_and_wait(chan
, "vm-msgsaved");
4259 /* Remove surrogate file */
4263 /* If anything failed above, we still have this list to free */
4264 while ((vmtmp
= AST_LIST_REMOVE_HEAD(&extensions
, list
)))
4266 return res
? res
: cmd
;
4269 static int wait_file2(struct ast_channel
*chan
, struct vm_state
*vms
, char *file
)
4272 if ((res
= ast_stream_and_wait(chan
, file
, chan
->language
, AST_DIGIT_ANY
)) < 0)
4273 ast_log(LOG_WARNING
, "Unable to play message %s\n", file
);
4277 static int wait_file(struct ast_channel
*chan
, struct vm_state
*vms
, char *file
)
4279 return ast_control_streamfile(chan
, file
, "#", "*", "1456789", "0", "2", skipms
);
4282 static int play_message_category(struct ast_channel
*chan
, const char *category
)
4286 if (!ast_strlen_zero(category
))
4287 res
= ast_play_and_wait(chan
, category
);
4290 ast_log(LOG_WARNING
, "No sound file for category '%s' was found.\n", category
);
4297 static int play_message_datetime(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, const char *origtime
, const char *filename
)
4300 struct vm_zone
*the_zone
= NULL
;
4303 if (ast_get_time_t(origtime
, &t
, 0, NULL
)) {
4304 ast_log(LOG_WARNING
, "Couldn't find origtime in %s\n", filename
);
4308 /* Does this user have a timezone specified? */
4309 if (!ast_strlen_zero(vmu
->zonetag
)) {
4310 /* Find the zone in the list */
4312 AST_LIST_LOCK(&zones
);
4313 AST_LIST_TRAVERSE(&zones
, z
, list
) {
4314 if (!strcmp(z
->name
, vmu
->zonetag
)) {
4319 AST_LIST_UNLOCK(&zones
);
4322 /* No internal variable parsing for now, so we'll comment it out for the time being */
4324 /* Set the DIFF_* variables */
4325 ast_localtime(&t
, &time_now
, NULL
);
4326 tv_now
= ast_tvnow();
4327 tnow
= tv_now
.tv_sec
;
4328 ast_localtime(&tnow
, &time_then
, NULL
);
4330 /* Day difference */
4331 if (time_now
.tm_year
== time_then
.tm_year
)
4332 snprintf(temp
,sizeof(temp
),"%d",time_now
.tm_yday
);
4334 snprintf(temp
,sizeof(temp
),"%d",(time_now
.tm_year
- time_then
.tm_year
) * 365 + (time_now
.tm_yday
- time_then
.tm_yday
));
4335 pbx_builtin_setvar_helper(chan
, "DIFF_DAY", temp
);
4337 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
4340 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, the_zone
->msg_format
, the_zone
->timezone
);
4341 else if (!strcasecmp(chan
->language
,"pl")) /* POLISH syntax */
4342 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Q HM", NULL
);
4343 else if (!strcasecmp(chan
->language
,"se")) /* SWEDISH syntax */
4344 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' dB 'digits/at' k 'and' M", NULL
);
4345 else if (!strcasecmp(chan
->language
,"no")) /* NORWEGIAN syntax */
4346 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Q 'digits/at' HM", NULL
);
4347 else if (!strcasecmp(chan
->language
,"de")) /* GERMAN syntax */
4348 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Q 'digits/at' HM", NULL
);
4349 else if (!strcasecmp(chan
->language
,"nl")) /* DUTCH syntax */
4350 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' q 'digits/nl-om' HM", NULL
);
4351 else if (!strcasecmp(chan
->language
,"it")) /* ITALIAN syntax */
4352 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
);
4353 else if (!strcasecmp(chan
->language
,"gr"))
4354 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' q H 'digits/kai' M ", NULL
);
4355 else if (!strcasecmp(chan
->language
,"pt_BR"))
4356 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL
);
4358 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' q 'digits/at' IMp", NULL
);
4360 pbx_builtin_setvar_helper(chan
, "DIFF_DAY", NULL
);
4367 static int play_message_callerid(struct ast_channel
*chan
, struct vm_state
*vms
, char *cid
, const char *context
, int callback
)
4371 char *callerid
, *name
;
4372 char prefile
[PATH_MAX
] = "";
4375 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
4376 /* 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 */
4377 if ((cid
== NULL
)||(context
== NULL
))
4380 /* Strip off caller ID number from name */
4381 if (option_debug
> 2)
4382 ast_log(LOG_DEBUG
, "VM-CID: composite caller ID received: %s, context: %s\n", cid
, context
);
4383 ast_callerid_parse(cid
, &name
, &callerid
);
4384 if ((!ast_strlen_zero(callerid
)) && strcmp(callerid
, "Unknown")) {
4385 /* Check for internal contexts and only */
4386 /* say extension when the call didn't come from an internal context in the list */
4387 for (i
= 0 ; i
< MAX_NUM_CID_CONTEXTS
; i
++){
4388 if (option_debug
> 2)
4389 ast_log(LOG_DEBUG
, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts
[i
]);
4390 if ((strcmp(cidinternalcontexts
[i
], context
) == 0))
4393 if (i
!= MAX_NUM_CID_CONTEXTS
){ /* internal context? */
4395 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/greet", VM_SPOOL_DIR
, context
, callerid
);
4396 if (!ast_strlen_zero(prefile
)) {
4397 /* See if we can find a recorded name for this person instead of their extension number */
4398 if (ast_fileexists(prefile
, NULL
, NULL
) > 0) {
4399 if (option_verbose
> 2)
4400 ast_verbose(VERBOSE_PREFIX_3
"Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid
);
4402 res
= wait_file2(chan
, vms
, "vm-from");
4403 res
= ast_stream_and_wait(chan
, prefile
, chan
->language
, "");
4405 if (option_verbose
> 2)
4406 ast_verbose(VERBOSE_PREFIX_3
"Playing envelope info: message from '%s'\n", callerid
);
4407 /* BB: Say "from extension" as one saying to sound smoother */
4409 res
= wait_file2(chan
, vms
, "vm-from-extension");
4410 res
= ast_say_digit_str(chan
, callerid
, "", chan
->language
);
4417 if (option_debug
> 2)
4418 ast_log(LOG_DEBUG
, "VM-CID: Numeric caller id: (%s)\n",callerid
);
4419 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
4421 res
= wait_file2(chan
, vms
, "vm-from-phonenumber");
4422 res
= ast_say_digit_str(chan
, callerid
, AST_DIGIT_ANY
, chan
->language
);
4425 /* Number unknown */
4427 ast_log(LOG_DEBUG
, "VM-CID: From an unknown number\n");
4428 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
4429 res
= wait_file2(chan
, vms
, "vm-unknown-caller");
4434 static int play_message_duration(struct ast_channel
*chan
, struct vm_state
*vms
, const char *duration
, int minduration
)
4439 /* Verify that we have a duration for the message */
4440 if (duration
== NULL
)
4443 /* Convert from seconds to minutes */
4444 durations
=atoi(duration
);
4445 durationm
=(durations
/ 60);
4447 if (option_debug
> 2)
4448 ast_log(LOG_DEBUG
, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations
, durationm
);
4450 if ((!res
) && (durationm
>= minduration
)) {
4451 res
= wait_file2(chan
, vms
, "vm-duration");
4454 if (!strcasecmp(chan
->language
, "pl")) {
4455 div_t num
= div(durationm
, 10);
4457 if (durationm
== 1) {
4458 res
= ast_play_and_wait(chan
, "digits/1z");
4459 res
= res
? res
: ast_play_and_wait(chan
, "vm-minute-ta");
4460 } else if (num
.rem
> 1 && num
.rem
< 5 && num
.quot
!= 1) {
4463 res
= ast_play_and_wait(chan
, "digits/2-ie");
4465 res
= say_and_wait(chan
, durationm
- 2 , chan
->language
);
4466 res
= res
? res
: ast_play_and_wait(chan
, "digits/2-ie");
4469 res
= say_and_wait(chan
, durationm
, chan
->language
);
4471 res
= res
? res
: ast_play_and_wait(chan
, "vm-minute-ty");
4473 res
= say_and_wait(chan
, durationm
, chan
->language
);
4474 res
= res
? res
: ast_play_and_wait(chan
, "vm-minute-t");
4476 /* DEFAULT syntax */
4478 res
= ast_say_number(chan
, durationm
, AST_DIGIT_ANY
, chan
->language
, NULL
);
4479 res
= wait_file2(chan
, vms
, "vm-minutes");
4486 static int play_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
)
4489 char *header_content
;
4495 char todir
[PATH_MAX
];
4497 char *attachedfilefmt
;
4501 if (option_debug
> 2)
4502 ast_log (LOG_DEBUG
,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms
->curmsg
, vms
->msgArray
[vms
->curmsg
]);
4503 if (vms
->msgArray
[vms
->curmsg
] == 0) {
4504 ast_log (LOG_WARNING
,"Trying to access unknown message\n");
4508 /* This will only work for new messages... */
4509 header_content
= mail_fetchheader (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
]);
4510 /* empty string means no valid header */
4511 if (ast_strlen_zero(header_content
)) {
4512 ast_log (LOG_ERROR
,"Could not fetch header for message number %ld\n",vms
->msgArray
[vms
->curmsg
]);
4515 snprintf(todir
, sizeof(todir
), "%s%s/%s/tmp", VM_SPOOL_DIR
, vmu
->context
, vmu
->mailbox
);
4516 make_gsm_file(vms
->fn
, sizeof(vms
->fn
), vms
->imapuser
, todir
, vms
->curmsg
);
4518 mail_fetchstructure (vms
->mailstream
,vms
->msgArray
[vms
->curmsg
],&body
);
4520 /* We have the body, now we extract the file name of the first attachment. */
4521 if (body
->nested
.part
&& body
->nested
.part
->next
&& body
->nested
.part
->next
->body
.parameter
->value
) {
4522 attachedfilefmt
= ast_strdupa(body
->nested
.part
->next
->body
.parameter
->value
);
4524 ast_log(LOG_ERROR
, "There is no file attached to this IMAP message.\n");
4528 /* Find the format of the attached file */
4530 strsep(&attachedfilefmt
, ".");
4531 if (!attachedfilefmt
) {
4532 ast_log(LOG_ERROR
, "File format could not be obtained from IMAP message attachment\n");
4535 save_body(body
, vms
, "2", attachedfilefmt
);
4537 adsi_message(chan
, vms
);
4539 res
= wait_file2(chan
, vms
, "vm-first"); /* "First" */
4540 else if (vms
->curmsg
== vms
->lastmsg
)
4541 res
= wait_file2(chan
, vms
, "vm-last"); /* "last" */
4543 res
= wait_file2(chan
, vms
, "vm-message"); /* "message" */
4544 if (vms
->curmsg
&& (vms
->curmsg
!= vms
->lastmsg
)) {
4546 res
= ast_say_number(chan
, vms
->curmsg
+ 1, AST_DIGIT_ANY
, chan
->language
, (char *) NULL
);
4550 /* Get info from headers!! */
4551 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Caller-ID-Num:");
4554 ast_copy_string(cid
, temp
, sizeof(cid
));
4558 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Context:");
4561 ast_copy_string(context
, temp
, sizeof(context
));
4565 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Orig-time:");
4568 ast_copy_string(origtime
, temp
, sizeof(origtime
));
4572 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Duration:");
4575 ast_copy_string(duration
,temp
, sizeof(duration
));
4579 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Category:");
4582 ast_copy_string(category
,temp
, sizeof(category
));
4586 /*if (!strncasecmp("macro",context,5)) Macro names in contexts are useless for our needs */
4587 /* context = ast_variable_retrieve(msg_cfg, "message","macrocontext"); */
4591 if ((!res
) && !ast_strlen_zero(category
)) {
4592 res
= play_message_category(chan
, category
);
4595 if ((!res
) && (ast_test_flag(vmu
, VM_ENVELOPE
)) && origtime
[0] != '\0')
4596 res
= play_message_datetime(chan
, vmu
, origtime
, "IMAP_STORAGE");
4597 if ((!res
) && (ast_test_flag(vmu
, VM_SAYCID
)) && cid
[0] !='\0' && context
[0] !='\0')
4598 res
= play_message_callerid(chan
, vms
, cid
, context
, 0);
4600 if ((!res
) && (ast_test_flag(vmu
, VM_SAYDURATION
)) && duration
[0] != '\0')
4601 res
= play_message_duration(chan
, vms
, duration
, vmu
->saydurationm
);
4603 /* Allow pressing '1' to skip envelope / callerid */
4607 /*ast_config_destroy(msg_cfg);*/
4611 vms
->heard
[vms
->curmsg
] = 1;
4612 res
= wait_file(chan
, vms
, vms
->fn
);
4614 DISPOSE(vms
->curdir
, vms
->curmsg
);
4615 DELETE(0, 0, vms
->fn
);
4619 static int play_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
)
4622 char filename
[256], *cid
;
4623 const char *origtime
, *context
, *category
, *duration
;
4624 struct ast_config
*msg_cfg
;
4627 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, vms
->curmsg
);
4628 adsi_message(chan
, vms
);
4630 res
= wait_file2(chan
, vms
, "vm-first"); /* "First" */
4631 else if (vms
->curmsg
== vms
->lastmsg
)
4632 res
= wait_file2(chan
, vms
, "vm-last"); /* "last" */
4635 if (!strcasecmp(chan
->language
, "pl")) {
4636 if (vms
->curmsg
&& (vms
->curmsg
!= vms
->lastmsg
)) {
4639 ten
= (vms
->curmsg
+ 1) / 10;
4640 one
= (vms
->curmsg
+ 1) % 10;
4642 if (vms
->curmsg
< 20) {
4643 snprintf(nextmsg
, sizeof(nextmsg
), "digits/n-%d", vms
->curmsg
+ 1);
4644 res
= wait_file2(chan
, vms
, nextmsg
);
4646 snprintf(nextmsg
, sizeof(nextmsg
), "digits/n-%d", ten
* 10);
4647 res
= wait_file2(chan
, vms
, nextmsg
);
4650 snprintf(nextmsg
, sizeof(nextmsg
), "digits/n-%d", one
);
4651 res
= wait_file2(chan
, vms
, nextmsg
);
4657 res
= wait_file2(chan
, vms
, "vm-message");
4659 if (!strcasecmp(chan
->language
, "se")) /* SWEDISH syntax */
4660 res
= wait_file2(chan
, vms
, "vm-meddelandet"); /* "message" */
4661 else /* DEFAULT syntax */
4662 res
= wait_file2(chan
, vms
, "vm-message");
4663 if (vms
->curmsg
&& (vms
->curmsg
!= vms
->lastmsg
)) {
4665 res
= ast_say_number(chan
, vms
->curmsg
+ 1, AST_DIGIT_ANY
, chan
->language
, NULL
);
4670 /* Retrieve info from VM attribute file */
4671 make_file(vms
->fn2
, sizeof(vms
->fn2
), vms
->curdir
, vms
->curmsg
);
4672 snprintf(filename
, sizeof(filename
), "%s.txt", vms
->fn2
);
4673 RETRIEVE(vms
->curdir
, vms
->curmsg
);
4674 msg_cfg
= ast_config_load(filename
);
4676 ast_log(LOG_WARNING
, "No message attribute file?!! (%s)\n", filename
);
4680 if (!(origtime
= ast_variable_retrieve(msg_cfg
, "message", "origtime"))) {
4681 ast_log(LOG_WARNING
, "No origtime?!\n");
4682 DISPOSE(vms
->curdir
, vms
->curmsg
);
4683 ast_config_destroy(msg_cfg
);
4687 cid
= ast_strdupa(ast_variable_retrieve(msg_cfg
, "message", "callerid"));
4688 duration
= ast_variable_retrieve(msg_cfg
, "message", "duration");
4689 category
= ast_variable_retrieve(msg_cfg
, "message", "category");
4691 context
= ast_variable_retrieve(msg_cfg
, "message", "context");
4692 if (!strncasecmp("macro",context
,5)) /* Macro names in contexts are useless for our needs */
4693 context
= ast_variable_retrieve(msg_cfg
, "message","macrocontext");
4695 res
= play_message_category(chan
, category
);
4696 if ((!res
) && (ast_test_flag(vmu
, VM_ENVELOPE
)))
4697 res
= play_message_datetime(chan
, vmu
, origtime
, filename
);
4698 if ((!res
) && (ast_test_flag(vmu
, VM_SAYCID
)))
4699 res
= play_message_callerid(chan
, vms
, cid
, context
, 0);
4700 if ((!res
) && (ast_test_flag(vmu
, VM_SAYDURATION
)))
4701 res
= play_message_duration(chan
, vms
, duration
, vmu
->saydurationm
);
4702 /* Allow pressing '1' to skip envelope / callerid */
4705 ast_config_destroy(msg_cfg
);
4708 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, vms
->curmsg
);
4709 vms
->heard
[vms
->curmsg
] = 1;
4710 if ((res
= wait_file(chan
, vms
, vms
->fn
)) < 0) {
4711 ast_log(LOG_WARNING
, "Playback of message %s failed\n", vms
->fn
);
4715 DISPOSE(vms
->curdir
, vms
->curmsg
);
4721 static void imap_mailbox_name(char *spec
, size_t len
, struct vm_state
*vms
, int box
, int use_folder
)
4723 char tmp
[256], *t
= tmp
;
4724 size_t left
= sizeof(tmp
);
4727 ast_copy_string(vms
->curbox
, mbox(0), sizeof(vms
->curbox
));
4728 snprintf(vms
->vmbox
, sizeof(vms
->vmbox
), "vm-%s", mbox(1));
4730 ast_copy_string(vms
->curbox
, mbox(box
), sizeof(vms
->curbox
));
4731 snprintf(vms
->vmbox
, sizeof(vms
->vmbox
), "vm-%s", vms
->curbox
);
4734 /* Build up server information */
4735 ast_build_string(&t
, &left
, "{%s:%s/imap", imapserver
, imapport
);
4737 /* Add authentication user if present */
4738 if (!ast_strlen_zero(authuser
))
4739 ast_build_string(&t
, &left
, "/authuser=%s", authuser
);
4741 /* Add flags if present */
4742 if (!ast_strlen_zero(imapflags
))
4743 ast_build_string(&t
, &left
, "/%s", imapflags
);
4745 /* End with username */
4746 ast_build_string(&t
, &left
, "/user=%s}", vms
->imapuser
);
4748 if (box
== 0 || box
== 1)
4749 snprintf(spec
, len
, "%s%s", tmp
, use_folder
? imapfolder
: "INBOX");
4751 snprintf(spec
, len
, "%s%s%c%s", tmp
, imapfolder
, delimiter
, mbox(box
));
4754 static int init_mailstream(struct vm_state
*vms
, int box
)
4756 MAILSTREAM
*stream
= NIL
;
4761 ast_log (LOG_ERROR
,"vm_state is NULL!\n");
4764 if (option_debug
> 2)
4765 ast_log (LOG_DEBUG
,"vm_state user is:%s\n",vms
->imapuser
);
4766 if (vms
->mailstream
== NIL
|| !vms
->mailstream
) {
4768 ast_log (LOG_DEBUG
,"mailstream not set.\n");
4770 stream
= vms
->mailstream
;
4772 /* debug = T; user wants protocol telemetry? */
4773 debug
= NIL
; /* NO protocol telemetry? */
4775 if (delimiter
== '\0') { /* did not probe the server yet */
4777 #ifdef USE_SYSTEM_IMAP
4778 #include <imap/linkage.c>
4779 #elif defined(USE_SYSTEM_CCLIENT)
4780 #include <c-client/linkage.c>
4782 #include "linkage.c"
4784 /* Connect to INBOX first to get folders delimiter */
4785 imap_mailbox_name(tmp
, sizeof(tmp
), vms
, 0, 1);
4786 ast_mutex_lock(&vms
->lock
);
4787 stream
= mail_open (stream
, tmp
, debug
? OP_DEBUG
: NIL
);
4788 ast_mutex_unlock(&vms
->lock
);
4789 if (stream
== NIL
) {
4790 ast_log (LOG_ERROR
, "Can't connect to imap server %s\n", tmp
);
4793 get_mailbox_delimiter(stream
);
4794 /* update delimiter in imapfolder */
4795 for (cp
= imapfolder
; *cp
; cp
++)
4799 /* Now connect to the target folder */
4800 imap_mailbox_name(tmp
, sizeof(tmp
), vms
, box
, 1);
4801 if (option_debug
> 2)
4802 ast_log (LOG_DEBUG
,"Before mail_open, server: %s, box:%d\n", tmp
, box
);
4803 ast_mutex_lock(&vms
->lock
);
4804 vms
->mailstream
= mail_open (stream
, tmp
, debug
? OP_DEBUG
: NIL
);
4805 ast_mutex_unlock(&vms
->lock
);
4806 if (vms
->mailstream
== NIL
) {
4813 static int open_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
, int box
)
4819 ast_copy_string(vms
->imapuser
,vmu
->imapuser
, sizeof(vms
->imapuser
));
4820 if (option_debug
> 2)
4821 ast_log(LOG_DEBUG
,"Before init_mailstream, user is %s\n",vmu
->imapuser
);
4822 ret
= init_mailstream(vms
, box
);
4823 if (ret
!= 0 || !vms
->mailstream
) {
4824 ast_log (LOG_ERROR
,"Could not initialize mailstream\n");
4830 if (option_debug
> 2)
4831 ast_log(LOG_DEBUG
, "Mailbox name set to: %s, about to check quotas\n", mbox(box
));
4832 check_quota(vms
,(char *)mbox(box
));
4835 pgm
= mail_newsearchpgm();
4837 /* Check IMAP folder for Asterisk messages only... */
4838 hdr
= mail_newsearchheader ("X-Asterisk-VM-Extension", vmu
->mailbox
);
4843 /* if box = 0, check for new, if box = 1, check for read */
4847 } else if (box
== 1) {
4852 vms
->vmArrayIndex
= 0;
4853 if (option_debug
> 2)
4854 ast_log(LOG_DEBUG
,"Before mail_search_full, user is %s\n",vmu
->imapuser
);
4855 mail_search_full (vms
->mailstream
, NULL
, pgm
, NIL
);
4858 vms
->lastmsg
= vms
->vmArrayIndex
- 1;
4860 mail_free_searchpgm(&pgm
);
4864 static int open_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
,int box
)
4867 int count_msg
, last_msg
;
4869 ast_copy_string(vms
->curbox
, mbox(box
), sizeof(vms
->curbox
));
4871 /* Rename the member vmbox HERE so that we don't try to return before
4872 * we know what's going on.
4874 snprintf(vms
->vmbox
, sizeof(vms
->vmbox
), "vm-%s", vms
->curbox
);
4876 /* Faster to make the directory than to check if it exists. */
4877 create_dirpath(vms
->curdir
, sizeof(vms
->curdir
), vmu
->context
, vms
->username
, vms
->curbox
);
4879 count_msg
= count_messages(vmu
, vms
->curdir
);
4883 vms
->lastmsg
= count_msg
- 1;
4886 The following test is needed in case sequencing gets messed up.
4887 There appears to be more than one way to mess up sequence, so
4888 we will not try to find all of the root causes--just fix it when
4892 last_msg
= last_message_index(vmu
, vms
->curdir
);
4895 else if (vms
->lastmsg
!= last_msg
)
4897 ast_log(LOG_NOTICE
, "Resequencing Mailbox: %s\n", vms
->curdir
);
4898 res
= resequence_mailbox(vmu
, vms
->curdir
);
4907 static int close_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
)
4910 #ifndef IMAP_STORAGE
4911 int res
= 0, nummsg
;
4914 if (vms
->lastmsg
<= -1)
4918 #ifndef IMAP_STORAGE
4919 /* Get the deleted messages fixed */
4920 if (vm_lock_path(vms
->curdir
))
4921 return ERROR_LOCK_PATH
;
4923 for (x
= 0; x
< vmu
->maxmsg
; x
++) {
4924 if (!vms
->deleted
[x
] && (strcasecmp(vms
->curbox
, "INBOX") || !vms
->heard
[x
])) {
4925 /* Save this message. It's not in INBOX or hasn't been heard */
4926 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, x
);
4927 if (!EXISTS(vms
->curdir
, x
, vms
->fn
, NULL
))
4930 make_file(vms
->fn2
, sizeof(vms
->fn2
), vms
->curdir
, vms
->curmsg
);
4931 if (strcmp(vms
->fn
, vms
->fn2
)) {
4932 RENAME(vms
->curdir
, x
, vmu
->mailbox
,vmu
->context
, vms
->curdir
, vms
->curmsg
, vms
->fn
, vms
->fn2
);
4934 } else if (!strcasecmp(vms
->curbox
, "INBOX") && vms
->heard
[x
] && !vms
->deleted
[x
]) {
4935 /* Move to old folder before deleting */
4936 res
= save_to_folder(vmu
, vms
, x
, 1);
4937 if (res
== ERROR_LOCK_PATH
|| res
== ERROR_MAILBOX_FULL
) {
4938 /* If save failed do not delete the message */
4939 ast_log(LOG_WARNING
, "Save failed. Not moving message: %s.\n", res
== ERROR_LOCK_PATH
? "unable to lock path" : "destination folder full");
4940 vms
->deleted
[x
] = 0;
4947 /* Delete ALL remaining messages */
4949 for (x
= vms
->curmsg
+ 1; x
<= nummsg
; x
++) {
4950 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, x
);
4951 if (EXISTS(vms
->curdir
, x
, vms
->fn
, NULL
))
4952 DELETE(vms
->curdir
, x
, vms
->fn
);
4954 ast_unlock_path(vms
->curdir
);
4957 for (x
=0;x
< vmu
->maxmsg
;x
++) {
4958 if (vms
->deleted
[x
]) {
4959 if (option_debug
> 2)
4960 ast_log(LOG_DEBUG
,"IMAP delete of %d\n",x
);
4961 IMAP_DELETE(vms
->curdir
, x
, vms
->fn
, vms
);
4969 memset(vms
->deleted
, 0, vmu
->maxmsg
* sizeof(int));
4971 memset(vms
->heard
, 0, vmu
->maxmsg
* sizeof(int));
4976 /* In Greek even though we CAN use a syntax like "friends messages"
4977 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
4978 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
4979 * syntax for the above three categories which is more elegant.
4982 static int vm_play_folder_name_gr(struct ast_channel
*chan
, char *mbox
)
4987 buf
= alloca(strlen(mbox
)+2);
4991 if (!strcasecmp(mbox
, "vm-INBOX") || !strcasecmp(mbox
, "vm-Old")){
4992 cmd
= ast_play_and_wait(chan
, buf
); /* "NEA / PALIA" */
4993 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages"); /* "messages" -> "MYNHMATA" */
4995 cmd
= ast_play_and_wait(chan
, "vm-messages"); /* "messages" -> "MYNHMATA" */
4996 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
5000 static int vm_play_folder_name_pl(struct ast_channel
*chan
, char *mbox
)
5004 if (!strcasecmp(mbox
, "vm-INBOX") || !strcasecmp(mbox
, "vm-Old")) {
5005 if (!strcasecmp(mbox
, "vm-INBOX"))
5006 cmd
= ast_play_and_wait(chan
, "vm-new-e");
5008 cmd
= ast_play_and_wait(chan
, "vm-old-e");
5009 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages");
5011 cmd
= ast_play_and_wait(chan
, "vm-messages");
5012 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
);
5016 static int vm_play_folder_name_ua(struct ast_channel
*chan
, char *mbox
)
5020 if (!strcasecmp(mbox
, "vm-Family") || !strcasecmp(mbox
, "vm-Friends") || !strcasecmp(mbox
, "vm-Work")){
5021 cmd
= ast_play_and_wait(chan
, "vm-messages");
5022 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
);
5024 cmd
= ast_play_and_wait(chan
, mbox
);
5025 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages");
5029 static int vm_play_folder_name(struct ast_channel
*chan
, char *mbox
)
5033 if (!strcasecmp(chan
->language
, "it") || !strcasecmp(chan
->language
, "es") || !strcasecmp(chan
->language
, "pt") || !strcasecmp(chan
->language
, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
5034 cmd
= ast_play_and_wait(chan
, "vm-messages"); /* "messages */
5035 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
);
5036 } else if (!strcasecmp(chan
->language
, "gr")){
5037 return vm_play_folder_name_gr(chan
, mbox
);
5038 } else if (!strcasecmp(chan
->language
, "pl")){
5039 return vm_play_folder_name_pl(chan
, mbox
);
5040 } else if (!strcasecmp(chan
->language
, "ua")){ /* Ukrainian syntax */
5041 return vm_play_folder_name_ua(chan
, mbox
);
5042 } else { /* Default English */
5043 cmd
= ast_play_and_wait(chan
, mbox
);
5044 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages"); /* "messages */
5049 In greek the plural for old/new is
5050 different so we need the following files
5051 We also need vm-denExeteMynhmata because
5052 this syntax is different.
5054 -> vm-Olds.wav : "Palia"
5055 -> vm-INBOXs.wav : "Nea"
5056 -> vm-denExeteMynhmata : "den exete mynhmata"
5060 static int vm_intro_gr(struct ast_channel
*chan
, struct vm_state
*vms
)
5064 if (vms
->newmessages
) {
5065 res
= ast_play_and_wait(chan
, "vm-youhave");
5067 res
= ast_say_number(chan
, vms
->newmessages
, AST_DIGIT_ANY
, chan
->language
, NULL
);
5069 if ((vms
->newmessages
== 1)) {
5070 res
= ast_play_and_wait(chan
, "vm-INBOX");
5072 res
= ast_play_and_wait(chan
, "vm-message");
5074 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5076 res
= ast_play_and_wait(chan
, "vm-messages");
5079 } else if (vms
->oldmessages
){
5080 res
= ast_play_and_wait(chan
, "vm-youhave");
5082 res
= ast_say_number(chan
, vms
->oldmessages
, AST_DIGIT_ANY
, chan
->language
, NULL
);
5083 if ((vms
->oldmessages
== 1)){
5084 res
= ast_play_and_wait(chan
, "vm-Old");
5086 res
= ast_play_and_wait(chan
, "vm-message");
5088 res
= ast_play_and_wait(chan
, "vm-Olds");
5090 res
= ast_play_and_wait(chan
, "vm-messages");
5092 } else if (!vms
->oldmessages
&& !vms
->newmessages
)
5093 res
= ast_play_and_wait(chan
, "vm-denExeteMynhmata");
5097 /* Default English syntax */
5098 static int vm_intro_en(struct ast_channel
*chan
, struct vm_state
*vms
)
5102 /* Introduce messages they have */
5103 res
= ast_play_and_wait(chan
, "vm-youhave");
5105 if (vms
->newmessages
) {
5106 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5108 res
= ast_play_and_wait(chan
, "vm-INBOX");
5109 if (vms
->oldmessages
&& !res
)
5110 res
= ast_play_and_wait(chan
, "vm-and");
5112 if ((vms
->newmessages
== 1))
5113 res
= ast_play_and_wait(chan
, "vm-message");
5115 res
= ast_play_and_wait(chan
, "vm-messages");
5119 if (!res
&& vms
->oldmessages
) {
5120 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5122 res
= ast_play_and_wait(chan
, "vm-Old");
5124 if (vms
->oldmessages
== 1)
5125 res
= ast_play_and_wait(chan
, "vm-message");
5127 res
= ast_play_and_wait(chan
, "vm-messages");
5131 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5132 res
= ast_play_and_wait(chan
, "vm-no");
5134 res
= ast_play_and_wait(chan
, "vm-messages");
5141 /* ITALIAN syntax */
5142 static int vm_intro_it(struct ast_channel
*chan
, struct vm_state
*vms
)
5144 /* Introduce messages they have */
5146 if (!vms
->oldmessages
&& !vms
->newmessages
)
5147 res
= ast_play_and_wait(chan
, "vm-no") ||
5148 ast_play_and_wait(chan
, "vm-message");
5150 res
= ast_play_and_wait(chan
, "vm-youhave");
5151 if (!res
&& vms
->newmessages
) {
5152 res
= (vms
->newmessages
== 1) ?
5153 ast_play_and_wait(chan
, "digits/un") ||
5154 ast_play_and_wait(chan
, "vm-nuovo") ||
5155 ast_play_and_wait(chan
, "vm-message") :
5156 /* 2 or more new messages */
5157 say_and_wait(chan
, vms
->newmessages
, chan
->language
) ||
5158 ast_play_and_wait(chan
, "vm-nuovi") ||
5159 ast_play_and_wait(chan
, "vm-messages");
5160 if (!res
&& vms
->oldmessages
)
5161 res
= ast_play_and_wait(chan
, "vm-and");
5163 if (!res
&& vms
->oldmessages
) {
5164 res
= (vms
->oldmessages
== 1) ?
5165 ast_play_and_wait(chan
, "digits/un") ||
5166 ast_play_and_wait(chan
, "vm-vecchio") ||
5167 ast_play_and_wait(chan
, "vm-message") :
5168 /* 2 or more old messages */
5169 say_and_wait(chan
, vms
->oldmessages
, chan
->language
) ||
5170 ast_play_and_wait(chan
, "vm-vecchi") ||
5171 ast_play_and_wait(chan
, "vm-messages");
5173 return res
? -1 : 0;
5177 static int vm_intro_pl(struct ast_channel
*chan
, struct vm_state
*vms
)
5179 /* Introduce messages they have */
5183 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5184 res
= ast_play_and_wait(chan
, "vm-no");
5185 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5188 res
= ast_play_and_wait(chan
, "vm-youhave");
5191 if (vms
->newmessages
) {
5192 num
= div(vms
->newmessages
, 10);
5193 if (vms
->newmessages
== 1) {
5194 res
= ast_play_and_wait(chan
, "digits/1-a");
5195 res
= res
? res
: ast_play_and_wait(chan
, "vm-new-a");
5196 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5197 } else if (num
.rem
> 1 && num
.rem
< 5 && num
.quot
!= 1) {
5200 res
= ast_play_and_wait(chan
, "digits/2-ie");
5202 res
= say_and_wait(chan
, vms
->newmessages
- 2 , chan
->language
);
5203 res
= res
? res
: ast_play_and_wait(chan
, "digits/2-ie");
5206 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5208 res
= res
? res
: ast_play_and_wait(chan
, "vm-new-e");
5209 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5211 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5212 res
= res
? res
: ast_play_and_wait(chan
, "vm-new-ych");
5213 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5215 if (!res
&& vms
->oldmessages
)
5216 res
= ast_play_and_wait(chan
, "vm-and");
5218 if (!res
&& vms
->oldmessages
) {
5219 num
= div(vms
->oldmessages
, 10);
5220 if (vms
->oldmessages
== 1) {
5221 res
= ast_play_and_wait(chan
, "digits/1-a");
5222 res
= res
? res
: ast_play_and_wait(chan
, "vm-old-a");
5223 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5224 } else if (num
.rem
> 1 && num
.rem
< 5 && num
.quot
!= 1) {
5227 res
= ast_play_and_wait(chan
, "digits/2-ie");
5229 res
= say_and_wait(chan
, vms
->oldmessages
- 2 , chan
->language
);
5230 res
= res
? res
: ast_play_and_wait(chan
, "digits/2-ie");
5233 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5235 res
= res
? res
: ast_play_and_wait(chan
, "vm-old-e");
5236 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5238 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5239 res
= res
? res
: ast_play_and_wait(chan
, "vm-old-ych");
5240 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5247 /* SWEDISH syntax */
5248 static int vm_intro_se(struct ast_channel
*chan
, struct vm_state
*vms
)
5250 /* Introduce messages they have */
5253 res
= ast_play_and_wait(chan
, "vm-youhave");
5257 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5258 res
= ast_play_and_wait(chan
, "vm-no");
5259 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5263 if (vms
->newmessages
) {
5264 if ((vms
->newmessages
== 1)) {
5265 res
= ast_play_and_wait(chan
, "digits/ett");
5266 res
= res
? res
: ast_play_and_wait(chan
, "vm-nytt");
5267 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5269 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5270 res
= res
? res
: ast_play_and_wait(chan
, "vm-nya");
5271 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5273 if (!res
&& vms
->oldmessages
)
5274 res
= ast_play_and_wait(chan
, "vm-and");
5276 if (!res
&& vms
->oldmessages
) {
5277 if (vms
->oldmessages
== 1) {
5278 res
= ast_play_and_wait(chan
, "digits/ett");
5279 res
= res
? res
: ast_play_and_wait(chan
, "vm-gammalt");
5280 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5282 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5283 res
= res
? res
: ast_play_and_wait(chan
, "vm-gamla");
5284 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5291 /* NORWEGIAN syntax */
5292 static int vm_intro_no(struct ast_channel
*chan
,struct vm_state
*vms
)
5294 /* Introduce messages they have */
5297 res
= ast_play_and_wait(chan
, "vm-youhave");
5301 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5302 res
= ast_play_and_wait(chan
, "vm-no");
5303 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5307 if (vms
->newmessages
) {
5308 if ((vms
->newmessages
== 1)) {
5309 res
= ast_play_and_wait(chan
, "digits/1");
5310 res
= res
? res
: ast_play_and_wait(chan
, "vm-ny");
5311 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5313 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5314 res
= res
? res
: ast_play_and_wait(chan
, "vm-nye");
5315 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5317 if (!res
&& vms
->oldmessages
)
5318 res
= ast_play_and_wait(chan
, "vm-and");
5320 if (!res
&& vms
->oldmessages
) {
5321 if (vms
->oldmessages
== 1) {
5322 res
= ast_play_and_wait(chan
, "digits/1");
5323 res
= res
? res
: ast_play_and_wait(chan
, "vm-gamel");
5324 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5326 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5327 res
= res
? res
: ast_play_and_wait(chan
, "vm-gamle");
5328 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5336 static int vm_intro_de(struct ast_channel
*chan
,struct vm_state
*vms
)
5338 /* Introduce messages they have */
5340 res
= ast_play_and_wait(chan
, "vm-youhave");
5342 if (vms
->newmessages
) {
5343 if ((vms
->newmessages
== 1))
5344 res
= ast_play_and_wait(chan
, "digits/1F");
5346 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5348 res
= ast_play_and_wait(chan
, "vm-INBOX");
5349 if (vms
->oldmessages
&& !res
)
5350 res
= ast_play_and_wait(chan
, "vm-and");
5352 if ((vms
->newmessages
== 1))
5353 res
= ast_play_and_wait(chan
, "vm-message");
5355 res
= ast_play_and_wait(chan
, "vm-messages");
5359 if (!res
&& vms
->oldmessages
) {
5360 if (vms
->oldmessages
== 1)
5361 res
= ast_play_and_wait(chan
, "digits/1F");
5363 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5365 res
= ast_play_and_wait(chan
, "vm-Old");
5367 if (vms
->oldmessages
== 1)
5368 res
= ast_play_and_wait(chan
, "vm-message");
5370 res
= ast_play_and_wait(chan
, "vm-messages");
5374 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5375 res
= ast_play_and_wait(chan
, "vm-no");
5377 res
= ast_play_and_wait(chan
, "vm-messages");
5384 /* SPANISH syntax */
5385 static int vm_intro_es(struct ast_channel
*chan
,struct vm_state
*vms
)
5387 /* Introduce messages they have */
5389 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5390 res
= ast_play_and_wait(chan
, "vm-youhaveno");
5392 res
= ast_play_and_wait(chan
, "vm-messages");
5394 res
= ast_play_and_wait(chan
, "vm-youhave");
5397 if (vms
->newmessages
) {
5399 if ((vms
->newmessages
== 1)) {
5400 res
= ast_play_and_wait(chan
, "digits/1M");
5402 res
= ast_play_and_wait(chan
, "vm-message");
5404 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5406 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5408 res
= ast_play_and_wait(chan
, "vm-messages");
5410 res
= ast_play_and_wait(chan
, "vm-INBOX");
5413 if (vms
->oldmessages
&& !res
)
5414 res
= ast_play_and_wait(chan
, "vm-and");
5416 if (vms
->oldmessages
) {
5418 if (vms
->oldmessages
== 1) {
5419 res
= ast_play_and_wait(chan
, "digits/1M");
5421 res
= ast_play_and_wait(chan
, "vm-message");
5423 res
= ast_play_and_wait(chan
, "vm-Olds");
5425 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5427 res
= ast_play_and_wait(chan
, "vm-messages");
5429 res
= ast_play_and_wait(chan
, "vm-Old");
5437 /* BRAZILIAN PORTUGUESE syntax */
5438 static int vm_intro_pt_BR(struct ast_channel
*chan
,struct vm_state
*vms
) {
5439 /* Introduce messages they have */
5441 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5442 res
= ast_play_and_wait(chan
, "vm-nomessages");
5446 res
= ast_play_and_wait(chan
, "vm-youhave");
5448 if (vms
->newmessages
) {
5450 res
= ast_say_number(chan
, vms
->newmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
5451 if ((vms
->newmessages
== 1)) {
5453 res
= ast_play_and_wait(chan
, "vm-message");
5455 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5459 res
= ast_play_and_wait(chan
, "vm-messages");
5461 res
= ast_play_and_wait(chan
, "vm-INBOX");
5463 if (vms
->oldmessages
&& !res
)
5464 res
= ast_play_and_wait(chan
, "vm-and");
5466 if (vms
->oldmessages
) {
5468 res
= ast_say_number(chan
, vms
->oldmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
5469 if (vms
->oldmessages
== 1) {
5471 res
= ast_play_and_wait(chan
, "vm-message");
5473 res
= ast_play_and_wait(chan
, "vm-Olds");
5477 res
= ast_play_and_wait(chan
, "vm-messages");
5479 res
= ast_play_and_wait(chan
, "vm-Old");
5486 static int vm_intro_fr(struct ast_channel
*chan
,struct vm_state
*vms
)
5488 /* Introduce messages they have */
5490 res
= ast_play_and_wait(chan
, "vm-youhave");
5492 if (vms
->newmessages
) {
5493 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5495 res
= ast_play_and_wait(chan
, "vm-INBOX");
5496 if (vms
->oldmessages
&& !res
)
5497 res
= ast_play_and_wait(chan
, "vm-and");
5499 if ((vms
->newmessages
== 1))
5500 res
= ast_play_and_wait(chan
, "vm-message");
5502 res
= ast_play_and_wait(chan
, "vm-messages");
5506 if (!res
&& vms
->oldmessages
) {
5507 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5509 res
= ast_play_and_wait(chan
, "vm-Old");
5511 if (vms
->oldmessages
== 1)
5512 res
= ast_play_and_wait(chan
, "vm-message");
5514 res
= ast_play_and_wait(chan
, "vm-messages");
5518 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5519 res
= ast_play_and_wait(chan
, "vm-no");
5521 res
= ast_play_and_wait(chan
, "vm-messages");
5529 static int vm_intro_nl(struct ast_channel
*chan
,struct vm_state
*vms
)
5531 /* Introduce messages they have */
5533 res
= ast_play_and_wait(chan
, "vm-youhave");
5535 if (vms
->newmessages
) {
5536 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5538 if (vms
->newmessages
== 1)
5539 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5541 res
= ast_play_and_wait(chan
, "vm-INBOX");
5543 if (vms
->oldmessages
&& !res
)
5544 res
= ast_play_and_wait(chan
, "vm-and");
5546 if ((vms
->newmessages
== 1))
5547 res
= ast_play_and_wait(chan
, "vm-message");
5549 res
= ast_play_and_wait(chan
, "vm-messages");
5553 if (!res
&& vms
->oldmessages
) {
5554 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5556 if (vms
->oldmessages
== 1)
5557 res
= ast_play_and_wait(chan
, "vm-Olds");
5559 res
= ast_play_and_wait(chan
, "vm-Old");
5562 if (vms
->oldmessages
== 1)
5563 res
= ast_play_and_wait(chan
, "vm-message");
5565 res
= ast_play_and_wait(chan
, "vm-messages");
5569 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5570 res
= ast_play_and_wait(chan
, "vm-no");
5572 res
= ast_play_and_wait(chan
, "vm-messages");
5579 /* PORTUGUESE syntax */
5580 static int vm_intro_pt(struct ast_channel
*chan
,struct vm_state
*vms
)
5582 /* Introduce messages they have */
5584 res
= ast_play_and_wait(chan
, "vm-youhave");
5586 if (vms
->newmessages
) {
5587 res
= ast_say_number(chan
, vms
->newmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
5589 if ((vms
->newmessages
== 1)) {
5590 res
= ast_play_and_wait(chan
, "vm-message");
5592 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5594 res
= ast_play_and_wait(chan
, "vm-messages");
5596 res
= ast_play_and_wait(chan
, "vm-INBOX");
5599 if (vms
->oldmessages
&& !res
)
5600 res
= ast_play_and_wait(chan
, "vm-and");
5602 if (!res
&& vms
->oldmessages
) {
5603 res
= ast_say_number(chan
, vms
->oldmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
5605 if (vms
->oldmessages
== 1) {
5606 res
= ast_play_and_wait(chan
, "vm-message");
5608 res
= ast_play_and_wait(chan
, "vm-Olds");
5610 res
= ast_play_and_wait(chan
, "vm-messages");
5612 res
= ast_play_and_wait(chan
, "vm-Old");
5617 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5618 res
= ast_play_and_wait(chan
, "vm-no");
5620 res
= ast_play_and_wait(chan
, "vm-messages");
5629 /* in czech there must be declension of word new and message
5630 * czech : english : czech : english
5631 * --------------------------------------------------------
5632 * vm-youhave : you have
5633 * vm-novou : one new : vm-zpravu : message
5634 * vm-nove : 2-4 new : vm-zpravy : messages
5635 * vm-novych : 5-infinite new : vm-zprav : messages
5636 * vm-starou : one old
5637 * vm-stare : 2-4 old
5638 * vm-starych : 5-infinite old
5639 * jednu : one - falling 4.
5640 * vm-no : no ( no messages )
5643 static int vm_intro_cz(struct ast_channel
*chan
,struct vm_state
*vms
)
5646 res
= ast_play_and_wait(chan
, "vm-youhave");
5648 if (vms
->newmessages
) {
5649 if (vms
->newmessages
== 1) {
5650 res
= ast_play_and_wait(chan
, "digits/jednu");
5652 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5655 if ((vms
->newmessages
== 1))
5656 res
= ast_play_and_wait(chan
, "vm-novou");
5657 if ((vms
->newmessages
) > 1 && (vms
->newmessages
< 5))
5658 res
= ast_play_and_wait(chan
, "vm-nove");
5659 if (vms
->newmessages
> 4)
5660 res
= ast_play_and_wait(chan
, "vm-novych");
5662 if (vms
->oldmessages
&& !res
)
5663 res
= ast_play_and_wait(chan
, "vm-and");
5665 if ((vms
->newmessages
== 1))
5666 res
= ast_play_and_wait(chan
, "vm-zpravu");
5667 if ((vms
->newmessages
) > 1 && (vms
->newmessages
< 5))
5668 res
= ast_play_and_wait(chan
, "vm-zpravy");
5669 if (vms
->newmessages
> 4)
5670 res
= ast_play_and_wait(chan
, "vm-zprav");
5673 if (!res
&& vms
->oldmessages
) {
5674 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5676 if ((vms
->oldmessages
== 1))
5677 res
= ast_play_and_wait(chan
, "vm-starou");
5678 if ((vms
->oldmessages
) > 1 && (vms
->oldmessages
< 5))
5679 res
= ast_play_and_wait(chan
, "vm-stare");
5680 if (vms
->oldmessages
> 4)
5681 res
= ast_play_and_wait(chan
, "vm-starych");
5684 if ((vms
->oldmessages
== 1))
5685 res
= ast_play_and_wait(chan
, "vm-zpravu");
5686 if ((vms
->oldmessages
) > 1 && (vms
->oldmessages
< 5))
5687 res
= ast_play_and_wait(chan
, "vm-zpravy");
5688 if (vms
->oldmessages
> 4)
5689 res
= ast_play_and_wait(chan
, "vm-zprav");
5693 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5694 res
= ast_play_and_wait(chan
, "vm-no");
5696 res
= ast_play_and_wait(chan
, "vm-zpravy");
5703 static int get_lastdigits(int num
)
5706 return (num
< 20) ? num
: num
% 10;
5709 static int vm_intro_ru(struct ast_channel
*chan
,struct vm_state
*vms
)
5715 res
= ast_play_and_wait(chan
, "vm-youhave");
5716 if (!res
&& vms
->newmessages
) {
5717 lastnum
= get_lastdigits(vms
->newmessages
);
5718 dcnum
= vms
->newmessages
- lastnum
;
5720 res
= say_and_wait(chan
, dcnum
, chan
->language
);
5721 if (!res
&& lastnum
) {
5723 res
= ast_play_and_wait(chan
, "digits/odno");
5725 res
= say_and_wait(chan
, lastnum
, chan
->language
);
5729 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-novoe" : "vm-novyh");
5731 if (!res
&& vms
->oldmessages
)
5732 res
= ast_play_and_wait(chan
, "vm-and");
5735 if (!res
&& vms
->oldmessages
) {
5736 lastnum
= get_lastdigits(vms
->oldmessages
);
5737 dcnum
= vms
->oldmessages
- lastnum
;
5739 res
= say_and_wait(chan
, dcnum
, chan
->language
);
5740 if (!res
&& lastnum
) {
5742 res
= ast_play_and_wait(chan
, "digits/odno");
5744 res
= say_and_wait(chan
, lastnum
, chan
->language
);
5748 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-staroe" : "vm-staryh");
5751 if (!res
&& !vms
->newmessages
&& !vms
->oldmessages
) {
5753 res
= ast_play_and_wait(chan
, "vm-no");
5759 res
= ast_play_and_wait(chan
, "vm-soobshenie");
5764 res
= ast_play_and_wait(chan
, "vm-soobsheniya");
5767 res
= ast_play_and_wait(chan
, "vm-soobsheniy");
5775 /* UKRAINIAN syntax */
5776 /* in ukrainian the syntax is different so we need the following files
5777 * --------------------------------------------------------
5778 * /digits/ua/1e 'odne'
5783 static int vm_intro_ua(struct ast_channel
*chan
,struct vm_state
*vms
)
5789 res
= ast_play_and_wait(chan
, "vm-youhave");
5790 if (!res
&& vms
->newmessages
) {
5791 lastnum
= get_lastdigits(vms
->newmessages
);
5792 dcnum
= vms
->newmessages
- lastnum
;
5794 res
= say_and_wait(chan
, dcnum
, chan
->language
);
5795 if (!res
&& lastnum
) {
5797 res
= ast_play_and_wait(chan
, "digits/ua/1e");
5799 res
= say_and_wait(chan
, lastnum
, chan
->language
);
5803 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-nove" : "vm-INBOX");
5805 if (!res
&& vms
->oldmessages
)
5806 res
= ast_play_and_wait(chan
, "vm-and");
5809 if (!res
&& vms
->oldmessages
) {
5810 lastnum
= get_lastdigits(vms
->oldmessages
);
5811 dcnum
= vms
->oldmessages
- lastnum
;
5813 res
= say_and_wait(chan
, dcnum
, chan
->language
);
5814 if (!res
&& lastnum
) {
5816 res
= ast_play_and_wait(chan
, "digits/ua/1e");
5818 res
= say_and_wait(chan
, lastnum
, chan
->language
);
5822 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-stare" : "vm-Old");
5825 if (!res
&& !vms
->newmessages
&& !vms
->oldmessages
) {
5827 res
= ast_play_and_wait(chan
, "vm-no");
5836 res
= ast_play_and_wait(chan
, "vm-message");
5839 res
= ast_play_and_wait(chan
, "vm-messages");
5848 static int vm_intro(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
)
5852 /* Notify the user that the temp greeting is set and give them the option to remove it */
5853 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
5854 if (ast_test_flag(vmu
, VM_TEMPGREETWARN
)) {
5855 if (ast_fileexists(prefile
, NULL
, NULL
) > 0)
5856 ast_play_and_wait(chan
, "vm-tempgreetactive");
5859 /* Play voicemail intro - syntax is different for different languages */
5860 if (!strcasecmp(chan
->language
, "de")) { /* GERMAN syntax */
5861 return vm_intro_de(chan
, vms
);
5862 } else if (!strcasecmp(chan
->language
, "es")) { /* SPANISH syntax */
5863 return vm_intro_es(chan
, vms
);
5864 } else if (!strcasecmp(chan
->language
, "it")) { /* ITALIAN syntax */
5865 return vm_intro_it(chan
, vms
);
5866 } else if (!strcasecmp(chan
->language
, "fr")) { /* FRENCH syntax */
5867 return vm_intro_fr(chan
, vms
);
5868 } else if (!strcasecmp(chan
->language
, "nl")) { /* DUTCH syntax */
5869 return vm_intro_nl(chan
, vms
);
5870 } else if (!strcasecmp(chan
->language
, "pt")) { /* PORTUGUESE syntax */
5871 return vm_intro_pt(chan
, vms
);
5872 } else if (!strcasecmp(chan
->language
, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
5873 return vm_intro_pt_BR(chan
, vms
);
5874 } else if (!strcasecmp(chan
->language
, "cz")) { /* CZECH syntax */
5875 return vm_intro_cz(chan
, vms
);
5876 } else if (!strcasecmp(chan
->language
, "gr")) { /* GREEK syntax */
5877 return vm_intro_gr(chan
, vms
);
5878 } else if (!strcasecmp(chan
->language
, "pl")) { /* POLISH syntax */
5879 return vm_intro_pl(chan
, vms
);
5880 } else if (!strcasecmp(chan
->language
, "se")) { /* SWEDISH syntax */
5881 return vm_intro_se(chan
, vms
);
5882 } else if (!strcasecmp(chan
->language
, "no")) { /* NORWEGIAN syntax */
5883 return vm_intro_no(chan
, vms
);
5884 } else if (!strcasecmp(chan
->language
, "ru")) { /* RUSSIAN syntax */
5885 return vm_intro_ru(chan
, vms
);
5886 } else if (!strcasecmp(chan
->language
, "ua")) { /* UKRAINIAN syntax */
5887 return vm_intro_ua(chan
, vms
);
5888 } else { /* Default to ENGLISH */
5889 return vm_intro_en(chan
, vms
);
5893 static int vm_instructions(struct ast_channel
*chan
, struct vm_state
*vms
, int skipadvanced
)
5896 /* Play instructions and wait for new command */
5898 if (vms
->starting
) {
5899 if (vms
->lastmsg
> -1) {
5900 res
= ast_play_and_wait(chan
, "vm-onefor");
5902 res
= vm_play_folder_name(chan
, vms
->vmbox
);
5905 res
= ast_play_and_wait(chan
, "vm-opts");
5908 res
= ast_play_and_wait(chan
, "vm-prev");
5909 if (!res
&& !skipadvanced
)
5910 res
= ast_play_and_wait(chan
, "vm-advopts");
5912 res
= ast_play_and_wait(chan
, "vm-repeat");
5913 if (!res
&& (vms
->curmsg
!= vms
->lastmsg
))
5914 res
= ast_play_and_wait(chan
, "vm-next");
5916 if (!vms
->deleted
[vms
->curmsg
])
5917 res
= ast_play_and_wait(chan
, "vm-delete");
5919 res
= ast_play_and_wait(chan
, "vm-undelete");
5921 res
= ast_play_and_wait(chan
, "vm-toforward");
5923 res
= ast_play_and_wait(chan
, "vm-savemessage");
5927 res
= ast_play_and_wait(chan
, "vm-helpexit");
5929 res
= ast_waitfordigit(chan
, 6000);
5932 if (vms
->repeats
> 2) {
5940 static int vm_newuser(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
)
5945 char newpassword
[80] = "";
5946 char newpassword2
[80] = "";
5947 char prefile
[PATH_MAX
] = "";
5948 unsigned char buf
[256];
5951 if (ast_adsi_available(chan
)) {
5952 bytes
+= adsi_logo(buf
+ bytes
);
5953 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "New User Setup", "");
5954 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Not Done", "");
5955 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
5956 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
5957 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
5960 /* First, have the user change their password
5961 so they won't get here again */
5963 newpassword
[1] = '\0';
5964 newpassword
[0] = cmd
= ast_play_and_wait(chan
,"vm-newpassword");
5966 newpassword
[0] = '\0';
5967 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5969 cmd
= ast_readstring(chan
,newpassword
+ strlen(newpassword
),sizeof(newpassword
)-1,2000,10000,"#");
5970 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5972 newpassword2
[1] = '\0';
5973 newpassword2
[0] = cmd
= ast_play_and_wait(chan
,"vm-reenterpassword");
5975 newpassword2
[0] = '\0';
5976 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5978 cmd
= ast_readstring(chan
,newpassword2
+ strlen(newpassword2
),sizeof(newpassword2
)-1,2000,10000,"#");
5979 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5981 if (!strcmp(newpassword
, newpassword2
))
5983 ast_log(LOG_NOTICE
,"Password mismatch for user %s (%s != %s)\n", vms
->username
, newpassword
, newpassword2
);
5984 cmd
= ast_play_and_wait(chan
, "vm-mismatch");
5988 if (ast_strlen_zero(ext_pass_cmd
))
5989 vm_change_password(vmu
,newpassword
);
5991 vm_change_password_shell(vmu
,newpassword
);
5992 if (option_debug
> 2)
5993 ast_log(LOG_DEBUG
,"User %s set password to %s of length %d\n",vms
->username
,newpassword
,(int)strlen(newpassword
));
5994 cmd
= ast_play_and_wait(chan
,"vm-passchanged");
5996 /* If forcename is set, have the user record their name */
5997 if (ast_test_flag(vmu
, VM_FORCENAME
)) {
5998 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/greet", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
5999 if (ast_fileexists(prefile
, NULL
, NULL
) < 1) {
6000 #ifndef IMAP_STORAGE
6001 cmd
= play_record_review(chan
, "vm-rec-name", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6003 cmd
= play_record_review(chan
, "vm-rec-name", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6005 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
6010 /* If forcegreetings is set, have the user record their greetings */
6011 if (ast_test_flag(vmu
, VM_FORCEGREET
)) {
6012 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/unavail", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6013 if (ast_fileexists(prefile
, NULL
, NULL
) < 1) {
6014 #ifndef IMAP_STORAGE
6015 cmd
= play_record_review(chan
, "vm-rec-unv", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6017 cmd
= play_record_review(chan
, "vm-rec-unv", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6019 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
6023 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/busy", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6024 if (ast_fileexists(prefile
, NULL
, NULL
) < 1) {
6025 #ifndef IMAP_STORAGE
6026 cmd
= play_record_review(chan
, "vm-rec-busy", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6028 cmd
= play_record_review(chan
, "vm-rec-busy", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6030 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
6038 static int vm_options(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
)
6043 char newpassword
[80] = "";
6044 char newpassword2
[80] = "";
6045 char prefile
[PATH_MAX
] = "";
6046 unsigned char buf
[256];
6049 if (ast_adsi_available(chan
))
6051 bytes
+= adsi_logo(buf
+ bytes
);
6052 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Options Menu", "");
6053 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Not Done", "");
6054 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
6055 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
6056 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
6058 while ((cmd
>= 0) && (cmd
!= 't')) {
6063 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/unavail", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6064 #ifndef IMAP_STORAGE
6065 cmd
= play_record_review(chan
,"vm-rec-unv",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6067 cmd
= play_record_review(chan
,"vm-rec-unv",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6071 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/busy", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6072 #ifndef IMAP_STORAGE
6073 cmd
= play_record_review(chan
,"vm-rec-busy",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6075 cmd
= play_record_review(chan
,"vm-rec-busy",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6079 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/greet", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6080 #ifndef IMAP_STORAGE
6081 cmd
= play_record_review(chan
,"vm-rec-name",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6083 cmd
= play_record_review(chan
,"vm-rec-name",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6087 cmd
= vm_tempgreeting(chan
, vmu
, vms
, fmtc
, record_gain
);
6090 if (vmu
->password
[0] == '-') {
6091 cmd
= ast_play_and_wait(chan
, "vm-no");
6094 newpassword
[1] = '\0';
6095 newpassword
[0] = cmd
= ast_play_and_wait(chan
,"vm-newpassword");
6097 newpassword
[0] = '\0';
6101 if ((cmd
= ast_readstring(chan
,newpassword
+ strlen(newpassword
),sizeof(newpassword
)-1,2000,10000,"#")) < 0) {
6105 newpassword2
[1] = '\0';
6106 newpassword2
[0] = cmd
= ast_play_and_wait(chan
,"vm-reenterpassword");
6108 newpassword2
[0] = '\0';
6113 if ((cmd
= ast_readstring(chan
,newpassword2
+ strlen(newpassword2
),sizeof(newpassword2
)-1,2000,10000,"#"))) {
6117 if (strcmp(newpassword
, newpassword2
)) {
6118 ast_log(LOG_NOTICE
,"Password mismatch for user %s (%s != %s)\n", vms
->username
, newpassword
, newpassword2
);
6119 cmd
= ast_play_and_wait(chan
, "vm-mismatch");
6122 if (ast_strlen_zero(ext_pass_cmd
))
6123 vm_change_password(vmu
,newpassword
);
6125 vm_change_password_shell(vmu
,newpassword
);
6126 if (option_debug
> 2)
6127 ast_log(LOG_DEBUG
,"User %s set password to %s of length %d\n",vms
->username
,newpassword
,(int)strlen(newpassword
));
6128 cmd
= ast_play_and_wait(chan
,"vm-passchanged");
6135 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6136 if (ast_fileexists(prefile
, NULL
, NULL
))
6137 cmd
= ast_play_and_wait(chan
, "vm-tmpexists");
6139 cmd
= ast_play_and_wait(chan
, "vm-options");
6141 cmd
= ast_waitfordigit(chan
,6000);
6153 static int vm_tempgreeting(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
)
6159 char prefile
[PATH_MAX
] = "";
6160 unsigned char buf
[256];
6161 char dest
[PATH_MAX
];
6164 if (ast_adsi_available(chan
)) {
6165 bytes
+= adsi_logo(buf
+ bytes
);
6166 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Temp Greeting Menu", "");
6167 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Not Done", "");
6168 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
6169 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
6170 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
6173 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6174 if ((res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, vms
->username
, "temp"))) {
6175 ast_log(LOG_WARNING
, "Failed to create directory (%s).\n", prefile
);
6178 while ((cmd
>= 0) && (cmd
!= 't')) {
6181 RETRIEVE(prefile
, -1);
6182 if (ast_fileexists(prefile
, NULL
, NULL
) <= 0) {
6183 #ifndef IMAP_STORAGE
6184 play_record_review(chan
, "vm-rec-temp", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6186 play_record_review(chan
, "vm-rec-temp", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6192 #ifndef IMAP_STORAGE
6193 cmd
= play_record_review(chan
, "vm-rec-temp", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6195 cmd
= play_record_review(chan
, "vm-rec-temp", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6199 DELETE(prefile
, -1, prefile
);
6200 ast_play_and_wait(chan
, "vm-tempremoved");
6207 cmd
= ast_play_and_wait(chan
,
6208 ast_fileexists(prefile
, NULL
, NULL
) > 0 ? /* XXX always true ? */
6209 "vm-tempgreeting2" : "vm-tempgreeting");
6211 cmd
= ast_waitfordigit(chan
,6000);
6218 DISPOSE(prefile
, -1);
6227 static int vm_browse_messages_gr(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6231 if (vms
->lastmsg
> -1) {
6232 cmd
= play_message(chan
, vmu
, vms
);
6234 cmd
= ast_play_and_wait(chan
, "vm-youhaveno");
6235 if (!strcasecmp(vms
->vmbox
, "vm-INBOX") ||!strcasecmp(vms
->vmbox
, "vm-Old")){
6237 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%ss", vms
->curbox
);
6238 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6241 cmd
= ast_play_and_wait(chan
, "vm-messages");
6244 cmd
= ast_play_and_wait(chan
, "vm-messages");
6246 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6247 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6254 /* Default English syntax */
6255 static int vm_browse_messages_en(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6259 if (vms
->lastmsg
> -1) {
6260 cmd
= play_message(chan
, vmu
, vms
);
6262 cmd
= ast_play_and_wait(chan
, "vm-youhave");
6264 cmd
= ast_play_and_wait(chan
, "vm-no");
6266 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6267 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6270 cmd
= ast_play_and_wait(chan
, "vm-messages");
6275 /* ITALIAN syntax */
6276 static int vm_browse_messages_it(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6280 if (vms
->lastmsg
> -1) {
6281 cmd
= play_message(chan
, vmu
, vms
);
6283 cmd
= ast_play_and_wait(chan
, "vm-no");
6285 cmd
= ast_play_and_wait(chan
, "vm-message");
6287 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6288 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6294 /* SPANISH syntax */
6295 static int vm_browse_messages_es(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6299 if (vms
->lastmsg
> -1) {
6300 cmd
= play_message(chan
, vmu
, vms
);
6302 cmd
= ast_play_and_wait(chan
, "vm-youhaveno");
6304 cmd
= ast_play_and_wait(chan
, "vm-messages");
6306 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6307 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6313 /* PORTUGUESE syntax */
6314 static int vm_browse_messages_pt(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6318 if (vms
->lastmsg
> -1) {
6319 cmd
= play_message(chan
, vmu
, vms
);
6321 cmd
= ast_play_and_wait(chan
, "vm-no");
6323 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6324 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6327 cmd
= ast_play_and_wait(chan
, "vm-messages");
6332 static int vm_browse_messages(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6334 if (!strcasecmp(chan
->language
, "es")) { /* SPANISH */
6335 return vm_browse_messages_es(chan
, vms
, vmu
);
6336 } else if (!strcasecmp(chan
->language
, "it")) { /* ITALIAN */
6337 return vm_browse_messages_it(chan
, vms
, vmu
);
6338 } else if (!strcasecmp(chan
->language
, "pt") || !strcasecmp(chan
->language
, "pt_BR")) { /* PORTUGUESE */
6339 return vm_browse_messages_pt(chan
, vms
, vmu
);
6340 } else if (!strcasecmp(chan
->language
, "gr")){
6341 return vm_browse_messages_gr(chan
, vms
, vmu
); /* GREEK */
6342 } else { /* Default to English syntax */
6343 return vm_browse_messages_en(chan
, vms
, vmu
);
6347 static int vm_authenticate(struct ast_channel
*chan
, char *mailbox
, int mailbox_size
,
6348 struct ast_vm_user
*res_vmu
, const char *context
, const char *prefix
,
6349 int skipuser
, int maxlogins
, int silent
)
6351 int useadsi
=0, valid
=0, logretries
=0;
6352 char password
[AST_MAX_EXTENSION
]="", *passptr
;
6353 struct ast_vm_user vmus
, *vmu
= NULL
;
6355 /* If ADSI is supported, setup login screen */
6356 adsi_begin(chan
, &useadsi
);
6357 if (!skipuser
&& useadsi
)
6359 if (!silent
&& !skipuser
&& ast_streamfile(chan
, "vm-login", chan
->language
)) {
6360 ast_log(LOG_WARNING
, "Couldn't stream login file\n");
6364 /* Authenticate them and get their mailbox/password */
6366 while (!valid
&& (logretries
< maxlogins
)) {
6367 /* Prompt for, and read in the username */
6368 if (!skipuser
&& ast_readstring(chan
, mailbox
, mailbox_size
- 1, 2000, 10000, "#") < 0) {
6369 ast_log(LOG_WARNING
, "Couldn't read username\n");
6372 if (ast_strlen_zero(mailbox
)) {
6373 if (chan
->cid
.cid_num
) {
6374 ast_copy_string(mailbox
, chan
->cid
.cid_num
, mailbox_size
);
6376 if (option_verbose
> 2)
6377 ast_verbose(VERBOSE_PREFIX_3
"Username not entered\n");
6382 adsi_password(chan
);
6384 if (!ast_strlen_zero(prefix
)) {
6385 char fullusername
[80] = "";
6386 ast_copy_string(fullusername
, prefix
, sizeof(fullusername
));
6387 strncat(fullusername
, mailbox
, sizeof(fullusername
) - 1 - strlen(fullusername
));
6388 ast_copy_string(mailbox
, fullusername
, mailbox_size
);
6392 ast_log(LOG_DEBUG
, "Before find user for mailbox %s\n",mailbox
);
6393 vmu
= find_user(&vmus
, context
, mailbox
);
6394 if (vmu
&& (vmu
->password
[0] == '\0' || (vmu
->password
[0] == '-' && vmu
->password
[1] == '\0'))) {
6395 /* saved password is blank, so don't bother asking */
6398 if (ast_streamfile(chan
, "vm-password", chan
->language
)) {
6399 ast_log(LOG_WARNING
, "Unable to stream password file\n");
6402 if (ast_readstring(chan
, password
, sizeof(password
) - 1, 2000, 10000, "#") < 0) {
6403 ast_log(LOG_WARNING
, "Unable to read password\n");
6409 passptr
= vmu
->password
;
6410 if (passptr
[0] == '-') passptr
++;
6412 if (vmu
&& !strcmp(passptr
, password
))
6415 if (option_verbose
> 2)
6416 ast_verbose( VERBOSE_PREFIX_3
"Incorrect password '%s' for user '%s' (context = %s)\n", password
, mailbox
, context
? context
: "default");
6417 if (!ast_strlen_zero(prefix
))
6422 if (skipuser
|| logretries
>= maxlogins
) {
6423 if (ast_streamfile(chan
, "vm-incorrect", chan
->language
)) {
6424 ast_log(LOG_WARNING
, "Unable to stream incorrect message\n");
6430 if (ast_streamfile(chan
, "vm-incorrect-mailbox", chan
->language
)) {
6431 ast_log(LOG_WARNING
, "Unable to stream incorrect mailbox message\n");
6435 if (ast_waitstream(chan
, "")) /* Channel is hung up */
6439 if (!valid
&& (logretries
>= maxlogins
)) {
6440 ast_stopstream(chan
);
6441 ast_play_and_wait(chan
, "vm-goodbye");
6444 if (vmu
&& !skipuser
) {
6445 memcpy(res_vmu
, vmu
, sizeof(struct ast_vm_user
));
6450 static int vm_execmain(struct ast_channel
*chan
, void *data
)
6452 /* XXX This is, admittedly, some pretty horrendus code. For some
6453 reason it just seemed a lot easier to do with GOTO's. I feel
6454 like I'm back in my GWBASIC days. XXX */
6458 struct ast_module_user
*u
;
6459 char prefixstr
[80] ="";
6460 char ext_context
[256]="";
6464 struct vm_state vms
;
6465 struct ast_vm_user
*vmu
= NULL
, vmus
;
6468 struct ast_flags flags
= { 0 };
6469 signed char record_gain
= 0;
6471 int play_folder
= 0;
6475 u
= ast_module_user_add(chan
);
6477 /* Add the vm_state to the active list and keep it active */
6478 memset(&vms
, 0, sizeof(vms
));
6481 memset(&vmus
, 0, sizeof(vmus
));
6483 if (chan
->_state
!= AST_STATE_UP
) {
6485 ast_log(LOG_DEBUG
, "Before ast_answer\n");
6489 if (!ast_strlen_zero(data
)) {
6490 char *opts
[OPT_ARG_ARRAY_SIZE
];
6492 AST_DECLARE_APP_ARGS(args
,
6497 parse
= ast_strdupa(data
);
6499 AST_STANDARD_APP_ARGS(args
, parse
);
6501 if (args
.argc
== 2) {
6502 if (ast_app_parse_options(vm_app_options
, &flags
, opts
, args
.argv1
)) {
6503 ast_module_user_remove(u
);
6506 if (ast_test_flag(&flags
, OPT_RECORDGAIN
)) {
6508 if (!ast_strlen_zero(opts
[OPT_ARG_RECORDGAIN
])) {
6509 if (sscanf(opts
[OPT_ARG_RECORDGAIN
], "%d", &gain
) != 1) {
6510 ast_log(LOG_WARNING
, "Invalid value '%s' provided for record gain option\n", opts
[OPT_ARG_RECORDGAIN
]);
6511 ast_module_user_remove(u
);
6514 record_gain
= (signed char) gain
;
6517 ast_log(LOG_WARNING
, "Invalid Gain level set with option g\n");
6520 if (ast_test_flag(&flags
, OPT_AUTOPLAY
) ) {
6522 if (opts
[OPT_ARG_PLAYFOLDER
]) {
6523 if (sscanf(opts
[OPT_ARG_PLAYFOLDER
], "%d", &play_folder
) != 1) {
6524 ast_log(LOG_WARNING
, "Invalid value '%s' provided for folder autoplay option\n", opts
[OPT_ARG_PLAYFOLDER
]);
6527 ast_log(LOG_WARNING
, "Invalid folder set with option a\n");
6529 if ( play_folder
> 9 || play_folder
< 0) {
6530 ast_log(LOG_WARNING
, "Invalid value '%d' provided for folder autoplay option\n", play_folder
);
6535 /* old style options parsing */
6536 while (*(args
.argv0
)) {
6537 if (*(args
.argv0
) == 's')
6538 ast_set_flag(&flags
, OPT_SILENT
);
6539 else if (*(args
.argv0
) == 'p')
6540 ast_set_flag(&flags
, OPT_PREPEND_MAILBOX
);
6548 valid
= ast_test_flag(&flags
, OPT_SILENT
);
6550 if ((context
= strchr(args
.argv0
, '@')))
6553 if (ast_test_flag(&flags
, OPT_PREPEND_MAILBOX
))
6554 ast_copy_string(prefixstr
, args
.argv0
, sizeof(prefixstr
));
6556 ast_copy_string(vms
.username
, args
.argv0
, sizeof(vms
.username
));
6558 if (!ast_strlen_zero(vms
.username
) && (vmu
= find_user(&vmus
, context
,vms
.username
)))
6565 res
= vm_authenticate(chan
, vms
.username
, sizeof(vms
.username
), &vmus
, context
, prefixstr
, skipuser
, maxlogins
, 0);
6568 ast_log(LOG_DEBUG
, "After vm_authenticate\n");
6577 /* If ADSI is supported, setup login screen */
6578 adsi_begin(chan
, &useadsi
);
6581 vms
.interactive
= 1;
6583 vmstate_insert(&vms
);
6584 init_vm_state(&vms
);
6589 if (!(vms
.deleted
= ast_calloc(vmu
->maxmsg
, sizeof(int)))) {
6590 /* TODO: Handle memory allocation failure */
6592 if (!(vms
.heard
= ast_calloc(vmu
->maxmsg
, sizeof(int)))) {
6593 /* TODO: Handle memory allocation failure */
6596 /* Set language from config to override channel language */
6597 if (!ast_strlen_zero(vmu
->language
))
6598 ast_string_field_set(chan
, language
, vmu
->language
);
6599 create_dirpath(vms
.curdir
, sizeof(vms
.curdir
), vmu
->context
, vms
.username
, "");
6600 /* Retrieve old and new message counts */
6602 ast_log(LOG_DEBUG
, "Before open_mailbox\n");
6603 res
= open_mailbox(&vms
, vmu
, 1);
6604 if (res
== ERROR_LOCK_PATH
)
6606 vms
.oldmessages
= vms
.lastmsg
+ 1;
6607 if (option_debug
> 2)
6608 ast_log(LOG_DEBUG
, "Number of old messages: %d\n",vms
.oldmessages
);
6609 /* Start in INBOX */
6610 res
= open_mailbox(&vms
, vmu
, 0);
6611 if (res
== ERROR_LOCK_PATH
)
6613 vms
.newmessages
= vms
.lastmsg
+ 1;
6614 if (option_debug
> 2)
6615 ast_log(LOG_DEBUG
, "Number of new messages: %d\n",vms
.newmessages
);
6617 /* Select proper mailbox FIRST!! */
6619 res
= open_mailbox(&vms
, vmu
, play_folder
);
6620 if (res
== ERROR_LOCK_PATH
)
6623 /* If there are no new messages, inform the user and hangup */
6624 if (vms
.lastmsg
== -1) {
6625 cmd
= vm_browse_messages(chan
, &vms
, vmu
);
6630 if (!vms
.newmessages
&& vms
.oldmessages
) {
6631 /* If we only have old messages start here */
6632 res
= open_mailbox(&vms
, vmu
, 1);
6634 if (res
== ERROR_LOCK_PATH
)
6640 adsi_status(chan
, &vms
);
6643 /* Check to see if this is a new user */
6644 if (!strcasecmp(vmu
->mailbox
, vmu
->password
) &&
6645 (ast_test_flag(vmu
, VM_FORCENAME
| VM_FORCEGREET
))) {
6646 if (ast_play_and_wait(chan
, "vm-newuser") == -1)
6647 ast_log(LOG_WARNING
, "Couldn't stream new user file\n");
6648 cmd
= vm_newuser(chan
, vmu
, &vms
, vmfmts
, record_gain
);
6649 if ((cmd
== 't') || (cmd
== '#')) {
6653 } else if (cmd
< 0) {
6660 if (option_debug
> 2)
6661 ast_log(LOG_DEBUG
, "Checking quotas: comparing %u to %u\n",vms
.quota_usage
,vms
.quota_limit
);
6662 if (vms
.quota_limit
&& vms
.quota_usage
>= vms
.quota_limit
) {
6664 ast_log(LOG_DEBUG
, "*** QUOTA EXCEEDED!!\n");
6665 cmd
= ast_play_and_wait(chan
, "vm-mailboxfull");
6667 if (option_debug
> 2)
6668 ast_log(LOG_DEBUG
, "Checking quotas: User has %d messages and limit is %d.\n",(vms
.newmessages
+ vms
.oldmessages
),vmu
->maxmsg
);
6669 if ((vms
.newmessages
+ vms
.oldmessages
) >= vmu
->maxmsg
) {
6670 ast_log(LOG_WARNING
, "No more messages possible. User has %d messages and limit is %d.\n",(vms
.newmessages
+ vms
.oldmessages
),vmu
->maxmsg
);
6671 cmd
= ast_play_and_wait(chan
, "vm-mailboxfull");
6677 cmd
= vm_intro(chan
, vmu
, &vms
);
6682 while ((cmd
> -1) && (cmd
!= 't') && (cmd
!= '#')) {
6689 cmd
= vm_browse_messages(chan
, &vms
, vmu
);
6691 case '2': /* Change folders */
6693 adsi_folders(chan
, 0, "Change to folder...");
6694 cmd
= get_folder2(chan
, "vm-changeto", 0);
6697 } else if (cmd
> 0) {
6699 res
= close_mailbox(&vms
, vmu
);
6700 if (res
== ERROR_LOCK_PATH
)
6702 res
= open_mailbox(&vms
, vmu
, cmd
);
6703 if (res
== ERROR_LOCK_PATH
)
6709 adsi_status2(chan
, &vms
);
6712 cmd
= vm_play_folder_name(chan
, vms
.vmbox
);
6716 case '3': /* Advanced options */
6719 while ((cmd
> -1) && (cmd
!= 't') && (cmd
!= '#')) {
6721 case '1': /* Reply */
6722 if (vms
.lastmsg
> -1 && !vms
.starting
) {
6723 cmd
= advanced_options(chan
, vmu
, &vms
, vms
.curmsg
, 1, record_gain
);
6724 if (cmd
== ERROR_LOCK_PATH
) {
6729 cmd
= ast_play_and_wait(chan
, "vm-sorry");
6732 case '2': /* Callback */
6733 if (option_verbose
> 2 && !vms
.starting
)
6734 ast_verbose( VERBOSE_PREFIX_3
"Callback Requested\n");
6735 if (!ast_strlen_zero(vmu
->callback
) && vms
.lastmsg
> -1 && !vms
.starting
) {
6736 cmd
= advanced_options(chan
, vmu
, &vms
, vms
.curmsg
, 2, record_gain
);
6740 } else if (cmd
== ERROR_LOCK_PATH
) {
6746 cmd
= ast_play_and_wait(chan
, "vm-sorry");
6749 case '3': /* Envelope */
6750 if (vms
.lastmsg
> -1 && !vms
.starting
) {
6751 cmd
= advanced_options(chan
, vmu
, &vms
, vms
.curmsg
, 3, record_gain
);
6752 if (cmd
== ERROR_LOCK_PATH
) {
6757 cmd
= ast_play_and_wait(chan
, "vm-sorry");
6760 case '4': /* Dialout */
6761 if (!ast_strlen_zero(vmu
->dialout
)) {
6762 cmd
= dialout(chan
, vmu
, NULL
, vmu
->dialout
);
6769 cmd
= ast_play_and_wait(chan
, "vm-sorry");
6773 case '5': /* Leave VoiceMail */
6774 if (ast_test_flag(vmu
, VM_SVMAIL
)) {
6775 cmd
= forward_message(chan
, context
, &vms
, vmu
, vmfmts
, 1, record_gain
);
6776 if (cmd
== ERROR_LOCK_PATH
) {
6778 ast_log(LOG_WARNING
, "forward_message failed to lock path.\n");
6782 cmd
= ast_play_and_wait(chan
,"vm-sorry");
6786 case '*': /* Return to main menu */
6792 if (!vms
.starting
) {
6793 cmd
= ast_play_and_wait(chan
, "vm-toreply");
6795 if (!ast_strlen_zero(vmu
->callback
) && !vms
.starting
&& !cmd
) {
6796 cmd
= ast_play_and_wait(chan
, "vm-tocallback");
6798 if (!cmd
&& !vms
.starting
) {
6799 cmd
= ast_play_and_wait(chan
, "vm-tohearenv");
6801 if (!ast_strlen_zero(vmu
->dialout
) && !cmd
) {
6802 cmd
= ast_play_and_wait(chan
, "vm-tomakecall");
6804 if (ast_test_flag(vmu
, VM_SVMAIL
) && !cmd
)
6805 cmd
=ast_play_and_wait(chan
, "vm-leavemsg");
6807 cmd
= ast_play_and_wait(chan
, "vm-starmain");
6809 cmd
= ast_waitfordigit(chan
,6000);
6812 if (vms
.repeats
> 3)
6822 if (vms
.curmsg
> 0) {
6824 cmd
= play_message(chan
, vmu
, &vms
);
6826 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6830 if (vms
.curmsg
< vms
.lastmsg
) {
6832 cmd
= play_message(chan
, vmu
, &vms
);
6834 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6838 if (vms
.curmsg
>= 0 && vms
.curmsg
<= vms
.lastmsg
) {
6839 vms
.deleted
[vms
.curmsg
] = !vms
.deleted
[vms
.curmsg
];
6841 adsi_delete(chan
, &vms
);
6842 if (vms
.deleted
[vms
.curmsg
]) {
6843 if (play_folder
== 0)
6845 else if (play_folder
== 1)
6847 cmd
= ast_play_and_wait(chan
, "vm-deleted");
6850 if (play_folder
== 0)
6852 else if (play_folder
== 1)
6854 cmd
= ast_play_and_wait(chan
, "vm-undeleted");
6856 if (ast_test_flag((&globalflags
), VM_SKIPAFTERCMD
)) {
6857 if (vms
.curmsg
< vms
.lastmsg
) {
6859 cmd
= play_message(chan
, vmu
, &vms
);
6861 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6864 } else /* Delete not valid if we haven't selected a message */
6872 if (vms
.lastmsg
> -1) {
6873 cmd
= forward_message(chan
, context
, &vms
, vmu
, vmfmts
, 0, record_gain
);
6874 if (cmd
== ERROR_LOCK_PATH
) {
6879 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6882 if (vms
.curmsg
< 0 || vms
.curmsg
> vms
.lastmsg
) {
6883 /* No message selected */
6888 adsi_folders(chan
, 1, "Save to folder...");
6889 cmd
= get_folder2(chan
, "vm-savefolder", 1);
6890 box
= 0; /* Shut up compiler */
6894 } else if (cmd
> 0) {
6895 box
= cmd
= cmd
- '0';
6896 cmd
= save_to_folder(vmu
, &vms
, vms
.curmsg
, cmd
);
6897 if (cmd
== ERROR_LOCK_PATH
) {
6901 } else if (cmd
== 10) {
6905 vms
.deleted
[vms
.curmsg
] = 1;
6907 vms
.deleted
[vms
.curmsg
] = 0;
6908 vms
.heard
[vms
.curmsg
] = 0;
6911 make_file(vms
.fn
, sizeof(vms
.fn
), vms
.curdir
, vms
.curmsg
);
6913 adsi_message(chan
, &vms
);
6914 snprintf(vms
.fn
, sizeof(vms
.fn
), "vm-%s", mbox(box
));
6916 cmd
= ast_play_and_wait(chan
, "vm-message");
6918 cmd
= say_and_wait(chan
, vms
.curmsg
+ 1, chan
->language
);
6920 cmd
= ast_play_and_wait(chan
, "vm-savedto");
6922 cmd
= vm_play_folder_name(chan
, vms
.fn
);
6924 cmd
= ast_play_and_wait(chan
, "vm-mailboxfull");
6926 if (ast_test_flag((&globalflags
), VM_SKIPAFTERCMD
)) {
6927 if (vms
.curmsg
< vms
.lastmsg
) {
6929 cmd
= play_message(chan
, vmu
, &vms
);
6931 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6936 if (!vms
.starting
) {
6937 cmd
= ast_play_and_wait(chan
, "vm-onefor");
6939 cmd
= vm_play_folder_name(chan
, vms
.vmbox
);
6941 cmd
= ast_play_and_wait(chan
, "vm-opts");
6943 cmd
= vm_instructions(chan
, &vms
, 1);
6948 cmd
= vm_options(chan
, vmu
, &vms
, vmfmts
, record_gain
);
6950 adsi_status(chan
, &vms
);
6952 default: /* Nothing */
6953 cmd
= vm_instructions(chan
, &vms
, 0);
6957 if ((cmd
== 't') || (cmd
== '#')) {
6967 ast_stopstream(chan
);
6971 res
= ast_play_and_wait(chan
, "vm-dialout");
6973 res
= ast_play_and_wait(chan
, "vm-goodbye");
6978 ast_adsi_unload_session(chan
);
6981 close_mailbox(&vms
, vmu
);
6983 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", vms
.username
, vmu
->context
);
6984 manager_event(EVENT_FLAG_CALL
, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context
, has_voicemail(ext_context
, NULL
));
6985 run_externnotify(vmu
->context
, vmu
->mailbox
);
6988 /* expunge message - use UID Expunge if supported on IMAP server*/
6989 if (option_debug
> 2)
6990 ast_log(LOG_DEBUG
, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted
,expungeonhangup
);
6991 if (vmu
&& deleted
== 1 && expungeonhangup
== 1) {
6992 #ifdef HAVE_IMAP_TK2006
6993 if (LEVELUIDPLUS (vms
.mailstream
)) {
6994 mail_expunge_full(vms
.mailstream
,NIL
,EX_UID
);
6997 mail_expunge(vms
.mailstream
);
6999 /* before we delete the state, we should copy pertinent info
7000 * back to the persistent model */
7001 vmstate_delete(&vms
);
7009 ast_module_user_remove(u
);
7014 static int vm_exec(struct ast_channel
*chan
, void *data
)
7017 struct ast_module_user
*u
;
7019 struct leave_vm_options leave_options
;
7020 struct ast_flags flags
= { 0 };
7021 static int deprecate_warning
= 0;
7022 char *opts
[OPT_ARG_ARRAY_SIZE
];
7023 AST_DECLARE_APP_ARGS(args
,
7028 u
= ast_module_user_add(chan
);
7030 memset(&leave_options
, 0, sizeof(leave_options
));
7032 if (chan
->_state
!= AST_STATE_UP
)
7035 if (!ast_strlen_zero(data
)) {
7036 tmp
= ast_strdupa(data
);
7037 AST_STANDARD_APP_ARGS(args
, tmp
);
7038 if (args
.argc
== 2) {
7039 if (ast_app_parse_options(vm_app_options
, &flags
, opts
, args
.argv1
)) {
7040 ast_module_user_remove(u
);
7043 ast_copy_flags(&leave_options
, &flags
, OPT_SILENT
| OPT_BUSY_GREETING
| OPT_UNAVAIL_GREETING
| OPT_PRIORITY_JUMP
);
7044 if (ast_test_flag(&flags
, OPT_RECORDGAIN
)) {
7047 if (sscanf(opts
[OPT_ARG_RECORDGAIN
], "%d", &gain
) != 1) {
7048 ast_log(LOG_WARNING
, "Invalid value '%s' provided for record gain option\n", opts
[OPT_ARG_RECORDGAIN
]);
7049 ast_module_user_remove(u
);
7052 leave_options
.record_gain
= (signed char) gain
;
7056 /* old style options parsing */
7058 char *orig_argv0
= args
.argv0
;
7059 while (*(args
.argv0
)) {
7060 if (*(args
.argv0
) == 's') {
7062 ast_set_flag(&leave_options
, OPT_SILENT
);
7063 } else if (*(args
.argv0
) == 'b') {
7065 ast_set_flag(&leave_options
, OPT_BUSY_GREETING
);
7066 } else if (*(args
.argv0
) == 'u') {
7068 ast_set_flag(&leave_options
, OPT_UNAVAIL_GREETING
);
7069 } else if (*(args
.argv0
) == 'j') {
7071 ast_set_flag(&leave_options
, OPT_PRIORITY_JUMP
);
7076 if (!deprecate_warning
&& old
) {
7077 deprecate_warning
= 1;
7078 ast_log(LOG_WARNING
, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0
);
7079 ast_log(LOG_WARNING
, "Please move all leading options to the second argument.\n");
7084 res
= ast_app_getdata(chan
, "vm-whichbox", tmp
, sizeof(tmp
) - 1, 0);
7086 ast_module_user_remove(u
);
7089 if (ast_strlen_zero(tmp
)) {
7090 ast_module_user_remove(u
);
7093 args
.argv0
= ast_strdupa(tmp
);
7096 res
= leave_voicemail(chan
, args
.argv0
, &leave_options
);
7098 if (res
== ERROR_LOCK_PATH
) {
7099 ast_log(LOG_ERROR
, "Could not leave voicemail. The path is already locked.\n");
7100 /*Send the call to n+101 priority, where n is the current priority*/
7101 if (ast_test_flag(&leave_options
, OPT_PRIORITY_JUMP
) || ast_opt_priority_jumping
)
7102 if (ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101))
7103 ast_log(LOG_WARNING
, "Extension %s, priority %d doesn't exist.\n", chan
->exten
, chan
->priority
+ 101);
7104 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
7108 ast_module_user_remove(u
);
7113 static struct ast_vm_user
*find_or_create(char *context
, char *mbox
)
7115 struct ast_vm_user
*vmu
;
7116 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
7117 if (ast_test_flag((&globalflags
), VM_SEARCH
) && !strcasecmp(mbox
, vmu
->mailbox
))
7119 if (context
&& (!strcasecmp(context
, vmu
->context
)) && (!strcasecmp(mbox
, vmu
->mailbox
)))
7124 if ((vmu
= ast_calloc(1, sizeof(*vmu
)))) {
7125 ast_copy_string(vmu
->context
, context
, sizeof(vmu
->context
));
7126 ast_copy_string(vmu
->mailbox
, mbox
, sizeof(vmu
->mailbox
));
7127 AST_LIST_INSERT_TAIL(&users
, vmu
, list
);
7133 static int append_mailbox(char *context
, char *mbox
, char *data
)
7135 /* Assumes lock is already held */
7139 struct ast_vm_user
*vmu
;
7141 tmp
= ast_strdupa(data
);
7143 if ((vmu
= find_or_create(context
, mbox
))) {
7144 populate_defaults(vmu
);
7147 if ((s
= strsep(&stringp
, ",")))
7148 ast_copy_string(vmu
->password
, s
, sizeof(vmu
->password
));
7149 if (stringp
&& (s
= strsep(&stringp
, ",")))
7150 ast_copy_string(vmu
->fullname
, s
, sizeof(vmu
->fullname
));
7151 if (stringp
&& (s
= strsep(&stringp
, ",")))
7152 ast_copy_string(vmu
->email
, s
, sizeof(vmu
->email
));
7153 if (stringp
&& (s
= strsep(&stringp
, ",")))
7154 ast_copy_string(vmu
->pager
, s
, sizeof(vmu
->pager
));
7155 if (stringp
&& (s
= strsep(&stringp
, ",")))
7156 apply_options(vmu
, s
);
7161 static int vm_box_exists(struct ast_channel
*chan
, void *data
)
7163 struct ast_module_user
*u
;
7164 struct ast_vm_user svm
;
7165 char *context
, *box
;
7166 int priority_jump
= 0;
7167 AST_DECLARE_APP_ARGS(args
,
7169 AST_APP_ARG(options
);
7172 if (ast_strlen_zero(data
)) {
7173 ast_log(LOG_ERROR
, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
7177 u
= ast_module_user_add(chan
);
7179 box
= ast_strdupa(data
);
7181 AST_STANDARD_APP_ARGS(args
, box
);
7184 if (strchr(args
.options
, 'j'))
7188 if ((context
= strchr(args
.mbox
, '@'))) {
7193 if (find_user(&svm
, context
, args
.mbox
)) {
7194 pbx_builtin_setvar_helper(chan
, "VMBOXEXISTSSTATUS", "SUCCESS");
7195 if (priority_jump
|| ast_opt_priority_jumping
)
7196 if (ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101))
7197 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);
7199 pbx_builtin_setvar_helper(chan
, "VMBOXEXISTSSTATUS", "FAILED");
7200 ast_module_user_remove(u
);
7204 static int vmauthenticate(struct ast_channel
*chan
, void *data
)
7206 struct ast_module_user
*u
;
7207 char *s
= data
, *user
=NULL
, *context
=NULL
, mailbox
[AST_MAX_EXTENSION
] = "";
7208 struct ast_vm_user vmus
;
7209 char *options
= NULL
;
7210 int silent
= 0, skipuser
= 0;
7213 u
= ast_module_user_add(chan
);
7217 user
= strsep(&s
, "|");
7218 options
= strsep(&s
, "|");
7221 user
= strsep(&s
, "@");
7222 context
= strsep(&s
, "");
7223 if (!ast_strlen_zero(user
))
7225 ast_copy_string(mailbox
, user
, sizeof(mailbox
));
7230 silent
= (strchr(options
, 's')) != NULL
;
7233 if (!vm_authenticate(chan
, mailbox
, sizeof(mailbox
), &vmus
, context
, NULL
, skipuser
, 3, silent
)) {
7234 pbx_builtin_setvar_helper(chan
, "AUTH_MAILBOX", mailbox
);
7235 pbx_builtin_setvar_helper(chan
, "AUTH_CONTEXT", vmus
.context
);
7236 ast_play_and_wait(chan
, "auth-thankyou");
7240 ast_module_user_remove(u
);
7244 static char voicemail_show_users_help
[] =
7245 "Usage: voicemail show users [for <context>]\n"
7246 " Lists all mailboxes currently set up\n";
7248 static char voicemail_show_zones_help
[] =
7249 "Usage: voicemail show zones\n"
7250 " Lists zone message formats\n";
7252 static int handle_voicemail_show_users(int fd
, int argc
, char *argv
[])
7254 struct ast_vm_user
*vmu
;
7255 char *output_format
= "%-10s %-5s %-25s %-10s %6s\n";
7257 if ((argc
< 3) || (argc
> 5) || (argc
== 4)) return RESULT_SHOWUSAGE
;
7258 else if ((argc
== 5) && strcmp(argv
[3],"for")) return RESULT_SHOWUSAGE
;
7260 AST_LIST_LOCK(&users
);
7261 if (!AST_LIST_EMPTY(&users
)) {
7263 ast_cli(fd
, output_format
, "Context", "Mbox", "User", "Zone", "NewMsg");
7266 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
7267 if (!strcmp(argv
[4],vmu
->context
))
7271 ast_cli(fd
, output_format
, "Context", "Mbox", "User", "Zone", "NewMsg");
7273 ast_cli(fd
, "No such voicemail context \"%s\"\n", argv
[4]);
7274 AST_LIST_UNLOCK(&users
);
7275 return RESULT_FAILURE
;
7278 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
7279 int newmsgs
= 0, oldmsgs
= 0;
7280 char count
[12], tmp
[256] = "";
7282 if ((argc
== 3) || ((argc
== 5) && !strcmp(argv
[4],vmu
->context
))) {
7283 snprintf(tmp
, sizeof(tmp
), "%s@%s", vmu
->mailbox
, ast_strlen_zero(vmu
->context
) ? "default" : vmu
->context
);
7284 inboxcount(tmp
, &newmsgs
, &oldmsgs
);
7285 snprintf(count
,sizeof(count
),"%d",newmsgs
);
7286 ast_cli(fd
, output_format
, vmu
->context
, vmu
->mailbox
, vmu
->fullname
, vmu
->zonetag
, count
);
7290 ast_cli(fd
, "There are no voicemail users currently defined\n");
7291 AST_LIST_UNLOCK(&users
);
7292 return RESULT_FAILURE
;
7294 AST_LIST_UNLOCK(&users
);
7295 return RESULT_SUCCESS
;
7298 static int handle_voicemail_show_zones(int fd
, int argc
, char *argv
[])
7300 struct vm_zone
*zone
;
7301 char *output_format
= "%-15s %-20s %-45s\n";
7302 int res
= RESULT_SUCCESS
;
7305 return RESULT_SHOWUSAGE
;
7307 AST_LIST_LOCK(&zones
);
7308 if (!AST_LIST_EMPTY(&zones
)) {
7309 ast_cli(fd
, output_format
, "Zone", "Timezone", "Message Format");
7310 AST_LIST_TRAVERSE(&zones
, zone
, list
) {
7311 ast_cli(fd
, output_format
, zone
->name
, zone
->timezone
, zone
->msg_format
);
7314 ast_cli(fd
, "There are no voicemail zones currently defined\n");
7315 res
= RESULT_FAILURE
;
7317 AST_LIST_UNLOCK(&zones
);
7322 static char *complete_voicemail_show_users(const char *line
, const char *word
, int pos
, int state
)
7326 struct ast_vm_user
*vmu
;
7327 const char *context
= "";
7329 /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
7333 return (state
== 0) ? ast_strdup("for") : NULL
;
7334 wordlen
= strlen(word
);
7335 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
7336 if (!strncasecmp(word
, vmu
->context
, wordlen
)) {
7337 if (context
&& strcmp(context
, vmu
->context
) && ++which
> state
)
7338 return ast_strdup(vmu
->context
);
7339 /* ignore repeated contexts ? */
7340 context
= vmu
->context
;
7346 static struct ast_cli_entry cli_show_voicemail_users_deprecated
= {
7347 { "show", "voicemail", "users", NULL
},
7348 handle_voicemail_show_users
, NULL
,
7349 NULL
, complete_voicemail_show_users
};
7351 static struct ast_cli_entry cli_show_voicemail_zones_deprecated
= {
7352 { "show", "voicemail", "zones", NULL
},
7353 handle_voicemail_show_zones
, NULL
,
7356 static struct ast_cli_entry cli_voicemail
[] = {
7357 { { "voicemail", "show", "users", NULL
},
7358 handle_voicemail_show_users
, "List defined voicemail boxes",
7359 voicemail_show_users_help
, complete_voicemail_show_users
, &cli_show_voicemail_users_deprecated
},
7361 { { "voicemail", "show", "zones", NULL
},
7362 handle_voicemail_show_zones
, "List zone message formats",
7363 voicemail_show_zones_help
, NULL
, &cli_show_voicemail_zones_deprecated
},
7366 static int load_config(void)
7368 struct ast_vm_user
*cur
;
7369 struct vm_zone
*zcur
;
7370 struct ast_config
*cfg
, *ucfg
;
7372 struct ast_variable
*var
;
7373 const char *notifystr
= NULL
;
7374 const char *smdistr
= NULL
;
7375 const char *astattach
;
7376 const char *astsearch
;
7377 const char *astsaycid
;
7378 const char *send_voicemail
;
7380 const char *imap_server
;
7381 const char *imap_port
;
7382 const char *imap_flags
;
7383 const char *imap_folder
;
7384 const char *auth_user
;
7385 const char *auth_password
;
7386 const char *expunge_on_hangup
;
7388 const char *astcallop
;
7389 const char *astreview
;
7390 const char *asttempgreetwarn
;
7391 const char *astskipcmd
;
7392 const char *asthearenv
;
7393 const char *astsaydurationinfo
;
7394 const char *astsaydurationminfo
;
7395 const char *silencestr
;
7396 const char *maxmsgstr
;
7397 const char *astdirfwd
;
7398 const char *thresholdstr
;
7400 const char *astemail
;
7401 const char *ucontext
;
7402 const char *astmailcmd
= SENDMAIL
;
7403 const char *astforcename
;
7404 const char *astforcegreet
;
7407 const char *dialoutcxt
= NULL
;
7408 const char *callbackcxt
= NULL
;
7409 const char *exitcxt
= NULL
;
7411 const char *emaildateformatstr
;
7412 const char *volgainstr
;
7416 cfg
= ast_config_load(VOICEMAIL_CONFIG
);
7418 AST_LIST_LOCK(&users
);
7419 while ((cur
= AST_LIST_REMOVE_HEAD(&users
, list
))) {
7420 ast_set_flag(cur
, VM_ALLOCED
);
7424 AST_LIST_LOCK(&zones
);
7425 while ((zcur
= AST_LIST_REMOVE_HEAD(&zones
, list
)))
7427 AST_LIST_UNLOCK(&zones
);
7429 memset(ext_pass_cmd
, 0, sizeof(ext_pass_cmd
));
7432 /* General settings */
7434 if (!(ucontext
= ast_variable_retrieve(cfg
, "general", "userscontext")))
7435 ucontext
= "default";
7436 ast_copy_string(userscontext
, ucontext
, sizeof(userscontext
));
7437 /* Attach voice message to mail message ? */
7438 if (!(astattach
= ast_variable_retrieve(cfg
, "general", "attach")))
7440 ast_set2_flag((&globalflags
), ast_true(astattach
), VM_ATTACH
);
7442 if (!(astsearch
= ast_variable_retrieve(cfg
, "general", "searchcontexts")))
7444 ast_set2_flag((&globalflags
), ast_true(astsearch
), VM_SEARCH
);
7447 if ((volgainstr
= ast_variable_retrieve(cfg
, "general", "volgain")))
7448 sscanf(volgainstr
, "%lf", &volgain
);
7451 strcpy(odbc_database
, "asterisk");
7452 if ((thresholdstr
= ast_variable_retrieve(cfg
, "general", "odbcstorage"))) {
7453 ast_copy_string(odbc_database
, thresholdstr
, sizeof(odbc_database
));
7455 strcpy(odbc_table
, "voicemessages");
7456 if ((thresholdstr
= ast_variable_retrieve(cfg
, "general", "odbctable"))) {
7457 ast_copy_string(odbc_table
, thresholdstr
, sizeof(odbc_table
));
7461 strcpy(mailcmd
, SENDMAIL
);
7462 if ((astmailcmd
= ast_variable_retrieve(cfg
, "general", "mailcmd")))
7463 ast_copy_string(mailcmd
, astmailcmd
, sizeof(mailcmd
)); /* User setting */
7466 if ((silencestr
= ast_variable_retrieve(cfg
, "general", "maxsilence"))) {
7467 maxsilence
= atoi(silencestr
);
7472 if (!(maxmsgstr
= ast_variable_retrieve(cfg
, "general", "maxmsg"))) {
7475 maxmsg
= atoi(maxmsgstr
);
7477 ast_log(LOG_WARNING
, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr
, MAXMSG
);
7479 } else if (maxmsg
> MAXMSGLIMIT
) {
7480 ast_log(LOG_WARNING
, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT
, maxmsgstr
);
7481 maxmsg
= MAXMSGLIMIT
;
7485 /* Load date format config for voicemail mail */
7486 if ((emaildateformatstr
= ast_variable_retrieve(cfg
, "general", "emaildateformat"))) {
7487 ast_copy_string(emaildateformat
, emaildateformatstr
, sizeof(emaildateformat
));
7490 /* External password changing command */
7491 if ((extpc
= ast_variable_retrieve(cfg
, "general", "externpass"))) {
7492 ast_copy_string(ext_pass_cmd
,extpc
,sizeof(ext_pass_cmd
));
7495 /* IMAP server address */
7496 if ((imap_server
= ast_variable_retrieve(cfg
, "general", "imapserver"))) {
7497 ast_copy_string(imapserver
, imap_server
, sizeof(imapserver
));
7499 ast_copy_string(imapserver
,"localhost", sizeof(imapserver
));
7501 /* IMAP server port */
7502 if ((imap_port
= ast_variable_retrieve(cfg
, "general", "imapport"))) {
7503 ast_copy_string(imapport
, imap_port
, sizeof(imapport
));
7505 ast_copy_string(imapport
,"143", sizeof(imapport
));
7507 /* IMAP server flags */
7508 if ((imap_flags
= ast_variable_retrieve(cfg
, "general", "imapflags"))) {
7509 ast_copy_string(imapflags
, imap_flags
, sizeof(imapflags
));
7511 /* IMAP server master username */
7512 if ((auth_user
= ast_variable_retrieve(cfg
, "general", "authuser"))) {
7513 ast_copy_string(authuser
, auth_user
, sizeof(authuser
));
7515 /* IMAP server master password */
7516 if ((auth_password
= ast_variable_retrieve(cfg
, "general", "authpassword"))) {
7517 ast_copy_string(authpassword
, auth_password
, sizeof(authpassword
));
7519 /* Expunge on exit */
7520 if ((expunge_on_hangup
= ast_variable_retrieve(cfg
, "general", "expungeonhangup"))) {
7521 if (ast_false(expunge_on_hangup
))
7522 expungeonhangup
= 0;
7524 expungeonhangup
= 1;
7526 expungeonhangup
= 1;
7528 /* IMAP voicemail folder */
7529 if ((imap_folder
= ast_variable_retrieve(cfg
, "general", "imapfolder"))) {
7530 ast_copy_string(imapfolder
, imap_folder
, sizeof(imapfolder
));
7532 ast_copy_string(imapfolder
,"INBOX", sizeof(imapfolder
));
7535 /* External voicemail notify application */
7537 if ((notifystr
= ast_variable_retrieve(cfg
, "general", "externnotify"))) {
7538 ast_copy_string(externnotify
, notifystr
, sizeof(externnotify
));
7539 if (option_debug
> 2)
7540 ast_log(LOG_DEBUG
, "found externnotify: %s\n", externnotify
);
7541 if (!strcasecmp(externnotify
, "smdi")) {
7543 ast_log(LOG_DEBUG
, "Using SMDI for external voicemail notification\n");
7544 if ((smdistr
= ast_variable_retrieve(cfg
, "general", "smdiport"))) {
7545 smdi_iface
= ast_smdi_interface_find(smdistr
);
7548 ast_log(LOG_DEBUG
, "No SMDI interface set, trying default (/dev/ttyS0)\n");
7549 smdi_iface
= ast_smdi_interface_find("/dev/ttyS0");
7553 ast_log(LOG_ERROR
, "No valid SMDI interface specfied, disabling external voicemail notification\n");
7554 externnotify
[0] = '\0';
7558 externnotify
[0] = '\0';
7561 /* Silence treshold */
7562 silencethreshold
= 256;
7563 if ((thresholdstr
= ast_variable_retrieve(cfg
, "general", "silencethreshold")))
7564 silencethreshold
= atoi(thresholdstr
);
7566 if (!(astemail
= ast_variable_retrieve(cfg
, "general", "serveremail")))
7567 astemail
= ASTERISK_USERNAME
;
7568 ast_copy_string(serveremail
, astemail
, sizeof(serveremail
));
7571 if ((s
= ast_variable_retrieve(cfg
, "general", "maxmessage"))) {
7572 if (sscanf(s
, "%d", &x
) == 1) {
7575 ast_log(LOG_WARNING
, "Invalid max message time length\n");
7580 if ((s
= ast_variable_retrieve(cfg
, "general", "minmessage"))) {
7581 if (sscanf(s
, "%d", &x
) == 1) {
7583 if (maxsilence
<= vmminmessage
)
7584 ast_log(LOG_WARNING
, "maxsilence should be less than minmessage or you may get empty messages\n");
7586 ast_log(LOG_WARNING
, "Invalid min message time length\n");
7589 fmt
= ast_variable_retrieve(cfg
, "general", "format");
7592 ast_copy_string(vmfmts
, fmt
, sizeof(vmfmts
));
7595 if ((s
= ast_variable_retrieve(cfg
, "general", "maxgreet"))) {
7596 if (sscanf(s
, "%d", &x
) == 1) {
7599 ast_log(LOG_WARNING
, "Invalid max message greeting length\n");
7603 if ((s
= ast_variable_retrieve(cfg
, "general", "skipms"))) {
7604 if (sscanf(s
, "%d", &x
) == 1) {
7607 ast_log(LOG_WARNING
, "Invalid skipms value\n");
7612 if ((s
= ast_variable_retrieve(cfg
, "general", "maxlogins"))) {
7613 if (sscanf(s
, "%d", &x
) == 1) {
7616 ast_log(LOG_WARNING
, "Invalid max failed login attempts\n");
7620 /* Force new user to record name ? */
7621 if (!(astforcename
= ast_variable_retrieve(cfg
, "general", "forcename")))
7622 astforcename
= "no";
7623 ast_set2_flag((&globalflags
), ast_true(astforcename
), VM_FORCENAME
);
7625 /* Force new user to record greetings ? */
7626 if (!(astforcegreet
= ast_variable_retrieve(cfg
, "general", "forcegreetings")))
7627 astforcegreet
= "no";
7628 ast_set2_flag((&globalflags
), ast_true(astforcegreet
), VM_FORCEGREET
);
7630 if ((s
= ast_variable_retrieve(cfg
, "general", "cidinternalcontexts"))){
7631 if (option_debug
> 2)
7632 ast_log(LOG_DEBUG
,"VM_CID Internal context string: %s\n",s
);
7633 stringp
= ast_strdupa(s
);
7634 for (x
= 0 ; x
< MAX_NUM_CID_CONTEXTS
; x
++){
7635 if (!ast_strlen_zero(stringp
)) {
7636 q
= strsep(&stringp
,",");
7637 while ((*q
== ' ')||(*q
== '\t')) /* Eat white space between contexts */
7639 ast_copy_string(cidinternalcontexts
[x
], q
, sizeof(cidinternalcontexts
[x
]));
7640 if (option_debug
> 2)
7641 ast_log(LOG_DEBUG
,"VM_CID Internal context %d: %s\n", x
, cidinternalcontexts
[x
]);
7643 cidinternalcontexts
[x
][0] = '\0';
7647 if (!(astreview
= ast_variable_retrieve(cfg
, "general", "review"))){
7649 ast_log(LOG_DEBUG
,"VM Review Option disabled globally\n");
7652 ast_set2_flag((&globalflags
), ast_true(astreview
), VM_REVIEW
);
7654 /*Temperary greeting reminder */
7655 if (!(asttempgreetwarn
= ast_variable_retrieve(cfg
, "general", "tempgreetwarn"))) {
7657 ast_log(LOG_DEBUG
, "VM Temperary Greeting Reminder Option disabled globally\n");
7658 asttempgreetwarn
= "no";
7661 ast_log(LOG_DEBUG
, "VM Temperary Greeting Reminder Option enabled globally\n");
7663 ast_set2_flag((&globalflags
), ast_true(asttempgreetwarn
), VM_TEMPGREETWARN
);
7665 if (!(astcallop
= ast_variable_retrieve(cfg
, "general", "operator"))){
7667 ast_log(LOG_DEBUG
,"VM Operator break disabled globally\n");
7670 ast_set2_flag((&globalflags
), ast_true(astcallop
), VM_OPERATOR
);
7672 if (!(astsaycid
= ast_variable_retrieve(cfg
, "general", "saycid"))) {
7674 ast_log(LOG_DEBUG
,"VM CID Info before msg disabled globally\n");
7677 ast_set2_flag((&globalflags
), ast_true(astsaycid
), VM_SAYCID
);
7679 if (!(send_voicemail
= ast_variable_retrieve(cfg
,"general", "sendvoicemail"))){
7681 ast_log(LOG_DEBUG
,"Send Voicemail msg disabled globally\n");
7682 send_voicemail
= "no";
7684 ast_set2_flag((&globalflags
), ast_true(send_voicemail
), VM_SVMAIL
);
7686 if (!(asthearenv
= ast_variable_retrieve(cfg
, "general", "envelope"))) {
7688 ast_log(LOG_DEBUG
,"ENVELOPE before msg enabled globally\n");
7691 ast_set2_flag((&globalflags
), ast_true(asthearenv
), VM_ENVELOPE
);
7693 if (!(astsaydurationinfo
= ast_variable_retrieve(cfg
, "general", "sayduration"))) {
7695 ast_log(LOG_DEBUG
,"Duration info before msg enabled globally\n");
7696 astsaydurationinfo
= "yes";
7698 ast_set2_flag((&globalflags
), ast_true(astsaydurationinfo
), VM_SAYDURATION
);
7700 saydurationminfo
= 2;
7701 if ((astsaydurationminfo
= ast_variable_retrieve(cfg
, "general", "saydurationm"))) {
7702 if (sscanf(astsaydurationminfo
, "%d", &x
) == 1) {
7703 saydurationminfo
= x
;
7705 ast_log(LOG_WARNING
, "Invalid min duration for say duration\n");
7709 if (!(astskipcmd
= ast_variable_retrieve(cfg
, "general", "nextaftercmd"))) {
7711 ast_log(LOG_DEBUG
,"We are not going to skip to the next msg after save/delete\n");
7714 ast_set2_flag((&globalflags
), ast_true(astskipcmd
), VM_SKIPAFTERCMD
);
7716 if ((dialoutcxt
= ast_variable_retrieve(cfg
, "general", "dialout"))) {
7717 ast_copy_string(dialcontext
, dialoutcxt
, sizeof(dialcontext
));
7719 ast_log(LOG_DEBUG
, "found dialout context: %s\n", dialcontext
);
7721 dialcontext
[0] = '\0';
7724 if ((callbackcxt
= ast_variable_retrieve(cfg
, "general", "callback"))) {
7725 ast_copy_string(callcontext
, callbackcxt
, sizeof(callcontext
));
7727 ast_log(LOG_DEBUG
, "found callback context: %s\n", callcontext
);
7729 callcontext
[0] = '\0';
7732 if ((exitcxt
= ast_variable_retrieve(cfg
, "general", "exitcontext"))) {
7733 ast_copy_string(exitcontext
, exitcxt
, sizeof(exitcontext
));
7735 ast_log(LOG_DEBUG
, "found operator context: %s\n", exitcontext
);
7737 exitcontext
[0] = '\0';
7740 if (!(astdirfwd
= ast_variable_retrieve(cfg
, "general", "usedirectory")))
7742 ast_set2_flag((&globalflags
), ast_true(astdirfwd
), VM_DIRECFORWARD
);
7743 if ((ucfg
= ast_config_load("users.conf"))) {
7744 for (cat
= ast_category_browse(ucfg
, NULL
); cat
; cat
= ast_category_browse(ucfg
, cat
)) {
7745 if (!ast_true(ast_config_option(ucfg
, cat
, "hasvoicemail")))
7747 if ((cur
= find_or_create(userscontext
, cat
))) {
7748 populate_defaults(cur
);
7749 apply_options_full(cur
, ast_variable_browse(ucfg
, cat
));
7750 ast_copy_string(cur
->context
, userscontext
, sizeof(cur
->context
));
7753 ast_config_destroy(ucfg
);
7755 cat
= ast_category_browse(cfg
, NULL
);
7757 if (strcasecmp(cat
, "general")) {
7758 var
= ast_variable_browse(cfg
, cat
);
7759 if (strcasecmp(cat
, "zonemessages")) {
7760 /* Process mailboxes in this context */
7762 append_mailbox(cat
, var
->name
, var
->value
);
7766 /* Timezones in this context */
7769 if ((z
= ast_malloc(sizeof(*z
)))) {
7770 char *msg_format
, *timezone
;
7771 msg_format
= ast_strdupa(var
->value
);
7772 timezone
= strsep(&msg_format
, "|");
7774 ast_copy_string(z
->name
, var
->name
, sizeof(z
->name
));
7775 ast_copy_string(z
->timezone
, timezone
, sizeof(z
->timezone
));
7776 ast_copy_string(z
->msg_format
, msg_format
, sizeof(z
->msg_format
));
7777 AST_LIST_LOCK(&zones
);
7778 AST_LIST_INSERT_HEAD(&zones
, z
, list
);
7779 AST_LIST_UNLOCK(&zones
);
7781 ast_log(LOG_WARNING
, "Invalid timezone definition at line %d\n", var
->lineno
);
7786 AST_LIST_UNLOCK(&users
);
7787 ast_config_destroy(cfg
);
7794 cat
= ast_category_browse(cfg
, cat
);
7796 memset(fromstring
,0,sizeof(fromstring
));
7797 memset(pagerfromstring
,0,sizeof(pagerfromstring
));
7798 memset(emailtitle
,0,sizeof(emailtitle
));
7799 strcpy(charset
, "ISO-8859-1");
7806 emailsubject
= NULL
;
7814 pagersubject
= NULL
;
7816 if ((s
= ast_variable_retrieve(cfg
, "general", "pbxskip")))
7817 ast_set2_flag((&globalflags
), ast_true(s
), VM_PBXSKIP
);
7818 if ((s
= ast_variable_retrieve(cfg
, "general", "fromstring")))
7819 ast_copy_string(fromstring
,s
,sizeof(fromstring
));
7820 if ((s
= ast_variable_retrieve(cfg
, "general", "pagerfromstring")))
7821 ast_copy_string(pagerfromstring
,s
,sizeof(pagerfromstring
));
7822 if ((s
= ast_variable_retrieve(cfg
, "general", "charset")))
7823 ast_copy_string(charset
,s
,sizeof(charset
));
7824 if ((s
= ast_variable_retrieve(cfg
, "general", "adsifdn"))) {
7825 sscanf(s
, "%2x%2x%2x%2x", &tmpadsi
[0], &tmpadsi
[1], &tmpadsi
[2], &tmpadsi
[3]);
7826 for (x
= 0; x
< 4; x
++) {
7827 memcpy(&adsifdn
[x
], &tmpadsi
[x
], 1);
7830 if ((s
= ast_variable_retrieve(cfg
, "general", "adsisec"))) {
7831 sscanf(s
, "%2x%2x%2x%2x", &tmpadsi
[0], &tmpadsi
[1], &tmpadsi
[2], &tmpadsi
[3]);
7832 for (x
= 0; x
< 4; x
++) {
7833 memcpy(&adsisec
[x
], &tmpadsi
[x
], 1);
7836 if ((s
= ast_variable_retrieve(cfg
, "general", "adsiver")))
7840 if ((s
= ast_variable_retrieve(cfg
, "general", "emailtitle"))) {
7841 ast_log(LOG_NOTICE
, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
7842 ast_copy_string(emailtitle
,s
,sizeof(emailtitle
));
7844 if ((s
= ast_variable_retrieve(cfg
, "general", "emailsubject")))
7845 emailsubject
= ast_strdup(s
);
7846 if ((s
= ast_variable_retrieve(cfg
, "general", "emailbody"))) {
7847 char *tmpread
, *tmpwrite
;
7848 emailbody
= ast_strdup(s
);
7850 /* substitute strings \t and \n into the appropriate characters */
7851 tmpread
= tmpwrite
= emailbody
;
7852 while ((tmpwrite
= strchr(tmpread
,'\\'))) {
7853 switch (tmpwrite
[1]) {
7855 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7859 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7863 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7867 ast_log(LOG_NOTICE
, "Substitution routine does not support this character: %c\n", tmpwrite
[1]);
7869 tmpread
= tmpwrite
+ 1;
7872 if ((s
= ast_variable_retrieve(cfg
, "general", "pagersubject")))
7873 pagersubject
= ast_strdup(s
);
7874 if ((s
= ast_variable_retrieve(cfg
, "general", "pagerbody"))) {
7875 char *tmpread
, *tmpwrite
;
7876 pagerbody
= ast_strdup(s
);
7878 /* substitute strings \t and \n into the appropriate characters */
7879 tmpread
= tmpwrite
= pagerbody
;
7880 while ((tmpwrite
= strchr(tmpread
, '\\'))) {
7881 switch (tmpwrite
[1]) {
7883 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7887 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7891 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7895 ast_log(LOG_NOTICE
, "Substitution routine does not support this character: %c\n", tmpwrite
[1]);
7897 tmpread
= tmpwrite
+ 1;
7900 AST_LIST_UNLOCK(&users
);
7901 ast_config_destroy(cfg
);
7904 AST_LIST_UNLOCK(&users
);
7905 ast_log(LOG_WARNING
, "Failed to load configuration file.\n");
7910 static int reload(void)
7912 return(load_config());
7915 static int unload_module(void)
7919 res
= ast_unregister_application(app
);
7920 res
|= ast_unregister_application(app2
);
7921 res
|= ast_unregister_application(app3
);
7922 res
|= ast_unregister_application(app4
);
7923 ast_cli_unregister_multiple(cli_voicemail
, sizeof(cli_voicemail
) / sizeof(struct ast_cli_entry
));
7924 ast_uninstall_vm_functions();
7926 ast_module_user_hangup_all();
7931 static int load_module(void)
7934 char *adsi_loaded
= ast_module_helper("", "res_adsi.so", 0, 0, 0, 0);
7937 /* If embedded, res_adsi may be known as "res_adsi" not "res_adsi.so" */
7938 adsi_loaded
= ast_module_helper("", "res_adsi", 0, 0, 0, 0);
7939 ast_free(adsi_loaded
);
7941 ast_log(LOG_ERROR
, "app_voicemail.so depends upon res_adsi.so\n");
7942 return AST_MODULE_LOAD_DECLINE
;
7946 my_umask
= umask(0);
7948 res
= ast_register_application(app
, vm_exec
, synopsis_vm
, descrip_vm
);
7949 res
|= ast_register_application(app2
, vm_execmain
, synopsis_vmain
, descrip_vmain
);
7950 res
|= ast_register_application(app3
, vm_box_exists
, synopsis_vm_box_exists
, descrip_vm_box_exists
);
7951 res
|= ast_register_application(app4
, vmauthenticate
, synopsis_vmauthenticate
, descrip_vmauthenticate
);
7955 if ((res
=load_config())) {
7959 ast_cli_register_multiple(cli_voicemail
, sizeof(cli_voicemail
) / sizeof(struct ast_cli_entry
));
7961 /* compute the location of the voicemail spool directory */
7962 snprintf(VM_SPOOL_DIR
, sizeof(VM_SPOOL_DIR
), "%s/voicemail/", ast_config_AST_SPOOL_DIR
);
7964 ast_install_vm_functions(has_voicemail
, inboxcount
, messagecount
);
7969 static int dialout(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *num
, char *outgoing_context
)
7972 char destination
[80] = "";
7976 if (option_verbose
> 2)
7977 ast_verbose( VERBOSE_PREFIX_3
"Destination number will be entered manually\n");
7978 while (retries
< 3 && cmd
!= 't') {
7979 destination
[1] = '\0';
7980 destination
[0] = cmd
= ast_play_and_wait(chan
,"vm-enter-num-to-call");
7982 destination
[0] = cmd
= ast_play_and_wait(chan
, "vm-then-pound");
7984 destination
[0] = cmd
= ast_play_and_wait(chan
, "vm-star-cancel");
7986 cmd
= ast_waitfordigit(chan
, 6000);
7988 destination
[0] = cmd
;
7997 if (option_verbose
> 2)
7998 ast_verbose( VERBOSE_PREFIX_3
"User hit '*' to cancel outgoing call\n");
8001 if ((cmd
= ast_readstring(chan
,destination
+ strlen(destination
),sizeof(destination
)-1,6000,10000,"#")) < 0)
8012 if (option_verbose
> 2)
8013 ast_verbose( VERBOSE_PREFIX_3
"Destination number is CID number '%s'\n", num
);
8014 ast_copy_string(destination
, num
, sizeof(destination
));
8017 if (!ast_strlen_zero(destination
)) {
8018 if (destination
[strlen(destination
) -1 ] == '*')
8020 if (option_verbose
> 2)
8021 ast_verbose( VERBOSE_PREFIX_3
"Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination
, outgoing_context
, chan
->context
);
8022 ast_copy_string(chan
->exten
, destination
, sizeof(chan
->exten
));
8023 ast_copy_string(chan
->context
, outgoing_context
, sizeof(chan
->context
));
8030 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
)
8034 char origtimeS
[256],cidS
[256],contextS
[256];
8035 char *header_content
,*temp
;
8037 char filename
[PATH_MAX
];
8038 struct ast_config
*msg_cfg
= NULL
;
8039 const char *origtime
, *context
;
8040 char *cid
, *name
, *num
;
8046 /* get the message info!! */
8047 if (option_debug
> 2)
8048 ast_log (LOG_DEBUG
,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms
->curmsg
, vms
->msgArray
[vms
->curmsg
]);
8049 if (vms
->msgArray
[vms
->curmsg
] == 0) {
8050 ast_log (LOG_WARNING
,"Trying to access unknown message\n");
8054 /* This will only work for new messages... */
8055 header_content
= mail_fetchheader (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
]);
8056 /* empty string means no valid header */
8057 if (ast_strlen_zero(header_content
)) {
8058 ast_log (LOG_ERROR
,"Could not fetch header for message number %ld\n",vms
->msgArray
[vms
->curmsg
]);
8062 /* Get info from headers!! */
8063 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Caller-ID-Num:");
8066 ast_copy_string(cidS
,temp
, sizeof(cidS
));
8071 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Context:");
8074 ast_copy_string(contextS
,temp
, sizeof(contextS
));
8078 context
= &contextS
[0];
8079 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Orig-time:");
8082 ast_copy_string(origtimeS
,temp
, sizeof(origtimeS
));
8084 origtimeS
[0] = '\0';
8086 origtime
= &origtimeS
[0];
8088 ast_copy_string(filename
, "IMAP_STORAGE", sizeof(filename
));
8090 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, msg
);
8092 /* Retrieve info from VM attribute file */
8094 make_file(vms
->fn2
, sizeof(vms
->fn2
), vms
->curdir
, vms
->curmsg
);
8095 snprintf(filename
,sizeof(filename
), "%s.txt", vms
->fn2
);
8096 RETRIEVE(vms
->curdir
, vms
->curmsg
);
8097 msg_cfg
= ast_config_load(filename
);
8098 DISPOSE(vms
->curdir
, vms
->curmsg
);
8100 ast_log(LOG_WARNING
, "No message attribute file?!! (%s)\n", filename
);
8104 if (!(origtime
= ast_variable_retrieve(msg_cfg
, "message", "origtime"))) {
8105 ast_config_destroy(msg_cfg
);
8109 cid
= ast_strdupa(ast_variable_retrieve(msg_cfg
, "message", "callerid"));
8111 context
= ast_variable_retrieve(msg_cfg
, "message", "context");
8112 if (!strncasecmp("macro",context
,5)) /* Macro names in contexts are useless for our needs */
8113 context
= ast_variable_retrieve(msg_cfg
, "message","macrocontext");
8118 res
= play_message_datetime(chan
, vmu
, origtime
, filename
);
8120 res
= play_message_callerid(chan
, vms
, cid
, context
, 0);
8125 case 2: /* Call back */
8127 if (ast_strlen_zero(cid
))
8130 ast_callerid_parse(cid
, &name
, &num
);
8131 while ((res
> -1) && (res
!= 't')) {
8135 /* Dial the CID number */
8136 res
= dialout(chan
, vmu
, num
, vmu
->callback
);
8138 ast_config_destroy(msg_cfg
);
8147 /* Want to enter a different number, can only do this if there's a dialout context for this user */
8148 if (!ast_strlen_zero(vmu
->dialout
)) {
8149 res
= dialout(chan
, vmu
, NULL
, vmu
->dialout
);
8151 ast_config_destroy(msg_cfg
);
8155 if (option_verbose
> 2)
8156 ast_verbose( VERBOSE_PREFIX_3
"Caller can not specify callback number - no dialout context available\n");
8157 res
= ast_play_and_wait(chan
, "vm-sorry");
8159 ast_config_destroy(msg_cfg
);
8173 res
= ast_play_and_wait(chan
, "vm-sorry");
8178 if (option_verbose
> 2)
8179 ast_verbose( VERBOSE_PREFIX_3
"Confirm CID number '%s' is number to use for callback\n", num
);
8180 res
= ast_play_and_wait(chan
, "vm-num-i-have");
8182 res
= play_message_callerid(chan
, vms
, num
, vmu
->context
, 1);
8184 res
= ast_play_and_wait(chan
, "vm-tocallnum");
8185 /* Only prompt for a caller-specified number if there is a dialout context specified */
8186 if (!ast_strlen_zero(vmu
->dialout
)) {
8188 res
= ast_play_and_wait(chan
, "vm-calldiffnum");
8191 res
= ast_play_and_wait(chan
, "vm-nonumber");
8192 if (!ast_strlen_zero(vmu
->dialout
)) {
8194 res
= ast_play_and_wait(chan
, "vm-toenternumber");
8198 res
= ast_play_and_wait(chan
, "vm-star-cancel");
8200 res
= ast_waitfordigit(chan
, 6000);
8211 else if (res
== '*')
8217 /* Send reply directly to sender */
8218 if (ast_strlen_zero(cid
))
8221 ast_callerid_parse(cid
, &name
, &num
);
8223 if (option_verbose
> 2)
8224 ast_verbose(VERBOSE_PREFIX_3
"No CID number available, no reply sent\n");
8226 res
= ast_play_and_wait(chan
, "vm-nonumber");
8227 ast_config_destroy(msg_cfg
);
8230 struct ast_vm_user vmu2
;
8231 if (find_user(&vmu2
, vmu
->context
, num
)) {
8232 struct leave_vm_options leave_options
;
8233 char mailbox
[AST_MAX_EXTENSION
* 2 + 2];
8234 snprintf(mailbox
, sizeof(mailbox
), "%s@%s", num
, vmu
->context
);
8236 if (option_verbose
> 2)
8237 ast_verbose(VERBOSE_PREFIX_3
"Leaving voicemail for '%s' in context '%s'\n", num
, vmu
->context
);
8239 memset(&leave_options
, 0, sizeof(leave_options
));
8240 leave_options
.record_gain
= record_gain
;
8241 res
= leave_voicemail(chan
, mailbox
, &leave_options
);
8244 ast_config_destroy(msg_cfg
);
8247 /* Sender has no mailbox, can't reply */
8248 if (option_verbose
> 2)
8249 ast_verbose( VERBOSE_PREFIX_3
"No mailbox number '%s' in context '%s', no reply sent\n", num
, vmu
->context
);
8250 ast_play_and_wait(chan
, "vm-nobox");
8252 ast_config_destroy(msg_cfg
);
8261 #ifndef IMAP_STORAGE
8262 ast_config_destroy(msg_cfg
);
8265 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, msg
);
8266 vms
->heard
[msg
] = 1;
8267 res
= wait_file(chan
, vms
, vms
->fn
);
8273 static int play_record_review(struct ast_channel
*chan
, char *playfile
, char *recordfile
, int maxtime
, char *fmt
,
8274 int outsidecaller
, struct ast_vm_user
*vmu
, int *duration
, const char *unlockdir
,
8275 signed char record_gain
, struct vm_state
*vms
)
8277 /* Record message & let caller review or re-record it, or set options if applicable */
8280 int max_attempts
= 3;
8283 int message_exists
= 0;
8284 signed char zero_gain
= 0;
8285 char tempfile
[PATH_MAX
];
8286 char *acceptdtmf
= "#";
8287 char *canceldtmf
= "";
8289 /* Note that urgent and private are for flagging messages as such in the future */
8291 /* barf if no pointer passed to store duration in */
8292 if (duration
== NULL
) {
8293 ast_log(LOG_WARNING
, "Error play_record_review called without duration pointer\n");
8298 snprintf(tempfile
, sizeof(tempfile
), "%s.tmp", recordfile
);
8300 ast_copy_string(tempfile
, recordfile
, sizeof(tempfile
));
8302 cmd
= '3'; /* Want to start by recording */
8304 while ((cmd
>= 0) && (cmd
!= 't')) {
8307 if (!message_exists
) {
8308 /* In this case, 1 is to record a message */
8312 /* Otherwise 1 is to save the existing message */
8313 if (option_verbose
> 2)
8314 ast_verbose(VERBOSE_PREFIX_3
"Saving message as is\n");
8316 ast_filerename(tempfile
, recordfile
, NULL
);
8317 ast_stream_and_wait(chan
, "vm-msgsaved", chan
->language
, "");
8318 if (!outsidecaller
) {
8319 STORE(recordfile
, vmu
->mailbox
, vmu
->context
, -1, chan
, vmu
, fmt
, *duration
, vms
);
8320 DISPOSE(recordfile
, -1);
8327 if (option_verbose
> 2)
8328 ast_verbose(VERBOSE_PREFIX_3
"Reviewing the message\n");
8329 cmd
= ast_stream_and_wait(chan
, tempfile
, chan
->language
, AST_DIGIT_ANY
);
8334 if (recorded
== 1) {
8335 if (option_verbose
> 2)
8336 ast_verbose(VERBOSE_PREFIX_3
"Re-recording the message\n");
8338 if (option_verbose
> 2)
8339 ast_verbose(VERBOSE_PREFIX_3
"Recording the message\n");
8341 if (recorded
&& outsidecaller
) {
8342 cmd
= ast_play_and_wait(chan
, INTRO
);
8343 cmd
= ast_play_and_wait(chan
, "beep");
8346 /* 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 */
8348 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &record_gain
, sizeof(record_gain
), 0);
8349 if (ast_test_flag(vmu
, VM_OPERATOR
))
8351 cmd
= ast_play_and_record_full(chan
, playfile
, tempfile
, maxtime
, fmt
, duration
, silencethreshold
, maxsilence
, unlockdir
, acceptdtmf
, canceldtmf
);
8353 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &zero_gain
, sizeof(zero_gain
), 0);
8355 /* User has hung up, no options to give */
8356 if (!outsidecaller
) {
8357 /* user was recording a greeting and they hung up, so let's delete the recording. */
8358 ast_filedelete(tempfile
, NULL
);
8364 } else if (cmd
== '*') {
8368 else if (vmu
->review
&& (*duration
< 5)) {
8369 /* Message is too short */
8370 if (option_verbose
> 2)
8371 ast_verbose(VERBOSE_PREFIX_3
"Message too short\n");
8372 cmd
= ast_play_and_wait(chan
, "vm-tooshort");
8373 cmd
= ast_filedelete(tempfile
, NULL
);
8376 else if (vmu
->review
&& (cmd
== 2 && *duration
< (maxsilence
+ 3))) {
8377 /* Message is all silence */
8378 if (option_verbose
> 2)
8379 ast_verbose(VERBOSE_PREFIX_3
"Nothing recorded\n");
8380 cmd
= ast_filedelete(tempfile
, NULL
);
8381 cmd
= ast_play_and_wait(chan
, "vm-nothingrecorded");
8383 cmd
= ast_play_and_wait(chan
, "vm-speakup");
8388 /* If all is well, a message exists */
8401 cmd
= ast_play_and_wait(chan
, "vm-sorry");
8404 /* XXX Commented out for the moment because of the dangers of deleting
8405 a message while recording (can put the message numbers out of sync) */
8407 /* Cancel recording, delete message, offer to take another message*/
8408 cmd
= ast_play_and_wait(chan
, "vm-deleted");
8409 cmd
= ast_filedelete(tempfile
, NULL
);
8410 if (outsidecaller
) {
8411 res
= vm_exec(chan
, NULL
);
8418 if (!ast_test_flag(vmu
, VM_OPERATOR
)) {
8419 cmd
= ast_play_and_wait(chan
, "vm-sorry");
8422 if (message_exists
|| recorded
) {
8423 cmd
= ast_play_and_wait(chan
, "vm-saveoper");
8425 cmd
= ast_waitfordigit(chan
, 3000);
8427 ast_play_and_wait(chan
, "vm-msgsaved");
8430 ast_play_and_wait(chan
, "vm-deleted");
8431 DELETE(recordfile
, -1, recordfile
);
8437 /* If the caller is an ouside caller, and the review option is enabled,
8438 allow them to review the message, but let the owner of the box review
8440 if (outsidecaller
&& !ast_test_flag(vmu
, VM_REVIEW
))
8442 if (message_exists
) {
8443 cmd
= ast_play_and_wait(chan
, "vm-review");
8446 cmd
= ast_play_and_wait(chan
, "vm-torerecord");
8448 cmd
= ast_waitfordigit(chan
, 600);
8451 if (!cmd
&& outsidecaller
&& ast_test_flag(vmu
, VM_OPERATOR
)) {
8452 cmd
= ast_play_and_wait(chan
, "vm-reachoper");
8454 cmd
= ast_waitfordigit(chan
, 600);
8458 cmd
= ast_play_and_wait(chan
, "vm-tocancelmsg");
8461 cmd
= ast_waitfordigit(chan
, 6000);
8465 if (attempts
> max_attempts
) {
8471 ast_play_and_wait(chan
, "vm-goodbye");
8479 static void write_file(char *filename
, char *buffer
, unsigned long len
)
8483 output
= fopen (filename
, "w");
8484 fwrite (buffer
, len
, 1, output
);
8488 void mm_searched(MAILSTREAM
*stream
, unsigned long number
)
8490 struct vm_state
*vms
;
8493 mailbox
= stream
->mailbox
;
8494 user
= get_user_by_mailbox(mailbox
);
8495 vms
= get_vm_state_by_imapuser(user
,2);
8497 if (option_debug
> 2)
8498 ast_log (LOG_DEBUG
, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number
,vms
->vmArrayIndex
,vms
->interactive
);
8499 vms
->msgArray
[vms
->vmArrayIndex
++] = number
;
8501 ast_log (LOG_ERROR
, "No state found.\n");
8506 #if 0 /*No need for this. */
8508 * Accepts: MAIL stream
8510 static void status(MAILSTREAM
*stream
)
8513 char *s
, date
[MAILTMPLEN
];
8515 AUTHENTICATOR
*auth
;
8517 ast_log (LOG_NOTICE
,"%s\n",date
);
8519 if (stream
->mailbox
)
8520 ast_log (LOG_NOTICE
," %s mailbox: %s, %lu messages, %lu recent\n",
8521 stream
->dtb
->name
, stream
->mailbox
, stream
->nmsgs
,stream
->recent
);
8523 ast_log (LOG_NOTICE
,"No mailbox is open on this stream\n");
8524 if (stream
->user_flags
[0]) {
8525 ast_log (LOG_NOTICE
,"Keywords: %s\n", stream
->user_flags
[0]);
8526 for (i
= 1; i
< NUSERFLAGS
&& stream
->user_flags
[i
]; ++i
)
8527 ast_log (LOG_NOTICE
," %s\n", stream
->user_flags
[i
]);
8529 if (!strcmp (stream
->dtb
->name
, "imap")) {
8530 if (LEVELIMAP4rev1 (stream
))
8531 s
= "IMAP4rev1 (RFC 3501)";
8532 else if (LEVEL1730 (stream
))
8533 s
= "IMAP4 (RFC 1730)";
8534 else if (LEVELIMAP2bis (stream
))
8536 else if (LEVEL1176 (stream
))
8537 s
= "IMAP2 (RFC 1176)";
8539 s
= "IMAP2 (RFC 1064)";
8540 ast_log (LOG_NOTICE
,"%s server %s\n", s
, imap_host (stream
));
8541 if (LEVELIMAP4 (stream
)) {
8542 if ((i
= (imap_cap(stream
)->auth
))) {
8544 ast_log (LOG_NOTICE
,"Mutually-supported SASL mechanisms:\n");
8545 while ((auth
= mail_lookup_auth (find_rightmost_bit (&i
) + 1))) {
8546 ast_log (LOG_NOTICE
," %s\n", auth
->name
);
8547 if (!strcmp (auth
->name
, "PLAIN"))
8548 s
= "\n [LOGIN will not be listed here if PLAIN is supported]\n";
8550 ast_log (LOG_NOTICE
,s
);
8552 ast_log (LOG_NOTICE
,"Supported standard extensions:\n");
8553 if (LEVELACL (stream
))
8554 ast_log (LOG_NOTICE
," Access Control lists (RFC 2086)\n");
8555 if (LEVELQUOTA (stream
))
8556 ast_log (LOG_NOTICE
," Quotas (RFC 2087)\n");
8557 if (LEVELLITERALPLUS (stream
))
8558 ast_log (LOG_NOTICE
," Non-synchronizing literals (RFC 2088)\n");
8559 if (LEVELIDLE (stream
))
8560 ast_log (LOG_NOTICE
," IDLE unsolicited update (RFC 2177)\n");
8561 if (LEVELMBX_REF (stream
))
8562 ast_log (LOG_NOTICE
," Mailbox referrals (RFC 2193)\n");
8563 if (LEVELLOG_REF (stream
))
8564 ast_log (LOG_NOTICE
," Login referrals (RFC 2221)\n");
8565 if (LEVELANONYMOUS (stream
))
8566 ast_log (LOG_NOTICE
," Anonymous access (RFC 2245)\n");
8567 if (LEVELNAMESPACE (stream
))
8568 ast_log (LOG_NOTICE
," Multiple namespaces (RFC 2342)\n");
8569 if (LEVELUIDPLUS (stream
))
8570 ast_log (LOG_NOTICE
," Extended UID behavior (RFC 2359)\n");
8571 if (LEVELSTARTTLS (stream
))
8572 ast_log (LOG_NOTICE
," Transport Layer Security (RFC 2595)\n");
8573 if (LEVELLOGINDISABLED (stream
))
8574 ast_log (LOG_NOTICE
," LOGIN command disabled (RFC 2595)\n");
8575 if (LEVELID (stream
))
8576 ast_log (LOG_NOTICE
," Implementation identity negotiation (RFC 2971)\n");
8577 if (LEVELCHILDREN (stream
))
8578 ast_log (LOG_NOTICE
," LIST children announcement (RFC 3348)\n");
8579 if (LEVELMULTIAPPEND (stream
))
8580 ast_log (LOG_NOTICE
," Atomic multiple APPEND (RFC 3502)\n");
8581 if (LEVELBINARY (stream
))
8582 ast_log (LOG_NOTICE
," Binary body content (RFC 3516)\n");
8583 ast_log (LOG_NOTICE
,"Supported draft extensions:\n");
8584 if (LEVELUNSELECT (stream
))
8585 ast_log (LOG_NOTICE
," Mailbox unselect\n");
8586 if (LEVELSASLIR (stream
))
8587 ast_log (LOG_NOTICE
," SASL initial client response\n");
8588 if (LEVELSORT (stream
))
8589 ast_log (LOG_NOTICE
," Server-based sorting\n");
8590 if (LEVELTHREAD (stream
)) {
8591 ast_log (LOG_NOTICE
," Server-based threading:\n");
8592 for (thr
= imap_cap(stream
)->threader
; thr
; thr
= thr
->next
)
8593 ast_log (LOG_NOTICE
," %s\n", thr
->name
);
8595 if (LEVELSCAN (stream
))
8596 ast_log (LOG_NOTICE
," Mailbox text scan\n");
8597 if ((i
= imap_cap(stream
)->extlevel
)) {
8598 ast_log (LOG_NOTICE
,"Supported BODYSTRUCTURE extensions:\n");
8601 ast_log (LOG_NOTICE
," location\n");
8603 ast_log (LOG_NOTICE
," language\n");
8605 ast_log (LOG_NOTICE
," disposition\n");
8607 ast_log (LOG_NOTICE
," MD5\n");
8611 ast_log (LOG_NOTICE
,"\n");
8617 static struct ast_vm_user
*find_user_realtime_imapuser(const char *imapuser
)
8619 struct ast_variable
*var
;
8620 struct ast_vm_user
*vmu
;
8622 vmu
= ast_calloc(1, sizeof *vmu
);
8625 ast_set_flag(vmu
, VM_ALLOCED
);
8626 populate_defaults(vmu
);
8628 var
= ast_load_realtime("voicemail", "imapuser", imapuser
, NULL
);
8630 apply_options_full(vmu
, var
);
8631 ast_variables_destroy(var
);
8639 /* Interfaces to C-client */
8641 void mm_exists(MAILSTREAM
* stream
, unsigned long number
)
8643 /* mail_ping will callback here if new mail! */
8644 if (option_debug
> 3)
8645 ast_log (LOG_DEBUG
, "Entering EXISTS callback for message %ld\n", number
);
8646 if (number
== 0) return;
8651 void mm_expunged(MAILSTREAM
* stream
, unsigned long number
)
8653 /* mail_ping will callback here if expunged mail! */
8654 if (option_debug
> 3)
8655 ast_log (LOG_DEBUG
, "Entering EXPUNGE callback for message %ld\n", number
);
8656 if (number
== 0) return;
8661 void mm_flags(MAILSTREAM
* stream
, unsigned long number
)
8663 /* mail_ping will callback here if read mail! */
8664 if (option_debug
> 3)
8665 ast_log (LOG_DEBUG
, "Entering FLAGS callback for message %ld\n", number
);
8666 if (number
== 0) return;
8671 void mm_notify(MAILSTREAM
* stream
, char *string
, long errflg
)
8673 mm_log (string
, errflg
);
8677 void mm_list(MAILSTREAM
* stream
, int delim
, char *mailbox
, long attributes
)
8679 if (delimiter
== '\0') {
8682 if (option_debug
> 4) {
8683 ast_log(LOG_DEBUG
, "Delimiter set to %c and mailbox %s\n",delim
, mailbox
);
8684 if (attributes
& LATT_NOINFERIORS
)
8685 ast_log(LOG_DEBUG
, "no inferiors\n");
8686 if (attributes
& LATT_NOSELECT
)
8687 ast_log(LOG_DEBUG
, "no select\n");
8688 if (attributes
& LATT_MARKED
)
8689 ast_log(LOG_DEBUG
, "marked\n");
8690 if (attributes
& LATT_UNMARKED
)
8691 ast_log(LOG_DEBUG
, "unmarked\n");
8696 void mm_lsub(MAILSTREAM
* stream
, int delimiter
, char *mailbox
, long attributes
)
8698 if (option_debug
> 4) {
8699 ast_log(LOG_DEBUG
, "Delimiter set to %c and mailbox %s\n",delimiter
, mailbox
);
8700 if (attributes
& LATT_NOINFERIORS
)
8701 ast_log(LOG_DEBUG
, "no inferiors\n");
8702 if (attributes
& LATT_NOSELECT
)
8703 ast_log(LOG_DEBUG
, "no select\n");
8704 if (attributes
& LATT_MARKED
)
8705 ast_log(LOG_DEBUG
, "marked\n");
8706 if (attributes
& LATT_UNMARKED
)
8707 ast_log(LOG_DEBUG
, "unmarked\n");
8712 void mm_status(MAILSTREAM
* stream
, char *mailbox
, MAILSTATUS
* status
)
8714 ast_log (LOG_NOTICE
," Mailbox %s", mailbox
);
8715 if (status
->flags
& SA_MESSAGES
)
8716 ast_log (LOG_NOTICE
,", %lu messages", status
->messages
);
8717 if (status
->flags
& SA_RECENT
)
8718 ast_log (LOG_NOTICE
,", %lu recent", status
->recent
);
8719 if (status
->flags
& SA_UNSEEN
)
8720 ast_log (LOG_NOTICE
,", %lu unseen", status
->unseen
);
8721 if (status
->flags
& SA_UIDVALIDITY
)
8722 ast_log (LOG_NOTICE
,", %lu UID validity", status
->uidvalidity
);
8723 if (status
->flags
& SA_UIDNEXT
)
8724 ast_log (LOG_NOTICE
,", %lu next UID", status
->uidnext
);
8725 ast_log (LOG_NOTICE
,"\n");
8729 void mm_log(char *string
, long errflg
)
8731 switch ((short) errflg
) {
8734 ast_log(LOG_DEBUG
,"IMAP Info: %s\n", string
);
8738 ast_log (LOG_WARNING
,"IMAP Warning: %s\n", string
);
8741 ast_log (LOG_ERROR
,"IMAP Error: %s\n", string
);
8747 void mm_dlog(char *string
)
8749 ast_log (LOG_NOTICE
, "%s\n", string
);
8753 void mm_login(NETMBX
* mb
, char *user
, char *pwd
, long trial
)
8755 struct ast_vm_user
*vmu
;
8757 if (option_debug
> 3)
8758 ast_log(LOG_DEBUG
, "Entering callback mm_login\n");
8760 ast_copy_string(user
, mb
->user
, MAILTMPLEN
);
8762 /* We should only do this when necessary */
8763 if (!ast_strlen_zero(authpassword
)) {
8764 ast_copy_string(pwd
, authpassword
, MAILTMPLEN
);
8766 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
8767 if (!strcasecmp(mb
->user
, vmu
->imapuser
)) {
8768 ast_copy_string(pwd
, vmu
->imappassword
, MAILTMPLEN
);
8773 if ((vmu
= find_user_realtime_imapuser(mb
->user
))) {
8774 ast_copy_string(pwd
, vmu
->imappassword
, MAILTMPLEN
);
8782 void mm_critical(MAILSTREAM
* stream
)
8787 void mm_nocritical(MAILSTREAM
* stream
)
8792 long mm_diskerror(MAILSTREAM
* stream
, long errcode
, long serious
)
8794 kill (getpid (), SIGSTOP
);
8799 void mm_fatal(char *string
)
8801 ast_log(LOG_ERROR
,"IMAP access FATAL error: %s\n", string
);
8804 /* C-client callback to handle quota */
8805 static void mm_parsequota(MAILSTREAM
*stream
, unsigned char *msg
, QUOTALIST
*pquota
)
8807 struct vm_state
*vms
;
8810 unsigned long usage
= 0;
8811 unsigned long limit
= 0;
8814 usage
= pquota
->usage
;
8815 limit
= pquota
->limit
;
8816 pquota
= pquota
->next
;
8819 mailbox
= stream
->mailbox
;
8820 user
= get_user_by_mailbox(mailbox
);
8821 vms
= get_vm_state_by_imapuser(user
,2);
8823 if (option_debug
> 2)
8824 ast_log (LOG_DEBUG
, "User %s usage is %lu, limit is %lu\n",user
,usage
,limit
);
8825 vms
->quota_usage
= usage
;
8826 vms
->quota_limit
= limit
;
8828 ast_log (LOG_ERROR
, "No state found.\n");
8832 static char *get_header_by_tag(char *header
, char *tag
)
8838 if (!header
|| !tag
)
8841 taglen
= strlen(tag
) + 1;
8845 start
= strstr(header
, tag
);
8849 ast_mutex_lock(&imaptemp_lock
);
8850 ast_copy_string(imaptemp
, start
+taglen
, sizeof(imaptemp
));
8851 ast_mutex_unlock(&imaptemp_lock
);
8852 if ((eol_pnt
= strchr(imaptemp
,'\r')) || (eol_pnt
= strchr(imaptemp
,'\n')))
8857 static char *get_user_by_mailbox(char *mailbox
)
8859 char *start
, *quote
;
8865 start
= strstr(mailbox
,"/user=");
8869 ast_mutex_lock(&imaptemp_lock
);
8870 ast_copy_string(imaptemp
, start
+6, sizeof(imaptemp
));
8871 ast_mutex_unlock(&imaptemp_lock
);
8873 quote
= strchr(imaptemp
,'\"');
8874 if (!quote
) { /* if username is not in quotes */
8875 eol_pnt
= strchr(imaptemp
,'/');
8877 eol_pnt
= strchr(imaptemp
,'}');
8882 eol_pnt
= strchr(imaptemp
+1,'\"');
8888 static struct vm_state
*get_vm_state_by_imapuser(char *user
, int interactive
)
8890 struct vmstate
*vlist
= NULL
;
8892 ast_mutex_lock(&vmstate_lock
);
8896 if (vlist
->vms
->imapuser
) {
8897 if (!strcmp(vlist
->vms
->imapuser
,user
)) {
8898 if (interactive
== 2) {
8899 ast_mutex_unlock(&vmstate_lock
);
8901 } else if (vlist
->vms
->interactive
== interactive
) {
8902 ast_mutex_unlock(&vmstate_lock
);
8907 if (option_debug
> 2)
8908 ast_log(LOG_DEBUG
, " error: imapuser is NULL for %s\n",user
);
8911 if (option_debug
> 2)
8912 ast_log(LOG_DEBUG
, " error: vms is NULL for %s\n",user
);
8914 vlist
= vlist
->next
;
8916 ast_mutex_unlock(&vmstate_lock
);
8917 if (option_debug
> 2)
8918 ast_log(LOG_DEBUG
, "%s not found in vmstates\n",user
);
8922 static struct vm_state
*get_vm_state_by_mailbox(const char *mailbox
, int interactive
)
8924 struct vmstate
*vlist
= NULL
;
8926 ast_mutex_lock(&vmstate_lock
);
8928 if (option_debug
> 2)
8929 ast_log(LOG_DEBUG
, "Mailbox set to %s\n",mailbox
);
8932 if (vlist
->vms
->username
) {
8933 if (option_debug
> 2)
8934 ast_log(LOG_DEBUG
, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox
,interactive
,vlist
->vms
->username
,vlist
->vms
->interactive
);
8935 if (!strcmp(vlist
->vms
->username
,mailbox
) && vlist
->vms
->interactive
== interactive
) {
8936 if (option_debug
> 2)
8937 ast_log(LOG_DEBUG
, " Found it!\n");
8938 ast_mutex_unlock(&vmstate_lock
);
8942 if (option_debug
> 2)
8943 ast_log(LOG_DEBUG
, " error: username is NULL for %s\n",mailbox
);
8946 if (option_debug
> 2)
8947 ast_log(LOG_DEBUG
, " error: vms is NULL for %s\n",mailbox
);
8949 vlist
= vlist
->next
;
8951 ast_mutex_unlock(&vmstate_lock
);
8952 if (option_debug
> 2)
8953 ast_log(LOG_DEBUG
, "%s not found in vmstates\n",mailbox
);
8957 static void vmstate_insert(struct vm_state
*vms
)
8960 struct vm_state
*altvms
;
8962 /* If interactive, it probably already exists, and we should
8963 use the one we already have since it is more up to date.
8964 We can compare the username to find the duplicate */
8965 if (vms
->interactive
== 1) {
8966 altvms
= get_vm_state_by_mailbox(vms
->username
,0);
8968 if (option_debug
> 2)
8969 ast_log(LOG_DEBUG
, "Duplicate mailbox %s, copying message info...\n",vms
->username
);
8970 vms
->newmessages
= altvms
->newmessages
;
8971 vms
->oldmessages
= altvms
->oldmessages
;
8972 if (option_debug
> 2)
8973 ast_log(LOG_DEBUG
, "check_msgArray before memcpy\n");
8974 check_msgArray(vms
);
8975 /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
8976 copy_msgArray(vms
, altvms
);
8977 if (option_debug
> 2)
8978 ast_log(LOG_DEBUG
, "check_msgArray after memcpy\n");
8979 check_msgArray(vms
);
8980 vms
->vmArrayIndex
= altvms
->vmArrayIndex
;
8981 vms
->lastmsg
= altvms
->lastmsg
;
8982 vms
->curmsg
= altvms
->curmsg
;
8983 /* get a pointer to the persistent store */
8984 vms
->persist_vms
= altvms
;
8985 /* Reuse the mailstream? */
8986 vms
->mailstream
= altvms
->mailstream
;
8987 /* vms->mailstream = NIL; */
8991 v
= (struct vmstate
*)malloc(sizeof(struct vmstate
));
8993 ast_log(LOG_ERROR
, "Out of memory\n");
8995 if (option_debug
> 2)
8996 ast_log(LOG_DEBUG
, "Inserting vm_state for user:%s, mailbox %s\n",vms
->imapuser
,vms
->username
);
8997 ast_mutex_lock(&vmstate_lock
);
9001 ast_mutex_unlock(&vmstate_lock
);
9004 static void vmstate_delete(struct vm_state
*vms
)
9006 struct vmstate
*vc
, *vf
= NULL
, *vl
= NULL
;
9007 struct vm_state
*altvms
;
9009 /* If interactive, we should copy pertainent info
9010 back to the persistent state (to make update immediate) */
9011 if (vms
->interactive
== 1) {
9012 altvms
= vms
->persist_vms
;
9014 if (option_debug
> 2)
9015 ast_log(LOG_DEBUG
, "Duplicate mailbox %s, copying message info...\n",vms
->username
);
9016 altvms
->newmessages
= vms
->newmessages
;
9017 altvms
->oldmessages
= vms
->oldmessages
;
9018 altvms
->updated
= 1;
9022 ast_mutex_lock(&vmstate_lock
);
9024 if (option_debug
> 2)
9025 ast_log(LOG_DEBUG
, "Removing vm_state for user:%s, mailbox %s\n",vms
->imapuser
,vms
->username
);
9027 if (vc
->vms
== vms
) {
9030 vl
->next
= vc
->next
;
9032 vmstates
= vc
->next
;
9039 ast_log(LOG_ERROR
, "No vmstate found for user:%s, mailbox %s\n",vms
->imapuser
,vms
->username
);
9041 ast_mutex_destroy(&vf
->vms
->lock
);
9044 ast_mutex_unlock(&vmstate_lock
);
9047 static void set_update(MAILSTREAM
* stream
)
9049 struct vm_state
*vms
;
9053 mailbox
= stream
->mailbox
;
9054 user
= get_user_by_mailbox(mailbox
);
9055 vms
= get_vm_state_by_imapuser(user
, 0);
9057 if (option_debug
> 2)
9058 ast_log (LOG_DEBUG
, "User %s mailbox set for update.\n",user
);
9059 vms
->updated
= 1; /* set updated flag since mailbox changed */
9061 if (option_debug
> 2)
9062 ast_log (LOG_WARNING
, "User %s mailbox not found for update.\n",user
);
9066 static void init_vm_state(struct vm_state
*vms
)
9069 vms
->vmArrayIndex
= 0;
9070 for (x
= 0; x
< 256; x
++) {
9071 vms
->msgArray
[x
] = 0;
9073 ast_mutex_init(&vms
->lock
);
9076 static void check_msgArray(struct vm_state
*vms
)
9079 for (x
= 0; x
<256; x
++) {
9080 if (vms
->msgArray
[x
]!=0) {
9082 ast_log (LOG_DEBUG
, "Item %d set to %ld\n",x
,vms
->msgArray
[x
]);
9087 static void copy_msgArray(struct vm_state
*dst
, struct vm_state
*src
)
9090 for (x
= 0; x
<256; x
++) {
9091 dst
->msgArray
[x
] = src
->msgArray
[x
];
9095 static int save_body(BODY
*body
, struct vm_state
*vms
, char *section
, char *format
)
9100 unsigned long newlen
;
9103 if (!body
|| body
== NIL
)
9105 body_content
= mail_fetchbody (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
], section
, &len
);
9106 if (body_content
!= NIL
) {
9107 snprintf(filename
, sizeof(filename
), "%s.%s", vms
->fn
, format
);
9108 /* ast_log (LOG_DEBUG,body_content); */
9109 body_decoded
= rfc822_base64 ((unsigned char *)body_content
, len
, &newlen
);
9110 write_file (filename
, (char *) body_decoded
, newlen
);
9115 /* get delimiter via mm_list callback */
9116 static void get_mailbox_delimiter(MAILSTREAM
*stream
) {
9118 snprintf(tmp
, sizeof(tmp
), "{%s}", imapserver
);
9119 mail_list(stream
, tmp
, "*");
9122 /* Check Quota for user */
9123 static void check_quota(struct vm_state
*vms
, char *mailbox
) {
9124 mail_parameters(NULL
, SET_QUOTA
, (void *) mm_parsequota
);
9125 if (option_debug
> 2)
9126 ast_log(LOG_DEBUG
, "Mailbox name set to: %s, about to check quotas\n", mailbox
);
9127 if (vms
&& vms
->mailstream
!= NULL
) {
9128 imap_getquotaroot(vms
->mailstream
, mailbox
);
9130 ast_log(LOG_WARNING
,"Mailstream not available for mailbox: %s\n",mailbox
);
9134 #endif /* IMAP_STORAGE */
9136 /* This is a workaround so that menuselect displays a proper description
9137 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
9140 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, tdesc
,
9141 .load
= load_module
,
9142 .unload
= unload_module
,