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 " s - Skip the playback of instructions for leaving a message to the\n"
473 " u - Play the 'unavailable' greeting.\n"
474 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
477 static char *synopsis_vmain
=
478 "Check Voicemail messages";
480 static char *descrip_vmain
=
481 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
482 "calling party to check voicemail messages. A specific mailbox, and optional\n"
483 "corresponding context, may be specified. If a mailbox is not provided, the\n"
484 "calling party will be prompted to enter one. If a context is not specified,\n"
485 "the 'default' context will be used.\n\n"
487 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
488 " is entered by the caller.\n"
489 " g(#) - Use the specified amount of gain when recording a voicemail\n"
490 " message. The units are whole-number decibels (dB).\n"
491 " s - Skip checking the passcode for the mailbox.\n"
492 " a(#) - Skip folder prompt and go directly to folder specified.\n"
493 " Defaults to INBOX\n";
495 static char *synopsis_vm_box_exists
=
496 "Check to see if Voicemail mailbox exists";
498 static char *descrip_vm_box_exists
=
499 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
500 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
502 " This application will set the following channel variable upon completion:\n"
503 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
504 " MailboxExists application. Possible values include:\n"
505 " SUCCESS | FAILED\n\n"
507 " j - Jump to priority n+101 if the mailbox is found.\n";
509 static char *synopsis_vmauthenticate
=
510 "Authenticate with Voicemail passwords";
512 static char *descrip_vmauthenticate
=
513 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
514 "same way as the Authenticate application, but the passwords are taken from\n"
516 " If the mailbox is specified, only that mailbox's password will be considered\n"
517 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
518 "be set with the authenticated mailbox.\n\n"
520 " s - Skip playing the initial prompts.\n";
522 /* Leave a message */
523 static char *app
= "VoiceMail";
525 /* Check mail, control, etc */
526 static char *app2
= "VoiceMailMain";
528 static char *app3
= "MailboxExists";
529 static char *app4
= "VMAuthenticate";
531 static AST_LIST_HEAD_STATIC(users
, ast_vm_user
);
532 static AST_LIST_HEAD_STATIC(zones
, vm_zone
);
533 static int maxsilence
;
535 static int silencethreshold
= 128;
536 static char serveremail
[80];
537 static char mailcmd
[160]; /* Configurable mail cmd */
538 static char externnotify
[160];
539 static struct ast_smdi_interface
*smdi_iface
= NULL
;
540 static char vmfmts
[80];
541 static double volgain
;
542 static int vmminmessage
;
543 static int vmmaxmessage
;
546 static int maxlogins
;
548 static struct ast_flags globalflags
= {0};
550 static int saydurationminfo
;
552 static char dialcontext
[AST_MAX_CONTEXT
];
553 static char callcontext
[AST_MAX_CONTEXT
];
554 static char exitcontext
[AST_MAX_CONTEXT
];
556 static char cidinternalcontexts
[MAX_NUM_CID_CONTEXTS
][64];
559 static char *emailbody
= NULL
;
560 static char *emailsubject
= NULL
;
561 static char *pagerbody
= NULL
;
562 static char *pagersubject
= NULL
;
563 static char fromstring
[100];
564 static char pagerfromstring
[100];
565 static char emailtitle
[100];
566 static char charset
[32] = "ISO-8859-1";
568 static unsigned char adsifdn
[4] = "\x00\x00\x00\x0F";
569 static unsigned char adsisec
[4] = "\x9B\xDB\xF7\xAC";
570 static int adsiver
= 1;
571 static char emaildateformat
[32] = "%A, %B %d, %Y at %r";
574 static void populate_defaults(struct ast_vm_user
*vmu
)
576 ast_copy_flags(vmu
, (&globalflags
), AST_FLAGS_ALL
);
577 if (saydurationminfo
)
578 vmu
->saydurationm
= saydurationminfo
;
579 ast_copy_string(vmu
->callback
, callcontext
, sizeof(vmu
->callback
));
580 ast_copy_string(vmu
->dialout
, dialcontext
, sizeof(vmu
->dialout
));
581 ast_copy_string(vmu
->exit
, exitcontext
, sizeof(vmu
->exit
));
583 vmu
->maxmsg
= maxmsg
;
584 vmu
->volgain
= volgain
;
587 static void apply_option(struct ast_vm_user
*vmu
, const char *var
, const char *value
)
590 if (!strcasecmp(var
, "attach")) {
591 ast_set2_flag(vmu
, ast_true(value
), VM_ATTACH
);
592 } else if (!strcasecmp(var
, "attachfmt")) {
593 ast_copy_string(vmu
->attachfmt
, value
, sizeof(vmu
->attachfmt
));
594 } else if (!strcasecmp(var
, "serveremail")) {
595 ast_copy_string(vmu
->serveremail
, value
, sizeof(vmu
->serveremail
));
596 } else if (!strcasecmp(var
, "language")) {
597 ast_copy_string(vmu
->language
, value
, sizeof(vmu
->language
));
598 } else if (!strcasecmp(var
, "tz")) {
599 ast_copy_string(vmu
->zonetag
, value
, sizeof(vmu
->zonetag
));
601 } else if (!strcasecmp(var
, "imapuser")) {
602 ast_copy_string(vmu
->imapuser
, value
, sizeof(vmu
->imapuser
));
603 } else if (!strcasecmp(var
, "imappassword")) {
604 ast_copy_string(vmu
->imappassword
, value
, sizeof(vmu
->imappassword
));
606 } else if (!strcasecmp(var
, "delete") || !strcasecmp(var
, "deletevoicemail")) {
607 ast_set2_flag(vmu
, ast_true(value
), VM_DELETE
);
608 } else if (!strcasecmp(var
, "saycid")){
609 ast_set2_flag(vmu
, ast_true(value
), VM_SAYCID
);
610 } else if (!strcasecmp(var
,"sendvoicemail")){
611 ast_set2_flag(vmu
, ast_true(value
), VM_SVMAIL
);
612 } else if (!strcasecmp(var
, "review")){
613 ast_set2_flag(vmu
, ast_true(value
), VM_REVIEW
);
614 } else if (!strcasecmp(var
, "tempgreetwarn")){
615 ast_set2_flag(vmu
, ast_true(value
), VM_TEMPGREETWARN
);
616 } else if (!strcasecmp(var
, "operator")){
617 ast_set2_flag(vmu
, ast_true(value
), VM_OPERATOR
);
618 } else if (!strcasecmp(var
, "envelope")){
619 ast_set2_flag(vmu
, ast_true(value
), VM_ENVELOPE
);
620 } else if (!strcasecmp(var
, "sayduration")){
621 ast_set2_flag(vmu
, ast_true(value
), VM_SAYDURATION
);
622 } else if (!strcasecmp(var
, "saydurationm")){
623 if (sscanf(value
, "%d", &x
) == 1) {
624 vmu
->saydurationm
= x
;
626 ast_log(LOG_WARNING
, "Invalid min duration for say duration\n");
628 } else if (!strcasecmp(var
, "forcename")){
629 ast_set2_flag(vmu
, ast_true(value
), VM_FORCENAME
);
630 } else if (!strcasecmp(var
, "forcegreetings")){
631 ast_set2_flag(vmu
, ast_true(value
), VM_FORCEGREET
);
632 } else if (!strcasecmp(var
, "callback")) {
633 ast_copy_string(vmu
->callback
, value
, sizeof(vmu
->callback
));
634 } else if (!strcasecmp(var
, "dialout")) {
635 ast_copy_string(vmu
->dialout
, value
, sizeof(vmu
->dialout
));
636 } else if (!strcasecmp(var
, "exitcontext")) {
637 ast_copy_string(vmu
->exit
, value
, sizeof(vmu
->exit
));
638 } else if (!strcasecmp(var
, "maxmsg")) {
639 vmu
->maxmsg
= atoi(value
);
640 if (vmu
->maxmsg
<= 0) {
641 ast_log(LOG_WARNING
, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value
, MAXMSG
);
642 vmu
->maxmsg
= MAXMSG
;
643 } else if (vmu
->maxmsg
> MAXMSGLIMIT
) {
644 ast_log(LOG_WARNING
, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT
, value
);
645 vmu
->maxmsg
= MAXMSGLIMIT
;
647 } else if (!strcasecmp(var
, "volgain")) {
648 sscanf(value
, "%lf", &vmu
->volgain
);
649 } else if (!strcasecmp(var
, "options")) {
650 apply_options(vmu
, value
);
654 static int change_password_realtime(struct ast_vm_user
*vmu
, const char *password
)
657 if (!ast_strlen_zero(vmu
->uniqueid
)) {
658 res
= ast_update_realtime("voicemail", "uniqueid", vmu
->uniqueid
, "password", password
, NULL
);
660 ast_copy_string(vmu
->password
, password
, sizeof(vmu
->password
));
670 static void apply_options(struct ast_vm_user
*vmu
, const char *options
)
671 { /* Destructively Parse options and apply */
675 stringp
= ast_strdupa(options
);
676 while ((s
= strsep(&stringp
, "|"))) {
678 if ((var
= strsep(&value
, "=")) && value
) {
679 apply_option(vmu
, var
, value
);
684 static void apply_options_full(struct ast_vm_user
*retval
, struct ast_variable
*var
)
686 struct ast_variable
*tmp
;
689 if (!strcasecmp(tmp
->name
, "vmsecret")) {
690 ast_copy_string(retval
->password
, tmp
->value
, sizeof(retval
->password
));
691 } else if (!strcasecmp(tmp
->name
, "secret") || !strcasecmp(tmp
->name
, "password")) { /* don't overwrite vmsecret if it exists */
692 if (ast_strlen_zero(retval
->password
))
693 ast_copy_string(retval
->password
, tmp
->value
, sizeof(retval
->password
));
694 } else if (!strcasecmp(tmp
->name
, "uniqueid")) {
695 ast_copy_string(retval
->uniqueid
, tmp
->value
, sizeof(retval
->uniqueid
));
696 } else if (!strcasecmp(tmp
->name
, "pager")) {
697 ast_copy_string(retval
->pager
, tmp
->value
, sizeof(retval
->pager
));
698 } else if (!strcasecmp(tmp
->name
, "email")) {
699 ast_copy_string(retval
->email
, tmp
->value
, sizeof(retval
->email
));
700 } else if (!strcasecmp(tmp
->name
, "fullname")) {
701 ast_copy_string(retval
->fullname
, tmp
->value
, sizeof(retval
->fullname
));
702 } else if (!strcasecmp(tmp
->name
, "context")) {
703 ast_copy_string(retval
->context
, tmp
->value
, sizeof(retval
->context
));
705 } else if (!strcasecmp(tmp
->name
, "imapuser")) {
706 ast_copy_string(retval
->imapuser
, tmp
->value
, sizeof(retval
->imapuser
));
707 } else if (!strcasecmp(tmp
->name
, "imappassword")) {
708 ast_copy_string(retval
->imappassword
, tmp
->value
, sizeof(retval
->imappassword
));
711 apply_option(retval
, tmp
->name
, tmp
->value
);
716 static struct ast_vm_user
*find_user_realtime(struct ast_vm_user
*ivm
, const char *context
, const char *mailbox
)
718 struct ast_variable
*var
;
719 struct ast_vm_user
*retval
;
721 if ((retval
= (ivm
? ivm
: ast_calloc(1, sizeof(*retval
))))) {
723 ast_set_flag(retval
, VM_ALLOCED
);
725 memset(retval
, 0, sizeof(*retval
));
727 ast_copy_string(retval
->mailbox
, mailbox
, sizeof(retval
->mailbox
));
728 populate_defaults(retval
);
729 if (!context
&& ast_test_flag((&globalflags
), VM_SEARCH
))
730 var
= ast_load_realtime("voicemail", "mailbox", mailbox
, NULL
);
732 var
= ast_load_realtime("voicemail", "mailbox", mailbox
, "context", context
, NULL
);
734 apply_options_full(retval
, var
);
735 ast_variables_destroy(var
);
745 static struct ast_vm_user
*find_user(struct ast_vm_user
*ivm
, const char *context
, const char *mailbox
)
747 /* This function could be made to generate one from a database, too */
748 struct ast_vm_user
*vmu
=NULL
, *cur
;
749 AST_LIST_LOCK(&users
);
751 if (!context
&& !ast_test_flag((&globalflags
), VM_SEARCH
))
754 AST_LIST_TRAVERSE(&users
, cur
, list
) {
755 if (ast_test_flag((&globalflags
), VM_SEARCH
) && !strcasecmp(mailbox
, cur
->mailbox
))
757 if (context
&& (!strcasecmp(context
, cur
->context
)) && (!strcasecmp(mailbox
, cur
->mailbox
)))
761 /* Make a copy, so that on a reload, we have no race */
762 if ((vmu
= (ivm
? ivm
: ast_malloc(sizeof(*vmu
))))) {
763 memcpy(vmu
, cur
, sizeof(*vmu
));
764 ast_set2_flag(vmu
, !ivm
, VM_ALLOCED
);
765 AST_LIST_NEXT(vmu
, list
) = NULL
;
768 vmu
= find_user_realtime(ivm
, context
, mailbox
);
769 AST_LIST_UNLOCK(&users
);
773 static int reset_user_pw(const char *context
, const char *mailbox
, const char *newpass
)
775 /* This function could be made to generate one from a database, too */
776 struct ast_vm_user
*cur
;
778 AST_LIST_LOCK(&users
);
779 AST_LIST_TRAVERSE(&users
, cur
, list
) {
780 if ((!context
|| !strcasecmp(context
, cur
->context
)) &&
781 (!strcasecmp(mailbox
, cur
->mailbox
)))
785 ast_copy_string(cur
->password
, newpass
, sizeof(cur
->password
));
788 AST_LIST_UNLOCK(&users
);
792 static void vm_change_password(struct ast_vm_user
*vmu
, const char *newpassword
)
794 struct ast_config
*cfg
=NULL
;
795 struct ast_variable
*var
=NULL
;
796 struct ast_category
*cat
=NULL
;
797 char *category
=NULL
, *value
=NULL
, *new=NULL
;
798 const char *tmp
=NULL
;
800 if (!change_password_realtime(vmu
, newpassword
))
803 /* check voicemail.conf */
804 if ((cfg
= ast_config_load_with_comments(VOICEMAIL_CONFIG
))) {
805 while ((category
= ast_category_browse(cfg
, category
))) {
806 if (!strcasecmp(category
, vmu
->context
)) {
807 tmp
= ast_variable_retrieve(cfg
, category
, vmu
->mailbox
);
809 ast_log(LOG_WARNING
, "We could not find the mailbox.\n");
812 value
= strstr(tmp
,",");
814 ast_log(LOG_WARNING
, "variable has bad format.\n");
817 new = alloca((strlen(value
)+strlen(newpassword
)+1));
818 sprintf(new,"%s%s", newpassword
, value
);
819 if (!(cat
= ast_category_get(cfg
, category
))) {
820 ast_log(LOG_WARNING
, "Failed to get category structure.\n");
823 ast_variable_update(cat
, vmu
->mailbox
, new, NULL
, 0);
826 /* save the results */
827 reset_user_pw(vmu
->context
, vmu
->mailbox
, newpassword
);
828 ast_copy_string(vmu
->password
, newpassword
, sizeof(vmu
->password
));
829 config_text_file_save(VOICEMAIL_CONFIG
, cfg
, "AppVoicemail");
833 /* check users.conf and update the password stored for the mailbox*/
834 /* if no vmsecret entry exists create one. */
835 if ((cfg
= ast_config_load_with_comments("users.conf"))) {
836 if (option_debug
> 3)
837 ast_log(LOG_DEBUG
, "we are looking for %s\n", vmu
->mailbox
);
838 while ((category
= ast_category_browse(cfg
, category
))) {
839 if (option_debug
> 3)
840 ast_log(LOG_DEBUG
, "users.conf: %s\n", category
);
841 if (!strcasecmp(category
, vmu
->mailbox
)) {
842 if (!(tmp
= ast_variable_retrieve(cfg
, category
, "vmsecret"))) {
843 if (option_debug
> 3)
844 ast_log(LOG_DEBUG
, "looks like we need to make vmsecret!\n");
845 var
= ast_variable_new("vmsecret", newpassword
);
847 new = alloca(strlen(newpassword
)+1);
848 sprintf(new, "%s", newpassword
);
849 if (!(cat
= ast_category_get(cfg
, category
))) {
850 if (option_debug
> 3)
851 ast_log(LOG_DEBUG
, "failed to get category!\n");
855 ast_variable_update(cat
, "vmsecret", new, NULL
, 0);
857 ast_variable_append(cat
, var
);
860 /* save the results and clean things up */
861 reset_user_pw(vmu
->context
, vmu
->mailbox
, newpassword
);
862 ast_copy_string(vmu
->password
, newpassword
, sizeof(vmu
->password
));
863 config_text_file_save("users.conf", cfg
, "AppVoicemail");
867 static void vm_change_password_shell(struct ast_vm_user
*vmu
, char *newpassword
)
870 snprintf(buf
,255,"%s %s %s %s",ext_pass_cmd
,vmu
->context
,vmu
->mailbox
,newpassword
);
871 if (!ast_safe_system(buf
)) {
872 ast_copy_string(vmu
->password
, newpassword
, sizeof(vmu
->password
));
873 /* Reset the password in memory, too */
874 reset_user_pw(vmu
->context
, vmu
->mailbox
, newpassword
);
878 static int make_dir(char *dest
, int len
, const char *context
, const char *ext
, const char *folder
)
880 return snprintf(dest
, len
, "%s%s/%s/%s", VM_SPOOL_DIR
, context
, ext
, folder
);
884 static int make_gsm_file(char *dest
, size_t len
, char *imapuser
, char *dir
, int num
)
886 if (mkdir(dir
, 01777) && (errno
!= EEXIST
)) {
887 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dir
, strerror(errno
));
888 return snprintf(dest
, len
, "%s/msg%04d", dir
, num
);
890 return snprintf(dest
, len
, "%s/msg%04d", dir
, num
);
893 static void vm_imap_delete(int msgnum
, struct vm_state
*vms
)
895 unsigned long messageNum
= 0;
898 /* find real message number based on msgnum */
899 /* this may be an index into vms->msgArray based on the msgnum. */
901 messageNum
= vms
->msgArray
[msgnum
];
902 if (messageNum
== 0) {
903 ast_log(LOG_WARNING
, "msgnum %d, mailbox message %lu is zero.\n",msgnum
,messageNum
);
906 if (option_debug
> 2)
907 ast_log(LOG_DEBUG
, "deleting msgnum %d, which is mailbox message %lu\n",msgnum
,messageNum
);
909 snprintf (arg
, sizeof(arg
), "%lu",messageNum
);
910 mail_setflag (vms
->mailstream
,arg
,"\\DELETED");
914 static int make_file(char *dest
, int len
, char *dir
, int num
)
916 return snprintf(dest
, len
, "%s/msg%04d", dir
, num
);
919 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
920 * \param dest String. base directory.
921 * \param len Length of dest.
922 * \param context String. Ignored if is null or empty string.
923 * \param ext String. Ignored if is null or empty string.
924 * \param folder String. Ignored if is null or empty string.
925 * \return -1 on failure, 0 on success.
927 static int create_dirpath(char *dest
, int len
, const char *context
, const char *ext
, const char *folder
)
929 mode_t mode
= VOICEMAIL_DIR_MODE
;
931 if (!ast_strlen_zero(context
)) {
932 make_dir(dest
, len
, context
, "", "");
933 if (mkdir(dest
, mode
) && errno
!= EEXIST
) {
934 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dest
, strerror(errno
));
938 if (!ast_strlen_zero(ext
)) {
939 make_dir(dest
, len
, context
, ext
, "");
940 if (mkdir(dest
, mode
) && errno
!= EEXIST
) {
941 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dest
, strerror(errno
));
945 if (!ast_strlen_zero(folder
)) {
946 make_dir(dest
, len
, context
, ext
, folder
);
947 if (mkdir(dest
, mode
) && errno
!= EEXIST
) {
948 ast_log(LOG_WARNING
, "mkdir '%s' failed: %s\n", dest
, strerror(errno
));
955 /* only return failure if ast_lock_path returns 'timeout',
956 not if the path does not exist or any other reason
958 static int vm_lock_path(const char *path
)
960 switch (ast_lock_path(path
)) {
961 case AST_LOCK_TIMEOUT
:
970 struct generic_prepare_struct
{
976 static SQLHSTMT
generic_prepare(struct odbc_obj
*obj
, void *data
)
978 struct generic_prepare_struct
*gps
= data
;
982 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
983 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
984 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
987 res
= SQLPrepare(stmt
, (unsigned char *)gps
->sql
, SQL_NTS
);
988 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
989 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", gps
->sql
);
990 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
993 for (i
= 0; i
< gps
->argc
; i
++)
994 SQLBindParameter(stmt
, i
+ 1, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(gps
->argv
[i
]), 0, gps
->argv
[i
], 0, NULL
);
999 static int retrieve_file(char *dir
, int msgnum
)
1005 void *fdm
= MAP_FAILED
;
1006 SQLSMALLINT colcount
=0;
1013 SQLSMALLINT datatype
;
1014 SQLSMALLINT decimaldigits
;
1015 SQLSMALLINT nullable
;
1021 char full_fn
[PATH_MAX
];
1023 char *argv
[] = { dir
, msgnums
};
1024 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 2, .argv
= argv
};
1026 struct odbc_obj
*obj
;
1027 obj
= ast_odbc_request_obj(odbc_database
, 0);
1029 ast_copy_string(fmt
, vmfmts
, sizeof(fmt
));
1030 c
= strchr(fmt
, '|');
1033 if (!strcasecmp(fmt
, "wav49"))
1035 snprintf(msgnums
, sizeof(msgnums
),"%d", msgnum
);
1037 make_file(fn
, sizeof(fn
), dir
, msgnum
);
1039 ast_copy_string(fn
, dir
, sizeof(fn
));
1040 snprintf(full_fn
, sizeof(full_fn
), "%s.txt", fn
);
1042 if (!(f
= fopen(full_fn
, "w+"))) {
1043 ast_log(LOG_WARNING
, "Failed to open/create '%s'\n", full_fn
);
1047 snprintf(full_fn
, sizeof(full_fn
), "%s.%s", fn
, fmt
);
1048 snprintf(sql
, sizeof(sql
), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table
);
1049 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
1051 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1052 ast_odbc_release_obj(obj
);
1055 res
= SQLFetch(stmt
);
1056 if (res
== SQL_NO_DATA
) {
1057 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1058 ast_odbc_release_obj(obj
);
1061 else if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1062 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
1063 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1064 ast_odbc_release_obj(obj
);
1067 fd
= open(full_fn
, O_RDWR
| O_CREAT
| O_TRUNC
, 0770);
1069 ast_log(LOG_WARNING
, "Failed to write '%s': %s\n", full_fn
, strerror(errno
));
1070 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1071 ast_odbc_release_obj(obj
);
1074 res
= SQLNumResultCols(stmt
, &colcount
);
1075 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1076 ast_log(LOG_WARNING
, "SQL Column Count error!\n[%s]\n\n", sql
);
1077 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1078 ast_odbc_release_obj(obj
);
1082 fprintf(f
, "[message]\n");
1083 for (x
=0;x
<colcount
;x
++) {
1085 collen
= sizeof(coltitle
);
1086 res
= SQLDescribeCol(stmt
, x
+ 1, (unsigned char *)coltitle
, sizeof(coltitle
), &collen
,
1087 &datatype
, &colsize
, &decimaldigits
, &nullable
);
1088 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1089 ast_log(LOG_WARNING
, "SQL Describe Column error!\n[%s]\n\n", sql
);
1090 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1091 ast_odbc_release_obj(obj
);
1094 if (!strcasecmp(coltitle
, "recording")) {
1096 res
= SQLGetData(stmt
, x
+ 1, SQL_BINARY
, rowdata
, 0, &colsize2
);
1100 lseek(fd
, fdlen
- 1, SEEK_SET
);
1101 if (write(fd
, tmp
, 1) != 1) {
1106 /* Read out in small chunks */
1107 for (offset
= 0; offset
< colsize2
; offset
+= CHUNKSIZE
) {
1108 if ((fdm
= mmap(NULL
, CHUNKSIZE
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, fd
, offset
)) == MAP_FAILED
) {
1109 ast_log(LOG_WARNING
, "Could not mmap the output file: %s (%d)\n", strerror(errno
), errno
);
1110 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
1111 ast_odbc_release_obj(obj
);
1114 res
= SQLGetData(stmt
, x
+ 1, SQL_BINARY
, fdm
, CHUNKSIZE
, NULL
);
1115 munmap(fdm
, CHUNKSIZE
);
1116 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1117 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
1119 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
1120 ast_odbc_release_obj(obj
);
1125 truncate(full_fn
, fdlen
);
1128 res
= SQLGetData(stmt
, x
+ 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
1129 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1130 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
1131 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1132 ast_odbc_release_obj(obj
);
1135 if (strcasecmp(coltitle
, "msgnum") && strcasecmp(coltitle
, "dir") && f
)
1136 fprintf(f
, "%s=%s\n", coltitle
, rowdata
);
1139 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1140 ast_odbc_release_obj(obj
);
1142 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1151 static int remove_file(char *dir
, int msgnum
)
1154 char full_fn
[PATH_MAX
];
1158 snprintf(msgnums
, sizeof(msgnums
), "%d", msgnum
);
1159 make_file(fn
, sizeof(fn
), dir
, msgnum
);
1161 ast_copy_string(fn
, dir
, sizeof(fn
));
1162 ast_filedelete(fn
, NULL
);
1163 snprintf(full_fn
, sizeof(full_fn
), "%s.txt", fn
);
1168 static int last_message_index(struct ast_vm_user
*vmu
, char *dir
)
1175 char *argv
[] = { dir
};
1176 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 1, .argv
= argv
};
1178 struct odbc_obj
*obj
;
1179 obj
= ast_odbc_request_obj(odbc_database
, 0);
1181 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table
);
1182 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
1184 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1185 ast_odbc_release_obj(obj
);
1188 res
= SQLFetch(stmt
);
1189 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1190 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
1191 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1192 ast_odbc_release_obj(obj
);
1195 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
1196 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1197 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
1198 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1199 ast_odbc_release_obj(obj
);
1202 if (sscanf(rowdata
, "%d", &x
) != 1)
1203 ast_log(LOG_WARNING
, "Failed to read message count!\n");
1204 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1205 ast_odbc_release_obj(obj
);
1207 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1212 static int message_exists(char *dir
, int msgnum
)
1220 char *argv
[] = { dir
, msgnums
};
1221 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 2, .argv
= argv
};
1223 struct odbc_obj
*obj
;
1224 obj
= ast_odbc_request_obj(odbc_database
, 0);
1226 snprintf(msgnums
, sizeof(msgnums
), "%d", msgnum
);
1227 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table
);
1228 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
1230 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1231 ast_odbc_release_obj(obj
);
1234 res
= SQLFetch(stmt
);
1235 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1236 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
1237 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1238 ast_odbc_release_obj(obj
);
1241 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
1242 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1243 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
1244 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1245 ast_odbc_release_obj(obj
);
1248 if (sscanf(rowdata
, "%d", &x
) != 1)
1249 ast_log(LOG_WARNING
, "Failed to read message count!\n");
1250 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1251 ast_odbc_release_obj(obj
);
1253 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1258 static int count_messages(struct ast_vm_user
*vmu
, char *dir
)
1260 return last_message_index(vmu
, dir
) + 1;
1263 static void delete_file(char *sdir
, int smsg
)
1268 char *argv
[] = { sdir
, msgnums
};
1269 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 2, .argv
= argv
};
1271 struct odbc_obj
*obj
;
1272 obj
= ast_odbc_request_obj(odbc_database
, 0);
1274 snprintf(msgnums
, sizeof(msgnums
), "%d", smsg
);
1275 snprintf(sql
, sizeof(sql
), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table
);
1276 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
1278 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1280 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1281 ast_odbc_release_obj(obj
);
1283 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1287 static void copy_file(char *sdir
, int smsg
, char *ddir
, int dmsg
, char *dmailboxuser
, char *dmailboxcontext
)
1293 struct odbc_obj
*obj
;
1294 char *argv
[] = { ddir
, msgnumd
, dmailboxuser
, dmailboxcontext
, sdir
, msgnums
};
1295 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 6, .argv
= argv
};
1297 delete_file(ddir
, dmsg
);
1298 obj
= ast_odbc_request_obj(odbc_database
, 0);
1300 snprintf(msgnums
, sizeof(msgnums
), "%d", smsg
);
1301 snprintf(msgnumd
, sizeof(msgnumd
), "%d", dmsg
);
1302 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
);
1303 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
1305 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql
);
1307 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
1308 ast_odbc_release_obj(obj
);
1310 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1314 static int store_file(char *dir
, char *mailboxuser
, char *mailboxcontext
, int msgnum
)
1319 void *fdm
= MAP_FAILED
;
1326 char full_fn
[PATH_MAX
];
1329 const char *context
="", *macrocontext
="", *callerid
="", *origtime
="", *duration
="";
1330 const char *category
= "";
1331 struct ast_config
*cfg
=NULL
;
1332 struct odbc_obj
*obj
;
1334 delete_file(dir
, msgnum
);
1335 obj
= ast_odbc_request_obj(odbc_database
, 0);
1337 ast_copy_string(fmt
, vmfmts
, sizeof(fmt
));
1338 c
= strchr(fmt
, '|');
1341 if (!strcasecmp(fmt
, "wav49"))
1343 snprintf(msgnums
, sizeof(msgnums
),"%d", msgnum
);
1345 make_file(fn
, sizeof(fn
), dir
, msgnum
);
1347 ast_copy_string(fn
, dir
, sizeof(fn
));
1348 snprintf(full_fn
, sizeof(full_fn
), "%s.txt", fn
);
1349 cfg
= ast_config_load(full_fn
);
1350 snprintf(full_fn
, sizeof(full_fn
), "%s.%s", fn
, fmt
);
1351 fd
= open(full_fn
, O_RDWR
);
1353 ast_log(LOG_WARNING
, "Open of sound file '%s' failed: %s\n", full_fn
, strerror(errno
));
1354 ast_odbc_release_obj(obj
);
1358 context
= ast_variable_retrieve(cfg
, "message", "context");
1359 if (!context
) context
= "";
1360 macrocontext
= ast_variable_retrieve(cfg
, "message", "macrocontext");
1361 if (!macrocontext
) macrocontext
= "";
1362 callerid
= ast_variable_retrieve(cfg
, "message", "callerid");
1363 if (!callerid
) callerid
= "";
1364 origtime
= ast_variable_retrieve(cfg
, "message", "origtime");
1365 if (!origtime
) origtime
= "";
1366 duration
= ast_variable_retrieve(cfg
, "message", "duration");
1367 if (!duration
) duration
= "";
1368 category
= ast_variable_retrieve(cfg
, "message", "category");
1369 if (!category
) category
= "";
1371 fdlen
= lseek(fd
, 0, SEEK_END
);
1372 lseek(fd
, 0, SEEK_SET
);
1373 printf("Length is %zd\n", fdlen
);
1374 fdm
= mmap(NULL
, fdlen
, PROT_READ
| PROT_WRITE
, MAP_SHARED
,fd
, 0);
1375 if (fdm
== MAP_FAILED
) {
1376 ast_log(LOG_WARNING
, "Memory map failed!\n");
1377 ast_odbc_release_obj(obj
);
1380 res
= SQLAllocHandle(SQL_HANDLE_STMT
, obj
->con
, &stmt
);
1381 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1382 ast_log(LOG_WARNING
, "SQL Alloc Handle failed!\n");
1383 ast_odbc_release_obj(obj
);
1386 if (!ast_strlen_zero(category
))
1387 snprintf(sql
, sizeof(sql
), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table
);
1389 snprintf(sql
, sizeof(sql
), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?,?,?,?,?,?,?,?,?)",odbc_table
);
1390 res
= SQLPrepare(stmt
, (unsigned char *)sql
, SQL_NTS
);
1391 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1392 ast_log(LOG_WARNING
, "SQL Prepare failed![%s]\n", sql
);
1393 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1394 ast_odbc_release_obj(obj
);
1397 len
= fdlen
; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
1398 SQLBindParameter(stmt
, 1, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(dir
), 0, (void *)dir
, 0, NULL
);
1399 SQLBindParameter(stmt
, 2, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(msgnums
), 0, (void *)msgnums
, 0, NULL
);
1400 SQLBindParameter(stmt
, 3, SQL_PARAM_INPUT
, SQL_C_BINARY
, SQL_LONGVARBINARY
, fdlen
, 0, (void *)fdm
, fdlen
, &len
);
1401 SQLBindParameter(stmt
, 4, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(context
), 0, (void *)context
, 0, NULL
);
1402 SQLBindParameter(stmt
, 5, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(macrocontext
), 0, (void *)macrocontext
, 0, NULL
);
1403 SQLBindParameter(stmt
, 6, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(callerid
), 0, (void *)callerid
, 0, NULL
);
1404 SQLBindParameter(stmt
, 7, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(origtime
), 0, (void *)origtime
, 0, NULL
);
1405 SQLBindParameter(stmt
, 8, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(duration
), 0, (void *)duration
, 0, NULL
);
1406 SQLBindParameter(stmt
, 9, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(mailboxuser
), 0, (void *)mailboxuser
, 0, NULL
);
1407 SQLBindParameter(stmt
, 10, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(mailboxcontext
), 0, (void *)mailboxcontext
, 0, NULL
);
1408 if (!ast_strlen_zero(category
))
1409 SQLBindParameter(stmt
, 11, SQL_PARAM_INPUT
, SQL_C_CHAR
, SQL_CHAR
, strlen(category
), 0, (void *)category
, 0, NULL
);
1410 res
= ast_odbc_smart_execute(obj
, stmt
);
1411 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
1412 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1413 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1414 ast_odbc_release_obj(obj
);
1417 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
1418 ast_odbc_release_obj(obj
);
1420 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1423 ast_config_destroy(cfg
);
1424 if (fdm
!= MAP_FAILED
)
1431 static void rename_file(char *sdir
, int smsg
, char *mailboxuser
, char *mailboxcontext
, char *ddir
, int dmsg
)
1437 struct odbc_obj
*obj
;
1438 char *argv
[] = { ddir
, msgnumd
, mailboxuser
, mailboxcontext
, sdir
, msgnums
};
1439 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 6, .argv
= argv
};
1441 delete_file(ddir
, dmsg
);
1442 obj
= ast_odbc_request_obj(odbc_database
, 0);
1444 snprintf(msgnums
, sizeof(msgnums
), "%d", smsg
);
1445 snprintf(msgnumd
, sizeof(msgnumd
), "%d", dmsg
);
1446 snprintf(sql
, sizeof(sql
), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table
);
1447 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
1449 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
1451 SQLFreeHandle(SQL_HANDLE_STMT
, stmt
);
1452 ast_odbc_release_obj(obj
);
1454 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
1459 #ifndef IMAP_STORAGE
1460 static int count_messages(struct ast_vm_user
*vmu
, char *dir
)
1462 /* Find all .txt files - even if they are not in sequence from 0000 */
1466 struct dirent
*vment
= NULL
;
1468 if (vm_lock_path(dir
))
1469 return ERROR_LOCK_PATH
;
1471 if ((vmdir
= opendir(dir
))) {
1472 while ((vment
= readdir(vmdir
))) {
1473 if (strlen(vment
->d_name
) > 7 && !strncmp(vment
->d_name
+ 7, ".txt", 4))
1478 ast_unlock_path(dir
);
1483 static void rename_file(char *sfn
, char *dfn
)
1485 char stxt
[PATH_MAX
];
1486 char dtxt
[PATH_MAX
];
1487 ast_filerename(sfn
,dfn
,NULL
);
1488 snprintf(stxt
, sizeof(stxt
), "%s.txt", sfn
);
1489 snprintf(dtxt
, sizeof(dtxt
), "%s.txt", dfn
);
1495 * A negative return value indicates an error.
1497 #if (!defined(IMAP_STORAGE) && !defined(ODBC_STORAGE))
1498 static int last_message_index(struct ast_vm_user
*vmu
, char *dir
)
1503 if (vm_lock_path(dir
))
1504 return ERROR_LOCK_PATH
;
1506 for (x
= 0; x
< vmu
->maxmsg
; x
++) {
1507 make_file(fn
, sizeof(fn
), dir
, x
);
1508 if (ast_fileexists(fn
, NULL
, NULL
) < 1)
1511 ast_unlock_path(dir
);
1518 static int copy(char *infile
, char *outfile
)
1526 #ifdef HARDLINK_WHEN_POSSIBLE
1527 /* Hard link if possible; saves disk space & is faster */
1528 if (link(infile
, outfile
)) {
1530 if ((ifd
= open(infile
, O_RDONLY
)) < 0) {
1531 ast_log(LOG_WARNING
, "Unable to open %s in read-only mode\n", infile
);
1534 if ((ofd
= open(outfile
, O_WRONLY
| O_TRUNC
| O_CREAT
, VOICEMAIL_FILE_MODE
)) < 0) {
1535 ast_log(LOG_WARNING
, "Unable to open %s in write-only mode\n", outfile
);
1540 len
= read(ifd
, buf
, sizeof(buf
));
1542 ast_log(LOG_WARNING
, "Read failed on %s: %s\n", infile
, strerror(errno
));
1548 res
= write(ofd
, buf
, len
);
1549 if (errno
== ENOMEM
|| errno
== ENOSPC
|| res
!= len
) {
1550 ast_log(LOG_WARNING
, "Write failed on %s (%d of %d): %s\n", outfile
, res
, len
, strerror(errno
));
1560 #ifdef HARDLINK_WHEN_POSSIBLE
1562 /* Hard link succeeded */
1568 static void copy_plain_file(char *frompath
, char *topath
)
1570 char frompath2
[PATH_MAX
], topath2
[PATH_MAX
];
1571 ast_filecopy(frompath
, topath
, NULL
);
1572 snprintf(frompath2
, sizeof(frompath2
), "%s.txt", frompath
);
1573 snprintf(topath2
, sizeof(topath2
), "%s.txt", topath
);
1574 copy(frompath2
, topath2
);
1577 static int vm_delete(char *file
)
1582 txtsize
= (strlen(file
) + 5)*sizeof(char);
1583 txt
= alloca(txtsize
);
1584 /* Sprintf here would safe because we alloca'd exactly the right length,
1585 * but trying to eliminate all sprintf's anyhow
1587 snprintf(txt
, txtsize
, "%s.txt", file
);
1589 return ast_filedelete(file
, NULL
);
1592 static int inbuf(struct baseio
*bio
, FILE *fi
)
1599 if ((l
= fread(bio
->iobuf
,1,BASEMAXINLINE
,fi
)) <= 0) {
1613 static int inchar(struct baseio
*bio
, FILE *fi
)
1615 if (bio
->iocp
>=bio
->iolen
) {
1616 if (!inbuf(bio
, fi
))
1620 return bio
->iobuf
[bio
->iocp
++];
1623 static int ochar(struct baseio
*bio
, int c
, FILE *so
)
1625 if (bio
->linelength
>=BASELINELEN
) {
1626 if (fputs(eol
,so
)==EOF
)
1632 if (putc(((unsigned char)c
),so
)==EOF
)
1640 static int base_encode(char *filename
, FILE *so
)
1642 unsigned char dtable
[BASEMAXINLINE
];
1647 memset(&bio
, 0, sizeof(bio
));
1648 bio
.iocp
= BASEMAXINLINE
;
1650 if (!(fi
= fopen(filename
, "rb"))) {
1651 ast_log(LOG_WARNING
, "Failed to open file: %s: %s\n", filename
, strerror(errno
));
1655 for (i
= 0;i
<9;i
++) {
1658 dtable
[26+i
]= 'a'+i
;
1659 dtable
[26+i
+9]= 'j'+i
;
1661 for (i
= 0;i
<8;i
++) {
1662 dtable
[i
+18]= 'S'+i
;
1663 dtable
[26+i
+18]= 's'+i
;
1665 for (i
= 0;i
<10;i
++) {
1666 dtable
[52+i
]= '0'+i
;
1672 unsigned char igroup
[3],ogroup
[4];
1675 igroup
[0]= igroup
[1]= igroup
[2]= 0;
1677 for (n
= 0;n
<3;n
++) {
1678 if ((c
= inchar(&bio
, fi
)) == EOF
) {
1683 igroup
[n
]= (unsigned char)c
;
1687 ogroup
[0]= dtable
[igroup
[0]>>2];
1688 ogroup
[1]= dtable
[((igroup
[0]&3)<<4)|(igroup
[1]>>4)];
1689 ogroup
[2]= dtable
[((igroup
[1]&0xF)<<2)|(igroup
[2]>>6)];
1690 ogroup
[3]= dtable
[igroup
[2]&0x3F];
1700 ochar(&bio
, ogroup
[i
], so
);
1706 if (fputs(eol
,so
)==EOF
)
1712 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
)
1715 /* Prepare variables for substition in email body and subject */
1716 pbx_builtin_setvar_helper(ast
, "VM_NAME", vmu
->fullname
);
1717 pbx_builtin_setvar_helper(ast
, "VM_DUR", dur
);
1718 snprintf(passdata
, passdatasize
, "%d", msgnum
);
1719 pbx_builtin_setvar_helper(ast
, "VM_MSGNUM", passdata
);
1720 pbx_builtin_setvar_helper(ast
, "VM_CONTEXT", context
);
1721 pbx_builtin_setvar_helper(ast
, "VM_MAILBOX", mailbox
);
1722 pbx_builtin_setvar_helper(ast
, "VM_CALLERID", ast_callerid_merge(callerid
, sizeof(callerid
), cidname
, cidnum
, "Unknown Caller"));
1723 pbx_builtin_setvar_helper(ast
, "VM_CIDNAME", (cidname
? cidname
: "an unknown caller"));
1724 pbx_builtin_setvar_helper(ast
, "VM_CIDNUM", (cidnum
? cidnum
: "an unknown caller"));
1725 pbx_builtin_setvar_helper(ast
, "VM_DATE", date
);
1726 pbx_builtin_setvar_helper(ast
, "VM_CATEGORY", category
? ast_strdupa(category
) : "no category");
1729 static char *quote(const char *from
, char *to
, size_t len
)
1733 for (; ptr
< to
+ len
- 1; from
++) {
1736 else if (*from
== '\0')
1740 if (ptr
< to
+ len
- 1)
1746 * fill in *tm for current time according to the proper timezone, if any.
1747 * Return tm so it can be used as a function argument.
1749 static const struct tm
*vmu_tm(const struct ast_vm_user
*vmu
, struct tm
*tm
)
1751 const struct vm_zone
*z
= NULL
;
1752 time_t t
= time(NULL
);
1754 /* Does this user have a timezone specified? */
1755 if (!ast_strlen_zero(vmu
->zonetag
)) {
1756 /* Find the zone in the list */
1757 AST_LIST_LOCK(&zones
);
1758 AST_LIST_TRAVERSE(&zones
, z
, list
) {
1759 if (!strcmp(z
->name
, vmu
->zonetag
))
1762 AST_LIST_UNLOCK(&zones
);
1764 ast_localtime(&t
, tm
, z
? z
->timezone
: NULL
);
1768 /* same as mkstemp, but return a FILE * */
1769 static FILE *vm_mkftemp(char *template)
1772 int pfd
= mkstemp(template);
1773 chmod(template, VOICEMAIL_FILE_MODE
& ~my_umask
);
1775 p
= fdopen(pfd
, "w+");
1784 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
)
1787 char host
[MAXHOSTNAMELEN
] = "";
1795 size_t len_passdata
;
1802 gethostname(host
, sizeof(host
) - 1);
1803 if (strchr(srcemail
, '@'))
1804 ast_copy_string(who
, srcemail
, sizeof(who
));
1806 snprintf(who
, sizeof(who
), "%s@%s", srcemail
, host
);
1808 snprintf(dur
, sizeof(dur
), "%d:%02d", duration
/ 60, duration
% 60);
1809 strftime(date
, sizeof(date
), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu
, &tm
));
1810 fprintf(p
, "Date: %s" ENDL
, date
);
1812 /* Set date format for voicemail mail */
1813 strftime(date
, sizeof(date
), emaildateformat
, &tm
);
1816 struct ast_channel
*ast
;
1817 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
1819 int vmlen
= strlen(fromstring
)*3 + 200;
1820 if ((passdata
= alloca(vmlen
))) {
1821 memset(passdata
, 0, vmlen
);
1822 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
1823 pbx_substitute_variables_helper(ast
, fromstring
, passdata
, vmlen
);
1824 len_passdata
= strlen(passdata
) * 2 + 3;
1825 passdata2
= alloca(len_passdata
);
1826 fprintf(p
, "From: %s <%s>" ENDL
, quote(passdata
, passdata2
, len_passdata
), who
);
1828 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
1829 ast_channel_free(ast
);
1831 ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
1833 fprintf(p
, "From: Asterisk PBX <%s>" ENDL
, who
);
1834 len_passdata
= strlen(vmu
->fullname
) * 2 + 3;
1835 passdata2
= alloca(len_passdata
);
1836 fprintf(p
, "To: %s <%s>" ENDL
, quote(vmu
->fullname
, passdata2
, len_passdata
), vmu
->email
);
1838 struct ast_channel
*ast
;
1839 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
1841 int vmlen
= strlen(emailsubject
)*3 + 200;
1842 if ((passdata
= alloca(vmlen
))) {
1843 memset(passdata
, 0, vmlen
);
1844 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
1845 pbx_substitute_variables_helper(ast
, emailsubject
, passdata
, vmlen
);
1846 fprintf(p
, "Subject: %s" ENDL
, passdata
);
1848 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
1849 ast_channel_free(ast
);
1851 ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
1852 } else if (*emailtitle
) {
1853 fprintf(p
, emailtitle
, msgnum
+ 1, mailbox
) ;
1855 } else if (ast_test_flag((&globalflags
), VM_PBXSKIP
))
1856 fprintf(p
, "Subject: New message %d in mailbox %s" ENDL
, msgnum
+ 1, mailbox
);
1858 fprintf(p
, "Subject: [PBX]: New message %d in mailbox %s" ENDL
, msgnum
+ 1, mailbox
);
1859 fprintf(p
, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL
, msgnum
+ 1, (unsigned int)ast_random(), mailbox
, (int)getpid(), host
);
1861 /* additional information needed for IMAP searching */
1862 fprintf(p
, "X-Asterisk-VM-Message-Num: %d" ENDL
, msgnum
+ 1);
1863 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
1864 fprintf(p
, "X-Asterisk-VM-Server-Name: %s" ENDL
, fromstring
);
1865 fprintf(p
, "X-Asterisk-VM-Context: %s" ENDL
, context
);
1866 fprintf(p
, "X-Asterisk-VM-Extension: %s" ENDL
, mailbox
);
1867 fprintf(p
, "X-Asterisk-VM-Priority: %d" ENDL
, chan
->priority
);
1868 fprintf(p
, "X-Asterisk-VM-Caller-channel: %s" ENDL
, chan
->name
);
1869 fprintf(p
, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL
, cidnum
);
1870 fprintf(p
, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL
, cidname
);
1871 fprintf(p
, "X-Asterisk-VM-Duration: %d" ENDL
, duration
);
1872 if (!ast_strlen_zero(category
))
1873 fprintf(p
, "X-Asterisk-VM-Category: %s" ENDL
, category
);
1874 fprintf(p
, "X-Asterisk-VM-Orig-date: %s" ENDL
, date
);
1875 fprintf(p
, "X-Asterisk-VM-Orig-time: %ld" ENDL
, (long)time(NULL
));
1877 if (!ast_strlen_zero(cidnum
))
1878 fprintf(p
, "X-Asterisk-CallerID: %s" ENDL
, cidnum
);
1879 if (!ast_strlen_zero(cidname
))
1880 fprintf(p
, "X-Asterisk-CallerIDName: %s" ENDL
, cidname
);
1881 fprintf(p
, "MIME-Version: 1.0" ENDL
);
1882 if (attach_user_voicemail
) {
1883 /* Something unique. */
1884 snprintf(bound
, sizeof(bound
), "----voicemail_%d%s%d%d", msgnum
+ 1, mailbox
, (int)getpid(), (unsigned int)ast_random());
1886 fprintf(p
, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL
, bound
);
1887 fprintf(p
, ENDL ENDL
"This is a multi-part message in MIME format." ENDL ENDL
);
1888 fprintf(p
, "--%s" ENDL
, bound
);
1890 fprintf(p
, "Content-Type: text/plain; charset=%s" ENDL
"Content-Transfer-Encoding: 8bit" ENDL ENDL
, charset
);
1892 struct ast_channel
*ast
;
1893 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
1895 int vmlen
= strlen(emailbody
)*3 + 200;
1896 if ((passdata
= alloca(vmlen
))) {
1897 memset(passdata
, 0, vmlen
);
1898 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
1899 pbx_substitute_variables_helper(ast
, emailbody
, passdata
, vmlen
);
1900 fprintf(p
, "%s" ENDL
, passdata
);
1902 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
1903 ast_channel_free(ast
);
1905 ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
1907 fprintf(p
, "Dear %s:" ENDL ENDL
"\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
1909 "in mailbox %s from %s, on %s so you might" ENDL
1910 "want to check it when you get a chance. Thanks!" ENDL ENDL
"\t\t\t\t--Asterisk" ENDL ENDL
, vmu
->fullname
,
1911 dur
, msgnum
+ 1, mailbox
, (cidname
? cidname
: (cidnum
? cidnum
: "an unknown caller")), date
);
1913 if (attach_user_voicemail
) {
1914 /* Eww. We want formats to tell us their own MIME type */
1915 char *ctype
= (!strcasecmp(format
, "ogg")) ? "application/" : "audio/x-";
1916 char tmpdir
[256], newtmp
[256];
1919 if (vmu
->volgain
< -.001 || vmu
->volgain
> .001) {
1920 create_dirpath(tmpdir
, sizeof(tmpdir
), vmu
->context
, vmu
->mailbox
, "tmp");
1921 snprintf(newtmp
, sizeof(newtmp
), "%s/XXXXXX", tmpdir
);
1922 tmpfd
= mkstemp(newtmp
);
1923 chmod(newtmp
, VOICEMAIL_FILE_MODE
& ~my_umask
);
1924 if (option_debug
> 2)
1925 ast_log(LOG_DEBUG
, "newtmp: %s\n", newtmp
);
1927 snprintf(tmpcmd
, sizeof(tmpcmd
), "sox -v %.4f %s.%s %s.%s", vmu
->volgain
, attach
, format
, newtmp
, format
);
1928 ast_safe_system(tmpcmd
);
1930 if (option_debug
> 2)
1931 ast_log(LOG_DEBUG
, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach
, format
, vmu
->volgain
, mailbox
);
1934 fprintf(p
, "--%s" ENDL
, bound
);
1935 fprintf(p
, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL
, ctype
, format
, msgnum
+ 1, format
);
1936 fprintf(p
, "Content-Transfer-Encoding: base64" ENDL
);
1937 fprintf(p
, "Content-Description: Voicemail sound attachment." ENDL
);
1938 fprintf(p
, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL
, msgnum
+ 1, format
);
1939 snprintf(fname
, sizeof(fname
), "%s.%s", attach
, format
);
1940 base_encode(fname
, p
);
1941 fprintf(p
, ENDL
"--%s--" ENDL
"." ENDL
, bound
);
1950 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
)
1953 char tmp
[80] = "/tmp/astmail-XXXXXX";
1956 if (vmu
&& ast_strlen_zero(vmu
->email
)) {
1957 ast_log(LOG_WARNING
, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu
->mailbox
);
1960 if (!strcmp(format
, "wav49"))
1962 if (option_debug
> 2)
1963 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
));
1964 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1966 if ((p
= vm_mkftemp(tmp
)) == NULL
) {
1967 ast_log(LOG_WARNING
, "Unable to launch '%s' (can't create temporary file)\n", mailcmd
);
1970 make_email_file(p
, srcemail
, vmu
, msgnum
, context
, mailbox
, cidnum
, cidname
, attach
, format
, duration
, attach_user_voicemail
, chan
, category
, 0);
1972 snprintf(tmp2
, sizeof(tmp2
), "( %s < %s ; rm -f %s ) &", mailcmd
, tmp
, tmp
);
1973 ast_safe_system(tmp2
);
1974 if (option_debug
> 2)
1975 ast_log(LOG_DEBUG
, "Sent mail to %s with command '%s'\n", vmu
->email
, mailcmd
);
1980 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
)
1983 char host
[MAXHOSTNAMELEN
] = "";
1986 char tmp
[80] = "/tmp/astmail-XXXXXX";
1987 char tmp2
[PATH_MAX
];
1991 if ((p
= vm_mkftemp(tmp
)) == NULL
) {
1992 ast_log(LOG_WARNING
, "Unable to launch '%s' (can't create temporary file)\n", mailcmd
);
1995 gethostname(host
, sizeof(host
)-1);
1996 if (strchr(srcemail
, '@'))
1997 ast_copy_string(who
, srcemail
, sizeof(who
));
1999 snprintf(who
, sizeof(who
), "%s@%s", srcemail
, host
);
2001 snprintf(dur
, sizeof(dur
), "%d:%02d", duration
/ 60, duration
% 60);
2002 strftime(date
, sizeof(date
), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu
, &tm
));
2003 fprintf(p
, "Date: %s\n", date
);
2005 if (*pagerfromstring
) {
2006 struct ast_channel
*ast
;
2007 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
2009 int vmlen
= strlen(fromstring
)*3 + 200;
2010 if ((passdata
= alloca(vmlen
))) {
2011 memset(passdata
, 0, vmlen
);
2012 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
2013 pbx_substitute_variables_helper(ast
, pagerfromstring
, passdata
, vmlen
);
2014 fprintf(p
, "From: %s <%s>\n", passdata
, who
);
2016 ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
2017 ast_channel_free(ast
);
2018 } else ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
2020 fprintf(p
, "From: Asterisk PBX <%s>\n", who
);
2021 fprintf(p
, "To: %s\n", pager
);
2023 struct ast_channel
*ast
;
2024 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
2026 int vmlen
= strlen(pagersubject
) * 3 + 200;
2027 if ((passdata
= alloca(vmlen
))) {
2028 memset(passdata
, 0, vmlen
);
2029 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
2030 pbx_substitute_variables_helper(ast
, pagersubject
, passdata
, vmlen
);
2031 fprintf(p
, "Subject: %s\n\n", passdata
);
2032 } else ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
2033 ast_channel_free(ast
);
2034 } else ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
2036 fprintf(p
, "Subject: New VM\n\n");
2037 strftime(date
, sizeof(date
), "%A, %B %d, %Y at %r", &tm
);
2039 struct ast_channel
*ast
;
2040 if ((ast
= ast_channel_alloc(0, AST_STATE_DOWN
, 0, 0, "", "", "", 0, 0))) {
2042 int vmlen
= strlen(pagerbody
)*3 + 200;
2043 if ((passdata
= alloca(vmlen
))) {
2044 memset(passdata
, 0, vmlen
);
2045 prep_email_sub_vars(ast
, vmu
, msgnum
+ 1, context
, mailbox
, cidnum
, cidname
, dur
, date
, passdata
, vmlen
, category
);
2046 pbx_substitute_variables_helper(ast
, pagerbody
, passdata
, vmlen
);
2047 fprintf(p
, "%s\n", passdata
);
2048 } else ast_log(LOG_WARNING
, "Cannot allocate workspace for variable substitution\n");
2049 ast_channel_free(ast
);
2050 } else ast_log(LOG_WARNING
, "Cannot allocate the channel for variables substitution\n");
2052 fprintf(p
, "New %s long msg in box %s\n"
2053 "from %s, on %s", dur
, mailbox
, (cidname
? cidname
: (cidnum
? cidnum
: "unknown")), date
);
2056 snprintf(tmp2
, sizeof(tmp2
), "( %s < %s ; rm -f %s ) &", mailcmd
, tmp
, tmp
);
2057 ast_safe_system(tmp2
);
2058 if (option_debug
> 2)
2059 ast_log(LOG_DEBUG
, "Sent page to %s with command '%s'\n", pager
, mailcmd
);
2064 static int get_date(char *s
, int len
)
2071 ast_localtime(&t
, &tm
, NULL
);
2073 return strftime(s
, len
, "%a %b %e %r %Z %Y", &tm
);
2076 static int play_greeting(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *filename
, char *ecodes
)
2083 RETRIEVE(filename
, -1);
2084 if (ast_fileexists(filename
, NULL
, NULL
) > 0) {
2085 res
= ast_streamfile(chan
, filename
, chan
->language
);
2087 res
= ast_waitstream(chan
, ecodes
);
2089 if (success
== -1) {
2090 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
2092 ast_log(LOG_DEBUG
, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
2093 store_file(filename
, vmu
->mailbox
, vmu
->context
, -1);
2097 DISPOSE(filename
, -1);
2102 static int invent_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *ext
, int busy
, char *ecodes
)
2106 char dest
[PATH_MAX
];
2108 snprintf(fn
, sizeof(fn
), "%s%s/%s/greet", VM_SPOOL_DIR
, vmu
->context
, ext
);
2110 if ((res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, ext
, "greet"))) {
2111 ast_log(LOG_WARNING
, "Failed to make directory(%s)\n", fn
);
2115 res
= play_greeting(chan
, vmu
, fn
, ecodes
);
2117 /* File did not exist */
2118 res
= ast_stream_and_wait(chan
, "vm-theperson", chan
->language
, ecodes
);
2121 res
= ast_say_digit_str(chan
, ext
, ecodes
, chan
->language
);
2127 res
= ast_stream_and_wait(chan
, busy
? "vm-isonphone" : "vm-isunavail", chan
->language
, ecodes
);
2131 static void free_user(struct ast_vm_user
*vmu
)
2133 if (ast_test_flag(vmu
, VM_ALLOCED
))
2137 static void free_zone(struct vm_zone
*z
)
2142 static const char *mbox(int id
)
2144 static const char *msgs
[] = {
2156 return (id
>= 0 && id
< (sizeof(msgs
)/sizeof(msgs
[0]))) ? msgs
[id
] : "tmp";
2159 static int folder_int(const char *folder
)
2161 /*assume a NULL folder means INBOX*/
2164 if (!strcasecmp(folder
, "INBOX"))
2166 else if (!strcasecmp(folder
, "Old"))
2168 else if (!strcasecmp(folder
, "Work"))
2170 else if (!strcasecmp(folder
, "Family"))
2172 else if (!strcasecmp(folder
, "Friends"))
2174 else if (!strcasecmp(folder
, "Cust1"))
2176 else if (!strcasecmp(folder
, "Cust2"))
2178 else if (!strcasecmp(folder
, "Cust3"))
2180 else if (!strcasecmp(folder
, "Cust4"))
2182 else if (!strcasecmp(folder
, "Cust5"))
2184 else /*assume they meant INBOX if folder is not found otherwise*/
2190 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
2191 static int inboxcount(const char *mailbox
, int *newmsgs
, int *oldmsgs
)
2198 char tmp
[PATH_MAX
] = "";
2199 struct odbc_obj
*obj
;
2201 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 0 };
2208 /* If no mailbox, return immediately */
2209 if (ast_strlen_zero(mailbox
))
2212 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2214 context
= strchr(tmp
, '@');
2219 context
= "default";
2221 obj
= ast_odbc_request_obj(odbc_database
, 0);
2223 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table
, VM_SPOOL_DIR
, context
, tmp
, "INBOX");
2224 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
2226 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2227 ast_odbc_release_obj(obj
);
2230 res
= SQLFetch(stmt
);
2231 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2232 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
2233 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2234 ast_odbc_release_obj(obj
);
2237 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
2238 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2239 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
2240 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2241 ast_odbc_release_obj(obj
);
2244 *newmsgs
= atoi(rowdata
);
2245 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2247 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table
, VM_SPOOL_DIR
, context
, tmp
, "Old");
2248 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
2250 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2251 ast_odbc_release_obj(obj
);
2254 res
= SQLFetch(stmt
);
2255 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2256 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
2257 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2258 ast_odbc_release_obj(obj
);
2261 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
2262 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2263 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
2264 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2265 ast_odbc_release_obj(obj
);
2268 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2269 ast_odbc_release_obj(obj
);
2270 *oldmsgs
= atoi(rowdata
);
2273 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
2279 static int messagecount(const char *context
, const char *mailbox
, const char *folder
)
2281 struct odbc_obj
*obj
= NULL
;
2284 SQLHSTMT stmt
= NULL
;
2287 struct generic_prepare_struct gps
= { .sql
= sql
, .argc
= 0 };
2290 /* If no mailbox, return immediately */
2291 if (ast_strlen_zero(mailbox
))
2294 obj
= ast_odbc_request_obj(odbc_database
, 0);
2296 snprintf(sql
, sizeof(sql
), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table
, VM_SPOOL_DIR
, context
, mailbox
, folder
);
2297 stmt
= ast_odbc_prepare_and_execute(obj
, generic_prepare
, &gps
);
2299 ast_log(LOG_WARNING
, "SQL Execute error!\n[%s]\n\n", sql
);
2302 res
= SQLFetch(stmt
);
2303 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2304 ast_log(LOG_WARNING
, "SQL Fetch error!\n[%s]\n\n", sql
);
2305 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2308 res
= SQLGetData(stmt
, 1, SQL_CHAR
, rowdata
, sizeof(rowdata
), NULL
);
2309 if ((res
!= SQL_SUCCESS
) && (res
!= SQL_SUCCESS_WITH_INFO
)) {
2310 ast_log(LOG_WARNING
, "SQL Get Data error!\n[%s]\n\n", sql
);
2311 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2314 nummsgs
= atoi(rowdata
);
2315 SQLFreeHandle (SQL_HANDLE_STMT
, stmt
);
2317 ast_log(LOG_WARNING
, "Failed to obtain database object for '%s'!\n", odbc_database
);
2321 ast_odbc_release_obj(obj
);
2325 static int has_voicemail(const char *mailbox
, const char *folder
)
2327 char tmp
[256], *tmp2
= tmp
, *mbox
, *context
;
2328 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2329 while ((context
= mbox
= strsep(&tmp2
, ","))) {
2330 strsep(&context
, "@");
2331 if (ast_strlen_zero(context
))
2332 context
= "default";
2333 if (messagecount(context
, mbox
, folder
))
2339 #elif defined(IMAP_STORAGE)
2341 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
)
2343 char *myserveremail
= serveremail
;
2348 char tmp
[80] = "/tmp/astmail-XXXXXX";
2354 /*Greetings are not retrieved from IMAP, so there is no reason to attempt storing them there either*/
2358 /* Attach only the first format */
2359 fmt
= ast_strdupa(fmt
);
2361 strsep(&stringp
, "|");
2363 if (!ast_strlen_zero(vmu
->serveremail
))
2364 myserveremail
= vmu
->serveremail
;
2366 make_file(fn
, sizeof(fn
), dir
, msgnum
);
2368 if (ast_strlen_zero(vmu
->email
)) {
2369 /*we need the vmu->email to be set when we call make_email_file, but if we keep it set,
2370 * a duplicate e-mail will be created. So at the end of this function, we will revert back to an empty
2371 * string if tempcopy is 1
2373 ast_copy_string(vmu
->email
, vmu
->imapuser
, sizeof(vmu
->email
));
2377 if (!strcmp(fmt
, "wav49"))
2379 if (option_debug
> 2)
2380 ast_log(LOG_DEBUG
, "Storing file '%s', format '%s'\n", fn
, fmt
);
2381 /* Make a temporary file instead of piping directly to sendmail, in case the mail
2383 if ((p
= vm_mkftemp(tmp
)) == NULL
) {
2384 ast_log(LOG_WARNING
, "Unable to store '%s' (can't create temporary file)\n", fn
);
2386 *(vmu
->email
) = '\0';
2389 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);
2390 /* read mail file to memory */
2393 if ((buf
= ast_malloc(len
+1)) == NIL
) {
2394 ast_log(LOG_ERROR
, "Can't allocate %ld bytes to read message\n", len
+1);
2398 fread(buf
, len
, 1, p
);
2399 ((char *)buf
)[len
] = '\0';
2400 INIT(&str
, mail_string
, buf
, len
);
2401 init_mailstream(vms
, 0);
2402 imap_mailbox_name(mailbox
, sizeof(mailbox
), vms
, 0, 1);
2403 if (!mail_append(vms
->mailstream
, mailbox
, &str
))
2404 ast_log(LOG_ERROR
, "Error while sending the message to %s\n", mailbox
);
2408 if (option_debug
> 2)
2409 ast_log(LOG_DEBUG
, "%s stored\n", fn
);
2412 *(vmu
->email
) = '\0';
2417 static int messagecount(const char *context
, const char *mailbox
, const char *folder
)
2422 struct ast_vm_user
*vmu
, vmus
;
2423 struct vm_state
*vms_p
;
2425 int fold
= folder_int(folder
);
2427 if (ast_strlen_zero(mailbox
))
2430 /* We have to get the user before we can open the stream! */
2431 /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
2432 vmu
= find_user(&vmus
, context
, mailbox
);
2434 ast_log (LOG_ERROR
,"Couldn't find mailbox %s in context %s\n",mailbox
,context
);
2437 /* No IMAP account available */
2438 if (vmu
->imapuser
[0] == '\0') {
2439 ast_log (LOG_WARNING
,"IMAP user not set for mailbox %s\n",vmu
->mailbox
);
2444 /* check if someone is accessing this box right now... */
2445 vms_p
= get_vm_state_by_imapuser(vmu
->imapuser
,1);
2447 vms_p
= get_vm_state_by_mailbox(mailbox
,1);
2450 if (option_debug
> 2)
2451 ast_log (LOG_DEBUG
,"Returning before search - user is logged in\n");
2452 if (fold
== 0) {/*INBOX*/
2453 return vms_p
->newmessages
;
2455 if (fold
== 1) {/*Old messages*/
2456 return vms_p
->oldmessages
;
2460 /* add one if not there... */
2461 vms_p
= get_vm_state_by_imapuser(vmu
->imapuser
,0);
2463 vms_p
= get_vm_state_by_mailbox(mailbox
,0);
2467 if (option_debug
> 2)
2468 ast_log (LOG_DEBUG
,"Adding new vmstate for %s\n",vmu
->imapuser
);
2469 if (!(vms_p
= ast_calloc(1, sizeof(*vms_p
)))) {
2472 ast_copy_string(vms_p
->imapuser
,vmu
->imapuser
, sizeof(vms_p
->imapuser
));
2473 ast_copy_string(vms_p
->username
, mailbox
, sizeof(vms_p
->username
)); /* save for access from interactive entry point */
2474 vms_p
->mailstream
= NIL
; /* save for access from interactive entry point */
2475 if (option_debug
> 2)
2476 ast_log (LOG_DEBUG
,"Copied %s to %s\n",vmu
->imapuser
,vms_p
->imapuser
);
2478 /* set mailbox to INBOX! */
2479 ast_copy_string(vms_p
->curbox
, mbox(fold
), sizeof(vms_p
->curbox
));
2480 init_vm_state(vms_p
);
2481 vmstate_insert(vms_p
);
2483 ret
= init_mailstream(vms_p
, fold
);
2484 if (!vms_p
->mailstream
) {
2485 ast_log (LOG_ERROR
,"IMAP mailstream is NULL\n");
2489 pgm
= mail_newsearchpgm ();
2490 hdr
= mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox
);
2496 /* In the special case where fold is 1 (old messages) we have to do things a bit
2497 * differently. Old messages are stored in the INBOX but are marked as "seen"
2506 vms_p
->vmArrayIndex
= 0;
2507 mail_search_full (vms_p
->mailstream
, NULL
, pgm
, NIL
);
2509 vms_p
->newmessages
= vms_p
->vmArrayIndex
;
2511 vms_p
->oldmessages
= vms_p
->vmArrayIndex
;
2512 /*Freeing the searchpgm also frees the searchhdr*/
2513 mail_free_searchpgm(&pgm
);
2515 return vms_p
->vmArrayIndex
;
2517 mail_ping(vms_p
->mailstream
);
2521 static int inboxcount(const char *mailbox_context
, int *newmsgs
, int *oldmsgs
)
2523 char tmp
[PATH_MAX
] = "";
2533 if (option_debug
> 2)
2534 ast_log (LOG_DEBUG
,"Mailbox is set to %s\n",mailbox_context
);
2535 /* If no mailbox, return immediately */
2536 if (ast_strlen_zero(mailbox_context
))
2539 ast_copy_string(tmp
, mailbox_context
, sizeof(tmp
));
2540 context
= strchr(tmp
, '@');
2541 if (strchr(mailbox_context
, ',')) {
2543 ast_copy_string(tmp
, mailbox_context
, sizeof(tmp
));
2545 while ((cur
= strsep(&mb
, ", "))) {
2546 if (!ast_strlen_zero(cur
)) {
2547 if (inboxcount(cur
, newmsgs
? &tmpnew
: NULL
, oldmsgs
? &tmpold
: NULL
))
2564 context
= "default";
2565 mailboxnc
= (char *)mailbox_context
;
2568 if ((*newmsgs
= messagecount(context
, mailboxnc
, "INBOX")) < 0)
2572 if ((*oldmsgs
= messagecount(context
, mailboxnc
, "Old")) < 0)
2579 static int has_voicemail(const char *mailbox
, const char *folder
)
2581 char tmp
[256], *tmp2
, *mbox
, *context
;
2582 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2584 if (strchr(tmp2
, ',')) {
2585 while ((mbox
= strsep(&tmp2
, ","))) {
2586 if (!ast_strlen_zero(mbox
)) {
2587 if (has_voicemail(mbox
, folder
))
2592 if ((context
= strchr(tmp
, '@')))
2595 context
= "default";
2596 return messagecount(context
, tmp
, folder
) ? 1 : 0;
2599 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
)
2601 struct vm_state
*sendvms
= NULL
, *destvms
= NULL
;
2602 char messagestring
[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
2603 if (msgnum
>= recip
->maxmsg
) {
2604 ast_log(LOG_WARNING
, "Unable to copy mail, mailbox %s is full\n", recip
->mailbox
);
2607 if (!(sendvms
= get_vm_state_by_imapuser(vmu
->imapuser
, 0))) {
2608 ast_log(LOG_ERROR
, "Couldn't get vm_state for originator's mailbox!!\n");
2611 if (!(destvms
= get_vm_state_by_imapuser(recip
->imapuser
, 0))) {
2612 ast_log(LOG_ERROR
, "Couldn't get vm_state for destination mailbox!\n");
2615 snprintf(messagestring
, sizeof(messagestring
), "%ld", sendvms
->msgArray
[msgnum
]);
2616 if ((mail_copy(sendvms
->mailstream
, messagestring
, (char *) mbox(imbox
)) == T
))
2618 ast_log(LOG_WARNING
, "Unable to copy message from mailbox %s to mailbox %s\n", vmu
->mailbox
, recip
->mailbox
);
2623 #ifndef IMAP_STORAGE
2624 /* copy message only used by file storage */
2625 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
)
2627 char fromdir
[PATH_MAX
], todir
[PATH_MAX
], frompath
[PATH_MAX
], topath
[PATH_MAX
];
2628 const char *frombox
= mbox(imbox
);
2631 ast_log(LOG_NOTICE
, "Copying message from %s@%s to %s@%s\n", vmu
->mailbox
, vmu
->context
, recip
->mailbox
, recip
->context
);
2633 create_dirpath(todir
, sizeof(todir
), recip
->context
, recip
->mailbox
, "INBOX");
2636 make_dir(fromdir
, sizeof(fromdir
), vmu
->context
, vmu
->mailbox
, frombox
);
2638 ast_copy_string(fromdir
, dir
, sizeof(fromdir
));
2640 make_file(frompath
, sizeof(frompath
), fromdir
, msgnum
);
2642 if (vm_lock_path(todir
))
2643 return ERROR_LOCK_PATH
;
2647 make_file(topath
, sizeof(topath
), todir
, recipmsgnum
);
2648 if (!EXISTS(todir
, recipmsgnum
, topath
, chan
->language
))
2651 } while (recipmsgnum
< recip
->maxmsg
);
2652 if (recipmsgnum
< recip
->maxmsg
) {
2653 COPY(fromdir
, msgnum
, todir
, recipmsgnum
, recip
->mailbox
, recip
->context
, frompath
, topath
);
2655 ast_log(LOG_ERROR
, "Recipient mailbox %s@%s is full\n", recip
->mailbox
, recip
->context
);
2657 ast_unlock_path(todir
);
2658 notify_new_message(chan
, recip
, recipmsgnum
, duration
, fmt
, S_OR(chan
->cid
.cid_num
, NULL
), S_OR(chan
->cid
.cid_name
, NULL
));
2663 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2664 static int messagecount(const char *context
, const char *mailbox
, const char *folder
)
2666 return __has_voicemail(context
, mailbox
, folder
, 0);
2670 static int __has_voicemail(const char *context
, const char *mailbox
, const char *folder
, int shortcircuit
)
2678 /* If no mailbox, return immediately */
2679 if (ast_strlen_zero(mailbox
))
2682 context
= "default";
2683 snprintf(fn
, sizeof(fn
), "%s%s/%s/%s", VM_SPOOL_DIR
, context
, mailbox
, folder
);
2687 while ((de
= readdir(dir
))) {
2688 if (!strncasecmp(de
->d_name
, "msg", 3)) {
2692 } else if (!strncasecmp(de
->d_name
+ 8, "txt", 3))
2701 static int has_voicemail(const char *mailbox
, const char *folder
)
2703 char tmp
[256], *tmp2
= tmp
, *mbox
, *context
;
2704 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2705 while ((mbox
= strsep(&tmp2
, ","))) {
2706 if ((context
= strchr(mbox
, '@')))
2709 context
= "default";
2710 if (__has_voicemail(context
, mbox
, folder
, 1))
2717 static int inboxcount(const char *mailbox
, int *newmsgs
, int *oldmsgs
)
2726 /* If no mailbox, return immediately */
2727 if (ast_strlen_zero(mailbox
))
2729 if (strchr(mailbox
, ',')) {
2733 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2735 while ((cur
= strsep(&mb
, ", "))) {
2736 if (!ast_strlen_zero(cur
)) {
2737 if (inboxcount(cur
, newmsgs
? &tmpnew
: NULL
, oldmsgs
? &tmpold
: NULL
))
2749 ast_copy_string(tmp
, mailbox
, sizeof(tmp
));
2750 context
= strchr(tmp
, '@');
2755 context
= "default";
2757 *newmsgs
= __has_voicemail(context
, tmp
, "INBOX", 0);
2759 *oldmsgs
= __has_voicemail(context
, tmp
, "Old", 0);
2765 static void run_externnotify(char *context
, char *extension
)
2767 char arguments
[255];
2768 char ext_context
[256] = "";
2769 int newvoicemails
= 0, oldvoicemails
= 0;
2770 struct ast_smdi_mwi_message
*mwi_msg
;
2772 if (!ast_strlen_zero(context
))
2773 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", extension
, context
);
2775 ast_copy_string(ext_context
, extension
, sizeof(ext_context
));
2777 if (!strcasecmp(externnotify
, "smdi")) {
2778 if (ast_app_has_voicemail(ext_context
, NULL
))
2779 ast_smdi_mwi_set(smdi_iface
, extension
);
2781 ast_smdi_mwi_unset(smdi_iface
, extension
);
2783 if ((mwi_msg
= ast_smdi_mwi_message_wait_station(smdi_iface
, SMDI_MWI_WAIT_TIMEOUT
, extension
))) {
2784 ast_log(LOG_ERROR
, "Error executing SMDI MWI change for %s\n", extension
);
2785 if (!strncmp(mwi_msg
->cause
, "INV", 3))
2786 ast_log(LOG_ERROR
, "Invalid MWI extension: %s\n", mwi_msg
->fwd_st
);
2787 else if (!strncmp(mwi_msg
->cause
, "BLK", 3))
2788 ast_log(LOG_WARNING
, "MWI light was already on or off for %s\n", mwi_msg
->fwd_st
);
2789 ast_log(LOG_WARNING
, "The switch reported '%s'\n", mwi_msg
->cause
);
2790 ASTOBJ_UNREF(mwi_msg
, ast_smdi_mwi_message_destroy
);
2793 ast_log(LOG_DEBUG
, "Successfully executed SMDI MWI change for %s\n", extension
);
2795 } else if (!ast_strlen_zero(externnotify
)) {
2796 if (inboxcount(ext_context
, &newvoicemails
, &oldvoicemails
)) {
2797 ast_log(LOG_ERROR
, "Problem in calculating number of voicemail messages available for extension %s\n", extension
);
2799 snprintf(arguments
, sizeof(arguments
), "%s %s %s %d&", externnotify
, context
, extension
, newvoicemails
);
2801 ast_log(LOG_DEBUG
, "Executing %s\n", arguments
);
2802 ast_safe_system(arguments
);
2807 struct leave_vm_options
{
2809 signed char record_gain
;
2812 static int leave_voicemail(struct ast_channel
*chan
, char *ext
, struct leave_vm_options
*options
)
2815 int newmsgs
, oldmsgs
;
2816 struct vm_state
*vms
= NULL
;
2818 char txtfile
[PATH_MAX
], tmptxtfile
[PATH_MAX
];
2829 char dir
[PATH_MAX
], tmpdir
[PATH_MAX
];
2830 char dest
[PATH_MAX
];
2832 char prefile
[PATH_MAX
] = "";
2833 char tempfile
[PATH_MAX
] = "";
2834 char ext_context
[256] = "";
2837 char ecodes
[16] = "#";
2838 char tmp
[1024] = "", *tmpptr
;
2839 struct ast_vm_user
*vmu
;
2840 struct ast_vm_user svm
;
2841 const char *category
= NULL
;
2843 ast_copy_string(tmp
, ext
, sizeof(tmp
));
2845 context
= strchr(tmp
, '@');
2848 tmpptr
= strchr(context
, '&');
2850 tmpptr
= strchr(ext
, '&');
2856 category
= pbx_builtin_getvar_helper(chan
, "VM_CATEGORY");
2858 if (option_debug
> 2)
2859 ast_log(LOG_DEBUG
, "Before find_user\n");
2860 if (!(vmu
= find_user(&svm
, context
, ext
))) {
2861 ast_log(LOG_WARNING
, "No entry in voicemail config file for '%s'\n", ext
);
2862 if (ast_test_flag(options
, OPT_PRIORITY_JUMP
) || ast_opt_priority_jumping
)
2863 ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101);
2864 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
2867 /* Setup pre-file if appropriate */
2868 if (strcmp(vmu
->context
, "default"))
2869 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", ext
, vmu
->context
);
2871 ast_copy_string(ext_context
, vmu
->mailbox
, sizeof(ext_context
));
2872 if (ast_test_flag(options
, OPT_BUSY_GREETING
)) {
2873 res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, ext
, "busy");
2874 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/busy", VM_SPOOL_DIR
, vmu
->context
, ext
);
2875 } else if (ast_test_flag(options
, OPT_UNAVAIL_GREETING
)) {
2876 res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, ext
, "unavail");
2877 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/unavail", VM_SPOOL_DIR
, vmu
->context
, ext
);
2879 snprintf(tempfile
, sizeof(tempfile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, ext
);
2880 if ((res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, ext
, "temp"))) {
2881 ast_log(LOG_WARNING
, "Failed to make directory (%s)\n", tempfile
);
2884 RETRIEVE(tempfile
, -1);
2885 if (ast_fileexists(tempfile
, NULL
, NULL
) > 0)
2886 ast_copy_string(prefile
, tempfile
, sizeof(prefile
));
2887 DISPOSE(tempfile
, -1);
2888 /* It's easier just to try to make it than to check for its existence */
2889 create_dirpath(dir
, sizeof(dir
), vmu
->context
, ext
, "INBOX");
2890 create_dirpath(tmpdir
, sizeof(tmpdir
), vmu
->context
, ext
, "tmp");
2892 /* Check current or macro-calling context for special extensions */
2893 if (ast_test_flag(vmu
, VM_OPERATOR
)) {
2894 if (!ast_strlen_zero(vmu
->exit
)) {
2895 if (ast_exists_extension(chan
, vmu
->exit
, "o", 1, chan
->cid
.cid_num
)) {
2896 strncat(ecodes
, "0", sizeof(ecodes
) - strlen(ecodes
) - 1);
2899 } else if (ast_exists_extension(chan
, chan
->context
, "o", 1, chan
->cid
.cid_num
)) {
2900 strncat(ecodes
, "0", sizeof(ecodes
) - strlen(ecodes
) - 1);
2903 else if (!ast_strlen_zero(chan
->macrocontext
) && ast_exists_extension(chan
, chan
->macrocontext
, "o", 1, chan
->cid
.cid_num
)) {
2904 strncat(ecodes
, "0", sizeof(ecodes
) - strlen(ecodes
) - 1);
2909 if (!ast_strlen_zero(vmu
->exit
)) {
2910 if (ast_exists_extension(chan
, vmu
->exit
, "a", 1, chan
->cid
.cid_num
))
2911 strncat(ecodes
, "*", sizeof(ecodes
) - strlen(ecodes
) - 1);
2912 } else if (ast_exists_extension(chan
, chan
->context
, "a", 1, chan
->cid
.cid_num
))
2913 strncat(ecodes
, "*", sizeof(ecodes
) - strlen(ecodes
) - 1);
2914 else if (!ast_strlen_zero(chan
->macrocontext
) && ast_exists_extension(chan
, chan
->macrocontext
, "a", 1, chan
->cid
.cid_num
)) {
2915 strncat(ecodes
, "*", sizeof(ecodes
) - strlen(ecodes
) - 1);
2919 /* Play the beginning intro if desired */
2920 if (!ast_strlen_zero(prefile
)) {
2921 res
= play_greeting(chan
, vmu
, prefile
, ecodes
);
2923 /* The file did not exist */
2925 ast_log(LOG_DEBUG
, "%s doesn't exist, doing what we can\n", prefile
);
2926 res
= invent_message(chan
, vmu
, ext
, ast_test_flag(options
, OPT_BUSY_GREETING
), ecodes
);
2930 ast_log(LOG_DEBUG
, "Hang up during prefile playback\n");
2932 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
2937 /* On a '#' we skip the instructions */
2938 ast_set_flag(options
, OPT_SILENT
);
2941 if (!res
&& !ast_test_flag(options
, OPT_SILENT
)) {
2942 res
= ast_stream_and_wait(chan
, INTRO
, chan
->language
, ecodes
);
2944 ast_set_flag(options
, OPT_SILENT
);
2949 ast_stopstream(chan
);
2950 /* Check for a '*' here in case the caller wants to escape from voicemail to something
2951 other than the operator -- an automated attendant or mailbox login for example */
2953 chan
->exten
[0] = 'a';
2954 chan
->exten
[1] = '\0';
2955 if (!ast_strlen_zero(vmu
->exit
)) {
2956 ast_copy_string(chan
->context
, vmu
->exit
, sizeof(chan
->context
));
2957 } else if (ausemacro
&& !ast_strlen_zero(chan
->macrocontext
)) {
2958 ast_copy_string(chan
->context
, chan
->macrocontext
, sizeof(chan
->context
));
2962 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "USEREXIT");
2966 /* Check for a '0' here */
2969 if (ouseexten
|| ousemacro
) {
2970 chan
->exten
[0] = 'o';
2971 chan
->exten
[1] = '\0';
2972 if (!ast_strlen_zero(vmu
->exit
)) {
2973 ast_copy_string(chan
->context
, vmu
->exit
, sizeof(chan
->context
));
2974 } else if (ousemacro
&& !ast_strlen_zero(chan
->macrocontext
)) {
2975 ast_copy_string(chan
->context
, chan
->macrocontext
, sizeof(chan
->context
));
2977 ast_play_and_wait(chan
, "transfer");
2980 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "USEREXIT");
2986 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
2989 /* The meat of recording the message... All the announcements and beeps have been played*/
2990 ast_copy_string(fmt
, vmfmts
, sizeof(fmt
));
2991 if (!ast_strlen_zero(fmt
)) {
2995 /* Is ext a mailbox? */
2996 /* must open stream for this user to get info! */
2997 res
= inboxcount(ext_context
, &newmsgs
, &oldmsgs
);
2999 ast_log(LOG_NOTICE
,"Can not leave voicemail, unable to count messages\n");
3002 if (!(vms
= get_vm_state_by_mailbox(ext
,0))) {
3003 /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
3005 if (!(vms
= ast_calloc(1, sizeof(*vms
)))) {
3006 ast_log(LOG_ERROR
, "Couldn't allocate necessary space\n");
3009 ast_copy_string(vms
->imapuser
, vmu
->imapuser
, sizeof(vms
->imapuser
));
3010 ast_copy_string(vms
->username
, ext
, sizeof(vms
->username
));
3011 vms
->mailstream
= NIL
;
3012 if (option_debug
> 2)
3013 ast_log(LOG_DEBUG
, "Copied %s to %s\n", vmu
->imapuser
, vms
->imapuser
);
3015 ast_copy_string(vms
->curbox
, mbox(0), sizeof(vms
->curbox
));
3017 vmstate_insert(vms
);
3018 vms
= get_vm_state_by_mailbox(ext
,0);
3021 /* here is a big difference! We add one to it later */
3022 msgnum
= newmsgs
+ oldmsgs
;
3023 if (option_debug
> 2)
3024 ast_log(LOG_DEBUG
, "Messagecount set to %d\n",msgnum
);
3025 snprintf(fn
, sizeof(fn
), "%s/imap/msg%s%04d", VM_SPOOL_DIR
, vmu
->mailbox
, msgnum
);
3026 /* set variable for compatability */
3027 pbx_builtin_setvar_helper(chan
, "VM_MESSAGEFILE", "IMAP_STORAGE");
3029 /* Check if mailbox is full */
3030 check_quota(vms
, imapfolder
);
3031 if (vms
->quota_limit
&& vms
->quota_usage
>= vms
->quota_limit
) {
3033 ast_log(LOG_DEBUG
, "*** QUOTA EXCEEDED!! %u >= %u\n", vms
->quota_usage
, vms
->quota_limit
);
3034 ast_play_and_wait(chan
, "vm-mailboxfull");
3037 if (option_debug
> 2)
3038 ast_log(LOG_DEBUG
, "Checking message number quota - mailbox has %d messages, maximum is set to %d\n",msgnum
,vmu
->maxmsg
);
3039 if (msgnum
>= vmu
->maxmsg
) {
3040 res
= ast_streamfile(chan
, "vm-mailboxfull", chan
->language
);
3042 res
= ast_waitstream(chan
, "");
3043 ast_log(LOG_WARNING
, "No more messages possible\n");
3044 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3048 /* Check if we have exceeded maxmsg */
3049 if (msgnum
>= vmu
->maxmsg
) {
3050 ast_log(LOG_WARNING
, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum
, vmu
->maxmsg
);
3051 ast_play_and_wait(chan
, "vm-mailboxfull");
3054 /* here is a big difference! We add one to it later */
3055 if (option_debug
> 2)
3056 ast_log(LOG_DEBUG
, "Messagecount set to %d\n",msgnum
);
3058 if (count_messages(vmu
, dir
) >= vmu
->maxmsg
) {
3059 res
= ast_streamfile(chan
, "vm-mailboxfull", chan
->language
);
3061 res
= ast_waitstream(chan
, "");
3062 ast_log(LOG_WARNING
, "No more messages possible\n");
3063 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3068 snprintf(tmptxtfile
, sizeof(tmptxtfile
), "%s/XXXXXX", tmpdir
);
3069 txtdes
= mkstemp(tmptxtfile
);
3070 chmod(tmptxtfile
, VOICEMAIL_FILE_MODE
& ~my_umask
);
3072 res
= ast_streamfile(chan
, "vm-mailboxfull", chan
->language
);
3074 res
= ast_waitstream(chan
, "");
3075 ast_log(LOG_ERROR
, "Unable to create message file: %s\n", strerror(errno
));
3076 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3080 /* Now play the beep once we have the message number for our next message. */
3082 /* Unless we're *really* silent, try to send the beep */
3083 res
= ast_stream_and_wait(chan
, "beep", chan
->language
, "");
3086 /* Store information */
3087 txt
= fdopen(txtdes
, "w+");
3089 get_date(date
, sizeof(date
));
3092 "; Message Information file\n"
3111 ast_callerid_merge(callerid
, sizeof(callerid
), S_OR(chan
->cid
.cid_name
, NULL
), S_OR(chan
->cid
.cid_num
, NULL
), "Unknown"),
3112 date
, (long)time(NULL
),
3113 category
? category
: "");
3115 ast_log(LOG_WARNING
, "Error opening text file for output\n");
3117 res
= play_record_review(chan
, NULL
, tmptxtfile
, vmmaxmessage
, fmt
, 1, vmu
, &duration
, NULL
, options
->record_gain
, vms
);
3119 res
= play_record_review(chan
, NULL
, tmptxtfile
, vmmaxmessage
, fmt
, 1, vmu
, &duration
, NULL
, options
->record_gain
, NULL
);
3123 if (duration
< vmminmessage
) {
3125 if (option_verbose
> 2)
3126 ast_verbose( VERBOSE_PREFIX_3
"Recording was %d seconds long but needs to be at least %d - abandoning\n", duration
, vmminmessage
);
3127 ast_filedelete(tmptxtfile
, NULL
);
3130 fprintf(txt
, "duration=%d\n", duration
);
3132 if (vm_lock_path(dir
)) {
3133 ast_log(LOG_ERROR
, "Couldn't lock directory %s. Voicemail will be lost.\n", dir
);
3135 ast_filedelete(tmptxtfile
, NULL
);
3137 } else if (ast_fileexists(tmptxtfile
, NULL
, NULL
) <= 0) {
3139 ast_log(LOG_DEBUG
, "The recorded media file is gone, so we should remove the .txt file too!\n");
3141 ast_unlock_path(dir
);
3144 make_file(fn
, sizeof(fn
), dir
, msgnum
);
3145 if (!EXISTS(dir
, msgnum
, fn
, NULL
))
3150 /* assign a variable with the name of the voicemail file */
3151 #ifndef IMAP_STORAGE
3152 pbx_builtin_setvar_helper(chan
, "VM_MESSAGEFILE", fn
);
3154 pbx_builtin_setvar_helper(chan
, "VM_MESSAGEFILE", "IMAP_STORAGE");
3157 snprintf(txtfile
, sizeof(txtfile
), "%s.txt", fn
);
3158 ast_filerename(tmptxtfile
, fn
, NULL
);
3159 rename(tmptxtfile
, txtfile
);
3161 ast_unlock_path(dir
);
3162 /* We must store the file first, before copying the message, because
3163 * ODBC storage does the entire copy with SQL.
3165 if (ast_fileexists(fn
, NULL
, NULL
) > 0) {
3166 STORE(dir
, vmu
->mailbox
, vmu
->context
, msgnum
, chan
, vmu
, fmt
, duration
, vms
);
3169 /* Are there to be more recipients of this message? */
3171 struct ast_vm_user recipu
, *recip
;
3172 char *exten
, *context
;
3174 exten
= strsep(&tmpptr
, "&");
3175 context
= strchr(exten
, '@');
3180 if ((recip
= find_user(&recipu
, context
, exten
))) {
3181 copy_message(chan
, vmu
, 0, msgnum
, duration
, recip
, fmt
, dir
);
3185 /* Notification and disposal needs to happen after the copy, though. */
3186 if (ast_fileexists(fn
, NULL
, NULL
)) {
3187 notify_new_message(chan
, vmu
, msgnum
, duration
, fmt
, S_OR(chan
->cid
.cid_num
, NULL
), S_OR(chan
->cid
.cid_name
, NULL
));
3188 DISPOSE(dir
, msgnum
);
3198 if (duration
< vmminmessage
)
3199 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
3200 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
3202 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "SUCCESS");
3204 ast_log(LOG_WARNING
, "No format for saving voicemail?\n");
3211 #ifndef IMAP_STORAGE
3212 static int resequence_mailbox(struct ast_vm_user
*vmu
, char *dir
)
3214 /* we know max messages, so stop process when number is hit */
3220 if (vm_lock_path(dir
))
3221 return ERROR_LOCK_PATH
;
3223 for (x
= 0, dest
= 0; x
< vmu
->maxmsg
; x
++) {
3224 make_file(sfn
, sizeof(sfn
), dir
, x
);
3225 if (EXISTS(dir
, x
, sfn
, NULL
)) {
3228 make_file(dfn
, sizeof(dfn
), dir
, dest
);
3229 RENAME(dir
, x
, vmu
->mailbox
, vmu
->context
, dir
, dest
, sfn
, dfn
);
3235 ast_unlock_path(dir
);
3241 static int say_and_wait(struct ast_channel
*chan
, int num
, const char *language
)
3244 d
= ast_say_number(chan
, num
, AST_DIGIT_ANY
, language
, (char *) NULL
);
3248 static int save_to_folder(struct ast_vm_user
*vmu
, struct vm_state
*vms
, int msg
, int box
)
3251 /* we must use mbox(x) folder names, and copy the message there */
3256 /* if save to Old folder, just leave in INBOX */
3257 if (box
== 1) return 10;
3258 /* get the real IMAP message number for this message */
3259 snprintf(sequence
, sizeof(sequence
), "%ld", vms
->msgArray
[msg
]);
3260 if (option_debug
> 2)
3261 ast_log(LOG_DEBUG
, "Copying sequence %s to mailbox %s\n",sequence
,(char *) mbox(box
));
3262 res
= mail_copy(vms
->mailstream
,sequence
,(char *) mbox(box
));
3263 if (res
== 1) return 0;
3266 char *dir
= vms
->curdir
;
3267 char *username
= vms
->username
;
3268 char *context
= vmu
->context
;
3271 char ddir
[PATH_MAX
];
3272 const char *dbox
= mbox(box
);
3274 make_file(sfn
, sizeof(sfn
), dir
, msg
);
3275 create_dirpath(ddir
, sizeof(ddir
), context
, username
, dbox
);
3277 if (vm_lock_path(ddir
))
3278 return ERROR_LOCK_PATH
;
3280 for (x
= 0; x
< vmu
->maxmsg
; x
++) {
3281 make_file(dfn
, sizeof(dfn
), ddir
, x
);
3282 if (!EXISTS(ddir
, x
, dfn
, NULL
))
3285 if (x
>= vmu
->maxmsg
) {
3286 ast_unlock_path(ddir
);
3287 return ERROR_MAILBOX_FULL
;
3289 if (strcmp(sfn
, dfn
)) {
3290 COPY(dir
, msg
, ddir
, x
, username
, context
, sfn
, dfn
);
3292 ast_unlock_path(ddir
);
3297 static int adsi_logo(unsigned char *buf
)
3300 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_CENT
, 0, "Comedian Mail", "");
3301 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_CENT
, 0, "(C)2002-2006 Digium, Inc.", "");
3305 static int adsi_load_vmail(struct ast_channel
*chan
, int *useadsi
)
3307 unsigned char buf
[256];
3313 bytes
+= ast_adsi_data_mode(buf
+ bytes
);
3314 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3317 bytes
+= adsi_logo(buf
);
3318 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Downloading Scripts", "");
3320 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " .", "");
3322 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3323 bytes
+= ast_adsi_data_mode(buf
+ bytes
);
3324 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3326 if (ast_adsi_begin_download(chan
, addesc
, adsifdn
, adsisec
, adsiver
)) {
3328 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Load Cancelled.", "");
3329 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "ADSI Unavailable", "");
3330 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3331 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3332 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3339 bytes
+= ast_adsi_logo(buf
);
3340 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Downloading Scripts", "");
3341 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " ..", "");
3342 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3343 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3346 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 0, "Listen", "Listen", "1", 1);
3347 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 1, "Folder", "Folder", "2", 1);
3348 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 2, "Advanced", "Advnced", "3", 1);
3349 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 3, "Options", "Options", "0", 1);
3350 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 4, "Help", "Help", "*", 1);
3351 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 5, "Exit", "Exit", "#", 1);
3352 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
3355 /* Add another dot */
3357 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " ...", "");
3358 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3360 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3361 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3365 /* These buttons we load but don't use yet */
3366 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 6, "Previous", "Prev", "4", 1);
3367 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 8, "Repeat", "Repeat", "5", 1);
3368 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 7, "Delete", "Delete", "7", 1);
3369 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 9, "Next", "Next", "6", 1);
3370 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 10, "Save", "Save", "9", 1);
3371 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 11, "Undelete", "Restore", "7", 1);
3372 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
3375 /* Add another dot */
3377 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " ....", "");
3378 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3379 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3384 snprintf(num
, sizeof(num
), "%d", x
);
3385 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 12 + x
, mbox(x
), mbox(x
), num
, 1);
3387 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 12 + 5, "Cancel", "Cancel", "#", 1);
3388 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
3391 /* Add another dot */
3393 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, " .....", "");
3394 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3395 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3398 if (ast_adsi_end_download(chan
)) {
3400 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Download Unsuccessful.", "");
3401 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "ADSI Unavailable", "");
3402 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3403 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3404 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3408 bytes
+= ast_adsi_download_disconnect(buf
+ bytes
);
3409 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3410 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DOWNLOAD
);
3413 ast_log(LOG_DEBUG
, "Done downloading scripts...\n");
3418 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, " ......", "");
3419 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3422 ast_log(LOG_DEBUG
, "Restarting session...\n");
3425 /* Load the session now */
3426 if (ast_adsi_load_session(chan
, adsifdn
, adsiver
, 1) == 1) {
3428 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Scripts Loaded!", "");
3430 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Load Failed!", "");
3432 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3436 static void adsi_begin(struct ast_channel
*chan
, int *useadsi
)
3439 if (!ast_adsi_available(chan
))
3441 x
= ast_adsi_load_session(chan
, adsifdn
, adsiver
, 1);
3445 if (adsi_load_vmail(chan
, useadsi
)) {
3446 ast_log(LOG_WARNING
, "Unable to upload voicemail scripts\n");
3453 static void adsi_login(struct ast_channel
*chan
)
3455 unsigned char buf
[256];
3457 unsigned char keys
[8];
3459 if (!ast_adsi_available(chan
))
3464 /* Set one key for next */
3465 keys
[3] = ADSI_KEY_APPS
+ 3;
3467 bytes
+= adsi_logo(buf
+ bytes
);
3468 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, " ", "");
3469 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, " ", "");
3470 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3471 bytes
+= ast_adsi_input_format(buf
+ bytes
, 1, ADSI_DIR_FROM_LEFT
, 0, "Mailbox: ******", "");
3472 bytes
+= ast_adsi_input_control(buf
+ bytes
, ADSI_COMM_PAGE
, 4, 1, 1, ADSI_JUST_LEFT
);
3473 bytes
+= ast_adsi_load_soft_key(buf
+ bytes
, ADSI_KEY_APPS
+ 3, "Enter", "Enter", "#", 1);
3474 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3475 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3476 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3479 static void adsi_password(struct ast_channel
*chan
)
3481 unsigned char buf
[256];
3483 unsigned char keys
[8];
3485 if (!ast_adsi_available(chan
))
3490 /* Set one key for next */
3491 keys
[3] = ADSI_KEY_APPS
+ 3;
3493 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3494 bytes
+= ast_adsi_input_format(buf
+ bytes
, 1, ADSI_DIR_FROM_LEFT
, 0, "Password: ******", "");
3495 bytes
+= ast_adsi_input_control(buf
+ bytes
, ADSI_COMM_PAGE
, 4, 0, 1, ADSI_JUST_LEFT
);
3496 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3497 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3498 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3501 static void adsi_folders(struct ast_channel
*chan
, int start
, char *label
)
3503 unsigned char buf
[256];
3505 unsigned char keys
[8];
3508 if (!ast_adsi_available(chan
))
3512 y
= ADSI_KEY_APPS
+ 12 + start
+ x
;
3513 if (y
> ADSI_KEY_APPS
+ 12 + 4)
3515 keys
[x
] = ADSI_KEY_SKT
| y
;
3517 keys
[5] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 17);
3521 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_CENT
, 0, label
, "");
3522 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_CENT
, 0, " ", "");
3523 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3524 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3525 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3527 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3530 static void adsi_message(struct ast_channel
*chan
, struct vm_state
*vms
)
3533 unsigned char buf
[256];
3534 char buf1
[256], buf2
[256];
3540 char datetime
[21]="";
3543 unsigned char keys
[8];
3547 if (!ast_adsi_available(chan
))
3550 /* Retrieve important info */
3551 snprintf(fn2
, sizeof(fn2
), "%s.txt", vms
->fn
);
3552 f
= fopen(fn2
, "r");
3555 fgets((char *)buf
, sizeof(buf
), f
);
3558 stringp
= (char *)buf
;
3559 strsep(&stringp
, "=");
3560 val
= strsep(&stringp
, "=");
3561 if (!ast_strlen_zero(val
)) {
3562 if (!strcmp((char *)buf
, "callerid"))
3563 ast_copy_string(cid
, val
, sizeof(cid
));
3564 if (!strcmp((char *)buf
, "origdate"))
3565 ast_copy_string(datetime
, val
, sizeof(datetime
));
3571 /* New meaning for keys */
3573 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 6 + x
);
3578 /* No prev key, provide "Folder" instead */
3579 keys
[0] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
3581 if (vms
->curmsg
>= vms
->lastmsg
) {
3582 /* If last message ... */
3584 /* but not only message, provide "Folder" instead */
3585 keys
[3] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
3586 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3589 /* Otherwise if only message, leave blank */
3594 if (!ast_strlen_zero(cid
)) {
3595 ast_callerid_parse(cid
, &name
, &num
);
3599 name
= "Unknown Caller";
3601 /* If deleted, show "undeleted" */
3603 if (vms
->deleted
[vms
->curmsg
])
3604 keys
[1] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 11);
3607 keys
[5] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 5);
3608 snprintf(buf1
, sizeof(buf1
), "%s%s", vms
->curbox
,
3609 strcasecmp(vms
->curbox
, "INBOX") ? " Messages" : "");
3610 snprintf(buf2
, sizeof(buf2
), "Message %d of %d", vms
->curmsg
+ 1, vms
->lastmsg
+ 1);
3612 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_LEFT
, 0, buf1
, "");
3613 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_LEFT
, 0, buf2
, "");
3614 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_LEFT
, 0, name
, "");
3615 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_LEFT
, 0, datetime
, "");
3616 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3617 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3618 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3620 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3623 static void adsi_delete(struct ast_channel
*chan
, struct vm_state
*vms
)
3626 unsigned char buf
[256];
3627 unsigned char keys
[8];
3631 if (!ast_adsi_available(chan
))
3634 /* New meaning for keys */
3636 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 6 + x
);
3642 /* No prev key, provide "Folder" instead */
3643 keys
[0] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
3645 if (vms
->curmsg
>= vms
->lastmsg
) {
3646 /* If last message ... */
3648 /* but not only message, provide "Folder" instead */
3649 keys
[3] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 1);
3651 /* Otherwise if only message, leave blank */
3656 /* If deleted, show "undeleted" */
3657 if (vms
->deleted
[vms
->curmsg
])
3658 keys
[1] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 11);
3661 keys
[5] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ 5);
3662 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3663 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3665 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3668 static void adsi_status(struct ast_channel
*chan
, struct vm_state
*vms
)
3670 unsigned char buf
[256] = "";
3671 char buf1
[256] = "", buf2
[256] = "";
3673 unsigned char keys
[8];
3676 char *newm
= (vms
->newmessages
== 1) ? "message" : "messages";
3677 char *oldm
= (vms
->oldmessages
== 1) ? "message" : "messages";
3678 if (!ast_adsi_available(chan
))
3680 if (vms
->newmessages
) {
3681 snprintf(buf1
, sizeof(buf1
), "You have %d new", vms
->newmessages
);
3682 if (vms
->oldmessages
) {
3683 strncat(buf1
, " and", sizeof(buf1
) - strlen(buf1
) - 1);
3684 snprintf(buf2
, sizeof(buf2
), "%d old %s.", vms
->oldmessages
, oldm
);
3686 snprintf(buf2
, sizeof(buf2
), "%s.", newm
);
3688 } else if (vms
->oldmessages
) {
3689 snprintf(buf1
, sizeof(buf1
), "You have %d old", vms
->oldmessages
);
3690 snprintf(buf2
, sizeof(buf2
), "%s.", oldm
);
3692 strcpy(buf1
, "You have no messages.");
3696 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_LEFT
, 0, buf1
, "");
3697 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_LEFT
, 0, buf2
, "");
3698 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3701 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ x
);
3705 /* Don't let them listen if there are none */
3706 if (vms
->lastmsg
< 0)
3708 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3710 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3712 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3715 static void adsi_status2(struct ast_channel
*chan
, struct vm_state
*vms
)
3717 unsigned char buf
[256] = "";
3718 char buf1
[256] = "", buf2
[256] = "";
3720 unsigned char keys
[8];
3723 char *mess
= (vms
->lastmsg
== 0) ? "message" : "messages";
3725 if (!ast_adsi_available(chan
))
3728 /* Original command keys */
3730 keys
[x
] = ADSI_KEY_SKT
| (ADSI_KEY_APPS
+ x
);
3735 if ((vms
->lastmsg
+ 1) < 1)
3738 snprintf(buf1
, sizeof(buf1
), "%s%s has", vms
->curbox
,
3739 strcasecmp(vms
->curbox
, "INBOX") ? " folder" : "");
3741 if (vms
->lastmsg
+ 1)
3742 snprintf(buf2
, sizeof(buf2
), "%d %s.", vms
->lastmsg
+ 1, mess
);
3744 strcpy(buf2
, "no messages.");
3745 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 1, ADSI_JUST_LEFT
, 0, buf1
, "");
3746 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 2, ADSI_JUST_LEFT
, 0, buf2
, "");
3747 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_LEFT
, 0, "", "");
3748 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3749 bytes
+= ast_adsi_set_keys(buf
+ bytes
, keys
);
3751 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3753 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3758 static void adsi_clear(struct ast_channel *chan)
3762 if (!ast_adsi_available(chan))
3764 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
3765 bytes += ast_adsi_voice_mode(buf + bytes, 0);
3767 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
3771 static void adsi_goodbye(struct ast_channel
*chan
)
3773 unsigned char buf
[256];
3776 if (!ast_adsi_available(chan
))
3778 bytes
+= adsi_logo(buf
+ bytes
);
3779 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_LEFT
, 0, " ", "");
3780 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Goodbye", "");
3781 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
3782 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
3784 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
3787 /*--- get_folder: Folder menu ---*/
3788 /* Plays "press 1 for INBOX messages" etc
3789 Should possibly be internationalized
3791 static int get_folder(struct ast_channel
*chan
, int start
)
3796 d
= ast_play_and_wait(chan
, "vm-press"); /* "Press" */
3799 for (x
= start
; x
< 5; x
++) { /* For all folders */
3800 if ((d
= ast_say_number(chan
, x
, AST_DIGIT_ANY
, chan
->language
, (char *) NULL
)))
3802 d
= ast_play_and_wait(chan
, "vm-for"); /* "for" */
3805 snprintf(fn
, sizeof(fn
), "vm-%s", mbox(x
)); /* Folder name */
3806 d
= vm_play_folder_name(chan
, fn
);
3809 d
= ast_waitfordigit(chan
, 500);
3813 d
= ast_play_and_wait(chan
, "vm-tocancel"); /* "or pound to cancel" */
3816 d
= ast_waitfordigit(chan
, 4000);
3820 static int get_folder2(struct ast_channel
*chan
, char *fn
, int start
)
3823 res
= ast_play_and_wait(chan
, fn
); /* Folder name */
3824 while (((res
< '0') || (res
> '9')) &&
3825 (res
!= '#') && (res
>= 0)) {
3826 res
= get_folder(chan
, 0);
3831 static int vm_forwardoptions(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *curdir
, int curmsg
, char *vmfmts
,
3832 char *context
, signed char record_gain
, long *duration
, struct vm_state
*vms
)
3835 int retries
= 0, prepend_duration
= 0, already_recorded
= 0;
3836 signed char zero_gain
= 0;
3837 struct ast_config
*msg_cfg
;
3838 const char *duration_str
;
3839 char msgfile
[PATH_MAX
], backup
[PATH_MAX
];
3840 char textfile
[PATH_MAX
];
3842 /* Must always populate duration correctly */
3843 make_file(msgfile
, sizeof(msgfile
), curdir
, curmsg
);
3844 strcpy(textfile
, msgfile
);
3845 strcpy(backup
, msgfile
);
3846 strncat(textfile
, ".txt", sizeof(textfile
) - strlen(textfile
) - 1);
3847 strncat(backup
, "-bak", sizeof(backup
) - strlen(backup
) - 1);
3849 if (!(msg_cfg
= ast_config_load(textfile
))) {
3854 if ((duration_str
= ast_variable_retrieve(msg_cfg
, "message", "duration")))
3855 *duration
= atoi(duration_str
);
3857 while ((cmd
>= 0) && (cmd
!= 't') && (cmd
!= '*')) {
3862 /* prepend a message to the current message, update the metadata and return */
3864 prepend_duration
= 0;
3866 /* if we can't read the message metadata, stop now */
3872 /* Back up the original file, so we can retry the prepend */
3873 if (already_recorded
)
3874 ast_filecopy(backup
, msgfile
, NULL
);
3876 ast_filecopy(msgfile
, backup
, NULL
);
3877 already_recorded
= 1;
3880 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &record_gain
, sizeof(record_gain
), 0);
3882 cmd
= ast_play_and_prepend(chan
, NULL
, msgfile
, 0, vmfmts
, &prepend_duration
, 1, silencethreshold
, maxsilence
);
3884 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &zero_gain
, sizeof(zero_gain
), 0);
3886 if (prepend_duration
) {
3887 struct ast_category
*msg_cat
;
3888 /* need enough space for a maximum-length message duration */
3889 char duration_str
[12];
3891 prepend_duration
+= *duration
;
3892 msg_cat
= ast_category_get(msg_cfg
, "message");
3893 snprintf(duration_str
, 11, "%d", prepend_duration
);
3894 if (!ast_variable_update(msg_cat
, "duration", duration_str
, NULL
, 0)) {
3895 config_text_file_save(textfile
, msg_cfg
, "app_voicemail");
3896 STORE(curdir
, vmu
->mailbox
, context
, curmsg
, chan
, vmu
, vmfmts
, prepend_duration
, vms
);
3909 cmd
= ast_play_and_wait(chan
,"vm-forwardoptions");
3910 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
3912 cmd
= ast_play_and_wait(chan
,"vm-starmain");
3913 /* "press star to return to the main menu" */
3915 cmd
= ast_waitfordigit(chan
,6000);
3923 ast_config_destroy(msg_cfg
);
3924 if (already_recorded
)
3925 ast_filedelete(backup
, NULL
);
3926 if (prepend_duration
)
3927 *duration
= prepend_duration
;
3929 if (cmd
== 't' || cmd
== 'S')
3934 static int notify_new_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, int msgnum
, long duration
, char *fmt
, char *cidnum
, char *cidname
)
3936 char todir
[PATH_MAX
], fn
[PATH_MAX
], ext_context
[PATH_MAX
], *stringp
;
3937 int newmsgs
= 0, oldmsgs
= 0;
3938 const char *category
= pbx_builtin_getvar_helper(chan
, "VM_CATEGORY");
3940 make_dir(todir
, sizeof(todir
), vmu
->context
, vmu
->mailbox
, "INBOX");
3941 make_file(fn
, sizeof(fn
), todir
, msgnum
);
3942 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", vmu
->mailbox
, vmu
->context
);
3944 if (!ast_strlen_zero(vmu
->attachfmt
)) {
3945 if (strstr(fmt
, vmu
->attachfmt
)) {
3946 fmt
= vmu
->attachfmt
;
3948 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
);
3952 /* Attach only the first format */
3953 fmt
= ast_strdupa(fmt
);
3955 strsep(&stringp
, "|");
3957 if (!ast_strlen_zero(vmu
->email
)) {
3958 int attach_user_voicemail
= ast_test_flag((&globalflags
), VM_ATTACH
);
3959 char *myserveremail
= serveremail
;
3960 attach_user_voicemail
= ast_test_flag(vmu
, VM_ATTACH
);
3961 if (!ast_strlen_zero(vmu
->serveremail
))
3962 myserveremail
= vmu
->serveremail
;
3964 if (attach_user_voicemail
)
3965 RETRIEVE(todir
, msgnum
);
3967 /*XXX possible imap issue, should category be NULL XXX*/
3968 sendmail(myserveremail
, vmu
, msgnum
, vmu
->context
, vmu
->mailbox
, cidnum
, cidname
, fn
, fmt
, duration
, attach_user_voicemail
, chan
, category
);
3970 if (attach_user_voicemail
)
3971 DISPOSE(todir
, msgnum
);
3974 if (!ast_strlen_zero(vmu
->pager
)) {
3975 char *myserveremail
= serveremail
;
3976 if (!ast_strlen_zero(vmu
->serveremail
))
3977 myserveremail
= vmu
->serveremail
;
3978 sendpage(myserveremail
, vmu
->pager
, msgnum
, vmu
->context
, vmu
->mailbox
, cidnum
, cidname
, duration
, vmu
, category
);
3981 if (ast_test_flag(vmu
, VM_DELETE
)) {
3982 DELETE(todir
, msgnum
, fn
);
3986 DELETE(todir
, msgnum
, fn
);
3988 /* Leave voicemail for someone */
3989 if (ast_app_has_voicemail(ext_context
, NULL
)) {
3990 ast_app_inboxcount(ext_context
, &newmsgs
, &oldmsgs
);
3992 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
);
3993 run_externnotify(vmu
->context
, vmu
->mailbox
);
3997 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
)
4001 char *header_content
;
4005 struct vm_state
*dstvms
;
4007 char username
[70]="";
4008 int res
= 0, cmd
= 0;
4009 struct ast_vm_user
*receiver
= NULL
, *vmtmp
;
4010 AST_LIST_HEAD_NOLOCK_STATIC(extensions
, ast_vm_user
);
4013 int saved_messages
= 0, found
= 0;
4014 int valid_extensions
= 0;
4018 if (vms
== NULL
) return -1;
4020 curmsg
= vms
->curmsg
;
4022 while (!res
&& !valid_extensions
) {
4023 int use_directory
= 0;
4024 if (ast_test_flag((&globalflags
), VM_DIRECFORWARD
)) {
4028 while ((cmd
>= 0) && !done
){
4045 /* Press 1 to enter an extension press 2 to use the directory */
4046 cmd
= ast_play_and_wait(chan
,"vm-forward");
4048 cmd
= ast_waitfordigit(chan
,3000);
4059 if (cmd
< 0 || cmd
== 't')
4063 if (use_directory
) {
4064 /* use app_directory */
4066 char old_context
[sizeof(chan
->context
)];
4067 char old_exten
[sizeof(chan
->exten
)];
4069 struct ast_app
* app
;
4072 app
= pbx_findapp("Directory");
4074 char vmcontext
[256];
4075 /* make backup copies */
4076 memcpy(old_context
, chan
->context
, sizeof(chan
->context
));
4077 memcpy(old_exten
, chan
->exten
, sizeof(chan
->exten
));
4078 old_priority
= chan
->priority
;
4080 /* call the the Directory, changes the channel */
4081 snprintf(vmcontext
, sizeof(vmcontext
), "%s||v", context
? context
: "default");
4082 res
= pbx_exec(chan
, app
, vmcontext
);
4084 ast_copy_string(username
, chan
->exten
, sizeof(username
));
4086 /* restore the old context, exten, and priority */
4087 memcpy(chan
->context
, old_context
, sizeof(chan
->context
));
4088 memcpy(chan
->exten
, old_exten
, sizeof(chan
->exten
));
4089 chan
->priority
= old_priority
;
4092 ast_log(LOG_WARNING
, "Could not find the Directory application, disabling directory_forward\n");
4093 ast_clear_flag((&globalflags
), VM_DIRECFORWARD
);
4096 /* Ask for an extension */
4097 res
= ast_streamfile(chan
, "vm-extension", chan
->language
); /* "extension" */
4100 if ((res
= ast_readstring(chan
, username
, sizeof(username
) - 1, 2000, 10000, "#") < 0))
4104 /* start all over if no username */
4105 if (ast_strlen_zero(username
))
4108 s
= strsep(&stringp
, "*");
4109 /* start optimistic */
4110 valid_extensions
= 1;
4112 /* 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 */
4113 if ((flag
== 1 || strcmp(s
,sender
->mailbox
)) && (receiver
= find_user(NULL
, context
, s
))) {
4114 AST_LIST_INSERT_HEAD(&extensions
, receiver
, list
);
4117 valid_extensions
= 0;
4120 s
= strsep(&stringp
, "*");
4122 /* break from the loop of reading the extensions */
4123 if (valid_extensions
)
4125 /* "I am sorry, that's not a valid extension. Please try again." */
4126 res
= ast_play_and_wait(chan
, "pbx-invalid");
4128 /* check if we're clear to proceed */
4129 if (AST_LIST_EMPTY(&extensions
) || !valid_extensions
)
4132 struct leave_vm_options leave_options
;
4133 char mailbox
[AST_MAX_EXTENSION
* 2 + 2];
4134 /* Make sure that context doesn't get set as a literal "(null)" (or else find_user won't find it) */
4136 snprintf(mailbox
, sizeof(mailbox
), "%s@%s", username
, context
);
4138 ast_copy_string(mailbox
, username
, sizeof(mailbox
));
4140 /* Send VoiceMail */
4141 memset(&leave_options
, 0, sizeof(leave_options
));
4142 leave_options
.record_gain
= record_gain
;
4143 cmd
= leave_voicemail(chan
, mailbox
, &leave_options
);
4145 /* Forward VoiceMail */
4147 char origmsgfile
[PATH_MAX
], msgfile
[PATH_MAX
];
4148 struct vm_state vmstmp
;
4150 memcpy(&vmstmp
, vms
, sizeof(vmstmp
));
4152 make_file(origmsgfile
, sizeof(origmsgfile
), dir
, curmsg
);
4153 create_dirpath(vmstmp
.curdir
, sizeof(vmstmp
.curdir
), sender
->context
, vmstmp
.username
, "tmp");
4154 make_file(msgfile
, sizeof(msgfile
), vmstmp
.curdir
, curmsg
);
4156 RETRIEVE(dir
, curmsg
);
4158 /* Alter a surrogate file, only */
4159 copy_plain_file(origmsgfile
, msgfile
);
4161 cmd
= vm_forwardoptions(chan
, sender
, vmstmp
.curdir
, curmsg
, vmfmts
, S_OR(context
, "default"), record_gain
, &duration
, &vmstmp
);
4163 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions
, vmtmp
, list
) {
4165 char *myserveremail
;
4166 int attach_user_voicemail
;
4167 /* Need to get message content */
4168 if (option_debug
> 2)
4169 ast_log (LOG_DEBUG
, "Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", vms
->curmsg
, vms
->msgArray
[vms
->curmsg
]);
4170 if (vms
->msgArray
[vms
->curmsg
] == 0) {
4171 ast_log (LOG_WARNING
,"Trying to access unknown message\n");
4175 /* This will only work for new messages... */
4176 header_content
= mail_fetchheader (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
]);
4177 /* empty string means no valid header */
4178 if (ast_strlen_zero(header_content
)) {
4179 ast_log (LOG_ERROR
,"Could not fetch header for message number %ld\n",vms
->msgArray
[vms
->curmsg
]);
4182 /* Get header info needed by sendmail */
4183 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Duration:");
4185 duration
= atoi(temp
);
4189 /* Attach only the first format */
4190 fmt
= ast_strdupa(fmt
);
4193 strsep(&stringp
, "|");
4195 ast_log (LOG_ERROR
,"audio format not set. Default to WAV\n");
4198 if (!strcasecmp(fmt
, "wav49"))
4200 if (option_debug
> 2)
4201 ast_log (LOG_DEBUG
,"**** format set to %s, vmfmts set to %s\n",fmt
,vmfmts
);
4202 /* ast_copy_string(fmt, vmfmts, sizeof(fmt));*/
4203 /* if (!ast_strlen_zero(fmt)) { */
4204 snprintf(todir
, sizeof(todir
), "%s%s/%s/tmp", VM_SPOOL_DIR
, vmtmp
->context
, vmtmp
->mailbox
);
4205 make_gsm_file(vms
->fn
, sizeof(vms
->fn
), vms
->imapuser
, todir
, vms
->curmsg
);
4206 if (option_debug
> 2)
4207 ast_log (LOG_DEBUG
,"Before mail_fetchstructure, message number is %ld, filename is:%s\n",vms
->msgArray
[vms
->curmsg
], vms
->fn
);
4208 /*mail_fetchstructure (mailstream, vmArray[0], &body); */
4209 mail_fetchstructure (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
], &body
);
4210 save_body(body
,vms
,"3","gsm");
4211 /* should not assume "fmt" here! */
4212 save_body(body
,vms
,"2",fmt
);
4214 /* get destination mailbox */
4215 dstvms
= get_vm_state_by_mailbox(vmtmp
->mailbox
,0);
4217 init_mailstream(dstvms
, 0);
4218 if (!dstvms
->mailstream
) {
4219 ast_log (LOG_ERROR
,"IMAP mailstream for %s is NULL\n",vmtmp
->mailbox
);
4221 STORE(todir
, vmtmp
->mailbox
, vmtmp
->context
, dstvms
->curmsg
, chan
, vmtmp
, fmt
, duration
, dstvms
);
4222 run_externnotify(vmtmp
->context
, vmtmp
->mailbox
);
4225 ast_log (LOG_ERROR
,"Could not find state information for mailbox %s\n",vmtmp
->mailbox
);
4228 myserveremail
= serveremail
;
4229 if (!ast_strlen_zero(vmtmp
->serveremail
))
4230 myserveremail
= vmtmp
->serveremail
;
4231 attach_user_voicemail
= ast_test_flag((&globalflags
), VM_ATTACH
);
4232 attach_user_voicemail
= ast_test_flag(vmtmp
, VM_ATTACH
);
4233 /* NULL category for IMAP storage */
4234 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
);
4236 copy_message(chan
, sender
, -1, curmsg
, duration
, vmtmp
, fmt
, vmstmp
.curdir
);
4239 AST_LIST_REMOVE_CURRENT(&extensions
, list
);
4244 AST_LIST_TRAVERSE_SAFE_END
;
4245 if (saved_messages
> 0) {
4246 /* give confirmation that the message was saved */
4247 /* commented out since we can't forward batches yet
4248 if (saved_messages == 1)
4249 res = ast_play_and_wait(chan, "vm-message");
4251 res = ast_play_and_wait(chan, "vm-messages");
4253 res = ast_play_and_wait(chan, "vm-saved"); */
4254 res
= ast_play_and_wait(chan
, "vm-msgsaved");
4258 /* Remove surrogate file */
4262 /* If anything failed above, we still have this list to free */
4263 while ((vmtmp
= AST_LIST_REMOVE_HEAD(&extensions
, list
)))
4265 return res
? res
: cmd
;
4268 static int wait_file2(struct ast_channel
*chan
, struct vm_state
*vms
, char *file
)
4271 if ((res
= ast_stream_and_wait(chan
, file
, chan
->language
, AST_DIGIT_ANY
)) < 0)
4272 ast_log(LOG_WARNING
, "Unable to play message %s\n", file
);
4276 static int wait_file(struct ast_channel
*chan
, struct vm_state
*vms
, char *file
)
4278 return ast_control_streamfile(chan
, file
, "#", "*", "1456789", "0", "2", skipms
);
4281 static int play_message_category(struct ast_channel
*chan
, const char *category
)
4285 if (!ast_strlen_zero(category
))
4286 res
= ast_play_and_wait(chan
, category
);
4289 ast_log(LOG_WARNING
, "No sound file for category '%s' was found.\n", category
);
4296 static int play_message_datetime(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, const char *origtime
, const char *filename
)
4299 struct vm_zone
*the_zone
= NULL
;
4302 if (ast_get_time_t(origtime
, &t
, 0, NULL
)) {
4303 ast_log(LOG_WARNING
, "Couldn't find origtime in %s\n", filename
);
4307 /* Does this user have a timezone specified? */
4308 if (!ast_strlen_zero(vmu
->zonetag
)) {
4309 /* Find the zone in the list */
4311 AST_LIST_LOCK(&zones
);
4312 AST_LIST_TRAVERSE(&zones
, z
, list
) {
4313 if (!strcmp(z
->name
, vmu
->zonetag
)) {
4318 AST_LIST_UNLOCK(&zones
);
4321 /* No internal variable parsing for now, so we'll comment it out for the time being */
4323 /* Set the DIFF_* variables */
4324 ast_localtime(&t
, &time_now
, NULL
);
4325 tv_now
= ast_tvnow();
4326 tnow
= tv_now
.tv_sec
;
4327 ast_localtime(&tnow
, &time_then
, NULL
);
4329 /* Day difference */
4330 if (time_now
.tm_year
== time_then
.tm_year
)
4331 snprintf(temp
,sizeof(temp
),"%d",time_now
.tm_yday
);
4333 snprintf(temp
,sizeof(temp
),"%d",(time_now
.tm_year
- time_then
.tm_year
) * 365 + (time_now
.tm_yday
- time_then
.tm_yday
));
4334 pbx_builtin_setvar_helper(chan
, "DIFF_DAY", temp
);
4336 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
4339 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, the_zone
->msg_format
, the_zone
->timezone
);
4340 else if (!strcasecmp(chan
->language
,"pl")) /* POLISH syntax */
4341 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Q HM", NULL
);
4342 else if (!strcasecmp(chan
->language
,"se")) /* SWEDISH syntax */
4343 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' dB 'digits/at' k 'and' M", NULL
);
4344 else if (!strcasecmp(chan
->language
,"no")) /* NORWEGIAN syntax */
4345 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Q 'digits/at' HM", NULL
);
4346 else if (!strcasecmp(chan
->language
,"de")) /* GERMAN syntax */
4347 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' Q 'digits/at' HM", NULL
);
4348 else if (!strcasecmp(chan
->language
,"nl")) /* DUTCH syntax */
4349 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' q 'digits/nl-om' HM", NULL
);
4350 else if (!strcasecmp(chan
->language
,"it")) /* ITALIAN syntax */
4351 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
);
4352 else if (!strcasecmp(chan
->language
,"gr"))
4353 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' q H 'digits/kai' M ", NULL
);
4354 else if (!strcasecmp(chan
->language
,"pt_BR"))
4355 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
);
4357 res
= ast_say_date_with_format(chan
, t
, AST_DIGIT_ANY
, chan
->language
, "'vm-received' q 'digits/at' IMp", NULL
);
4359 pbx_builtin_setvar_helper(chan
, "DIFF_DAY", NULL
);
4366 static int play_message_callerid(struct ast_channel
*chan
, struct vm_state
*vms
, char *cid
, const char *context
, int callback
)
4370 char *callerid
, *name
;
4371 char prefile
[PATH_MAX
] = "";
4374 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
4375 /* 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 */
4376 if ((cid
== NULL
)||(context
== NULL
))
4379 /* Strip off caller ID number from name */
4380 if (option_debug
> 2)
4381 ast_log(LOG_DEBUG
, "VM-CID: composite caller ID received: %s, context: %s\n", cid
, context
);
4382 ast_callerid_parse(cid
, &name
, &callerid
);
4383 if ((!ast_strlen_zero(callerid
)) && strcmp(callerid
, "Unknown")) {
4384 /* Check for internal contexts and only */
4385 /* say extension when the call didn't come from an internal context in the list */
4386 for (i
= 0 ; i
< MAX_NUM_CID_CONTEXTS
; i
++){
4387 if (option_debug
> 2)
4388 ast_log(LOG_DEBUG
, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts
[i
]);
4389 if ((strcmp(cidinternalcontexts
[i
], context
) == 0))
4392 if (i
!= MAX_NUM_CID_CONTEXTS
){ /* internal context? */
4394 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/greet", VM_SPOOL_DIR
, context
, callerid
);
4395 if (!ast_strlen_zero(prefile
)) {
4396 /* See if we can find a recorded name for this person instead of their extension number */
4397 if (ast_fileexists(prefile
, NULL
, NULL
) > 0) {
4398 if (option_verbose
> 2)
4399 ast_verbose(VERBOSE_PREFIX_3
"Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid
);
4401 res
= wait_file2(chan
, vms
, "vm-from");
4402 res
= ast_stream_and_wait(chan
, prefile
, chan
->language
, "");
4404 if (option_verbose
> 2)
4405 ast_verbose(VERBOSE_PREFIX_3
"Playing envelope info: message from '%s'\n", callerid
);
4406 /* BB: Say "from extension" as one saying to sound smoother */
4408 res
= wait_file2(chan
, vms
, "vm-from-extension");
4409 res
= ast_say_digit_str(chan
, callerid
, "", chan
->language
);
4416 if (option_debug
> 2)
4417 ast_log(LOG_DEBUG
, "VM-CID: Numeric caller id: (%s)\n",callerid
);
4418 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
4420 res
= wait_file2(chan
, vms
, "vm-from-phonenumber");
4421 res
= ast_say_digit_str(chan
, callerid
, AST_DIGIT_ANY
, chan
->language
);
4424 /* Number unknown */
4426 ast_log(LOG_DEBUG
, "VM-CID: From an unknown number\n");
4427 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
4428 res
= wait_file2(chan
, vms
, "vm-unknown-caller");
4433 static int play_message_duration(struct ast_channel
*chan
, struct vm_state
*vms
, const char *duration
, int minduration
)
4438 /* Verify that we have a duration for the message */
4439 if (duration
== NULL
)
4442 /* Convert from seconds to minutes */
4443 durations
=atoi(duration
);
4444 durationm
=(durations
/ 60);
4446 if (option_debug
> 2)
4447 ast_log(LOG_DEBUG
, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations
, durationm
);
4449 if ((!res
) && (durationm
>= minduration
)) {
4450 res
= wait_file2(chan
, vms
, "vm-duration");
4453 if (!strcasecmp(chan
->language
, "pl")) {
4454 div_t num
= div(durationm
, 10);
4456 if (durationm
== 1) {
4457 res
= ast_play_and_wait(chan
, "digits/1z");
4458 res
= res
? res
: ast_play_and_wait(chan
, "vm-minute-ta");
4459 } else if (num
.rem
> 1 && num
.rem
< 5 && num
.quot
!= 1) {
4462 res
= ast_play_and_wait(chan
, "digits/2-ie");
4464 res
= say_and_wait(chan
, durationm
- 2 , chan
->language
);
4465 res
= res
? res
: ast_play_and_wait(chan
, "digits/2-ie");
4468 res
= say_and_wait(chan
, durationm
, chan
->language
);
4470 res
= res
? res
: ast_play_and_wait(chan
, "vm-minute-ty");
4472 res
= say_and_wait(chan
, durationm
, chan
->language
);
4473 res
= res
? res
: ast_play_and_wait(chan
, "vm-minute-t");
4475 /* DEFAULT syntax */
4477 res
= ast_say_number(chan
, durationm
, AST_DIGIT_ANY
, chan
->language
, NULL
);
4478 res
= wait_file2(chan
, vms
, "vm-minutes");
4485 static int play_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
)
4488 char *header_content
;
4494 char todir
[PATH_MAX
];
4496 char *attachedfilefmt
;
4500 if (option_debug
> 2)
4501 ast_log (LOG_DEBUG
,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms
->curmsg
, vms
->msgArray
[vms
->curmsg
]);
4502 if (vms
->msgArray
[vms
->curmsg
] == 0) {
4503 ast_log (LOG_WARNING
,"Trying to access unknown message\n");
4507 /* This will only work for new messages... */
4508 header_content
= mail_fetchheader (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
]);
4509 /* empty string means no valid header */
4510 if (ast_strlen_zero(header_content
)) {
4511 ast_log (LOG_ERROR
,"Could not fetch header for message number %ld\n",vms
->msgArray
[vms
->curmsg
]);
4514 snprintf(todir
, sizeof(todir
), "%s%s/%s/tmp", VM_SPOOL_DIR
, vmu
->context
, vmu
->mailbox
);
4515 make_gsm_file(vms
->fn
, sizeof(vms
->fn
), vms
->imapuser
, todir
, vms
->curmsg
);
4517 mail_fetchstructure (vms
->mailstream
,vms
->msgArray
[vms
->curmsg
],&body
);
4519 /* We have the body, now we extract the file name of the first attachment. */
4520 if (body
->nested
.part
&& body
->nested
.part
->next
&& body
->nested
.part
->next
->body
.parameter
->value
) {
4521 attachedfilefmt
= ast_strdupa(body
->nested
.part
->next
->body
.parameter
->value
);
4523 ast_log(LOG_ERROR
, "There is no file attached to this IMAP message.\n");
4527 /* Find the format of the attached file */
4529 strsep(&attachedfilefmt
, ".");
4530 if (!attachedfilefmt
) {
4531 ast_log(LOG_ERROR
, "File format could not be obtained from IMAP message attachment\n");
4534 save_body(body
, vms
, "2", attachedfilefmt
);
4536 adsi_message(chan
, vms
);
4538 res
= wait_file2(chan
, vms
, "vm-first"); /* "First" */
4539 else if (vms
->curmsg
== vms
->lastmsg
)
4540 res
= wait_file2(chan
, vms
, "vm-last"); /* "last" */
4542 res
= wait_file2(chan
, vms
, "vm-message"); /* "message" */
4543 if (vms
->curmsg
&& (vms
->curmsg
!= vms
->lastmsg
)) {
4545 res
= ast_say_number(chan
, vms
->curmsg
+ 1, AST_DIGIT_ANY
, chan
->language
, (char *) NULL
);
4549 /* Get info from headers!! */
4550 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Caller-ID-Num:");
4553 ast_copy_string(cid
, temp
, sizeof(cid
));
4557 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Context:");
4560 ast_copy_string(context
, temp
, sizeof(context
));
4564 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Orig-time:");
4567 ast_copy_string(origtime
, temp
, sizeof(origtime
));
4571 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Duration:");
4574 ast_copy_string(duration
,temp
, sizeof(duration
));
4578 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Category:");
4581 ast_copy_string(category
,temp
, sizeof(category
));
4585 /*if (!strncasecmp("macro",context,5)) Macro names in contexts are useless for our needs */
4586 /* context = ast_variable_retrieve(msg_cfg, "message","macrocontext"); */
4590 if ((!res
) && !ast_strlen_zero(category
)) {
4591 res
= play_message_category(chan
, category
);
4594 if ((!res
) && (ast_test_flag(vmu
, VM_ENVELOPE
)) && origtime
[0] != '\0')
4595 res
= play_message_datetime(chan
, vmu
, origtime
, "IMAP_STORAGE");
4596 if ((!res
) && (ast_test_flag(vmu
, VM_SAYCID
)) && cid
[0] !='\0' && context
[0] !='\0')
4597 res
= play_message_callerid(chan
, vms
, cid
, context
, 0);
4599 if ((!res
) && (ast_test_flag(vmu
, VM_SAYDURATION
)) && duration
[0] != '\0')
4600 res
= play_message_duration(chan
, vms
, duration
, vmu
->saydurationm
);
4602 /* Allow pressing '1' to skip envelope / callerid */
4606 /*ast_config_destroy(msg_cfg);*/
4610 vms
->heard
[vms
->curmsg
] = 1;
4611 res
= wait_file(chan
, vms
, vms
->fn
);
4613 DISPOSE(vms
->curdir
, vms
->curmsg
);
4614 DELETE(0, 0, vms
->fn
);
4618 static int play_message(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
)
4621 char filename
[256], *cid
;
4622 const char *origtime
, *context
, *category
, *duration
;
4623 struct ast_config
*msg_cfg
;
4626 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, vms
->curmsg
);
4627 adsi_message(chan
, vms
);
4629 res
= wait_file2(chan
, vms
, "vm-first"); /* "First" */
4630 else if (vms
->curmsg
== vms
->lastmsg
)
4631 res
= wait_file2(chan
, vms
, "vm-last"); /* "last" */
4634 if (!strcasecmp(chan
->language
, "pl")) {
4635 if (vms
->curmsg
&& (vms
->curmsg
!= vms
->lastmsg
)) {
4638 ten
= (vms
->curmsg
+ 1) / 10;
4639 one
= (vms
->curmsg
+ 1) % 10;
4641 if (vms
->curmsg
< 20) {
4642 snprintf(nextmsg
, sizeof(nextmsg
), "digits/n-%d", vms
->curmsg
+ 1);
4643 res
= wait_file2(chan
, vms
, nextmsg
);
4645 snprintf(nextmsg
, sizeof(nextmsg
), "digits/n-%d", ten
* 10);
4646 res
= wait_file2(chan
, vms
, nextmsg
);
4649 snprintf(nextmsg
, sizeof(nextmsg
), "digits/n-%d", one
);
4650 res
= wait_file2(chan
, vms
, nextmsg
);
4656 res
= wait_file2(chan
, vms
, "vm-message");
4658 if (!strcasecmp(chan
->language
, "se")) /* SWEDISH syntax */
4659 res
= wait_file2(chan
, vms
, "vm-meddelandet"); /* "message" */
4660 else /* DEFAULT syntax */
4661 res
= wait_file2(chan
, vms
, "vm-message");
4662 if (vms
->curmsg
&& (vms
->curmsg
!= vms
->lastmsg
)) {
4664 res
= ast_say_number(chan
, vms
->curmsg
+ 1, AST_DIGIT_ANY
, chan
->language
, NULL
);
4669 /* Retrieve info from VM attribute file */
4670 make_file(vms
->fn2
, sizeof(vms
->fn2
), vms
->curdir
, vms
->curmsg
);
4671 snprintf(filename
, sizeof(filename
), "%s.txt", vms
->fn2
);
4672 RETRIEVE(vms
->curdir
, vms
->curmsg
);
4673 msg_cfg
= ast_config_load(filename
);
4675 ast_log(LOG_WARNING
, "No message attribute file?!! (%s)\n", filename
);
4679 if (!(origtime
= ast_variable_retrieve(msg_cfg
, "message", "origtime"))) {
4680 ast_log(LOG_WARNING
, "No origtime?!\n");
4681 DISPOSE(vms
->curdir
, vms
->curmsg
);
4682 ast_config_destroy(msg_cfg
);
4686 cid
= ast_strdupa(ast_variable_retrieve(msg_cfg
, "message", "callerid"));
4687 duration
= ast_variable_retrieve(msg_cfg
, "message", "duration");
4688 category
= ast_variable_retrieve(msg_cfg
, "message", "category");
4690 context
= ast_variable_retrieve(msg_cfg
, "message", "context");
4691 if (!strncasecmp("macro",context
,5)) /* Macro names in contexts are useless for our needs */
4692 context
= ast_variable_retrieve(msg_cfg
, "message","macrocontext");
4694 res
= play_message_category(chan
, category
);
4695 if ((!res
) && (ast_test_flag(vmu
, VM_ENVELOPE
)))
4696 res
= play_message_datetime(chan
, vmu
, origtime
, filename
);
4697 if ((!res
) && (ast_test_flag(vmu
, VM_SAYCID
)))
4698 res
= play_message_callerid(chan
, vms
, cid
, context
, 0);
4699 if ((!res
) && (ast_test_flag(vmu
, VM_SAYDURATION
)))
4700 res
= play_message_duration(chan
, vms
, duration
, vmu
->saydurationm
);
4701 /* Allow pressing '1' to skip envelope / callerid */
4704 ast_config_destroy(msg_cfg
);
4707 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, vms
->curmsg
);
4708 vms
->heard
[vms
->curmsg
] = 1;
4709 if ((res
= wait_file(chan
, vms
, vms
->fn
)) < 0) {
4710 ast_log(LOG_WARNING
, "Playback of message %s failed\n", vms
->fn
);
4714 DISPOSE(vms
->curdir
, vms
->curmsg
);
4720 static void imap_mailbox_name(char *spec
, size_t len
, struct vm_state
*vms
, int box
, int use_folder
)
4722 char tmp
[256], *t
= tmp
;
4723 size_t left
= sizeof(tmp
);
4726 ast_copy_string(vms
->curbox
, mbox(0), sizeof(vms
->curbox
));
4727 snprintf(vms
->vmbox
, sizeof(vms
->vmbox
), "vm-%s", mbox(1));
4729 ast_copy_string(vms
->curbox
, mbox(box
), sizeof(vms
->curbox
));
4730 snprintf(vms
->vmbox
, sizeof(vms
->vmbox
), "vm-%s", vms
->curbox
);
4733 /* Build up server information */
4734 ast_build_string(&t
, &left
, "{%s:%s/imap", imapserver
, imapport
);
4736 /* Add authentication user if present */
4737 if (!ast_strlen_zero(authuser
))
4738 ast_build_string(&t
, &left
, "/authuser=%s", authuser
);
4740 /* Add flags if present */
4741 if (!ast_strlen_zero(imapflags
))
4742 ast_build_string(&t
, &left
, "/%s", imapflags
);
4744 /* End with username */
4745 ast_build_string(&t
, &left
, "/user=%s}", vms
->imapuser
);
4747 if (box
== 0 || box
== 1)
4748 snprintf(spec
, len
, "%s%s", tmp
, use_folder
? imapfolder
: "INBOX");
4750 snprintf(spec
, len
, "%s%s%c%s", tmp
, imapfolder
, delimiter
, mbox(box
));
4753 static int init_mailstream(struct vm_state
*vms
, int box
)
4755 MAILSTREAM
*stream
= NIL
;
4760 ast_log (LOG_ERROR
,"vm_state is NULL!\n");
4763 if (option_debug
> 2)
4764 ast_log (LOG_DEBUG
,"vm_state user is:%s\n",vms
->imapuser
);
4765 if (vms
->mailstream
== NIL
|| !vms
->mailstream
) {
4767 ast_log (LOG_DEBUG
,"mailstream not set.\n");
4769 stream
= vms
->mailstream
;
4771 /* debug = T; user wants protocol telemetry? */
4772 debug
= NIL
; /* NO protocol telemetry? */
4774 if (delimiter
== '\0') { /* did not probe the server yet */
4776 #ifdef USE_SYSTEM_IMAP
4777 #include <imap/linkage.c>
4778 #elif defined(USE_SYSTEM_CCLIENT)
4779 #include <c-client/linkage.c>
4781 #include "linkage.c"
4783 /* Connect to INBOX first to get folders delimiter */
4784 imap_mailbox_name(tmp
, sizeof(tmp
), vms
, 0, 1);
4785 ast_mutex_lock(&vms
->lock
);
4786 stream
= mail_open (stream
, tmp
, debug
? OP_DEBUG
: NIL
);
4787 ast_mutex_unlock(&vms
->lock
);
4788 if (stream
== NIL
) {
4789 ast_log (LOG_ERROR
, "Can't connect to imap server %s\n", tmp
);
4792 get_mailbox_delimiter(stream
);
4793 /* update delimiter in imapfolder */
4794 for (cp
= imapfolder
; *cp
; cp
++)
4798 /* Now connect to the target folder */
4799 imap_mailbox_name(tmp
, sizeof(tmp
), vms
, box
, 1);
4800 if (option_debug
> 2)
4801 ast_log (LOG_DEBUG
,"Before mail_open, server: %s, box:%d\n", tmp
, box
);
4802 ast_mutex_lock(&vms
->lock
);
4803 vms
->mailstream
= mail_open (stream
, tmp
, debug
? OP_DEBUG
: NIL
);
4804 ast_mutex_unlock(&vms
->lock
);
4805 if (vms
->mailstream
== NIL
) {
4812 static int open_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
, int box
)
4818 ast_copy_string(vms
->imapuser
,vmu
->imapuser
, sizeof(vms
->imapuser
));
4819 if (option_debug
> 2)
4820 ast_log(LOG_DEBUG
,"Before init_mailstream, user is %s\n",vmu
->imapuser
);
4821 ret
= init_mailstream(vms
, box
);
4822 if (ret
!= 0 || !vms
->mailstream
) {
4823 ast_log (LOG_ERROR
,"Could not initialize mailstream\n");
4829 if (option_debug
> 2)
4830 ast_log(LOG_DEBUG
, "Mailbox name set to: %s, about to check quotas\n", mbox(box
));
4831 check_quota(vms
,(char *)mbox(box
));
4834 pgm
= mail_newsearchpgm();
4836 /* Check IMAP folder for Asterisk messages only... */
4837 hdr
= mail_newsearchheader ("X-Asterisk-VM-Extension", vmu
->mailbox
);
4842 /* if box = 0, check for new, if box = 1, check for read */
4846 } else if (box
== 1) {
4851 vms
->vmArrayIndex
= 0;
4852 if (option_debug
> 2)
4853 ast_log(LOG_DEBUG
,"Before mail_search_full, user is %s\n",vmu
->imapuser
);
4854 mail_search_full (vms
->mailstream
, NULL
, pgm
, NIL
);
4857 vms
->lastmsg
= vms
->vmArrayIndex
- 1;
4859 mail_free_searchpgm(&pgm
);
4863 static int open_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
,int box
)
4866 int count_msg
, last_msg
;
4868 ast_copy_string(vms
->curbox
, mbox(box
), sizeof(vms
->curbox
));
4870 /* Rename the member vmbox HERE so that we don't try to return before
4871 * we know what's going on.
4873 snprintf(vms
->vmbox
, sizeof(vms
->vmbox
), "vm-%s", vms
->curbox
);
4875 /* Faster to make the directory than to check if it exists. */
4876 create_dirpath(vms
->curdir
, sizeof(vms
->curdir
), vmu
->context
, vms
->username
, vms
->curbox
);
4878 count_msg
= count_messages(vmu
, vms
->curdir
);
4882 vms
->lastmsg
= count_msg
- 1;
4885 The following test is needed in case sequencing gets messed up.
4886 There appears to be more than one way to mess up sequence, so
4887 we will not try to find all of the root causes--just fix it when
4891 last_msg
= last_message_index(vmu
, vms
->curdir
);
4894 else if (vms
->lastmsg
!= last_msg
)
4896 ast_log(LOG_NOTICE
, "Resequencing Mailbox: %s\n", vms
->curdir
);
4897 res
= resequence_mailbox(vmu
, vms
->curdir
);
4906 static int close_mailbox(struct vm_state
*vms
, struct ast_vm_user
*vmu
)
4909 #ifndef IMAP_STORAGE
4910 int res
= 0, nummsg
;
4913 if (vms
->lastmsg
<= -1)
4917 #ifndef IMAP_STORAGE
4918 /* Get the deleted messages fixed */
4919 if (vm_lock_path(vms
->curdir
))
4920 return ERROR_LOCK_PATH
;
4922 for (x
= 0; x
< vmu
->maxmsg
; x
++) {
4923 if (!vms
->deleted
[x
] && (strcasecmp(vms
->curbox
, "INBOX") || !vms
->heard
[x
])) {
4924 /* Save this message. It's not in INBOX or hasn't been heard */
4925 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, x
);
4926 if (!EXISTS(vms
->curdir
, x
, vms
->fn
, NULL
))
4929 make_file(vms
->fn2
, sizeof(vms
->fn2
), vms
->curdir
, vms
->curmsg
);
4930 if (strcmp(vms
->fn
, vms
->fn2
)) {
4931 RENAME(vms
->curdir
, x
, vmu
->mailbox
,vmu
->context
, vms
->curdir
, vms
->curmsg
, vms
->fn
, vms
->fn2
);
4933 } else if (!strcasecmp(vms
->curbox
, "INBOX") && vms
->heard
[x
] && !vms
->deleted
[x
]) {
4934 /* Move to old folder before deleting */
4935 res
= save_to_folder(vmu
, vms
, x
, 1);
4936 if (res
== ERROR_LOCK_PATH
|| res
== ERROR_MAILBOX_FULL
) {
4937 /* If save failed do not delete the message */
4938 ast_log(LOG_WARNING
, "Save failed. Not moving message: %s.\n", res
== ERROR_LOCK_PATH
? "unable to lock path" : "destination folder full");
4939 vms
->deleted
[x
] = 0;
4946 /* Delete ALL remaining messages */
4948 for (x
= vms
->curmsg
+ 1; x
<= nummsg
; x
++) {
4949 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, x
);
4950 if (EXISTS(vms
->curdir
, x
, vms
->fn
, NULL
))
4951 DELETE(vms
->curdir
, x
, vms
->fn
);
4953 ast_unlock_path(vms
->curdir
);
4956 for (x
=0;x
< vmu
->maxmsg
;x
++) {
4957 if (vms
->deleted
[x
]) {
4958 if (option_debug
> 2)
4959 ast_log(LOG_DEBUG
,"IMAP delete of %d\n",x
);
4960 IMAP_DELETE(vms
->curdir
, x
, vms
->fn
, vms
);
4968 memset(vms
->deleted
, 0, vmu
->maxmsg
* sizeof(int));
4970 memset(vms
->heard
, 0, vmu
->maxmsg
* sizeof(int));
4975 /* In Greek even though we CAN use a syntax like "friends messages"
4976 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
4977 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
4978 * syntax for the above three categories which is more elegant.
4981 static int vm_play_folder_name_gr(struct ast_channel
*chan
, char *mbox
)
4986 buf
= alloca(strlen(mbox
)+2);
4990 if (!strcasecmp(mbox
, "vm-INBOX") || !strcasecmp(mbox
, "vm-Old")){
4991 cmd
= ast_play_and_wait(chan
, buf
); /* "NEA / PALIA" */
4992 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages"); /* "messages" -> "MYNHMATA" */
4994 cmd
= ast_play_and_wait(chan
, "vm-messages"); /* "messages" -> "MYNHMATA" */
4995 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
4999 static int vm_play_folder_name_pl(struct ast_channel
*chan
, char *mbox
)
5003 if (!strcasecmp(mbox
, "vm-INBOX") || !strcasecmp(mbox
, "vm-Old")) {
5004 if (!strcasecmp(mbox
, "vm-INBOX"))
5005 cmd
= ast_play_and_wait(chan
, "vm-new-e");
5007 cmd
= ast_play_and_wait(chan
, "vm-old-e");
5008 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages");
5010 cmd
= ast_play_and_wait(chan
, "vm-messages");
5011 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
);
5015 static int vm_play_folder_name_ua(struct ast_channel
*chan
, char *mbox
)
5019 if (!strcasecmp(mbox
, "vm-Family") || !strcasecmp(mbox
, "vm-Friends") || !strcasecmp(mbox
, "vm-Work")){
5020 cmd
= ast_play_and_wait(chan
, "vm-messages");
5021 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
);
5023 cmd
= ast_play_and_wait(chan
, mbox
);
5024 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages");
5028 static int vm_play_folder_name(struct ast_channel
*chan
, char *mbox
)
5032 if (!strcasecmp(chan
->language
, "it") || !strcasecmp(chan
->language
, "es") || !strcasecmp(chan
->language
, "pt") || !strcasecmp(chan
->language
, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
5033 cmd
= ast_play_and_wait(chan
, "vm-messages"); /* "messages */
5034 return cmd
? cmd
: ast_play_and_wait(chan
, mbox
);
5035 } else if (!strcasecmp(chan
->language
, "gr")){
5036 return vm_play_folder_name_gr(chan
, mbox
);
5037 } else if (!strcasecmp(chan
->language
, "pl")){
5038 return vm_play_folder_name_pl(chan
, mbox
);
5039 } else if (!strcasecmp(chan
->language
, "ua")){ /* Ukrainian syntax */
5040 return vm_play_folder_name_ua(chan
, mbox
);
5041 } else { /* Default English */
5042 cmd
= ast_play_and_wait(chan
, mbox
);
5043 return cmd
? cmd
: ast_play_and_wait(chan
, "vm-messages"); /* "messages */
5048 In greek the plural for old/new is
5049 different so we need the following files
5050 We also need vm-denExeteMynhmata because
5051 this syntax is different.
5053 -> vm-Olds.wav : "Palia"
5054 -> vm-INBOXs.wav : "Nea"
5055 -> vm-denExeteMynhmata : "den exete mynhmata"
5059 static int vm_intro_gr(struct ast_channel
*chan
, struct vm_state
*vms
)
5063 if (vms
->newmessages
) {
5064 res
= ast_play_and_wait(chan
, "vm-youhave");
5066 res
= ast_say_number(chan
, vms
->newmessages
, AST_DIGIT_ANY
, chan
->language
, NULL
);
5068 if ((vms
->newmessages
== 1)) {
5069 res
= ast_play_and_wait(chan
, "vm-INBOX");
5071 res
= ast_play_and_wait(chan
, "vm-message");
5073 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5075 res
= ast_play_and_wait(chan
, "vm-messages");
5078 } else if (vms
->oldmessages
){
5079 res
= ast_play_and_wait(chan
, "vm-youhave");
5081 res
= ast_say_number(chan
, vms
->oldmessages
, AST_DIGIT_ANY
, chan
->language
, NULL
);
5082 if ((vms
->oldmessages
== 1)){
5083 res
= ast_play_and_wait(chan
, "vm-Old");
5085 res
= ast_play_and_wait(chan
, "vm-message");
5087 res
= ast_play_and_wait(chan
, "vm-Olds");
5089 res
= ast_play_and_wait(chan
, "vm-messages");
5091 } else if (!vms
->oldmessages
&& !vms
->newmessages
)
5092 res
= ast_play_and_wait(chan
, "vm-denExeteMynhmata");
5096 /* Default English syntax */
5097 static int vm_intro_en(struct ast_channel
*chan
, struct vm_state
*vms
)
5101 /* Introduce messages they have */
5102 res
= ast_play_and_wait(chan
, "vm-youhave");
5104 if (vms
->newmessages
) {
5105 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5107 res
= ast_play_and_wait(chan
, "vm-INBOX");
5108 if (vms
->oldmessages
&& !res
)
5109 res
= ast_play_and_wait(chan
, "vm-and");
5111 if ((vms
->newmessages
== 1))
5112 res
= ast_play_and_wait(chan
, "vm-message");
5114 res
= ast_play_and_wait(chan
, "vm-messages");
5118 if (!res
&& vms
->oldmessages
) {
5119 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5121 res
= ast_play_and_wait(chan
, "vm-Old");
5123 if (vms
->oldmessages
== 1)
5124 res
= ast_play_and_wait(chan
, "vm-message");
5126 res
= ast_play_and_wait(chan
, "vm-messages");
5130 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5131 res
= ast_play_and_wait(chan
, "vm-no");
5133 res
= ast_play_and_wait(chan
, "vm-messages");
5140 /* ITALIAN syntax */
5141 static int vm_intro_it(struct ast_channel
*chan
, struct vm_state
*vms
)
5143 /* Introduce messages they have */
5145 if (!vms
->oldmessages
&& !vms
->newmessages
)
5146 res
= ast_play_and_wait(chan
, "vm-no") ||
5147 ast_play_and_wait(chan
, "vm-message");
5149 res
= ast_play_and_wait(chan
, "vm-youhave");
5150 if (!res
&& vms
->newmessages
) {
5151 res
= (vms
->newmessages
== 1) ?
5152 ast_play_and_wait(chan
, "digits/un") ||
5153 ast_play_and_wait(chan
, "vm-nuovo") ||
5154 ast_play_and_wait(chan
, "vm-message") :
5155 /* 2 or more new messages */
5156 say_and_wait(chan
, vms
->newmessages
, chan
->language
) ||
5157 ast_play_and_wait(chan
, "vm-nuovi") ||
5158 ast_play_and_wait(chan
, "vm-messages");
5159 if (!res
&& vms
->oldmessages
)
5160 res
= ast_play_and_wait(chan
, "vm-and");
5162 if (!res
&& vms
->oldmessages
) {
5163 res
= (vms
->oldmessages
== 1) ?
5164 ast_play_and_wait(chan
, "digits/un") ||
5165 ast_play_and_wait(chan
, "vm-vecchio") ||
5166 ast_play_and_wait(chan
, "vm-message") :
5167 /* 2 or more old messages */
5168 say_and_wait(chan
, vms
->oldmessages
, chan
->language
) ||
5169 ast_play_and_wait(chan
, "vm-vecchi") ||
5170 ast_play_and_wait(chan
, "vm-messages");
5172 return res
? -1 : 0;
5176 static int vm_intro_pl(struct ast_channel
*chan
, struct vm_state
*vms
)
5178 /* Introduce messages they have */
5182 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5183 res
= ast_play_and_wait(chan
, "vm-no");
5184 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5187 res
= ast_play_and_wait(chan
, "vm-youhave");
5190 if (vms
->newmessages
) {
5191 num
= div(vms
->newmessages
, 10);
5192 if (vms
->newmessages
== 1) {
5193 res
= ast_play_and_wait(chan
, "digits/1-a");
5194 res
= res
? res
: ast_play_and_wait(chan
, "vm-new-a");
5195 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5196 } else if (num
.rem
> 1 && num
.rem
< 5 && num
.quot
!= 1) {
5199 res
= ast_play_and_wait(chan
, "digits/2-ie");
5201 res
= say_and_wait(chan
, vms
->newmessages
- 2 , chan
->language
);
5202 res
= res
? res
: ast_play_and_wait(chan
, "digits/2-ie");
5205 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5207 res
= res
? res
: ast_play_and_wait(chan
, "vm-new-e");
5208 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5210 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5211 res
= res
? res
: ast_play_and_wait(chan
, "vm-new-ych");
5212 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5214 if (!res
&& vms
->oldmessages
)
5215 res
= ast_play_and_wait(chan
, "vm-and");
5217 if (!res
&& vms
->oldmessages
) {
5218 num
= div(vms
->oldmessages
, 10);
5219 if (vms
->oldmessages
== 1) {
5220 res
= ast_play_and_wait(chan
, "digits/1-a");
5221 res
= res
? res
: ast_play_and_wait(chan
, "vm-old-a");
5222 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5223 } else if (num
.rem
> 1 && num
.rem
< 5 && num
.quot
!= 1) {
5226 res
= ast_play_and_wait(chan
, "digits/2-ie");
5228 res
= say_and_wait(chan
, vms
->oldmessages
- 2 , chan
->language
);
5229 res
= res
? res
: ast_play_and_wait(chan
, "digits/2-ie");
5232 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5234 res
= res
? res
: ast_play_and_wait(chan
, "vm-old-e");
5235 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5237 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5238 res
= res
? res
: ast_play_and_wait(chan
, "vm-old-ych");
5239 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5246 /* SWEDISH syntax */
5247 static int vm_intro_se(struct ast_channel
*chan
, struct vm_state
*vms
)
5249 /* Introduce messages they have */
5252 res
= ast_play_and_wait(chan
, "vm-youhave");
5256 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5257 res
= ast_play_and_wait(chan
, "vm-no");
5258 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5262 if (vms
->newmessages
) {
5263 if ((vms
->newmessages
== 1)) {
5264 res
= ast_play_and_wait(chan
, "digits/ett");
5265 res
= res
? res
: ast_play_and_wait(chan
, "vm-nytt");
5266 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5268 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5269 res
= res
? res
: ast_play_and_wait(chan
, "vm-nya");
5270 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5272 if (!res
&& vms
->oldmessages
)
5273 res
= ast_play_and_wait(chan
, "vm-and");
5275 if (!res
&& vms
->oldmessages
) {
5276 if (vms
->oldmessages
== 1) {
5277 res
= ast_play_and_wait(chan
, "digits/ett");
5278 res
= res
? res
: ast_play_and_wait(chan
, "vm-gammalt");
5279 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5281 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5282 res
= res
? res
: ast_play_and_wait(chan
, "vm-gamla");
5283 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5290 /* NORWEGIAN syntax */
5291 static int vm_intro_no(struct ast_channel
*chan
,struct vm_state
*vms
)
5293 /* Introduce messages they have */
5296 res
= ast_play_and_wait(chan
, "vm-youhave");
5300 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5301 res
= ast_play_and_wait(chan
, "vm-no");
5302 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5306 if (vms
->newmessages
) {
5307 if ((vms
->newmessages
== 1)) {
5308 res
= ast_play_and_wait(chan
, "digits/1");
5309 res
= res
? res
: ast_play_and_wait(chan
, "vm-ny");
5310 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5312 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5313 res
= res
? res
: ast_play_and_wait(chan
, "vm-nye");
5314 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5316 if (!res
&& vms
->oldmessages
)
5317 res
= ast_play_and_wait(chan
, "vm-and");
5319 if (!res
&& vms
->oldmessages
) {
5320 if (vms
->oldmessages
== 1) {
5321 res
= ast_play_and_wait(chan
, "digits/1");
5322 res
= res
? res
: ast_play_and_wait(chan
, "vm-gamel");
5323 res
= res
? res
: ast_play_and_wait(chan
, "vm-message");
5325 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5326 res
= res
? res
: ast_play_and_wait(chan
, "vm-gamle");
5327 res
= res
? res
: ast_play_and_wait(chan
, "vm-messages");
5335 static int vm_intro_de(struct ast_channel
*chan
,struct vm_state
*vms
)
5337 /* Introduce messages they have */
5339 res
= ast_play_and_wait(chan
, "vm-youhave");
5341 if (vms
->newmessages
) {
5342 if ((vms
->newmessages
== 1))
5343 res
= ast_play_and_wait(chan
, "digits/1F");
5345 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5347 res
= ast_play_and_wait(chan
, "vm-INBOX");
5348 if (vms
->oldmessages
&& !res
)
5349 res
= ast_play_and_wait(chan
, "vm-and");
5351 if ((vms
->newmessages
== 1))
5352 res
= ast_play_and_wait(chan
, "vm-message");
5354 res
= ast_play_and_wait(chan
, "vm-messages");
5358 if (!res
&& vms
->oldmessages
) {
5359 if (vms
->oldmessages
== 1)
5360 res
= ast_play_and_wait(chan
, "digits/1F");
5362 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5364 res
= ast_play_and_wait(chan
, "vm-Old");
5366 if (vms
->oldmessages
== 1)
5367 res
= ast_play_and_wait(chan
, "vm-message");
5369 res
= ast_play_and_wait(chan
, "vm-messages");
5373 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5374 res
= ast_play_and_wait(chan
, "vm-no");
5376 res
= ast_play_and_wait(chan
, "vm-messages");
5383 /* SPANISH syntax */
5384 static int vm_intro_es(struct ast_channel
*chan
,struct vm_state
*vms
)
5386 /* Introduce messages they have */
5388 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5389 res
= ast_play_and_wait(chan
, "vm-youhaveno");
5391 res
= ast_play_and_wait(chan
, "vm-messages");
5393 res
= ast_play_and_wait(chan
, "vm-youhave");
5396 if (vms
->newmessages
) {
5398 if ((vms
->newmessages
== 1)) {
5399 res
= ast_play_and_wait(chan
, "digits/1M");
5401 res
= ast_play_and_wait(chan
, "vm-message");
5403 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5405 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5407 res
= ast_play_and_wait(chan
, "vm-messages");
5409 res
= ast_play_and_wait(chan
, "vm-INBOX");
5412 if (vms
->oldmessages
&& !res
)
5413 res
= ast_play_and_wait(chan
, "vm-and");
5415 if (vms
->oldmessages
) {
5417 if (vms
->oldmessages
== 1) {
5418 res
= ast_play_and_wait(chan
, "digits/1M");
5420 res
= ast_play_and_wait(chan
, "vm-message");
5422 res
= ast_play_and_wait(chan
, "vm-Olds");
5424 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5426 res
= ast_play_and_wait(chan
, "vm-messages");
5428 res
= ast_play_and_wait(chan
, "vm-Old");
5436 /* BRAZILIAN PORTUGUESE syntax */
5437 static int vm_intro_pt_BR(struct ast_channel
*chan
,struct vm_state
*vms
) {
5438 /* Introduce messages they have */
5440 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5441 res
= ast_play_and_wait(chan
, "vm-nomessages");
5445 res
= ast_play_and_wait(chan
, "vm-youhave");
5447 if (vms
->newmessages
) {
5449 res
= ast_say_number(chan
, vms
->newmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
5450 if ((vms
->newmessages
== 1)) {
5452 res
= ast_play_and_wait(chan
, "vm-message");
5454 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5458 res
= ast_play_and_wait(chan
, "vm-messages");
5460 res
= ast_play_and_wait(chan
, "vm-INBOX");
5462 if (vms
->oldmessages
&& !res
)
5463 res
= ast_play_and_wait(chan
, "vm-and");
5465 if (vms
->oldmessages
) {
5467 res
= ast_say_number(chan
, vms
->oldmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
5468 if (vms
->oldmessages
== 1) {
5470 res
= ast_play_and_wait(chan
, "vm-message");
5472 res
= ast_play_and_wait(chan
, "vm-Olds");
5476 res
= ast_play_and_wait(chan
, "vm-messages");
5478 res
= ast_play_and_wait(chan
, "vm-Old");
5485 static int vm_intro_fr(struct ast_channel
*chan
,struct vm_state
*vms
)
5487 /* Introduce messages they have */
5489 res
= ast_play_and_wait(chan
, "vm-youhave");
5491 if (vms
->newmessages
) {
5492 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5494 res
= ast_play_and_wait(chan
, "vm-INBOX");
5495 if (vms
->oldmessages
&& !res
)
5496 res
= ast_play_and_wait(chan
, "vm-and");
5498 if ((vms
->newmessages
== 1))
5499 res
= ast_play_and_wait(chan
, "vm-message");
5501 res
= ast_play_and_wait(chan
, "vm-messages");
5505 if (!res
&& vms
->oldmessages
) {
5506 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5508 res
= ast_play_and_wait(chan
, "vm-Old");
5510 if (vms
->oldmessages
== 1)
5511 res
= ast_play_and_wait(chan
, "vm-message");
5513 res
= ast_play_and_wait(chan
, "vm-messages");
5517 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5518 res
= ast_play_and_wait(chan
, "vm-no");
5520 res
= ast_play_and_wait(chan
, "vm-messages");
5528 static int vm_intro_nl(struct ast_channel
*chan
,struct vm_state
*vms
)
5530 /* Introduce messages they have */
5532 res
= ast_play_and_wait(chan
, "vm-youhave");
5534 if (vms
->newmessages
) {
5535 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5537 if (vms
->newmessages
== 1)
5538 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5540 res
= ast_play_and_wait(chan
, "vm-INBOX");
5542 if (vms
->oldmessages
&& !res
)
5543 res
= ast_play_and_wait(chan
, "vm-and");
5545 if ((vms
->newmessages
== 1))
5546 res
= ast_play_and_wait(chan
, "vm-message");
5548 res
= ast_play_and_wait(chan
, "vm-messages");
5552 if (!res
&& vms
->oldmessages
) {
5553 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5555 if (vms
->oldmessages
== 1)
5556 res
= ast_play_and_wait(chan
, "vm-Olds");
5558 res
= ast_play_and_wait(chan
, "vm-Old");
5561 if (vms
->oldmessages
== 1)
5562 res
= ast_play_and_wait(chan
, "vm-message");
5564 res
= ast_play_and_wait(chan
, "vm-messages");
5568 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5569 res
= ast_play_and_wait(chan
, "vm-no");
5571 res
= ast_play_and_wait(chan
, "vm-messages");
5578 /* PORTUGUESE syntax */
5579 static int vm_intro_pt(struct ast_channel
*chan
,struct vm_state
*vms
)
5581 /* Introduce messages they have */
5583 res
= ast_play_and_wait(chan
, "vm-youhave");
5585 if (vms
->newmessages
) {
5586 res
= ast_say_number(chan
, vms
->newmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
5588 if ((vms
->newmessages
== 1)) {
5589 res
= ast_play_and_wait(chan
, "vm-message");
5591 res
= ast_play_and_wait(chan
, "vm-INBOXs");
5593 res
= ast_play_and_wait(chan
, "vm-messages");
5595 res
= ast_play_and_wait(chan
, "vm-INBOX");
5598 if (vms
->oldmessages
&& !res
)
5599 res
= ast_play_and_wait(chan
, "vm-and");
5601 if (!res
&& vms
->oldmessages
) {
5602 res
= ast_say_number(chan
, vms
->oldmessages
, AST_DIGIT_ANY
, chan
->language
, "f");
5604 if (vms
->oldmessages
== 1) {
5605 res
= ast_play_and_wait(chan
, "vm-message");
5607 res
= ast_play_and_wait(chan
, "vm-Olds");
5609 res
= ast_play_and_wait(chan
, "vm-messages");
5611 res
= ast_play_and_wait(chan
, "vm-Old");
5616 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5617 res
= ast_play_and_wait(chan
, "vm-no");
5619 res
= ast_play_and_wait(chan
, "vm-messages");
5628 /* in czech there must be declension of word new and message
5629 * czech : english : czech : english
5630 * --------------------------------------------------------
5631 * vm-youhave : you have
5632 * vm-novou : one new : vm-zpravu : message
5633 * vm-nove : 2-4 new : vm-zpravy : messages
5634 * vm-novych : 5-infinite new : vm-zprav : messages
5635 * vm-starou : one old
5636 * vm-stare : 2-4 old
5637 * vm-starych : 5-infinite old
5638 * jednu : one - falling 4.
5639 * vm-no : no ( no messages )
5642 static int vm_intro_cz(struct ast_channel
*chan
,struct vm_state
*vms
)
5645 res
= ast_play_and_wait(chan
, "vm-youhave");
5647 if (vms
->newmessages
) {
5648 if (vms
->newmessages
== 1) {
5649 res
= ast_play_and_wait(chan
, "digits/jednu");
5651 res
= say_and_wait(chan
, vms
->newmessages
, chan
->language
);
5654 if ((vms
->newmessages
== 1))
5655 res
= ast_play_and_wait(chan
, "vm-novou");
5656 if ((vms
->newmessages
) > 1 && (vms
->newmessages
< 5))
5657 res
= ast_play_and_wait(chan
, "vm-nove");
5658 if (vms
->newmessages
> 4)
5659 res
= ast_play_and_wait(chan
, "vm-novych");
5661 if (vms
->oldmessages
&& !res
)
5662 res
= ast_play_and_wait(chan
, "vm-and");
5664 if ((vms
->newmessages
== 1))
5665 res
= ast_play_and_wait(chan
, "vm-zpravu");
5666 if ((vms
->newmessages
) > 1 && (vms
->newmessages
< 5))
5667 res
= ast_play_and_wait(chan
, "vm-zpravy");
5668 if (vms
->newmessages
> 4)
5669 res
= ast_play_and_wait(chan
, "vm-zprav");
5672 if (!res
&& vms
->oldmessages
) {
5673 res
= say_and_wait(chan
, vms
->oldmessages
, chan
->language
);
5675 if ((vms
->oldmessages
== 1))
5676 res
= ast_play_and_wait(chan
, "vm-starou");
5677 if ((vms
->oldmessages
) > 1 && (vms
->oldmessages
< 5))
5678 res
= ast_play_and_wait(chan
, "vm-stare");
5679 if (vms
->oldmessages
> 4)
5680 res
= ast_play_and_wait(chan
, "vm-starych");
5683 if ((vms
->oldmessages
== 1))
5684 res
= ast_play_and_wait(chan
, "vm-zpravu");
5685 if ((vms
->oldmessages
) > 1 && (vms
->oldmessages
< 5))
5686 res
= ast_play_and_wait(chan
, "vm-zpravy");
5687 if (vms
->oldmessages
> 4)
5688 res
= ast_play_and_wait(chan
, "vm-zprav");
5692 if (!vms
->oldmessages
&& !vms
->newmessages
) {
5693 res
= ast_play_and_wait(chan
, "vm-no");
5695 res
= ast_play_and_wait(chan
, "vm-zpravy");
5702 static int get_lastdigits(int num
)
5705 return (num
< 20) ? num
: num
% 10;
5708 static int vm_intro_ru(struct ast_channel
*chan
,struct vm_state
*vms
)
5714 res
= ast_play_and_wait(chan
, "vm-youhave");
5715 if (!res
&& vms
->newmessages
) {
5716 lastnum
= get_lastdigits(vms
->newmessages
);
5717 dcnum
= vms
->newmessages
- lastnum
;
5719 res
= say_and_wait(chan
, dcnum
, chan
->language
);
5720 if (!res
&& lastnum
) {
5722 res
= ast_play_and_wait(chan
, "digits/odno");
5724 res
= say_and_wait(chan
, lastnum
, chan
->language
);
5728 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-novoe" : "vm-novyh");
5730 if (!res
&& vms
->oldmessages
)
5731 res
= ast_play_and_wait(chan
, "vm-and");
5734 if (!res
&& vms
->oldmessages
) {
5735 lastnum
= get_lastdigits(vms
->oldmessages
);
5736 dcnum
= vms
->oldmessages
- lastnum
;
5738 res
= say_and_wait(chan
, dcnum
, chan
->language
);
5739 if (!res
&& lastnum
) {
5741 res
= ast_play_and_wait(chan
, "digits/odno");
5743 res
= say_and_wait(chan
, lastnum
, chan
->language
);
5747 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-staroe" : "vm-staryh");
5750 if (!res
&& !vms
->newmessages
&& !vms
->oldmessages
) {
5752 res
= ast_play_and_wait(chan
, "vm-no");
5758 res
= ast_play_and_wait(chan
, "vm-soobshenie");
5763 res
= ast_play_and_wait(chan
, "vm-soobsheniya");
5766 res
= ast_play_and_wait(chan
, "vm-soobsheniy");
5774 /* UKRAINIAN syntax */
5775 /* in ukrainian the syntax is different so we need the following files
5776 * --------------------------------------------------------
5777 * /digits/ua/1e 'odne'
5782 static int vm_intro_ua(struct ast_channel
*chan
,struct vm_state
*vms
)
5788 res
= ast_play_and_wait(chan
, "vm-youhave");
5789 if (!res
&& vms
->newmessages
) {
5790 lastnum
= get_lastdigits(vms
->newmessages
);
5791 dcnum
= vms
->newmessages
- lastnum
;
5793 res
= say_and_wait(chan
, dcnum
, chan
->language
);
5794 if (!res
&& lastnum
) {
5796 res
= ast_play_and_wait(chan
, "digits/ua/1e");
5798 res
= say_and_wait(chan
, lastnum
, chan
->language
);
5802 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-nove" : "vm-INBOX");
5804 if (!res
&& vms
->oldmessages
)
5805 res
= ast_play_and_wait(chan
, "vm-and");
5808 if (!res
&& vms
->oldmessages
) {
5809 lastnum
= get_lastdigits(vms
->oldmessages
);
5810 dcnum
= vms
->oldmessages
- lastnum
;
5812 res
= say_and_wait(chan
, dcnum
, chan
->language
);
5813 if (!res
&& lastnum
) {
5815 res
= ast_play_and_wait(chan
, "digits/ua/1e");
5817 res
= say_and_wait(chan
, lastnum
, chan
->language
);
5821 res
= ast_play_and_wait(chan
, (lastnum
== 1) ? "vm-stare" : "vm-Old");
5824 if (!res
&& !vms
->newmessages
&& !vms
->oldmessages
) {
5826 res
= ast_play_and_wait(chan
, "vm-no");
5835 res
= ast_play_and_wait(chan
, "vm-message");
5838 res
= ast_play_and_wait(chan
, "vm-messages");
5847 static int vm_intro(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
)
5851 /* Notify the user that the temp greeting is set and give them the option to remove it */
5852 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
5853 if (ast_test_flag(vmu
, VM_TEMPGREETWARN
)) {
5854 if (ast_fileexists(prefile
, NULL
, NULL
) > 0)
5855 ast_play_and_wait(chan
, "vm-tempgreetactive");
5858 /* Play voicemail intro - syntax is different for different languages */
5859 if (!strcasecmp(chan
->language
, "de")) { /* GERMAN syntax */
5860 return vm_intro_de(chan
, vms
);
5861 } else if (!strcasecmp(chan
->language
, "es")) { /* SPANISH syntax */
5862 return vm_intro_es(chan
, vms
);
5863 } else if (!strcasecmp(chan
->language
, "it")) { /* ITALIAN syntax */
5864 return vm_intro_it(chan
, vms
);
5865 } else if (!strcasecmp(chan
->language
, "fr")) { /* FRENCH syntax */
5866 return vm_intro_fr(chan
, vms
);
5867 } else if (!strcasecmp(chan
->language
, "nl")) { /* DUTCH syntax */
5868 return vm_intro_nl(chan
, vms
);
5869 } else if (!strcasecmp(chan
->language
, "pt")) { /* PORTUGUESE syntax */
5870 return vm_intro_pt(chan
, vms
);
5871 } else if (!strcasecmp(chan
->language
, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
5872 return vm_intro_pt_BR(chan
, vms
);
5873 } else if (!strcasecmp(chan
->language
, "cz")) { /* CZECH syntax */
5874 return vm_intro_cz(chan
, vms
);
5875 } else if (!strcasecmp(chan
->language
, "gr")) { /* GREEK syntax */
5876 return vm_intro_gr(chan
, vms
);
5877 } else if (!strcasecmp(chan
->language
, "pl")) { /* POLISH syntax */
5878 return vm_intro_pl(chan
, vms
);
5879 } else if (!strcasecmp(chan
->language
, "se")) { /* SWEDISH syntax */
5880 return vm_intro_se(chan
, vms
);
5881 } else if (!strcasecmp(chan
->language
, "no")) { /* NORWEGIAN syntax */
5882 return vm_intro_no(chan
, vms
);
5883 } else if (!strcasecmp(chan
->language
, "ru")) { /* RUSSIAN syntax */
5884 return vm_intro_ru(chan
, vms
);
5885 } else if (!strcasecmp(chan
->language
, "ua")) { /* UKRAINIAN syntax */
5886 return vm_intro_ua(chan
, vms
);
5887 } else { /* Default to ENGLISH */
5888 return vm_intro_en(chan
, vms
);
5892 static int vm_instructions(struct ast_channel
*chan
, struct vm_state
*vms
, int skipadvanced
)
5895 /* Play instructions and wait for new command */
5897 if (vms
->starting
) {
5898 if (vms
->lastmsg
> -1) {
5899 res
= ast_play_and_wait(chan
, "vm-onefor");
5901 res
= vm_play_folder_name(chan
, vms
->vmbox
);
5904 res
= ast_play_and_wait(chan
, "vm-opts");
5907 res
= ast_play_and_wait(chan
, "vm-prev");
5908 if (!res
&& !skipadvanced
)
5909 res
= ast_play_and_wait(chan
, "vm-advopts");
5911 res
= ast_play_and_wait(chan
, "vm-repeat");
5912 if (!res
&& (vms
->curmsg
!= vms
->lastmsg
))
5913 res
= ast_play_and_wait(chan
, "vm-next");
5915 if (!vms
->deleted
[vms
->curmsg
])
5916 res
= ast_play_and_wait(chan
, "vm-delete");
5918 res
= ast_play_and_wait(chan
, "vm-undelete");
5920 res
= ast_play_and_wait(chan
, "vm-toforward");
5922 res
= ast_play_and_wait(chan
, "vm-savemessage");
5926 res
= ast_play_and_wait(chan
, "vm-helpexit");
5928 res
= ast_waitfordigit(chan
, 6000);
5931 if (vms
->repeats
> 2) {
5939 static int vm_newuser(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
)
5944 char newpassword
[80] = "";
5945 char newpassword2
[80] = "";
5946 char prefile
[PATH_MAX
] = "";
5947 unsigned char buf
[256];
5950 if (ast_adsi_available(chan
)) {
5951 bytes
+= adsi_logo(buf
+ bytes
);
5952 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "New User Setup", "");
5953 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Not Done", "");
5954 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
5955 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
5956 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
5959 /* First, have the user change their password
5960 so they won't get here again */
5962 newpassword
[1] = '\0';
5963 newpassword
[0] = cmd
= ast_play_and_wait(chan
,"vm-newpassword");
5965 newpassword
[0] = '\0';
5966 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5968 cmd
= ast_readstring(chan
,newpassword
+ strlen(newpassword
),sizeof(newpassword
)-1,2000,10000,"#");
5969 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5971 newpassword2
[1] = '\0';
5972 newpassword2
[0] = cmd
= ast_play_and_wait(chan
,"vm-reenterpassword");
5974 newpassword2
[0] = '\0';
5975 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5977 cmd
= ast_readstring(chan
,newpassword2
+ strlen(newpassword2
),sizeof(newpassword2
)-1,2000,10000,"#");
5978 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
5980 if (!strcmp(newpassword
, newpassword2
))
5982 ast_log(LOG_NOTICE
,"Password mismatch for user %s (%s != %s)\n", vms
->username
, newpassword
, newpassword2
);
5983 cmd
= ast_play_and_wait(chan
, "vm-mismatch");
5987 if (ast_strlen_zero(ext_pass_cmd
))
5988 vm_change_password(vmu
,newpassword
);
5990 vm_change_password_shell(vmu
,newpassword
);
5991 if (option_debug
> 2)
5992 ast_log(LOG_DEBUG
,"User %s set password to %s of length %d\n",vms
->username
,newpassword
,(int)strlen(newpassword
));
5993 cmd
= ast_play_and_wait(chan
,"vm-passchanged");
5995 /* If forcename is set, have the user record their name */
5996 if (ast_test_flag(vmu
, VM_FORCENAME
)) {
5997 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/greet", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
5998 if (ast_fileexists(prefile
, NULL
, NULL
) < 1) {
5999 #ifndef IMAP_STORAGE
6000 cmd
= play_record_review(chan
, "vm-rec-name", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6002 cmd
= play_record_review(chan
, "vm-rec-name", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6004 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
6009 /* If forcegreetings is set, have the user record their greetings */
6010 if (ast_test_flag(vmu
, VM_FORCEGREET
)) {
6011 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/unavail", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6012 if (ast_fileexists(prefile
, NULL
, NULL
) < 1) {
6013 #ifndef IMAP_STORAGE
6014 cmd
= play_record_review(chan
, "vm-rec-unv", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6016 cmd
= play_record_review(chan
, "vm-rec-unv", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6018 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
6022 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/busy", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6023 if (ast_fileexists(prefile
, NULL
, NULL
) < 1) {
6024 #ifndef IMAP_STORAGE
6025 cmd
= play_record_review(chan
, "vm-rec-busy", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6027 cmd
= play_record_review(chan
, "vm-rec-busy", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6029 if (cmd
< 0 || cmd
== 't' || cmd
== '#')
6037 static int vm_options(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
)
6042 char newpassword
[80] = "";
6043 char newpassword2
[80] = "";
6044 char prefile
[PATH_MAX
] = "";
6045 unsigned char buf
[256];
6048 if (ast_adsi_available(chan
))
6050 bytes
+= adsi_logo(buf
+ bytes
);
6051 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Options Menu", "");
6052 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Not Done", "");
6053 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
6054 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
6055 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
6057 while ((cmd
>= 0) && (cmd
!= 't')) {
6062 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/unavail", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6063 #ifndef IMAP_STORAGE
6064 cmd
= play_record_review(chan
,"vm-rec-unv",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6066 cmd
= play_record_review(chan
,"vm-rec-unv",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6070 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/busy", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6071 #ifndef IMAP_STORAGE
6072 cmd
= play_record_review(chan
,"vm-rec-busy",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6074 cmd
= play_record_review(chan
,"vm-rec-busy",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6078 snprintf(prefile
,sizeof(prefile
), "%s%s/%s/greet", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6079 #ifndef IMAP_STORAGE
6080 cmd
= play_record_review(chan
,"vm-rec-name",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6082 cmd
= play_record_review(chan
,"vm-rec-name",prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6086 cmd
= vm_tempgreeting(chan
, vmu
, vms
, fmtc
, record_gain
);
6089 if (vmu
->password
[0] == '-') {
6090 cmd
= ast_play_and_wait(chan
, "vm-no");
6093 newpassword
[1] = '\0';
6094 newpassword
[0] = cmd
= ast_play_and_wait(chan
,"vm-newpassword");
6096 newpassword
[0] = '\0';
6100 if ((cmd
= ast_readstring(chan
,newpassword
+ strlen(newpassword
),sizeof(newpassword
)-1,2000,10000,"#")) < 0) {
6104 newpassword2
[1] = '\0';
6105 newpassword2
[0] = cmd
= ast_play_and_wait(chan
,"vm-reenterpassword");
6107 newpassword2
[0] = '\0';
6112 if ((cmd
= ast_readstring(chan
,newpassword2
+ strlen(newpassword2
),sizeof(newpassword2
)-1,2000,10000,"#"))) {
6116 if (strcmp(newpassword
, newpassword2
)) {
6117 ast_log(LOG_NOTICE
,"Password mismatch for user %s (%s != %s)\n", vms
->username
, newpassword
, newpassword2
);
6118 cmd
= ast_play_and_wait(chan
, "vm-mismatch");
6121 if (ast_strlen_zero(ext_pass_cmd
))
6122 vm_change_password(vmu
,newpassword
);
6124 vm_change_password_shell(vmu
,newpassword
);
6125 if (option_debug
> 2)
6126 ast_log(LOG_DEBUG
,"User %s set password to %s of length %d\n",vms
->username
,newpassword
,(int)strlen(newpassword
));
6127 cmd
= ast_play_and_wait(chan
,"vm-passchanged");
6134 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6135 if (ast_fileexists(prefile
, NULL
, NULL
))
6136 cmd
= ast_play_and_wait(chan
, "vm-tmpexists");
6138 cmd
= ast_play_and_wait(chan
, "vm-options");
6140 cmd
= ast_waitfordigit(chan
,6000);
6152 static int vm_tempgreeting(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, struct vm_state
*vms
, char *fmtc
, signed char record_gain
)
6158 char prefile
[PATH_MAX
] = "";
6159 unsigned char buf
[256];
6160 char dest
[PATH_MAX
];
6163 if (ast_adsi_available(chan
)) {
6164 bytes
+= adsi_logo(buf
+ bytes
);
6165 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 3, ADSI_JUST_CENT
, 0, "Temp Greeting Menu", "");
6166 bytes
+= ast_adsi_display(buf
+ bytes
, ADSI_COMM_PAGE
, 4, ADSI_JUST_CENT
, 0, "Not Done", "");
6167 bytes
+= ast_adsi_set_line(buf
+ bytes
, ADSI_COMM_PAGE
, 1);
6168 bytes
+= ast_adsi_voice_mode(buf
+ bytes
, 0);
6169 ast_adsi_transmit_message(chan
, buf
, bytes
, ADSI_MSG_DISPLAY
);
6172 snprintf(prefile
, sizeof(prefile
), "%s%s/%s/temp", VM_SPOOL_DIR
, vmu
->context
, vms
->username
);
6173 if ((res
= create_dirpath(dest
, sizeof(dest
), vmu
->context
, vms
->username
, "temp"))) {
6174 ast_log(LOG_WARNING
, "Failed to create directory (%s).\n", prefile
);
6177 while ((cmd
>= 0) && (cmd
!= 't')) {
6180 RETRIEVE(prefile
, -1);
6181 if (ast_fileexists(prefile
, NULL
, NULL
) <= 0) {
6182 #ifndef IMAP_STORAGE
6183 play_record_review(chan
, "vm-rec-temp", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6185 play_record_review(chan
, "vm-rec-temp", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6191 #ifndef IMAP_STORAGE
6192 cmd
= play_record_review(chan
, "vm-rec-temp", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, NULL
);
6194 cmd
= play_record_review(chan
, "vm-rec-temp", prefile
, maxgreet
, fmtc
, 0, vmu
, &duration
, NULL
, record_gain
, vms
);
6198 DELETE(prefile
, -1, prefile
);
6199 ast_play_and_wait(chan
, "vm-tempremoved");
6206 cmd
= ast_play_and_wait(chan
,
6207 ast_fileexists(prefile
, NULL
, NULL
) > 0 ? /* XXX always true ? */
6208 "vm-tempgreeting2" : "vm-tempgreeting");
6210 cmd
= ast_waitfordigit(chan
,6000);
6217 DISPOSE(prefile
, -1);
6226 static int vm_browse_messages_gr(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6230 if (vms
->lastmsg
> -1) {
6231 cmd
= play_message(chan
, vmu
, vms
);
6233 cmd
= ast_play_and_wait(chan
, "vm-youhaveno");
6234 if (!strcasecmp(vms
->vmbox
, "vm-INBOX") ||!strcasecmp(vms
->vmbox
, "vm-Old")){
6236 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%ss", vms
->curbox
);
6237 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6240 cmd
= ast_play_and_wait(chan
, "vm-messages");
6243 cmd
= ast_play_and_wait(chan
, "vm-messages");
6245 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6246 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6253 /* Default English syntax */
6254 static int vm_browse_messages_en(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6258 if (vms
->lastmsg
> -1) {
6259 cmd
= play_message(chan
, vmu
, vms
);
6261 cmd
= ast_play_and_wait(chan
, "vm-youhave");
6263 cmd
= ast_play_and_wait(chan
, "vm-no");
6265 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6266 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6269 cmd
= ast_play_and_wait(chan
, "vm-messages");
6274 /* ITALIAN syntax */
6275 static int vm_browse_messages_it(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6279 if (vms
->lastmsg
> -1) {
6280 cmd
= play_message(chan
, vmu
, vms
);
6282 cmd
= ast_play_and_wait(chan
, "vm-no");
6284 cmd
= ast_play_and_wait(chan
, "vm-message");
6286 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6287 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6293 /* SPANISH syntax */
6294 static int vm_browse_messages_es(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6298 if (vms
->lastmsg
> -1) {
6299 cmd
= play_message(chan
, vmu
, vms
);
6301 cmd
= ast_play_and_wait(chan
, "vm-youhaveno");
6303 cmd
= ast_play_and_wait(chan
, "vm-messages");
6305 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6306 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6312 /* PORTUGUESE syntax */
6313 static int vm_browse_messages_pt(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6317 if (vms
->lastmsg
> -1) {
6318 cmd
= play_message(chan
, vmu
, vms
);
6320 cmd
= ast_play_and_wait(chan
, "vm-no");
6322 snprintf(vms
->fn
, sizeof(vms
->fn
), "vm-%s", vms
->curbox
);
6323 cmd
= ast_play_and_wait(chan
, vms
->fn
);
6326 cmd
= ast_play_and_wait(chan
, "vm-messages");
6331 static int vm_browse_messages(struct ast_channel
*chan
, struct vm_state
*vms
, struct ast_vm_user
*vmu
)
6333 if (!strcasecmp(chan
->language
, "es")) { /* SPANISH */
6334 return vm_browse_messages_es(chan
, vms
, vmu
);
6335 } else if (!strcasecmp(chan
->language
, "it")) { /* ITALIAN */
6336 return vm_browse_messages_it(chan
, vms
, vmu
);
6337 } else if (!strcasecmp(chan
->language
, "pt") || !strcasecmp(chan
->language
, "pt_BR")) { /* PORTUGUESE */
6338 return vm_browse_messages_pt(chan
, vms
, vmu
);
6339 } else if (!strcasecmp(chan
->language
, "gr")){
6340 return vm_browse_messages_gr(chan
, vms
, vmu
); /* GREEK */
6341 } else { /* Default to English syntax */
6342 return vm_browse_messages_en(chan
, vms
, vmu
);
6346 static int vm_authenticate(struct ast_channel
*chan
, char *mailbox
, int mailbox_size
,
6347 struct ast_vm_user
*res_vmu
, const char *context
, const char *prefix
,
6348 int skipuser
, int maxlogins
, int silent
)
6350 int useadsi
=0, valid
=0, logretries
=0;
6351 char password
[AST_MAX_EXTENSION
]="", *passptr
;
6352 struct ast_vm_user vmus
, *vmu
= NULL
;
6354 /* If ADSI is supported, setup login screen */
6355 adsi_begin(chan
, &useadsi
);
6356 if (!skipuser
&& useadsi
)
6358 if (!silent
&& !skipuser
&& ast_streamfile(chan
, "vm-login", chan
->language
)) {
6359 ast_log(LOG_WARNING
, "Couldn't stream login file\n");
6363 /* Authenticate them and get their mailbox/password */
6365 while (!valid
&& (logretries
< maxlogins
)) {
6366 /* Prompt for, and read in the username */
6367 if (!skipuser
&& ast_readstring(chan
, mailbox
, mailbox_size
- 1, 2000, 10000, "#") < 0) {
6368 ast_log(LOG_WARNING
, "Couldn't read username\n");
6371 if (ast_strlen_zero(mailbox
)) {
6372 if (chan
->cid
.cid_num
) {
6373 ast_copy_string(mailbox
, chan
->cid
.cid_num
, mailbox_size
);
6375 if (option_verbose
> 2)
6376 ast_verbose(VERBOSE_PREFIX_3
"Username not entered\n");
6381 adsi_password(chan
);
6383 if (!ast_strlen_zero(prefix
)) {
6384 char fullusername
[80] = "";
6385 ast_copy_string(fullusername
, prefix
, sizeof(fullusername
));
6386 strncat(fullusername
, mailbox
, sizeof(fullusername
) - 1 - strlen(fullusername
));
6387 ast_copy_string(mailbox
, fullusername
, mailbox_size
);
6391 ast_log(LOG_DEBUG
, "Before find user for mailbox %s\n",mailbox
);
6392 vmu
= find_user(&vmus
, context
, mailbox
);
6393 if (vmu
&& (vmu
->password
[0] == '\0' || (vmu
->password
[0] == '-' && vmu
->password
[1] == '\0'))) {
6394 /* saved password is blank, so don't bother asking */
6397 if (ast_streamfile(chan
, "vm-password", chan
->language
)) {
6398 ast_log(LOG_WARNING
, "Unable to stream password file\n");
6401 if (ast_readstring(chan
, password
, sizeof(password
) - 1, 2000, 10000, "#") < 0) {
6402 ast_log(LOG_WARNING
, "Unable to read password\n");
6408 passptr
= vmu
->password
;
6409 if (passptr
[0] == '-') passptr
++;
6411 if (vmu
&& !strcmp(passptr
, password
))
6414 if (option_verbose
> 2)
6415 ast_verbose( VERBOSE_PREFIX_3
"Incorrect password '%s' for user '%s' (context = %s)\n", password
, mailbox
, context
? context
: "default");
6416 if (!ast_strlen_zero(prefix
))
6421 if (skipuser
|| logretries
>= maxlogins
) {
6422 if (ast_streamfile(chan
, "vm-incorrect", chan
->language
)) {
6423 ast_log(LOG_WARNING
, "Unable to stream incorrect message\n");
6429 if (ast_streamfile(chan
, "vm-incorrect-mailbox", chan
->language
)) {
6430 ast_log(LOG_WARNING
, "Unable to stream incorrect mailbox message\n");
6434 if (ast_waitstream(chan
, "")) /* Channel is hung up */
6438 if (!valid
&& (logretries
>= maxlogins
)) {
6439 ast_stopstream(chan
);
6440 ast_play_and_wait(chan
, "vm-goodbye");
6443 if (vmu
&& !skipuser
) {
6444 memcpy(res_vmu
, vmu
, sizeof(struct ast_vm_user
));
6449 static int vm_execmain(struct ast_channel
*chan
, void *data
)
6451 /* XXX This is, admittedly, some pretty horrendus code. For some
6452 reason it just seemed a lot easier to do with GOTO's. I feel
6453 like I'm back in my GWBASIC days. XXX */
6457 struct ast_module_user
*u
;
6458 char prefixstr
[80] ="";
6459 char ext_context
[256]="";
6463 struct vm_state vms
;
6464 struct ast_vm_user
*vmu
= NULL
, vmus
;
6467 struct ast_flags flags
= { 0 };
6468 signed char record_gain
= 0;
6470 int play_folder
= 0;
6474 u
= ast_module_user_add(chan
);
6476 /* Add the vm_state to the active list and keep it active */
6477 memset(&vms
, 0, sizeof(vms
));
6480 memset(&vmus
, 0, sizeof(vmus
));
6482 if (chan
->_state
!= AST_STATE_UP
) {
6484 ast_log(LOG_DEBUG
, "Before ast_answer\n");
6488 if (!ast_strlen_zero(data
)) {
6489 char *opts
[OPT_ARG_ARRAY_SIZE
];
6491 AST_DECLARE_APP_ARGS(args
,
6496 parse
= ast_strdupa(data
);
6498 AST_STANDARD_APP_ARGS(args
, parse
);
6500 if (args
.argc
== 2) {
6501 if (ast_app_parse_options(vm_app_options
, &flags
, opts
, args
.argv1
)) {
6502 ast_module_user_remove(u
);
6505 if (ast_test_flag(&flags
, OPT_RECORDGAIN
)) {
6507 if (!ast_strlen_zero(opts
[OPT_ARG_RECORDGAIN
])) {
6508 if (sscanf(opts
[OPT_ARG_RECORDGAIN
], "%d", &gain
) != 1) {
6509 ast_log(LOG_WARNING
, "Invalid value '%s' provided for record gain option\n", opts
[OPT_ARG_RECORDGAIN
]);
6510 ast_module_user_remove(u
);
6513 record_gain
= (signed char) gain
;
6516 ast_log(LOG_WARNING
, "Invalid Gain level set with option g\n");
6519 if (ast_test_flag(&flags
, OPT_AUTOPLAY
) ) {
6521 if (opts
[OPT_ARG_PLAYFOLDER
]) {
6522 if (sscanf(opts
[OPT_ARG_PLAYFOLDER
], "%d", &play_folder
) != 1) {
6523 ast_log(LOG_WARNING
, "Invalid value '%s' provided for folder autoplay option\n", opts
[OPT_ARG_PLAYFOLDER
]);
6526 ast_log(LOG_WARNING
, "Invalid folder set with option a\n");
6528 if ( play_folder
> 9 || play_folder
< 0) {
6529 ast_log(LOG_WARNING
, "Invalid value '%d' provided for folder autoplay option\n", play_folder
);
6534 /* old style options parsing */
6535 while (*(args
.argv0
)) {
6536 if (*(args
.argv0
) == 's')
6537 ast_set_flag(&flags
, OPT_SILENT
);
6538 else if (*(args
.argv0
) == 'p')
6539 ast_set_flag(&flags
, OPT_PREPEND_MAILBOX
);
6547 valid
= ast_test_flag(&flags
, OPT_SILENT
);
6549 if ((context
= strchr(args
.argv0
, '@')))
6552 if (ast_test_flag(&flags
, OPT_PREPEND_MAILBOX
))
6553 ast_copy_string(prefixstr
, args
.argv0
, sizeof(prefixstr
));
6555 ast_copy_string(vms
.username
, args
.argv0
, sizeof(vms
.username
));
6557 if (!ast_strlen_zero(vms
.username
) && (vmu
= find_user(&vmus
, context
,vms
.username
)))
6564 res
= vm_authenticate(chan
, vms
.username
, sizeof(vms
.username
), &vmus
, context
, prefixstr
, skipuser
, maxlogins
, 0);
6567 ast_log(LOG_DEBUG
, "After vm_authenticate\n");
6576 /* If ADSI is supported, setup login screen */
6577 adsi_begin(chan
, &useadsi
);
6580 vms
.interactive
= 1;
6582 vmstate_insert(&vms
);
6583 init_vm_state(&vms
);
6588 if (!(vms
.deleted
= ast_calloc(vmu
->maxmsg
, sizeof(int)))) {
6589 /* TODO: Handle memory allocation failure */
6591 if (!(vms
.heard
= ast_calloc(vmu
->maxmsg
, sizeof(int)))) {
6592 /* TODO: Handle memory allocation failure */
6595 /* Set language from config to override channel language */
6596 if (!ast_strlen_zero(vmu
->language
))
6597 ast_string_field_set(chan
, language
, vmu
->language
);
6598 create_dirpath(vms
.curdir
, sizeof(vms
.curdir
), vmu
->context
, vms
.username
, "");
6599 /* Retrieve old and new message counts */
6601 ast_log(LOG_DEBUG
, "Before open_mailbox\n");
6602 res
= open_mailbox(&vms
, vmu
, 1);
6603 if (res
== ERROR_LOCK_PATH
)
6605 vms
.oldmessages
= vms
.lastmsg
+ 1;
6606 if (option_debug
> 2)
6607 ast_log(LOG_DEBUG
, "Number of old messages: %d\n",vms
.oldmessages
);
6608 /* Start in INBOX */
6609 res
= open_mailbox(&vms
, vmu
, 0);
6610 if (res
== ERROR_LOCK_PATH
)
6612 vms
.newmessages
= vms
.lastmsg
+ 1;
6613 if (option_debug
> 2)
6614 ast_log(LOG_DEBUG
, "Number of new messages: %d\n",vms
.newmessages
);
6616 /* Select proper mailbox FIRST!! */
6618 res
= open_mailbox(&vms
, vmu
, play_folder
);
6619 if (res
== ERROR_LOCK_PATH
)
6622 /* If there are no new messages, inform the user and hangup */
6623 if (vms
.lastmsg
== -1) {
6624 cmd
= vm_browse_messages(chan
, &vms
, vmu
);
6629 if (!vms
.newmessages
&& vms
.oldmessages
) {
6630 /* If we only have old messages start here */
6631 res
= open_mailbox(&vms
, vmu
, 1);
6633 if (res
== ERROR_LOCK_PATH
)
6639 adsi_status(chan
, &vms
);
6642 /* Check to see if this is a new user */
6643 if (!strcasecmp(vmu
->mailbox
, vmu
->password
) &&
6644 (ast_test_flag(vmu
, VM_FORCENAME
| VM_FORCEGREET
))) {
6645 if (ast_play_and_wait(chan
, "vm-newuser") == -1)
6646 ast_log(LOG_WARNING
, "Couldn't stream new user file\n");
6647 cmd
= vm_newuser(chan
, vmu
, &vms
, vmfmts
, record_gain
);
6648 if ((cmd
== 't') || (cmd
== '#')) {
6652 } else if (cmd
< 0) {
6659 if (option_debug
> 2)
6660 ast_log(LOG_DEBUG
, "Checking quotas: comparing %u to %u\n",vms
.quota_usage
,vms
.quota_limit
);
6661 if (vms
.quota_limit
&& vms
.quota_usage
>= vms
.quota_limit
) {
6663 ast_log(LOG_DEBUG
, "*** QUOTA EXCEEDED!!\n");
6664 cmd
= ast_play_and_wait(chan
, "vm-mailboxfull");
6666 if (option_debug
> 2)
6667 ast_log(LOG_DEBUG
, "Checking quotas: User has %d messages and limit is %d.\n",(vms
.newmessages
+ vms
.oldmessages
),vmu
->maxmsg
);
6668 if ((vms
.newmessages
+ vms
.oldmessages
) >= vmu
->maxmsg
) {
6669 ast_log(LOG_WARNING
, "No more messages possible. User has %d messages and limit is %d.\n",(vms
.newmessages
+ vms
.oldmessages
),vmu
->maxmsg
);
6670 cmd
= ast_play_and_wait(chan
, "vm-mailboxfull");
6676 cmd
= vm_intro(chan
, vmu
, &vms
);
6681 while ((cmd
> -1) && (cmd
!= 't') && (cmd
!= '#')) {
6688 cmd
= vm_browse_messages(chan
, &vms
, vmu
);
6690 case '2': /* Change folders */
6692 adsi_folders(chan
, 0, "Change to folder...");
6693 cmd
= get_folder2(chan
, "vm-changeto", 0);
6696 } else if (cmd
> 0) {
6698 res
= close_mailbox(&vms
, vmu
);
6699 if (res
== ERROR_LOCK_PATH
)
6701 res
= open_mailbox(&vms
, vmu
, cmd
);
6702 if (res
== ERROR_LOCK_PATH
)
6708 adsi_status2(chan
, &vms
);
6711 cmd
= vm_play_folder_name(chan
, vms
.vmbox
);
6715 case '3': /* Advanced options */
6718 while ((cmd
> -1) && (cmd
!= 't') && (cmd
!= '#')) {
6720 case '1': /* Reply */
6721 if (vms
.lastmsg
> -1 && !vms
.starting
) {
6722 cmd
= advanced_options(chan
, vmu
, &vms
, vms
.curmsg
, 1, record_gain
);
6723 if (cmd
== ERROR_LOCK_PATH
) {
6728 cmd
= ast_play_and_wait(chan
, "vm-sorry");
6731 case '2': /* Callback */
6732 if (option_verbose
> 2 && !vms
.starting
)
6733 ast_verbose( VERBOSE_PREFIX_3
"Callback Requested\n");
6734 if (!ast_strlen_zero(vmu
->callback
) && vms
.lastmsg
> -1 && !vms
.starting
) {
6735 cmd
= advanced_options(chan
, vmu
, &vms
, vms
.curmsg
, 2, record_gain
);
6739 } else if (cmd
== ERROR_LOCK_PATH
) {
6745 cmd
= ast_play_and_wait(chan
, "vm-sorry");
6748 case '3': /* Envelope */
6749 if (vms
.lastmsg
> -1 && !vms
.starting
) {
6750 cmd
= advanced_options(chan
, vmu
, &vms
, vms
.curmsg
, 3, record_gain
);
6751 if (cmd
== ERROR_LOCK_PATH
) {
6756 cmd
= ast_play_and_wait(chan
, "vm-sorry");
6759 case '4': /* Dialout */
6760 if (!ast_strlen_zero(vmu
->dialout
)) {
6761 cmd
= dialout(chan
, vmu
, NULL
, vmu
->dialout
);
6768 cmd
= ast_play_and_wait(chan
, "vm-sorry");
6772 case '5': /* Leave VoiceMail */
6773 if (ast_test_flag(vmu
, VM_SVMAIL
)) {
6774 cmd
= forward_message(chan
, context
, &vms
, vmu
, vmfmts
, 1, record_gain
);
6775 if (cmd
== ERROR_LOCK_PATH
) {
6777 ast_log(LOG_WARNING
, "forward_message failed to lock path.\n");
6781 cmd
= ast_play_and_wait(chan
,"vm-sorry");
6785 case '*': /* Return to main menu */
6791 if (!vms
.starting
) {
6792 cmd
= ast_play_and_wait(chan
, "vm-toreply");
6794 if (!ast_strlen_zero(vmu
->callback
) && !vms
.starting
&& !cmd
) {
6795 cmd
= ast_play_and_wait(chan
, "vm-tocallback");
6797 if (!cmd
&& !vms
.starting
) {
6798 cmd
= ast_play_and_wait(chan
, "vm-tohearenv");
6800 if (!ast_strlen_zero(vmu
->dialout
) && !cmd
) {
6801 cmd
= ast_play_and_wait(chan
, "vm-tomakecall");
6803 if (ast_test_flag(vmu
, VM_SVMAIL
) && !cmd
)
6804 cmd
=ast_play_and_wait(chan
, "vm-leavemsg");
6806 cmd
= ast_play_and_wait(chan
, "vm-starmain");
6808 cmd
= ast_waitfordigit(chan
,6000);
6811 if (vms
.repeats
> 3)
6821 if (vms
.curmsg
> 0) {
6823 cmd
= play_message(chan
, vmu
, &vms
);
6825 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6829 if (vms
.curmsg
< vms
.lastmsg
) {
6831 cmd
= play_message(chan
, vmu
, &vms
);
6833 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6837 if (vms
.curmsg
>= 0 && vms
.curmsg
<= vms
.lastmsg
) {
6838 vms
.deleted
[vms
.curmsg
] = !vms
.deleted
[vms
.curmsg
];
6840 adsi_delete(chan
, &vms
);
6841 if (vms
.deleted
[vms
.curmsg
]) {
6842 if (play_folder
== 0)
6844 else if (play_folder
== 1)
6846 cmd
= ast_play_and_wait(chan
, "vm-deleted");
6849 if (play_folder
== 0)
6851 else if (play_folder
== 1)
6853 cmd
= ast_play_and_wait(chan
, "vm-undeleted");
6855 if (ast_test_flag((&globalflags
), VM_SKIPAFTERCMD
)) {
6856 if (vms
.curmsg
< vms
.lastmsg
) {
6858 cmd
= play_message(chan
, vmu
, &vms
);
6860 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6863 } else /* Delete not valid if we haven't selected a message */
6871 if (vms
.lastmsg
> -1) {
6872 cmd
= forward_message(chan
, context
, &vms
, vmu
, vmfmts
, 0, record_gain
);
6873 if (cmd
== ERROR_LOCK_PATH
) {
6878 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6881 if (vms
.curmsg
< 0 || vms
.curmsg
> vms
.lastmsg
) {
6882 /* No message selected */
6887 adsi_folders(chan
, 1, "Save to folder...");
6888 cmd
= get_folder2(chan
, "vm-savefolder", 1);
6889 box
= 0; /* Shut up compiler */
6893 } else if (cmd
> 0) {
6894 box
= cmd
= cmd
- '0';
6895 cmd
= save_to_folder(vmu
, &vms
, vms
.curmsg
, cmd
);
6896 if (cmd
== ERROR_LOCK_PATH
) {
6900 } else if (cmd
== 10) {
6904 vms
.deleted
[vms
.curmsg
] = 1;
6906 vms
.deleted
[vms
.curmsg
] = 0;
6907 vms
.heard
[vms
.curmsg
] = 0;
6910 make_file(vms
.fn
, sizeof(vms
.fn
), vms
.curdir
, vms
.curmsg
);
6912 adsi_message(chan
, &vms
);
6913 snprintf(vms
.fn
, sizeof(vms
.fn
), "vm-%s", mbox(box
));
6915 cmd
= ast_play_and_wait(chan
, "vm-message");
6917 cmd
= say_and_wait(chan
, vms
.curmsg
+ 1, chan
->language
);
6919 cmd
= ast_play_and_wait(chan
, "vm-savedto");
6921 cmd
= vm_play_folder_name(chan
, vms
.fn
);
6923 cmd
= ast_play_and_wait(chan
, "vm-mailboxfull");
6925 if (ast_test_flag((&globalflags
), VM_SKIPAFTERCMD
)) {
6926 if (vms
.curmsg
< vms
.lastmsg
) {
6928 cmd
= play_message(chan
, vmu
, &vms
);
6930 cmd
= ast_play_and_wait(chan
, "vm-nomore");
6935 if (!vms
.starting
) {
6936 cmd
= ast_play_and_wait(chan
, "vm-onefor");
6938 cmd
= vm_play_folder_name(chan
, vms
.vmbox
);
6940 cmd
= ast_play_and_wait(chan
, "vm-opts");
6942 cmd
= vm_instructions(chan
, &vms
, 1);
6947 cmd
= vm_options(chan
, vmu
, &vms
, vmfmts
, record_gain
);
6949 adsi_status(chan
, &vms
);
6951 default: /* Nothing */
6952 cmd
= vm_instructions(chan
, &vms
, 0);
6956 if ((cmd
== 't') || (cmd
== '#')) {
6966 ast_stopstream(chan
);
6970 res
= ast_play_and_wait(chan
, "vm-dialout");
6972 res
= ast_play_and_wait(chan
, "vm-goodbye");
6977 ast_adsi_unload_session(chan
);
6980 close_mailbox(&vms
, vmu
);
6982 snprintf(ext_context
, sizeof(ext_context
), "%s@%s", vms
.username
, vmu
->context
);
6983 manager_event(EVENT_FLAG_CALL
, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context
, has_voicemail(ext_context
, NULL
));
6984 run_externnotify(vmu
->context
, vmu
->mailbox
);
6987 /* expunge message - use UID Expunge if supported on IMAP server*/
6988 if (option_debug
> 2)
6989 ast_log(LOG_DEBUG
, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted
,expungeonhangup
);
6990 if (vmu
&& deleted
== 1 && expungeonhangup
== 1) {
6991 #ifdef HAVE_IMAP_TK2006
6992 if (LEVELUIDPLUS (vms
.mailstream
)) {
6993 mail_expunge_full(vms
.mailstream
,NIL
,EX_UID
);
6996 mail_expunge(vms
.mailstream
);
6998 /* before we delete the state, we should copy pertinent info
6999 * back to the persistent model */
7000 vmstate_delete(&vms
);
7008 ast_module_user_remove(u
);
7013 static int vm_exec(struct ast_channel
*chan
, void *data
)
7016 struct ast_module_user
*u
;
7018 struct leave_vm_options leave_options
;
7019 struct ast_flags flags
= { 0 };
7020 static int deprecate_warning
= 0;
7021 char *opts
[OPT_ARG_ARRAY_SIZE
];
7022 AST_DECLARE_APP_ARGS(args
,
7027 u
= ast_module_user_add(chan
);
7029 memset(&leave_options
, 0, sizeof(leave_options
));
7031 if (chan
->_state
!= AST_STATE_UP
)
7034 if (!ast_strlen_zero(data
)) {
7035 tmp
= ast_strdupa(data
);
7036 AST_STANDARD_APP_ARGS(args
, tmp
);
7037 if (args
.argc
== 2) {
7038 if (ast_app_parse_options(vm_app_options
, &flags
, opts
, args
.argv1
)) {
7039 ast_module_user_remove(u
);
7042 ast_copy_flags(&leave_options
, &flags
, OPT_SILENT
| OPT_BUSY_GREETING
| OPT_UNAVAIL_GREETING
| OPT_PRIORITY_JUMP
);
7043 if (ast_test_flag(&flags
, OPT_RECORDGAIN
)) {
7046 if (sscanf(opts
[OPT_ARG_RECORDGAIN
], "%d", &gain
) != 1) {
7047 ast_log(LOG_WARNING
, "Invalid value '%s' provided for record gain option\n", opts
[OPT_ARG_RECORDGAIN
]);
7048 ast_module_user_remove(u
);
7051 leave_options
.record_gain
= (signed char) gain
;
7055 /* old style options parsing */
7057 char *orig_argv0
= args
.argv0
;
7058 while (*(args
.argv0
)) {
7059 if (*(args
.argv0
) == 's') {
7061 ast_set_flag(&leave_options
, OPT_SILENT
);
7062 } else if (*(args
.argv0
) == 'b') {
7064 ast_set_flag(&leave_options
, OPT_BUSY_GREETING
);
7065 } else if (*(args
.argv0
) == 'u') {
7067 ast_set_flag(&leave_options
, OPT_UNAVAIL_GREETING
);
7068 } else if (*(args
.argv0
) == 'j') {
7070 ast_set_flag(&leave_options
, OPT_PRIORITY_JUMP
);
7075 if (!deprecate_warning
&& old
) {
7076 deprecate_warning
= 1;
7077 ast_log(LOG_WARNING
, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0
);
7078 ast_log(LOG_WARNING
, "Please move all leading options to the second argument.\n");
7083 res
= ast_app_getdata(chan
, "vm-whichbox", tmp
, sizeof(tmp
) - 1, 0);
7085 ast_module_user_remove(u
);
7088 if (ast_strlen_zero(tmp
)) {
7089 ast_module_user_remove(u
);
7092 args
.argv0
= ast_strdupa(tmp
);
7095 res
= leave_voicemail(chan
, args
.argv0
, &leave_options
);
7097 if (res
== ERROR_LOCK_PATH
) {
7098 ast_log(LOG_ERROR
, "Could not leave voicemail. The path is already locked.\n");
7099 /*Send the call to n+101 priority, where n is the current priority*/
7100 if (ast_test_flag(&leave_options
, OPT_PRIORITY_JUMP
) || ast_opt_priority_jumping
)
7101 if (ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101))
7102 ast_log(LOG_WARNING
, "Extension %s, priority %d doesn't exist.\n", chan
->exten
, chan
->priority
+ 101);
7103 pbx_builtin_setvar_helper(chan
, "VMSTATUS", "FAILED");
7107 ast_module_user_remove(u
);
7112 static struct ast_vm_user
*find_or_create(char *context
, char *mbox
)
7114 struct ast_vm_user
*vmu
;
7115 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
7116 if (ast_test_flag((&globalflags
), VM_SEARCH
) && !strcasecmp(mbox
, vmu
->mailbox
))
7118 if (context
&& (!strcasecmp(context
, vmu
->context
)) && (!strcasecmp(mbox
, vmu
->mailbox
)))
7123 if ((vmu
= ast_calloc(1, sizeof(*vmu
)))) {
7124 ast_copy_string(vmu
->context
, context
, sizeof(vmu
->context
));
7125 ast_copy_string(vmu
->mailbox
, mbox
, sizeof(vmu
->mailbox
));
7126 AST_LIST_INSERT_TAIL(&users
, vmu
, list
);
7132 static int append_mailbox(char *context
, char *mbox
, char *data
)
7134 /* Assumes lock is already held */
7138 struct ast_vm_user
*vmu
;
7140 tmp
= ast_strdupa(data
);
7142 if ((vmu
= find_or_create(context
, mbox
))) {
7143 populate_defaults(vmu
);
7146 if ((s
= strsep(&stringp
, ",")))
7147 ast_copy_string(vmu
->password
, s
, sizeof(vmu
->password
));
7148 if (stringp
&& (s
= strsep(&stringp
, ",")))
7149 ast_copy_string(vmu
->fullname
, s
, sizeof(vmu
->fullname
));
7150 if (stringp
&& (s
= strsep(&stringp
, ",")))
7151 ast_copy_string(vmu
->email
, s
, sizeof(vmu
->email
));
7152 if (stringp
&& (s
= strsep(&stringp
, ",")))
7153 ast_copy_string(vmu
->pager
, s
, sizeof(vmu
->pager
));
7154 if (stringp
&& (s
= strsep(&stringp
, ",")))
7155 apply_options(vmu
, s
);
7160 static int vm_box_exists(struct ast_channel
*chan
, void *data
)
7162 struct ast_module_user
*u
;
7163 struct ast_vm_user svm
;
7164 char *context
, *box
;
7165 int priority_jump
= 0;
7166 AST_DECLARE_APP_ARGS(args
,
7168 AST_APP_ARG(options
);
7171 if (ast_strlen_zero(data
)) {
7172 ast_log(LOG_ERROR
, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
7176 u
= ast_module_user_add(chan
);
7178 box
= ast_strdupa(data
);
7180 AST_STANDARD_APP_ARGS(args
, box
);
7183 if (strchr(args
.options
, 'j'))
7187 if ((context
= strchr(args
.mbox
, '@'))) {
7192 if (find_user(&svm
, context
, args
.mbox
)) {
7193 pbx_builtin_setvar_helper(chan
, "VMBOXEXISTSSTATUS", "SUCCESS");
7194 if (priority_jump
|| ast_opt_priority_jumping
)
7195 if (ast_goto_if_exists(chan
, chan
->context
, chan
->exten
, chan
->priority
+ 101))
7196 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);
7198 pbx_builtin_setvar_helper(chan
, "VMBOXEXISTSSTATUS", "FAILED");
7199 ast_module_user_remove(u
);
7203 static int vmauthenticate(struct ast_channel
*chan
, void *data
)
7205 struct ast_module_user
*u
;
7206 char *s
= data
, *user
=NULL
, *context
=NULL
, mailbox
[AST_MAX_EXTENSION
] = "";
7207 struct ast_vm_user vmus
;
7208 char *options
= NULL
;
7209 int silent
= 0, skipuser
= 0;
7212 u
= ast_module_user_add(chan
);
7216 user
= strsep(&s
, "|");
7217 options
= strsep(&s
, "|");
7220 user
= strsep(&s
, "@");
7221 context
= strsep(&s
, "");
7222 if (!ast_strlen_zero(user
))
7224 ast_copy_string(mailbox
, user
, sizeof(mailbox
));
7229 silent
= (strchr(options
, 's')) != NULL
;
7232 if (!vm_authenticate(chan
, mailbox
, sizeof(mailbox
), &vmus
, context
, NULL
, skipuser
, 3, silent
)) {
7233 pbx_builtin_setvar_helper(chan
, "AUTH_MAILBOX", mailbox
);
7234 pbx_builtin_setvar_helper(chan
, "AUTH_CONTEXT", vmus
.context
);
7235 ast_play_and_wait(chan
, "auth-thankyou");
7239 ast_module_user_remove(u
);
7243 static char voicemail_show_users_help
[] =
7244 "Usage: voicemail show users [for <context>]\n"
7245 " Lists all mailboxes currently set up\n";
7247 static char voicemail_show_zones_help
[] =
7248 "Usage: voicemail show zones\n"
7249 " Lists zone message formats\n";
7251 static int handle_voicemail_show_users(int fd
, int argc
, char *argv
[])
7253 struct ast_vm_user
*vmu
;
7254 char *output_format
= "%-10s %-5s %-25s %-10s %6s\n";
7256 if ((argc
< 3) || (argc
> 5) || (argc
== 4)) return RESULT_SHOWUSAGE
;
7257 else if ((argc
== 5) && strcmp(argv
[3],"for")) return RESULT_SHOWUSAGE
;
7259 AST_LIST_LOCK(&users
);
7260 if (!AST_LIST_EMPTY(&users
)) {
7262 ast_cli(fd
, output_format
, "Context", "Mbox", "User", "Zone", "NewMsg");
7265 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
7266 if (!strcmp(argv
[4],vmu
->context
))
7270 ast_cli(fd
, output_format
, "Context", "Mbox", "User", "Zone", "NewMsg");
7272 ast_cli(fd
, "No such voicemail context \"%s\"\n", argv
[4]);
7273 AST_LIST_UNLOCK(&users
);
7274 return RESULT_FAILURE
;
7277 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
7278 int newmsgs
= 0, oldmsgs
= 0;
7279 char count
[12], tmp
[256] = "";
7281 if ((argc
== 3) || ((argc
== 5) && !strcmp(argv
[4],vmu
->context
))) {
7282 snprintf(tmp
, sizeof(tmp
), "%s@%s", vmu
->mailbox
, ast_strlen_zero(vmu
->context
) ? "default" : vmu
->context
);
7283 inboxcount(tmp
, &newmsgs
, &oldmsgs
);
7284 snprintf(count
,sizeof(count
),"%d",newmsgs
);
7285 ast_cli(fd
, output_format
, vmu
->context
, vmu
->mailbox
, vmu
->fullname
, vmu
->zonetag
, count
);
7289 ast_cli(fd
, "There are no voicemail users currently defined\n");
7290 AST_LIST_UNLOCK(&users
);
7291 return RESULT_FAILURE
;
7293 AST_LIST_UNLOCK(&users
);
7294 return RESULT_SUCCESS
;
7297 static int handle_voicemail_show_zones(int fd
, int argc
, char *argv
[])
7299 struct vm_zone
*zone
;
7300 char *output_format
= "%-15s %-20s %-45s\n";
7301 int res
= RESULT_SUCCESS
;
7304 return RESULT_SHOWUSAGE
;
7306 AST_LIST_LOCK(&zones
);
7307 if (!AST_LIST_EMPTY(&zones
)) {
7308 ast_cli(fd
, output_format
, "Zone", "Timezone", "Message Format");
7309 AST_LIST_TRAVERSE(&zones
, zone
, list
) {
7310 ast_cli(fd
, output_format
, zone
->name
, zone
->timezone
, zone
->msg_format
);
7313 ast_cli(fd
, "There are no voicemail zones currently defined\n");
7314 res
= RESULT_FAILURE
;
7316 AST_LIST_UNLOCK(&zones
);
7321 static char *complete_voicemail_show_users(const char *line
, const char *word
, int pos
, int state
)
7325 struct ast_vm_user
*vmu
;
7326 const char *context
= "";
7328 /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
7332 return (state
== 0) ? ast_strdup("for") : NULL
;
7333 wordlen
= strlen(word
);
7334 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
7335 if (!strncasecmp(word
, vmu
->context
, wordlen
)) {
7336 if (context
&& strcmp(context
, vmu
->context
) && ++which
> state
)
7337 return ast_strdup(vmu
->context
);
7338 /* ignore repeated contexts ? */
7339 context
= vmu
->context
;
7345 static struct ast_cli_entry cli_show_voicemail_users_deprecated
= {
7346 { "show", "voicemail", "users", NULL
},
7347 handle_voicemail_show_users
, NULL
,
7348 NULL
, complete_voicemail_show_users
};
7350 static struct ast_cli_entry cli_show_voicemail_zones_deprecated
= {
7351 { "show", "voicemail", "zones", NULL
},
7352 handle_voicemail_show_zones
, NULL
,
7355 static struct ast_cli_entry cli_voicemail
[] = {
7356 { { "voicemail", "show", "users", NULL
},
7357 handle_voicemail_show_users
, "List defined voicemail boxes",
7358 voicemail_show_users_help
, complete_voicemail_show_users
, &cli_show_voicemail_users_deprecated
},
7360 { { "voicemail", "show", "zones", NULL
},
7361 handle_voicemail_show_zones
, "List zone message formats",
7362 voicemail_show_zones_help
, NULL
, &cli_show_voicemail_zones_deprecated
},
7365 static int load_config(void)
7367 struct ast_vm_user
*cur
;
7368 struct vm_zone
*zcur
;
7369 struct ast_config
*cfg
, *ucfg
;
7371 struct ast_variable
*var
;
7372 const char *notifystr
= NULL
;
7373 const char *smdistr
= NULL
;
7374 const char *astattach
;
7375 const char *astsearch
;
7376 const char *astsaycid
;
7377 const char *send_voicemail
;
7379 const char *imap_server
;
7380 const char *imap_port
;
7381 const char *imap_flags
;
7382 const char *imap_folder
;
7383 const char *auth_user
;
7384 const char *auth_password
;
7385 const char *expunge_on_hangup
;
7387 const char *astcallop
;
7388 const char *astreview
;
7389 const char *asttempgreetwarn
;
7390 const char *astskipcmd
;
7391 const char *asthearenv
;
7392 const char *astsaydurationinfo
;
7393 const char *astsaydurationminfo
;
7394 const char *silencestr
;
7395 const char *maxmsgstr
;
7396 const char *astdirfwd
;
7397 const char *thresholdstr
;
7399 const char *astemail
;
7400 const char *ucontext
;
7401 const char *astmailcmd
= SENDMAIL
;
7402 const char *astforcename
;
7403 const char *astforcegreet
;
7406 const char *dialoutcxt
= NULL
;
7407 const char *callbackcxt
= NULL
;
7408 const char *exitcxt
= NULL
;
7410 const char *emaildateformatstr
;
7411 const char *volgainstr
;
7415 cfg
= ast_config_load(VOICEMAIL_CONFIG
);
7417 AST_LIST_LOCK(&users
);
7418 while ((cur
= AST_LIST_REMOVE_HEAD(&users
, list
))) {
7419 ast_set_flag(cur
, VM_ALLOCED
);
7423 AST_LIST_LOCK(&zones
);
7424 while ((zcur
= AST_LIST_REMOVE_HEAD(&zones
, list
)))
7426 AST_LIST_UNLOCK(&zones
);
7428 memset(ext_pass_cmd
, 0, sizeof(ext_pass_cmd
));
7431 /* General settings */
7433 if (!(ucontext
= ast_variable_retrieve(cfg
, "general", "userscontext")))
7434 ucontext
= "default";
7435 ast_copy_string(userscontext
, ucontext
, sizeof(userscontext
));
7436 /* Attach voice message to mail message ? */
7437 if (!(astattach
= ast_variable_retrieve(cfg
, "general", "attach")))
7439 ast_set2_flag((&globalflags
), ast_true(astattach
), VM_ATTACH
);
7441 if (!(astsearch
= ast_variable_retrieve(cfg
, "general", "searchcontexts")))
7443 ast_set2_flag((&globalflags
), ast_true(astsearch
), VM_SEARCH
);
7446 if ((volgainstr
= ast_variable_retrieve(cfg
, "general", "volgain")))
7447 sscanf(volgainstr
, "%lf", &volgain
);
7450 strcpy(odbc_database
, "asterisk");
7451 if ((thresholdstr
= ast_variable_retrieve(cfg
, "general", "odbcstorage"))) {
7452 ast_copy_string(odbc_database
, thresholdstr
, sizeof(odbc_database
));
7454 strcpy(odbc_table
, "voicemessages");
7455 if ((thresholdstr
= ast_variable_retrieve(cfg
, "general", "odbctable"))) {
7456 ast_copy_string(odbc_table
, thresholdstr
, sizeof(odbc_table
));
7460 strcpy(mailcmd
, SENDMAIL
);
7461 if ((astmailcmd
= ast_variable_retrieve(cfg
, "general", "mailcmd")))
7462 ast_copy_string(mailcmd
, astmailcmd
, sizeof(mailcmd
)); /* User setting */
7465 if ((silencestr
= ast_variable_retrieve(cfg
, "general", "maxsilence"))) {
7466 maxsilence
= atoi(silencestr
);
7471 if (!(maxmsgstr
= ast_variable_retrieve(cfg
, "general", "maxmsg"))) {
7474 maxmsg
= atoi(maxmsgstr
);
7476 ast_log(LOG_WARNING
, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr
, MAXMSG
);
7478 } else if (maxmsg
> MAXMSGLIMIT
) {
7479 ast_log(LOG_WARNING
, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT
, maxmsgstr
);
7480 maxmsg
= MAXMSGLIMIT
;
7484 /* Load date format config for voicemail mail */
7485 if ((emaildateformatstr
= ast_variable_retrieve(cfg
, "general", "emaildateformat"))) {
7486 ast_copy_string(emaildateformat
, emaildateformatstr
, sizeof(emaildateformat
));
7489 /* External password changing command */
7490 if ((extpc
= ast_variable_retrieve(cfg
, "general", "externpass"))) {
7491 ast_copy_string(ext_pass_cmd
,extpc
,sizeof(ext_pass_cmd
));
7494 /* IMAP server address */
7495 if ((imap_server
= ast_variable_retrieve(cfg
, "general", "imapserver"))) {
7496 ast_copy_string(imapserver
, imap_server
, sizeof(imapserver
));
7498 ast_copy_string(imapserver
,"localhost", sizeof(imapserver
));
7500 /* IMAP server port */
7501 if ((imap_port
= ast_variable_retrieve(cfg
, "general", "imapport"))) {
7502 ast_copy_string(imapport
, imap_port
, sizeof(imapport
));
7504 ast_copy_string(imapport
,"143", sizeof(imapport
));
7506 /* IMAP server flags */
7507 if ((imap_flags
= ast_variable_retrieve(cfg
, "general", "imapflags"))) {
7508 ast_copy_string(imapflags
, imap_flags
, sizeof(imapflags
));
7510 /* IMAP server master username */
7511 if ((auth_user
= ast_variable_retrieve(cfg
, "general", "authuser"))) {
7512 ast_copy_string(authuser
, auth_user
, sizeof(authuser
));
7514 /* IMAP server master password */
7515 if ((auth_password
= ast_variable_retrieve(cfg
, "general", "authpassword"))) {
7516 ast_copy_string(authpassword
, auth_password
, sizeof(authpassword
));
7518 /* Expunge on exit */
7519 if ((expunge_on_hangup
= ast_variable_retrieve(cfg
, "general", "expungeonhangup"))) {
7520 if (ast_false(expunge_on_hangup
))
7521 expungeonhangup
= 0;
7523 expungeonhangup
= 1;
7525 expungeonhangup
= 1;
7527 /* IMAP voicemail folder */
7528 if ((imap_folder
= ast_variable_retrieve(cfg
, "general", "imapfolder"))) {
7529 ast_copy_string(imapfolder
, imap_folder
, sizeof(imapfolder
));
7531 ast_copy_string(imapfolder
,"INBOX", sizeof(imapfolder
));
7534 /* External voicemail notify application */
7536 if ((notifystr
= ast_variable_retrieve(cfg
, "general", "externnotify"))) {
7537 ast_copy_string(externnotify
, notifystr
, sizeof(externnotify
));
7538 if (option_debug
> 2)
7539 ast_log(LOG_DEBUG
, "found externnotify: %s\n", externnotify
);
7540 if (!strcasecmp(externnotify
, "smdi")) {
7542 ast_log(LOG_DEBUG
, "Using SMDI for external voicemail notification\n");
7543 if ((smdistr
= ast_variable_retrieve(cfg
, "general", "smdiport"))) {
7544 smdi_iface
= ast_smdi_interface_find(smdistr
);
7547 ast_log(LOG_DEBUG
, "No SMDI interface set, trying default (/dev/ttyS0)\n");
7548 smdi_iface
= ast_smdi_interface_find("/dev/ttyS0");
7552 ast_log(LOG_ERROR
, "No valid SMDI interface specfied, disabling external voicemail notification\n");
7553 externnotify
[0] = '\0';
7557 externnotify
[0] = '\0';
7560 /* Silence treshold */
7561 silencethreshold
= 256;
7562 if ((thresholdstr
= ast_variable_retrieve(cfg
, "general", "silencethreshold")))
7563 silencethreshold
= atoi(thresholdstr
);
7565 if (!(astemail
= ast_variable_retrieve(cfg
, "general", "serveremail")))
7566 astemail
= ASTERISK_USERNAME
;
7567 ast_copy_string(serveremail
, astemail
, sizeof(serveremail
));
7570 if ((s
= ast_variable_retrieve(cfg
, "general", "maxmessage"))) {
7571 if (sscanf(s
, "%d", &x
) == 1) {
7574 ast_log(LOG_WARNING
, "Invalid max message time length\n");
7579 if ((s
= ast_variable_retrieve(cfg
, "general", "minmessage"))) {
7580 if (sscanf(s
, "%d", &x
) == 1) {
7582 if (maxsilence
<= vmminmessage
)
7583 ast_log(LOG_WARNING
, "maxsilence should be less than minmessage or you may get empty messages\n");
7585 ast_log(LOG_WARNING
, "Invalid min message time length\n");
7588 fmt
= ast_variable_retrieve(cfg
, "general", "format");
7591 ast_copy_string(vmfmts
, fmt
, sizeof(vmfmts
));
7594 if ((s
= ast_variable_retrieve(cfg
, "general", "maxgreet"))) {
7595 if (sscanf(s
, "%d", &x
) == 1) {
7598 ast_log(LOG_WARNING
, "Invalid max message greeting length\n");
7602 if ((s
= ast_variable_retrieve(cfg
, "general", "skipms"))) {
7603 if (sscanf(s
, "%d", &x
) == 1) {
7606 ast_log(LOG_WARNING
, "Invalid skipms value\n");
7611 if ((s
= ast_variable_retrieve(cfg
, "general", "maxlogins"))) {
7612 if (sscanf(s
, "%d", &x
) == 1) {
7615 ast_log(LOG_WARNING
, "Invalid max failed login attempts\n");
7619 /* Force new user to record name ? */
7620 if (!(astforcename
= ast_variable_retrieve(cfg
, "general", "forcename")))
7621 astforcename
= "no";
7622 ast_set2_flag((&globalflags
), ast_true(astforcename
), VM_FORCENAME
);
7624 /* Force new user to record greetings ? */
7625 if (!(astforcegreet
= ast_variable_retrieve(cfg
, "general", "forcegreetings")))
7626 astforcegreet
= "no";
7627 ast_set2_flag((&globalflags
), ast_true(astforcegreet
), VM_FORCEGREET
);
7629 if ((s
= ast_variable_retrieve(cfg
, "general", "cidinternalcontexts"))){
7630 if (option_debug
> 2)
7631 ast_log(LOG_DEBUG
,"VM_CID Internal context string: %s\n",s
);
7632 stringp
= ast_strdupa(s
);
7633 for (x
= 0 ; x
< MAX_NUM_CID_CONTEXTS
; x
++){
7634 if (!ast_strlen_zero(stringp
)) {
7635 q
= strsep(&stringp
,",");
7636 while ((*q
== ' ')||(*q
== '\t')) /* Eat white space between contexts */
7638 ast_copy_string(cidinternalcontexts
[x
], q
, sizeof(cidinternalcontexts
[x
]));
7639 if (option_debug
> 2)
7640 ast_log(LOG_DEBUG
,"VM_CID Internal context %d: %s\n", x
, cidinternalcontexts
[x
]);
7642 cidinternalcontexts
[x
][0] = '\0';
7646 if (!(astreview
= ast_variable_retrieve(cfg
, "general", "review"))){
7648 ast_log(LOG_DEBUG
,"VM Review Option disabled globally\n");
7651 ast_set2_flag((&globalflags
), ast_true(astreview
), VM_REVIEW
);
7653 /*Temperary greeting reminder */
7654 if (!(asttempgreetwarn
= ast_variable_retrieve(cfg
, "general", "tempgreetwarn"))) {
7656 ast_log(LOG_DEBUG
, "VM Temperary Greeting Reminder Option disabled globally\n");
7657 asttempgreetwarn
= "no";
7660 ast_log(LOG_DEBUG
, "VM Temperary Greeting Reminder Option enabled globally\n");
7662 ast_set2_flag((&globalflags
), ast_true(asttempgreetwarn
), VM_TEMPGREETWARN
);
7664 if (!(astcallop
= ast_variable_retrieve(cfg
, "general", "operator"))){
7666 ast_log(LOG_DEBUG
,"VM Operator break disabled globally\n");
7669 ast_set2_flag((&globalflags
), ast_true(astcallop
), VM_OPERATOR
);
7671 if (!(astsaycid
= ast_variable_retrieve(cfg
, "general", "saycid"))) {
7673 ast_log(LOG_DEBUG
,"VM CID Info before msg disabled globally\n");
7676 ast_set2_flag((&globalflags
), ast_true(astsaycid
), VM_SAYCID
);
7678 if (!(send_voicemail
= ast_variable_retrieve(cfg
,"general", "sendvoicemail"))){
7680 ast_log(LOG_DEBUG
,"Send Voicemail msg disabled globally\n");
7681 send_voicemail
= "no";
7683 ast_set2_flag((&globalflags
), ast_true(send_voicemail
), VM_SVMAIL
);
7685 if (!(asthearenv
= ast_variable_retrieve(cfg
, "general", "envelope"))) {
7687 ast_log(LOG_DEBUG
,"ENVELOPE before msg enabled globally\n");
7690 ast_set2_flag((&globalflags
), ast_true(asthearenv
), VM_ENVELOPE
);
7692 if (!(astsaydurationinfo
= ast_variable_retrieve(cfg
, "general", "sayduration"))) {
7694 ast_log(LOG_DEBUG
,"Duration info before msg enabled globally\n");
7695 astsaydurationinfo
= "yes";
7697 ast_set2_flag((&globalflags
), ast_true(astsaydurationinfo
), VM_SAYDURATION
);
7699 saydurationminfo
= 2;
7700 if ((astsaydurationminfo
= ast_variable_retrieve(cfg
, "general", "saydurationm"))) {
7701 if (sscanf(astsaydurationminfo
, "%d", &x
) == 1) {
7702 saydurationminfo
= x
;
7704 ast_log(LOG_WARNING
, "Invalid min duration for say duration\n");
7708 if (!(astskipcmd
= ast_variable_retrieve(cfg
, "general", "nextaftercmd"))) {
7710 ast_log(LOG_DEBUG
,"We are not going to skip to the next msg after save/delete\n");
7713 ast_set2_flag((&globalflags
), ast_true(astskipcmd
), VM_SKIPAFTERCMD
);
7715 if ((dialoutcxt
= ast_variable_retrieve(cfg
, "general", "dialout"))) {
7716 ast_copy_string(dialcontext
, dialoutcxt
, sizeof(dialcontext
));
7718 ast_log(LOG_DEBUG
, "found dialout context: %s\n", dialcontext
);
7720 dialcontext
[0] = '\0';
7723 if ((callbackcxt
= ast_variable_retrieve(cfg
, "general", "callback"))) {
7724 ast_copy_string(callcontext
, callbackcxt
, sizeof(callcontext
));
7726 ast_log(LOG_DEBUG
, "found callback context: %s\n", callcontext
);
7728 callcontext
[0] = '\0';
7731 if ((exitcxt
= ast_variable_retrieve(cfg
, "general", "exitcontext"))) {
7732 ast_copy_string(exitcontext
, exitcxt
, sizeof(exitcontext
));
7734 ast_log(LOG_DEBUG
, "found operator context: %s\n", exitcontext
);
7736 exitcontext
[0] = '\0';
7739 if (!(astdirfwd
= ast_variable_retrieve(cfg
, "general", "usedirectory")))
7741 ast_set2_flag((&globalflags
), ast_true(astdirfwd
), VM_DIRECFORWARD
);
7742 if ((ucfg
= ast_config_load("users.conf"))) {
7743 for (cat
= ast_category_browse(ucfg
, NULL
); cat
; cat
= ast_category_browse(ucfg
, cat
)) {
7744 if (!ast_true(ast_config_option(ucfg
, cat
, "hasvoicemail")))
7746 if ((cur
= find_or_create(userscontext
, cat
))) {
7747 populate_defaults(cur
);
7748 apply_options_full(cur
, ast_variable_browse(ucfg
, cat
));
7749 ast_copy_string(cur
->context
, userscontext
, sizeof(cur
->context
));
7752 ast_config_destroy(ucfg
);
7754 cat
= ast_category_browse(cfg
, NULL
);
7756 if (strcasecmp(cat
, "general")) {
7757 var
= ast_variable_browse(cfg
, cat
);
7758 if (strcasecmp(cat
, "zonemessages")) {
7759 /* Process mailboxes in this context */
7761 append_mailbox(cat
, var
->name
, var
->value
);
7765 /* Timezones in this context */
7768 if ((z
= ast_malloc(sizeof(*z
)))) {
7769 char *msg_format
, *timezone
;
7770 msg_format
= ast_strdupa(var
->value
);
7771 timezone
= strsep(&msg_format
, "|");
7773 ast_copy_string(z
->name
, var
->name
, sizeof(z
->name
));
7774 ast_copy_string(z
->timezone
, timezone
, sizeof(z
->timezone
));
7775 ast_copy_string(z
->msg_format
, msg_format
, sizeof(z
->msg_format
));
7776 AST_LIST_LOCK(&zones
);
7777 AST_LIST_INSERT_HEAD(&zones
, z
, list
);
7778 AST_LIST_UNLOCK(&zones
);
7780 ast_log(LOG_WARNING
, "Invalid timezone definition at line %d\n", var
->lineno
);
7785 AST_LIST_UNLOCK(&users
);
7786 ast_config_destroy(cfg
);
7793 cat
= ast_category_browse(cfg
, cat
);
7795 memset(fromstring
,0,sizeof(fromstring
));
7796 memset(pagerfromstring
,0,sizeof(pagerfromstring
));
7797 memset(emailtitle
,0,sizeof(emailtitle
));
7798 strcpy(charset
, "ISO-8859-1");
7805 emailsubject
= NULL
;
7813 pagersubject
= NULL
;
7815 if ((s
= ast_variable_retrieve(cfg
, "general", "pbxskip")))
7816 ast_set2_flag((&globalflags
), ast_true(s
), VM_PBXSKIP
);
7817 if ((s
= ast_variable_retrieve(cfg
, "general", "fromstring")))
7818 ast_copy_string(fromstring
,s
,sizeof(fromstring
));
7819 if ((s
= ast_variable_retrieve(cfg
, "general", "pagerfromstring")))
7820 ast_copy_string(pagerfromstring
,s
,sizeof(pagerfromstring
));
7821 if ((s
= ast_variable_retrieve(cfg
, "general", "charset")))
7822 ast_copy_string(charset
,s
,sizeof(charset
));
7823 if ((s
= ast_variable_retrieve(cfg
, "general", "adsifdn"))) {
7824 sscanf(s
, "%2x%2x%2x%2x", &tmpadsi
[0], &tmpadsi
[1], &tmpadsi
[2], &tmpadsi
[3]);
7825 for (x
= 0; x
< 4; x
++) {
7826 memcpy(&adsifdn
[x
], &tmpadsi
[x
], 1);
7829 if ((s
= ast_variable_retrieve(cfg
, "general", "adsisec"))) {
7830 sscanf(s
, "%2x%2x%2x%2x", &tmpadsi
[0], &tmpadsi
[1], &tmpadsi
[2], &tmpadsi
[3]);
7831 for (x
= 0; x
< 4; x
++) {
7832 memcpy(&adsisec
[x
], &tmpadsi
[x
], 1);
7835 if ((s
= ast_variable_retrieve(cfg
, "general", "adsiver")))
7839 if ((s
= ast_variable_retrieve(cfg
, "general", "emailtitle"))) {
7840 ast_log(LOG_NOTICE
, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
7841 ast_copy_string(emailtitle
,s
,sizeof(emailtitle
));
7843 if ((s
= ast_variable_retrieve(cfg
, "general", "emailsubject")))
7844 emailsubject
= ast_strdup(s
);
7845 if ((s
= ast_variable_retrieve(cfg
, "general", "emailbody"))) {
7846 char *tmpread
, *tmpwrite
;
7847 emailbody
= ast_strdup(s
);
7849 /* substitute strings \t and \n into the appropriate characters */
7850 tmpread
= tmpwrite
= emailbody
;
7851 while ((tmpwrite
= strchr(tmpread
,'\\'))) {
7852 switch (tmpwrite
[1]) {
7854 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7858 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7862 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7866 ast_log(LOG_NOTICE
, "Substitution routine does not support this character: %c\n", tmpwrite
[1]);
7868 tmpread
= tmpwrite
+ 1;
7871 if ((s
= ast_variable_retrieve(cfg
, "general", "pagersubject")))
7872 pagersubject
= ast_strdup(s
);
7873 if ((s
= ast_variable_retrieve(cfg
, "general", "pagerbody"))) {
7874 char *tmpread
, *tmpwrite
;
7875 pagerbody
= ast_strdup(s
);
7877 /* substitute strings \t and \n into the appropriate characters */
7878 tmpread
= tmpwrite
= pagerbody
;
7879 while ((tmpwrite
= strchr(tmpread
, '\\'))) {
7880 switch (tmpwrite
[1]) {
7882 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7886 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7890 memmove(tmpwrite
+ 1, tmpwrite
+ 2, strlen(tmpwrite
+ 2) + 1);
7894 ast_log(LOG_NOTICE
, "Substitution routine does not support this character: %c\n", tmpwrite
[1]);
7896 tmpread
= tmpwrite
+ 1;
7899 AST_LIST_UNLOCK(&users
);
7900 ast_config_destroy(cfg
);
7903 AST_LIST_UNLOCK(&users
);
7904 ast_log(LOG_WARNING
, "Failed to load configuration file.\n");
7909 static int reload(void)
7911 return(load_config());
7914 static int unload_module(void)
7918 res
= ast_unregister_application(app
);
7919 res
|= ast_unregister_application(app2
);
7920 res
|= ast_unregister_application(app3
);
7921 res
|= ast_unregister_application(app4
);
7922 ast_cli_unregister_multiple(cli_voicemail
, sizeof(cli_voicemail
) / sizeof(struct ast_cli_entry
));
7923 ast_uninstall_vm_functions();
7925 ast_module_user_hangup_all();
7930 static int load_module(void)
7933 char *adsi_loaded
= ast_module_helper("", "res_adsi.so", 0, 0, 0, 0);
7936 /* If embedded, res_adsi may be known as "res_adsi" not "res_adsi.so" */
7937 adsi_loaded
= ast_module_helper("", "res_adsi", 0, 0, 0, 0);
7938 ast_free(adsi_loaded
);
7940 ast_log(LOG_ERROR
, "app_voicemail.so depends upon res_adsi.so\n");
7941 return AST_MODULE_LOAD_DECLINE
;
7945 my_umask
= umask(0);
7947 res
= ast_register_application(app
, vm_exec
, synopsis_vm
, descrip_vm
);
7948 res
|= ast_register_application(app2
, vm_execmain
, synopsis_vmain
, descrip_vmain
);
7949 res
|= ast_register_application(app3
, vm_box_exists
, synopsis_vm_box_exists
, descrip_vm_box_exists
);
7950 res
|= ast_register_application(app4
, vmauthenticate
, synopsis_vmauthenticate
, descrip_vmauthenticate
);
7954 if ((res
=load_config())) {
7958 ast_cli_register_multiple(cli_voicemail
, sizeof(cli_voicemail
) / sizeof(struct ast_cli_entry
));
7960 /* compute the location of the voicemail spool directory */
7961 snprintf(VM_SPOOL_DIR
, sizeof(VM_SPOOL_DIR
), "%s/voicemail/", ast_config_AST_SPOOL_DIR
);
7963 ast_install_vm_functions(has_voicemail
, inboxcount
, messagecount
);
7968 static int dialout(struct ast_channel
*chan
, struct ast_vm_user
*vmu
, char *num
, char *outgoing_context
)
7971 char destination
[80] = "";
7975 if (option_verbose
> 2)
7976 ast_verbose( VERBOSE_PREFIX_3
"Destination number will be entered manually\n");
7977 while (retries
< 3 && cmd
!= 't') {
7978 destination
[1] = '\0';
7979 destination
[0] = cmd
= ast_play_and_wait(chan
,"vm-enter-num-to-call");
7981 destination
[0] = cmd
= ast_play_and_wait(chan
, "vm-then-pound");
7983 destination
[0] = cmd
= ast_play_and_wait(chan
, "vm-star-cancel");
7985 cmd
= ast_waitfordigit(chan
, 6000);
7987 destination
[0] = cmd
;
7996 if (option_verbose
> 2)
7997 ast_verbose( VERBOSE_PREFIX_3
"User hit '*' to cancel outgoing call\n");
8000 if ((cmd
= ast_readstring(chan
,destination
+ strlen(destination
),sizeof(destination
)-1,6000,10000,"#")) < 0)
8011 if (option_verbose
> 2)
8012 ast_verbose( VERBOSE_PREFIX_3
"Destination number is CID number '%s'\n", num
);
8013 ast_copy_string(destination
, num
, sizeof(destination
));
8016 if (!ast_strlen_zero(destination
)) {
8017 if (destination
[strlen(destination
) -1 ] == '*')
8019 if (option_verbose
> 2)
8020 ast_verbose( VERBOSE_PREFIX_3
"Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination
, outgoing_context
, chan
->context
);
8021 ast_copy_string(chan
->exten
, destination
, sizeof(chan
->exten
));
8022 ast_copy_string(chan
->context
, outgoing_context
, sizeof(chan
->context
));
8029 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
)
8033 char origtimeS
[256],cidS
[256],contextS
[256];
8034 char *header_content
,*temp
;
8036 char filename
[PATH_MAX
];
8037 struct ast_config
*msg_cfg
= NULL
;
8038 const char *origtime
, *context
;
8039 char *cid
, *name
, *num
;
8045 /* get the message info!! */
8046 if (option_debug
> 2)
8047 ast_log (LOG_DEBUG
,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n",vms
->curmsg
, vms
->msgArray
[vms
->curmsg
]);
8048 if (vms
->msgArray
[vms
->curmsg
] == 0) {
8049 ast_log (LOG_WARNING
,"Trying to access unknown message\n");
8053 /* This will only work for new messages... */
8054 header_content
= mail_fetchheader (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
]);
8055 /* empty string means no valid header */
8056 if (ast_strlen_zero(header_content
)) {
8057 ast_log (LOG_ERROR
,"Could not fetch header for message number %ld\n",vms
->msgArray
[vms
->curmsg
]);
8061 /* Get info from headers!! */
8062 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Caller-ID-Num:");
8065 ast_copy_string(cidS
,temp
, sizeof(cidS
));
8070 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Context:");
8073 ast_copy_string(contextS
,temp
, sizeof(contextS
));
8077 context
= &contextS
[0];
8078 temp
= get_header_by_tag(header_content
, "X-Asterisk-VM-Orig-time:");
8081 ast_copy_string(origtimeS
,temp
, sizeof(origtimeS
));
8083 origtimeS
[0] = '\0';
8085 origtime
= &origtimeS
[0];
8087 ast_copy_string(filename
, "IMAP_STORAGE", sizeof(filename
));
8089 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, msg
);
8091 /* Retrieve info from VM attribute file */
8093 make_file(vms
->fn2
, sizeof(vms
->fn2
), vms
->curdir
, vms
->curmsg
);
8094 snprintf(filename
,sizeof(filename
), "%s.txt", vms
->fn2
);
8095 RETRIEVE(vms
->curdir
, vms
->curmsg
);
8096 msg_cfg
= ast_config_load(filename
);
8097 DISPOSE(vms
->curdir
, vms
->curmsg
);
8099 ast_log(LOG_WARNING
, "No message attribute file?!! (%s)\n", filename
);
8103 if (!(origtime
= ast_variable_retrieve(msg_cfg
, "message", "origtime"))) {
8104 ast_config_destroy(msg_cfg
);
8108 cid
= ast_strdupa(ast_variable_retrieve(msg_cfg
, "message", "callerid"));
8110 context
= ast_variable_retrieve(msg_cfg
, "message", "context");
8111 if (!strncasecmp("macro",context
,5)) /* Macro names in contexts are useless for our needs */
8112 context
= ast_variable_retrieve(msg_cfg
, "message","macrocontext");
8117 res
= play_message_datetime(chan
, vmu
, origtime
, filename
);
8119 res
= play_message_callerid(chan
, vms
, cid
, context
, 0);
8124 case 2: /* Call back */
8126 if (ast_strlen_zero(cid
))
8129 ast_callerid_parse(cid
, &name
, &num
);
8130 while ((res
> -1) && (res
!= 't')) {
8134 /* Dial the CID number */
8135 res
= dialout(chan
, vmu
, num
, vmu
->callback
);
8137 ast_config_destroy(msg_cfg
);
8146 /* Want to enter a different number, can only do this if there's a dialout context for this user */
8147 if (!ast_strlen_zero(vmu
->dialout
)) {
8148 res
= dialout(chan
, vmu
, NULL
, vmu
->dialout
);
8150 ast_config_destroy(msg_cfg
);
8154 if (option_verbose
> 2)
8155 ast_verbose( VERBOSE_PREFIX_3
"Caller can not specify callback number - no dialout context available\n");
8156 res
= ast_play_and_wait(chan
, "vm-sorry");
8158 ast_config_destroy(msg_cfg
);
8172 res
= ast_play_and_wait(chan
, "vm-sorry");
8177 if (option_verbose
> 2)
8178 ast_verbose( VERBOSE_PREFIX_3
"Confirm CID number '%s' is number to use for callback\n", num
);
8179 res
= ast_play_and_wait(chan
, "vm-num-i-have");
8181 res
= play_message_callerid(chan
, vms
, num
, vmu
->context
, 1);
8183 res
= ast_play_and_wait(chan
, "vm-tocallnum");
8184 /* Only prompt for a caller-specified number if there is a dialout context specified */
8185 if (!ast_strlen_zero(vmu
->dialout
)) {
8187 res
= ast_play_and_wait(chan
, "vm-calldiffnum");
8190 res
= ast_play_and_wait(chan
, "vm-nonumber");
8191 if (!ast_strlen_zero(vmu
->dialout
)) {
8193 res
= ast_play_and_wait(chan
, "vm-toenternumber");
8197 res
= ast_play_and_wait(chan
, "vm-star-cancel");
8199 res
= ast_waitfordigit(chan
, 6000);
8210 else if (res
== '*')
8216 /* Send reply directly to sender */
8217 if (ast_strlen_zero(cid
))
8220 ast_callerid_parse(cid
, &name
, &num
);
8222 if (option_verbose
> 2)
8223 ast_verbose(VERBOSE_PREFIX_3
"No CID number available, no reply sent\n");
8225 res
= ast_play_and_wait(chan
, "vm-nonumber");
8226 ast_config_destroy(msg_cfg
);
8229 struct ast_vm_user vmu2
;
8230 if (find_user(&vmu2
, vmu
->context
, num
)) {
8231 struct leave_vm_options leave_options
;
8232 char mailbox
[AST_MAX_EXTENSION
* 2 + 2];
8233 snprintf(mailbox
, sizeof(mailbox
), "%s@%s", num
, vmu
->context
);
8235 if (option_verbose
> 2)
8236 ast_verbose(VERBOSE_PREFIX_3
"Leaving voicemail for '%s' in context '%s'\n", num
, vmu
->context
);
8238 memset(&leave_options
, 0, sizeof(leave_options
));
8239 leave_options
.record_gain
= record_gain
;
8240 res
= leave_voicemail(chan
, mailbox
, &leave_options
);
8243 ast_config_destroy(msg_cfg
);
8246 /* Sender has no mailbox, can't reply */
8247 if (option_verbose
> 2)
8248 ast_verbose( VERBOSE_PREFIX_3
"No mailbox number '%s' in context '%s', no reply sent\n", num
, vmu
->context
);
8249 ast_play_and_wait(chan
, "vm-nobox");
8251 ast_config_destroy(msg_cfg
);
8260 #ifndef IMAP_STORAGE
8261 ast_config_destroy(msg_cfg
);
8264 make_file(vms
->fn
, sizeof(vms
->fn
), vms
->curdir
, msg
);
8265 vms
->heard
[msg
] = 1;
8266 res
= wait_file(chan
, vms
, vms
->fn
);
8272 static int play_record_review(struct ast_channel
*chan
, char *playfile
, char *recordfile
, int maxtime
, char *fmt
,
8273 int outsidecaller
, struct ast_vm_user
*vmu
, int *duration
, const char *unlockdir
,
8274 signed char record_gain
, struct vm_state
*vms
)
8276 /* Record message & let caller review or re-record it, or set options if applicable */
8279 int max_attempts
= 3;
8282 int message_exists
= 0;
8283 signed char zero_gain
= 0;
8284 char tempfile
[PATH_MAX
];
8285 char *acceptdtmf
= "#";
8286 char *canceldtmf
= "";
8288 /* Note that urgent and private are for flagging messages as such in the future */
8290 /* barf if no pointer passed to store duration in */
8291 if (duration
== NULL
) {
8292 ast_log(LOG_WARNING
, "Error play_record_review called without duration pointer\n");
8297 snprintf(tempfile
, sizeof(tempfile
), "%s.tmp", recordfile
);
8299 ast_copy_string(tempfile
, recordfile
, sizeof(tempfile
));
8301 cmd
= '3'; /* Want to start by recording */
8303 while ((cmd
>= 0) && (cmd
!= 't')) {
8306 if (!message_exists
) {
8307 /* In this case, 1 is to record a message */
8311 /* Otherwise 1 is to save the existing message */
8312 if (option_verbose
> 2)
8313 ast_verbose(VERBOSE_PREFIX_3
"Saving message as is\n");
8315 ast_filerename(tempfile
, recordfile
, NULL
);
8316 ast_stream_and_wait(chan
, "vm-msgsaved", chan
->language
, "");
8317 if (!outsidecaller
) {
8318 STORE(recordfile
, vmu
->mailbox
, vmu
->context
, -1, chan
, vmu
, fmt
, *duration
, vms
);
8319 DISPOSE(recordfile
, -1);
8326 if (option_verbose
> 2)
8327 ast_verbose(VERBOSE_PREFIX_3
"Reviewing the message\n");
8328 cmd
= ast_stream_and_wait(chan
, tempfile
, chan
->language
, AST_DIGIT_ANY
);
8333 if (recorded
== 1) {
8334 if (option_verbose
> 2)
8335 ast_verbose(VERBOSE_PREFIX_3
"Re-recording the message\n");
8337 if (option_verbose
> 2)
8338 ast_verbose(VERBOSE_PREFIX_3
"Recording the message\n");
8340 if (recorded
&& outsidecaller
) {
8341 cmd
= ast_play_and_wait(chan
, INTRO
);
8342 cmd
= ast_play_and_wait(chan
, "beep");
8345 /* 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 */
8347 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &record_gain
, sizeof(record_gain
), 0);
8348 if (ast_test_flag(vmu
, VM_OPERATOR
))
8350 cmd
= ast_play_and_record_full(chan
, playfile
, tempfile
, maxtime
, fmt
, duration
, silencethreshold
, maxsilence
, unlockdir
, acceptdtmf
, canceldtmf
);
8352 ast_channel_setoption(chan
, AST_OPTION_RXGAIN
, &zero_gain
, sizeof(zero_gain
), 0);
8354 /* User has hung up, no options to give */
8355 if (!outsidecaller
) {
8356 /* user was recording a greeting and they hung up, so let's delete the recording. */
8357 ast_filedelete(tempfile
, NULL
);
8363 } else if (cmd
== '*') {
8367 else if (vmu
->review
&& (*duration
< 5)) {
8368 /* Message is too short */
8369 if (option_verbose
> 2)
8370 ast_verbose(VERBOSE_PREFIX_3
"Message too short\n");
8371 cmd
= ast_play_and_wait(chan
, "vm-tooshort");
8372 cmd
= ast_filedelete(tempfile
, NULL
);
8375 else if (vmu
->review
&& (cmd
== 2 && *duration
< (maxsilence
+ 3))) {
8376 /* Message is all silence */
8377 if (option_verbose
> 2)
8378 ast_verbose(VERBOSE_PREFIX_3
"Nothing recorded\n");
8379 cmd
= ast_filedelete(tempfile
, NULL
);
8380 cmd
= ast_play_and_wait(chan
, "vm-nothingrecorded");
8382 cmd
= ast_play_and_wait(chan
, "vm-speakup");
8387 /* If all is well, a message exists */
8400 cmd
= ast_play_and_wait(chan
, "vm-sorry");
8403 /* XXX Commented out for the moment because of the dangers of deleting
8404 a message while recording (can put the message numbers out of sync) */
8406 /* Cancel recording, delete message, offer to take another message*/
8407 cmd
= ast_play_and_wait(chan
, "vm-deleted");
8408 cmd
= ast_filedelete(tempfile
, NULL
);
8409 if (outsidecaller
) {
8410 res
= vm_exec(chan
, NULL
);
8417 if (!ast_test_flag(vmu
, VM_OPERATOR
)) {
8418 cmd
= ast_play_and_wait(chan
, "vm-sorry");
8421 if (message_exists
|| recorded
) {
8422 cmd
= ast_play_and_wait(chan
, "vm-saveoper");
8424 cmd
= ast_waitfordigit(chan
, 3000);
8426 ast_play_and_wait(chan
, "vm-msgsaved");
8429 ast_play_and_wait(chan
, "vm-deleted");
8430 DELETE(recordfile
, -1, recordfile
);
8436 /* If the caller is an ouside caller, and the review option is enabled,
8437 allow them to review the message, but let the owner of the box review
8439 if (outsidecaller
&& !ast_test_flag(vmu
, VM_REVIEW
))
8441 if (message_exists
) {
8442 cmd
= ast_play_and_wait(chan
, "vm-review");
8445 cmd
= ast_play_and_wait(chan
, "vm-torerecord");
8447 cmd
= ast_waitfordigit(chan
, 600);
8450 if (!cmd
&& outsidecaller
&& ast_test_flag(vmu
, VM_OPERATOR
)) {
8451 cmd
= ast_play_and_wait(chan
, "vm-reachoper");
8453 cmd
= ast_waitfordigit(chan
, 600);
8457 cmd
= ast_play_and_wait(chan
, "vm-tocancelmsg");
8460 cmd
= ast_waitfordigit(chan
, 6000);
8464 if (attempts
> max_attempts
) {
8470 ast_play_and_wait(chan
, "vm-goodbye");
8478 static void write_file(char *filename
, char *buffer
, unsigned long len
)
8482 output
= fopen (filename
, "w");
8483 fwrite (buffer
, len
, 1, output
);
8487 void mm_searched(MAILSTREAM
*stream
, unsigned long number
)
8489 struct vm_state
*vms
;
8492 mailbox
= stream
->mailbox
;
8493 user
= get_user_by_mailbox(mailbox
);
8494 vms
= get_vm_state_by_imapuser(user
,2);
8496 if (option_debug
> 2)
8497 ast_log (LOG_DEBUG
, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number
,vms
->vmArrayIndex
,vms
->interactive
);
8498 vms
->msgArray
[vms
->vmArrayIndex
++] = number
;
8500 ast_log (LOG_ERROR
, "No state found.\n");
8505 #if 0 /*No need for this. */
8507 * Accepts: MAIL stream
8509 static void status(MAILSTREAM
*stream
)
8512 char *s
, date
[MAILTMPLEN
];
8514 AUTHENTICATOR
*auth
;
8516 ast_log (LOG_NOTICE
,"%s\n",date
);
8518 if (stream
->mailbox
)
8519 ast_log (LOG_NOTICE
," %s mailbox: %s, %lu messages, %lu recent\n",
8520 stream
->dtb
->name
, stream
->mailbox
, stream
->nmsgs
,stream
->recent
);
8522 ast_log (LOG_NOTICE
,"No mailbox is open on this stream\n");
8523 if (stream
->user_flags
[0]) {
8524 ast_log (LOG_NOTICE
,"Keywords: %s\n", stream
->user_flags
[0]);
8525 for (i
= 1; i
< NUSERFLAGS
&& stream
->user_flags
[i
]; ++i
)
8526 ast_log (LOG_NOTICE
," %s\n", stream
->user_flags
[i
]);
8528 if (!strcmp (stream
->dtb
->name
, "imap")) {
8529 if (LEVELIMAP4rev1 (stream
))
8530 s
= "IMAP4rev1 (RFC 3501)";
8531 else if (LEVEL1730 (stream
))
8532 s
= "IMAP4 (RFC 1730)";
8533 else if (LEVELIMAP2bis (stream
))
8535 else if (LEVEL1176 (stream
))
8536 s
= "IMAP2 (RFC 1176)";
8538 s
= "IMAP2 (RFC 1064)";
8539 ast_log (LOG_NOTICE
,"%s server %s\n", s
, imap_host (stream
));
8540 if (LEVELIMAP4 (stream
)) {
8541 if ((i
= (imap_cap(stream
)->auth
))) {
8543 ast_log (LOG_NOTICE
,"Mutually-supported SASL mechanisms:\n");
8544 while ((auth
= mail_lookup_auth (find_rightmost_bit (&i
) + 1))) {
8545 ast_log (LOG_NOTICE
," %s\n", auth
->name
);
8546 if (!strcmp (auth
->name
, "PLAIN"))
8547 s
= "\n [LOGIN will not be listed here if PLAIN is supported]\n";
8549 ast_log (LOG_NOTICE
,s
);
8551 ast_log (LOG_NOTICE
,"Supported standard extensions:\n");
8552 if (LEVELACL (stream
))
8553 ast_log (LOG_NOTICE
," Access Control lists (RFC 2086)\n");
8554 if (LEVELQUOTA (stream
))
8555 ast_log (LOG_NOTICE
," Quotas (RFC 2087)\n");
8556 if (LEVELLITERALPLUS (stream
))
8557 ast_log (LOG_NOTICE
," Non-synchronizing literals (RFC 2088)\n");
8558 if (LEVELIDLE (stream
))
8559 ast_log (LOG_NOTICE
," IDLE unsolicited update (RFC 2177)\n");
8560 if (LEVELMBX_REF (stream
))
8561 ast_log (LOG_NOTICE
," Mailbox referrals (RFC 2193)\n");
8562 if (LEVELLOG_REF (stream
))
8563 ast_log (LOG_NOTICE
," Login referrals (RFC 2221)\n");
8564 if (LEVELANONYMOUS (stream
))
8565 ast_log (LOG_NOTICE
," Anonymous access (RFC 2245)\n");
8566 if (LEVELNAMESPACE (stream
))
8567 ast_log (LOG_NOTICE
," Multiple namespaces (RFC 2342)\n");
8568 if (LEVELUIDPLUS (stream
))
8569 ast_log (LOG_NOTICE
," Extended UID behavior (RFC 2359)\n");
8570 if (LEVELSTARTTLS (stream
))
8571 ast_log (LOG_NOTICE
," Transport Layer Security (RFC 2595)\n");
8572 if (LEVELLOGINDISABLED (stream
))
8573 ast_log (LOG_NOTICE
," LOGIN command disabled (RFC 2595)\n");
8574 if (LEVELID (stream
))
8575 ast_log (LOG_NOTICE
," Implementation identity negotiation (RFC 2971)\n");
8576 if (LEVELCHILDREN (stream
))
8577 ast_log (LOG_NOTICE
," LIST children announcement (RFC 3348)\n");
8578 if (LEVELMULTIAPPEND (stream
))
8579 ast_log (LOG_NOTICE
," Atomic multiple APPEND (RFC 3502)\n");
8580 if (LEVELBINARY (stream
))
8581 ast_log (LOG_NOTICE
," Binary body content (RFC 3516)\n");
8582 ast_log (LOG_NOTICE
,"Supported draft extensions:\n");
8583 if (LEVELUNSELECT (stream
))
8584 ast_log (LOG_NOTICE
," Mailbox unselect\n");
8585 if (LEVELSASLIR (stream
))
8586 ast_log (LOG_NOTICE
," SASL initial client response\n");
8587 if (LEVELSORT (stream
))
8588 ast_log (LOG_NOTICE
," Server-based sorting\n");
8589 if (LEVELTHREAD (stream
)) {
8590 ast_log (LOG_NOTICE
," Server-based threading:\n");
8591 for (thr
= imap_cap(stream
)->threader
; thr
; thr
= thr
->next
)
8592 ast_log (LOG_NOTICE
," %s\n", thr
->name
);
8594 if (LEVELSCAN (stream
))
8595 ast_log (LOG_NOTICE
," Mailbox text scan\n");
8596 if ((i
= imap_cap(stream
)->extlevel
)) {
8597 ast_log (LOG_NOTICE
,"Supported BODYSTRUCTURE extensions:\n");
8600 ast_log (LOG_NOTICE
," location\n");
8602 ast_log (LOG_NOTICE
," language\n");
8604 ast_log (LOG_NOTICE
," disposition\n");
8606 ast_log (LOG_NOTICE
," MD5\n");
8610 ast_log (LOG_NOTICE
,"\n");
8616 static struct ast_vm_user
*find_user_realtime_imapuser(const char *imapuser
)
8618 struct ast_variable
*var
;
8619 struct ast_vm_user
*vmu
;
8621 vmu
= ast_calloc(1, sizeof *vmu
);
8624 ast_set_flag(vmu
, VM_ALLOCED
);
8625 populate_defaults(vmu
);
8627 var
= ast_load_realtime("voicemail", "imapuser", imapuser
, NULL
);
8629 apply_options_full(vmu
, var
);
8630 ast_variables_destroy(var
);
8638 /* Interfaces to C-client */
8640 void mm_exists(MAILSTREAM
* stream
, unsigned long number
)
8642 /* mail_ping will callback here if new mail! */
8643 if (option_debug
> 3)
8644 ast_log (LOG_DEBUG
, "Entering EXISTS callback for message %ld\n", number
);
8645 if (number
== 0) return;
8650 void mm_expunged(MAILSTREAM
* stream
, unsigned long number
)
8652 /* mail_ping will callback here if expunged mail! */
8653 if (option_debug
> 3)
8654 ast_log (LOG_DEBUG
, "Entering EXPUNGE callback for message %ld\n", number
);
8655 if (number
== 0) return;
8660 void mm_flags(MAILSTREAM
* stream
, unsigned long number
)
8662 /* mail_ping will callback here if read mail! */
8663 if (option_debug
> 3)
8664 ast_log (LOG_DEBUG
, "Entering FLAGS callback for message %ld\n", number
);
8665 if (number
== 0) return;
8670 void mm_notify(MAILSTREAM
* stream
, char *string
, long errflg
)
8672 mm_log (string
, errflg
);
8676 void mm_list(MAILSTREAM
* stream
, int delim
, char *mailbox
, long attributes
)
8678 if (delimiter
== '\0') {
8681 if (option_debug
> 4) {
8682 ast_log(LOG_DEBUG
, "Delimiter set to %c and mailbox %s\n",delim
, mailbox
);
8683 if (attributes
& LATT_NOINFERIORS
)
8684 ast_log(LOG_DEBUG
, "no inferiors\n");
8685 if (attributes
& LATT_NOSELECT
)
8686 ast_log(LOG_DEBUG
, "no select\n");
8687 if (attributes
& LATT_MARKED
)
8688 ast_log(LOG_DEBUG
, "marked\n");
8689 if (attributes
& LATT_UNMARKED
)
8690 ast_log(LOG_DEBUG
, "unmarked\n");
8695 void mm_lsub(MAILSTREAM
* stream
, int delimiter
, char *mailbox
, long attributes
)
8697 if (option_debug
> 4) {
8698 ast_log(LOG_DEBUG
, "Delimiter set to %c and mailbox %s\n",delimiter
, mailbox
);
8699 if (attributes
& LATT_NOINFERIORS
)
8700 ast_log(LOG_DEBUG
, "no inferiors\n");
8701 if (attributes
& LATT_NOSELECT
)
8702 ast_log(LOG_DEBUG
, "no select\n");
8703 if (attributes
& LATT_MARKED
)
8704 ast_log(LOG_DEBUG
, "marked\n");
8705 if (attributes
& LATT_UNMARKED
)
8706 ast_log(LOG_DEBUG
, "unmarked\n");
8711 void mm_status(MAILSTREAM
* stream
, char *mailbox
, MAILSTATUS
* status
)
8713 ast_log (LOG_NOTICE
," Mailbox %s", mailbox
);
8714 if (status
->flags
& SA_MESSAGES
)
8715 ast_log (LOG_NOTICE
,", %lu messages", status
->messages
);
8716 if (status
->flags
& SA_RECENT
)
8717 ast_log (LOG_NOTICE
,", %lu recent", status
->recent
);
8718 if (status
->flags
& SA_UNSEEN
)
8719 ast_log (LOG_NOTICE
,", %lu unseen", status
->unseen
);
8720 if (status
->flags
& SA_UIDVALIDITY
)
8721 ast_log (LOG_NOTICE
,", %lu UID validity", status
->uidvalidity
);
8722 if (status
->flags
& SA_UIDNEXT
)
8723 ast_log (LOG_NOTICE
,", %lu next UID", status
->uidnext
);
8724 ast_log (LOG_NOTICE
,"\n");
8728 void mm_log(char *string
, long errflg
)
8730 switch ((short) errflg
) {
8733 ast_log(LOG_DEBUG
,"IMAP Info: %s\n", string
);
8737 ast_log (LOG_WARNING
,"IMAP Warning: %s\n", string
);
8740 ast_log (LOG_ERROR
,"IMAP Error: %s\n", string
);
8746 void mm_dlog(char *string
)
8748 ast_log (LOG_NOTICE
, "%s\n", string
);
8752 void mm_login(NETMBX
* mb
, char *user
, char *pwd
, long trial
)
8754 struct ast_vm_user
*vmu
;
8756 if (option_debug
> 3)
8757 ast_log(LOG_DEBUG
, "Entering callback mm_login\n");
8759 ast_copy_string(user
, mb
->user
, MAILTMPLEN
);
8761 /* We should only do this when necessary */
8762 if (!ast_strlen_zero(authpassword
)) {
8763 ast_copy_string(pwd
, authpassword
, MAILTMPLEN
);
8765 AST_LIST_TRAVERSE(&users
, vmu
, list
) {
8766 if (!strcasecmp(mb
->user
, vmu
->imapuser
)) {
8767 ast_copy_string(pwd
, vmu
->imappassword
, MAILTMPLEN
);
8772 if ((vmu
= find_user_realtime_imapuser(mb
->user
))) {
8773 ast_copy_string(pwd
, vmu
->imappassword
, MAILTMPLEN
);
8781 void mm_critical(MAILSTREAM
* stream
)
8786 void mm_nocritical(MAILSTREAM
* stream
)
8791 long mm_diskerror(MAILSTREAM
* stream
, long errcode
, long serious
)
8793 kill (getpid (), SIGSTOP
);
8798 void mm_fatal(char *string
)
8800 ast_log(LOG_ERROR
,"IMAP access FATAL error: %s\n", string
);
8803 /* C-client callback to handle quota */
8804 static void mm_parsequota(MAILSTREAM
*stream
, unsigned char *msg
, QUOTALIST
*pquota
)
8806 struct vm_state
*vms
;
8809 unsigned long usage
= 0;
8810 unsigned long limit
= 0;
8813 usage
= pquota
->usage
;
8814 limit
= pquota
->limit
;
8815 pquota
= pquota
->next
;
8818 mailbox
= stream
->mailbox
;
8819 user
= get_user_by_mailbox(mailbox
);
8820 vms
= get_vm_state_by_imapuser(user
,2);
8822 if (option_debug
> 2)
8823 ast_log (LOG_DEBUG
, "User %s usage is %lu, limit is %lu\n",user
,usage
,limit
);
8824 vms
->quota_usage
= usage
;
8825 vms
->quota_limit
= limit
;
8827 ast_log (LOG_ERROR
, "No state found.\n");
8831 static char *get_header_by_tag(char *header
, char *tag
)
8837 if (!header
|| !tag
)
8840 taglen
= strlen(tag
) + 1;
8844 start
= strstr(header
, tag
);
8848 ast_mutex_lock(&imaptemp_lock
);
8849 ast_copy_string(imaptemp
, start
+taglen
, sizeof(imaptemp
));
8850 ast_mutex_unlock(&imaptemp_lock
);
8851 if ((eol_pnt
= strchr(imaptemp
,'\r')) || (eol_pnt
= strchr(imaptemp
,'\n')))
8856 static char *get_user_by_mailbox(char *mailbox
)
8858 char *start
, *quote
;
8864 start
= strstr(mailbox
,"/user=");
8868 ast_mutex_lock(&imaptemp_lock
);
8869 ast_copy_string(imaptemp
, start
+6, sizeof(imaptemp
));
8870 ast_mutex_unlock(&imaptemp_lock
);
8872 quote
= strchr(imaptemp
,'\"');
8873 if (!quote
) { /* if username is not in quotes */
8874 eol_pnt
= strchr(imaptemp
,'/');
8876 eol_pnt
= strchr(imaptemp
,'}');
8881 eol_pnt
= strchr(imaptemp
+1,'\"');
8887 static struct vm_state
*get_vm_state_by_imapuser(char *user
, int interactive
)
8889 struct vmstate
*vlist
= NULL
;
8891 ast_mutex_lock(&vmstate_lock
);
8895 if (vlist
->vms
->imapuser
) {
8896 if (!strcmp(vlist
->vms
->imapuser
,user
)) {
8897 if (interactive
== 2) {
8898 ast_mutex_unlock(&vmstate_lock
);
8900 } else if (vlist
->vms
->interactive
== interactive
) {
8901 ast_mutex_unlock(&vmstate_lock
);
8906 if (option_debug
> 2)
8907 ast_log(LOG_DEBUG
, " error: imapuser is NULL for %s\n",user
);
8910 if (option_debug
> 2)
8911 ast_log(LOG_DEBUG
, " error: vms is NULL for %s\n",user
);
8913 vlist
= vlist
->next
;
8915 ast_mutex_unlock(&vmstate_lock
);
8916 if (option_debug
> 2)
8917 ast_log(LOG_DEBUG
, "%s not found in vmstates\n",user
);
8921 static struct vm_state
*get_vm_state_by_mailbox(const char *mailbox
, int interactive
)
8923 struct vmstate
*vlist
= NULL
;
8925 ast_mutex_lock(&vmstate_lock
);
8927 if (option_debug
> 2)
8928 ast_log(LOG_DEBUG
, "Mailbox set to %s\n",mailbox
);
8931 if (vlist
->vms
->username
) {
8932 if (option_debug
> 2)
8933 ast_log(LOG_DEBUG
, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox
,interactive
,vlist
->vms
->username
,vlist
->vms
->interactive
);
8934 if (!strcmp(vlist
->vms
->username
,mailbox
) && vlist
->vms
->interactive
== interactive
) {
8935 if (option_debug
> 2)
8936 ast_log(LOG_DEBUG
, " Found it!\n");
8937 ast_mutex_unlock(&vmstate_lock
);
8941 if (option_debug
> 2)
8942 ast_log(LOG_DEBUG
, " error: username is NULL for %s\n",mailbox
);
8945 if (option_debug
> 2)
8946 ast_log(LOG_DEBUG
, " error: vms is NULL for %s\n",mailbox
);
8948 vlist
= vlist
->next
;
8950 ast_mutex_unlock(&vmstate_lock
);
8951 if (option_debug
> 2)
8952 ast_log(LOG_DEBUG
, "%s not found in vmstates\n",mailbox
);
8956 static void vmstate_insert(struct vm_state
*vms
)
8959 struct vm_state
*altvms
;
8961 /* If interactive, it probably already exists, and we should
8962 use the one we already have since it is more up to date.
8963 We can compare the username to find the duplicate */
8964 if (vms
->interactive
== 1) {
8965 altvms
= get_vm_state_by_mailbox(vms
->username
,0);
8967 if (option_debug
> 2)
8968 ast_log(LOG_DEBUG
, "Duplicate mailbox %s, copying message info...\n",vms
->username
);
8969 vms
->newmessages
= altvms
->newmessages
;
8970 vms
->oldmessages
= altvms
->oldmessages
;
8971 if (option_debug
> 2)
8972 ast_log(LOG_DEBUG
, "check_msgArray before memcpy\n");
8973 check_msgArray(vms
);
8974 /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
8975 copy_msgArray(vms
, altvms
);
8976 if (option_debug
> 2)
8977 ast_log(LOG_DEBUG
, "check_msgArray after memcpy\n");
8978 check_msgArray(vms
);
8979 vms
->vmArrayIndex
= altvms
->vmArrayIndex
;
8980 vms
->lastmsg
= altvms
->lastmsg
;
8981 vms
->curmsg
= altvms
->curmsg
;
8982 /* get a pointer to the persistent store */
8983 vms
->persist_vms
= altvms
;
8984 /* Reuse the mailstream? */
8985 vms
->mailstream
= altvms
->mailstream
;
8986 /* vms->mailstream = NIL; */
8990 v
= (struct vmstate
*)malloc(sizeof(struct vmstate
));
8992 ast_log(LOG_ERROR
, "Out of memory\n");
8994 if (option_debug
> 2)
8995 ast_log(LOG_DEBUG
, "Inserting vm_state for user:%s, mailbox %s\n",vms
->imapuser
,vms
->username
);
8996 ast_mutex_lock(&vmstate_lock
);
9000 ast_mutex_unlock(&vmstate_lock
);
9003 static void vmstate_delete(struct vm_state
*vms
)
9005 struct vmstate
*vc
, *vf
= NULL
, *vl
= NULL
;
9006 struct vm_state
*altvms
;
9008 /* If interactive, we should copy pertainent info
9009 back to the persistent state (to make update immediate) */
9010 if (vms
->interactive
== 1) {
9011 altvms
= vms
->persist_vms
;
9013 if (option_debug
> 2)
9014 ast_log(LOG_DEBUG
, "Duplicate mailbox %s, copying message info...\n",vms
->username
);
9015 altvms
->newmessages
= vms
->newmessages
;
9016 altvms
->oldmessages
= vms
->oldmessages
;
9017 altvms
->updated
= 1;
9021 ast_mutex_lock(&vmstate_lock
);
9023 if (option_debug
> 2)
9024 ast_log(LOG_DEBUG
, "Removing vm_state for user:%s, mailbox %s\n",vms
->imapuser
,vms
->username
);
9026 if (vc
->vms
== vms
) {
9029 vl
->next
= vc
->next
;
9031 vmstates
= vc
->next
;
9038 ast_log(LOG_ERROR
, "No vmstate found for user:%s, mailbox %s\n",vms
->imapuser
,vms
->username
);
9040 ast_mutex_destroy(&vf
->vms
->lock
);
9043 ast_mutex_unlock(&vmstate_lock
);
9046 static void set_update(MAILSTREAM
* stream
)
9048 struct vm_state
*vms
;
9052 mailbox
= stream
->mailbox
;
9053 user
= get_user_by_mailbox(mailbox
);
9054 vms
= get_vm_state_by_imapuser(user
, 0);
9056 if (option_debug
> 2)
9057 ast_log (LOG_DEBUG
, "User %s mailbox set for update.\n",user
);
9058 vms
->updated
= 1; /* set updated flag since mailbox changed */
9060 if (option_debug
> 2)
9061 ast_log (LOG_WARNING
, "User %s mailbox not found for update.\n",user
);
9065 static void init_vm_state(struct vm_state
*vms
)
9068 vms
->vmArrayIndex
= 0;
9069 for (x
= 0; x
< 256; x
++) {
9070 vms
->msgArray
[x
] = 0;
9072 ast_mutex_init(&vms
->lock
);
9075 static void check_msgArray(struct vm_state
*vms
)
9078 for (x
= 0; x
<256; x
++) {
9079 if (vms
->msgArray
[x
]!=0) {
9081 ast_log (LOG_DEBUG
, "Item %d set to %ld\n",x
,vms
->msgArray
[x
]);
9086 static void copy_msgArray(struct vm_state
*dst
, struct vm_state
*src
)
9089 for (x
= 0; x
<256; x
++) {
9090 dst
->msgArray
[x
] = src
->msgArray
[x
];
9094 static int save_body(BODY
*body
, struct vm_state
*vms
, char *section
, char *format
)
9099 unsigned long newlen
;
9102 if (!body
|| body
== NIL
)
9104 body_content
= mail_fetchbody (vms
->mailstream
, vms
->msgArray
[vms
->curmsg
], section
, &len
);
9105 if (body_content
!= NIL
) {
9106 snprintf(filename
, sizeof(filename
), "%s.%s", vms
->fn
, format
);
9107 /* ast_log (LOG_DEBUG,body_content); */
9108 body_decoded
= rfc822_base64 ((unsigned char *)body_content
, len
, &newlen
);
9109 write_file (filename
, (char *) body_decoded
, newlen
);
9114 /* get delimiter via mm_list callback */
9115 static void get_mailbox_delimiter(MAILSTREAM
*stream
) {
9117 snprintf(tmp
, sizeof(tmp
), "{%s}", imapserver
);
9118 mail_list(stream
, tmp
, "*");
9121 /* Check Quota for user */
9122 static void check_quota(struct vm_state
*vms
, char *mailbox
) {
9123 mail_parameters(NULL
, SET_QUOTA
, (void *) mm_parsequota
);
9124 if (option_debug
> 2)
9125 ast_log(LOG_DEBUG
, "Mailbox name set to: %s, about to check quotas\n", mailbox
);
9126 if (vms
&& vms
->mailstream
!= NULL
) {
9127 imap_getquotaroot(vms
->mailstream
, mailbox
);
9129 ast_log(LOG_WARNING
,"Mailstream not available for mailbox: %s\n",mailbox
);
9133 #endif /* IMAP_STORAGE */
9135 /* This is a workaround so that menuselect displays a proper description
9136 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
9139 AST_MODULE_INFO(ASTERISK_GPL_KEY
, AST_MODFLAG_DEFAULT
, tdesc
,
9140 .load
= load_module
,
9141 .unload
= unload_module
,