Let's also include aclocal.m4
[asterisk-bristuff.git] / apps / app_voicemail.c
blobf6401b2b5f205465b1ff71d8c37b61d3d2bbf51d
1 /*
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.
19 /*! \file
21 * \brief Comedian Mail - Voicemail System
23 * \author Mark Spencer <markster@digium.com>
25 * \par See also
26 * \arg \ref Config_vm
27 * \ingroup applications
28 * \note This module requires res_adsi to load.
31 /*** MODULEINFO
32 <depend>res_adsi</depend>
33 <depend>res_smdi</depend>
34 ***/
36 /*** MAKEOPTS
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>
40 <depend>ltdl</depend>
41 <conflict>IMAP_STORAGE</conflict>
42 <defaultenabled>no</defaultenabled>
43 </member>
44 <member name="IMAP_STORAGE" displayname="Storage of Voicemail using IMAP4">
45 <depend>imap_tk</depend>
46 <conflict>ODBC_STORAGE</conflict>
47 <use>ssl</use>
48 <defaultenabled>no</defaultenabled>
49 </member>
50 </category>
51 ***/
53 #include "asterisk.h"
55 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
57 #include <stdlib.h>
58 #include <errno.h>
59 #include <unistd.h>
60 #include <string.h>
61 #include <stdlib.h>
62 #include <stdio.h>
63 #include <sys/time.h>
64 #include <sys/stat.h>
65 #include <sys/types.h>
66 #include <sys/mman.h>
67 #include <time.h>
68 #include <dirent.h>
69 #ifdef IMAP_STORAGE
70 #include <ctype.h>
71 #include <signal.h>
72 #include <pwd.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>
81 #else
82 #include "c-client.h"
83 #include "imap4r1.h"
84 #include "linkage.h"
85 #endif
86 #endif
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"
105 #ifdef ODBC_STORAGE
106 #include "asterisk/res_odbc.h"
107 #endif
109 #ifdef IMAP_STORAGE
110 AST_MUTEX_DEFINE_STATIC(imaptemp_lock);
111 static char imaptemp[1024];
112 static char imapserver[48];
113 static char imapport[8];
114 static char imapflags[128];
115 static char imapfolder[64];
116 static char authuser[32];
117 static char authpassword[42];
119 static int expungeonhangup = 1;
120 static char delimiter = '\0';
121 static const long DEFAULT_IMAP_TCP_TIMEOUT = 60L;
123 struct vm_state;
124 struct ast_vm_user;
126 static int init_mailstream (struct vm_state *vms, int box);
127 static void write_file (char *filename, char *buffer, unsigned long len);
128 /*static void status (MAILSTREAM *stream); */ /* No need for this. */
129 static char *get_header_by_tag(char *header, char *tag);
130 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu);
131 static char *get_user_by_mailbox(char *mailbox);
132 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive);
133 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive);
134 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu);
135 static void vmstate_insert(struct vm_state *vms);
136 static void vmstate_delete(struct vm_state *vms);
137 static void set_update(MAILSTREAM * stream);
138 static void init_vm_state(struct vm_state *vms);
139 static void copy_msgArray(struct vm_state *dst, struct vm_state *src);
140 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format);
141 static void get_mailbox_delimiter(MAILSTREAM *stream);
142 static void mm_parsequota (MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota);
143 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int target);
144 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms);
145 static void check_quota(struct vm_state *vms, char *mailbox);
146 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box);
147 struct vmstate {
148 struct vm_state *vms;
149 struct vmstate *next;
151 AST_MUTEX_DEFINE_STATIC(vmstate_lock);
152 static struct vmstate *vmstates = NULL;
153 #endif
155 #define SMDI_MWI_WAIT_TIMEOUT 1000 /* 1 second */
157 #define COMMAND_TIMEOUT 5000
158 /* Don't modify these here; set your umask at runtime instead */
159 #define VOICEMAIL_DIR_MODE 0777
160 #define VOICEMAIL_FILE_MODE 0666
161 #define CHUNKSIZE 65536
163 #define VOICEMAIL_CONFIG "voicemail.conf"
164 #define ASTERISK_USERNAME "asterisk"
166 /* Default mail command to mail voicemail. Change it with the
167 mailcmd= command in voicemail.conf */
168 #define SENDMAIL "/usr/sbin/sendmail -t"
170 #define INTRO "vm-intro"
172 #define MAXMSG 100
173 #ifndef IMAP_STORAGE
174 #define MAXMSGLIMIT 9999
175 #else
176 #define MAXMSGLIMIT 255
177 #endif
179 #define BASEMAXINLINE 256
180 #define BASELINELEN 72
181 #define BASEMAXINLINE 256
182 #define eol "\r\n"
184 #define MAX_DATETIME_FORMAT 512
185 #define MAX_NUM_CID_CONTEXTS 10
187 #define VM_REVIEW (1 << 0)
188 #define VM_OPERATOR (1 << 1)
189 #define VM_SAYCID (1 << 2)
190 #define VM_SVMAIL (1 << 3)
191 #define VM_ENVELOPE (1 << 4)
192 #define VM_SAYDURATION (1 << 5)
193 #define VM_SKIPAFTERCMD (1 << 6)
194 #define VM_FORCENAME (1 << 7) /*!< Have new users record their name */
195 #define VM_FORCEGREET (1 << 8) /*!< Have new users record their greetings */
196 #define VM_PBXSKIP (1 << 9)
197 #define VM_DIRECFORWARD (1 << 10) /*!< directory_forward */
198 #define VM_ATTACH (1 << 11)
199 #define VM_DELETE (1 << 12)
200 #define VM_ALLOCED (1 << 13)
201 #define VM_SEARCH (1 << 14)
202 #define VM_TEMPGREETWARN (1 << 15) /*!< Remind user tempgreeting is set */
203 #define ERROR_LOCK_PATH -100
204 #define ERROR_MAILBOX_FULL -200
207 enum {
208 OPT_SILENT = (1 << 0),
209 OPT_BUSY_GREETING = (1 << 1),
210 OPT_UNAVAIL_GREETING = (1 << 2),
211 OPT_RECORDGAIN = (1 << 3),
212 OPT_PREPEND_MAILBOX = (1 << 4),
213 OPT_PRIORITY_JUMP = (1 << 5),
214 OPT_AUTOPLAY = (1 << 6),
215 } vm_option_flags;
217 enum {
218 OPT_ARG_RECORDGAIN = 0,
219 OPT_ARG_PLAYFOLDER = 1,
220 /* This *must* be the last value in this enum! */
221 OPT_ARG_ARRAY_SIZE = 2,
222 } vm_option_args;
224 AST_APP_OPTIONS(vm_app_options, {
225 AST_APP_OPTION('s', OPT_SILENT),
226 AST_APP_OPTION('b', OPT_BUSY_GREETING),
227 AST_APP_OPTION('u', OPT_UNAVAIL_GREETING),
228 AST_APP_OPTION_ARG('g', OPT_RECORDGAIN, OPT_ARG_RECORDGAIN),
229 AST_APP_OPTION('p', OPT_PREPEND_MAILBOX),
230 AST_APP_OPTION('j', OPT_PRIORITY_JUMP),
231 AST_APP_OPTION_ARG('a', OPT_AUTOPLAY, OPT_ARG_PLAYFOLDER),
234 static int load_config(void);
236 /*! \page vmlang Voicemail Language Syntaxes Supported
238 \par Syntaxes supported, not really language codes.
239 \arg \b en - English
240 \arg \b de - German
241 \arg \b es - Spanish
242 \arg \b fr - French
243 \arg \b it = Italian
244 \arg \b nl - Dutch
245 \arg \b pt - Polish
246 \arg \b pt - Portuguese
247 \arg \b pt_BR - Portuguese (Brazil)
248 \arg \b gr - Greek
249 \arg \b no - Norwegian
250 \arg \b se - Swedish
251 \arg \b ua - Ukrainian
252 \arg \b he - Hebrew
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'
264 NB these are plural:
265 \arg \b vm-INBOX nieuwe (nl)
266 \arg \b vm-Old oude (nl)
268 Polish uses:
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'
278 Swedish uses:
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'
285 Norwegian uses:
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'
291 Dutch also uses:
292 \arg \b nl-om 'at'?
294 Spanish also uses:
295 \arg \b vm-youhaveno
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:
304 For vm_intro_it:
305 \arg \b vm-nuovo new
306 \arg \b vm-nuovi new plural
307 \arg \b vm-vecchio old
308 \arg \b vm-vecchi old plural
310 Hebrew also uses:
311 \arg \b vm-INBOX1 '1 new message'
312 \arg \b vm-OLD1 '1 old message'
313 \arg \b vm-shtei 'shtei'
314 \arg \b vm-nomessages 'you have no new messages'
316 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
317 spelled among others when you have to change folder. For the above reasons, vm-INBOX
318 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
322 struct baseio {
323 int iocp;
324 int iolen;
325 int linelength;
326 int ateof;
327 unsigned char iobuf[BASEMAXINLINE];
330 /*! Structure for linked list of users */
331 struct ast_vm_user {
332 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
333 char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
334 char password[80]; /*!< Secret pin code, numbers only */
335 char fullname[80]; /*!< Full name, for directory app */
336 char email[80]; /*!< E-mail address */
337 char pager[80]; /*!< E-mail address to pager (no attachment) */
338 char serveremail[80]; /*!< From: Mail address */
339 char mailcmd[160]; /*!< Configurable mail command */
340 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
341 char zonetag[80]; /*!< Time zone */
342 char callback[80];
343 char dialout[80];
344 char uniqueid[80]; /*!< Unique integer identifier */
345 char exit[80];
346 char attachfmt[20]; /*!< Attachment format */
347 unsigned int flags; /*!< VM_ flags */
348 int saydurationm;
349 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
350 #ifdef IMAP_STORAGE
351 char imapuser[80]; /* IMAP server login */
352 char imappassword[80]; /* IMAP server password if authpassword not defined */
353 #endif
354 double volgain; /*!< Volume gain for voicemails sent via email */
355 AST_LIST_ENTRY(ast_vm_user) list;
358 struct vm_zone {
359 AST_LIST_ENTRY(vm_zone) list;
360 char name[80];
361 char timezone[80];
362 char msg_format[512];
365 struct vm_state {
366 char curbox[80];
367 char username[80];
368 char context[80];
369 char curdir[PATH_MAX];
370 char vmbox[PATH_MAX];
371 char fn[PATH_MAX];
372 char fn2[PATH_MAX];
373 int *deleted;
374 int *heard;
375 int curmsg;
376 int lastmsg;
377 int newmessages;
378 int oldmessages;
379 int starting;
380 int repeats;
381 #ifdef IMAP_STORAGE
382 ast_mutex_t lock;
383 int updated; /* decremented on each mail check until 1 -allows delay */
384 long msgArray[256];
385 MAILSTREAM *mailstream;
386 int vmArrayIndex;
387 char imapuser[80]; /* IMAP server login */
388 int interactive;
389 unsigned int quota_limit;
390 unsigned int quota_usage;
391 struct vm_state *persist_vms;
392 #endif
394 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
395 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
396 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
397 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
398 signed char record_gain, struct vm_state *vms);
399 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
400 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
401 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
402 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
403 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
404 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
405 #endif
406 static void apply_options(struct ast_vm_user *vmu, const char *options);
408 #ifdef ODBC_STORAGE
409 static char odbc_database[80];
410 static char odbc_table[80];
411 #define RETRIEVE(a,b,c) retrieve_file(a,b)
412 #define DISPOSE(a,b) remove_file(a,b)
413 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
414 #define EXISTS(a,b,c,d) (message_exists(a,b))
415 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
416 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
417 #define DELETE(a,b,c,d) (delete_file(a,b))
418 #else
419 #ifdef IMAP_STORAGE
420 #define RETRIEVE(a,b,c) imap_retrieve_file(a,b,c)
421 #define DISPOSE(a,b) remove_file(a,b)
422 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
423 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
424 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
425 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
426 #define DELETE(a,b,c,d) (vm_imap_delete(b,d))
427 #else
428 #define RETRIEVE(a,b,c)
429 #define DISPOSE(a,b)
430 #define STORE(a,b,c,d,e,f,g,h,i)
431 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
432 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
433 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
434 #define DELETE(a,b,c,d) (vm_delete(c))
435 #endif
436 #endif
438 static char VM_SPOOL_DIR[PATH_MAX];
440 static char ext_pass_cmd[128];
442 int my_umask;
444 #if ODBC_STORAGE
445 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
446 #elif IMAP_STORAGE
447 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
448 #else
449 #define tdesc "Comedian Mail (Voicemail System)"
450 #endif
452 static char userscontext[AST_MAX_EXTENSION] = "default";
454 static char *addesc = "Comedian Mail";
456 static char *synopsis_vm =
457 "Leave a Voicemail message";
459 static char *descrip_vm =
460 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
461 "application allows the calling party to leave a message for the specified\n"
462 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
463 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
464 "specified mailbox does not exist.\n"
465 " The Voicemail application will exit if any of the following DTMF digits are\n"
466 "received:\n"
467 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
468 " * - Jump to the 'a' extension in the current dialplan context.\n"
469 " This application will set the following channel variable upon completion:\n"
470 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
471 " application. The possible values are:\n"
472 " SUCCESS | USEREXIT | FAILED\n\n"
473 " Options:\n"
474 " b - Play the 'busy' greeting to the calling party.\n"
475 " g(#) - Use the specified amount of gain when recording the voicemail\n"
476 " message. The units are whole-number decibels (dB).\n"
477 " Only works on supported technologies, which is Zap only.\n"
478 " s - Skip the playback of instructions for leaving a message to the\n"
479 " calling party.\n"
480 " u - Play the 'unavailable' greeting.\n"
481 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
482 " error occurs.\n";
484 static char *synopsis_vmain =
485 "Check Voicemail messages";
487 static char *descrip_vmain =
488 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
489 "calling party to check voicemail messages. A specific mailbox, and optional\n"
490 "corresponding context, may be specified. If a mailbox is not provided, the\n"
491 "calling party will be prompted to enter one. If a context is not specified,\n"
492 "the 'default' context will be used.\n\n"
493 " Options:\n"
494 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
495 " is entered by the caller.\n"
496 " g(#) - Use the specified amount of gain when recording a voicemail\n"
497 " message. The units are whole-number decibels (dB).\n"
498 " s - Skip checking the passcode for the mailbox.\n"
499 " a(#) - Skip folder prompt and go directly to folder specified.\n"
500 " Defaults to INBOX\n";
502 static char *synopsis_vm_box_exists =
503 "Check to see if Voicemail mailbox exists";
505 static char *descrip_vm_box_exists =
506 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
507 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
508 "will be used.\n"
509 " This application will set the following channel variable upon completion:\n"
510 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
511 " MailboxExists application. Possible values include:\n"
512 " SUCCESS | FAILED\n\n"
513 " Options:\n"
514 " j - Jump to priority n+101 if the mailbox is found.\n";
516 static char *synopsis_vmauthenticate =
517 "Authenticate with Voicemail passwords";
519 static char *descrip_vmauthenticate =
520 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
521 "same way as the Authenticate application, but the passwords are taken from\n"
522 "voicemail.conf.\n"
523 " If the mailbox is specified, only that mailbox's password will be considered\n"
524 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
525 "be set with the authenticated mailbox.\n\n"
526 " Options:\n"
527 " s - Skip playing the initial prompts.\n";
529 /* Leave a message */
530 static char *app = "VoiceMail";
532 /* Check mail, control, etc */
533 static char *app2 = "VoiceMailMain";
535 static char *app3 = "MailboxExists";
536 static char *app4 = "VMAuthenticate";
538 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
539 static AST_LIST_HEAD_STATIC(zones, vm_zone);
540 static int maxsilence;
541 static int maxmsg;
542 static int silencethreshold = 128;
543 static char serveremail[80];
544 static char mailcmd[160]; /* Configurable mail cmd */
545 static char externnotify[160];
546 static struct ast_smdi_interface *smdi_iface = NULL;
547 static char vmfmts[80];
548 static double volgain;
549 static int vmminmessage;
550 static int vmmaxmessage;
551 static int maxgreet;
552 static int skipms;
553 static int maxlogins;
555 static struct ast_flags globalflags = {0};
557 static int saydurationminfo;
559 static char dialcontext[AST_MAX_CONTEXT];
560 static char callcontext[AST_MAX_CONTEXT];
561 static char exitcontext[AST_MAX_CONTEXT];
563 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
566 static char *emailbody = NULL;
567 static char *emailsubject = NULL;
568 static char *pagerbody = NULL;
569 static char *pagersubject = NULL;
570 static char fromstring[100];
571 static char pagerfromstring[100];
572 static char emailtitle[100];
573 static char charset[32] = "ISO-8859-1";
575 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
576 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
577 static int adsiver = 1;
578 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
581 static char *strip_control(const char *input, char *buf, size_t buflen)
583 char *bufptr = buf;
584 for (; *input; input++) {
585 if (*input < 32) {
586 continue;
588 *bufptr++ = *input;
589 if (bufptr == buf + buflen - 1) {
590 break;
593 *bufptr = '\0';
594 return buf;
597 static void populate_defaults(struct ast_vm_user *vmu)
599 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
600 if (saydurationminfo)
601 vmu->saydurationm = saydurationminfo;
602 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
603 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
604 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
605 if (maxmsg)
606 vmu->maxmsg = maxmsg;
607 vmu->volgain = volgain;
610 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
612 int x;
613 if (!strcasecmp(var, "attach")) {
614 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
615 } else if (!strcasecmp(var, "attachfmt")) {
616 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
617 } else if (!strcasecmp(var, "serveremail")) {
618 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
619 } else if (!strcasecmp(var, "language")) {
620 ast_copy_string(vmu->language, value, sizeof(vmu->language));
621 } else if (!strcasecmp(var, "tz")) {
622 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
623 #ifdef IMAP_STORAGE
624 } else if (!strcasecmp(var, "imapuser")) {
625 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
626 } else if (!strcasecmp(var, "imappassword") || !strcasecmp(var, "imapsecret")) {
627 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
628 #endif
629 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
630 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
631 } else if (!strcasecmp(var, "saycid")){
632 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
633 } else if (!strcasecmp(var,"sendvoicemail")){
634 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
635 } else if (!strcasecmp(var, "review")){
636 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
637 } else if (!strcasecmp(var, "tempgreetwarn")){
638 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
639 } else if (!strcasecmp(var, "operator")){
640 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
641 } else if (!strcasecmp(var, "envelope")){
642 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
643 } else if (!strcasecmp(var, "sayduration")){
644 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
645 } else if (!strcasecmp(var, "saydurationm")){
646 if (sscanf(value, "%d", &x) == 1) {
647 vmu->saydurationm = x;
648 } else {
649 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
651 } else if (!strcasecmp(var, "forcename")){
652 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
653 } else if (!strcasecmp(var, "forcegreetings")){
654 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
655 } else if (!strcasecmp(var, "callback")) {
656 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
657 } else if (!strcasecmp(var, "dialout")) {
658 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
659 } else if (!strcasecmp(var, "exitcontext")) {
660 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
661 } else if (!strcasecmp(var, "maxmsg")) {
662 vmu->maxmsg = atoi(value);
663 if (vmu->maxmsg <= 0) {
664 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
665 vmu->maxmsg = MAXMSG;
666 } else if (vmu->maxmsg > MAXMSGLIMIT) {
667 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
668 vmu->maxmsg = MAXMSGLIMIT;
670 } else if (!strcasecmp(var, "volgain")) {
671 sscanf(value, "%lf", &vmu->volgain);
672 } else if (!strcasecmp(var, "options")) {
673 apply_options(vmu, value);
677 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
679 int res;
680 if (!ast_strlen_zero(vmu->uniqueid)) {
681 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
682 if (res > 0) {
683 ast_copy_string(vmu->password, password, sizeof(vmu->password));
684 res = 0;
685 } else if (!res) {
686 res = -1;
688 return res;
690 return -1;
693 static void apply_options(struct ast_vm_user *vmu, const char *options)
694 { /* Destructively Parse options and apply */
695 char *stringp;
696 char *s;
697 char *var, *value;
698 stringp = ast_strdupa(options);
699 while ((s = strsep(&stringp, "|"))) {
700 value = s;
701 if ((var = strsep(&value, "=")) && value) {
702 apply_option(vmu, var, value);
707 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
709 struct ast_variable *tmp;
710 tmp = var;
711 while (tmp) {
712 if (!strcasecmp(tmp->name, "vmsecret")) {
713 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
714 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
715 if (ast_strlen_zero(retval->password))
716 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
717 } else if (!strcasecmp(tmp->name, "uniqueid")) {
718 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
719 } else if (!strcasecmp(tmp->name, "pager")) {
720 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
721 } else if (!strcasecmp(tmp->name, "email")) {
722 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
723 } else if (!strcasecmp(tmp->name, "fullname")) {
724 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
725 } else if (!strcasecmp(tmp->name, "context")) {
726 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
727 #ifdef IMAP_STORAGE
728 } else if (!strcasecmp(tmp->name, "imapuser")) {
729 ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
730 } else if (!strcasecmp(tmp->name, "imappassword") || !strcasecmp(tmp->name, "imapsecret")) {
731 ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
732 #endif
733 } else
734 apply_option(retval, tmp->name, tmp->value);
735 tmp = tmp->next;
739 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
741 struct ast_variable *var;
742 struct ast_vm_user *retval;
744 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
745 if (!ivm)
746 ast_set_flag(retval, VM_ALLOCED);
747 else
748 memset(retval, 0, sizeof(*retval));
749 if (mailbox)
750 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
751 populate_defaults(retval);
752 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
753 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
754 else
755 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
756 if (var) {
757 apply_options_full(retval, var);
758 ast_variables_destroy(var);
759 } else {
760 if (!ivm)
761 free(retval);
762 retval = NULL;
765 return retval;
768 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
770 /* This function could be made to generate one from a database, too */
771 struct ast_vm_user *vmu=NULL, *cur;
772 AST_LIST_LOCK(&users);
774 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
775 context = "default";
777 AST_LIST_TRAVERSE(&users, cur, list) {
778 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
779 break;
780 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
781 break;
783 if (cur) {
784 /* Make a copy, so that on a reload, we have no race */
785 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
786 memcpy(vmu, cur, sizeof(*vmu));
787 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
788 AST_LIST_NEXT(vmu, list) = NULL;
790 } else
791 vmu = find_user_realtime(ivm, context, mailbox);
792 AST_LIST_UNLOCK(&users);
793 return vmu;
796 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
798 /* This function could be made to generate one from a database, too */
799 struct ast_vm_user *cur;
800 int res = -1;
801 AST_LIST_LOCK(&users);
802 AST_LIST_TRAVERSE(&users, cur, list) {
803 if ((!context || !strcasecmp(context, cur->context)) &&
804 (!strcasecmp(mailbox, cur->mailbox)))
805 break;
807 if (cur) {
808 ast_copy_string(cur->password, newpass, sizeof(cur->password));
809 res = 0;
811 AST_LIST_UNLOCK(&users);
812 return res;
815 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
817 struct ast_config *cfg=NULL;
818 struct ast_variable *var=NULL;
819 struct ast_category *cat=NULL;
820 char *category=NULL, *value=NULL, *new=NULL;
821 const char *tmp=NULL;
823 if (!change_password_realtime(vmu, newpassword))
824 return;
826 /* check voicemail.conf */
827 if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
828 while ((category = ast_category_browse(cfg, category))) {
829 if (!strcasecmp(category, vmu->context)) {
830 tmp = ast_variable_retrieve(cfg, category, vmu->mailbox);
831 if (!tmp) {
832 ast_log(LOG_WARNING, "We could not find the mailbox.\n");
833 break;
835 value = strstr(tmp,",");
836 if (!value) {
837 ast_log(LOG_WARNING, "variable has bad format.\n");
838 break;
840 new = alloca((strlen(value)+strlen(newpassword)+1));
841 sprintf(new,"%s%s", newpassword, value);
842 if (!(cat = ast_category_get(cfg, category))) {
843 ast_log(LOG_WARNING, "Failed to get category structure.\n");
844 break;
846 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
849 /* save the results */
850 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
851 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
852 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
854 category = NULL;
855 var = NULL;
856 /* check users.conf and update the password stored for the mailbox*/
857 /* if no vmsecret entry exists create one. */
858 if ((cfg = ast_config_load_with_comments("users.conf"))) {
859 if (option_debug > 3)
860 ast_log(LOG_DEBUG, "we are looking for %s\n", vmu->mailbox);
861 while ((category = ast_category_browse(cfg, category))) {
862 if (option_debug > 3)
863 ast_log(LOG_DEBUG, "users.conf: %s\n", category);
864 if (!strcasecmp(category, vmu->mailbox)) {
865 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
866 if (option_debug > 3)
867 ast_log(LOG_DEBUG, "looks like we need to make vmsecret!\n");
868 var = ast_variable_new("vmsecret", newpassword);
870 new = alloca(strlen(newpassword)+1);
871 sprintf(new, "%s", newpassword);
872 if (!(cat = ast_category_get(cfg, category))) {
873 if (option_debug > 3)
874 ast_log(LOG_DEBUG, "failed to get category!\n");
875 break;
877 if (!var)
878 ast_variable_update(cat, "vmsecret", new, NULL, 0);
879 else
880 ast_variable_append(cat, var);
883 /* save the results and clean things up */
884 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
885 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
886 config_text_file_save("users.conf", cfg, "AppVoicemail");
890 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
892 char buf[255];
893 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
894 if (!ast_safe_system(buf)) {
895 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
896 /* Reset the password in memory, too */
897 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
901 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
903 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
906 static int make_file(char *dest, const int len, const char *dir, const int num)
908 return snprintf(dest, len, "%s/msg%04d", dir, num);
911 /* same as mkstemp, but return a FILE * */
912 static FILE *vm_mkftemp(char *template)
914 FILE *p = NULL;
915 int pfd = mkstemp(template);
916 chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
917 if (pfd > -1) {
918 p = fdopen(pfd, "w+");
919 if (!p) {
920 close(pfd);
921 pfd = -1;
924 return p;
927 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
928 * \param dest String. base directory.
929 * \param len Length of dest.
930 * \param context String. Ignored if is null or empty string.
931 * \param ext String. Ignored if is null or empty string.
932 * \param folder String. Ignored if is null or empty string.
933 * \return -1 on failure, 0 on success.
935 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
937 mode_t mode = VOICEMAIL_DIR_MODE;
939 if (!ast_strlen_zero(context)) {
940 make_dir(dest, len, context, "", "");
941 if (mkdir(dest, mode) && errno != EEXIST) {
942 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
943 return -1;
946 if (!ast_strlen_zero(ext)) {
947 make_dir(dest, len, context, ext, "");
948 if (mkdir(dest, mode) && errno != EEXIST) {
949 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
950 return -1;
953 if (!ast_strlen_zero(folder)) {
954 make_dir(dest, len, context, ext, folder);
955 if (mkdir(dest, mode) && errno != EEXIST) {
956 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
957 return -1;
960 return 0;
963 static char *mbox(int id)
965 static char *msgs[] = {
966 "INBOX",
967 "Old",
968 "Work",
969 "Family",
970 "Friends",
971 "Cust1",
972 "Cust2",
973 "Cust3",
974 "Cust4",
975 "Cust5",
977 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "tmp";
980 static void free_user(struct ast_vm_user *vmu)
982 if (ast_test_flag(vmu, VM_ALLOCED))
983 free(vmu);
986 /* All IMAP-specific functions should go in this block. This
987 * keeps them from being spread out all over the code */
988 #ifdef IMAP_STORAGE
989 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu)
991 char arg[10];
992 struct vm_state *vms;
993 unsigned long messageNum;
995 /* Greetings aren't stored in IMAP, so we can't delete them there */
996 if (msgnum < 0) {
997 return;
1000 if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1001 ast_log(LOG_WARNING, "Couldn't find a vm_state for mailbox %s. Unable to set \\DELETED flag for message %d\n", vmu->mailbox, msgnum);
1002 return;
1005 /* find real message number based on msgnum */
1006 /* this may be an index into vms->msgArray based on the msgnum. */
1007 messageNum = vms->msgArray[msgnum];
1008 if (messageNum == 0) {
1009 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
1010 return;
1012 if (option_debug > 2)
1013 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1014 /* delete message */
1015 snprintf (arg, sizeof(arg), "%lu",messageNum);
1016 mail_setflag (vms->mailstream,arg,"\\DELETED");
1019 static int imap_retrieve_file(const char *dir, const int msgnum, const struct ast_vm_user *vmu)
1021 BODY *body;
1022 char *header_content;
1023 char *attachedfilefmt;
1024 const char *cid_num;
1025 const char *cid_name;
1026 const char *duration;
1027 const char *context;
1028 const char *category;
1029 const char *origtime;
1030 struct vm_state *vms;
1031 char text_file[PATH_MAX];
1032 FILE *text_file_ptr;
1034 /* Greetings are not stored on the IMAP server, so we should not
1035 * attempt to retrieve them.
1037 if (msgnum < 0) {
1038 return 0;
1041 /* Before anything can happen, we need a vm_state so that we can
1042 * actually access the imap server through the vms->mailstream
1044 if(!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1045 /* This should not happen. If it does, then I guess we'd
1046 * need to create the vm_state, extract which mailbox to
1047 * open, and then set up the msgArray so that the correct
1048 * IMAP message could be accessed. If I have seen correctly
1049 * though, the vms should be obtainable from the vmstates list
1050 * and should have its msgArray properly set up.
1052 ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
1055 make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
1057 /* Don't try to retrieve a message from IMAP if it already is on the file system */
1058 if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
1059 return 0;
1062 if (option_debug > 2)
1063 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
1064 if (vms->msgArray[msgnum] == 0) {
1065 ast_log (LOG_WARNING,"Trying to access unknown message\n");
1066 return -1;
1069 /* This will only work for new messages... */
1070 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
1071 /* empty string means no valid header */
1072 if (ast_strlen_zero(header_content)) {
1073 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
1074 return -1;
1077 mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
1079 /* We have the body, now we extract the file name of the first attachment. */
1080 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1081 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
1082 } else {
1083 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
1084 return -1;
1087 /* Find the format of the attached file */
1089 strsep(&attachedfilefmt, ".");
1090 if (!attachedfilefmt) {
1091 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
1092 return -1;
1095 save_body(body, vms, "2", attachedfilefmt);
1097 /* Get info from headers!! */
1098 snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
1100 if (!(text_file_ptr = fopen(text_file, "w"))) {
1101 ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
1104 fprintf(text_file_ptr, "%s\n", "[message]");
1106 cid_num = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
1107 cid_name = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:");
1108 fprintf(text_file_ptr, "callerid=\"%s\" <%s>\n", S_OR(cid_name, ""), S_OR(cid_num, ""));
1109 context = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
1110 fprintf(text_file_ptr, "context=%s\n", S_OR(context, ""));
1111 origtime = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
1112 fprintf(text_file_ptr, "origtime=%s\n", S_OR(origtime, ""));
1113 duration = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
1114 fprintf(text_file_ptr, "duration=%s\n", S_OR(origtime, ""));
1115 category = get_header_by_tag(header_content, "X-Asterisk-VM-Category:");
1116 fprintf(text_file_ptr, "category=%s\n", S_OR(category, ""));
1118 fclose(text_file_ptr);
1119 return 0;
1122 static int folder_int(const char *folder)
1124 /*assume a NULL folder means INBOX*/
1125 if (!folder)
1126 return 0;
1127 if (!strcasecmp(folder, "INBOX"))
1128 return 0;
1129 else if (!strcasecmp(folder, "Old"))
1130 return 1;
1131 else if (!strcasecmp(folder, "Work"))
1132 return 2;
1133 else if (!strcasecmp(folder, "Family"))
1134 return 3;
1135 else if (!strcasecmp(folder, "Friends"))
1136 return 4;
1137 else if (!strcasecmp(folder, "Cust1"))
1138 return 5;
1139 else if (!strcasecmp(folder, "Cust2"))
1140 return 6;
1141 else if (!strcasecmp(folder, "Cust3"))
1142 return 7;
1143 else if (!strcasecmp(folder, "Cust4"))
1144 return 8;
1145 else if (!strcasecmp(folder, "Cust5"))
1146 return 9;
1147 else /*assume they meant INBOX if folder is not found otherwise*/
1148 return 0;
1151 static int messagecount(const char *context, const char *mailbox, const char *folder)
1153 SEARCHPGM *pgm;
1154 SEARCHHEADER *hdr;
1156 struct ast_vm_user *vmu, vmus;
1157 struct vm_state *vms_p;
1158 int ret = 0;
1159 int fold = folder_int(folder);
1161 if (ast_strlen_zero(mailbox))
1162 return 0;
1164 /* We have to get the user before we can open the stream! */
1165 /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
1166 vmu = find_user(&vmus, context, mailbox);
1167 if (!vmu) {
1168 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
1169 return -1;
1170 } else {
1171 /* No IMAP account available */
1172 if (vmu->imapuser[0] == '\0') {
1173 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
1174 return -1;
1178 /* check if someone is accessing this box right now... */
1179 vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
1180 if (!vms_p) {
1181 vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
1183 if (vms_p) {
1184 if (option_debug > 2)
1185 ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
1186 if (fold == 0) {/*INBOX*/
1187 return vms_p->newmessages;
1189 if (fold == 1) {/*Old messages*/
1190 return vms_p->oldmessages;
1194 /* add one if not there... */
1195 vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
1196 if (!vms_p) {
1197 vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
1200 if (!vms_p) {
1201 if (!(vms_p = create_vm_state_from_user(vmu))) {
1202 ast_log(LOG_WARNING, "Unable to allocate space for new vm_state!\n");
1203 return -1;
1206 ret = init_mailstream(vms_p, fold);
1207 if (!vms_p->mailstream) {
1208 ast_log (LOG_ERROR,"IMAP mailstream is NULL\n");
1209 return -1;
1211 if (ret == 0) {
1212 pgm = mail_newsearchpgm ();
1213 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
1214 pgm->header = hdr;
1215 if (fold != 1) {
1216 pgm->unseen = 1;
1217 pgm->seen = 0;
1219 /* In the special case where fold is 1 (old messages) we have to do things a bit
1220 * differently. Old messages are stored in the INBOX but are marked as "seen"
1222 else {
1223 pgm->unseen = 0;
1224 pgm->seen = 1;
1226 pgm->undeleted = 1;
1227 pgm->deleted = 0;
1229 vms_p->vmArrayIndex = 0;
1230 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
1231 if (fold == 0)
1232 vms_p->newmessages = vms_p->vmArrayIndex;
1233 if (fold == 1)
1234 vms_p->oldmessages = vms_p->vmArrayIndex;
1235 /*Freeing the searchpgm also frees the searchhdr*/
1236 mail_free_searchpgm(&pgm);
1237 vms_p->updated = 0;
1238 return vms_p->vmArrayIndex;
1239 } else {
1240 mail_ping(vms_p->mailstream);
1242 return 0;
1245 static int imap_store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum, struct ast_channel *chan, struct ast_vm_user *vmu, char *fmt, int duration, struct vm_state *vms)
1247 char *myserveremail = serveremail;
1248 char fn[PATH_MAX];
1249 char mailbox[256];
1250 char *stringp;
1251 FILE *p=NULL;
1252 char tmp[80] = "/tmp/astmail-XXXXXX";
1253 long len;
1254 void *buf;
1255 int tempcopy = 0;
1256 STRING str;
1258 /*Greetings are not retrieved from IMAP, so there is no reason to attempt storing them there either*/
1259 if (msgnum < 0)
1260 return 0;
1262 /* Attach only the first format */
1263 fmt = ast_strdupa(fmt);
1264 stringp = fmt;
1265 strsep(&stringp, "|");
1267 if (!ast_strlen_zero(vmu->serveremail))
1268 myserveremail = vmu->serveremail;
1270 make_file(fn, sizeof(fn), dir, msgnum);
1272 if (ast_strlen_zero(vmu->email)) {
1273 /*we need the vmu->email to be set when we call make_email_file, but if we keep it set,
1274 * a duplicate e-mail will be created. So at the end of this function, we will revert back to an empty
1275 * string if tempcopy is 1
1277 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
1278 tempcopy = 1;
1281 if (!strcmp(fmt, "wav49"))
1282 fmt = "WAV";
1283 if (option_debug > 2)
1284 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
1285 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1286 command hangs */
1287 if ((p = vm_mkftemp(tmp)) == NULL) {
1288 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
1289 if (tempcopy)
1290 *(vmu->email) = '\0';
1291 return -1;
1292 } else {
1293 make_email_file(p, myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), fn, fmt, duration, 1, chan, NULL, 1);
1294 /* read mail file to memory */
1295 len = ftell(p);
1296 rewind(p);
1297 if ((buf = ast_malloc(len+1)) == NIL) {
1298 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
1299 fclose(p);
1300 return -1;
1302 fread(buf, len, 1, p);
1303 ((char *)buf)[len] = '\0';
1304 INIT(&str, mail_string, buf, len);
1305 init_mailstream(vms, 0);
1306 imap_mailbox_name(mailbox, sizeof(mailbox), vms, 0, 1);
1307 if (!mail_append(vms->mailstream, mailbox, &str))
1308 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
1309 fclose(p);
1310 unlink(tmp);
1311 ast_free(buf);
1312 if (option_debug > 2)
1313 ast_log(LOG_DEBUG, "%s stored\n", fn);
1314 /* Using messagecount to populate the last place in the msgArray
1315 * is less than optimal, but it's the only way given the current setup
1317 messagecount(vmu->context, vmu->mailbox, "INBOX");
1319 if (tempcopy)
1320 *(vmu->email) = '\0';
1321 return 0;
1325 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
1327 char tmp[PATH_MAX] = "";
1328 char *mailboxnc;
1329 char *context;
1330 char *mb;
1331 char *cur;
1332 if (newmsgs)
1333 *newmsgs = 0;
1334 if (oldmsgs)
1335 *oldmsgs = 0;
1337 if (option_debug > 2)
1338 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox_context);
1339 /* If no mailbox, return immediately */
1340 if (ast_strlen_zero(mailbox_context))
1341 return 0;
1343 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
1344 context = strchr(tmp, '@');
1345 if (strchr(mailbox_context, ',')) {
1346 int tmpnew, tmpold;
1347 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
1348 mb = tmp;
1349 while ((cur = strsep(&mb, ", "))) {
1350 if (!ast_strlen_zero(cur)) {
1351 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
1352 return -1;
1353 else {
1354 if (newmsgs)
1355 *newmsgs += tmpnew;
1356 if (oldmsgs)
1357 *oldmsgs += tmpold;
1361 return 0;
1363 if (context) {
1364 *context = '\0';
1365 mailboxnc = tmp;
1366 context++;
1367 } else {
1368 context = "default";
1369 mailboxnc = (char *)mailbox_context;
1371 if (newmsgs) {
1372 if ((*newmsgs = messagecount(context, mailboxnc, "INBOX")) < 0)
1373 return -1;
1375 if (oldmsgs) {
1376 if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
1377 return -1;
1379 return 0;
1383 static int has_voicemail(const char *mailbox, const char *folder)
1385 char tmp[256], *tmp2, *mbox, *context;
1386 ast_copy_string(tmp, mailbox, sizeof(tmp));
1387 tmp2 = tmp;
1388 if (strchr(tmp2, ',')) {
1389 while ((mbox = strsep(&tmp2, ","))) {
1390 if (!ast_strlen_zero(mbox)) {
1391 if (has_voicemail(mbox, folder))
1392 return 1;
1396 if ((context= strchr(tmp, '@')))
1397 *context++ = '\0';
1398 else
1399 context = "default";
1400 return messagecount(context, tmp, folder) ? 1 : 0;
1403 static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir)
1405 struct vm_state *sendvms = NULL, *destvms = NULL;
1406 char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
1407 if (msgnum >= recip->maxmsg) {
1408 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
1409 return -1;
1411 if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
1412 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
1413 return -1;
1415 if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
1416 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
1417 return -1;
1419 snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
1420 if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
1421 return 0;
1422 ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
1423 return -1;
1426 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
1428 char tmp[256], *t = tmp;
1429 size_t left = sizeof(tmp);
1431 if (box == 1) {
1432 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
1433 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(1));
1434 } else {
1435 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
1436 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
1439 /* Build up server information */
1440 ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
1442 /* Add authentication user if present */
1443 if (!ast_strlen_zero(authuser))
1444 ast_build_string(&t, &left, "/authuser=%s", authuser);
1446 /* Add flags if present */
1447 if (!ast_strlen_zero(imapflags))
1448 ast_build_string(&t, &left, "/%s", imapflags);
1450 /* End with username */
1451 ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
1453 if (box == 0 || box == 1)
1454 snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
1455 else
1456 snprintf(spec, len, "%s%s%c%s", tmp, imapfolder, delimiter, mbox(box));
1459 static int init_mailstream(struct vm_state *vms, int box)
1461 MAILSTREAM *stream = NIL;
1462 long debug;
1463 char tmp[256];
1465 if (!vms) {
1466 ast_log (LOG_ERROR,"vm_state is NULL!\n");
1467 return -1;
1469 if (option_debug > 2)
1470 ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
1471 if (vms->mailstream == NIL || !vms->mailstream) {
1472 if (option_debug)
1473 ast_log (LOG_DEBUG,"mailstream not set.\n");
1474 } else {
1475 stream = vms->mailstream;
1477 /* debug = T; user wants protocol telemetry? */
1478 debug = NIL; /* NO protocol telemetry? */
1480 if (delimiter == '\0') { /* did not probe the server yet */
1481 char *cp;
1482 #ifdef USE_SYSTEM_IMAP
1483 #include <imap/linkage.c>
1484 #elif defined(USE_SYSTEM_CCLIENT)
1485 #include <c-client/linkage.c>
1486 #else
1487 #include "linkage.c"
1488 #endif
1489 /* Connect to INBOX first to get folders delimiter */
1490 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
1491 ast_mutex_lock(&vms->lock);
1492 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
1493 ast_mutex_unlock(&vms->lock);
1494 if (stream == NIL) {
1495 ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
1496 return -1;
1498 get_mailbox_delimiter(stream);
1499 /* update delimiter in imapfolder */
1500 for (cp = imapfolder; *cp; cp++)
1501 if (*cp == '/')
1502 *cp = delimiter;
1504 /* Now connect to the target folder */
1505 imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
1506 if (option_debug > 2)
1507 ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
1508 ast_mutex_lock(&vms->lock);
1509 vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
1510 ast_mutex_unlock(&vms->lock);
1511 if (vms->mailstream == NIL) {
1512 return -1;
1513 } else {
1514 return 0;
1518 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
1520 SEARCHPGM *pgm;
1521 SEARCHHEADER *hdr;
1522 int ret;
1524 ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
1525 if (option_debug > 2)
1526 ast_log(LOG_DEBUG,"Before init_mailstream, user is %s\n",vmu->imapuser);
1527 ret = init_mailstream(vms, box);
1528 if (ret != 0 || !vms->mailstream) {
1529 ast_log (LOG_ERROR,"Could not initialize mailstream\n");
1530 return -1;
1533 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
1535 /* Check Quota */
1536 if (box == 0) {
1537 if (option_debug > 2)
1538 ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
1539 check_quota(vms,(char *)mbox(box));
1542 pgm = mail_newsearchpgm();
1544 /* Check IMAP folder for Asterisk messages only... */
1545 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", vmu->mailbox);
1546 pgm->header = hdr;
1547 pgm->deleted = 0;
1548 pgm->undeleted = 1;
1550 /* if box = 0, check for new, if box = 1, check for read */
1551 if (box == 0) {
1552 pgm->unseen = 1;
1553 pgm->seen = 0;
1554 } else if (box == 1) {
1555 pgm->seen = 1;
1556 pgm->unseen = 0;
1559 vms->vmArrayIndex = 0;
1560 if (option_debug > 2)
1561 ast_log(LOG_DEBUG,"Before mail_search_full, user is %s\n",vmu->imapuser);
1562 mail_search_full (vms->mailstream, NULL, pgm, NIL);
1564 vms->lastmsg = vms->vmArrayIndex - 1;
1566 mail_free_searchpgm(&pgm);
1567 return 0;
1570 static void write_file(char *filename, char *buffer, unsigned long len)
1572 FILE *output;
1574 output = fopen (filename, "w");
1575 fwrite (buffer, len, 1, output);
1576 fclose (output);
1579 void mm_searched(MAILSTREAM *stream, unsigned long number)
1581 struct vm_state *vms;
1582 char *mailbox;
1583 char *user;
1584 mailbox = stream->mailbox;
1585 user = get_user_by_mailbox(mailbox);
1586 vms = get_vm_state_by_imapuser(user,2);
1587 if (vms) {
1588 if (option_debug > 2)
1589 ast_log (LOG_DEBUG, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number,vms->vmArrayIndex,vms->interactive);
1590 vms->msgArray[vms->vmArrayIndex++] = number;
1591 } else {
1592 ast_log (LOG_ERROR, "No state found.\n");
1596 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
1598 struct ast_variable *var;
1599 struct ast_vm_user *vmu;
1601 vmu = ast_calloc(1, sizeof *vmu);
1602 if (!vmu)
1603 return NULL;
1604 ast_set_flag(vmu, VM_ALLOCED);
1605 populate_defaults(vmu);
1607 var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
1608 if (var) {
1609 apply_options_full(vmu, var);
1610 ast_variables_destroy(var);
1611 return vmu;
1612 } else {
1613 free(vmu);
1614 return NULL;
1618 /* Interfaces to C-client */
1620 void mm_exists(MAILSTREAM * stream, unsigned long number)
1622 /* mail_ping will callback here if new mail! */
1623 if (option_debug > 3)
1624 ast_log (LOG_DEBUG, "Entering EXISTS callback for message %ld\n", number);
1625 if (number == 0) return;
1626 set_update(stream);
1630 void mm_expunged(MAILSTREAM * stream, unsigned long number)
1632 /* mail_ping will callback here if expunged mail! */
1633 if (option_debug > 3)
1634 ast_log (LOG_DEBUG, "Entering EXPUNGE callback for message %ld\n", number);
1635 if (number == 0) return;
1636 set_update(stream);
1640 void mm_flags(MAILSTREAM * stream, unsigned long number)
1642 /* mail_ping will callback here if read mail! */
1643 if (option_debug > 3)
1644 ast_log (LOG_DEBUG, "Entering FLAGS callback for message %ld\n", number);
1645 if (number == 0) return;
1646 set_update(stream);
1650 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
1652 mm_log (string, errflg);
1656 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
1658 if (delimiter == '\0') {
1659 delimiter = delim;
1661 if (option_debug > 4) {
1662 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
1663 if (attributes & LATT_NOINFERIORS)
1664 ast_log(LOG_DEBUG, "no inferiors\n");
1665 if (attributes & LATT_NOSELECT)
1666 ast_log(LOG_DEBUG, "no select\n");
1667 if (attributes & LATT_MARKED)
1668 ast_log(LOG_DEBUG, "marked\n");
1669 if (attributes & LATT_UNMARKED)
1670 ast_log(LOG_DEBUG, "unmarked\n");
1675 void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
1677 if (option_debug > 4) {
1678 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
1679 if (attributes & LATT_NOINFERIORS)
1680 ast_log(LOG_DEBUG, "no inferiors\n");
1681 if (attributes & LATT_NOSELECT)
1682 ast_log(LOG_DEBUG, "no select\n");
1683 if (attributes & LATT_MARKED)
1684 ast_log(LOG_DEBUG, "marked\n");
1685 if (attributes & LATT_UNMARKED)
1686 ast_log(LOG_DEBUG, "unmarked\n");
1691 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
1693 ast_log (LOG_NOTICE," Mailbox %s", mailbox);
1694 if (status->flags & SA_MESSAGES)
1695 ast_log (LOG_NOTICE,", %lu messages", status->messages);
1696 if (status->flags & SA_RECENT)
1697 ast_log (LOG_NOTICE,", %lu recent", status->recent);
1698 if (status->flags & SA_UNSEEN)
1699 ast_log (LOG_NOTICE,", %lu unseen", status->unseen);
1700 if (status->flags & SA_UIDVALIDITY)
1701 ast_log (LOG_NOTICE,", %lu UID validity", status->uidvalidity);
1702 if (status->flags & SA_UIDNEXT)
1703 ast_log (LOG_NOTICE,", %lu next UID", status->uidnext);
1704 ast_log (LOG_NOTICE,"\n");
1708 void mm_log(char *string, long errflg)
1710 switch ((short) errflg) {
1711 case NIL:
1712 if (option_debug)
1713 ast_log(LOG_DEBUG,"IMAP Info: %s\n", string);
1714 break;
1715 case PARSE:
1716 case WARN:
1717 ast_log (LOG_WARNING,"IMAP Warning: %s\n", string);
1718 break;
1719 case ERROR:
1720 ast_log (LOG_ERROR,"IMAP Error: %s\n", string);
1721 break;
1726 void mm_dlog(char *string)
1728 ast_log (LOG_NOTICE, "%s\n", string);
1732 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
1734 struct ast_vm_user *vmu;
1736 if (option_debug > 3)
1737 ast_log(LOG_DEBUG, "Entering callback mm_login\n");
1739 ast_copy_string(user, mb->user, MAILTMPLEN);
1741 /* We should only do this when necessary */
1742 if (!ast_strlen_zero(authpassword)) {
1743 ast_copy_string(pwd, authpassword, MAILTMPLEN);
1744 } else {
1745 AST_LIST_TRAVERSE(&users, vmu, list) {
1746 if (!strcasecmp(mb->user, vmu->imapuser)) {
1747 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
1748 break;
1751 if (!vmu) {
1752 if ((vmu = find_user_realtime_imapuser(mb->user))) {
1753 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
1754 free_user(vmu);
1761 void mm_critical(MAILSTREAM * stream)
1766 void mm_nocritical(MAILSTREAM * stream)
1771 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
1773 kill (getpid (), SIGSTOP);
1774 return NIL;
1778 void mm_fatal(char *string)
1780 ast_log(LOG_ERROR,"IMAP access FATAL error: %s\n", string);
1783 /* C-client callback to handle quota */
1784 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
1786 struct vm_state *vms;
1787 char *mailbox;
1788 char *user;
1789 unsigned long usage = 0;
1790 unsigned long limit = 0;
1792 while (pquota) {
1793 usage = pquota->usage;
1794 limit = pquota->limit;
1795 pquota = pquota->next;
1798 mailbox = stream->mailbox;
1799 user = get_user_by_mailbox(mailbox);
1800 vms = get_vm_state_by_imapuser(user,2);
1801 if (vms) {
1802 if (option_debug > 2)
1803 ast_log (LOG_DEBUG, "User %s usage is %lu, limit is %lu\n",user,usage,limit);
1804 vms->quota_usage = usage;
1805 vms->quota_limit = limit;
1806 } else {
1807 ast_log (LOG_ERROR, "No state found.\n");
1811 static char *get_header_by_tag(char *header, char *tag)
1813 char *start;
1814 int taglen;
1815 char *eol_pnt;
1817 if (!header || !tag)
1818 return NULL;
1820 taglen = strlen(tag) + 1;
1821 if (taglen < 1)
1822 return NULL;
1824 start = strstr(header, tag);
1825 if (!start)
1826 return NULL;
1828 ast_mutex_lock(&imaptemp_lock);
1829 ast_copy_string(imaptemp, start+taglen, sizeof(imaptemp));
1830 ast_mutex_unlock(&imaptemp_lock);
1831 if ((eol_pnt = strchr(imaptemp,'\r')) || (eol_pnt = strchr(imaptemp,'\n')))
1832 *eol_pnt = '\0';
1833 return imaptemp;
1836 static char *get_user_by_mailbox(char *mailbox)
1838 char *start, *quote;
1839 char *eol_pnt;
1841 if (!mailbox)
1842 return NULL;
1844 start = strstr(mailbox,"/user=");
1845 if (!start)
1846 return NULL;
1848 ast_mutex_lock(&imaptemp_lock);
1849 ast_copy_string(imaptemp, start+6, sizeof(imaptemp));
1850 ast_mutex_unlock(&imaptemp_lock);
1852 quote = strchr(imaptemp,'\"');
1853 if (!quote) { /* if username is not in quotes */
1854 eol_pnt = strchr(imaptemp,'/');
1855 if (!eol_pnt) {
1856 eol_pnt = strchr(imaptemp,'}');
1858 *eol_pnt = '\0';
1859 return imaptemp;
1860 } else {
1861 eol_pnt = strchr(imaptemp+1,'\"');
1862 *eol_pnt = '\0';
1863 return imaptemp+1;
1867 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
1869 struct vm_state *vms_p;
1871 if (option_debug > 4)
1872 ast_log(LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
1873 if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
1874 return NULL;
1875 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
1876 ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
1877 ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
1878 vms_p->mailstream = NIL; /* save for access from interactive entry point */
1879 if (option_debug > 4)
1880 ast_log(LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
1881 vms_p->updated = 1;
1882 /* set mailbox to INBOX! */
1883 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
1884 init_vm_state(vms_p);
1885 vmstate_insert(vms_p);
1886 return vms_p;
1889 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
1891 struct vmstate *vlist = NULL;
1893 ast_mutex_lock(&vmstate_lock);
1894 vlist = vmstates;
1895 while (vlist) {
1896 if (vlist->vms) {
1897 if (vlist->vms->imapuser) {
1898 if (!strcmp(vlist->vms->imapuser,user)) {
1899 if (interactive == 2) {
1900 ast_mutex_unlock(&vmstate_lock);
1901 return vlist->vms;
1902 } else if (vlist->vms->interactive == interactive) {
1903 ast_mutex_unlock(&vmstate_lock);
1904 return vlist->vms;
1907 } else {
1908 if (option_debug > 2)
1909 ast_log(LOG_DEBUG, " error: imapuser is NULL for %s\n",user);
1911 } else {
1912 if (option_debug > 2)
1913 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",user);
1915 vlist = vlist->next;
1917 ast_mutex_unlock(&vmstate_lock);
1918 if (option_debug > 2)
1919 ast_log(LOG_DEBUG, "%s not found in vmstates\n",user);
1920 return NULL;
1923 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
1925 struct vmstate *vlist = NULL;
1926 const char *local_context = S_OR(context, "default");
1928 ast_mutex_lock(&vmstate_lock);
1929 vlist = vmstates;
1930 if (option_debug > 2)
1931 ast_log(LOG_DEBUG, "Mailbox set to %s\n",mailbox);
1932 while (vlist) {
1933 if (vlist->vms) {
1934 if (vlist->vms->username && vlist->vms->context) {
1935 if (option_debug > 2)
1936 ast_log(LOG_DEBUG, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox,interactive,vlist->vms->username,vlist->vms->interactive);
1937 if (!strcmp(vlist->vms->username,mailbox) && !(strcmp(vlist->vms->context, local_context)) && vlist->vms->interactive == interactive) {
1938 if (option_debug > 2)
1939 ast_log(LOG_DEBUG, " Found it!\n");
1940 ast_mutex_unlock(&vmstate_lock);
1941 return vlist->vms;
1943 } else {
1944 if (option_debug > 2)
1945 ast_log(LOG_DEBUG, " error: username or context is NULL for %s\n",mailbox);
1947 } else {
1948 if (option_debug > 2)
1949 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",mailbox);
1951 vlist = vlist->next;
1953 ast_mutex_unlock(&vmstate_lock);
1954 if (option_debug > 2)
1955 ast_log(LOG_DEBUG, "%s not found in vmstates\n",mailbox);
1956 return NULL;
1959 static void vmstate_insert(struct vm_state *vms)
1961 struct vmstate *v;
1962 struct vm_state *altvms;
1964 /* If interactive, it probably already exists, and we should
1965 use the one we already have since it is more up to date.
1966 We can compare the username to find the duplicate */
1967 if (vms->interactive == 1) {
1968 altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
1969 if (altvms) {
1970 if (option_debug > 2)
1971 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
1972 vms->newmessages = altvms->newmessages;
1973 vms->oldmessages = altvms->oldmessages;
1974 /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
1975 copy_msgArray(vms, altvms);
1976 vms->vmArrayIndex = altvms->vmArrayIndex;
1977 vms->lastmsg = altvms->lastmsg;
1978 vms->curmsg = altvms->curmsg;
1979 /* get a pointer to the persistent store */
1980 vms->persist_vms = altvms;
1981 /* Reuse the mailstream? */
1982 vms->mailstream = altvms->mailstream;
1983 /* vms->mailstream = NIL; */
1987 v = (struct vmstate *)malloc(sizeof(struct vmstate));
1988 if (!v) {
1989 ast_log(LOG_ERROR, "Out of memory\n");
1991 if (option_debug > 2)
1992 ast_log(LOG_DEBUG, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
1993 ast_mutex_lock(&vmstate_lock);
1994 v->vms = vms;
1995 v->next = vmstates;
1996 vmstates = v;
1997 ast_mutex_unlock(&vmstate_lock);
2000 static void vmstate_delete(struct vm_state *vms)
2002 struct vmstate *vc, *vf = NULL, *vl = NULL;
2003 struct vm_state *altvms;
2005 /* If interactive, we should copy pertainent info
2006 back to the persistent state (to make update immediate) */
2007 if (vms->interactive == 1) {
2008 altvms = vms->persist_vms;
2009 if (altvms) {
2010 if (option_debug > 2)
2011 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
2012 altvms->newmessages = vms->newmessages;
2013 altvms->oldmessages = vms->oldmessages;
2014 altvms->updated = 1;
2018 ast_mutex_lock(&vmstate_lock);
2019 vc = vmstates;
2020 if (option_debug > 2)
2021 ast_log(LOG_DEBUG, "Removing vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
2022 while (vc) {
2023 if (vc->vms == vms) {
2024 vf = vc;
2025 if (vl)
2026 vl->next = vc->next;
2027 else
2028 vmstates = vc->next;
2029 break;
2031 vl = vc;
2032 vc = vc->next;
2034 if (!vf) {
2035 ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n",vms->imapuser,vms->username);
2036 } else {
2037 ast_mutex_destroy(&vf->vms->lock);
2038 free(vf);
2040 ast_mutex_unlock(&vmstate_lock);
2043 static void set_update(MAILSTREAM * stream)
2045 struct vm_state *vms;
2046 char *mailbox;
2047 char *user;
2049 mailbox = stream->mailbox;
2050 user = get_user_by_mailbox(mailbox);
2051 vms = get_vm_state_by_imapuser(user, 0);
2052 if (vms) {
2053 if (option_debug > 2)
2054 ast_log (LOG_DEBUG, "User %s mailbox set for update.\n",user);
2055 vms->updated = 1; /* set updated flag since mailbox changed */
2056 } else {
2057 if (option_debug > 2)
2058 ast_log (LOG_WARNING, "User %s mailbox not found for update.\n",user);
2062 static void init_vm_state(struct vm_state *vms)
2064 int x;
2065 vms->vmArrayIndex = 0;
2066 for (x = 0; x < 256; x++) {
2067 vms->msgArray[x] = 0;
2069 ast_mutex_init(&vms->lock);
2072 static void copy_msgArray(struct vm_state *dst, struct vm_state *src)
2074 int x;
2075 for (x = 0; x<256; x++) {
2076 dst->msgArray[x] = src->msgArray[x];
2080 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format)
2082 char *body_content;
2083 char *body_decoded;
2084 unsigned long len;
2085 unsigned long newlen;
2086 char filename[256];
2088 if (!body || body == NIL)
2089 return -1;
2090 body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
2091 if (body_content != NIL) {
2092 snprintf(filename, sizeof(filename), "%s.%s", vms->fn, format);
2093 /* ast_log (LOG_DEBUG,body_content); */
2094 body_decoded = rfc822_base64 ((unsigned char *)body_content, len, &newlen);
2095 write_file (filename, (char *) body_decoded, newlen);
2097 return 0;
2100 /* get delimiter via mm_list callback */
2101 static void get_mailbox_delimiter(MAILSTREAM *stream) {
2102 char tmp[50];
2103 snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
2104 mail_list(stream, tmp, "*");
2107 /* Check Quota for user */
2108 static void check_quota(struct vm_state *vms, char *mailbox) {
2109 mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
2110 if (option_debug > 2)
2111 ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mailbox);
2112 if (vms && vms->mailstream != NULL) {
2113 imap_getquotaroot(vms->mailstream, mailbox);
2114 } else {
2115 ast_log(LOG_WARNING,"Mailstream not available for mailbox: %s\n",mailbox);
2118 #endif
2120 /* only return failure if ast_lock_path returns 'timeout',
2121 not if the path does not exist or any other reason
2123 static int vm_lock_path(const char *path)
2125 switch (ast_lock_path(path)) {
2126 case AST_LOCK_TIMEOUT:
2127 return -1;
2128 default:
2129 return 0;
2134 #ifdef ODBC_STORAGE
2135 struct generic_prepare_struct {
2136 char *sql;
2137 int argc;
2138 char **argv;
2141 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
2143 struct generic_prepare_struct *gps = data;
2144 int res, i;
2145 SQLHSTMT stmt;
2147 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2148 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2149 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2150 return NULL;
2152 res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
2153 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2154 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
2155 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2156 return NULL;
2158 for (i = 0; i < gps->argc; i++)
2159 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
2161 return stmt;
2164 static int retrieve_file(char *dir, int msgnum)
2166 int x = 0;
2167 int res;
2168 int fd=-1;
2169 size_t fdlen = 0;
2170 void *fdm = MAP_FAILED;
2171 SQLSMALLINT colcount=0;
2172 SQLHSTMT stmt;
2173 char sql[PATH_MAX];
2174 char fmt[80]="";
2175 char *c;
2176 char coltitle[256];
2177 SQLSMALLINT collen;
2178 SQLSMALLINT datatype;
2179 SQLSMALLINT decimaldigits;
2180 SQLSMALLINT nullable;
2181 SQLULEN colsize;
2182 SQLLEN colsize2;
2183 FILE *f=NULL;
2184 char rowdata[80];
2185 char fn[PATH_MAX];
2186 char full_fn[PATH_MAX];
2187 char msgnums[80];
2188 char *argv[] = { dir, msgnums };
2189 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
2191 struct odbc_obj *obj;
2192 obj = ast_odbc_request_obj(odbc_database, 0);
2193 if (obj) {
2194 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2195 c = strchr(fmt, '|');
2196 if (c)
2197 *c = '\0';
2198 if (!strcasecmp(fmt, "wav49"))
2199 strcpy(fmt, "WAV");
2200 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
2201 if (msgnum > -1)
2202 make_file(fn, sizeof(fn), dir, msgnum);
2203 else
2204 ast_copy_string(fn, dir, sizeof(fn));
2205 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
2207 if (!(f = fopen(full_fn, "w+"))) {
2208 ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
2209 goto yuck;
2212 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
2213 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
2214 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2215 if (!stmt) {
2216 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2217 ast_odbc_release_obj(obj);
2218 goto yuck;
2220 res = SQLFetch(stmt);
2221 if (res == SQL_NO_DATA) {
2222 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2223 ast_odbc_release_obj(obj);
2224 goto yuck;
2226 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2227 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2228 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2229 ast_odbc_release_obj(obj);
2230 goto yuck;
2232 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
2233 if (fd < 0) {
2234 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
2235 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2236 ast_odbc_release_obj(obj);
2237 goto yuck;
2239 res = SQLNumResultCols(stmt, &colcount);
2240 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2241 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
2242 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2243 ast_odbc_release_obj(obj);
2244 goto yuck;
2246 if (f)
2247 fprintf(f, "[message]\n");
2248 for (x=0;x<colcount;x++) {
2249 rowdata[0] = '\0';
2250 collen = sizeof(coltitle);
2251 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
2252 &datatype, &colsize, &decimaldigits, &nullable);
2253 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2254 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
2255 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2256 ast_odbc_release_obj(obj);
2257 goto yuck;
2259 if (!strcasecmp(coltitle, "recording")) {
2260 off_t offset;
2261 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
2262 fdlen = colsize2;
2263 if (fd > -1) {
2264 char tmp[1]="";
2265 lseek(fd, fdlen - 1, SEEK_SET);
2266 if (write(fd, tmp, 1) != 1) {
2267 close(fd);
2268 fd = -1;
2269 continue;
2271 /* Read out in small chunks */
2272 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
2273 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
2274 ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
2275 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2276 ast_odbc_release_obj(obj);
2277 goto yuck;
2278 } else {
2279 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
2280 munmap(fdm, CHUNKSIZE);
2281 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2282 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2283 unlink(full_fn);
2284 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2285 ast_odbc_release_obj(obj);
2286 goto yuck;
2290 truncate(full_fn, fdlen);
2292 } else {
2293 SQLLEN ind;
2294 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &ind);
2295 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2296 SQLINTEGER nativeerror = 0;
2297 SQLSMALLINT diagbytes = 0;
2298 unsigned char state[10], diagnostic[256];
2299 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
2300 ast_log(LOG_WARNING, "SQL Get Data error: %s: %s!\n[%s]\n\n", state, diagnostic, sql);
2301 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2302 ast_odbc_release_obj(obj);
2303 goto yuck;
2305 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
2306 fprintf(f, "%s=%s\n", coltitle, rowdata);
2309 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2310 ast_odbc_release_obj(obj);
2311 } else
2312 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2313 yuck:
2314 if (f)
2315 fclose(f);
2316 if (fd > -1)
2317 close(fd);
2318 return x - 1;
2321 static int last_message_index(struct ast_vm_user *vmu, char *dir)
2323 int x = 0;
2324 int res;
2325 SQLHSTMT stmt;
2326 char sql[PATH_MAX];
2327 char rowdata[20];
2328 char *argv[] = { dir };
2329 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
2331 struct odbc_obj *obj;
2332 obj = ast_odbc_request_obj(odbc_database, 0);
2333 if (obj) {
2334 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
2335 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2336 if (!stmt) {
2337 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2338 ast_odbc_release_obj(obj);
2339 goto yuck;
2341 res = SQLFetch(stmt);
2342 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2343 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2344 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2345 ast_odbc_release_obj(obj);
2346 goto yuck;
2348 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2349 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2350 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2351 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2352 ast_odbc_release_obj(obj);
2353 goto yuck;
2355 if (sscanf(rowdata, "%d", &x) != 1)
2356 ast_log(LOG_WARNING, "Failed to read message count!\n");
2357 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2358 ast_odbc_release_obj(obj);
2359 } else
2360 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2361 yuck:
2362 return x - 1;
2365 static int message_exists(char *dir, int msgnum)
2367 int x = 0;
2368 int res;
2369 SQLHSTMT stmt;
2370 char sql[PATH_MAX];
2371 char rowdata[20];
2372 char msgnums[20];
2373 char *argv[] = { dir, msgnums };
2374 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
2376 struct odbc_obj *obj;
2377 obj = ast_odbc_request_obj(odbc_database, 0);
2378 if (obj) {
2379 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
2380 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
2381 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2382 if (!stmt) {
2383 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2384 ast_odbc_release_obj(obj);
2385 goto yuck;
2387 res = SQLFetch(stmt);
2388 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2389 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2390 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2391 ast_odbc_release_obj(obj);
2392 goto yuck;
2394 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2395 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2396 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2397 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2398 ast_odbc_release_obj(obj);
2399 goto yuck;
2401 if (sscanf(rowdata, "%d", &x) != 1)
2402 ast_log(LOG_WARNING, "Failed to read message count!\n");
2403 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2404 ast_odbc_release_obj(obj);
2405 } else
2406 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2407 yuck:
2408 return x;
2411 static int count_messages(struct ast_vm_user *vmu, char *dir)
2413 return last_message_index(vmu, dir) + 1;
2416 static void delete_file(char *sdir, int smsg)
2418 SQLHSTMT stmt;
2419 char sql[PATH_MAX];
2420 char msgnums[20];
2421 char *argv[] = { sdir, msgnums };
2422 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
2424 struct odbc_obj *obj;
2425 obj = ast_odbc_request_obj(odbc_database, 0);
2426 if (obj) {
2427 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
2428 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
2429 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2430 if (!stmt)
2431 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2432 else
2433 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2434 ast_odbc_release_obj(obj);
2435 } else
2436 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2437 return;
2440 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
2442 SQLHSTMT stmt;
2443 char sql[512];
2444 char msgnums[20];
2445 char msgnumd[20];
2446 struct odbc_obj *obj;
2447 char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
2448 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
2450 delete_file(ddir, dmsg);
2451 obj = ast_odbc_request_obj(odbc_database, 0);
2452 if (obj) {
2453 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
2454 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
2455 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir, msgnum, context, macrocontext, callerid, origtime, duration, recording, mailboxuser, mailboxcontext) SELECT ?,?,context,macrocontext,callerid,origtime,duration,recording,?,? FROM %s WHERE dir=? AND msgnum=?",odbc_table,odbc_table);
2456 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2457 if (!stmt)
2458 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
2459 else
2460 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2461 ast_odbc_release_obj(obj);
2462 } else
2463 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2464 return;
2467 struct insert_cb_struct {
2468 char *dir;
2469 char *msgnum;
2470 void *recording;
2471 size_t recordinglen;
2472 SQLLEN indlen;
2473 const char *context;
2474 const char *macrocontext;
2475 const char *callerid;
2476 const char *origtime;
2477 const char *duration;
2478 char *mailboxuser;
2479 char *mailboxcontext;
2480 const char *category;
2481 char *sql;
2484 static SQLHSTMT insert_cb(struct odbc_obj *obj, void *vd)
2486 struct insert_cb_struct *d = vd;
2487 int res;
2488 SQLHSTMT stmt;
2490 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2491 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2492 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2493 return NULL;
2496 res = SQLPrepare(stmt, (unsigned char *)d->sql, SQL_NTS);
2497 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2498 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", d->sql);
2499 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2500 return NULL;
2503 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->dir), 0, (void *)d->dir, 0, NULL);
2504 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->msgnum), 0, (void *)d->msgnum, 0, NULL);
2505 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, d->recordinglen, 0, (void *)d->recording, 0, &d->indlen);
2506 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->context), 0, (void *)d->context, 0, NULL);
2507 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->macrocontext), 0, (void *)d->macrocontext, 0, NULL);
2508 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->callerid), 0, (void *)d->callerid, 0, NULL);
2509 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->origtime), 0, (void *)d->origtime, 0, NULL);
2510 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->duration), 0, (void *)d->duration, 0, NULL);
2511 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->mailboxuser), 0, (void *)d->mailboxuser, 0, NULL);
2512 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->mailboxcontext), 0, (void *)d->mailboxcontext, 0, NULL);
2513 if (!ast_strlen_zero(d->category)) {
2514 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->category), 0, (void *)d->category, 0, NULL);
2517 return stmt;
2520 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
2522 int x = 0;
2523 int fd = -1;
2524 void *fdm = MAP_FAILED;
2525 size_t fdlen = -1;
2526 SQLHSTMT stmt;
2527 char sql[PATH_MAX];
2528 char msgnums[20];
2529 char fn[PATH_MAX];
2530 char full_fn[PATH_MAX];
2531 char fmt[80]="";
2532 char *c;
2533 struct insert_cb_struct d = {
2534 .dir = dir,
2535 .msgnum = msgnums,
2536 .context = "",
2537 .macrocontext = "",
2538 .callerid = "",
2539 .origtime = "",
2540 .duration = "",
2541 .mailboxuser = mailboxuser,
2542 .mailboxcontext = mailboxcontext,
2543 .category = "",
2544 .sql = sql
2546 struct ast_config *cfg=NULL;
2547 struct odbc_obj *obj;
2549 delete_file(dir, msgnum);
2550 obj = ast_odbc_request_obj(odbc_database, 0);
2551 if (obj) {
2552 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2553 c = strchr(fmt, '|');
2554 if (c)
2555 *c = '\0';
2556 if (!strcasecmp(fmt, "wav49"))
2557 strcpy(fmt, "WAV");
2558 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
2559 if (msgnum > -1)
2560 make_file(fn, sizeof(fn), dir, msgnum);
2561 else
2562 ast_copy_string(fn, dir, sizeof(fn));
2563 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
2564 cfg = ast_config_load(full_fn);
2565 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
2566 fd = open(full_fn, O_RDWR);
2567 if (fd < 0) {
2568 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
2569 ast_odbc_release_obj(obj);
2570 goto yuck;
2572 if (cfg) {
2573 d.context = ast_variable_retrieve(cfg, "message", "context");
2574 if (!d.context) d.context = "";
2575 d.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
2576 if (!d.macrocontext) d.macrocontext = "";
2577 d.callerid = ast_variable_retrieve(cfg, "message", "callerid");
2578 if (!d.callerid) d.callerid = "";
2579 d.origtime = ast_variable_retrieve(cfg, "message", "origtime");
2580 if (!d.origtime) d.origtime = "";
2581 d.duration = ast_variable_retrieve(cfg, "message", "duration");
2582 if (!d.duration) d.duration = "";
2583 d.category = ast_variable_retrieve(cfg, "message", "category");
2584 if (!d.category) d.category = "";
2586 fdlen = lseek(fd, 0, SEEK_END);
2587 lseek(fd, 0, SEEK_SET);
2588 printf("Length is %zd\n", fdlen);
2589 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
2590 if (fdm == MAP_FAILED) {
2591 ast_log(LOG_WARNING, "Memory map failed!\n");
2592 ast_odbc_release_obj(obj);
2593 goto yuck;
2595 d.recording = fdm;
2596 d.recordinglen = d.indlen = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
2597 if (!ast_strlen_zero(d.category))
2598 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
2599 else
2600 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?, ? , ?,?,?,?,?,?,?)",odbc_table);
2601 stmt = ast_odbc_prepare_and_execute(obj, insert_cb, &d);
2602 if (stmt) {
2603 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2605 ast_odbc_release_obj(obj);
2606 } else
2607 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2608 yuck:
2609 if (cfg)
2610 ast_config_destroy(cfg);
2611 if (fdm != MAP_FAILED)
2612 munmap(fdm, fdlen);
2613 if (fd > -1)
2614 close(fd);
2615 return x;
2618 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
2620 SQLHSTMT stmt;
2621 char sql[PATH_MAX];
2622 char msgnums[20];
2623 char msgnumd[20];
2624 struct odbc_obj *obj;
2625 char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
2626 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
2628 delete_file(ddir, dmsg);
2629 obj = ast_odbc_request_obj(odbc_database, 0);
2630 if (obj) {
2631 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
2632 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
2633 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
2634 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2635 if (!stmt)
2636 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2637 else
2638 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2639 ast_odbc_release_obj(obj);
2640 } else
2641 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2642 return;
2645 #else
2646 #ifndef IMAP_STORAGE
2647 static int count_messages(struct ast_vm_user *vmu, char *dir)
2649 /* Find all .txt files - even if they are not in sequence from 0000 */
2651 int vmcount = 0;
2652 DIR *vmdir = NULL;
2653 struct dirent *vment = NULL;
2655 if (vm_lock_path(dir))
2656 return ERROR_LOCK_PATH;
2658 if ((vmdir = opendir(dir))) {
2659 while ((vment = readdir(vmdir))) {
2660 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
2661 vmcount++;
2663 closedir(vmdir);
2665 ast_unlock_path(dir);
2667 return vmcount;
2670 static void rename_file(char *sfn, char *dfn)
2672 char stxt[PATH_MAX];
2673 char dtxt[PATH_MAX];
2674 ast_filerename(sfn,dfn,NULL);
2675 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
2676 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
2677 rename(stxt, dtxt);
2679 #endif
2682 * A negative return value indicates an error.
2684 #if (!defined(IMAP_STORAGE) && !defined(ODBC_STORAGE))
2685 static int last_message_index(struct ast_vm_user *vmu, char *dir)
2687 int x;
2688 char fn[PATH_MAX];
2690 if (vm_lock_path(dir))
2691 return ERROR_LOCK_PATH;
2693 for (x = 0; x < vmu->maxmsg; x++) {
2694 make_file(fn, sizeof(fn), dir, x);
2695 if (ast_fileexists(fn, NULL, NULL) < 1)
2696 break;
2698 ast_unlock_path(dir);
2700 return x - 1;
2702 #endif
2703 #endif
2704 #if (defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2705 static int remove_file(char *dir, int msgnum)
2707 char fn[PATH_MAX];
2708 char full_fn[PATH_MAX];
2709 char msgnums[80];
2711 if (msgnum > -1) {
2712 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
2713 make_file(fn, sizeof(fn), dir, msgnum);
2714 } else {
2715 #ifndef IMAP_STORAGE
2716 ast_copy_string(fn, dir, sizeof(fn));
2717 #else
2718 /*IMAP stores greetings locally so it should not
2719 * try to dispose of them
2721 return 0;
2722 #endif
2724 ast_filedelete(fn, NULL);
2725 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
2726 unlink(full_fn);
2727 return 0;
2729 #endif
2731 static int copy(char *infile, char *outfile)
2733 int ifd;
2734 int ofd;
2735 int res;
2736 int len;
2737 char buf[4096];
2739 #ifdef HARDLINK_WHEN_POSSIBLE
2740 /* Hard link if possible; saves disk space & is faster */
2741 if (link(infile, outfile)) {
2742 #endif
2743 if ((ifd = open(infile, O_RDONLY)) < 0) {
2744 ast_log(LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
2745 return -1;
2747 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
2748 ast_log(LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
2749 close(ifd);
2750 return -1;
2752 do {
2753 len = read(ifd, buf, sizeof(buf));
2754 if (len < 0) {
2755 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
2756 close(ifd);
2757 close(ofd);
2758 unlink(outfile);
2760 if (len) {
2761 res = write(ofd, buf, len);
2762 if (errno == ENOMEM || errno == ENOSPC || res != len) {
2763 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
2764 close(ifd);
2765 close(ofd);
2766 unlink(outfile);
2769 } while (len);
2770 close(ifd);
2771 close(ofd);
2772 return 0;
2773 #ifdef HARDLINK_WHEN_POSSIBLE
2774 } else {
2775 /* Hard link succeeded */
2776 return 0;
2778 #endif
2781 static void copy_plain_file(char *frompath, char *topath)
2783 char frompath2[PATH_MAX], topath2[PATH_MAX];
2784 ast_filecopy(frompath, topath, NULL);
2785 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
2786 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
2787 copy(frompath2, topath2);
2790 static int vm_delete(char *file)
2792 char *txt;
2793 int txtsize = 0;
2795 txtsize = (strlen(file) + 5)*sizeof(char);
2796 txt = alloca(txtsize);
2797 /* Sprintf here would safe because we alloca'd exactly the right length,
2798 * but trying to eliminate all sprintf's anyhow
2800 snprintf(txt, txtsize, "%s.txt", file);
2801 unlink(txt);
2802 return ast_filedelete(file, NULL);
2805 static int inbuf(struct baseio *bio, FILE *fi)
2807 int l;
2809 if (bio->ateof)
2810 return 0;
2812 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
2813 if (ferror(fi))
2814 return -1;
2816 bio->ateof = 1;
2817 return 0;
2820 bio->iolen= l;
2821 bio->iocp= 0;
2823 return 1;
2826 static int inchar(struct baseio *bio, FILE *fi)
2828 if (bio->iocp>=bio->iolen) {
2829 if (!inbuf(bio, fi))
2830 return EOF;
2833 return bio->iobuf[bio->iocp++];
2836 static int ochar(struct baseio *bio, int c, FILE *so)
2838 if (bio->linelength>=BASELINELEN) {
2839 if (fputs(eol,so)==EOF)
2840 return -1;
2842 bio->linelength= 0;
2845 if (putc(((unsigned char)c),so)==EOF)
2846 return -1;
2848 bio->linelength++;
2850 return 1;
2853 static int base_encode(char *filename, FILE *so)
2855 unsigned char dtable[BASEMAXINLINE];
2856 int i,hiteof= 0;
2857 FILE *fi;
2858 struct baseio bio;
2860 memset(&bio, 0, sizeof(bio));
2861 bio.iocp = BASEMAXINLINE;
2863 if (!(fi = fopen(filename, "rb"))) {
2864 ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
2865 return -1;
2868 for (i= 0;i<9;i++) {
2869 dtable[i]= 'A'+i;
2870 dtable[i+9]= 'J'+i;
2871 dtable[26+i]= 'a'+i;
2872 dtable[26+i+9]= 'j'+i;
2874 for (i= 0;i<8;i++) {
2875 dtable[i+18]= 'S'+i;
2876 dtable[26+i+18]= 's'+i;
2878 for (i= 0;i<10;i++) {
2879 dtable[52+i]= '0'+i;
2881 dtable[62]= '+';
2882 dtable[63]= '/';
2884 while (!hiteof){
2885 unsigned char igroup[3],ogroup[4];
2886 int c,n;
2888 igroup[0]= igroup[1]= igroup[2]= 0;
2890 for (n= 0;n<3;n++) {
2891 if ((c = inchar(&bio, fi)) == EOF) {
2892 hiteof= 1;
2893 break;
2896 igroup[n]= (unsigned char)c;
2899 if (n> 0) {
2900 ogroup[0]= dtable[igroup[0]>>2];
2901 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
2902 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
2903 ogroup[3]= dtable[igroup[2]&0x3F];
2905 if (n<3) {
2906 ogroup[3]= '=';
2908 if (n<2)
2909 ogroup[2]= '=';
2912 for (i= 0;i<4;i++)
2913 ochar(&bio, ogroup[i], so);
2917 fclose(fi);
2919 if (fputs(eol,so)==EOF)
2920 return 0;
2922 return 1;
2925 static void prep_email_sub_vars(struct ast_channel *ast, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *dur, char *date, char *passdata, size_t passdatasize, const char *category)
2927 char callerid[256];
2928 /* Prepare variables for substition in email body and subject */
2929 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
2930 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
2931 snprintf(passdata, passdatasize, "%d", msgnum);
2932 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
2933 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
2934 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
2935 pbx_builtin_setvar_helper(ast, "VM_CALLERID", (!ast_strlen_zero(cidname) || !ast_strlen_zero(cidnum)) ?
2936 ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, NULL) : "an unknown caller");
2937 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (!ast_strlen_zero(cidname) ? cidname : "an unknown caller"));
2938 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (!ast_strlen_zero(cidnum) ? cidnum : "an unknown caller"));
2939 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
2940 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
2943 static char *quote(const char *from, char *to, size_t len)
2945 char *ptr = to;
2946 *ptr++ = '"';
2947 for (; ptr < to + len - 1; from++) {
2948 if (*from == '"')
2949 *ptr++ = '\\';
2950 else if (*from == '\0')
2951 break;
2952 *ptr++ = *from;
2954 if (ptr < to + len - 1)
2955 *ptr++ = '"';
2956 *ptr = '\0';
2957 return to;
2960 * fill in *tm for current time according to the proper timezone, if any.
2961 * Return tm so it can be used as a function argument.
2963 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
2965 const struct vm_zone *z = NULL;
2966 time_t t = time(NULL);
2968 /* Does this user have a timezone specified? */
2969 if (!ast_strlen_zero(vmu->zonetag)) {
2970 /* Find the zone in the list */
2971 AST_LIST_LOCK(&zones);
2972 AST_LIST_TRAVERSE(&zones, z, list) {
2973 if (!strcmp(z->name, vmu->zonetag))
2974 break;
2976 AST_LIST_UNLOCK(&zones);
2978 ast_localtime(&t, tm, z ? z->timezone : NULL);
2979 return tm;
2982 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap)
2984 char date[256];
2985 char host[MAXHOSTNAMELEN] = "";
2986 char who[256];
2987 char bound[256];
2988 char fname[256];
2989 char dur[256];
2990 char tmpcmd[256];
2991 char enc_cidnum[256] = "", enc_cidname[256] = "";
2992 struct tm tm;
2993 char *passdata2;
2994 size_t len_passdata;
2995 #ifdef IMAP_STORAGE
2996 #define ENDL "\r\n"
2997 #else
2998 #define ENDL "\n"
2999 #endif
3001 if (cidnum) {
3002 strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
3004 if (cidname) {
3005 strip_control(cidname, enc_cidname, sizeof(enc_cidname));
3007 gethostname(host, sizeof(host) - 1);
3008 if (strchr(srcemail, '@'))
3009 ast_copy_string(who, srcemail, sizeof(who));
3010 else {
3011 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
3013 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
3014 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
3015 fprintf(p, "Date: %s" ENDL, date);
3017 /* Set date format for voicemail mail */
3018 strftime(date, sizeof(date), emaildateformat, &tm);
3020 if (*fromstring) {
3021 struct ast_channel *ast;
3022 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
3023 char *passdata;
3024 int vmlen = strlen(fromstring)*3 + 200;
3025 if ((passdata = alloca(vmlen))) {
3026 memset(passdata, 0, vmlen);
3027 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, enc_cidnum, enc_cidname, dur, date, passdata, vmlen, category);
3028 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
3029 len_passdata = strlen(passdata) * 2 + 3;
3030 passdata2 = alloca(len_passdata);
3031 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
3032 } else
3033 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
3034 ast_channel_free(ast);
3035 } else
3036 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
3037 } else
3038 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
3039 len_passdata = strlen(vmu->fullname) * 2 + 3;
3040 passdata2 = alloca(len_passdata);
3041 fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
3042 if (emailsubject) {
3043 struct ast_channel *ast;
3044 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
3045 char *passdata;
3046 int vmlen = strlen(emailsubject)*3 + 200;
3047 if ((passdata = alloca(vmlen))) {
3048 memset(passdata, 0, vmlen);
3049 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
3050 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
3051 fprintf(p, "Subject: %s" ENDL, passdata);
3052 } else {
3053 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
3055 ast_channel_free(ast);
3056 } else {
3057 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
3059 } else if (*emailtitle) {
3060 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
3061 fprintf(p, ENDL) ;
3062 } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
3063 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
3064 } else {
3065 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
3067 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
3068 if (imap) {
3069 /* additional information needed for IMAP searching */
3070 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
3071 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
3072 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
3073 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
3074 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
3075 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
3076 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
3077 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
3078 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
3079 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
3080 if (!ast_strlen_zero(category)) {
3081 fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
3083 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
3084 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
3086 if (!ast_strlen_zero(cidnum)) {
3087 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
3089 if (!ast_strlen_zero(cidname)) {
3090 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
3092 fprintf(p, "MIME-Version: 1.0" ENDL);
3093 if (attach_user_voicemail) {
3094 /* Something unique. */
3095 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
3097 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
3098 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
3099 fprintf(p, "--%s" ENDL, bound);
3101 fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
3102 if (emailbody) {
3103 struct ast_channel *ast;
3104 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
3105 char *passdata;
3106 int vmlen = strlen(emailbody)*3 + 200;
3107 if ((passdata = alloca(vmlen))) {
3108 memset(passdata, 0, vmlen);
3109 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
3110 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
3111 fprintf(p, "%s" ENDL, passdata);
3112 } else
3113 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
3114 ast_channel_free(ast);
3115 } else
3116 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
3117 } else {
3118 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
3120 "in mailbox %s from %s, on %s so you might" ENDL
3121 "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
3122 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
3124 if (attach_user_voicemail) {
3125 /* Eww. We want formats to tell us their own MIME type */
3126 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
3127 char tmpdir[256], newtmp[256];
3128 int tmpfd = -1;
3130 if (vmu->volgain < -.001 || vmu->volgain > .001) {
3131 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
3132 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
3133 tmpfd = mkstemp(newtmp);
3134 chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
3135 if (option_debug > 2)
3136 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
3137 if (tmpfd > -1) {
3138 int soxstatus;
3139 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
3140 if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
3141 attach = newtmp;
3142 if (option_debug > 2) {
3143 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
3145 } else {
3146 ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
3147 soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
3148 ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
3152 fprintf(p, "--%s" ENDL, bound);
3153 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
3154 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
3155 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
3156 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
3157 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
3158 base_encode(fname, p);
3159 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
3160 if (tmpfd > -1) {
3161 unlink(fname);
3162 close(tmpfd);
3163 unlink(newtmp);
3166 #undef ENDL
3168 static int sendmail(char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category)
3170 FILE *p=NULL;
3171 char tmp[80] = "/tmp/astmail-XXXXXX";
3172 char tmp2[256];
3174 if (vmu && ast_strlen_zero(vmu->email)) {
3175 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
3176 return(0);
3178 if (!strcmp(format, "wav49"))
3179 format = "WAV";
3180 if (option_debug > 2)
3181 ast_log(LOG_DEBUG, "Attaching file '%s', format '%s', uservm is '%d', global is %d\n", attach, format, attach_user_voicemail, ast_test_flag((&globalflags), VM_ATTACH));
3182 /* Make a temporary file instead of piping directly to sendmail, in case the mail
3183 command hangs */
3184 if ((p = vm_mkftemp(tmp)) == NULL) {
3185 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
3186 return -1;
3187 } else {
3188 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
3189 fclose(p);
3190 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
3191 ast_safe_system(tmp2);
3192 if (option_debug > 2)
3193 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
3195 return 0;
3198 static int sendpage(char *srcemail, char *pager, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, int duration, struct ast_vm_user *vmu, const char *category)
3200 char date[256];
3201 char host[MAXHOSTNAMELEN] = "";
3202 char who[256];
3203 char dur[PATH_MAX];
3204 char tmp[80] = "/tmp/astmail-XXXXXX";
3205 char tmp2[PATH_MAX];
3206 struct tm tm;
3207 FILE *p;
3209 if ((p = vm_mkftemp(tmp)) == NULL) {
3210 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
3211 return -1;
3212 } else {
3213 gethostname(host, sizeof(host)-1);
3214 if (strchr(srcemail, '@'))
3215 ast_copy_string(who, srcemail, sizeof(who));
3216 else {
3217 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
3219 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
3220 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
3221 fprintf(p, "Date: %s\n", date);
3223 if (*pagerfromstring) {
3224 struct ast_channel *ast;
3225 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
3226 char *passdata;
3227 int vmlen = strlen(fromstring)*3 + 200;
3228 if ((passdata = alloca(vmlen))) {
3229 memset(passdata, 0, vmlen);
3230 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
3231 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
3232 fprintf(p, "From: %s <%s>\n", passdata, who);
3233 } else
3234 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
3235 ast_channel_free(ast);
3236 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
3237 } else
3238 fprintf(p, "From: Asterisk PBX <%s>\n", who);
3239 fprintf(p, "To: %s\n", pager);
3240 if (pagersubject) {
3241 struct ast_channel *ast;
3242 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
3243 char *passdata;
3244 int vmlen = strlen(pagersubject) * 3 + 200;
3245 if ((passdata = alloca(vmlen))) {
3246 memset(passdata, 0, vmlen);
3247 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
3248 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
3249 fprintf(p, "Subject: %s\n\n", passdata);
3250 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
3251 ast_channel_free(ast);
3252 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
3253 } else
3254 fprintf(p, "Subject: New VM\n\n");
3255 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
3256 if (pagerbody) {
3257 struct ast_channel *ast;
3258 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
3259 char *passdata;
3260 int vmlen = strlen(pagerbody)*3 + 200;
3261 if ((passdata = alloca(vmlen))) {
3262 memset(passdata, 0, vmlen);
3263 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
3264 pbx_substitute_variables_helper(ast, pagerbody, passdata, vmlen);
3265 fprintf(p, "%s\n", passdata);
3266 } else ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
3267 ast_channel_free(ast);
3268 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
3269 } else {
3270 fprintf(p, "New %s long msg in box %s\n"
3271 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
3273 fclose(p);
3274 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
3275 ast_safe_system(tmp2);
3276 if (option_debug > 2)
3277 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
3279 return 0;
3282 static int get_date(char *s, int len)
3284 struct tm tm;
3285 time_t t;
3287 time(&t);
3289 ast_localtime(&t, &tm, NULL);
3291 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
3294 static int play_greeting(struct ast_channel *chan, struct ast_vm_user *vmu, char *filename, char *ecodes)
3296 int res = -2;
3298 #ifdef ODBC_STORAGE
3299 int success =
3300 #endif
3301 RETRIEVE(filename, -1, vmu);
3302 if (ast_fileexists(filename, NULL, NULL) > 0) {
3303 res = ast_streamfile(chan, filename, chan->language);
3304 if (res > -1)
3305 res = ast_waitstream(chan, ecodes);
3306 #ifdef ODBC_STORAGE
3307 if (success == -1) {
3308 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
3309 if (option_debug)
3310 ast_log(LOG_DEBUG, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
3311 store_file(filename, vmu->mailbox, vmu->context, -1);
3313 #endif
3315 DISPOSE(filename, -1);
3317 return res;
3320 static int invent_message(struct ast_channel *chan, struct ast_vm_user *vmu, char *ext, int busy, char *ecodes)
3322 int res;
3323 char fn[PATH_MAX];
3324 char dest[PATH_MAX];
3326 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, ext);
3328 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "greet"))) {
3329 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
3330 return -1;
3333 res = play_greeting(chan, vmu, fn, ecodes);
3334 if (res == -2) {
3335 /* File did not exist */
3336 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
3337 if (res)
3338 return res;
3339 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
3342 if (res)
3343 return res;
3345 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
3346 return res;
3349 static void free_zone(struct vm_zone *z)
3351 free(z);
3354 #ifdef ODBC_STORAGE
3355 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
3356 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
3358 int x = -1;
3359 int res;
3360 SQLHSTMT stmt;
3361 char sql[PATH_MAX];
3362 char rowdata[20];
3363 char tmp[PATH_MAX] = "";
3364 struct odbc_obj *obj;
3365 char *context;
3366 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
3368 if (newmsgs)
3369 *newmsgs = 0;
3370 if (oldmsgs)
3371 *oldmsgs = 0;
3373 /* If no mailbox, return immediately */
3374 if (ast_strlen_zero(mailbox))
3375 return 0;
3377 ast_copy_string(tmp, mailbox, sizeof(tmp));
3379 context = strchr(tmp, '@');
3380 if (context) {
3381 *context = '\0';
3382 context++;
3383 } else
3384 context = "default";
3386 obj = ast_odbc_request_obj(odbc_database, 0);
3387 if (obj) {
3388 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
3389 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3390 if (!stmt) {
3391 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3392 ast_odbc_release_obj(obj);
3393 goto yuck;
3395 res = SQLFetch(stmt);
3396 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3397 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3398 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3399 ast_odbc_release_obj(obj);
3400 goto yuck;
3402 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3403 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3404 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3405 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3406 ast_odbc_release_obj(obj);
3407 goto yuck;
3409 *newmsgs = atoi(rowdata);
3410 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3412 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
3413 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3414 if (!stmt) {
3415 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3416 ast_odbc_release_obj(obj);
3417 goto yuck;
3419 res = SQLFetch(stmt);
3420 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3421 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3422 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3423 ast_odbc_release_obj(obj);
3424 goto yuck;
3426 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3427 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3428 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3429 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3430 ast_odbc_release_obj(obj);
3431 goto yuck;
3433 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3434 ast_odbc_release_obj(obj);
3435 *oldmsgs = atoi(rowdata);
3436 x = 0;
3437 } else
3438 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3440 yuck:
3441 return x;
3444 static int messagecount(const char *context, const char *mailbox, const char *folder)
3446 struct odbc_obj *obj = NULL;
3447 int nummsgs = 0;
3448 int res;
3449 SQLHSTMT stmt = NULL;
3450 char sql[PATH_MAX];
3451 char rowdata[20];
3452 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
3453 if (!folder)
3454 folder = "INBOX";
3455 /* If no mailbox, return immediately */
3456 if (ast_strlen_zero(mailbox))
3457 return 0;
3459 obj = ast_odbc_request_obj(odbc_database, 0);
3460 if (obj) {
3461 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
3462 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3463 if (!stmt) {
3464 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3465 goto yuck;
3467 res = SQLFetch(stmt);
3468 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3469 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3470 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3471 goto yuck;
3473 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3474 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3475 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3476 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3477 goto yuck;
3479 nummsgs = atoi(rowdata);
3480 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3481 } else
3482 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3484 yuck:
3485 if (obj)
3486 ast_odbc_release_obj(obj);
3487 return nummsgs;
3490 static int has_voicemail(const char *mailbox, const char *folder)
3492 char tmp[256], *tmp2 = tmp, *mbox, *context;
3493 ast_copy_string(tmp, mailbox, sizeof(tmp));
3494 while ((context = mbox = strsep(&tmp2, ","))) {
3495 strsep(&context, "@");
3496 if (ast_strlen_zero(context))
3497 context = "default";
3498 if (messagecount(context, mbox, folder))
3499 return 1;
3501 return 0;
3503 #endif
3504 #ifndef IMAP_STORAGE
3505 /* copy message only used by file storage */
3506 static int copy_message(struct ast_channel *chan, struct ast_vm_user *vmu, int imbox, int msgnum, long duration, struct ast_vm_user *recip, char *fmt, char *dir)
3508 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
3509 const char *frombox = mbox(imbox);
3510 int recipmsgnum;
3512 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
3514 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
3516 if (!dir)
3517 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
3518 else
3519 ast_copy_string(fromdir, dir, sizeof(fromdir));
3521 make_file(frompath, sizeof(frompath), fromdir, msgnum);
3523 if (vm_lock_path(todir))
3524 return ERROR_LOCK_PATH;
3526 recipmsgnum = 0;
3527 do {
3528 make_file(topath, sizeof(topath), todir, recipmsgnum);
3529 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
3530 break;
3531 recipmsgnum++;
3532 } while (recipmsgnum < recip->maxmsg);
3533 if (recipmsgnum < recip->maxmsg) {
3534 if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
3535 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
3536 } else {
3537 /* For ODBC storage, if the file we want to copy isn't yet in the database, then the SQL
3538 * copy will fail. Instead, we need to create a local copy, store it, and delete the local
3539 * copy. We don't have to #ifdef this because if file storage reaches this point, there's a
3540 * much worse problem happening and IMAP storage doesn't call this function
3542 copy_plain_file(frompath, topath);
3543 STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL);
3544 vm_delete(topath);
3546 } else {
3547 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
3549 ast_unlock_path(todir);
3550 notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3552 return 0;
3554 #endif
3555 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
3556 static int messagecount(const char *context, const char *mailbox, const char *folder)
3558 return __has_voicemail(context, mailbox, folder, 0);
3562 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
3564 DIR *dir;
3565 struct dirent *de;
3566 char fn[256];
3567 int ret = 0;
3568 if (!folder)
3569 folder = "INBOX";
3570 /* If no mailbox, return immediately */
3571 if (ast_strlen_zero(mailbox))
3572 return 0;
3573 if (!context)
3574 context = "default";
3575 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
3576 dir = opendir(fn);
3577 if (!dir)
3578 return 0;
3579 while ((de = readdir(dir))) {
3580 if (!strncasecmp(de->d_name, "msg", 3)) {
3581 if (shortcircuit) {
3582 ret = 1;
3583 break;
3584 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
3585 ret++;
3588 closedir(dir);
3589 return ret;
3593 static int has_voicemail(const char *mailbox, const char *folder)
3595 char tmp[256], *tmp2 = tmp, *mbox, *context;
3596 ast_copy_string(tmp, mailbox, sizeof(tmp));
3597 while ((mbox = strsep(&tmp2, ","))) {
3598 if ((context = strchr(mbox, '@')))
3599 *context++ = '\0';
3600 else
3601 context = "default";
3602 if (__has_voicemail(context, mbox, folder, 1))
3603 return 1;
3605 return 0;
3609 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
3611 char tmp[256];
3612 char *context;
3614 if (newmsgs)
3615 *newmsgs = 0;
3616 if (oldmsgs)
3617 *oldmsgs = 0;
3618 /* If no mailbox, return immediately */
3619 if (ast_strlen_zero(mailbox))
3620 return 0;
3621 if (strchr(mailbox, ',')) {
3622 int tmpnew, tmpold;
3623 char *mb, *cur;
3625 ast_copy_string(tmp, mailbox, sizeof(tmp));
3626 mb = tmp;
3627 while ((cur = strsep(&mb, ", "))) {
3628 if (!ast_strlen_zero(cur)) {
3629 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
3630 return -1;
3631 else {
3632 if (newmsgs)
3633 *newmsgs += tmpnew;
3634 if (oldmsgs)
3635 *oldmsgs += tmpold;
3639 return 0;
3641 ast_copy_string(tmp, mailbox, sizeof(tmp));
3642 context = strchr(tmp, '@');
3643 if (context) {
3644 *context = '\0';
3645 context++;
3646 } else
3647 context = "default";
3648 if (newmsgs)
3649 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
3650 if (oldmsgs)
3651 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
3652 return 0;
3655 #endif
3657 static void run_externnotify(char *context, char *extension)
3659 char arguments[255];
3660 char ext_context[256] = "";
3661 int newvoicemails = 0, oldvoicemails = 0;
3662 struct ast_smdi_mwi_message *mwi_msg;
3664 if (!ast_strlen_zero(context))
3665 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
3666 else
3667 ast_copy_string(ext_context, extension, sizeof(ext_context));
3669 if (!strcasecmp(externnotify, "smdi")) {
3670 if (ast_app_has_voicemail(ext_context, NULL))
3671 ast_smdi_mwi_set(smdi_iface, extension);
3672 else
3673 ast_smdi_mwi_unset(smdi_iface, extension);
3675 if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
3676 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
3677 if (!strncmp(mwi_msg->cause, "INV", 3))
3678 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
3679 else if (!strncmp(mwi_msg->cause, "BLK", 3))
3680 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
3681 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
3682 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
3683 } else {
3684 if (option_debug)
3685 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s\n", extension);
3687 } else if (!ast_strlen_zero(externnotify)) {
3688 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
3689 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
3690 } else {
3691 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
3692 if (option_debug)
3693 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
3694 ast_safe_system(arguments);
3699 struct leave_vm_options {
3700 unsigned int flags;
3701 signed char record_gain;
3704 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
3706 #ifdef IMAP_STORAGE
3707 int newmsgs, oldmsgs;
3708 #endif
3709 struct vm_state *vms = NULL;
3710 char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
3711 char callerid[256];
3712 FILE *txt;
3713 char date[256];
3714 int txtdes;
3715 int res = 0;
3716 int msgnum;
3717 int duration = 0;
3718 int ausemacro = 0;
3719 int ousemacro = 0;
3720 int ouseexten = 0;
3721 char dir[PATH_MAX], tmpdir[PATH_MAX];
3722 char dest[PATH_MAX];
3723 char fn[PATH_MAX];
3724 char prefile[PATH_MAX] = "";
3725 char tempfile[PATH_MAX] = "";
3726 char ext_context[256] = "";
3727 char fmt[80];
3728 char *context;
3729 char ecodes[16] = "#";
3730 char tmp[1024] = "", *tmpptr;
3731 struct ast_vm_user *vmu;
3732 struct ast_vm_user svm;
3733 const char *category = NULL;
3735 ast_copy_string(tmp, ext, sizeof(tmp));
3736 ext = tmp;
3737 context = strchr(tmp, '@');
3738 if (context) {
3739 *context++ = '\0';
3740 tmpptr = strchr(context, '&');
3741 } else {
3742 tmpptr = strchr(ext, '&');
3745 if (tmpptr)
3746 *tmpptr++ = '\0';
3748 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3750 if (option_debug > 2)
3751 ast_log(LOG_DEBUG, "Before find_user\n");
3752 if (!(vmu = find_user(&svm, context, ext))) {
3753 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
3754 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
3755 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
3756 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3757 return res;
3759 /* Setup pre-file if appropriate */
3760 if (strcmp(vmu->context, "default"))
3761 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
3762 else
3763 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
3764 if (ast_test_flag(options, OPT_BUSY_GREETING)) {
3765 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
3766 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
3767 } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
3768 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
3769 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
3771 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
3772 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
3773 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
3774 return -1;
3776 RETRIEVE(tempfile, -1, vmu);
3777 if (ast_fileexists(tempfile, NULL, NULL) > 0)
3778 ast_copy_string(prefile, tempfile, sizeof(prefile));
3779 DISPOSE(tempfile, -1);
3780 /* It's easier just to try to make it than to check for its existence */
3781 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
3782 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
3784 /* Check current or macro-calling context for special extensions */
3785 if (ast_test_flag(vmu, VM_OPERATOR)) {
3786 if (!ast_strlen_zero(vmu->exit)) {
3787 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
3788 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3789 ouseexten = 1;
3791 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
3792 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3793 ouseexten = 1;
3795 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
3796 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3797 ousemacro = 1;
3801 if (!ast_strlen_zero(vmu->exit)) {
3802 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
3803 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3804 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
3805 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3806 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
3807 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3808 ausemacro = 1;
3811 /* Play the beginning intro if desired */
3812 if (!ast_strlen_zero(prefile)) {
3813 res = play_greeting(chan, vmu, prefile, ecodes);
3814 if (res == -2) {
3815 /* The file did not exist */
3816 if (option_debug)
3817 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
3818 res = invent_message(chan, vmu, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
3820 if (res < 0) {
3821 if (option_debug)
3822 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
3823 free_user(vmu);
3824 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3825 return -1;
3828 if (res == '#') {
3829 /* On a '#' we skip the instructions */
3830 ast_set_flag(options, OPT_SILENT);
3831 res = 0;
3833 if (!res && !ast_test_flag(options, OPT_SILENT)) {
3834 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
3835 if (res == '#') {
3836 ast_set_flag(options, OPT_SILENT);
3837 res = 0;
3840 if (res > 0)
3841 ast_stopstream(chan);
3842 /* Check for a '*' here in case the caller wants to escape from voicemail to something
3843 other than the operator -- an automated attendant or mailbox login for example */
3844 if (res == '*') {
3845 chan->exten[0] = 'a';
3846 chan->exten[1] = '\0';
3847 if (!ast_strlen_zero(vmu->exit)) {
3848 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3849 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
3850 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3852 chan->priority = 0;
3853 free_user(vmu);
3854 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3855 return 0;
3858 /* Check for a '0' here */
3859 if (res == '0') {
3860 transfer:
3861 if (ouseexten || ousemacro) {
3862 chan->exten[0] = 'o';
3863 chan->exten[1] = '\0';
3864 if (!ast_strlen_zero(vmu->exit)) {
3865 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3866 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3867 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3869 ast_play_and_wait(chan, "transfer");
3870 chan->priority = 0;
3871 free_user(vmu);
3872 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3874 return 0;
3876 if (res < 0) {
3877 free_user(vmu);
3878 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3879 return -1;
3881 /* The meat of recording the message... All the announcements and beeps have been played*/
3882 ast_copy_string(fmt, vmfmts, sizeof(fmt));
3883 if (!ast_strlen_zero(fmt)) {
3884 msgnum = 0;
3886 #ifdef IMAP_STORAGE
3887 /* Is ext a mailbox? */
3888 /* must open stream for this user to get info! */
3889 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
3890 if (res < 0) {
3891 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3892 return -1;
3894 if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
3895 /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
3896 * rarely be used*/
3897 if (!(vms = create_vm_state_from_user(vmu))) {
3898 ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
3899 return -1;
3902 vms->newmessages++;
3903 /* here is a big difference! We add one to it later */
3904 msgnum = newmsgs + oldmsgs;
3905 if (option_debug > 2)
3906 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3907 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3908 /* set variable for compatability */
3909 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3911 /* Check if mailbox is full */
3912 check_quota(vms, imapfolder);
3913 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3914 if (option_debug)
3915 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3916 ast_play_and_wait(chan, "vm-mailboxfull");
3917 return -1;
3919 if (option_debug > 2)
3920 ast_log(LOG_DEBUG, "Checking message number quota - mailbox has %d messages, maximum is set to %d\n",msgnum,vmu->maxmsg);
3921 if (msgnum >= vmu->maxmsg) {
3922 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3923 if (!res)
3924 res = ast_waitstream(chan, "");
3925 ast_log(LOG_WARNING, "No more messages possible\n");
3926 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3927 goto leave_vm_out;
3930 /* Check if we have exceeded maxmsg */
3931 if (msgnum >= vmu->maxmsg) {
3932 ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
3933 ast_play_and_wait(chan, "vm-mailboxfull");
3934 return -1;
3936 #else
3937 if (count_messages(vmu, dir) >= vmu->maxmsg) {
3938 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3939 if (!res)
3940 res = ast_waitstream(chan, "");
3941 ast_log(LOG_WARNING, "No more messages possible\n");
3942 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3943 goto leave_vm_out;
3946 #endif
3947 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3948 txtdes = mkstemp(tmptxtfile);
3949 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
3950 if (txtdes < 0) {
3951 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3952 if (!res)
3953 res = ast_waitstream(chan, "");
3954 ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3955 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3956 goto leave_vm_out;
3959 /* Now play the beep once we have the message number for our next message. */
3960 if (res >= 0) {
3961 /* Unless we're *really* silent, try to send the beep */
3962 res = ast_stream_and_wait(chan, "beep", chan->language, "");
3965 /* Store information */
3966 txt = fdopen(txtdes, "w+");
3967 if (txt) {
3968 get_date(date, sizeof(date));
3969 fprintf(txt,
3970 ";\n"
3971 "; Message Information file\n"
3972 ";\n"
3973 "[message]\n"
3974 "origmailbox=%s\n"
3975 "context=%s\n"
3976 "macrocontext=%s\n"
3977 "exten=%s\n"
3978 "priority=%d\n"
3979 "callerchan=%s\n"
3980 "callerid=%s\n"
3981 "origdate=%s\n"
3982 "origtime=%ld\n"
3983 "category=%s\n",
3984 ext,
3985 chan->context,
3986 chan->macrocontext,
3987 chan->exten,
3988 chan->priority,
3989 chan->name,
3990 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3991 date, (long)time(NULL),
3992 category ? category : "");
3993 } else
3994 ast_log(LOG_WARNING, "Error opening text file for output\n");
3995 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3997 if (txt) {
3998 if (duration < vmminmessage) {
3999 fclose(txt);
4000 if (option_verbose > 2)
4001 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
4002 ast_filedelete(tmptxtfile, NULL);
4003 unlink(tmptxtfile);
4004 } else {
4005 fprintf(txt, "duration=%d\n", duration);
4006 fclose(txt);
4007 if (vm_lock_path(dir)) {
4008 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
4009 /* Delete files */
4010 ast_filedelete(tmptxtfile, NULL);
4011 unlink(tmptxtfile);
4012 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
4013 if (option_debug)
4014 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
4015 unlink(tmptxtfile);
4016 ast_unlock_path(dir);
4017 } else {
4018 for (;;) {
4019 make_file(fn, sizeof(fn), dir, msgnum);
4020 if (!EXISTS(dir, msgnum, fn, NULL))
4021 break;
4022 msgnum++;
4025 /* assign a variable with the name of the voicemail file */
4026 #ifndef IMAP_STORAGE
4027 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
4028 #else
4029 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
4030 #endif
4032 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
4033 ast_filerename(tmptxtfile, fn, NULL);
4034 rename(tmptxtfile, txtfile);
4036 ast_unlock_path(dir);
4037 /* We must store the file first, before copying the message, because
4038 * ODBC storage does the entire copy with SQL.
4040 if (ast_fileexists(fn, NULL, NULL) > 0) {
4041 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
4044 /* Are there to be more recipients of this message? */
4045 while (tmpptr) {
4046 struct ast_vm_user recipu, *recip;
4047 char *exten, *context;
4049 exten = strsep(&tmpptr, "&");
4050 context = strchr(exten, '@');
4051 if (context) {
4052 *context = '\0';
4053 context++;
4055 if ((recip = find_user(&recipu, context, exten))) {
4056 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
4057 free_user(recip);
4060 /* Notification and disposal needs to happen after the copy, though. */
4061 if (ast_fileexists(fn, NULL, NULL)) {
4062 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
4063 DISPOSE(dir, msgnum);
4068 if (res == '0') {
4069 goto transfer;
4070 } else if (res > 0)
4071 res = 0;
4073 if (duration < vmminmessage)
4074 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
4075 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
4076 else
4077 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
4078 } else
4079 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
4080 leave_vm_out:
4081 free_user(vmu);
4083 return res;
4086 #ifndef IMAP_STORAGE
4087 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
4089 /* we know max messages, so stop process when number is hit */
4091 int x,dest;
4092 char sfn[PATH_MAX];
4093 char dfn[PATH_MAX];
4095 if (vm_lock_path(dir))
4096 return ERROR_LOCK_PATH;
4098 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
4099 make_file(sfn, sizeof(sfn), dir, x);
4100 if (EXISTS(dir, x, sfn, NULL)) {
4102 if (x != dest) {
4103 make_file(dfn, sizeof(dfn), dir, dest);
4104 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
4107 dest++;
4110 ast_unlock_path(dir);
4112 return 0;
4114 #endif
4116 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
4118 int d;
4119 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
4120 return d;
4123 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
4125 #ifdef IMAP_STORAGE
4126 /* we must use mbox(x) folder names, and copy the message there */
4127 /* simple. huh? */
4128 char sequence[10];
4129 /* get the real IMAP message number for this message */
4130 snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
4131 if (option_debug > 2)
4132 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,(char *) mbox(box));
4133 if (box == 1) {
4134 mail_setflag(vms->mailstream, sequence, "\\Seen");
4135 } else if (box == 0) {
4136 mail_clearflag(vms->mailstream, sequence, "\\Seen");
4138 if (!strcasecmp(mbox(0), vms->curbox) && (box == 0 || box == 1))
4139 return 0;
4140 else
4141 return !mail_copy(vms->mailstream,sequence,(char *) mbox(box));
4142 #else
4143 char *dir = vms->curdir;
4144 char *username = vms->username;
4145 char *context = vmu->context;
4146 char sfn[PATH_MAX];
4147 char dfn[PATH_MAX];
4148 char ddir[PATH_MAX];
4149 const char *dbox = mbox(box);
4150 int x;
4151 make_file(sfn, sizeof(sfn), dir, msg);
4152 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
4154 if (vm_lock_path(ddir))
4155 return ERROR_LOCK_PATH;
4157 for (x = 0; x < vmu->maxmsg; x++) {
4158 make_file(dfn, sizeof(dfn), ddir, x);
4159 if (!EXISTS(ddir, x, dfn, NULL))
4160 break;
4162 if (x >= vmu->maxmsg) {
4163 ast_unlock_path(ddir);
4164 return ERROR_MAILBOX_FULL;
4166 if (strcmp(sfn, dfn)) {
4167 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
4169 ast_unlock_path(ddir);
4170 #endif
4171 return 0;
4174 static int adsi_logo(unsigned char *buf)
4176 int bytes = 0;
4177 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
4178 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
4179 return bytes;
4182 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
4184 unsigned char buf[256];
4185 int bytes=0;
4186 int x;
4187 char num[5];
4189 *useadsi = 0;
4190 bytes += ast_adsi_data_mode(buf + bytes);
4191 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4193 bytes = 0;
4194 bytes += adsi_logo(buf);
4195 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
4196 #ifdef DISPLAY
4197 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
4198 #endif
4199 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4200 bytes += ast_adsi_data_mode(buf + bytes);
4201 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4203 if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
4204 bytes = 0;
4205 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
4206 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
4207 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4208 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4209 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4210 return 0;
4213 #ifdef DISPLAY
4214 /* Add a dot */
4215 bytes = 0;
4216 bytes += ast_adsi_logo(buf);
4217 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
4218 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
4219 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4220 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4221 #endif
4222 bytes = 0;
4223 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
4224 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
4225 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
4226 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
4227 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
4228 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
4229 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
4231 #ifdef DISPLAY
4232 /* Add another dot */
4233 bytes = 0;
4234 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
4235 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4237 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4238 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4239 #endif
4241 bytes = 0;
4242 /* These buttons we load but don't use yet */
4243 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
4244 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
4245 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
4246 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
4247 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
4248 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
4249 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
4251 #ifdef DISPLAY
4252 /* Add another dot */
4253 bytes = 0;
4254 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
4255 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4256 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4257 #endif
4259 bytes = 0;
4260 for (x=0;x<5;x++) {
4261 snprintf(num, sizeof(num), "%d", x);
4262 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
4264 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 1);
4265 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
4267 #ifdef DISPLAY
4268 /* Add another dot */
4269 bytes = 0;
4270 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .....", "");
4271 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4272 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4273 #endif
4275 if (ast_adsi_end_download(chan)) {
4276 bytes = 0;
4277 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
4278 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
4279 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4280 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4281 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4282 return 0;
4284 bytes = 0;
4285 bytes += ast_adsi_download_disconnect(buf + bytes);
4286 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4287 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
4289 if (option_debug)
4290 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
4292 #ifdef DISPLAY
4293 /* Add last dot */
4294 bytes = 0;
4295 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
4296 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4297 #endif
4298 if (option_debug)
4299 ast_log(LOG_DEBUG, "Restarting session...\n");
4301 bytes = 0;
4302 /* Load the session now */
4303 if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
4304 *useadsi = 1;
4305 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
4306 } else
4307 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
4309 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4310 return 0;
4313 static void adsi_begin(struct ast_channel *chan, int *useadsi)
4315 int x;
4316 if (!ast_adsi_available(chan))
4317 return;
4318 x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
4319 if (x < 0)
4320 return;
4321 if (!x) {
4322 if (adsi_load_vmail(chan, useadsi)) {
4323 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
4324 return;
4326 } else
4327 *useadsi = 1;
4330 static void adsi_login(struct ast_channel *chan)
4332 unsigned char buf[256];
4333 int bytes=0;
4334 unsigned char keys[8];
4335 int x;
4336 if (!ast_adsi_available(chan))
4337 return;
4339 for (x=0;x<8;x++)
4340 keys[x] = 0;
4341 /* Set one key for next */
4342 keys[3] = ADSI_KEY_APPS + 3;
4344 bytes += adsi_logo(buf + bytes);
4345 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
4346 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
4347 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4348 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
4349 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
4350 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
4351 bytes += ast_adsi_set_keys(buf + bytes, keys);
4352 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4353 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4356 static void adsi_password(struct ast_channel *chan)
4358 unsigned char buf[256];
4359 int bytes=0;
4360 unsigned char keys[8];
4361 int x;
4362 if (!ast_adsi_available(chan))
4363 return;
4365 for (x=0;x<8;x++)
4366 keys[x] = 0;
4367 /* Set one key for next */
4368 keys[3] = ADSI_KEY_APPS + 3;
4370 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4371 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
4372 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
4373 bytes += ast_adsi_set_keys(buf + bytes, keys);
4374 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4375 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4378 static void adsi_folders(struct ast_channel *chan, int start, char *label)
4380 unsigned char buf[256];
4381 int bytes=0;
4382 unsigned char keys[8];
4383 int x,y;
4385 if (!ast_adsi_available(chan))
4386 return;
4388 for (x=0;x<5;x++) {
4389 y = ADSI_KEY_APPS + 12 + start + x;
4390 if (y > ADSI_KEY_APPS + 12 + 4)
4391 y = 0;
4392 keys[x] = ADSI_KEY_SKT | y;
4394 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
4395 keys[6] = 0;
4396 keys[7] = 0;
4398 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
4399 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
4400 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4401 bytes += ast_adsi_set_keys(buf + bytes, keys);
4402 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4404 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4407 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
4409 int bytes=0;
4410 unsigned char buf[256];
4411 char buf1[256], buf2[256];
4412 char fn2[PATH_MAX];
4414 char cid[256]="";
4415 char *val;
4416 char *name, *num;
4417 char datetime[21]="";
4418 FILE *f;
4420 unsigned char keys[8];
4422 int x;
4424 if (!ast_adsi_available(chan))
4425 return;
4427 /* Retrieve important info */
4428 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
4429 f = fopen(fn2, "r");
4430 if (f) {
4431 while (!feof(f)) {
4432 fgets((char *)buf, sizeof(buf), f);
4433 if (!feof(f)) {
4434 char *stringp=NULL;
4435 stringp = (char *)buf;
4436 strsep(&stringp, "=");
4437 val = strsep(&stringp, "=");
4438 if (!ast_strlen_zero(val)) {
4439 if (!strcmp((char *)buf, "callerid"))
4440 ast_copy_string(cid, val, sizeof(cid));
4441 if (!strcmp((char *)buf, "origdate"))
4442 ast_copy_string(datetime, val, sizeof(datetime));
4446 fclose(f);
4448 /* New meaning for keys */
4449 for (x=0;x<5;x++)
4450 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
4451 keys[6] = 0x0;
4452 keys[7] = 0x0;
4454 if (!vms->curmsg) {
4455 /* No prev key, provide "Folder" instead */
4456 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
4458 if (vms->curmsg >= vms->lastmsg) {
4459 /* If last message ... */
4460 if (vms->curmsg) {
4461 /* but not only message, provide "Folder" instead */
4462 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
4463 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4465 } else {
4466 /* Otherwise if only message, leave blank */
4467 keys[3] = 1;
4471 if (!ast_strlen_zero(cid)) {
4472 ast_callerid_parse(cid, &name, &num);
4473 if (!name)
4474 name = num;
4475 } else
4476 name = "Unknown Caller";
4478 /* If deleted, show "undeleted" */
4480 if (vms->deleted[vms->curmsg])
4481 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
4483 /* Except "Exit" */
4484 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
4485 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
4486 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
4487 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
4489 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
4490 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
4491 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
4492 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
4493 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4494 bytes += ast_adsi_set_keys(buf + bytes, keys);
4495 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4497 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4500 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
4502 int bytes=0;
4503 unsigned char buf[256];
4504 unsigned char keys[8];
4506 int x;
4508 if (!ast_adsi_available(chan))
4509 return;
4511 /* New meaning for keys */
4512 for (x=0;x<5;x++)
4513 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
4515 keys[6] = 0x0;
4516 keys[7] = 0x0;
4518 if (!vms->curmsg) {
4519 /* No prev key, provide "Folder" instead */
4520 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
4522 if (vms->curmsg >= vms->lastmsg) {
4523 /* If last message ... */
4524 if (vms->curmsg) {
4525 /* but not only message, provide "Folder" instead */
4526 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
4527 } else {
4528 /* Otherwise if only message, leave blank */
4529 keys[3] = 1;
4533 /* If deleted, show "undeleted" */
4534 if (vms->deleted[vms->curmsg])
4535 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
4537 /* Except "Exit" */
4538 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
4539 bytes += ast_adsi_set_keys(buf + bytes, keys);
4540 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4542 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4545 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
4547 unsigned char buf[256] = "";
4548 char buf1[256] = "", buf2[256] = "";
4549 int bytes=0;
4550 unsigned char keys[8];
4551 int x;
4553 char *newm = (vms->newmessages == 1) ? "message" : "messages";
4554 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
4555 if (!ast_adsi_available(chan))
4556 return;
4557 if (vms->newmessages) {
4558 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
4559 if (vms->oldmessages) {
4560 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
4561 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
4562 } else {
4563 snprintf(buf2, sizeof(buf2), "%s.", newm);
4565 } else if (vms->oldmessages) {
4566 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
4567 snprintf(buf2, sizeof(buf2), "%s.", oldm);
4568 } else {
4569 strcpy(buf1, "You have no messages.");
4570 buf2[0] = ' ';
4571 buf2[1] = '\0';
4573 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
4574 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
4575 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4577 for (x=0;x<6;x++)
4578 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
4579 keys[6] = 0;
4580 keys[7] = 0;
4582 /* Don't let them listen if there are none */
4583 if (vms->lastmsg < 0)
4584 keys[0] = 1;
4585 bytes += ast_adsi_set_keys(buf + bytes, keys);
4587 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4589 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4592 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
4594 unsigned char buf[256] = "";
4595 char buf1[256] = "", buf2[256] = "";
4596 int bytes=0;
4597 unsigned char keys[8];
4598 int x;
4600 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
4602 if (!ast_adsi_available(chan))
4603 return;
4605 /* Original command keys */
4606 for (x=0;x<6;x++)
4607 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
4609 keys[6] = 0;
4610 keys[7] = 0;
4612 if ((vms->lastmsg + 1) < 1)
4613 keys[0] = 0;
4615 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
4616 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
4618 if (vms->lastmsg + 1)
4619 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
4620 else
4621 strcpy(buf2, "no messages.");
4622 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
4623 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
4624 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
4625 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4626 bytes += ast_adsi_set_keys(buf + bytes, keys);
4628 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4630 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4635 static void adsi_clear(struct ast_channel *chan)
4637 char buf[256];
4638 int bytes=0;
4639 if (!ast_adsi_available(chan))
4640 return;
4641 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4642 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4644 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4648 static void adsi_goodbye(struct ast_channel *chan)
4650 unsigned char buf[256];
4651 int bytes=0;
4653 if (!ast_adsi_available(chan))
4654 return;
4655 bytes += adsi_logo(buf + bytes);
4656 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
4657 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
4658 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4659 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4661 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4664 /*--- get_folder: Folder menu ---*/
4665 /* Plays "press 1 for INBOX messages" etc
4666 Should possibly be internationalized
4668 static int get_folder(struct ast_channel *chan, int start)
4670 int x;
4671 int d;
4672 char fn[PATH_MAX];
4673 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
4674 if (d)
4675 return d;
4676 for (x = start; x< 5; x++) { /* For all folders */
4677 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
4678 return d;
4679 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
4680 if (d)
4681 return d;
4682 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
4683 d = vm_play_folder_name(chan, fn);
4684 if (d)
4685 return d;
4686 d = ast_waitfordigit(chan, 500);
4687 if (d)
4688 return d;
4690 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
4691 if (d)
4692 return d;
4693 d = ast_waitfordigit(chan, 4000);
4694 return d;
4697 static int get_folder2(struct ast_channel *chan, char *fn, int start)
4699 int res = 0;
4700 res = ast_play_and_wait(chan, fn); /* Folder name */
4701 while (((res < '0') || (res > '9')) &&
4702 (res != '#') && (res >= 0)) {
4703 res = get_folder(chan, 0);
4705 return res;
4708 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
4709 char *context, signed char record_gain, long *duration, struct vm_state *vms)
4711 int cmd = 0;
4712 int retries = 0, prepend_duration = 0, already_recorded = 0;
4713 signed char zero_gain = 0;
4714 struct ast_config *msg_cfg;
4715 const char *duration_str;
4716 char msgfile[PATH_MAX], backup[PATH_MAX];
4717 char textfile[PATH_MAX];
4719 /* Must always populate duration correctly */
4720 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
4721 strcpy(textfile, msgfile);
4722 strcpy(backup, msgfile);
4723 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
4724 strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
4726 if (!(msg_cfg = ast_config_load(textfile))) {
4727 return -1;
4730 *duration = 0;
4731 if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
4732 *duration = atoi(duration_str);
4734 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
4735 if (cmd)
4736 retries = 0;
4737 switch (cmd) {
4738 case '1':
4739 /* prepend a message to the current message, update the metadata and return */
4741 prepend_duration = 0;
4743 /* if we can't read the message metadata, stop now */
4744 if (!msg_cfg) {
4745 cmd = 0;
4746 break;
4749 /* Back up the original file, so we can retry the prepend */
4750 if (already_recorded)
4751 ast_filecopy(backup, msgfile, NULL);
4752 else
4753 ast_filecopy(msgfile, backup, NULL);
4754 already_recorded = 1;
4756 if (record_gain)
4757 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
4759 cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
4760 if (record_gain)
4761 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
4763 if (prepend_duration) {
4764 struct ast_category *msg_cat;
4765 /* need enough space for a maximum-length message duration */
4766 char duration_str[12];
4768 prepend_duration += *duration;
4769 msg_cat = ast_category_get(msg_cfg, "message");
4770 snprintf(duration_str, 11, "%d", prepend_duration);
4771 if (!ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
4772 config_text_file_save(textfile, msg_cfg, "app_voicemail");
4776 break;
4778 case '2':
4779 cmd = 't';
4780 break;
4781 case '*':
4782 cmd = '*';
4783 break;
4784 default:
4785 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
4786 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
4787 if (!cmd)
4788 cmd = ast_play_and_wait(chan,"vm-starmain");
4789 /* "press star to return to the main menu" */
4790 if (!cmd)
4791 cmd = ast_waitfordigit(chan,6000);
4792 if (!cmd)
4793 retries++;
4794 if (retries > 3)
4795 cmd = 't';
4799 ast_config_destroy(msg_cfg);
4800 if (already_recorded)
4801 ast_filedelete(backup, NULL);
4802 if (prepend_duration)
4803 *duration = prepend_duration;
4805 if (cmd == 't' || cmd == 'S')
4806 cmd = 0;
4807 return cmd;
4810 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
4812 char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
4813 int newmsgs = 0, oldmsgs = 0;
4814 const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
4816 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
4817 make_file(fn, sizeof(fn), todir, msgnum);
4818 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
4820 if (!ast_strlen_zero(vmu->attachfmt)) {
4821 if (strstr(fmt, vmu->attachfmt)) {
4822 fmt = vmu->attachfmt;
4823 } else {
4824 ast_log(LOG_WARNING, "Attachment format '%s' is not one of the recorded formats '%s'. Falling back to default format for '%s@%s'.\n", vmu->attachfmt, fmt, vmu->mailbox, vmu->context);
4828 /* Attach only the first format */
4829 fmt = ast_strdupa(fmt);
4830 stringp = fmt;
4831 strsep(&stringp, "|");
4833 if (!ast_strlen_zero(vmu->email)) {
4834 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
4835 char *myserveremail = serveremail;
4836 attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
4837 if (!ast_strlen_zero(vmu->serveremail))
4838 myserveremail = vmu->serveremail;
4840 if (attach_user_voicemail)
4841 RETRIEVE(todir, msgnum, vmu);
4843 /*XXX possible imap issue, should category be NULL XXX*/
4844 sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
4846 if (attach_user_voicemail)
4847 DISPOSE(todir, msgnum);
4850 if (!ast_strlen_zero(vmu->pager)) {
4851 char *myserveremail = serveremail;
4852 if (!ast_strlen_zero(vmu->serveremail))
4853 myserveremail = vmu->serveremail;
4854 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
4857 if (ast_test_flag(vmu, VM_DELETE)) {
4858 DELETE(todir, msgnum, fn, vmu);
4861 /* Leave voicemail for someone */
4862 if (ast_app_has_voicemail(ext_context, NULL)) {
4863 ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
4865 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s@%s\r\nWaiting: %d\r\nNew: %d\r\nOld: %d\r\n", vmu->mailbox, vmu->context, ast_app_has_voicemail(ext_context, NULL), newmsgs, oldmsgs);
4866 run_externnotify(vmu->context, vmu->mailbox);
4867 return 0;
4870 static int forward_message(struct ast_channel *chan, char *context, struct vm_state *vms, struct ast_vm_user *sender, char *fmt, int flag, signed char record_gain)
4872 #ifdef IMAP_STORAGE
4873 int todircount=0;
4874 struct vm_state *dstvms;
4875 #endif
4876 char username[70]="";
4877 int res = 0, cmd = 0;
4878 struct ast_vm_user *receiver = NULL, *vmtmp;
4879 AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
4880 char *stringp;
4881 const char *s;
4882 int saved_messages = 0, found = 0;
4883 int valid_extensions = 0;
4884 char *dir;
4885 int curmsg;
4887 if (vms == NULL) return -1;
4888 dir = vms->curdir;
4889 curmsg = vms->curmsg;
4891 while (!res && !valid_extensions) {
4892 int use_directory = 0;
4893 if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
4894 int done = 0;
4895 int retries = 0;
4896 cmd=0;
4897 while ((cmd >= 0) && !done ){
4898 if (cmd)
4899 retries = 0;
4900 switch (cmd) {
4901 case '1':
4902 use_directory = 0;
4903 done = 1;
4904 break;
4905 case '2':
4906 use_directory = 1;
4907 done=1;
4908 break;
4909 case '*':
4910 cmd = 't';
4911 done = 1;
4912 break;
4913 default:
4914 /* Press 1 to enter an extension press 2 to use the directory */
4915 cmd = ast_play_and_wait(chan,"vm-forward");
4916 if (!cmd)
4917 cmd = ast_waitfordigit(chan,3000);
4918 if (!cmd)
4919 retries++;
4920 if (retries > 3)
4922 cmd = 't';
4923 done = 1;
4928 if (cmd < 0 || cmd == 't')
4929 break;
4932 if (use_directory) {
4933 /* use app_directory */
4935 char old_context[sizeof(chan->context)];
4936 char old_exten[sizeof(chan->exten)];
4937 int old_priority;
4938 struct ast_app* app;
4941 app = pbx_findapp("Directory");
4942 if (app) {
4943 char vmcontext[256];
4944 /* make backup copies */
4945 memcpy(old_context, chan->context, sizeof(chan->context));
4946 memcpy(old_exten, chan->exten, sizeof(chan->exten));
4947 old_priority = chan->priority;
4949 /* call the the Directory, changes the channel */
4950 snprintf(vmcontext, sizeof(vmcontext), "%s||v", context ? context : "default");
4951 res = pbx_exec(chan, app, vmcontext);
4953 ast_copy_string(username, chan->exten, sizeof(username));
4955 /* restore the old context, exten, and priority */
4956 memcpy(chan->context, old_context, sizeof(chan->context));
4957 memcpy(chan->exten, old_exten, sizeof(chan->exten));
4958 chan->priority = old_priority;
4960 } else {
4961 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
4962 ast_clear_flag((&globalflags), VM_DIRECFORWARD);
4964 } else {
4965 /* Ask for an extension */
4966 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
4967 if (res)
4968 break;
4969 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
4970 break;
4973 /* start all over if no username */
4974 if (ast_strlen_zero(username))
4975 continue;
4976 stringp = username;
4977 s = strsep(&stringp, "*");
4978 /* start optimistic */
4979 valid_extensions = 1;
4980 while (s) {
4981 /* Don't forward to ourselves but allow leaving a message for ourselves (flag == 1). find_user is going to malloc since we have a NULL as first argument */
4982 if ((flag == 1 || strcmp(s,sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
4983 AST_LIST_INSERT_HEAD(&extensions, receiver, list);
4984 found++;
4985 } else {
4986 valid_extensions = 0;
4987 break;
4989 s = strsep(&stringp, "*");
4991 /* break from the loop of reading the extensions */
4992 if (valid_extensions)
4993 break;
4994 /* "I am sorry, that's not a valid extension. Please try again." */
4995 res = ast_play_and_wait(chan, "pbx-invalid");
4997 /* check if we're clear to proceed */
4998 if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
4999 return res;
5000 if (flag==1) {
5001 struct leave_vm_options leave_options;
5002 char mailbox[AST_MAX_EXTENSION * 2 + 2];
5003 /* Make sure that context doesn't get set as a literal "(null)" (or else find_user won't find it) */
5004 if (context)
5005 snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
5006 else
5007 ast_copy_string(mailbox, username, sizeof(mailbox));
5009 /* Send VoiceMail */
5010 memset(&leave_options, 0, sizeof(leave_options));
5011 leave_options.record_gain = record_gain;
5012 cmd = leave_voicemail(chan, mailbox, &leave_options);
5013 } else {
5014 /* Forward VoiceMail */
5015 long duration = 0;
5016 char origmsgfile[PATH_MAX], msgfile[PATH_MAX];
5017 struct vm_state vmstmp;
5019 memcpy(&vmstmp, vms, sizeof(vmstmp));
5021 make_file(origmsgfile, sizeof(origmsgfile), dir, curmsg);
5022 create_dirpath(vmstmp.curdir, sizeof(vmstmp.curdir), sender->context, vmstmp.username, "tmp");
5023 make_file(msgfile, sizeof(msgfile), vmstmp.curdir, curmsg);
5025 RETRIEVE(dir, curmsg, sender);
5027 /* Alter a surrogate file, only */
5028 copy_plain_file(origmsgfile, msgfile);
5030 cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp);
5031 if (!cmd) {
5032 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
5033 #ifdef IMAP_STORAGE
5034 char *myserveremail;
5035 int attach_user_voicemail;
5036 /* get destination mailbox */
5037 dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
5038 if (!dstvms) {
5039 dstvms = create_vm_state_from_user(vmtmp);
5041 if (dstvms) {
5042 init_mailstream(dstvms, 0);
5043 if (!dstvms->mailstream) {
5044 ast_log (LOG_ERROR,"IMAP mailstream for %s is NULL\n",vmtmp->mailbox);
5045 } else {
5046 STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
5047 run_externnotify(vmtmp->context, vmtmp->mailbox);
5049 } else {
5050 ast_log (LOG_ERROR,"Could not find state information for mailbox %s\n",vmtmp->mailbox);
5052 myserveremail = serveremail;
5053 if (!ast_strlen_zero(vmtmp->serveremail))
5054 myserveremail = vmtmp->serveremail;
5055 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
5056 /* NULL category for IMAP storage */
5057 sendmail(myserveremail, vmtmp, todircount, vmtmp->context, vmtmp->mailbox, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL), vms->fn, fmt, duration, attach_user_voicemail, chan, NULL);
5058 #else
5059 copy_message(chan, sender, -1, curmsg, duration, vmtmp, fmt, vmstmp.curdir);
5060 #endif
5061 saved_messages++;
5062 AST_LIST_REMOVE_CURRENT(&extensions, list);
5063 free_user(vmtmp);
5064 if (res)
5065 break;
5067 AST_LIST_TRAVERSE_SAFE_END;
5068 if (saved_messages > 0) {
5069 /* give confirmation that the message was saved */
5070 /* commented out since we can't forward batches yet
5071 if (saved_messages == 1)
5072 res = ast_play_and_wait(chan, "vm-message");
5073 else
5074 res = ast_play_and_wait(chan, "vm-messages");
5075 if (!res)
5076 res = ast_play_and_wait(chan, "vm-saved"); */
5077 res = ast_play_and_wait(chan, "vm-msgsaved");
5081 /* Remove surrogate file */
5082 vm_delete(msgfile);
5083 DISPOSE(dir, curmsg);
5086 /* If anything failed above, we still have this list to free */
5087 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
5088 free_user(vmtmp);
5089 return res ? res : cmd;
5092 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
5094 int res;
5095 if ((res = ast_stream_and_wait(chan, file, chan->language, AST_DIGIT_ANY)) < 0)
5096 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
5097 return res;
5100 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
5102 return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
5105 static int play_message_category(struct ast_channel *chan, const char *category)
5107 int res = 0;
5109 if (!ast_strlen_zero(category))
5110 res = ast_play_and_wait(chan, category);
5112 if (res) {
5113 ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
5114 res = 0;
5117 return res;
5120 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
5122 int res = 0;
5123 struct vm_zone *the_zone = NULL;
5124 time_t t;
5126 if (ast_get_time_t(origtime, &t, 0, NULL)) {
5127 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
5128 return 0;
5131 /* Does this user have a timezone specified? */
5132 if (!ast_strlen_zero(vmu->zonetag)) {
5133 /* Find the zone in the list */
5134 struct vm_zone *z;
5135 AST_LIST_LOCK(&zones);
5136 AST_LIST_TRAVERSE(&zones, z, list) {
5137 if (!strcmp(z->name, vmu->zonetag)) {
5138 the_zone = z;
5139 break;
5142 AST_LIST_UNLOCK(&zones);
5145 /* No internal variable parsing for now, so we'll comment it out for the time being */
5146 #if 0
5147 /* Set the DIFF_* variables */
5148 ast_localtime(&t, &time_now, NULL);
5149 tv_now = ast_tvnow();
5150 tnow = tv_now.tv_sec;
5151 ast_localtime(&tnow, &time_then, NULL);
5153 /* Day difference */
5154 if (time_now.tm_year == time_then.tm_year)
5155 snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
5156 else
5157 snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
5158 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
5160 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
5161 #endif
5162 if (the_zone)
5163 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
5164 else if (!strcasecmp(chan->language,"pl")) /* POLISH syntax */
5165 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
5166 else if (!strcasecmp(chan->language,"se")) /* SWEDISH syntax */
5167 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
5168 else if (!strcasecmp(chan->language,"no")) /* NORWEGIAN syntax */
5169 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
5170 else if (!strcasecmp(chan->language,"de")) /* GERMAN syntax */
5171 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
5172 else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
5173 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
5174 else if (!strcasecmp(chan->language,"it")) /* ITALIAN syntax */
5175 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' 'digits/hours' k 'digits/e' M 'digits/minutes'", NULL);
5176 else if (!strcasecmp(chan->language,"gr"))
5177 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
5178 else if (!strcasecmp(chan->language,"pt_BR"))
5179 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'digits/pt-de' B 'digits/pt-de' Y 'digits/pt-as' HM ", NULL);
5180 else if (!strcasecmp(chan->language,"he"))
5181 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Ad 'at2' kM", NULL);
5182 else
5183 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
5184 #if 0
5185 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
5186 #endif
5187 return res;
5192 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
5194 int res = 0;
5195 int i;
5196 char *callerid, *name;
5197 char prefile[PATH_MAX] = "";
5200 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
5201 /* BB: Still need to change this so that if this function is called by the message envelope (and someone is explicitly requesting to hear the CID), it does not check to see if CID is enabled in the config file */
5202 if ((cid == NULL)||(context == NULL))
5203 return res;
5205 /* Strip off caller ID number from name */
5206 if (option_debug > 2)
5207 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
5208 ast_callerid_parse(cid, &name, &callerid);
5209 if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
5210 /* Check for internal contexts and only */
5211 /* say extension when the call didn't come from an internal context in the list */
5212 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
5213 if (option_debug > 2)
5214 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
5215 if ((strcmp(cidinternalcontexts[i], context) == 0))
5216 break;
5218 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
5219 if (!res) {
5220 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
5221 if (!ast_strlen_zero(prefile)) {
5222 /* See if we can find a recorded name for this person instead of their extension number */
5223 if (ast_fileexists(prefile, NULL, NULL) > 0) {
5224 if (option_verbose > 2)
5225 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
5226 if (!callback)
5227 res = wait_file2(chan, vms, "vm-from");
5228 res = ast_stream_and_wait(chan, prefile, chan->language, "");
5229 } else {
5230 if (option_verbose > 2)
5231 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
5232 /* BB: Say "from extension" as one saying to sound smoother */
5233 if (!callback)
5234 res = wait_file2(chan, vms, "vm-from-extension");
5235 res = ast_say_digit_str(chan, callerid, "", chan->language);
5241 else if (!res){
5242 if (option_debug > 2)
5243 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
5244 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
5245 if (!callback)
5246 res = wait_file2(chan, vms, "vm-from-phonenumber");
5247 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
5249 } else {
5250 /* Number unknown */
5251 if (option_debug)
5252 ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
5253 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
5254 res = wait_file2(chan, vms, "vm-unknown-caller");
5256 return res;
5259 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
5261 int res = 0;
5262 int durationm;
5263 int durations;
5264 /* Verify that we have a duration for the message */
5265 if (duration == NULL)
5266 return res;
5268 /* Convert from seconds to minutes */
5269 durations=atoi(duration);
5270 durationm=(durations / 60);
5272 if (option_debug > 2)
5273 ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
5275 if ((!res) && (durationm >= minduration)) {
5276 res = wait_file2(chan, vms, "vm-duration");
5278 /* POLISH syntax */
5279 if (!strcasecmp(chan->language, "pl")) {
5280 div_t num = div(durationm, 10);
5282 if (durationm == 1) {
5283 res = ast_play_and_wait(chan, "digits/1z");
5284 res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
5285 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5286 if (num.rem == 2) {
5287 if (!num.quot) {
5288 res = ast_play_and_wait(chan, "digits/2-ie");
5289 } else {
5290 res = say_and_wait(chan, durationm - 2 , chan->language);
5291 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5293 } else {
5294 res = say_and_wait(chan, durationm, chan->language);
5296 res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
5297 } else {
5298 res = say_and_wait(chan, durationm, chan->language);
5299 res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
5301 /* DEFAULT syntax */
5302 } else {
5303 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
5304 res = wait_file2(chan, vms, "vm-minutes");
5307 return res;
5310 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
5312 int res = 0;
5313 char filename[256], *cid;
5314 const char *origtime, *context, *category, *duration;
5315 struct ast_config *msg_cfg;
5317 vms->starting = 0;
5318 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
5319 adsi_message(chan, vms);
5320 if (!strcasecmp(chan->language, "he")) { /* HEBREW FORMAT */
5322 * The syntax in hebrew for counting the number of message is up side down
5323 * in comparison to english.
5325 if (!vms->curmsg) {
5326 res = wait_file2(chan, vms, "vm-message");
5327 res = wait_file2(chan, vms, "vm-first"); /* "First" */
5328 } else if (vms->curmsg == vms->lastmsg) {
5329 res = wait_file2(chan, vms, "vm-message");
5330 res = wait_file2(chan, vms, "vm-last"); /* "last" */
5331 } else {
5332 res = wait_file2(chan, vms, "vm-message"); /* "message" */
5333 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
5334 ast_log(LOG_DEBUG, "curmsg: %d\n", vms->curmsg);
5335 ast_log(LOG_DEBUG, "lagmsg: %d\n", vms->lastmsg);
5336 if (!res) {
5337 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, "f");
5341 } else {
5342 if (!vms->curmsg)
5343 res = wait_file2(chan, vms, "vm-first"); /* "First" */
5344 else if (vms->curmsg == vms->lastmsg)
5345 res = wait_file2(chan, vms, "vm-last"); /* "last" */
5347 if (!res) {
5348 /* POLISH syntax */
5349 if (!strcasecmp(chan->language, "pl")) {
5350 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
5351 int ten, one;
5352 char nextmsg[256];
5353 ten = (vms->curmsg + 1) / 10;
5354 one = (vms->curmsg + 1) % 10;
5356 if (vms->curmsg < 20) {
5357 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
5358 res = wait_file2(chan, vms, nextmsg);
5359 } else {
5360 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
5361 res = wait_file2(chan, vms, nextmsg);
5362 if (one > 0) {
5363 if (!res) {
5364 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
5365 res = wait_file2(chan, vms, nextmsg);
5370 if (!res)
5371 res = wait_file2(chan, vms, "vm-message");
5372 } else {
5373 if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
5374 res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
5375 else /* DEFAULT syntax */
5376 res = wait_file2(chan, vms, "vm-message");
5377 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
5378 if (!res)
5379 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
5384 /* Retrieve info from VM attribute file */
5385 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
5386 snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
5387 RETRIEVE(vms->curdir, vms->curmsg, vmu);
5388 msg_cfg = ast_config_load(filename);
5389 if (!msg_cfg) {
5390 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
5391 return 0;
5394 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
5395 ast_log(LOG_WARNING, "No origtime?!\n");
5396 DISPOSE(vms->curdir, vms->curmsg);
5397 ast_config_destroy(msg_cfg);
5398 return 0;
5401 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
5402 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
5403 category = ast_variable_retrieve(msg_cfg, "message", "category");
5405 context = ast_variable_retrieve(msg_cfg, "message", "context");
5406 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
5407 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
5408 if (!res)
5409 res = play_message_category(chan, category);
5410 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
5411 res = play_message_datetime(chan, vmu, origtime, filename);
5412 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
5413 res = play_message_callerid(chan, vms, cid, context, 0);
5414 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
5415 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
5416 /* Allow pressing '1' to skip envelope / callerid */
5417 if (res == '1')
5418 res = 0;
5419 ast_config_destroy(msg_cfg);
5421 if (!res) {
5422 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
5423 vms->heard[vms->curmsg] = 1;
5424 if ((res = wait_file(chan, vms, vms->fn)) < 0) {
5425 ast_log(LOG_WARNING, "Playback of message %s failed\n", vms->fn);
5426 res = 0;
5429 DISPOSE(vms->curdir, vms->curmsg);
5430 return res;
5433 #ifndef IMAP_STORAGE
5434 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
5436 int res = 0;
5437 int count_msg, last_msg;
5439 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
5441 /* Rename the member vmbox HERE so that we don't try to return before
5442 * we know what's going on.
5444 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
5446 /* Faster to make the directory than to check if it exists. */
5447 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
5449 count_msg = count_messages(vmu, vms->curdir);
5450 if (count_msg < 0)
5451 return count_msg;
5452 else
5453 vms->lastmsg = count_msg - 1;
5456 The following test is needed in case sequencing gets messed up.
5457 There appears to be more than one way to mess up sequence, so
5458 we will not try to find all of the root causes--just fix it when
5459 detected.
5462 last_msg = last_message_index(vmu, vms->curdir);
5463 if (last_msg < 0)
5464 return last_msg;
5465 else if (vms->lastmsg != last_msg)
5467 ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
5468 res = resequence_mailbox(vmu, vms->curdir);
5469 if (res)
5470 return res;
5473 return 0;
5475 #endif
5477 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
5479 int x = 0;
5480 #ifndef IMAP_STORAGE
5481 int res = 0, nummsg;
5482 #endif
5484 if (vms->lastmsg <= -1)
5485 goto done;
5487 vms->curmsg = -1;
5488 #ifndef IMAP_STORAGE
5489 /* Get the deleted messages fixed */
5490 if (vm_lock_path(vms->curdir))
5491 return ERROR_LOCK_PATH;
5493 for (x = 0; x < vmu->maxmsg; x++) {
5494 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
5495 /* Save this message. It's not in INBOX or hasn't been heard */
5496 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
5497 if (!EXISTS(vms->curdir, x, vms->fn, NULL))
5498 break;
5499 vms->curmsg++;
5500 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
5501 if (strcmp(vms->fn, vms->fn2)) {
5502 RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
5504 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
5505 /* Move to old folder before deleting */
5506 res = save_to_folder(vmu, vms, x, 1);
5507 if (res == ERROR_LOCK_PATH || res == ERROR_MAILBOX_FULL) {
5508 /* If save failed do not delete the message */
5509 ast_log(LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
5510 vms->deleted[x] = 0;
5511 vms->heard[x] = 0;
5512 --x;
5517 /* Delete ALL remaining messages */
5518 nummsg = x - 1;
5519 for (x = vms->curmsg + 1; x <= nummsg; x++) {
5520 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
5521 if (EXISTS(vms->curdir, x, vms->fn, NULL))
5522 DELETE(vms->curdir, x, vms->fn, vmu);
5524 ast_unlock_path(vms->curdir);
5525 #else
5526 if (vms->deleted) {
5527 for (x=0;x < vmu->maxmsg;x++) {
5528 if (vms->deleted[x]) {
5529 if (option_debug > 2)
5530 ast_log(LOG_DEBUG,"IMAP delete of %d\n",x);
5531 DELETE(vms->curdir, x, vms->fn, vmu);
5535 #endif
5537 done:
5538 if (vms->deleted)
5539 memset(vms->deleted, 0, vmu->maxmsg * sizeof(int));
5540 if (vms->heard)
5541 memset(vms->heard, 0, vmu->maxmsg * sizeof(int));
5543 return 0;
5546 /* In Greek even though we CAN use a syntax like "friends messages"
5547 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
5548 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
5549 * syntax for the above three categories which is more elegant.
5552 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
5554 int cmd;
5555 char *buf;
5557 buf = alloca(strlen(mbox)+2);
5558 strcpy(buf, mbox);
5559 strcat(buf,"s");
5561 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
5562 cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
5563 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
5564 } else {
5565 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
5566 return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
5570 static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
5572 int cmd;
5574 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
5575 if (!strcasecmp(mbox, "vm-INBOX"))
5576 cmd = ast_play_and_wait(chan, "vm-new-e");
5577 else
5578 cmd = ast_play_and_wait(chan, "vm-old-e");
5579 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
5580 } else {
5581 cmd = ast_play_and_wait(chan, "vm-messages");
5582 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5586 static int vm_play_folder_name_ua(struct ast_channel *chan, char *mbox)
5588 int cmd;
5590 if (!strcasecmp(mbox, "vm-Family") || !strcasecmp(mbox, "vm-Friends") || !strcasecmp(mbox, "vm-Work")){
5591 cmd = ast_play_and_wait(chan, "vm-messages");
5592 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5593 } else {
5594 cmd = ast_play_and_wait(chan, mbox);
5595 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
5599 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
5601 int cmd;
5603 if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
5604 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
5605 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5606 } else if (!strcasecmp(chan->language, "gr")){
5607 return vm_play_folder_name_gr(chan, mbox);
5608 } else if (!strcasecmp(chan->language, "pl")){
5609 return vm_play_folder_name_pl(chan, mbox);
5610 } else if (!strcasecmp(chan->language, "ua")){ /* Ukrainian syntax */
5611 return vm_play_folder_name_ua(chan, mbox);
5612 } else if (!strcasecmp(chan->language, "he")){ /* Hebrew syntax */
5613 cmd = ast_play_and_wait(chan, mbox);
5614 return cmd;
5615 } else { /* Default English */
5616 cmd = ast_play_and_wait(chan, mbox);
5617 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
5621 /* GREEK SYNTAX
5622 In greek the plural for old/new is
5623 different so we need the following files
5624 We also need vm-denExeteMynhmata because
5625 this syntax is different.
5627 -> vm-Olds.wav : "Palia"
5628 -> vm-INBOXs.wav : "Nea"
5629 -> vm-denExeteMynhmata : "den exete mynhmata"
5633 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
5635 int res = 0;
5637 if (vms->newmessages) {
5638 res = ast_play_and_wait(chan, "vm-youhave");
5639 if (!res)
5640 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
5641 if (!res) {
5642 if ((vms->newmessages == 1)) {
5643 res = ast_play_and_wait(chan, "vm-INBOX");
5644 if (!res)
5645 res = ast_play_and_wait(chan, "vm-message");
5646 } else {
5647 res = ast_play_and_wait(chan, "vm-INBOXs");
5648 if (!res)
5649 res = ast_play_and_wait(chan, "vm-messages");
5652 } else if (vms->oldmessages){
5653 res = ast_play_and_wait(chan, "vm-youhave");
5654 if (!res)
5655 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
5656 if ((vms->oldmessages == 1)){
5657 res = ast_play_and_wait(chan, "vm-Old");
5658 if (!res)
5659 res = ast_play_and_wait(chan, "vm-message");
5660 } else {
5661 res = ast_play_and_wait(chan, "vm-Olds");
5662 if (!res)
5663 res = ast_play_and_wait(chan, "vm-messages");
5665 } else if (!vms->oldmessages && !vms->newmessages)
5666 res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
5667 return res;
5670 /* Default English syntax */
5671 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
5673 int res;
5675 /* Introduce messages they have */
5676 res = ast_play_and_wait(chan, "vm-youhave");
5677 if (!res) {
5678 if (vms->newmessages) {
5679 res = say_and_wait(chan, vms->newmessages, chan->language);
5680 if (!res)
5681 res = ast_play_and_wait(chan, "vm-INBOX");
5682 if (vms->oldmessages && !res)
5683 res = ast_play_and_wait(chan, "vm-and");
5684 else if (!res) {
5685 if ((vms->newmessages == 1))
5686 res = ast_play_and_wait(chan, "vm-message");
5687 else
5688 res = ast_play_and_wait(chan, "vm-messages");
5692 if (!res && vms->oldmessages) {
5693 res = say_and_wait(chan, vms->oldmessages, chan->language);
5694 if (!res)
5695 res = ast_play_and_wait(chan, "vm-Old");
5696 if (!res) {
5697 if (vms->oldmessages == 1)
5698 res = ast_play_and_wait(chan, "vm-message");
5699 else
5700 res = ast_play_and_wait(chan, "vm-messages");
5703 if (!res) {
5704 if (!vms->oldmessages && !vms->newmessages) {
5705 res = ast_play_and_wait(chan, "vm-no");
5706 if (!res)
5707 res = ast_play_and_wait(chan, "vm-messages");
5711 return res;
5714 /* Default Hebrew syntax */
5715 static int vm_intro_he(struct ast_channel *chan, struct vm_state *vms)
5717 int res=0;
5719 /* Introduce messages they have */
5720 if (!res) {
5721 if ((vms->newmessages) || (vms->oldmessages)) {
5722 res = ast_play_and_wait(chan, "vm-youhave");
5725 * The word "shtei" refers to the number 2 in hebrew when performing a count
5726 * of elements. In Hebrew, there are 6 forms of enumerating the number 2 for
5727 * an element, this is one of them.
5729 if (vms->newmessages) {
5730 if (!res) {
5731 if (vms->newmessages == 1) {
5732 res = ast_play_and_wait(chan, "vm-INBOX1");
5733 } else {
5734 if (vms->newmessages == 2) {
5735 res = ast_play_and_wait(chan, "vm-shtei");
5736 } else {
5737 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
5739 res = ast_play_and_wait(chan, "vm-INBOX");
5742 if (vms->oldmessages && !res) {
5743 res = ast_play_and_wait(chan, "vm-and");
5744 if (vms->oldmessages == 1) {
5745 res = ast_play_and_wait(chan, "vm-Old1");
5746 } else {
5747 if (vms->oldmessages == 2) {
5748 res = ast_play_and_wait(chan, "vm-shtei");
5749 } else {
5750 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
5752 res = ast_play_and_wait(chan, "vm-Old");
5756 if (!res && vms->oldmessages && !vms->newmessages) {
5757 if (!res) {
5758 if (vms->oldmessages == 1) {
5759 res = ast_play_and_wait(chan, "vm-Old1");
5760 } else {
5761 if (vms->oldmessages == 2) {
5762 res = ast_play_and_wait(chan, "vm-shtei");
5763 } else {
5764 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
5766 res = ast_play_and_wait(chan, "vm-Old");
5770 if (!res) {
5771 if (!vms->oldmessages && !vms->newmessages) {
5772 if (!res) {
5773 res = ast_play_and_wait(chan, "vm-nomessages");
5778 return res;
5782 /* ITALIAN syntax */
5783 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
5785 /* Introduce messages they have */
5786 int res;
5787 if (!vms->oldmessages && !vms->newmessages)
5788 res = ast_play_and_wait(chan, "vm-no") ||
5789 ast_play_and_wait(chan, "vm-message");
5790 else
5791 res = ast_play_and_wait(chan, "vm-youhave");
5792 if (!res && vms->newmessages) {
5793 res = (vms->newmessages == 1) ?
5794 ast_play_and_wait(chan, "digits/un") ||
5795 ast_play_and_wait(chan, "vm-nuovo") ||
5796 ast_play_and_wait(chan, "vm-message") :
5797 /* 2 or more new messages */
5798 say_and_wait(chan, vms->newmessages, chan->language) ||
5799 ast_play_and_wait(chan, "vm-nuovi") ||
5800 ast_play_and_wait(chan, "vm-messages");
5801 if (!res && vms->oldmessages)
5802 res = ast_play_and_wait(chan, "vm-and");
5804 if (!res && vms->oldmessages) {
5805 res = (vms->oldmessages == 1) ?
5806 ast_play_and_wait(chan, "digits/un") ||
5807 ast_play_and_wait(chan, "vm-vecchio") ||
5808 ast_play_and_wait(chan, "vm-message") :
5809 /* 2 or more old messages */
5810 say_and_wait(chan, vms->oldmessages, chan->language) ||
5811 ast_play_and_wait(chan, "vm-vecchi") ||
5812 ast_play_and_wait(chan, "vm-messages");
5814 return res;
5817 /* POLISH syntax */
5818 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
5820 /* Introduce messages they have */
5821 int res;
5822 div_t num;
5824 if (!vms->oldmessages && !vms->newmessages) {
5825 res = ast_play_and_wait(chan, "vm-no");
5826 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5827 return res;
5828 } else {
5829 res = ast_play_and_wait(chan, "vm-youhave");
5832 if (vms->newmessages) {
5833 num = div(vms->newmessages, 10);
5834 if (vms->newmessages == 1) {
5835 res = ast_play_and_wait(chan, "digits/1-a");
5836 res = res ? res : ast_play_and_wait(chan, "vm-new-a");
5837 res = res ? res : ast_play_and_wait(chan, "vm-message");
5838 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5839 if (num.rem == 2) {
5840 if (!num.quot) {
5841 res = ast_play_and_wait(chan, "digits/2-ie");
5842 } else {
5843 res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
5844 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5846 } else {
5847 res = say_and_wait(chan, vms->newmessages, chan->language);
5849 res = res ? res : ast_play_and_wait(chan, "vm-new-e");
5850 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5851 } else {
5852 res = say_and_wait(chan, vms->newmessages, chan->language);
5853 res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
5854 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5856 if (!res && vms->oldmessages)
5857 res = ast_play_and_wait(chan, "vm-and");
5859 if (!res && vms->oldmessages) {
5860 num = div(vms->oldmessages, 10);
5861 if (vms->oldmessages == 1) {
5862 res = ast_play_and_wait(chan, "digits/1-a");
5863 res = res ? res : ast_play_and_wait(chan, "vm-old-a");
5864 res = res ? res : ast_play_and_wait(chan, "vm-message");
5865 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5866 if (num.rem == 2) {
5867 if (!num.quot) {
5868 res = ast_play_and_wait(chan, "digits/2-ie");
5869 } else {
5870 res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
5871 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5873 } else {
5874 res = say_and_wait(chan, vms->oldmessages, chan->language);
5876 res = res ? res : ast_play_and_wait(chan, "vm-old-e");
5877 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5878 } else {
5879 res = say_and_wait(chan, vms->oldmessages, chan->language);
5880 res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
5881 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5885 return res;
5888 /* SWEDISH syntax */
5889 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
5891 /* Introduce messages they have */
5892 int res;
5894 res = ast_play_and_wait(chan, "vm-youhave");
5895 if (res)
5896 return res;
5898 if (!vms->oldmessages && !vms->newmessages) {
5899 res = ast_play_and_wait(chan, "vm-no");
5900 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5901 return res;
5904 if (vms->newmessages) {
5905 if ((vms->newmessages == 1)) {
5906 res = ast_play_and_wait(chan, "digits/ett");
5907 res = res ? res : ast_play_and_wait(chan, "vm-nytt");
5908 res = res ? res : ast_play_and_wait(chan, "vm-message");
5909 } else {
5910 res = say_and_wait(chan, vms->newmessages, chan->language);
5911 res = res ? res : ast_play_and_wait(chan, "vm-nya");
5912 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5914 if (!res && vms->oldmessages)
5915 res = ast_play_and_wait(chan, "vm-and");
5917 if (!res && vms->oldmessages) {
5918 if (vms->oldmessages == 1) {
5919 res = ast_play_and_wait(chan, "digits/ett");
5920 res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
5921 res = res ? res : ast_play_and_wait(chan, "vm-message");
5922 } else {
5923 res = say_and_wait(chan, vms->oldmessages, chan->language);
5924 res = res ? res : ast_play_and_wait(chan, "vm-gamla");
5925 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5929 return res;
5932 /* NORWEGIAN syntax */
5933 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
5935 /* Introduce messages they have */
5936 int res;
5938 res = ast_play_and_wait(chan, "vm-youhave");
5939 if (res)
5940 return res;
5942 if (!vms->oldmessages && !vms->newmessages) {
5943 res = ast_play_and_wait(chan, "vm-no");
5944 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5945 return res;
5948 if (vms->newmessages) {
5949 if ((vms->newmessages == 1)) {
5950 res = ast_play_and_wait(chan, "digits/1");
5951 res = res ? res : ast_play_and_wait(chan, "vm-ny");
5952 res = res ? res : ast_play_and_wait(chan, "vm-message");
5953 } else {
5954 res = say_and_wait(chan, vms->newmessages, chan->language);
5955 res = res ? res : ast_play_and_wait(chan, "vm-nye");
5956 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5958 if (!res && vms->oldmessages)
5959 res = ast_play_and_wait(chan, "vm-and");
5961 if (!res && vms->oldmessages) {
5962 if (vms->oldmessages == 1) {
5963 res = ast_play_and_wait(chan, "digits/1");
5964 res = res ? res : ast_play_and_wait(chan, "vm-gamel");
5965 res = res ? res : ast_play_and_wait(chan, "vm-message");
5966 } else {
5967 res = say_and_wait(chan, vms->oldmessages, chan->language);
5968 res = res ? res : ast_play_and_wait(chan, "vm-gamle");
5969 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5973 return res;
5976 /* GERMAN syntax */
5977 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
5979 /* Introduce messages they have */
5980 int res;
5981 res = ast_play_and_wait(chan, "vm-youhave");
5982 if (!res) {
5983 if (vms->newmessages) {
5984 if ((vms->newmessages == 1))
5985 res = ast_play_and_wait(chan, "digits/1F");
5986 else
5987 res = say_and_wait(chan, vms->newmessages, chan->language);
5988 if (!res)
5989 res = ast_play_and_wait(chan, "vm-INBOX");
5990 if (vms->oldmessages && !res)
5991 res = ast_play_and_wait(chan, "vm-and");
5992 else if (!res) {
5993 if ((vms->newmessages == 1))
5994 res = ast_play_and_wait(chan, "vm-message");
5995 else
5996 res = ast_play_and_wait(chan, "vm-messages");
6000 if (!res && vms->oldmessages) {
6001 if (vms->oldmessages == 1)
6002 res = ast_play_and_wait(chan, "digits/1F");
6003 else
6004 res = say_and_wait(chan, vms->oldmessages, chan->language);
6005 if (!res)
6006 res = ast_play_and_wait(chan, "vm-Old");
6007 if (!res) {
6008 if (vms->oldmessages == 1)
6009 res = ast_play_and_wait(chan, "vm-message");
6010 else
6011 res = ast_play_and_wait(chan, "vm-messages");
6014 if (!res) {
6015 if (!vms->oldmessages && !vms->newmessages) {
6016 res = ast_play_and_wait(chan, "vm-no");
6017 if (!res)
6018 res = ast_play_and_wait(chan, "vm-messages");
6022 return res;
6025 /* SPANISH syntax */
6026 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
6028 /* Introduce messages they have */
6029 int res;
6030 if (!vms->oldmessages && !vms->newmessages) {
6031 res = ast_play_and_wait(chan, "vm-youhaveno");
6032 if (!res)
6033 res = ast_play_and_wait(chan, "vm-messages");
6034 } else {
6035 res = ast_play_and_wait(chan, "vm-youhave");
6037 if (!res) {
6038 if (vms->newmessages) {
6039 if (!res) {
6040 if ((vms->newmessages == 1)) {
6041 res = ast_play_and_wait(chan, "digits/1M");
6042 if (!res)
6043 res = ast_play_and_wait(chan, "vm-message");
6044 if (!res)
6045 res = ast_play_and_wait(chan, "vm-INBOXs");
6046 } else {
6047 res = say_and_wait(chan, vms->newmessages, chan->language);
6048 if (!res)
6049 res = ast_play_and_wait(chan, "vm-messages");
6050 if (!res)
6051 res = ast_play_and_wait(chan, "vm-INBOX");
6054 if (vms->oldmessages && !res)
6055 res = ast_play_and_wait(chan, "vm-and");
6057 if (vms->oldmessages) {
6058 if (!res) {
6059 if (vms->oldmessages == 1) {
6060 res = ast_play_and_wait(chan, "digits/1M");
6061 if (!res)
6062 res = ast_play_and_wait(chan, "vm-message");
6063 if (!res)
6064 res = ast_play_and_wait(chan, "vm-Olds");
6065 } else {
6066 res = say_and_wait(chan, vms->oldmessages, chan->language);
6067 if (!res)
6068 res = ast_play_and_wait(chan, "vm-messages");
6069 if (!res)
6070 res = ast_play_and_wait(chan, "vm-Old");
6075 return res;
6078 /* BRAZILIAN PORTUGUESE syntax */
6079 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
6080 /* Introduce messages they have */
6081 int res;
6082 if (!vms->oldmessages && !vms->newmessages) {
6083 res = ast_play_and_wait(chan, "vm-nomessages");
6084 return res;
6086 else {
6087 res = ast_play_and_wait(chan, "vm-youhave");
6089 if (vms->newmessages) {
6090 if (!res)
6091 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
6092 if ((vms->newmessages == 1)) {
6093 if (!res)
6094 res = ast_play_and_wait(chan, "vm-message");
6095 if (!res)
6096 res = ast_play_and_wait(chan, "vm-INBOXs");
6098 else {
6099 if (!res)
6100 res = ast_play_and_wait(chan, "vm-messages");
6101 if (!res)
6102 res = ast_play_and_wait(chan, "vm-INBOX");
6104 if (vms->oldmessages && !res)
6105 res = ast_play_and_wait(chan, "vm-and");
6107 if (vms->oldmessages) {
6108 if (!res)
6109 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
6110 if (vms->oldmessages == 1) {
6111 if (!res)
6112 res = ast_play_and_wait(chan, "vm-message");
6113 if (!res)
6114 res = ast_play_and_wait(chan, "vm-Olds");
6116 else {
6117 if (!res)
6118 res = ast_play_and_wait(chan, "vm-messages");
6119 if (!res)
6120 res = ast_play_and_wait(chan, "vm-Old");
6123 return res;
6126 /* FRENCH syntax */
6127 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
6129 /* Introduce messages they have */
6130 int res;
6131 res = ast_play_and_wait(chan, "vm-youhave");
6132 if (!res) {
6133 if (vms->newmessages) {
6134 res = say_and_wait(chan, vms->newmessages, chan->language);
6135 if (!res)
6136 res = ast_play_and_wait(chan, "vm-INBOX");
6137 if (vms->oldmessages && !res)
6138 res = ast_play_and_wait(chan, "vm-and");
6139 else if (!res) {
6140 if ((vms->newmessages == 1))
6141 res = ast_play_and_wait(chan, "vm-message");
6142 else
6143 res = ast_play_and_wait(chan, "vm-messages");
6147 if (!res && vms->oldmessages) {
6148 res = say_and_wait(chan, vms->oldmessages, chan->language);
6149 if (!res)
6150 res = ast_play_and_wait(chan, "vm-Old");
6151 if (!res) {
6152 if (vms->oldmessages == 1)
6153 res = ast_play_and_wait(chan, "vm-message");
6154 else
6155 res = ast_play_and_wait(chan, "vm-messages");
6158 if (!res) {
6159 if (!vms->oldmessages && !vms->newmessages) {
6160 res = ast_play_and_wait(chan, "vm-no");
6161 if (!res)
6162 res = ast_play_and_wait(chan, "vm-messages");
6166 return res;
6169 /* DUTCH syntax */
6170 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
6172 /* Introduce messages they have */
6173 int res;
6174 res = ast_play_and_wait(chan, "vm-youhave");
6175 if (!res) {
6176 if (vms->newmessages) {
6177 res = say_and_wait(chan, vms->newmessages, chan->language);
6178 if (!res) {
6179 if (vms->newmessages == 1)
6180 res = ast_play_and_wait(chan, "vm-INBOXs");
6181 else
6182 res = ast_play_and_wait(chan, "vm-INBOX");
6184 if (vms->oldmessages && !res)
6185 res = ast_play_and_wait(chan, "vm-and");
6186 else if (!res) {
6187 if ((vms->newmessages == 1))
6188 res = ast_play_and_wait(chan, "vm-message");
6189 else
6190 res = ast_play_and_wait(chan, "vm-messages");
6194 if (!res && vms->oldmessages) {
6195 res = say_and_wait(chan, vms->oldmessages, chan->language);
6196 if (!res) {
6197 if (vms->oldmessages == 1)
6198 res = ast_play_and_wait(chan, "vm-Olds");
6199 else
6200 res = ast_play_and_wait(chan, "vm-Old");
6202 if (!res) {
6203 if (vms->oldmessages == 1)
6204 res = ast_play_and_wait(chan, "vm-message");
6205 else
6206 res = ast_play_and_wait(chan, "vm-messages");
6209 if (!res) {
6210 if (!vms->oldmessages && !vms->newmessages) {
6211 res = ast_play_and_wait(chan, "vm-no");
6212 if (!res)
6213 res = ast_play_and_wait(chan, "vm-messages");
6217 return res;
6220 /* PORTUGUESE syntax */
6221 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
6223 /* Introduce messages they have */
6224 int res;
6225 res = ast_play_and_wait(chan, "vm-youhave");
6226 if (!res) {
6227 if (vms->newmessages) {
6228 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
6229 if (!res) {
6230 if ((vms->newmessages == 1)) {
6231 res = ast_play_and_wait(chan, "vm-message");
6232 if (!res)
6233 res = ast_play_and_wait(chan, "vm-INBOXs");
6234 } else {
6235 res = ast_play_and_wait(chan, "vm-messages");
6236 if (!res)
6237 res = ast_play_and_wait(chan, "vm-INBOX");
6240 if (vms->oldmessages && !res)
6241 res = ast_play_and_wait(chan, "vm-and");
6243 if (!res && vms->oldmessages) {
6244 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
6245 if (!res) {
6246 if (vms->oldmessages == 1) {
6247 res = ast_play_and_wait(chan, "vm-message");
6248 if (!res)
6249 res = ast_play_and_wait(chan, "vm-Olds");
6250 } else {
6251 res = ast_play_and_wait(chan, "vm-messages");
6252 if (!res)
6253 res = ast_play_and_wait(chan, "vm-Old");
6257 if (!res) {
6258 if (!vms->oldmessages && !vms->newmessages) {
6259 res = ast_play_and_wait(chan, "vm-no");
6260 if (!res)
6261 res = ast_play_and_wait(chan, "vm-messages");
6265 return res;
6269 /* CZECH syntax */
6270 /* in czech there must be declension of word new and message
6271 * czech : english : czech : english
6272 * --------------------------------------------------------
6273 * vm-youhave : you have
6274 * vm-novou : one new : vm-zpravu : message
6275 * vm-nove : 2-4 new : vm-zpravy : messages
6276 * vm-novych : 5-infinite new : vm-zprav : messages
6277 * vm-starou : one old
6278 * vm-stare : 2-4 old
6279 * vm-starych : 5-infinite old
6280 * jednu : one - falling 4.
6281 * vm-no : no ( no messages )
6284 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
6286 int res;
6287 res = ast_play_and_wait(chan, "vm-youhave");
6288 if (!res) {
6289 if (vms->newmessages) {
6290 if (vms->newmessages == 1) {
6291 res = ast_play_and_wait(chan, "digits/jednu");
6292 } else {
6293 res = say_and_wait(chan, vms->newmessages, chan->language);
6295 if (!res) {
6296 if ((vms->newmessages == 1))
6297 res = ast_play_and_wait(chan, "vm-novou");
6298 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
6299 res = ast_play_and_wait(chan, "vm-nove");
6300 if (vms->newmessages > 4)
6301 res = ast_play_and_wait(chan, "vm-novych");
6303 if (vms->oldmessages && !res)
6304 res = ast_play_and_wait(chan, "vm-and");
6305 else if (!res) {
6306 if ((vms->newmessages == 1))
6307 res = ast_play_and_wait(chan, "vm-zpravu");
6308 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
6309 res = ast_play_and_wait(chan, "vm-zpravy");
6310 if (vms->newmessages > 4)
6311 res = ast_play_and_wait(chan, "vm-zprav");
6314 if (!res && vms->oldmessages) {
6315 res = say_and_wait(chan, vms->oldmessages, chan->language);
6316 if (!res) {
6317 if ((vms->oldmessages == 1))
6318 res = ast_play_and_wait(chan, "vm-starou");
6319 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
6320 res = ast_play_and_wait(chan, "vm-stare");
6321 if (vms->oldmessages > 4)
6322 res = ast_play_and_wait(chan, "vm-starych");
6324 if (!res) {
6325 if ((vms->oldmessages == 1))
6326 res = ast_play_and_wait(chan, "vm-zpravu");
6327 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
6328 res = ast_play_and_wait(chan, "vm-zpravy");
6329 if (vms->oldmessages > 4)
6330 res = ast_play_and_wait(chan, "vm-zprav");
6333 if (!res) {
6334 if (!vms->oldmessages && !vms->newmessages) {
6335 res = ast_play_and_wait(chan, "vm-no");
6336 if (!res)
6337 res = ast_play_and_wait(chan, "vm-zpravy");
6341 return res;
6344 static int get_lastdigits(int num)
6346 num %= 100;
6347 return (num < 20) ? num : num % 10;
6350 static int vm_intro_ru(struct ast_channel *chan,struct vm_state *vms)
6352 int res;
6353 int lastnum = 0;
6354 int dcnum;
6356 res = ast_play_and_wait(chan, "vm-youhave");
6357 if (!res && vms->newmessages) {
6358 lastnum = get_lastdigits(vms->newmessages);
6359 dcnum = vms->newmessages - lastnum;
6360 if (dcnum)
6361 res = say_and_wait(chan, dcnum, chan->language);
6362 if (!res && lastnum) {
6363 if (lastnum == 1)
6364 res = ast_play_and_wait(chan, "digits/odno");
6365 else
6366 res = say_and_wait(chan, lastnum, chan->language);
6369 if (!res)
6370 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-novoe" : "vm-novyh");
6372 if (!res && vms->oldmessages)
6373 res = ast_play_and_wait(chan, "vm-and");
6376 if (!res && vms->oldmessages) {
6377 lastnum = get_lastdigits(vms->oldmessages);
6378 dcnum = vms->oldmessages - lastnum;
6379 if (dcnum)
6380 res = say_and_wait(chan, dcnum, chan->language);
6381 if (!res && lastnum) {
6382 if (lastnum == 1)
6383 res = ast_play_and_wait(chan, "digits/odno");
6384 else
6385 res = say_and_wait(chan, lastnum, chan->language);
6388 if (!res)
6389 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-staroe" : "vm-staryh");
6392 if (!res && !vms->newmessages && !vms->oldmessages) {
6393 lastnum = 0;
6394 res = ast_play_and_wait(chan, "vm-no");
6397 if (!res) {
6398 switch (lastnum) {
6399 case 1:
6400 res = ast_play_and_wait(chan, "vm-soobshenie");
6401 break;
6402 case 2:
6403 case 3:
6404 case 4:
6405 res = ast_play_and_wait(chan, "vm-soobsheniya");
6406 break;
6407 default:
6408 res = ast_play_and_wait(chan, "vm-soobsheniy");
6409 break;
6413 return res;
6416 /* UKRAINIAN syntax */
6417 /* in ukrainian the syntax is different so we need the following files
6418 * --------------------------------------------------------
6419 * /digits/ua/1e 'odne'
6420 * vm-nove 'nove'
6421 * vm-stare 'stare'
6424 static int vm_intro_ua(struct ast_channel *chan,struct vm_state *vms)
6426 int res;
6427 int lastnum = 0;
6428 int dcnum;
6430 res = ast_play_and_wait(chan, "vm-youhave");
6431 if (!res && vms->newmessages) {
6432 lastnum = get_lastdigits(vms->newmessages);
6433 dcnum = vms->newmessages - lastnum;
6434 if (dcnum)
6435 res = say_and_wait(chan, dcnum, chan->language);
6436 if (!res && lastnum) {
6437 if (lastnum == 1)
6438 res = ast_play_and_wait(chan, "digits/ua/1e");
6439 else
6440 res = say_and_wait(chan, lastnum, chan->language);
6443 if (!res)
6444 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-nove" : "vm-INBOX");
6446 if (!res && vms->oldmessages)
6447 res = ast_play_and_wait(chan, "vm-and");
6450 if (!res && vms->oldmessages) {
6451 lastnum = get_lastdigits(vms->oldmessages);
6452 dcnum = vms->oldmessages - lastnum;
6453 if (dcnum)
6454 res = say_and_wait(chan, dcnum, chan->language);
6455 if (!res && lastnum) {
6456 if (lastnum == 1)
6457 res = ast_play_and_wait(chan, "digits/ua/1e");
6458 else
6459 res = say_and_wait(chan, lastnum, chan->language);
6462 if (!res)
6463 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-stare" : "vm-Old");
6466 if (!res && !vms->newmessages && !vms->oldmessages) {
6467 lastnum = 0;
6468 res = ast_play_and_wait(chan, "vm-no");
6471 if (!res) {
6472 switch (lastnum) {
6473 case 1:
6474 case 2:
6475 case 3:
6476 case 4:
6477 res = ast_play_and_wait(chan, "vm-message");
6478 break;
6479 default:
6480 res = ast_play_and_wait(chan, "vm-messages");
6481 break;
6485 return res;
6489 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
6491 char prefile[256];
6493 /* Notify the user that the temp greeting is set and give them the option to remove it */
6494 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
6495 if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
6496 RETRIEVE(prefile, -1, vmu);
6497 if (ast_fileexists(prefile, NULL, NULL) > 0)
6498 ast_play_and_wait(chan, "vm-tempgreetactive");
6499 DISPOSE(prefile, -1);
6502 /* Play voicemail intro - syntax is different for different languages */
6503 if (!strcasecmp(chan->language, "de")) { /* GERMAN syntax */
6504 return vm_intro_de(chan, vms);
6505 } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
6506 return vm_intro_es(chan, vms);
6507 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
6508 return vm_intro_it(chan, vms);
6509 } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
6510 return vm_intro_fr(chan, vms);
6511 } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
6512 return vm_intro_nl(chan, vms);
6513 } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
6514 return vm_intro_pt(chan, vms);
6515 } else if (!strcasecmp(chan->language, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
6516 return vm_intro_pt_BR(chan, vms);
6517 } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
6518 return vm_intro_cz(chan, vms);
6519 } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
6520 return vm_intro_gr(chan, vms);
6521 } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
6522 return vm_intro_pl(chan, vms);
6523 } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
6524 return vm_intro_se(chan, vms);
6525 } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
6526 return vm_intro_no(chan, vms);
6527 } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
6528 return vm_intro_ru(chan, vms);
6529 } else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
6530 return vm_intro_ua(chan, vms);
6531 } else if (!strcasecmp(chan->language, "he")) { /* HEBREW syntax */
6532 return vm_intro_he(chan, vms);
6533 } else { /* Default to ENGLISH */
6534 return vm_intro_en(chan, vms);
6538 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
6540 int res = 0;
6541 /* Play instructions and wait for new command */
6542 while (!res) {
6543 if (vms->starting) {
6544 if (vms->lastmsg > -1) {
6545 res = ast_play_and_wait(chan, "vm-onefor");
6546 if (!strcasecmp(chan->language, "he"))
6547 res = ast_play_and_wait(chan, "vm-for");
6548 if (!res)
6549 res = vm_play_folder_name(chan, vms->vmbox);
6551 if (!res)
6552 res = ast_play_and_wait(chan, "vm-opts");
6553 } else {
6554 if (vms->curmsg)
6555 res = ast_play_and_wait(chan, "vm-prev");
6556 if (!res && !skipadvanced)
6557 res = ast_play_and_wait(chan, "vm-advopts");
6558 if (!res)
6559 res = ast_play_and_wait(chan, "vm-repeat");
6560 if (!res && (vms->curmsg != vms->lastmsg))
6561 res = ast_play_and_wait(chan, "vm-next");
6562 if (!res) {
6563 if (!vms->deleted[vms->curmsg])
6564 res = ast_play_and_wait(chan, "vm-delete");
6565 else
6566 res = ast_play_and_wait(chan, "vm-undelete");
6567 if (!res)
6568 res = ast_play_and_wait(chan, "vm-toforward");
6569 if (!res)
6570 res = ast_play_and_wait(chan, "vm-savemessage");
6573 if (!res)
6574 res = ast_play_and_wait(chan, "vm-helpexit");
6575 if (!res)
6576 res = ast_waitfordigit(chan, 6000);
6577 if (!res) {
6578 vms->repeats++;
6579 if (vms->repeats > 2) {
6580 res = 't';
6584 return res;
6587 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
6589 int cmd = 0;
6590 int duration = 0;
6591 int tries = 0;
6592 char newpassword[80] = "";
6593 char newpassword2[80] = "";
6594 char prefile[PATH_MAX] = "";
6595 unsigned char buf[256];
6596 int bytes=0;
6598 if (ast_adsi_available(chan)) {
6599 bytes += adsi_logo(buf + bytes);
6600 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
6601 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
6602 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6603 bytes += ast_adsi_voice_mode(buf + bytes, 0);
6604 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6607 /* First, have the user change their password
6608 so they won't get here again */
6609 for (;;) {
6610 newpassword[1] = '\0';
6611 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
6612 if (cmd == '#')
6613 newpassword[0] = '\0';
6614 if (cmd < 0 || cmd == 't' || cmd == '#')
6615 return cmd;
6616 cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
6617 if (cmd < 0 || cmd == 't' || cmd == '#')
6618 return cmd;
6619 newpassword2[1] = '\0';
6620 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
6621 if (cmd == '#')
6622 newpassword2[0] = '\0';
6623 if (cmd < 0 || cmd == 't' || cmd == '#')
6624 return cmd;
6625 cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
6626 if (cmd < 0 || cmd == 't' || cmd == '#')
6627 return cmd;
6628 if (!strcmp(newpassword, newpassword2))
6629 break;
6630 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
6631 cmd = ast_play_and_wait(chan, "vm-mismatch");
6632 if (++tries == 3)
6633 return -1;
6635 if (ast_strlen_zero(ext_pass_cmd))
6636 vm_change_password(vmu,newpassword);
6637 else
6638 vm_change_password_shell(vmu,newpassword);
6639 if (option_debug > 2)
6640 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
6641 cmd = ast_play_and_wait(chan,"vm-passchanged");
6643 /* If forcename is set, have the user record their name */
6644 if (ast_test_flag(vmu, VM_FORCENAME)) {
6645 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
6646 if (ast_fileexists(prefile, NULL, NULL) < 1) {
6647 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6648 if (cmd < 0 || cmd == 't' || cmd == '#')
6649 return cmd;
6653 /* If forcegreetings is set, have the user record their greetings */
6654 if (ast_test_flag(vmu, VM_FORCEGREET)) {
6655 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
6656 if (ast_fileexists(prefile, NULL, NULL) < 1) {
6657 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6658 if (cmd < 0 || cmd == 't' || cmd == '#')
6659 return cmd;
6662 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
6663 if (ast_fileexists(prefile, NULL, NULL) < 1) {
6664 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6665 if (cmd < 0 || cmd == 't' || cmd == '#')
6666 return cmd;
6670 return cmd;
6673 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
6675 int cmd = 0;
6676 int retries = 0;
6677 int duration = 0;
6678 char newpassword[80] = "";
6679 char newpassword2[80] = "";
6680 char prefile[PATH_MAX] = "";
6681 unsigned char buf[256];
6682 int bytes=0;
6684 if (ast_adsi_available(chan))
6686 bytes += adsi_logo(buf + bytes);
6687 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
6688 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
6689 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6690 bytes += ast_adsi_voice_mode(buf + bytes, 0);
6691 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6693 while ((cmd >= 0) && (cmd != 't')) {
6694 if (cmd)
6695 retries = 0;
6696 switch (cmd) {
6697 case '1':
6698 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
6699 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6700 break;
6701 case '2':
6702 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
6703 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6704 break;
6705 case '3':
6706 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
6707 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6708 break;
6709 case '4':
6710 cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
6711 break;
6712 case '5':
6713 if (vmu->password[0] == '-') {
6714 cmd = ast_play_and_wait(chan, "vm-no");
6715 break;
6717 newpassword[1] = '\0';
6718 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
6719 if (cmd == '#')
6720 newpassword[0] = '\0';
6721 else {
6722 if (cmd < 0)
6723 break;
6724 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
6725 break;
6728 newpassword2[1] = '\0';
6729 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
6730 if (cmd == '#')
6731 newpassword2[0] = '\0';
6732 else {
6733 if (cmd < 0)
6734 break;
6736 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
6737 break;
6740 if (strcmp(newpassword, newpassword2)) {
6741 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
6742 cmd = ast_play_and_wait(chan, "vm-mismatch");
6743 break;
6745 if (ast_strlen_zero(ext_pass_cmd))
6746 vm_change_password(vmu,newpassword);
6747 else
6748 vm_change_password_shell(vmu,newpassword);
6749 if (option_debug > 2)
6750 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
6751 cmd = ast_play_and_wait(chan,"vm-passchanged");
6752 break;
6753 case '*':
6754 cmd = 't';
6755 break;
6756 default:
6757 cmd = 0;
6758 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
6759 RETRIEVE(prefile, -1, vmu);
6760 if (ast_fileexists(prefile, NULL, NULL))
6761 cmd = ast_play_and_wait(chan, "vm-tmpexists");
6762 DISPOSE(prefile, -1);
6763 if (!cmd)
6764 cmd = ast_play_and_wait(chan, "vm-options");
6765 if (!cmd)
6766 cmd = ast_waitfordigit(chan,6000);
6767 if (!cmd)
6768 retries++;
6769 if (retries > 3)
6770 cmd = 't';
6773 if (cmd == 't')
6774 cmd = 0;
6775 return cmd;
6778 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
6780 int res;
6781 int cmd = 0;
6782 int retries = 0;
6783 int duration = 0;
6784 char prefile[PATH_MAX] = "";
6785 unsigned char buf[256];
6786 char dest[PATH_MAX];
6787 int bytes = 0;
6789 if (ast_adsi_available(chan)) {
6790 bytes += adsi_logo(buf + bytes);
6791 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
6792 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
6793 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6794 bytes += ast_adsi_voice_mode(buf + bytes, 0);
6795 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6798 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
6799 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, vms->username, "temp"))) {
6800 ast_log(LOG_WARNING, "Failed to create directory (%s).\n", prefile);
6801 return -1;
6803 while ((cmd >= 0) && (cmd != 't')) {
6804 if (cmd)
6805 retries = 0;
6806 RETRIEVE(prefile, -1, vmu);
6807 if (ast_fileexists(prefile, NULL, NULL) <= 0) {
6808 play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6809 cmd = 't';
6810 } else {
6811 switch (cmd) {
6812 case '1':
6813 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6814 break;
6815 case '2':
6816 DELETE(prefile, -1, prefile, vmu);
6817 ast_play_and_wait(chan, "vm-tempremoved");
6818 cmd = 't';
6819 break;
6820 case '*':
6821 cmd = 't';
6822 break;
6823 default:
6824 cmd = ast_play_and_wait(chan,
6825 ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
6826 "vm-tempgreeting2" : "vm-tempgreeting");
6827 if (!cmd)
6828 cmd = ast_waitfordigit(chan,6000);
6829 if (!cmd)
6830 retries++;
6831 if (retries > 3)
6832 cmd = 't';
6835 DISPOSE(prefile, -1);
6837 if (cmd == 't')
6838 cmd = 0;
6839 return cmd;
6842 /* GREEK SYNTAX */
6844 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6846 int cmd=0;
6848 if (vms->lastmsg > -1) {
6849 cmd = play_message(chan, vmu, vms);
6850 } else {
6851 cmd = ast_play_and_wait(chan, "vm-youhaveno");
6852 if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
6853 if (!cmd) {
6854 snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
6855 cmd = ast_play_and_wait(chan, vms->fn);
6857 if (!cmd)
6858 cmd = ast_play_and_wait(chan, "vm-messages");
6859 } else {
6860 if (!cmd)
6861 cmd = ast_play_and_wait(chan, "vm-messages");
6862 if (!cmd) {
6863 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6864 cmd = ast_play_and_wait(chan, vms->fn);
6868 return cmd;
6871 /* Default English syntax */
6872 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6874 int cmd=0;
6876 if (vms->lastmsg > -1) {
6877 cmd = play_message(chan, vmu, vms);
6878 } else {
6879 cmd = ast_play_and_wait(chan, "vm-youhave");
6880 if (!cmd)
6881 cmd = ast_play_and_wait(chan, "vm-no");
6882 if (!cmd) {
6883 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6884 cmd = ast_play_and_wait(chan, vms->fn);
6886 if (!cmd)
6887 cmd = ast_play_and_wait(chan, "vm-messages");
6889 return cmd;
6892 /* Hebrew Syntax */
6893 static int vm_browse_messages_he(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6895 int cmd = 0;
6897 if (vms->lastmsg > -1) {
6898 cmd = play_message(chan, vmu, vms);
6899 } else {
6900 if (!strcasecmp(vms->fn, "INBOX")) {
6901 cmd = ast_play_and_wait(chan, "vm-nonewmessages");
6902 } else {
6903 cmd = ast_play_and_wait(chan, "vm-nomessages");
6906 return cmd;
6910 /* ITALIAN syntax */
6911 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6913 int cmd=0;
6915 if (vms->lastmsg > -1) {
6916 cmd = play_message(chan, vmu, vms);
6917 } else {
6918 cmd = ast_play_and_wait(chan, "vm-no");
6919 if (!cmd)
6920 cmd = ast_play_and_wait(chan, "vm-message");
6921 if (!cmd) {
6922 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6923 cmd = ast_play_and_wait(chan, vms->fn);
6926 return cmd;
6929 /* SPANISH syntax */
6930 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6932 int cmd=0;
6934 if (vms->lastmsg > -1) {
6935 cmd = play_message(chan, vmu, vms);
6936 } else {
6937 cmd = ast_play_and_wait(chan, "vm-youhaveno");
6938 if (!cmd)
6939 cmd = ast_play_and_wait(chan, "vm-messages");
6940 if (!cmd) {
6941 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6942 cmd = ast_play_and_wait(chan, vms->fn);
6945 return cmd;
6948 /* PORTUGUESE syntax */
6949 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6951 int cmd=0;
6953 if (vms->lastmsg > -1) {
6954 cmd = play_message(chan, vmu, vms);
6955 } else {
6956 cmd = ast_play_and_wait(chan, "vm-no");
6957 if (!cmd) {
6958 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6959 cmd = ast_play_and_wait(chan, vms->fn);
6961 if (!cmd)
6962 cmd = ast_play_and_wait(chan, "vm-messages");
6964 return cmd;
6967 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6969 if (!strcasecmp(chan->language, "es")) { /* SPANISH */
6970 return vm_browse_messages_es(chan, vms, vmu);
6971 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
6972 return vm_browse_messages_it(chan, vms, vmu);
6973 } else if (!strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* PORTUGUESE */
6974 return vm_browse_messages_pt(chan, vms, vmu);
6975 } else if (!strcasecmp(chan->language, "gr")){
6976 return vm_browse_messages_gr(chan, vms, vmu); /* GREEK */
6977 } else if (!strcasecmp(chan->language, "he")) {
6978 return vm_browse_messages_he(chan, vms, vmu); /* HEBREW */
6979 } else { /* Default to English syntax */
6980 return vm_browse_messages_en(chan, vms, vmu);
6984 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
6985 struct ast_vm_user *res_vmu, const char *context, const char *prefix,
6986 int skipuser, int maxlogins, int silent)
6988 int useadsi=0, valid=0, logretries=0;
6989 char password[AST_MAX_EXTENSION]="", *passptr;
6990 struct ast_vm_user vmus, *vmu = NULL;
6992 /* If ADSI is supported, setup login screen */
6993 adsi_begin(chan, &useadsi);
6994 if (!skipuser && useadsi)
6995 adsi_login(chan);
6996 if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
6997 ast_log(LOG_WARNING, "Couldn't stream login file\n");
6998 return -1;
7001 /* Authenticate them and get their mailbox/password */
7003 while (!valid && (logretries < maxlogins)) {
7004 /* Prompt for, and read in the username */
7005 if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
7006 ast_log(LOG_WARNING, "Couldn't read username\n");
7007 return -1;
7009 if (ast_strlen_zero(mailbox)) {
7010 if (chan->cid.cid_num) {
7011 ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
7012 } else {
7013 if (option_verbose > 2)
7014 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
7015 return -1;
7018 if (useadsi)
7019 adsi_password(chan);
7021 if (!ast_strlen_zero(prefix)) {
7022 char fullusername[80] = "";
7023 ast_copy_string(fullusername, prefix, sizeof(fullusername));
7024 strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
7025 ast_copy_string(mailbox, fullusername, mailbox_size);
7028 if (option_debug)
7029 ast_log(LOG_DEBUG, "Before find user for mailbox %s\n",mailbox);
7030 vmu = find_user(&vmus, context, mailbox);
7031 if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
7032 /* saved password is blank, so don't bother asking */
7033 password[0] = '\0';
7034 } else {
7035 if (ast_streamfile(chan, "vm-password", chan->language)) {
7036 ast_log(LOG_WARNING, "Unable to stream password file\n");
7037 return -1;
7039 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
7040 ast_log(LOG_WARNING, "Unable to read password\n");
7041 return -1;
7045 if (vmu) {
7046 passptr = vmu->password;
7047 if (passptr[0] == '-') passptr++;
7049 if (vmu && !strcmp(passptr, password))
7050 valid++;
7051 else {
7052 if (option_verbose > 2)
7053 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
7054 if (!ast_strlen_zero(prefix))
7055 mailbox[0] = '\0';
7057 logretries++;
7058 if (!valid) {
7059 if (skipuser || logretries >= maxlogins) {
7060 if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
7061 ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
7062 return -1;
7064 } else {
7065 if (useadsi)
7066 adsi_login(chan);
7067 if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
7068 ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
7069 return -1;
7072 if (ast_waitstream(chan, "")) /* Channel is hung up */
7073 return -1;
7076 if (!valid && (logretries >= maxlogins)) {
7077 ast_stopstream(chan);
7078 ast_play_and_wait(chan, "vm-goodbye");
7079 return -1;
7081 if (vmu && !skipuser) {
7082 memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
7084 return 0;
7087 static int vm_execmain(struct ast_channel *chan, void *data)
7089 /* XXX This is, admittedly, some pretty horrendus code. For some
7090 reason it just seemed a lot easier to do with GOTO's. I feel
7091 like I'm back in my GWBASIC days. XXX */
7092 int res=-1;
7093 int cmd=0;
7094 int valid = 0;
7095 struct ast_module_user *u;
7096 char prefixstr[80] ="";
7097 char ext_context[256]="";
7098 int box;
7099 int useadsi = 0;
7100 int skipuser = 0;
7101 struct vm_state vms;
7102 struct ast_vm_user *vmu = NULL, vmus;
7103 char *context=NULL;
7104 int silentexit = 0;
7105 struct ast_flags flags = { 0 };
7106 signed char record_gain = 0;
7107 int play_auto = 0;
7108 int play_folder = 0;
7109 #ifdef IMAP_STORAGE
7110 int deleted = 0;
7111 #endif
7112 u = ast_module_user_add(chan);
7114 /* Add the vm_state to the active list and keep it active */
7115 memset(&vms, 0, sizeof(vms));
7116 vms.lastmsg = -1;
7118 memset(&vmus, 0, sizeof(vmus));
7120 if (chan->_state != AST_STATE_UP) {
7121 if (option_debug)
7122 ast_log(LOG_DEBUG, "Before ast_answer\n");
7123 ast_answer(chan);
7126 if (!ast_strlen_zero(data)) {
7127 char *opts[OPT_ARG_ARRAY_SIZE];
7128 char *parse;
7129 AST_DECLARE_APP_ARGS(args,
7130 AST_APP_ARG(argv0);
7131 AST_APP_ARG(argv1);
7134 parse = ast_strdupa(data);
7136 AST_STANDARD_APP_ARGS(args, parse);
7138 if (args.argc == 2) {
7139 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
7140 ast_module_user_remove(u);
7141 return -1;
7143 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
7144 int gain;
7145 if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
7146 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
7147 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
7148 ast_module_user_remove(u);
7149 return -1;
7150 } else {
7151 record_gain = (signed char) gain;
7153 } else {
7154 ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
7157 if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
7158 play_auto = 1;
7159 if (opts[OPT_ARG_PLAYFOLDER]) {
7160 if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%d", &play_folder) != 1) {
7161 ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
7163 } else {
7164 ast_log(LOG_WARNING, "Invalid folder set with option a\n");
7166 if ( play_folder > 9 || play_folder < 0) {
7167 ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
7168 play_folder = 0;
7171 } else {
7172 /* old style options parsing */
7173 while (*(args.argv0)) {
7174 if (*(args.argv0) == 's')
7175 ast_set_flag(&flags, OPT_SILENT);
7176 else if (*(args.argv0) == 'p')
7177 ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
7178 else
7179 break;
7180 (args.argv0)++;
7185 valid = ast_test_flag(&flags, OPT_SILENT);
7187 if ((context = strchr(args.argv0, '@')))
7188 *context++ = '\0';
7190 if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
7191 ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
7192 else
7193 ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
7195 if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
7196 skipuser++;
7197 else
7198 valid = 0;
7201 if (!valid)
7202 res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
7204 if (option_debug)
7205 ast_log(LOG_DEBUG, "After vm_authenticate\n");
7206 if (!res) {
7207 valid = 1;
7208 if (!skipuser)
7209 vmu = &vmus;
7210 } else {
7211 res = 0;
7214 /* If ADSI is supported, setup login screen */
7215 adsi_begin(chan, &useadsi);
7217 #ifdef IMAP_STORAGE
7218 vms.interactive = 1;
7219 vms.updated = 1;
7220 ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
7221 vmstate_insert(&vms);
7222 init_vm_state(&vms);
7223 #endif
7224 if (!valid)
7225 goto out;
7227 if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
7228 /* TODO: Handle memory allocation failure */
7230 if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
7231 /* TODO: Handle memory allocation failure */
7234 /* Set language from config to override channel language */
7235 if (!ast_strlen_zero(vmu->language))
7236 ast_string_field_set(chan, language, vmu->language);
7237 create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
7238 /* Retrieve old and new message counts */
7239 if (option_debug)
7240 ast_log(LOG_DEBUG, "Before open_mailbox\n");
7241 res = open_mailbox(&vms, vmu, 1);
7242 if (res == ERROR_LOCK_PATH)
7243 goto out;
7244 vms.oldmessages = vms.lastmsg + 1;
7245 if (option_debug > 2)
7246 ast_log(LOG_DEBUG, "Number of old messages: %d\n",vms.oldmessages);
7247 /* Start in INBOX */
7248 res = open_mailbox(&vms, vmu, 0);
7249 if (res == ERROR_LOCK_PATH)
7250 goto out;
7251 vms.newmessages = vms.lastmsg + 1;
7252 if (option_debug > 2)
7253 ast_log(LOG_DEBUG, "Number of new messages: %d\n",vms.newmessages);
7255 /* Select proper mailbox FIRST!! */
7256 if (play_auto) {
7257 res = open_mailbox(&vms, vmu, play_folder);
7258 if (res == ERROR_LOCK_PATH)
7259 goto out;
7261 /* If there are no new messages, inform the user and hangup */
7262 if (vms.lastmsg == -1) {
7263 cmd = vm_browse_messages(chan, &vms, vmu);
7264 res = 0;
7265 goto out;
7267 } else {
7268 if (!vms.newmessages && vms.oldmessages) {
7269 /* If we only have old messages start here */
7270 res = open_mailbox(&vms, vmu, 1);
7271 play_folder = 1;
7272 if (res == ERROR_LOCK_PATH)
7273 goto out;
7277 if (useadsi)
7278 adsi_status(chan, &vms);
7279 res = 0;
7281 /* Check to see if this is a new user */
7282 if (!strcasecmp(vmu->mailbox, vmu->password) &&
7283 (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
7284 if (ast_play_and_wait(chan, "vm-newuser") == -1)
7285 ast_log(LOG_WARNING, "Couldn't stream new user file\n");
7286 cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
7287 if ((cmd == 't') || (cmd == '#')) {
7288 /* Timeout */
7289 res = 0;
7290 goto out;
7291 } else if (cmd < 0) {
7292 /* Hangup */
7293 res = -1;
7294 goto out;
7297 #ifdef IMAP_STORAGE
7298 if (option_debug > 2)
7299 ast_log(LOG_DEBUG, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
7300 if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
7301 if (option_debug)
7302 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
7303 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
7305 if (option_debug > 2)
7306 ast_log(LOG_DEBUG, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
7307 if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
7308 ast_log(LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
7309 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
7311 #endif
7312 if (play_auto) {
7313 cmd = '1';
7314 } else {
7315 cmd = vm_intro(chan, vmu, &vms);
7318 vms.repeats = 0;
7319 vms.starting = 1;
7320 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
7321 /* Run main menu */
7322 switch (cmd) {
7323 case '1':
7324 vms.curmsg = 0;
7325 /* Fall through */
7326 case '5':
7327 cmd = vm_browse_messages(chan, &vms, vmu);
7328 break;
7329 case '2': /* Change folders */
7330 if (useadsi)
7331 adsi_folders(chan, 0, "Change to folder...");
7332 cmd = get_folder2(chan, "vm-changeto", 0);
7333 if (cmd == '#') {
7334 cmd = 0;
7335 } else if (cmd > 0) {
7336 cmd = cmd - '0';
7337 res = close_mailbox(&vms, vmu);
7338 if (res == ERROR_LOCK_PATH)
7339 goto out;
7340 res = open_mailbox(&vms, vmu, cmd);
7341 if (res == ERROR_LOCK_PATH)
7342 goto out;
7343 play_folder = cmd;
7344 cmd = 0;
7346 if (useadsi)
7347 adsi_status2(chan, &vms);
7349 if (!cmd)
7350 cmd = vm_play_folder_name(chan, vms.vmbox);
7352 vms.starting = 1;
7353 break;
7354 case '3': /* Advanced options */
7355 cmd = 0;
7356 vms.repeats = 0;
7357 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
7358 switch (cmd) {
7359 case '1': /* Reply */
7360 if (vms.lastmsg > -1 && !vms.starting) {
7361 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
7362 if (cmd == ERROR_LOCK_PATH) {
7363 res = cmd;
7364 goto out;
7366 } else
7367 cmd = ast_play_and_wait(chan, "vm-sorry");
7368 cmd = 't';
7369 break;
7370 case '2': /* Callback */
7371 if (option_verbose > 2 && !vms.starting)
7372 ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
7373 if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
7374 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
7375 if (cmd == 9) {
7376 silentexit = 1;
7377 goto out;
7378 } else if (cmd == ERROR_LOCK_PATH) {
7379 res = cmd;
7380 goto out;
7383 else
7384 cmd = ast_play_and_wait(chan, "vm-sorry");
7385 cmd = 't';
7386 break;
7387 case '3': /* Envelope */
7388 if (vms.lastmsg > -1 && !vms.starting) {
7389 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
7390 if (cmd == ERROR_LOCK_PATH) {
7391 res = cmd;
7392 goto out;
7394 } else
7395 cmd = ast_play_and_wait(chan, "vm-sorry");
7396 cmd = 't';
7397 break;
7398 case '4': /* Dialout */
7399 if (!ast_strlen_zero(vmu->dialout)) {
7400 cmd = dialout(chan, vmu, NULL, vmu->dialout);
7401 if (cmd == 9) {
7402 silentexit = 1;
7403 goto out;
7406 else
7407 cmd = ast_play_and_wait(chan, "vm-sorry");
7408 cmd = 't';
7409 break;
7411 case '5': /* Leave VoiceMail */
7412 if (ast_test_flag(vmu, VM_SVMAIL)) {
7413 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
7414 if (cmd == ERROR_LOCK_PATH) {
7415 res = cmd;
7416 ast_log(LOG_WARNING, "forward_message failed to lock path.\n");
7417 goto out;
7419 } else
7420 cmd = ast_play_and_wait(chan,"vm-sorry");
7421 cmd='t';
7422 break;
7424 case '*': /* Return to main menu */
7425 cmd = 't';
7426 break;
7428 default:
7429 cmd = 0;
7430 if (!vms.starting) {
7431 cmd = ast_play_and_wait(chan, "vm-toreply");
7433 if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
7434 cmd = ast_play_and_wait(chan, "vm-tocallback");
7436 if (!cmd && !vms.starting) {
7437 cmd = ast_play_and_wait(chan, "vm-tohearenv");
7439 if (!ast_strlen_zero(vmu->dialout) && !cmd) {
7440 cmd = ast_play_and_wait(chan, "vm-tomakecall");
7442 if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
7443 cmd=ast_play_and_wait(chan, "vm-leavemsg");
7444 if (!cmd)
7445 cmd = ast_play_and_wait(chan, "vm-starmain");
7446 if (!cmd)
7447 cmd = ast_waitfordigit(chan,6000);
7448 if (!cmd)
7449 vms.repeats++;
7450 if (vms.repeats > 3)
7451 cmd = 't';
7454 if (cmd == 't') {
7455 cmd = 0;
7456 vms.repeats = 0;
7458 break;
7459 case '4':
7460 if (vms.curmsg > 0) {
7461 vms.curmsg--;
7462 cmd = play_message(chan, vmu, &vms);
7463 } else {
7464 cmd = ast_play_and_wait(chan, "vm-nomore");
7466 break;
7467 case '6':
7468 if (vms.curmsg < vms.lastmsg) {
7469 vms.curmsg++;
7470 cmd = play_message(chan, vmu, &vms);
7471 } else {
7472 cmd = ast_play_and_wait(chan, "vm-nomore");
7474 break;
7475 case '7':
7476 if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
7477 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
7478 if (useadsi)
7479 adsi_delete(chan, &vms);
7480 if (vms.deleted[vms.curmsg]) {
7481 if (play_folder == 0)
7482 vms.newmessages--;
7483 else if (play_folder == 1)
7484 vms.oldmessages--;
7485 cmd = ast_play_and_wait(chan, "vm-deleted");
7487 else {
7488 if (play_folder == 0)
7489 vms.newmessages++;
7490 else if (play_folder == 1)
7491 vms.oldmessages++;
7492 cmd = ast_play_and_wait(chan, "vm-undeleted");
7494 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
7495 if (vms.curmsg < vms.lastmsg) {
7496 vms.curmsg++;
7497 cmd = play_message(chan, vmu, &vms);
7498 } else {
7499 cmd = ast_play_and_wait(chan, "vm-nomore");
7502 } else /* Delete not valid if we haven't selected a message */
7503 cmd = 0;
7504 #ifdef IMAP_STORAGE
7505 deleted = 1;
7506 #endif
7507 break;
7509 case '8':
7510 if (vms.lastmsg > -1) {
7511 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
7512 if (cmd == ERROR_LOCK_PATH) {
7513 res = cmd;
7514 goto out;
7516 } else
7517 cmd = ast_play_and_wait(chan, "vm-nomore");
7518 break;
7519 case '9':
7520 if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
7521 /* No message selected */
7522 cmd = 0;
7523 break;
7525 if (useadsi)
7526 adsi_folders(chan, 1, "Save to folder...");
7527 cmd = get_folder2(chan, "vm-savefolder", 1);
7528 box = 0; /* Shut up compiler */
7529 if (cmd == '#') {
7530 cmd = 0;
7531 break;
7532 } else if (cmd > 0) {
7533 box = cmd = cmd - '0';
7534 cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
7535 if (cmd == ERROR_LOCK_PATH) {
7536 res = cmd;
7537 goto out;
7538 #ifndef IMAP_STORAGE
7539 } else if (!cmd) {
7540 vms.deleted[vms.curmsg] = 1;
7541 #endif
7542 } else {
7543 vms.deleted[vms.curmsg] = 0;
7544 vms.heard[vms.curmsg] = 0;
7547 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
7548 if (useadsi)
7549 adsi_message(chan, &vms);
7550 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
7551 if (!cmd) {
7552 cmd = ast_play_and_wait(chan, "vm-message");
7553 if (!cmd)
7554 cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
7555 if (!cmd)
7556 cmd = ast_play_and_wait(chan, "vm-savedto");
7557 if (!cmd)
7558 cmd = vm_play_folder_name(chan, vms.fn);
7559 } else {
7560 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
7562 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
7563 if (vms.curmsg < vms.lastmsg) {
7564 vms.curmsg++;
7565 cmd = play_message(chan, vmu, &vms);
7566 } else {
7567 cmd = ast_play_and_wait(chan, "vm-nomore");
7570 break;
7571 case '*':
7572 if (!vms.starting) {
7573 cmd = ast_play_and_wait(chan, "vm-onefor");
7574 if (!strcasecmp(chan->language, "he"))
7575 cmd = ast_play_and_wait(chan, "vm-for");
7576 if (!cmd)
7577 cmd = vm_play_folder_name(chan, vms.vmbox);
7578 if (!cmd)
7579 cmd = ast_play_and_wait(chan, "vm-opts");
7580 if (!cmd)
7581 cmd = vm_instructions(chan, &vms, 1);
7582 } else
7583 cmd = 0;
7584 break;
7585 case '0':
7586 cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
7587 if (useadsi)
7588 adsi_status(chan, &vms);
7589 break;
7590 default: /* Nothing */
7591 cmd = vm_instructions(chan, &vms, 0);
7592 break;
7595 if ((cmd == 't') || (cmd == '#')) {
7596 /* Timeout */
7597 res = 0;
7598 } else {
7599 /* Hangup */
7600 res = -1;
7603 out:
7604 if (res > -1) {
7605 ast_stopstream(chan);
7606 adsi_goodbye(chan);
7607 if (valid) {
7608 if (silentexit)
7609 res = ast_play_and_wait(chan, "vm-dialout");
7610 else
7611 res = ast_play_and_wait(chan, "vm-goodbye");
7612 if (res > 0)
7613 res = 0;
7615 if (useadsi)
7616 ast_adsi_unload_session(chan);
7618 if (vmu)
7619 close_mailbox(&vms, vmu);
7620 if (valid) {
7621 snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
7622 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
7623 run_externnotify(vmu->context, vmu->mailbox);
7625 #ifdef IMAP_STORAGE
7626 /* expunge message - use UID Expunge if supported on IMAP server*/
7627 if (option_debug > 2)
7628 ast_log(LOG_DEBUG, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
7629 if (vmu && deleted == 1 && expungeonhangup == 1) {
7630 #ifdef HAVE_IMAP_TK2006
7631 if (LEVELUIDPLUS (vms.mailstream)) {
7632 mail_expunge_full(vms.mailstream,NIL,EX_UID);
7633 } else
7634 #endif
7635 mail_expunge(vms.mailstream);
7637 /* before we delete the state, we should copy pertinent info
7638 * back to the persistent model */
7639 vmstate_delete(&vms);
7640 #endif
7641 if (vmu)
7642 free_user(vmu);
7643 if (vms.deleted)
7644 free(vms.deleted);
7645 if (vms.heard)
7646 free(vms.heard);
7647 ast_module_user_remove(u);
7649 return res;
7652 static int vm_exec(struct ast_channel *chan, void *data)
7654 int res = 0;
7655 struct ast_module_user *u;
7656 char *tmp;
7657 struct leave_vm_options leave_options;
7658 struct ast_flags flags = { 0 };
7659 static int deprecate_warning = 0;
7660 char *opts[OPT_ARG_ARRAY_SIZE];
7661 AST_DECLARE_APP_ARGS(args,
7662 AST_APP_ARG(argv0);
7663 AST_APP_ARG(argv1);
7666 u = ast_module_user_add(chan);
7668 memset(&leave_options, 0, sizeof(leave_options));
7670 if (chan->_state != AST_STATE_UP)
7671 ast_answer(chan);
7673 if (!ast_strlen_zero(data)) {
7674 tmp = ast_strdupa(data);
7675 AST_STANDARD_APP_ARGS(args, tmp);
7676 if (args.argc == 2) {
7677 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
7678 ast_module_user_remove(u);
7679 return -1;
7681 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP);
7682 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
7683 int gain;
7685 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
7686 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
7687 ast_module_user_remove(u);
7688 return -1;
7689 } else {
7690 leave_options.record_gain = (signed char) gain;
7693 } else {
7694 /* old style options parsing */
7695 int old = 0;
7696 char *orig_argv0 = args.argv0;
7697 while (*(args.argv0)) {
7698 if (*(args.argv0) == 's') {
7699 old = 1;
7700 ast_set_flag(&leave_options, OPT_SILENT);
7701 } else if (*(args.argv0) == 'b') {
7702 old = 1;
7703 ast_set_flag(&leave_options, OPT_BUSY_GREETING);
7704 } else if (*(args.argv0) == 'u') {
7705 old = 1;
7706 ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
7707 } else if (*(args.argv0) == 'j') {
7708 old = 1;
7709 ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
7710 } else
7711 break;
7712 (args.argv0)++;
7714 if (!deprecate_warning && old) {
7715 deprecate_warning = 1;
7716 ast_log(LOG_WARNING, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0);
7717 ast_log(LOG_WARNING, "Please move all leading options to the second argument.\n");
7720 } else {
7721 char tmp[256];
7722 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
7723 if (res < 0) {
7724 ast_module_user_remove(u);
7725 return res;
7727 if (ast_strlen_zero(tmp)) {
7728 ast_module_user_remove(u);
7729 return 0;
7731 args.argv0 = ast_strdupa(tmp);
7734 res = leave_voicemail(chan, args.argv0, &leave_options);
7736 if (res == ERROR_LOCK_PATH) {
7737 ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
7738 /*Send the call to n+101 priority, where n is the current priority*/
7739 if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
7740 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
7741 ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
7742 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7743 res = 0;
7746 ast_module_user_remove(u);
7748 return res;
7751 static struct ast_vm_user *find_or_create(char *context, char *mbox)
7753 struct ast_vm_user *vmu;
7754 AST_LIST_TRAVERSE(&users, vmu, list) {
7755 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox))
7756 break;
7757 if (context && (!strcasecmp(context, vmu->context)) && (!strcasecmp(mbox, vmu->mailbox)))
7758 break;
7761 if (!vmu) {
7762 if ((vmu = ast_calloc(1, sizeof(*vmu)))) {
7763 ast_copy_string(vmu->context, context, sizeof(vmu->context));
7764 ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
7765 AST_LIST_INSERT_TAIL(&users, vmu, list);
7768 return vmu;
7771 static int append_mailbox(char *context, char *mbox, char *data)
7773 /* Assumes lock is already held */
7774 char *tmp;
7775 char *stringp;
7776 char *s;
7777 struct ast_vm_user *vmu;
7779 tmp = ast_strdupa(data);
7781 if ((vmu = find_or_create(context, mbox))) {
7782 populate_defaults(vmu);
7784 stringp = tmp;
7785 if ((s = strsep(&stringp, ",")))
7786 ast_copy_string(vmu->password, s, sizeof(vmu->password));
7787 if (stringp && (s = strsep(&stringp, ",")))
7788 ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
7789 if (stringp && (s = strsep(&stringp, ",")))
7790 ast_copy_string(vmu->email, s, sizeof(vmu->email));
7791 if (stringp && (s = strsep(&stringp, ",")))
7792 ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
7793 if (stringp && (s = strsep(&stringp, ",")))
7794 apply_options(vmu, s);
7796 return 0;
7799 static int vm_box_exists(struct ast_channel *chan, void *data)
7801 struct ast_module_user *u;
7802 struct ast_vm_user svm;
7803 char *context, *box;
7804 int priority_jump = 0;
7805 AST_DECLARE_APP_ARGS(args,
7806 AST_APP_ARG(mbox);
7807 AST_APP_ARG(options);
7810 if (ast_strlen_zero(data)) {
7811 ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
7812 return -1;
7815 u = ast_module_user_add(chan);
7817 box = ast_strdupa(data);
7819 AST_STANDARD_APP_ARGS(args, box);
7821 if (args.options) {
7822 if (strchr(args.options, 'j'))
7823 priority_jump = 1;
7826 if ((context = strchr(args.mbox, '@'))) {
7827 *context = '\0';
7828 context++;
7831 if (find_user(&svm, context, args.mbox)) {
7832 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
7833 if (priority_jump || ast_opt_priority_jumping)
7834 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
7835 ast_log(LOG_WARNING, "VM box %s@%s exists, but extension %s, priority %d doesn't exist\n", box, context, chan->exten, chan->priority + 101);
7836 } else
7837 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
7838 ast_module_user_remove(u);
7839 return 0;
7842 static int vmauthenticate(struct ast_channel *chan, void *data)
7844 struct ast_module_user *u;
7845 char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
7846 struct ast_vm_user vmus;
7847 char *options = NULL;
7848 int silent = 0, skipuser = 0;
7849 int res = -1;
7851 u = ast_module_user_add(chan);
7853 if (s) {
7854 s = ast_strdupa(s);
7855 user = strsep(&s, "|");
7856 options = strsep(&s, "|");
7857 if (user) {
7858 s = user;
7859 user = strsep(&s, "@");
7860 context = strsep(&s, "");
7861 if (!ast_strlen_zero(user))
7862 skipuser++;
7863 ast_copy_string(mailbox, user, sizeof(mailbox));
7867 if (options) {
7868 silent = (strchr(options, 's')) != NULL;
7871 if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
7872 pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
7873 pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
7874 ast_play_and_wait(chan, "auth-thankyou");
7875 res = 0;
7878 ast_module_user_remove(u);
7879 return res;
7882 static char voicemail_show_users_help[] =
7883 "Usage: voicemail show users [for <context>]\n"
7884 " Lists all mailboxes currently set up\n";
7886 static char voicemail_show_zones_help[] =
7887 "Usage: voicemail show zones\n"
7888 " Lists zone message formats\n";
7890 static int handle_voicemail_show_users(int fd, int argc, char *argv[])
7892 struct ast_vm_user *vmu;
7893 char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
7895 if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
7896 else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
7898 AST_LIST_LOCK(&users);
7899 if (!AST_LIST_EMPTY(&users)) {
7900 if (argc == 3)
7901 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
7902 else {
7903 int count = 0;
7904 AST_LIST_TRAVERSE(&users, vmu, list) {
7905 if (!strcmp(argv[4],vmu->context))
7906 count++;
7908 if (count) {
7909 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
7910 } else {
7911 ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
7912 AST_LIST_UNLOCK(&users);
7913 return RESULT_FAILURE;
7916 AST_LIST_TRAVERSE(&users, vmu, list) {
7917 int newmsgs = 0, oldmsgs = 0;
7918 char count[12], tmp[256] = "";
7920 if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
7921 snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
7922 inboxcount(tmp, &newmsgs, &oldmsgs);
7923 snprintf(count,sizeof(count),"%d",newmsgs);
7924 ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
7927 } else {
7928 ast_cli(fd, "There are no voicemail users currently defined\n");
7929 AST_LIST_UNLOCK(&users);
7930 return RESULT_FAILURE;
7932 AST_LIST_UNLOCK(&users);
7933 return RESULT_SUCCESS;
7936 static int handle_voicemail_show_zones(int fd, int argc, char *argv[])
7938 struct vm_zone *zone;
7939 char *output_format = "%-15s %-20s %-45s\n";
7940 int res = RESULT_SUCCESS;
7942 if (argc != 3)
7943 return RESULT_SHOWUSAGE;
7945 AST_LIST_LOCK(&zones);
7946 if (!AST_LIST_EMPTY(&zones)) {
7947 ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
7948 AST_LIST_TRAVERSE(&zones, zone, list) {
7949 ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
7951 } else {
7952 ast_cli(fd, "There are no voicemail zones currently defined\n");
7953 res = RESULT_FAILURE;
7955 AST_LIST_UNLOCK(&zones);
7957 return res;
7960 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
7962 int which = 0;
7963 int wordlen;
7964 struct ast_vm_user *vmu;
7965 const char *context = "";
7967 /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
7968 if (pos > 4)
7969 return NULL;
7970 if (pos == 3)
7971 return (state == 0) ? ast_strdup("for") : NULL;
7972 wordlen = strlen(word);
7973 AST_LIST_TRAVERSE(&users, vmu, list) {
7974 if (!strncasecmp(word, vmu->context, wordlen)) {
7975 if (context && strcmp(context, vmu->context) && ++which > state)
7976 return ast_strdup(vmu->context);
7977 /* ignore repeated contexts ? */
7978 context = vmu->context;
7981 return NULL;
7984 static struct ast_cli_entry cli_show_voicemail_users_deprecated = {
7985 { "show", "voicemail", "users", NULL },
7986 handle_voicemail_show_users, NULL,
7987 NULL, complete_voicemail_show_users };
7989 static struct ast_cli_entry cli_show_voicemail_zones_deprecated = {
7990 { "show", "voicemail", "zones", NULL },
7991 handle_voicemail_show_zones, NULL,
7992 NULL, NULL };
7994 static struct ast_cli_entry cli_voicemail[] = {
7995 { { "voicemail", "show", "users", NULL },
7996 handle_voicemail_show_users, "List defined voicemail boxes",
7997 voicemail_show_users_help, complete_voicemail_show_users, &cli_show_voicemail_users_deprecated },
7999 { { "voicemail", "show", "zones", NULL },
8000 handle_voicemail_show_zones, "List zone message formats",
8001 voicemail_show_zones_help, NULL, &cli_show_voicemail_zones_deprecated },
8004 static void free_vm_users(void)
8006 struct ast_vm_user *cur;
8007 struct vm_zone *zcur;
8009 AST_LIST_LOCK(&users);
8010 while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
8011 ast_set_flag(cur, VM_ALLOCED);
8012 free_user(cur);
8014 AST_LIST_UNLOCK(&users);
8016 AST_LIST_LOCK(&zones);
8017 while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list))) {
8018 free_zone(zcur);
8020 AST_LIST_UNLOCK(&zones);
8023 static int load_config(void)
8025 struct ast_vm_user *cur;
8026 struct ast_config *cfg, *ucfg;
8027 char *cat;
8028 struct ast_variable *var;
8029 const char *notifystr = NULL;
8030 const char *smdistr = NULL;
8031 const char *astattach;
8032 const char *astsearch;
8033 const char *astsaycid;
8034 const char *send_voicemail;
8035 #ifdef IMAP_STORAGE
8036 const char *imap_server;
8037 const char *imap_port;
8038 const char *imap_flags;
8039 const char *imap_folder;
8040 const char *auth_user;
8041 const char *auth_password;
8042 const char *expunge_on_hangup;
8043 const char *imap_timeout;
8044 #endif
8045 const char *astcallop;
8046 const char *astreview;
8047 const char *asttempgreetwarn;
8048 const char *astskipcmd;
8049 const char *asthearenv;
8050 const char *astsaydurationinfo;
8051 const char *astsaydurationminfo;
8052 const char *silencestr;
8053 const char *maxmsgstr;
8054 const char *astdirfwd;
8055 const char *thresholdstr;
8056 const char *fmt;
8057 const char *astemail;
8058 const char *ucontext;
8059 const char *astmailcmd = SENDMAIL;
8060 const char *astforcename;
8061 const char *astforcegreet;
8062 const char *s;
8063 char *q,*stringp;
8064 const char *dialoutcxt = NULL;
8065 const char *callbackcxt = NULL;
8066 const char *exitcxt = NULL;
8067 const char *extpc;
8068 const char *emaildateformatstr;
8069 const char *volgainstr;
8070 int x;
8071 int tmpadsi[4];
8073 cfg = ast_config_load(VOICEMAIL_CONFIG);
8075 free_vm_users();
8077 AST_LIST_LOCK(&users);
8079 memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
8081 if (cfg) {
8082 /* General settings */
8084 if (!(ucontext = ast_variable_retrieve(cfg, "general", "userscontext")))
8085 ucontext = "default";
8086 ast_copy_string(userscontext, ucontext, sizeof(userscontext));
8087 /* Attach voice message to mail message ? */
8088 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
8089 astattach = "yes";
8090 ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH);
8092 if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
8093 astsearch = "no";
8094 ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
8096 volgain = 0.0;
8097 if ((volgainstr = ast_variable_retrieve(cfg, "general", "volgain")))
8098 sscanf(volgainstr, "%lf", &volgain);
8100 #ifdef ODBC_STORAGE
8101 strcpy(odbc_database, "asterisk");
8102 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
8103 ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
8105 strcpy(odbc_table, "voicemessages");
8106 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
8107 ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
8109 #endif
8110 /* Mail command */
8111 strcpy(mailcmd, SENDMAIL);
8112 if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
8113 ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
8115 maxsilence = 0;
8116 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
8117 maxsilence = atoi(silencestr);
8118 if (maxsilence > 0)
8119 maxsilence *= 1000;
8122 if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
8123 maxmsg = MAXMSG;
8124 } else {
8125 maxmsg = atoi(maxmsgstr);
8126 if (maxmsg <= 0) {
8127 ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
8128 maxmsg = MAXMSG;
8129 } else if (maxmsg > MAXMSGLIMIT) {
8130 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
8131 maxmsg = MAXMSGLIMIT;
8135 /* Load date format config for voicemail mail */
8136 if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
8137 ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
8140 /* External password changing command */
8141 if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
8142 ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
8144 #ifdef IMAP_STORAGE
8145 /* IMAP server address */
8146 if ((imap_server = ast_variable_retrieve(cfg, "general", "imapserver"))) {
8147 ast_copy_string(imapserver, imap_server, sizeof(imapserver));
8148 } else {
8149 ast_copy_string(imapserver,"localhost", sizeof(imapserver));
8151 /* IMAP server port */
8152 if ((imap_port = ast_variable_retrieve(cfg, "general", "imapport"))) {
8153 ast_copy_string(imapport, imap_port, sizeof(imapport));
8154 } else {
8155 ast_copy_string(imapport,"143", sizeof(imapport));
8157 /* IMAP server flags */
8158 if ((imap_flags = ast_variable_retrieve(cfg, "general", "imapflags"))) {
8159 ast_copy_string(imapflags, imap_flags, sizeof(imapflags));
8161 /* IMAP server master username */
8162 if ((auth_user = ast_variable_retrieve(cfg, "general", "authuser"))) {
8163 ast_copy_string(authuser, auth_user, sizeof(authuser));
8165 /* IMAP server master password */
8166 if ((auth_password = ast_variable_retrieve(cfg, "general", "authpassword"))) {
8167 ast_copy_string(authpassword, auth_password, sizeof(authpassword));
8169 /* Expunge on exit */
8170 if ((expunge_on_hangup = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
8171 if (ast_false(expunge_on_hangup))
8172 expungeonhangup = 0;
8173 else
8174 expungeonhangup = 1;
8175 } else {
8176 expungeonhangup = 1;
8178 /* IMAP voicemail folder */
8179 if ((imap_folder = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
8180 ast_copy_string(imapfolder, imap_folder, sizeof(imapfolder));
8181 } else {
8182 ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
8185 /* There is some very unorthodox casting done here. This is due
8186 * to the way c-client handles the argument passed in. It expects a
8187 * void pointer and casts the pointer directly to a long without
8188 * first dereferencing it. */
8190 if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
8191 mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(imap_timeout)));
8192 } else {
8193 mail_parameters(NIL, SET_READTIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
8196 if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
8197 mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(imap_timeout)));
8198 } else {
8199 mail_parameters(NIL, SET_WRITETIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
8202 if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
8203 mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(imap_timeout)));
8204 } else {
8205 mail_parameters(NIL, SET_OPENTIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
8208 if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
8209 mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(imap_timeout)));
8210 } else {
8211 mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
8214 #endif
8215 /* External voicemail notify application */
8217 if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
8218 ast_copy_string(externnotify, notifystr, sizeof(externnotify));
8219 if (option_debug > 2)
8220 ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
8221 if (!strcasecmp(externnotify, "smdi")) {
8222 if (option_debug)
8223 ast_log(LOG_DEBUG, "Using SMDI for external voicemail notification\n");
8224 if ((smdistr = ast_variable_retrieve(cfg, "general", "smdiport"))) {
8225 smdi_iface = ast_smdi_interface_find(smdistr);
8226 } else {
8227 if (option_debug)
8228 ast_log(LOG_DEBUG, "No SMDI interface set, trying default (/dev/ttyS0)\n");
8229 smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
8232 if (!smdi_iface) {
8233 ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n");
8234 externnotify[0] = '\0';
8237 } else {
8238 externnotify[0] = '\0';
8241 /* Silence treshold */
8242 silencethreshold = 256;
8243 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
8244 silencethreshold = atoi(thresholdstr);
8246 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
8247 astemail = ASTERISK_USERNAME;
8248 ast_copy_string(serveremail, astemail, sizeof(serveremail));
8250 vmmaxmessage = 0;
8251 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
8252 if (sscanf(s, "%d", &x) == 1) {
8253 vmmaxmessage = x;
8254 } else {
8255 ast_log(LOG_WARNING, "Invalid max message time length\n");
8259 vmminmessage = 0;
8260 if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
8261 if (sscanf(s, "%d", &x) == 1) {
8262 vmminmessage = x;
8263 if (maxsilence <= vmminmessage)
8264 ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
8265 } else {
8266 ast_log(LOG_WARNING, "Invalid min message time length\n");
8269 fmt = ast_variable_retrieve(cfg, "general", "format");
8270 if (!fmt)
8271 fmt = "wav";
8272 ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
8274 skipms = 3000;
8275 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
8276 if (sscanf(s, "%d", &x) == 1) {
8277 maxgreet = x;
8278 } else {
8279 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
8283 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
8284 if (sscanf(s, "%d", &x) == 1) {
8285 skipms = x;
8286 } else {
8287 ast_log(LOG_WARNING, "Invalid skipms value\n");
8291 maxlogins = 3;
8292 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
8293 if (sscanf(s, "%d", &x) == 1) {
8294 maxlogins = x;
8295 } else {
8296 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
8300 /* Force new user to record name ? */
8301 if (!(astforcename = ast_variable_retrieve(cfg, "general", "forcename")))
8302 astforcename = "no";
8303 ast_set2_flag((&globalflags), ast_true(astforcename), VM_FORCENAME);
8305 /* Force new user to record greetings ? */
8306 if (!(astforcegreet = ast_variable_retrieve(cfg, "general", "forcegreetings")))
8307 astforcegreet = "no";
8308 ast_set2_flag((&globalflags), ast_true(astforcegreet), VM_FORCEGREET);
8310 if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
8311 if (option_debug > 2)
8312 ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
8313 stringp = ast_strdupa(s);
8314 for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
8315 if (!ast_strlen_zero(stringp)) {
8316 q = strsep(&stringp,",");
8317 while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
8318 q++;
8319 ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
8320 if (option_debug > 2)
8321 ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
8322 } else {
8323 cidinternalcontexts[x][0] = '\0';
8327 if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
8328 if (option_debug)
8329 ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
8330 astreview = "no";
8332 ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW);
8334 /*Temperary greeting reminder */
8335 if (!(asttempgreetwarn = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
8336 if (option_debug)
8337 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option disabled globally\n");
8338 asttempgreetwarn = "no";
8339 } else {
8340 if (option_debug)
8341 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option enabled globally\n");
8343 ast_set2_flag((&globalflags), ast_true(asttempgreetwarn), VM_TEMPGREETWARN);
8345 if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
8346 if (option_debug)
8347 ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
8348 astcallop = "no";
8350 ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);
8352 if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
8353 if (option_debug)
8354 ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
8355 astsaycid = "no";
8357 ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID);
8359 if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
8360 if (option_debug)
8361 ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
8362 send_voicemail = "no";
8364 ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
8366 if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
8367 if (option_debug)
8368 ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
8369 asthearenv = "yes";
8371 ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE);
8373 if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
8374 if (option_debug)
8375 ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
8376 astsaydurationinfo = "yes";
8378 ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);
8380 saydurationminfo = 2;
8381 if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
8382 if (sscanf(astsaydurationminfo, "%d", &x) == 1) {
8383 saydurationminfo = x;
8384 } else {
8385 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
8389 if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
8390 if (option_debug)
8391 ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
8392 astskipcmd = "no";
8394 ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
8396 if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
8397 ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
8398 if (option_debug)
8399 ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
8400 } else {
8401 dialcontext[0] = '\0';
8404 if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
8405 ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
8406 if (option_debug)
8407 ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
8408 } else {
8409 callcontext[0] = '\0';
8412 if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
8413 ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
8414 if (option_debug)
8415 ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
8416 } else {
8417 exitcontext[0] = '\0';
8420 if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory")))
8421 astdirfwd = "no";
8422 ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD);
8423 if ((ucfg = ast_config_load("users.conf"))) {
8424 for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
8425 if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
8426 continue;
8427 if ((cur = find_or_create(userscontext, cat))) {
8428 populate_defaults(cur);
8429 apply_options_full(cur, ast_variable_browse(ucfg, cat));
8430 ast_copy_string(cur->context, userscontext, sizeof(cur->context));
8433 ast_config_destroy(ucfg);
8435 cat = ast_category_browse(cfg, NULL);
8436 while (cat) {
8437 if (strcasecmp(cat, "general")) {
8438 var = ast_variable_browse(cfg, cat);
8439 if (strcasecmp(cat, "zonemessages")) {
8440 /* Process mailboxes in this context */
8441 while (var) {
8442 append_mailbox(cat, var->name, var->value);
8443 var = var->next;
8445 } else {
8446 /* Timezones in this context */
8447 while (var) {
8448 struct vm_zone *z;
8449 if ((z = ast_malloc(sizeof(*z)))) {
8450 char *msg_format, *timezone;
8451 msg_format = ast_strdupa(var->value);
8452 timezone = strsep(&msg_format, "|");
8453 if (msg_format) {
8454 ast_copy_string(z->name, var->name, sizeof(z->name));
8455 ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
8456 ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
8457 AST_LIST_LOCK(&zones);
8458 AST_LIST_INSERT_HEAD(&zones, z, list);
8459 AST_LIST_UNLOCK(&zones);
8460 } else {
8461 ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
8462 free(z);
8464 } else {
8465 free(z);
8466 AST_LIST_UNLOCK(&users);
8467 ast_config_destroy(cfg);
8468 return -1;
8470 var = var->next;
8474 cat = ast_category_browse(cfg, cat);
8476 memset(fromstring,0,sizeof(fromstring));
8477 memset(pagerfromstring,0,sizeof(pagerfromstring));
8478 memset(emailtitle,0,sizeof(emailtitle));
8479 strcpy(charset, "ISO-8859-1");
8480 if (emailbody) {
8481 free(emailbody);
8482 emailbody = NULL;
8484 if (emailsubject) {
8485 free(emailsubject);
8486 emailsubject = NULL;
8488 if (pagerbody) {
8489 free(pagerbody);
8490 pagerbody = NULL;
8492 if (pagersubject) {
8493 free(pagersubject);
8494 pagersubject = NULL;
8496 if ((s = ast_variable_retrieve(cfg, "general", "pbxskip")))
8497 ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
8498 if ((s = ast_variable_retrieve(cfg, "general", "fromstring")))
8499 ast_copy_string(fromstring,s,sizeof(fromstring));
8500 if ((s = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
8501 ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
8502 if ((s = ast_variable_retrieve(cfg, "general", "charset")))
8503 ast_copy_string(charset,s,sizeof(charset));
8504 if ((s = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
8505 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
8506 for (x = 0; x < 4; x++) {
8507 memcpy(&adsifdn[x], &tmpadsi[x], 1);
8510 if ((s = ast_variable_retrieve(cfg, "general", "adsisec"))) {
8511 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
8512 for (x = 0; x < 4; x++) {
8513 memcpy(&adsisec[x], &tmpadsi[x], 1);
8516 if ((s = ast_variable_retrieve(cfg, "general", "adsiver")))
8517 if (atoi(s)) {
8518 adsiver = atoi(s);
8520 if ((s = ast_variable_retrieve(cfg, "general", "emailtitle"))) {
8521 ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
8522 ast_copy_string(emailtitle,s,sizeof(emailtitle));
8524 if ((s = ast_variable_retrieve(cfg, "general", "emailsubject")))
8525 emailsubject = ast_strdup(s);
8526 if ((s = ast_variable_retrieve(cfg, "general", "emailbody"))) {
8527 char *tmpread, *tmpwrite;
8528 emailbody = ast_strdup(s);
8530 /* substitute strings \t and \n into the appropriate characters */
8531 tmpread = tmpwrite = emailbody;
8532 while ((tmpwrite = strchr(tmpread,'\\'))) {
8533 switch (tmpwrite[1]) {
8534 case 'r':
8535 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
8536 *tmpwrite = '\r';
8537 break;
8538 case 'n':
8539 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
8540 *tmpwrite = '\n';
8541 break;
8542 case 't':
8543 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
8544 *tmpwrite = '\t';
8545 break;
8546 default:
8547 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
8549 tmpread = tmpwrite + 1;
8552 if ((s = ast_variable_retrieve(cfg, "general", "pagersubject")))
8553 pagersubject = ast_strdup(s);
8554 if ((s = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
8555 char *tmpread, *tmpwrite;
8556 pagerbody = ast_strdup(s);
8558 /* substitute strings \t and \n into the appropriate characters */
8559 tmpread = tmpwrite = pagerbody;
8560 while ((tmpwrite = strchr(tmpread, '\\'))) {
8561 switch (tmpwrite[1]) {
8562 case 'r':
8563 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
8564 *tmpwrite = '\r';
8565 break;
8566 case 'n':
8567 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
8568 *tmpwrite = '\n';
8569 break;
8570 case 't':
8571 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
8572 *tmpwrite = '\t';
8573 break;
8574 default:
8575 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
8577 tmpread = tmpwrite + 1;
8580 AST_LIST_UNLOCK(&users);
8581 ast_config_destroy(cfg);
8582 return 0;
8583 } else {
8584 AST_LIST_UNLOCK(&users);
8585 ast_log(LOG_WARNING, "Failed to load configuration file.\n");
8586 return 0;
8590 static int reload(void)
8592 return(load_config());
8595 static int unload_module(void)
8597 int res;
8599 res = ast_unregister_application(app);
8600 res |= ast_unregister_application(app2);
8601 res |= ast_unregister_application(app3);
8602 res |= ast_unregister_application(app4);
8603 ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
8604 ast_uninstall_vm_functions();
8606 ast_module_user_hangup_all();
8608 return res;
8611 static int load_module(void)
8613 int res;
8614 char *adsi_loaded = ast_module_helper("", "res_adsi.so", 0, 0, 0, 0);
8615 free(adsi_loaded);
8616 if (!adsi_loaded) {
8617 /* If embedded, res_adsi may be known as "res_adsi" not "res_adsi.so" */
8618 adsi_loaded = ast_module_helper("", "res_adsi", 0, 0, 0, 0);
8619 ast_free(adsi_loaded);
8620 if (!adsi_loaded) {
8621 ast_log(LOG_ERROR, "app_voicemail.so depends upon res_adsi.so\n");
8622 return AST_MODULE_LOAD_DECLINE;
8626 my_umask = umask(0);
8627 umask(my_umask);
8628 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
8629 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
8630 res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
8631 res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
8632 if (res)
8633 return(res);
8635 if ((res=load_config())) {
8636 return(res);
8639 ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
8641 /* compute the location of the voicemail spool directory */
8642 snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
8644 ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
8646 return res;
8649 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
8651 int cmd = 0;
8652 char destination[80] = "";
8653 int retries = 0;
8655 if (!num) {
8656 if (option_verbose > 2)
8657 ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
8658 while (retries < 3 && cmd != 't') {
8659 destination[1] = '\0';
8660 destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
8661 if (!cmd)
8662 destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
8663 if (!cmd)
8664 destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
8665 if (!cmd) {
8666 cmd = ast_waitfordigit(chan, 6000);
8667 if (cmd)
8668 destination[0] = cmd;
8670 if (!cmd) {
8671 retries++;
8672 } else {
8674 if (cmd < 0)
8675 return 0;
8676 if (cmd == '*') {
8677 if (option_verbose > 2)
8678 ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
8679 return 0;
8681 if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0)
8682 retries++;
8683 else
8684 cmd = 't';
8687 if (retries >= 3) {
8688 return 0;
8691 } else {
8692 if (option_verbose > 2)
8693 ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
8694 ast_copy_string(destination, num, sizeof(destination));
8697 if (!ast_strlen_zero(destination)) {
8698 if (destination[strlen(destination) -1 ] == '*')
8699 return 0;
8700 if (option_verbose > 2)
8701 ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
8702 ast_copy_string(chan->exten, destination, sizeof(chan->exten));
8703 ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
8704 chan->priority = 0;
8705 return 9;
8707 return 0;
8710 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain)
8712 int res = 0;
8713 char filename[PATH_MAX];
8714 struct ast_config *msg_cfg = NULL;
8715 const char *origtime, *context;
8716 char *cid, *name, *num;
8717 int retries = 0;
8719 vms->starting = 0;
8720 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
8722 /* Retrieve info from VM attribute file */
8723 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
8724 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
8725 RETRIEVE(vms->curdir, vms->curmsg, vmu);
8726 msg_cfg = ast_config_load(filename);
8727 DISPOSE(vms->curdir, vms->curmsg);
8728 if (!msg_cfg) {
8729 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
8730 return 0;
8733 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
8734 ast_config_destroy(msg_cfg);
8735 return 0;
8738 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
8740 context = ast_variable_retrieve(msg_cfg, "message", "context");
8741 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
8742 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
8743 switch (option) {
8744 case 3:
8745 if (!res)
8746 res = play_message_datetime(chan, vmu, origtime, filename);
8747 if (!res)
8748 res = play_message_callerid(chan, vms, cid, context, 0);
8750 res = 't';
8751 break;
8753 case 2: /* Call back */
8755 if (ast_strlen_zero(cid))
8756 break;
8758 ast_callerid_parse(cid, &name, &num);
8759 while ((res > -1) && (res != 't')) {
8760 switch (res) {
8761 case '1':
8762 if (num) {
8763 /* Dial the CID number */
8764 res = dialout(chan, vmu, num, vmu->callback);
8765 if (res) {
8766 ast_config_destroy(msg_cfg);
8767 return 9;
8769 } else {
8770 res = '2';
8772 break;
8774 case '2':
8775 /* Want to enter a different number, can only do this if there's a dialout context for this user */
8776 if (!ast_strlen_zero(vmu->dialout)) {
8777 res = dialout(chan, vmu, NULL, vmu->dialout);
8778 if (res) {
8779 ast_config_destroy(msg_cfg);
8780 return 9;
8782 } else {
8783 if (option_verbose > 2)
8784 ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
8785 res = ast_play_and_wait(chan, "vm-sorry");
8787 ast_config_destroy(msg_cfg);
8788 return res;
8789 case '*':
8790 res = 't';
8791 break;
8792 case '3':
8793 case '4':
8794 case '5':
8795 case '6':
8796 case '7':
8797 case '8':
8798 case '9':
8799 case '0':
8801 res = ast_play_and_wait(chan, "vm-sorry");
8802 retries++;
8803 break;
8804 default:
8805 if (num) {
8806 if (option_verbose > 2)
8807 ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
8808 res = ast_play_and_wait(chan, "vm-num-i-have");
8809 if (!res)
8810 res = play_message_callerid(chan, vms, num, vmu->context, 1);
8811 if (!res)
8812 res = ast_play_and_wait(chan, "vm-tocallnum");
8813 /* Only prompt for a caller-specified number if there is a dialout context specified */
8814 if (!ast_strlen_zero(vmu->dialout)) {
8815 if (!res)
8816 res = ast_play_and_wait(chan, "vm-calldiffnum");
8818 } else {
8819 res = ast_play_and_wait(chan, "vm-nonumber");
8820 if (!ast_strlen_zero(vmu->dialout)) {
8821 if (!res)
8822 res = ast_play_and_wait(chan, "vm-toenternumber");
8825 if (!res)
8826 res = ast_play_and_wait(chan, "vm-star-cancel");
8827 if (!res)
8828 res = ast_waitfordigit(chan, 6000);
8829 if (!res) {
8830 retries++;
8831 if (retries > 3)
8832 res = 't';
8834 break;
8837 if (res == 't')
8838 res = 0;
8839 else if (res == '*')
8840 res = -1;
8842 break;
8844 case 1: /* Reply */
8845 /* Send reply directly to sender */
8846 if (ast_strlen_zero(cid))
8847 break;
8849 ast_callerid_parse(cid, &name, &num);
8850 if (!num) {
8851 if (option_verbose > 2)
8852 ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
8853 if (!res)
8854 res = ast_play_and_wait(chan, "vm-nonumber");
8855 ast_config_destroy(msg_cfg);
8856 return res;
8857 } else {
8858 struct ast_vm_user vmu2;
8859 if (find_user(&vmu2, vmu->context, num)) {
8860 struct leave_vm_options leave_options;
8861 char mailbox[AST_MAX_EXTENSION * 2 + 2];
8862 snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
8864 if (option_verbose > 2)
8865 ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
8867 memset(&leave_options, 0, sizeof(leave_options));
8868 leave_options.record_gain = record_gain;
8869 res = leave_voicemail(chan, mailbox, &leave_options);
8870 if (!res)
8871 res = 't';
8872 ast_config_destroy(msg_cfg);
8873 return res;
8874 } else {
8875 /* Sender has no mailbox, can't reply */
8876 if (option_verbose > 2)
8877 ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
8878 ast_play_and_wait(chan, "vm-nobox");
8879 res = 't';
8880 ast_config_destroy(msg_cfg);
8881 return res;
8884 res = 0;
8886 break;
8889 #ifndef IMAP_STORAGE
8890 ast_config_destroy(msg_cfg);
8892 if (!res) {
8893 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
8894 vms->heard[msg] = 1;
8895 res = wait_file(chan, vms, vms->fn);
8897 #endif
8898 return res;
8901 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
8902 int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
8903 signed char record_gain, struct vm_state *vms)
8905 /* Record message & let caller review or re-record it, or set options if applicable */
8906 int res = 0;
8907 int cmd = 0;
8908 int max_attempts = 3;
8909 int attempts = 0;
8910 int recorded = 0;
8911 int message_exists = 0;
8912 signed char zero_gain = 0;
8913 char tempfile[PATH_MAX];
8914 char *acceptdtmf = "#";
8915 char *canceldtmf = "";
8917 /* Note that urgent and private are for flagging messages as such in the future */
8919 /* barf if no pointer passed to store duration in */
8920 if (duration == NULL) {
8921 ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
8922 return -1;
8925 if (!outsidecaller)
8926 snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
8927 else
8928 ast_copy_string(tempfile, recordfile, sizeof(tempfile));
8930 cmd = '3'; /* Want to start by recording */
8932 while ((cmd >= 0) && (cmd != 't')) {
8933 switch (cmd) {
8934 case '1':
8935 if (!message_exists) {
8936 /* In this case, 1 is to record a message */
8937 cmd = '3';
8938 break;
8939 } else {
8940 /* Otherwise 1 is to save the existing message */
8941 if (option_verbose > 2)
8942 ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
8943 if (!outsidecaller)
8944 ast_filerename(tempfile, recordfile, NULL);
8945 ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
8946 if (!outsidecaller) {
8947 STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms);
8948 DISPOSE(recordfile, -1);
8950 cmd = 't';
8951 return res;
8953 case '2':
8954 /* Review */
8955 if (option_verbose > 2)
8956 ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
8957 cmd = ast_stream_and_wait(chan, tempfile, chan->language, AST_DIGIT_ANY);
8958 break;
8959 case '3':
8960 message_exists = 0;
8961 /* Record */
8962 if (recorded == 1) {
8963 if (option_verbose > 2)
8964 ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
8965 } else {
8966 if (option_verbose > 2)
8967 ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
8969 if (recorded && outsidecaller) {
8970 cmd = ast_play_and_wait(chan, INTRO);
8971 cmd = ast_play_and_wait(chan, "beep");
8973 recorded = 1;
8974 /* After an attempt has been made to record message, we have to take care of INTRO and beep for incoming messages, but not for greetings */
8975 if (record_gain)
8976 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
8977 if (ast_test_flag(vmu, VM_OPERATOR))
8978 canceldtmf = "0";
8979 cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
8980 if (record_gain)
8981 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
8982 if (cmd == -1) {
8983 /* User has hung up, no options to give */
8984 if (!outsidecaller) {
8985 /* user was recording a greeting and they hung up, so let's delete the recording. */
8986 ast_filedelete(tempfile, NULL);
8988 return cmd;
8990 if (cmd == '0') {
8991 break;
8992 } else if (cmd == '*') {
8993 break;
8995 #if 0
8996 else if (vmu->review && (*duration < 5)) {
8997 /* Message is too short */
8998 if (option_verbose > 2)
8999 ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
9000 cmd = ast_play_and_wait(chan, "vm-tooshort");
9001 cmd = ast_filedelete(tempfile, NULL);
9002 break;
9004 else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
9005 /* Message is all silence */
9006 if (option_verbose > 2)
9007 ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
9008 cmd = ast_filedelete(tempfile, NULL);
9009 cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
9010 if (!cmd)
9011 cmd = ast_play_and_wait(chan, "vm-speakup");
9012 break;
9014 #endif
9015 else {
9016 /* If all is well, a message exists */
9017 message_exists = 1;
9018 cmd = 0;
9020 break;
9021 case '4':
9022 case '5':
9023 case '6':
9024 case '7':
9025 case '8':
9026 case '9':
9027 case '*':
9028 case '#':
9029 cmd = ast_play_and_wait(chan, "vm-sorry");
9030 break;
9031 #if 0
9032 /* XXX Commented out for the moment because of the dangers of deleting
9033 a message while recording (can put the message numbers out of sync) */
9034 case '*':
9035 /* Cancel recording, delete message, offer to take another message*/
9036 cmd = ast_play_and_wait(chan, "vm-deleted");
9037 cmd = ast_filedelete(tempfile, NULL);
9038 if (outsidecaller) {
9039 res = vm_exec(chan, NULL);
9040 return res;
9042 else
9043 return 1;
9044 #endif
9045 case '0':
9046 if (!ast_test_flag(vmu, VM_OPERATOR)) {
9047 cmd = ast_play_and_wait(chan, "vm-sorry");
9048 break;
9050 if (message_exists || recorded) {
9051 cmd = ast_play_and_wait(chan, "vm-saveoper");
9052 if (!cmd)
9053 cmd = ast_waitfordigit(chan, 3000);
9054 if (cmd == '1') {
9055 ast_play_and_wait(chan, "vm-msgsaved");
9056 cmd = '0';
9057 } else {
9058 ast_play_and_wait(chan, "vm-deleted");
9059 DELETE(recordfile, -1, recordfile, vmu);
9060 cmd = '0';
9063 return cmd;
9064 default:
9065 /* If the caller is an ouside caller, and the review option is enabled,
9066 allow them to review the message, but let the owner of the box review
9067 their OGM's */
9068 if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
9069 return cmd;
9070 if (message_exists) {
9071 cmd = ast_play_and_wait(chan, "vm-review");
9073 else {
9074 cmd = ast_play_and_wait(chan, "vm-torerecord");
9075 if (!cmd)
9076 cmd = ast_waitfordigit(chan, 600);
9079 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
9080 cmd = ast_play_and_wait(chan, "vm-reachoper");
9081 if (!cmd)
9082 cmd = ast_waitfordigit(chan, 600);
9084 #if 0
9085 if (!cmd)
9086 cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
9087 #endif
9088 if (!cmd)
9089 cmd = ast_waitfordigit(chan, 6000);
9090 if (!cmd) {
9091 attempts++;
9093 if (attempts > max_attempts) {
9094 cmd = 't';
9098 if (outsidecaller)
9099 ast_play_and_wait(chan, "vm-goodbye");
9100 if (cmd == 't')
9101 cmd = 0;
9102 return cmd;
9105 /* This is a workaround so that menuselect displays a proper description
9106 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
9109 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
9110 .load = load_module,
9111 .unload = unload_module,
9112 .reload = reload,