Make it build with the bristuffed libpri
[asterisk-bristuff.git] / apps / app_voicemail.c
blob9277cdcb3ba06228d874f512d73d20b9cd0280e3
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
253 German requires the following additional soundfile:
254 \arg \b 1F einE (feminine)
256 Spanish requires the following additional soundfile:
257 \arg \b 1M un (masculine)
259 Dutch, Portuguese & Spanish require the following additional soundfiles:
260 \arg \b vm-INBOXs singular of 'new'
261 \arg \b vm-Olds singular of 'old/heard/read'
263 NB these are plural:
264 \arg \b vm-INBOX nieuwe (nl)
265 \arg \b vm-Old oude (nl)
267 Polish uses:
268 \arg \b vm-new-a 'new', feminine singular accusative
269 \arg \b vm-new-e 'new', feminine plural accusative
270 \arg \b vm-new-ych 'new', feminine plural genitive
271 \arg \b vm-old-a 'old', feminine singular accusative
272 \arg \b vm-old-e 'old', feminine plural accusative
273 \arg \b vm-old-ych 'old', feminine plural genitive
274 \arg \b digits/1-a 'one', not always same as 'digits/1'
275 \arg \b digits/2-ie 'two', not always same as 'digits/2'
277 Swedish uses:
278 \arg \b vm-nytt singular of 'new'
279 \arg \b vm-nya plural of 'new'
280 \arg \b vm-gammalt singular of 'old'
281 \arg \b vm-gamla plural of 'old'
282 \arg \b digits/ett 'one', not always same as 'digits/1'
284 Norwegian uses:
285 \arg \b vm-ny singular of 'new'
286 \arg \b vm-nye plural of 'new'
287 \arg \b vm-gammel singular of 'old'
288 \arg \b vm-gamle plural of 'old'
290 Dutch also uses:
291 \arg \b nl-om 'at'?
293 Spanish also uses:
294 \arg \b vm-youhaveno
296 Ukrainian requires the following additional soundfile:
297 \arg \b vm-nove 'nove'
298 \arg \b vm-stare 'stare'
299 \arg \b digits/ua/1e 'odne'
301 Italian requires the following additional soundfile:
303 For vm_intro_it:
304 \arg \b vm-nuovo new
305 \arg \b vm-nuovi new plural
306 \arg \b vm-vecchio old
307 \arg \b vm-vecchi old plural
309 \note Don't use vm-INBOX or vm-Old, because they are the name of the INBOX and Old folders,
310 spelled among others when you have to change folder. For the above reasons, vm-INBOX
311 and vm-Old are spelled plural, to make them sound more as folder name than an adjective.
315 struct baseio {
316 int iocp;
317 int iolen;
318 int linelength;
319 int ateof;
320 unsigned char iobuf[BASEMAXINLINE];
323 /*! Structure for linked list of users */
324 struct ast_vm_user {
325 char context[AST_MAX_CONTEXT]; /*!< Voicemail context */
326 char mailbox[AST_MAX_EXTENSION]; /*!< Mailbox id, unique within vm context */
327 char password[80]; /*!< Secret pin code, numbers only */
328 char fullname[80]; /*!< Full name, for directory app */
329 char email[80]; /*!< E-mail address */
330 char pager[80]; /*!< E-mail address to pager (no attachment) */
331 char serveremail[80]; /*!< From: Mail address */
332 char mailcmd[160]; /*!< Configurable mail command */
333 char language[MAX_LANGUAGE]; /*!< Config: Language setting */
334 char zonetag[80]; /*!< Time zone */
335 char callback[80];
336 char dialout[80];
337 char uniqueid[80]; /*!< Unique integer identifier */
338 char exit[80];
339 char attachfmt[20]; /*!< Attachment format */
340 unsigned int flags; /*!< VM_ flags */
341 int saydurationm;
342 int maxmsg; /*!< Maximum number of msgs per folder for this mailbox */
343 #ifdef IMAP_STORAGE
344 char imapuser[80]; /* IMAP server login */
345 char imappassword[80]; /* IMAP server password if authpassword not defined */
346 #endif
347 double volgain; /*!< Volume gain for voicemails sent via email */
348 AST_LIST_ENTRY(ast_vm_user) list;
351 struct vm_zone {
352 AST_LIST_ENTRY(vm_zone) list;
353 char name[80];
354 char timezone[80];
355 char msg_format[512];
358 struct vm_state {
359 char curbox[80];
360 char username[80];
361 char context[80];
362 char curdir[PATH_MAX];
363 char vmbox[PATH_MAX];
364 char fn[PATH_MAX];
365 char fn2[PATH_MAX];
366 int *deleted;
367 int *heard;
368 int curmsg;
369 int lastmsg;
370 int newmessages;
371 int oldmessages;
372 int starting;
373 int repeats;
374 #ifdef IMAP_STORAGE
375 ast_mutex_t lock;
376 int updated; /* decremented on each mail check until 1 -allows delay */
377 long msgArray[256];
378 MAILSTREAM *mailstream;
379 int vmArrayIndex;
380 char imapuser[80]; /* IMAP server login */
381 int interactive;
382 unsigned int quota_limit;
383 unsigned int quota_usage;
384 struct vm_state *persist_vms;
385 #endif
387 static int advanced_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, int msg, int option, signed char record_gain);
388 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context);
389 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime,
390 char *fmt, int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
391 signed char record_gain, struct vm_state *vms);
392 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain);
393 static int vm_play_folder_name(struct ast_channel *chan, char *mbox);
394 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname);
395 static void make_email_file(FILE *p, char *srcemail, struct ast_vm_user *vmu, int msgnum, char *context, char *mailbox, char *cidnum, char *cidname, char *attach, char *format, int duration, int attach_user_voicemail, struct ast_channel *chan, const char *category, int imap);
396 #if !(defined(ODBC_STORAGE) || defined(IMAP_STORAGE))
397 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit);
398 #endif
399 static void apply_options(struct ast_vm_user *vmu, const char *options);
401 #ifdef ODBC_STORAGE
402 static char odbc_database[80];
403 static char odbc_table[80];
404 #define RETRIEVE(a,b,c) retrieve_file(a,b)
405 #define DISPOSE(a,b) remove_file(a,b)
406 #define STORE(a,b,c,d,e,f,g,h,i) store_file(a,b,c,d)
407 #define EXISTS(a,b,c,d) (message_exists(a,b))
408 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(a,b,c,d,e,f))
409 #define COPY(a,b,c,d,e,f,g,h) (copy_file(a,b,c,d,e,f))
410 #define DELETE(a,b,c,d) (delete_file(a,b))
411 #else
412 #ifdef IMAP_STORAGE
413 #define RETRIEVE(a,b,c) imap_retrieve_file(a,b,c)
414 #define DISPOSE(a,b) remove_file(a,b)
415 #define STORE(a,b,c,d,e,f,g,h,i) (imap_store_file(a,b,c,d,e,f,g,h,i))
416 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
417 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
418 #define COPY(a,b,c,d,e,f,g,h) (copy_file(g,h));
419 #define DELETE(a,b,c,d) (vm_imap_delete(b,d))
420 #else
421 #define RETRIEVE(a,b,c)
422 #define DISPOSE(a,b)
423 #define STORE(a,b,c,d,e,f,g,h,i)
424 #define EXISTS(a,b,c,d) (ast_fileexists(c,NULL,d) > 0)
425 #define RENAME(a,b,c,d,e,f,g,h) (rename_file(g,h));
426 #define COPY(a,b,c,d,e,f,g,h) (copy_plain_file(g,h));
427 #define DELETE(a,b,c,d) (vm_delete(c))
428 #endif
429 #endif
431 static char VM_SPOOL_DIR[PATH_MAX];
433 static char ext_pass_cmd[128];
435 int my_umask;
437 #if ODBC_STORAGE
438 #define tdesc "Comedian Mail (Voicemail System) with ODBC Storage"
439 #elif IMAP_STORAGE
440 #define tdesc "Comedian Mail (Voicemail System) with IMAP Storage"
441 #else
442 #define tdesc "Comedian Mail (Voicemail System)"
443 #endif
445 static char userscontext[AST_MAX_EXTENSION] = "default";
447 static char *addesc = "Comedian Mail";
449 static char *synopsis_vm =
450 "Leave a Voicemail message";
452 static char *descrip_vm =
453 " VoiceMail(mailbox[@context][&mailbox[@context]][...][|options]): This\n"
454 "application allows the calling party to leave a message for the specified\n"
455 "list of mailboxes. When multiple mailboxes are specified, the greeting will\n"
456 "be taken from the first mailbox specified. Dialplan execution will stop if the\n"
457 "specified mailbox does not exist.\n"
458 " The Voicemail application will exit if any of the following DTMF digits are\n"
459 "received:\n"
460 " 0 - Jump to the 'o' extension in the current dialplan context.\n"
461 " * - Jump to the 'a' extension in the current dialplan context.\n"
462 " This application will set the following channel variable upon completion:\n"
463 " VMSTATUS - This indicates the status of the execution of the VoiceMail\n"
464 " application. The possible values are:\n"
465 " SUCCESS | USEREXIT | FAILED\n\n"
466 " Options:\n"
467 " b - Play the 'busy' greeting to the calling party.\n"
468 " g(#) - Use the specified amount of gain when recording the voicemail\n"
469 " message. The units are whole-number decibels (dB).\n"
470 " Only works on supported technologies, which is Zap only.\n"
471 " s - Skip the playback of instructions for leaving a message to the\n"
472 " calling party.\n"
473 " u - Play the 'unavailable' greeting.\n"
474 " j - Jump to priority n+101 if the mailbox is not found or some other\n"
475 " error occurs.\n";
477 static char *synopsis_vmain =
478 "Check Voicemail messages";
480 static char *descrip_vmain =
481 " VoiceMailMain([mailbox][@context][|options]): This application allows the\n"
482 "calling party to check voicemail messages. A specific mailbox, and optional\n"
483 "corresponding context, may be specified. If a mailbox is not provided, the\n"
484 "calling party will be prompted to enter one. If a context is not specified,\n"
485 "the 'default' context will be used.\n\n"
486 " Options:\n"
487 " p - Consider the mailbox parameter as a prefix to the mailbox that\n"
488 " is entered by the caller.\n"
489 " g(#) - Use the specified amount of gain when recording a voicemail\n"
490 " message. The units are whole-number decibels (dB).\n"
491 " s - Skip checking the passcode for the mailbox.\n"
492 " a(#) - Skip folder prompt and go directly to folder specified.\n"
493 " Defaults to INBOX\n";
495 static char *synopsis_vm_box_exists =
496 "Check to see if Voicemail mailbox exists";
498 static char *descrip_vm_box_exists =
499 " MailboxExists(mailbox[@context][|options]): Check to see if the specified\n"
500 "mailbox exists. If no voicemail context is specified, the 'default' context\n"
501 "will be used.\n"
502 " This application will set the following channel variable upon completion:\n"
503 " VMBOXEXISTSSTATUS - This will contain the status of the execution of the\n"
504 " MailboxExists application. Possible values include:\n"
505 " SUCCESS | FAILED\n\n"
506 " Options:\n"
507 " j - Jump to priority n+101 if the mailbox is found.\n";
509 static char *synopsis_vmauthenticate =
510 "Authenticate with Voicemail passwords";
512 static char *descrip_vmauthenticate =
513 " VMAuthenticate([mailbox][@context][|options]): This application behaves the\n"
514 "same way as the Authenticate application, but the passwords are taken from\n"
515 "voicemail.conf.\n"
516 " If the mailbox is specified, only that mailbox's password will be considered\n"
517 "valid. If the mailbox is not specified, the channel variable AUTH_MAILBOX will\n"
518 "be set with the authenticated mailbox.\n\n"
519 " Options:\n"
520 " s - Skip playing the initial prompts.\n";
522 /* Leave a message */
523 static char *app = "VoiceMail";
525 /* Check mail, control, etc */
526 static char *app2 = "VoiceMailMain";
528 static char *app3 = "MailboxExists";
529 static char *app4 = "VMAuthenticate";
531 static AST_LIST_HEAD_STATIC(users, ast_vm_user);
532 static AST_LIST_HEAD_STATIC(zones, vm_zone);
533 static int maxsilence;
534 static int maxmsg;
535 static int silencethreshold = 128;
536 static char serveremail[80];
537 static char mailcmd[160]; /* Configurable mail cmd */
538 static char externnotify[160];
539 static struct ast_smdi_interface *smdi_iface = NULL;
540 static char vmfmts[80];
541 static double volgain;
542 static int vmminmessage;
543 static int vmmaxmessage;
544 static int maxgreet;
545 static int skipms;
546 static int maxlogins;
548 static struct ast_flags globalflags = {0};
550 static int saydurationminfo;
552 static char dialcontext[AST_MAX_CONTEXT];
553 static char callcontext[AST_MAX_CONTEXT];
554 static char exitcontext[AST_MAX_CONTEXT];
556 static char cidinternalcontexts[MAX_NUM_CID_CONTEXTS][64];
559 static char *emailbody = NULL;
560 static char *emailsubject = NULL;
561 static char *pagerbody = NULL;
562 static char *pagersubject = NULL;
563 static char fromstring[100];
564 static char pagerfromstring[100];
565 static char emailtitle[100];
566 static char charset[32] = "ISO-8859-1";
568 static unsigned char adsifdn[4] = "\x00\x00\x00\x0F";
569 static unsigned char adsisec[4] = "\x9B\xDB\xF7\xAC";
570 static int adsiver = 1;
571 static char emaildateformat[32] = "%A, %B %d, %Y at %r";
574 static char *strip_control(const char *input, char *buf, size_t buflen)
576 char *bufptr = buf;
577 for (; *input; input++) {
578 if (*input < 32) {
579 continue;
581 *bufptr++ = *input;
582 if (bufptr == buf + buflen - 1) {
583 break;
586 *bufptr = '\0';
587 return buf;
590 static void populate_defaults(struct ast_vm_user *vmu)
592 ast_copy_flags(vmu, (&globalflags), AST_FLAGS_ALL);
593 if (saydurationminfo)
594 vmu->saydurationm = saydurationminfo;
595 ast_copy_string(vmu->callback, callcontext, sizeof(vmu->callback));
596 ast_copy_string(vmu->dialout, dialcontext, sizeof(vmu->dialout));
597 ast_copy_string(vmu->exit, exitcontext, sizeof(vmu->exit));
598 if (maxmsg)
599 vmu->maxmsg = maxmsg;
600 vmu->volgain = volgain;
603 static void apply_option(struct ast_vm_user *vmu, const char *var, const char *value)
605 int x;
606 if (!strcasecmp(var, "attach")) {
607 ast_set2_flag(vmu, ast_true(value), VM_ATTACH);
608 } else if (!strcasecmp(var, "attachfmt")) {
609 ast_copy_string(vmu->attachfmt, value, sizeof(vmu->attachfmt));
610 } else if (!strcasecmp(var, "serveremail")) {
611 ast_copy_string(vmu->serveremail, value, sizeof(vmu->serveremail));
612 } else if (!strcasecmp(var, "language")) {
613 ast_copy_string(vmu->language, value, sizeof(vmu->language));
614 } else if (!strcasecmp(var, "tz")) {
615 ast_copy_string(vmu->zonetag, value, sizeof(vmu->zonetag));
616 #ifdef IMAP_STORAGE
617 } else if (!strcasecmp(var, "imapuser")) {
618 ast_copy_string(vmu->imapuser, value, sizeof(vmu->imapuser));
619 } else if (!strcasecmp(var, "imappassword")) {
620 ast_copy_string(vmu->imappassword, value, sizeof(vmu->imappassword));
621 #endif
622 } else if (!strcasecmp(var, "delete") || !strcasecmp(var, "deletevoicemail")) {
623 ast_set2_flag(vmu, ast_true(value), VM_DELETE);
624 } else if (!strcasecmp(var, "saycid")){
625 ast_set2_flag(vmu, ast_true(value), VM_SAYCID);
626 } else if (!strcasecmp(var,"sendvoicemail")){
627 ast_set2_flag(vmu, ast_true(value), VM_SVMAIL);
628 } else if (!strcasecmp(var, "review")){
629 ast_set2_flag(vmu, ast_true(value), VM_REVIEW);
630 } else if (!strcasecmp(var, "tempgreetwarn")){
631 ast_set2_flag(vmu, ast_true(value), VM_TEMPGREETWARN);
632 } else if (!strcasecmp(var, "operator")){
633 ast_set2_flag(vmu, ast_true(value), VM_OPERATOR);
634 } else if (!strcasecmp(var, "envelope")){
635 ast_set2_flag(vmu, ast_true(value), VM_ENVELOPE);
636 } else if (!strcasecmp(var, "sayduration")){
637 ast_set2_flag(vmu, ast_true(value), VM_SAYDURATION);
638 } else if (!strcasecmp(var, "saydurationm")){
639 if (sscanf(value, "%d", &x) == 1) {
640 vmu->saydurationm = x;
641 } else {
642 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
644 } else if (!strcasecmp(var, "forcename")){
645 ast_set2_flag(vmu, ast_true(value), VM_FORCENAME);
646 } else if (!strcasecmp(var, "forcegreetings")){
647 ast_set2_flag(vmu, ast_true(value), VM_FORCEGREET);
648 } else if (!strcasecmp(var, "callback")) {
649 ast_copy_string(vmu->callback, value, sizeof(vmu->callback));
650 } else if (!strcasecmp(var, "dialout")) {
651 ast_copy_string(vmu->dialout, value, sizeof(vmu->dialout));
652 } else if (!strcasecmp(var, "exitcontext")) {
653 ast_copy_string(vmu->exit, value, sizeof(vmu->exit));
654 } else if (!strcasecmp(var, "maxmsg")) {
655 vmu->maxmsg = atoi(value);
656 if (vmu->maxmsg <= 0) {
657 ast_log(LOG_WARNING, "Invalid number of messages per folder maxmsg=%s. Using default value %i\n", value, MAXMSG);
658 vmu->maxmsg = MAXMSG;
659 } else if (vmu->maxmsg > MAXMSGLIMIT) {
660 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value maxmsg=%s\n", MAXMSGLIMIT, value);
661 vmu->maxmsg = MAXMSGLIMIT;
663 } else if (!strcasecmp(var, "volgain")) {
664 sscanf(value, "%lf", &vmu->volgain);
665 } else if (!strcasecmp(var, "options")) {
666 apply_options(vmu, value);
670 static int change_password_realtime(struct ast_vm_user *vmu, const char *password)
672 int res;
673 if (!ast_strlen_zero(vmu->uniqueid)) {
674 res = ast_update_realtime("voicemail", "uniqueid", vmu->uniqueid, "password", password, NULL);
675 if (res > 0) {
676 ast_copy_string(vmu->password, password, sizeof(vmu->password));
677 res = 0;
678 } else if (!res) {
679 res = -1;
681 return res;
683 return -1;
686 static void apply_options(struct ast_vm_user *vmu, const char *options)
687 { /* Destructively Parse options and apply */
688 char *stringp;
689 char *s;
690 char *var, *value;
691 stringp = ast_strdupa(options);
692 while ((s = strsep(&stringp, "|"))) {
693 value = s;
694 if ((var = strsep(&value, "=")) && value) {
695 apply_option(vmu, var, value);
700 static void apply_options_full(struct ast_vm_user *retval, struct ast_variable *var)
702 struct ast_variable *tmp;
703 tmp = var;
704 while (tmp) {
705 if (!strcasecmp(tmp->name, "vmsecret")) {
706 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
707 } else if (!strcasecmp(tmp->name, "secret") || !strcasecmp(tmp->name, "password")) { /* don't overwrite vmsecret if it exists */
708 if (ast_strlen_zero(retval->password))
709 ast_copy_string(retval->password, tmp->value, sizeof(retval->password));
710 } else if (!strcasecmp(tmp->name, "uniqueid")) {
711 ast_copy_string(retval->uniqueid, tmp->value, sizeof(retval->uniqueid));
712 } else if (!strcasecmp(tmp->name, "pager")) {
713 ast_copy_string(retval->pager, tmp->value, sizeof(retval->pager));
714 } else if (!strcasecmp(tmp->name, "email")) {
715 ast_copy_string(retval->email, tmp->value, sizeof(retval->email));
716 } else if (!strcasecmp(tmp->name, "fullname")) {
717 ast_copy_string(retval->fullname, tmp->value, sizeof(retval->fullname));
718 } else if (!strcasecmp(tmp->name, "context")) {
719 ast_copy_string(retval->context, tmp->value, sizeof(retval->context));
720 #ifdef IMAP_STORAGE
721 } else if (!strcasecmp(tmp->name, "imapuser")) {
722 ast_copy_string(retval->imapuser, tmp->value, sizeof(retval->imapuser));
723 } else if (!strcasecmp(tmp->name, "imappassword")) {
724 ast_copy_string(retval->imappassword, tmp->value, sizeof(retval->imappassword));
725 #endif
726 } else
727 apply_option(retval, tmp->name, tmp->value);
728 tmp = tmp->next;
732 static struct ast_vm_user *find_user_realtime(struct ast_vm_user *ivm, const char *context, const char *mailbox)
734 struct ast_variable *var;
735 struct ast_vm_user *retval;
737 if ((retval = (ivm ? ivm : ast_calloc(1, sizeof(*retval))))) {
738 if (!ivm)
739 ast_set_flag(retval, VM_ALLOCED);
740 else
741 memset(retval, 0, sizeof(*retval));
742 if (mailbox)
743 ast_copy_string(retval->mailbox, mailbox, sizeof(retval->mailbox));
744 populate_defaults(retval);
745 if (!context && ast_test_flag((&globalflags), VM_SEARCH))
746 var = ast_load_realtime("voicemail", "mailbox", mailbox, NULL);
747 else
748 var = ast_load_realtime("voicemail", "mailbox", mailbox, "context", context, NULL);
749 if (var) {
750 apply_options_full(retval, var);
751 ast_variables_destroy(var);
752 } else {
753 if (!ivm)
754 free(retval);
755 retval = NULL;
758 return retval;
761 static struct ast_vm_user *find_user(struct ast_vm_user *ivm, const char *context, const char *mailbox)
763 /* This function could be made to generate one from a database, too */
764 struct ast_vm_user *vmu=NULL, *cur;
765 AST_LIST_LOCK(&users);
767 if (!context && !ast_test_flag((&globalflags), VM_SEARCH))
768 context = "default";
770 AST_LIST_TRAVERSE(&users, cur, list) {
771 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mailbox, cur->mailbox))
772 break;
773 if (context && (!strcasecmp(context, cur->context)) && (!strcasecmp(mailbox, cur->mailbox)))
774 break;
776 if (cur) {
777 /* Make a copy, so that on a reload, we have no race */
778 if ((vmu = (ivm ? ivm : ast_malloc(sizeof(*vmu))))) {
779 memcpy(vmu, cur, sizeof(*vmu));
780 ast_set2_flag(vmu, !ivm, VM_ALLOCED);
781 AST_LIST_NEXT(vmu, list) = NULL;
783 } else
784 vmu = find_user_realtime(ivm, context, mailbox);
785 AST_LIST_UNLOCK(&users);
786 return vmu;
789 static int reset_user_pw(const char *context, const char *mailbox, const char *newpass)
791 /* This function could be made to generate one from a database, too */
792 struct ast_vm_user *cur;
793 int res = -1;
794 AST_LIST_LOCK(&users);
795 AST_LIST_TRAVERSE(&users, cur, list) {
796 if ((!context || !strcasecmp(context, cur->context)) &&
797 (!strcasecmp(mailbox, cur->mailbox)))
798 break;
800 if (cur) {
801 ast_copy_string(cur->password, newpass, sizeof(cur->password));
802 res = 0;
804 AST_LIST_UNLOCK(&users);
805 return res;
808 static void vm_change_password(struct ast_vm_user *vmu, const char *newpassword)
810 struct ast_config *cfg=NULL;
811 struct ast_variable *var=NULL;
812 struct ast_category *cat=NULL;
813 char *category=NULL, *value=NULL, *new=NULL;
814 const char *tmp=NULL;
816 if (!change_password_realtime(vmu, newpassword))
817 return;
819 /* check voicemail.conf */
820 if ((cfg = ast_config_load_with_comments(VOICEMAIL_CONFIG))) {
821 while ((category = ast_category_browse(cfg, category))) {
822 if (!strcasecmp(category, vmu->context)) {
823 tmp = ast_variable_retrieve(cfg, category, vmu->mailbox);
824 if (!tmp) {
825 ast_log(LOG_WARNING, "We could not find the mailbox.\n");
826 break;
828 value = strstr(tmp,",");
829 if (!value) {
830 ast_log(LOG_WARNING, "variable has bad format.\n");
831 break;
833 new = alloca((strlen(value)+strlen(newpassword)+1));
834 sprintf(new,"%s%s", newpassword, value);
835 if (!(cat = ast_category_get(cfg, category))) {
836 ast_log(LOG_WARNING, "Failed to get category structure.\n");
837 break;
839 ast_variable_update(cat, vmu->mailbox, new, NULL, 0);
842 /* save the results */
843 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
844 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
845 config_text_file_save(VOICEMAIL_CONFIG, cfg, "AppVoicemail");
847 category = NULL;
848 var = NULL;
849 /* check users.conf and update the password stored for the mailbox*/
850 /* if no vmsecret entry exists create one. */
851 if ((cfg = ast_config_load_with_comments("users.conf"))) {
852 if (option_debug > 3)
853 ast_log(LOG_DEBUG, "we are looking for %s\n", vmu->mailbox);
854 while ((category = ast_category_browse(cfg, category))) {
855 if (option_debug > 3)
856 ast_log(LOG_DEBUG, "users.conf: %s\n", category);
857 if (!strcasecmp(category, vmu->mailbox)) {
858 if (!(tmp = ast_variable_retrieve(cfg, category, "vmsecret"))) {
859 if (option_debug > 3)
860 ast_log(LOG_DEBUG, "looks like we need to make vmsecret!\n");
861 var = ast_variable_new("vmsecret", newpassword);
863 new = alloca(strlen(newpassword)+1);
864 sprintf(new, "%s", newpassword);
865 if (!(cat = ast_category_get(cfg, category))) {
866 if (option_debug > 3)
867 ast_log(LOG_DEBUG, "failed to get category!\n");
868 break;
870 if (!var)
871 ast_variable_update(cat, "vmsecret", new, NULL, 0);
872 else
873 ast_variable_append(cat, var);
876 /* save the results and clean things up */
877 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
878 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
879 config_text_file_save("users.conf", cfg, "AppVoicemail");
883 static void vm_change_password_shell(struct ast_vm_user *vmu, char *newpassword)
885 char buf[255];
886 snprintf(buf,255,"%s %s %s %s",ext_pass_cmd,vmu->context,vmu->mailbox,newpassword);
887 if (!ast_safe_system(buf)) {
888 ast_copy_string(vmu->password, newpassword, sizeof(vmu->password));
889 /* Reset the password in memory, too */
890 reset_user_pw(vmu->context, vmu->mailbox, newpassword);
894 static int make_dir(char *dest, int len, const char *context, const char *ext, const char *folder)
896 return snprintf(dest, len, "%s%s/%s/%s", VM_SPOOL_DIR, context, ext, folder);
899 static int make_file(char *dest, const int len, const char *dir, const int num)
901 return snprintf(dest, len, "%s/msg%04d", dir, num);
904 /* same as mkstemp, but return a FILE * */
905 static FILE *vm_mkftemp(char *template)
907 FILE *p = NULL;
908 int pfd = mkstemp(template);
909 chmod(template, VOICEMAIL_FILE_MODE & ~my_umask);
910 if (pfd > -1) {
911 p = fdopen(pfd, "w+");
912 if (!p) {
913 close(pfd);
914 pfd = -1;
917 return p;
920 /*! \brief basically mkdir -p $dest/$context/$ext/$folder
921 * \param dest String. base directory.
922 * \param len Length of dest.
923 * \param context String. Ignored if is null or empty string.
924 * \param ext String. Ignored if is null or empty string.
925 * \param folder String. Ignored if is null or empty string.
926 * \return -1 on failure, 0 on success.
928 static int create_dirpath(char *dest, int len, const char *context, const char *ext, const char *folder)
930 mode_t mode = VOICEMAIL_DIR_MODE;
932 if (!ast_strlen_zero(context)) {
933 make_dir(dest, len, context, "", "");
934 if (mkdir(dest, mode) && errno != EEXIST) {
935 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
936 return -1;
939 if (!ast_strlen_zero(ext)) {
940 make_dir(dest, len, context, ext, "");
941 if (mkdir(dest, mode) && errno != EEXIST) {
942 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
943 return -1;
946 if (!ast_strlen_zero(folder)) {
947 make_dir(dest, len, context, ext, folder);
948 if (mkdir(dest, mode) && errno != EEXIST) {
949 ast_log(LOG_WARNING, "mkdir '%s' failed: %s\n", dest, strerror(errno));
950 return -1;
953 return 0;
956 static char *mbox(int id)
958 static char *msgs[] = {
959 "INBOX",
960 "Old",
961 "Work",
962 "Family",
963 "Friends",
964 "Cust1",
965 "Cust2",
966 "Cust3",
967 "Cust4",
968 "Cust5",
970 return (id >= 0 && id < (sizeof(msgs)/sizeof(msgs[0]))) ? msgs[id] : "tmp";
973 static void free_user(struct ast_vm_user *vmu)
975 if (ast_test_flag(vmu, VM_ALLOCED))
976 free(vmu);
979 /* All IMAP-specific functions should go in this block. This
980 * keeps them from being spread out all over the code */
981 #ifdef IMAP_STORAGE
982 static void vm_imap_delete(int msgnum, struct ast_vm_user *vmu)
984 char arg[10];
985 struct vm_state *vms;
986 unsigned long messageNum;
988 /* Greetings aren't stored in IMAP, so we can't delete them there */
989 if (msgnum < 0) {
990 return;
993 if (!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
994 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);
995 return;
998 /* find real message number based on msgnum */
999 /* this may be an index into vms->msgArray based on the msgnum. */
1000 messageNum = vms->msgArray[msgnum];
1001 if (messageNum == 0) {
1002 ast_log(LOG_WARNING, "msgnum %d, mailbox message %lu is zero.\n",msgnum,messageNum);
1003 return;
1005 if (option_debug > 2)
1006 ast_log(LOG_DEBUG, "deleting msgnum %d, which is mailbox message %lu\n",msgnum,messageNum);
1007 /* delete message */
1008 snprintf (arg, sizeof(arg), "%lu",messageNum);
1009 mail_setflag (vms->mailstream,arg,"\\DELETED");
1012 static int imap_retrieve_file(const char *dir, const int msgnum, const struct ast_vm_user *vmu)
1014 BODY *body;
1015 char *header_content;
1016 char *attachedfilefmt;
1017 const char *cid_num;
1018 const char *cid_name;
1019 const char *duration;
1020 const char *context;
1021 const char *category;
1022 const char *origtime;
1023 struct vm_state *vms;
1024 char text_file[PATH_MAX];
1025 FILE *text_file_ptr;
1027 /* Greetings are not stored on the IMAP server, so we should not
1028 * attempt to retrieve them.
1030 if (msgnum < 0) {
1031 return 0;
1034 /* Before anything can happen, we need a vm_state so that we can
1035 * actually access the imap server through the vms->mailstream
1037 if(!(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 1)) && !(vms = get_vm_state_by_mailbox(vmu->mailbox, vmu->context, 0))) {
1038 /* This should not happen. If it does, then I guess we'd
1039 * need to create the vm_state, extract which mailbox to
1040 * open, and then set up the msgArray so that the correct
1041 * IMAP message could be accessed. If I have seen correctly
1042 * though, the vms should be obtainable from the vmstates list
1043 * and should have its msgArray properly set up.
1045 ast_log(LOG_ERROR, "Couldn't find a vm_state for mailbox %s!!! Oh no!\n", vmu->mailbox);
1048 make_file(vms->fn, sizeof(vms->fn), dir, msgnum);
1050 /* Don't try to retrieve a message from IMAP if it already is on the file system */
1051 if (ast_fileexists(vms->fn, NULL, NULL) > 0) {
1052 return 0;
1055 if (option_debug > 2)
1056 ast_log (LOG_DEBUG,"Before mail_fetchheaders, curmsg is: %d, imap messages is %lu\n", msgnum, vms->msgArray[msgnum]);
1057 if (vms->msgArray[msgnum] == 0) {
1058 ast_log (LOG_WARNING,"Trying to access unknown message\n");
1059 return -1;
1062 /* This will only work for new messages... */
1063 header_content = mail_fetchheader (vms->mailstream, vms->msgArray[msgnum]);
1064 /* empty string means no valid header */
1065 if (ast_strlen_zero(header_content)) {
1066 ast_log (LOG_ERROR,"Could not fetch header for message number %ld\n",vms->msgArray[msgnum]);
1067 return -1;
1070 mail_fetchstructure (vms->mailstream,vms->msgArray[msgnum],&body);
1072 /* We have the body, now we extract the file name of the first attachment. */
1073 if (body->nested.part && body->nested.part->next && body->nested.part->next->body.parameter->value) {
1074 attachedfilefmt = ast_strdupa(body->nested.part->next->body.parameter->value);
1075 } else {
1076 ast_log(LOG_ERROR, "There is no file attached to this IMAP message.\n");
1077 return -1;
1080 /* Find the format of the attached file */
1082 strsep(&attachedfilefmt, ".");
1083 if (!attachedfilefmt) {
1084 ast_log(LOG_ERROR, "File format could not be obtained from IMAP message attachment\n");
1085 return -1;
1088 save_body(body, vms, "2", attachedfilefmt);
1090 /* Get info from headers!! */
1091 snprintf(text_file, sizeof(text_file), "%s.%s", vms->fn, "txt");
1093 if (!(text_file_ptr = fopen(text_file, "w"))) {
1094 ast_log(LOG_WARNING, "Unable to open/create file %s: %s\n", text_file, strerror(errno));
1097 fprintf(text_file_ptr, "%s\n", "[message]");
1099 cid_num = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Num:");
1100 cid_name = get_header_by_tag(header_content, "X-Asterisk-VM-Caller-ID-Name:");
1101 fprintf(text_file_ptr, "callerid=\"%s\" <%s>\n", S_OR(cid_name, ""), S_OR(cid_num, ""));
1102 context = get_header_by_tag(header_content, "X-Asterisk-VM-Context:");
1103 fprintf(text_file_ptr, "context=%s\n", S_OR(context, ""));
1104 origtime = get_header_by_tag(header_content, "X-Asterisk-VM-Orig-time:");
1105 fprintf(text_file_ptr, "origtime=%s\n", S_OR(origtime, ""));
1106 duration = get_header_by_tag(header_content, "X-Asterisk-VM-Duration:");
1107 fprintf(text_file_ptr, "duration=%s\n", S_OR(origtime, ""));
1108 category = get_header_by_tag(header_content, "X-Asterisk-VM-Category:");
1109 fprintf(text_file_ptr, "category=%s\n", S_OR(category, ""));
1111 fclose(text_file_ptr);
1112 return 0;
1115 static int folder_int(const char *folder)
1117 /*assume a NULL folder means INBOX*/
1118 if (!folder)
1119 return 0;
1120 if (!strcasecmp(folder, "INBOX"))
1121 return 0;
1122 else if (!strcasecmp(folder, "Old"))
1123 return 1;
1124 else if (!strcasecmp(folder, "Work"))
1125 return 2;
1126 else if (!strcasecmp(folder, "Family"))
1127 return 3;
1128 else if (!strcasecmp(folder, "Friends"))
1129 return 4;
1130 else if (!strcasecmp(folder, "Cust1"))
1131 return 5;
1132 else if (!strcasecmp(folder, "Cust2"))
1133 return 6;
1134 else if (!strcasecmp(folder, "Cust3"))
1135 return 7;
1136 else if (!strcasecmp(folder, "Cust4"))
1137 return 8;
1138 else if (!strcasecmp(folder, "Cust5"))
1139 return 9;
1140 else /*assume they meant INBOX if folder is not found otherwise*/
1141 return 0;
1144 static int messagecount(const char *context, const char *mailbox, const char *folder)
1146 SEARCHPGM *pgm;
1147 SEARCHHEADER *hdr;
1149 struct ast_vm_user *vmu, vmus;
1150 struct vm_state *vms_p;
1151 int ret = 0;
1152 int fold = folder_int(folder);
1154 if (ast_strlen_zero(mailbox))
1155 return 0;
1157 /* We have to get the user before we can open the stream! */
1158 /* ast_log (LOG_DEBUG,"Before find_user, context is %s and mailbox is %s\n",context,mailbox); */
1159 vmu = find_user(&vmus, context, mailbox);
1160 if (!vmu) {
1161 ast_log (LOG_ERROR,"Couldn't find mailbox %s in context %s\n",mailbox,context);
1162 return -1;
1163 } else {
1164 /* No IMAP account available */
1165 if (vmu->imapuser[0] == '\0') {
1166 ast_log (LOG_WARNING,"IMAP user not set for mailbox %s\n",vmu->mailbox);
1167 return -1;
1171 /* check if someone is accessing this box right now... */
1172 vms_p = get_vm_state_by_imapuser(vmu->imapuser,1);
1173 if (!vms_p) {
1174 vms_p = get_vm_state_by_mailbox(mailbox, context, 1);
1176 if (vms_p) {
1177 if (option_debug > 2)
1178 ast_log (LOG_DEBUG,"Returning before search - user is logged in\n");
1179 if (fold == 0) {/*INBOX*/
1180 return vms_p->newmessages;
1182 if (fold == 1) {/*Old messages*/
1183 return vms_p->oldmessages;
1187 /* add one if not there... */
1188 vms_p = get_vm_state_by_imapuser(vmu->imapuser,0);
1189 if (!vms_p) {
1190 vms_p = get_vm_state_by_mailbox(mailbox, context, 0);
1193 if (!vms_p) {
1194 if (!(vms_p = create_vm_state_from_user(vmu))) {
1195 ast_log(LOG_WARNING, "Unable to allocate space for new vm_state!\n");
1196 return -1;
1199 ret = init_mailstream(vms_p, fold);
1200 if (!vms_p->mailstream) {
1201 ast_log (LOG_ERROR,"IMAP mailstream is NULL\n");
1202 return -1;
1204 if (ret == 0) {
1205 pgm = mail_newsearchpgm ();
1206 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", (char *)mailbox);
1207 pgm->header = hdr;
1208 if (fold != 1) {
1209 pgm->unseen = 1;
1210 pgm->seen = 0;
1212 /* In the special case where fold is 1 (old messages) we have to do things a bit
1213 * differently. Old messages are stored in the INBOX but are marked as "seen"
1215 else {
1216 pgm->unseen = 0;
1217 pgm->seen = 1;
1219 pgm->undeleted = 1;
1220 pgm->deleted = 0;
1222 vms_p->vmArrayIndex = 0;
1223 mail_search_full (vms_p->mailstream, NULL, pgm, NIL);
1224 if (fold == 0)
1225 vms_p->newmessages = vms_p->vmArrayIndex;
1226 if (fold == 1)
1227 vms_p->oldmessages = vms_p->vmArrayIndex;
1228 /*Freeing the searchpgm also frees the searchhdr*/
1229 mail_free_searchpgm(&pgm);
1230 vms_p->updated = 0;
1231 return vms_p->vmArrayIndex;
1232 } else {
1233 mail_ping(vms_p->mailstream);
1235 return 0;
1238 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)
1240 char *myserveremail = serveremail;
1241 char fn[PATH_MAX];
1242 char mailbox[256];
1243 char *stringp;
1244 FILE *p=NULL;
1245 char tmp[80] = "/tmp/astmail-XXXXXX";
1246 long len;
1247 void *buf;
1248 int tempcopy = 0;
1249 STRING str;
1251 /*Greetings are not retrieved from IMAP, so there is no reason to attempt storing them there either*/
1252 if (msgnum < 0)
1253 return 0;
1255 /* Attach only the first format */
1256 fmt = ast_strdupa(fmt);
1257 stringp = fmt;
1258 strsep(&stringp, "|");
1260 if (!ast_strlen_zero(vmu->serveremail))
1261 myserveremail = vmu->serveremail;
1263 make_file(fn, sizeof(fn), dir, msgnum);
1265 if (ast_strlen_zero(vmu->email)) {
1266 /*we need the vmu->email to be set when we call make_email_file, but if we keep it set,
1267 * a duplicate e-mail will be created. So at the end of this function, we will revert back to an empty
1268 * string if tempcopy is 1
1270 ast_copy_string(vmu->email, vmu->imapuser, sizeof(vmu->email));
1271 tempcopy = 1;
1274 if (!strcmp(fmt, "wav49"))
1275 fmt = "WAV";
1276 if (option_debug > 2)
1277 ast_log(LOG_DEBUG, "Storing file '%s', format '%s'\n", fn, fmt);
1278 /* Make a temporary file instead of piping directly to sendmail, in case the mail
1279 command hangs */
1280 if ((p = vm_mkftemp(tmp)) == NULL) {
1281 ast_log(LOG_WARNING, "Unable to store '%s' (can't create temporary file)\n", fn);
1282 if (tempcopy)
1283 *(vmu->email) = '\0';
1284 return -1;
1285 } else {
1286 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);
1287 /* read mail file to memory */
1288 len = ftell(p);
1289 rewind(p);
1290 if ((buf = ast_malloc(len+1)) == NIL) {
1291 ast_log(LOG_ERROR, "Can't allocate %ld bytes to read message\n", len+1);
1292 fclose(p);
1293 return -1;
1295 fread(buf, len, 1, p);
1296 ((char *)buf)[len] = '\0';
1297 INIT(&str, mail_string, buf, len);
1298 init_mailstream(vms, 0);
1299 imap_mailbox_name(mailbox, sizeof(mailbox), vms, 0, 1);
1300 if (!mail_append(vms->mailstream, mailbox, &str))
1301 ast_log(LOG_ERROR, "Error while sending the message to %s\n", mailbox);
1302 fclose(p);
1303 unlink(tmp);
1304 ast_free(buf);
1305 if (option_debug > 2)
1306 ast_log(LOG_DEBUG, "%s stored\n", fn);
1307 /* Using messagecount to populate the last place in the msgArray
1308 * is less than optimal, but it's the only way given the current setup
1310 messagecount(vmu->context, vmu->mailbox, "INBOX");
1312 if (tempcopy)
1313 *(vmu->email) = '\0';
1314 return 0;
1318 static int inboxcount(const char *mailbox_context, int *newmsgs, int *oldmsgs)
1320 char tmp[PATH_MAX] = "";
1321 char *mailboxnc;
1322 char *context;
1323 char *mb;
1324 char *cur;
1325 if (newmsgs)
1326 *newmsgs = 0;
1327 if (oldmsgs)
1328 *oldmsgs = 0;
1330 if (option_debug > 2)
1331 ast_log (LOG_DEBUG,"Mailbox is set to %s\n",mailbox_context);
1332 /* If no mailbox, return immediately */
1333 if (ast_strlen_zero(mailbox_context))
1334 return 0;
1336 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
1337 context = strchr(tmp, '@');
1338 if (strchr(mailbox_context, ',')) {
1339 int tmpnew, tmpold;
1340 ast_copy_string(tmp, mailbox_context, sizeof(tmp));
1341 mb = tmp;
1342 while ((cur = strsep(&mb, ", "))) {
1343 if (!ast_strlen_zero(cur)) {
1344 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
1345 return -1;
1346 else {
1347 if (newmsgs)
1348 *newmsgs += tmpnew;
1349 if (oldmsgs)
1350 *oldmsgs += tmpold;
1354 return 0;
1356 if (context) {
1357 *context = '\0';
1358 mailboxnc = tmp;
1359 context++;
1360 } else {
1361 context = "default";
1362 mailboxnc = (char *)mailbox_context;
1364 if (newmsgs) {
1365 if ((*newmsgs = messagecount(context, mailboxnc, "INBOX")) < 0)
1366 return -1;
1368 if (oldmsgs) {
1369 if ((*oldmsgs = messagecount(context, mailboxnc, "Old")) < 0)
1370 return -1;
1372 return 0;
1376 static int has_voicemail(const char *mailbox, const char *folder)
1378 char tmp[256], *tmp2, *mbox, *context;
1379 ast_copy_string(tmp, mailbox, sizeof(tmp));
1380 tmp2 = tmp;
1381 if (strchr(tmp2, ',')) {
1382 while ((mbox = strsep(&tmp2, ","))) {
1383 if (!ast_strlen_zero(mbox)) {
1384 if (has_voicemail(mbox, folder))
1385 return 1;
1389 if ((context= strchr(tmp, '@')))
1390 *context++ = '\0';
1391 else
1392 context = "default";
1393 return messagecount(context, tmp, folder) ? 1 : 0;
1396 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)
1398 struct vm_state *sendvms = NULL, *destvms = NULL;
1399 char messagestring[10]; /*I guess this could be a problem if someone has more than 999999999 messages...*/
1400 if (msgnum >= recip->maxmsg) {
1401 ast_log(LOG_WARNING, "Unable to copy mail, mailbox %s is full\n", recip->mailbox);
1402 return -1;
1404 if (!(sendvms = get_vm_state_by_imapuser(vmu->imapuser, 0))) {
1405 ast_log(LOG_ERROR, "Couldn't get vm_state for originator's mailbox!!\n");
1406 return -1;
1408 if (!(destvms = get_vm_state_by_imapuser(recip->imapuser, 0))) {
1409 ast_log(LOG_ERROR, "Couldn't get vm_state for destination mailbox!\n");
1410 return -1;
1412 snprintf(messagestring, sizeof(messagestring), "%ld", sendvms->msgArray[msgnum]);
1413 if ((mail_copy(sendvms->mailstream, messagestring, (char *) mbox(imbox)) == T))
1414 return 0;
1415 ast_log(LOG_WARNING, "Unable to copy message from mailbox %s to mailbox %s\n", vmu->mailbox, recip->mailbox);
1416 return -1;
1419 static void imap_mailbox_name(char *spec, size_t len, struct vm_state *vms, int box, int use_folder)
1421 char tmp[256], *t = tmp;
1422 size_t left = sizeof(tmp);
1424 if (box == 1) {
1425 ast_copy_string(vms->curbox, mbox(0), sizeof(vms->curbox));
1426 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", mbox(1));
1427 } else {
1428 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
1429 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
1432 /* Build up server information */
1433 ast_build_string(&t, &left, "{%s:%s/imap", imapserver, imapport);
1435 /* Add authentication user if present */
1436 if (!ast_strlen_zero(authuser))
1437 ast_build_string(&t, &left, "/authuser=%s", authuser);
1439 /* Add flags if present */
1440 if (!ast_strlen_zero(imapflags))
1441 ast_build_string(&t, &left, "/%s", imapflags);
1443 /* End with username */
1444 ast_build_string(&t, &left, "/user=%s}", vms->imapuser);
1446 if (box == 0 || box == 1)
1447 snprintf(spec, len, "%s%s", tmp, use_folder? imapfolder: "INBOX");
1448 else
1449 snprintf(spec, len, "%s%s%c%s", tmp, imapfolder, delimiter, mbox(box));
1452 static int init_mailstream(struct vm_state *vms, int box)
1454 MAILSTREAM *stream = NIL;
1455 long debug;
1456 char tmp[256];
1458 if (!vms) {
1459 ast_log (LOG_ERROR,"vm_state is NULL!\n");
1460 return -1;
1462 if (option_debug > 2)
1463 ast_log (LOG_DEBUG,"vm_state user is:%s\n",vms->imapuser);
1464 if (vms->mailstream == NIL || !vms->mailstream) {
1465 if (option_debug)
1466 ast_log (LOG_DEBUG,"mailstream not set.\n");
1467 } else {
1468 stream = vms->mailstream;
1470 /* debug = T; user wants protocol telemetry? */
1471 debug = NIL; /* NO protocol telemetry? */
1473 if (delimiter == '\0') { /* did not probe the server yet */
1474 char *cp;
1475 #ifdef USE_SYSTEM_IMAP
1476 #include <imap/linkage.c>
1477 #elif defined(USE_SYSTEM_CCLIENT)
1478 #include <c-client/linkage.c>
1479 #else
1480 #include "linkage.c"
1481 #endif
1482 /* Connect to INBOX first to get folders delimiter */
1483 imap_mailbox_name(tmp, sizeof(tmp), vms, 0, 1);
1484 ast_mutex_lock(&vms->lock);
1485 stream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
1486 ast_mutex_unlock(&vms->lock);
1487 if (stream == NIL) {
1488 ast_log (LOG_ERROR, "Can't connect to imap server %s\n", tmp);
1489 return -1;
1491 get_mailbox_delimiter(stream);
1492 /* update delimiter in imapfolder */
1493 for (cp = imapfolder; *cp; cp++)
1494 if (*cp == '/')
1495 *cp = delimiter;
1497 /* Now connect to the target folder */
1498 imap_mailbox_name(tmp, sizeof(tmp), vms, box, 1);
1499 if (option_debug > 2)
1500 ast_log (LOG_DEBUG,"Before mail_open, server: %s, box:%d\n", tmp, box);
1501 ast_mutex_lock(&vms->lock);
1502 vms->mailstream = mail_open (stream, tmp, debug ? OP_DEBUG : NIL);
1503 ast_mutex_unlock(&vms->lock);
1504 if (vms->mailstream == NIL) {
1505 return -1;
1506 } else {
1507 return 0;
1511 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu, int box)
1513 SEARCHPGM *pgm;
1514 SEARCHHEADER *hdr;
1515 int ret;
1517 ast_copy_string(vms->imapuser,vmu->imapuser, sizeof(vms->imapuser));
1518 if (option_debug > 2)
1519 ast_log(LOG_DEBUG,"Before init_mailstream, user is %s\n",vmu->imapuser);
1520 ret = init_mailstream(vms, box);
1521 if (ret != 0 || !vms->mailstream) {
1522 ast_log (LOG_ERROR,"Could not initialize mailstream\n");
1523 return -1;
1526 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
1528 /* Check Quota */
1529 if (box == 0) {
1530 if (option_debug > 2)
1531 ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mbox(box));
1532 check_quota(vms,(char *)mbox(box));
1535 pgm = mail_newsearchpgm();
1537 /* Check IMAP folder for Asterisk messages only... */
1538 hdr = mail_newsearchheader ("X-Asterisk-VM-Extension", vmu->mailbox);
1539 pgm->header = hdr;
1540 pgm->deleted = 0;
1541 pgm->undeleted = 1;
1543 /* if box = 0, check for new, if box = 1, check for read */
1544 if (box == 0) {
1545 pgm->unseen = 1;
1546 pgm->seen = 0;
1547 } else if (box == 1) {
1548 pgm->seen = 1;
1549 pgm->unseen = 0;
1552 vms->vmArrayIndex = 0;
1553 if (option_debug > 2)
1554 ast_log(LOG_DEBUG,"Before mail_search_full, user is %s\n",vmu->imapuser);
1555 mail_search_full (vms->mailstream, NULL, pgm, NIL);
1557 vms->lastmsg = vms->vmArrayIndex - 1;
1559 mail_free_searchpgm(&pgm);
1560 return 0;
1563 static void write_file(char *filename, char *buffer, unsigned long len)
1565 FILE *output;
1567 output = fopen (filename, "w");
1568 fwrite (buffer, len, 1, output);
1569 fclose (output);
1572 void mm_searched(MAILSTREAM *stream, unsigned long number)
1574 struct vm_state *vms;
1575 char *mailbox;
1576 char *user;
1577 mailbox = stream->mailbox;
1578 user = get_user_by_mailbox(mailbox);
1579 vms = get_vm_state_by_imapuser(user,2);
1580 if (vms) {
1581 if (option_debug > 2)
1582 ast_log (LOG_DEBUG, "saving mailbox message number %lu as message %d. Interactive set to %d\n",number,vms->vmArrayIndex,vms->interactive);
1583 vms->msgArray[vms->vmArrayIndex++] = number;
1584 } else {
1585 ast_log (LOG_ERROR, "No state found.\n");
1589 static struct ast_vm_user *find_user_realtime_imapuser(const char *imapuser)
1591 struct ast_variable *var;
1592 struct ast_vm_user *vmu;
1594 vmu = ast_calloc(1, sizeof *vmu);
1595 if (!vmu)
1596 return NULL;
1597 ast_set_flag(vmu, VM_ALLOCED);
1598 populate_defaults(vmu);
1600 var = ast_load_realtime("voicemail", "imapuser", imapuser, NULL);
1601 if (var) {
1602 apply_options_full(vmu, var);
1603 ast_variables_destroy(var);
1604 return vmu;
1605 } else {
1606 free(vmu);
1607 return NULL;
1611 /* Interfaces to C-client */
1613 void mm_exists(MAILSTREAM * stream, unsigned long number)
1615 /* mail_ping will callback here if new mail! */
1616 if (option_debug > 3)
1617 ast_log (LOG_DEBUG, "Entering EXISTS callback for message %ld\n", number);
1618 if (number == 0) return;
1619 set_update(stream);
1623 void mm_expunged(MAILSTREAM * stream, unsigned long number)
1625 /* mail_ping will callback here if expunged mail! */
1626 if (option_debug > 3)
1627 ast_log (LOG_DEBUG, "Entering EXPUNGE callback for message %ld\n", number);
1628 if (number == 0) return;
1629 set_update(stream);
1633 void mm_flags(MAILSTREAM * stream, unsigned long number)
1635 /* mail_ping will callback here if read mail! */
1636 if (option_debug > 3)
1637 ast_log (LOG_DEBUG, "Entering FLAGS callback for message %ld\n", number);
1638 if (number == 0) return;
1639 set_update(stream);
1643 void mm_notify(MAILSTREAM * stream, char *string, long errflg)
1645 mm_log (string, errflg);
1649 void mm_list(MAILSTREAM * stream, int delim, char *mailbox, long attributes)
1651 if (delimiter == '\0') {
1652 delimiter = delim;
1654 if (option_debug > 4) {
1655 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delim, mailbox);
1656 if (attributes & LATT_NOINFERIORS)
1657 ast_log(LOG_DEBUG, "no inferiors\n");
1658 if (attributes & LATT_NOSELECT)
1659 ast_log(LOG_DEBUG, "no select\n");
1660 if (attributes & LATT_MARKED)
1661 ast_log(LOG_DEBUG, "marked\n");
1662 if (attributes & LATT_UNMARKED)
1663 ast_log(LOG_DEBUG, "unmarked\n");
1668 void mm_lsub(MAILSTREAM * stream, int delimiter, char *mailbox, long attributes)
1670 if (option_debug > 4) {
1671 ast_log(LOG_DEBUG, "Delimiter set to %c and mailbox %s\n",delimiter, mailbox);
1672 if (attributes & LATT_NOINFERIORS)
1673 ast_log(LOG_DEBUG, "no inferiors\n");
1674 if (attributes & LATT_NOSELECT)
1675 ast_log(LOG_DEBUG, "no select\n");
1676 if (attributes & LATT_MARKED)
1677 ast_log(LOG_DEBUG, "marked\n");
1678 if (attributes & LATT_UNMARKED)
1679 ast_log(LOG_DEBUG, "unmarked\n");
1684 void mm_status(MAILSTREAM * stream, char *mailbox, MAILSTATUS * status)
1686 ast_log (LOG_NOTICE," Mailbox %s", mailbox);
1687 if (status->flags & SA_MESSAGES)
1688 ast_log (LOG_NOTICE,", %lu messages", status->messages);
1689 if (status->flags & SA_RECENT)
1690 ast_log (LOG_NOTICE,", %lu recent", status->recent);
1691 if (status->flags & SA_UNSEEN)
1692 ast_log (LOG_NOTICE,", %lu unseen", status->unseen);
1693 if (status->flags & SA_UIDVALIDITY)
1694 ast_log (LOG_NOTICE,", %lu UID validity", status->uidvalidity);
1695 if (status->flags & SA_UIDNEXT)
1696 ast_log (LOG_NOTICE,", %lu next UID", status->uidnext);
1697 ast_log (LOG_NOTICE,"\n");
1701 void mm_log(char *string, long errflg)
1703 switch ((short) errflg) {
1704 case NIL:
1705 if (option_debug)
1706 ast_log(LOG_DEBUG,"IMAP Info: %s\n", string);
1707 break;
1708 case PARSE:
1709 case WARN:
1710 ast_log (LOG_WARNING,"IMAP Warning: %s\n", string);
1711 break;
1712 case ERROR:
1713 ast_log (LOG_ERROR,"IMAP Error: %s\n", string);
1714 break;
1719 void mm_dlog(char *string)
1721 ast_log (LOG_NOTICE, "%s\n", string);
1725 void mm_login(NETMBX * mb, char *user, char *pwd, long trial)
1727 struct ast_vm_user *vmu;
1729 if (option_debug > 3)
1730 ast_log(LOG_DEBUG, "Entering callback mm_login\n");
1732 ast_copy_string(user, mb->user, MAILTMPLEN);
1734 /* We should only do this when necessary */
1735 if (!ast_strlen_zero(authpassword)) {
1736 ast_copy_string(pwd, authpassword, MAILTMPLEN);
1737 } else {
1738 AST_LIST_TRAVERSE(&users, vmu, list) {
1739 if (!strcasecmp(mb->user, vmu->imapuser)) {
1740 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
1741 break;
1744 if (!vmu) {
1745 if ((vmu = find_user_realtime_imapuser(mb->user))) {
1746 ast_copy_string(pwd, vmu->imappassword, MAILTMPLEN);
1747 free_user(vmu);
1754 void mm_critical(MAILSTREAM * stream)
1759 void mm_nocritical(MAILSTREAM * stream)
1764 long mm_diskerror(MAILSTREAM * stream, long errcode, long serious)
1766 kill (getpid (), SIGSTOP);
1767 return NIL;
1771 void mm_fatal(char *string)
1773 ast_log(LOG_ERROR,"IMAP access FATAL error: %s\n", string);
1776 /* C-client callback to handle quota */
1777 static void mm_parsequota(MAILSTREAM *stream, unsigned char *msg, QUOTALIST *pquota)
1779 struct vm_state *vms;
1780 char *mailbox;
1781 char *user;
1782 unsigned long usage = 0;
1783 unsigned long limit = 0;
1785 while (pquota) {
1786 usage = pquota->usage;
1787 limit = pquota->limit;
1788 pquota = pquota->next;
1791 mailbox = stream->mailbox;
1792 user = get_user_by_mailbox(mailbox);
1793 vms = get_vm_state_by_imapuser(user,2);
1794 if (vms) {
1795 if (option_debug > 2)
1796 ast_log (LOG_DEBUG, "User %s usage is %lu, limit is %lu\n",user,usage,limit);
1797 vms->quota_usage = usage;
1798 vms->quota_limit = limit;
1799 } else {
1800 ast_log (LOG_ERROR, "No state found.\n");
1804 static char *get_header_by_tag(char *header, char *tag)
1806 char *start;
1807 int taglen;
1808 char *eol_pnt;
1810 if (!header || !tag)
1811 return NULL;
1813 taglen = strlen(tag) + 1;
1814 if (taglen < 1)
1815 return NULL;
1817 start = strstr(header, tag);
1818 if (!start)
1819 return NULL;
1821 ast_mutex_lock(&imaptemp_lock);
1822 ast_copy_string(imaptemp, start+taglen, sizeof(imaptemp));
1823 ast_mutex_unlock(&imaptemp_lock);
1824 if ((eol_pnt = strchr(imaptemp,'\r')) || (eol_pnt = strchr(imaptemp,'\n')))
1825 *eol_pnt = '\0';
1826 return imaptemp;
1829 static char *get_user_by_mailbox(char *mailbox)
1831 char *start, *quote;
1832 char *eol_pnt;
1834 if (!mailbox)
1835 return NULL;
1837 start = strstr(mailbox,"/user=");
1838 if (!start)
1839 return NULL;
1841 ast_mutex_lock(&imaptemp_lock);
1842 ast_copy_string(imaptemp, start+6, sizeof(imaptemp));
1843 ast_mutex_unlock(&imaptemp_lock);
1845 quote = strchr(imaptemp,'\"');
1846 if (!quote) { /* if username is not in quotes */
1847 eol_pnt = strchr(imaptemp,'/');
1848 if (!eol_pnt) {
1849 eol_pnt = strchr(imaptemp,'}');
1851 *eol_pnt = '\0';
1852 return imaptemp;
1853 } else {
1854 eol_pnt = strchr(imaptemp+1,'\"');
1855 *eol_pnt = '\0';
1856 return imaptemp+1;
1860 static struct vm_state *create_vm_state_from_user(struct ast_vm_user *vmu)
1862 struct vm_state *vms_p;
1864 if (option_debug > 4)
1865 ast_log(LOG_DEBUG,"Adding new vmstate for %s\n",vmu->imapuser);
1866 if (!(vms_p = ast_calloc(1, sizeof(*vms_p))))
1867 return NULL;
1868 ast_copy_string(vms_p->imapuser,vmu->imapuser, sizeof(vms_p->imapuser));
1869 ast_copy_string(vms_p->username, vmu->mailbox, sizeof(vms_p->username)); /* save for access from interactive entry point */
1870 ast_copy_string(vms_p->context, vmu->context, sizeof(vms_p->context));
1871 vms_p->mailstream = NIL; /* save for access from interactive entry point */
1872 if (option_debug > 4)
1873 ast_log(LOG_DEBUG,"Copied %s to %s\n",vmu->imapuser,vms_p->imapuser);
1874 vms_p->updated = 1;
1875 /* set mailbox to INBOX! */
1876 ast_copy_string(vms_p->curbox, mbox(0), sizeof(vms_p->curbox));
1877 init_vm_state(vms_p);
1878 vmstate_insert(vms_p);
1879 return vms_p;
1882 static struct vm_state *get_vm_state_by_imapuser(char *user, int interactive)
1884 struct vmstate *vlist = NULL;
1886 ast_mutex_lock(&vmstate_lock);
1887 vlist = vmstates;
1888 while (vlist) {
1889 if (vlist->vms) {
1890 if (vlist->vms->imapuser) {
1891 if (!strcmp(vlist->vms->imapuser,user)) {
1892 if (interactive == 2) {
1893 ast_mutex_unlock(&vmstate_lock);
1894 return vlist->vms;
1895 } else if (vlist->vms->interactive == interactive) {
1896 ast_mutex_unlock(&vmstate_lock);
1897 return vlist->vms;
1900 } else {
1901 if (option_debug > 2)
1902 ast_log(LOG_DEBUG, " error: imapuser is NULL for %s\n",user);
1904 } else {
1905 if (option_debug > 2)
1906 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",user);
1908 vlist = vlist->next;
1910 ast_mutex_unlock(&vmstate_lock);
1911 if (option_debug > 2)
1912 ast_log(LOG_DEBUG, "%s not found in vmstates\n",user);
1913 return NULL;
1916 static struct vm_state *get_vm_state_by_mailbox(const char *mailbox, const char *context, int interactive)
1918 struct vmstate *vlist = NULL;
1919 const char *local_context = S_OR(context, "default");
1921 ast_mutex_lock(&vmstate_lock);
1922 vlist = vmstates;
1923 if (option_debug > 2)
1924 ast_log(LOG_DEBUG, "Mailbox set to %s\n",mailbox);
1925 while (vlist) {
1926 if (vlist->vms) {
1927 if (vlist->vms->username && vlist->vms->context) {
1928 if (option_debug > 2)
1929 ast_log(LOG_DEBUG, " comparing mailbox %s (i=%d) to vmstate mailbox %s (i=%d)\n",mailbox,interactive,vlist->vms->username,vlist->vms->interactive);
1930 if (!strcmp(vlist->vms->username,mailbox) && !(strcmp(vlist->vms->context, local_context)) && vlist->vms->interactive == interactive) {
1931 if (option_debug > 2)
1932 ast_log(LOG_DEBUG, " Found it!\n");
1933 ast_mutex_unlock(&vmstate_lock);
1934 return vlist->vms;
1936 } else {
1937 if (option_debug > 2)
1938 ast_log(LOG_DEBUG, " error: username or context is NULL for %s\n",mailbox);
1940 } else {
1941 if (option_debug > 2)
1942 ast_log(LOG_DEBUG, " error: vms is NULL for %s\n",mailbox);
1944 vlist = vlist->next;
1946 ast_mutex_unlock(&vmstate_lock);
1947 if (option_debug > 2)
1948 ast_log(LOG_DEBUG, "%s not found in vmstates\n",mailbox);
1949 return NULL;
1952 static void vmstate_insert(struct vm_state *vms)
1954 struct vmstate *v;
1955 struct vm_state *altvms;
1957 /* If interactive, it probably already exists, and we should
1958 use the one we already have since it is more up to date.
1959 We can compare the username to find the duplicate */
1960 if (vms->interactive == 1) {
1961 altvms = get_vm_state_by_mailbox(vms->username, vms->context, 0);
1962 if (altvms) {
1963 if (option_debug > 2)
1964 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
1965 vms->newmessages = altvms->newmessages;
1966 vms->oldmessages = altvms->oldmessages;
1967 /* memcpy(vms->msgArray, altvms->msgArray, sizeof(long)*256); */
1968 copy_msgArray(vms, altvms);
1969 vms->vmArrayIndex = altvms->vmArrayIndex;
1970 vms->lastmsg = altvms->lastmsg;
1971 vms->curmsg = altvms->curmsg;
1972 /* get a pointer to the persistent store */
1973 vms->persist_vms = altvms;
1974 /* Reuse the mailstream? */
1975 vms->mailstream = altvms->mailstream;
1976 /* vms->mailstream = NIL; */
1980 v = (struct vmstate *)malloc(sizeof(struct vmstate));
1981 if (!v) {
1982 ast_log(LOG_ERROR, "Out of memory\n");
1984 if (option_debug > 2)
1985 ast_log(LOG_DEBUG, "Inserting vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
1986 ast_mutex_lock(&vmstate_lock);
1987 v->vms = vms;
1988 v->next = vmstates;
1989 vmstates = v;
1990 ast_mutex_unlock(&vmstate_lock);
1993 static void vmstate_delete(struct vm_state *vms)
1995 struct vmstate *vc, *vf = NULL, *vl = NULL;
1996 struct vm_state *altvms;
1998 /* If interactive, we should copy pertainent info
1999 back to the persistent state (to make update immediate) */
2000 if (vms->interactive == 1) {
2001 altvms = vms->persist_vms;
2002 if (altvms) {
2003 if (option_debug > 2)
2004 ast_log(LOG_DEBUG, "Duplicate mailbox %s, copying message info...\n",vms->username);
2005 altvms->newmessages = vms->newmessages;
2006 altvms->oldmessages = vms->oldmessages;
2007 altvms->updated = 1;
2011 ast_mutex_lock(&vmstate_lock);
2012 vc = vmstates;
2013 if (option_debug > 2)
2014 ast_log(LOG_DEBUG, "Removing vm_state for user:%s, mailbox %s\n",vms->imapuser,vms->username);
2015 while (vc) {
2016 if (vc->vms == vms) {
2017 vf = vc;
2018 if (vl)
2019 vl->next = vc->next;
2020 else
2021 vmstates = vc->next;
2022 break;
2024 vl = vc;
2025 vc = vc->next;
2027 if (!vf) {
2028 ast_log(LOG_ERROR, "No vmstate found for user:%s, mailbox %s\n",vms->imapuser,vms->username);
2029 } else {
2030 ast_mutex_destroy(&vf->vms->lock);
2031 free(vf);
2033 ast_mutex_unlock(&vmstate_lock);
2036 static void set_update(MAILSTREAM * stream)
2038 struct vm_state *vms;
2039 char *mailbox;
2040 char *user;
2042 mailbox = stream->mailbox;
2043 user = get_user_by_mailbox(mailbox);
2044 vms = get_vm_state_by_imapuser(user, 0);
2045 if (vms) {
2046 if (option_debug > 2)
2047 ast_log (LOG_DEBUG, "User %s mailbox set for update.\n",user);
2048 vms->updated = 1; /* set updated flag since mailbox changed */
2049 } else {
2050 if (option_debug > 2)
2051 ast_log (LOG_WARNING, "User %s mailbox not found for update.\n",user);
2055 static void init_vm_state(struct vm_state *vms)
2057 int x;
2058 vms->vmArrayIndex = 0;
2059 for (x = 0; x < 256; x++) {
2060 vms->msgArray[x] = 0;
2062 ast_mutex_init(&vms->lock);
2065 static void copy_msgArray(struct vm_state *dst, struct vm_state *src)
2067 int x;
2068 for (x = 0; x<256; x++) {
2069 dst->msgArray[x] = src->msgArray[x];
2073 static int save_body(BODY *body, struct vm_state *vms, char *section, char *format)
2075 char *body_content;
2076 char *body_decoded;
2077 unsigned long len;
2078 unsigned long newlen;
2079 char filename[256];
2081 if (!body || body == NIL)
2082 return -1;
2083 body_content = mail_fetchbody (vms->mailstream, vms->msgArray[vms->curmsg], section, &len);
2084 if (body_content != NIL) {
2085 snprintf(filename, sizeof(filename), "%s.%s", vms->fn, format);
2086 /* ast_log (LOG_DEBUG,body_content); */
2087 body_decoded = rfc822_base64 ((unsigned char *)body_content, len, &newlen);
2088 write_file (filename, (char *) body_decoded, newlen);
2090 return 0;
2093 /* get delimiter via mm_list callback */
2094 static void get_mailbox_delimiter(MAILSTREAM *stream) {
2095 char tmp[50];
2096 snprintf(tmp, sizeof(tmp), "{%s}", imapserver);
2097 mail_list(stream, tmp, "*");
2100 /* Check Quota for user */
2101 static void check_quota(struct vm_state *vms, char *mailbox) {
2102 mail_parameters(NULL, SET_QUOTA, (void *) mm_parsequota);
2103 if (option_debug > 2)
2104 ast_log(LOG_DEBUG, "Mailbox name set to: %s, about to check quotas\n", mailbox);
2105 if (vms && vms->mailstream != NULL) {
2106 imap_getquotaroot(vms->mailstream, mailbox);
2107 } else {
2108 ast_log(LOG_WARNING,"Mailstream not available for mailbox: %s\n",mailbox);
2111 #endif
2113 /* only return failure if ast_lock_path returns 'timeout',
2114 not if the path does not exist or any other reason
2116 static int vm_lock_path(const char *path)
2118 switch (ast_lock_path(path)) {
2119 case AST_LOCK_TIMEOUT:
2120 return -1;
2121 default:
2122 return 0;
2127 #ifdef ODBC_STORAGE
2128 struct generic_prepare_struct {
2129 char *sql;
2130 int argc;
2131 char **argv;
2134 static SQLHSTMT generic_prepare(struct odbc_obj *obj, void *data)
2136 struct generic_prepare_struct *gps = data;
2137 int res, i;
2138 SQLHSTMT stmt;
2140 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2141 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2142 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2143 return NULL;
2145 res = SQLPrepare(stmt, (unsigned char *)gps->sql, SQL_NTS);
2146 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2147 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", gps->sql);
2148 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2149 return NULL;
2151 for (i = 0; i < gps->argc; i++)
2152 SQLBindParameter(stmt, i + 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(gps->argv[i]), 0, gps->argv[i], 0, NULL);
2154 return stmt;
2157 static int retrieve_file(char *dir, int msgnum)
2159 int x = 0;
2160 int res;
2161 int fd=-1;
2162 size_t fdlen = 0;
2163 void *fdm = MAP_FAILED;
2164 SQLSMALLINT colcount=0;
2165 SQLHSTMT stmt;
2166 char sql[PATH_MAX];
2167 char fmt[80]="";
2168 char *c;
2169 char coltitle[256];
2170 SQLSMALLINT collen;
2171 SQLSMALLINT datatype;
2172 SQLSMALLINT decimaldigits;
2173 SQLSMALLINT nullable;
2174 SQLULEN colsize;
2175 SQLLEN colsize2;
2176 FILE *f=NULL;
2177 char rowdata[80];
2178 char fn[PATH_MAX];
2179 char full_fn[PATH_MAX];
2180 char msgnums[80];
2181 char *argv[] = { dir, msgnums };
2182 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
2184 struct odbc_obj *obj;
2185 obj = ast_odbc_request_obj(odbc_database, 0);
2186 if (obj) {
2187 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2188 c = strchr(fmt, '|');
2189 if (c)
2190 *c = '\0';
2191 if (!strcasecmp(fmt, "wav49"))
2192 strcpy(fmt, "WAV");
2193 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
2194 if (msgnum > -1)
2195 make_file(fn, sizeof(fn), dir, msgnum);
2196 else
2197 ast_copy_string(fn, dir, sizeof(fn));
2198 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
2200 if (!(f = fopen(full_fn, "w+"))) {
2201 ast_log(LOG_WARNING, "Failed to open/create '%s'\n", full_fn);
2202 goto yuck;
2205 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
2206 snprintf(sql, sizeof(sql), "SELECT * FROM %s WHERE dir=? AND msgnum=?",odbc_table);
2207 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2208 if (!stmt) {
2209 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2210 ast_odbc_release_obj(obj);
2211 goto yuck;
2213 res = SQLFetch(stmt);
2214 if (res == SQL_NO_DATA) {
2215 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2216 ast_odbc_release_obj(obj);
2217 goto yuck;
2219 else if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2220 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2221 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2222 ast_odbc_release_obj(obj);
2223 goto yuck;
2225 fd = open(full_fn, O_RDWR | O_CREAT | O_TRUNC, 0770);
2226 if (fd < 0) {
2227 ast_log(LOG_WARNING, "Failed to write '%s': %s\n", full_fn, strerror(errno));
2228 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2229 ast_odbc_release_obj(obj);
2230 goto yuck;
2232 res = SQLNumResultCols(stmt, &colcount);
2233 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2234 ast_log(LOG_WARNING, "SQL Column Count error!\n[%s]\n\n", sql);
2235 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2236 ast_odbc_release_obj(obj);
2237 goto yuck;
2239 if (f)
2240 fprintf(f, "[message]\n");
2241 for (x=0;x<colcount;x++) {
2242 rowdata[0] = '\0';
2243 collen = sizeof(coltitle);
2244 res = SQLDescribeCol(stmt, x + 1, (unsigned char *)coltitle, sizeof(coltitle), &collen,
2245 &datatype, &colsize, &decimaldigits, &nullable);
2246 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2247 ast_log(LOG_WARNING, "SQL Describe Column error!\n[%s]\n\n", sql);
2248 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2249 ast_odbc_release_obj(obj);
2250 goto yuck;
2252 if (!strcasecmp(coltitle, "recording")) {
2253 off_t offset;
2254 res = SQLGetData(stmt, x + 1, SQL_BINARY, rowdata, 0, &colsize2);
2255 fdlen = colsize2;
2256 if (fd > -1) {
2257 char tmp[1]="";
2258 lseek(fd, fdlen - 1, SEEK_SET);
2259 if (write(fd, tmp, 1) != 1) {
2260 close(fd);
2261 fd = -1;
2262 continue;
2264 /* Read out in small chunks */
2265 for (offset = 0; offset < colsize2; offset += CHUNKSIZE) {
2266 if ((fdm = mmap(NULL, CHUNKSIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, offset)) == MAP_FAILED) {
2267 ast_log(LOG_WARNING, "Could not mmap the output file: %s (%d)\n", strerror(errno), errno);
2268 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2269 ast_odbc_release_obj(obj);
2270 goto yuck;
2271 } else {
2272 res = SQLGetData(stmt, x + 1, SQL_BINARY, fdm, CHUNKSIZE, NULL);
2273 munmap(fdm, CHUNKSIZE);
2274 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2275 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2276 unlink(full_fn);
2277 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2278 ast_odbc_release_obj(obj);
2279 goto yuck;
2283 truncate(full_fn, fdlen);
2285 } else {
2286 SQLLEN ind;
2287 res = SQLGetData(stmt, x + 1, SQL_CHAR, rowdata, sizeof(rowdata), &ind);
2288 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2289 SQLINTEGER nativeerror = 0;
2290 SQLSMALLINT diagbytes = 0;
2291 unsigned char state[10], diagnostic[256];
2292 SQLGetDiagRec(SQL_HANDLE_STMT, stmt, 1, state, &nativeerror, diagnostic, sizeof(diagnostic), &diagbytes);
2293 ast_log(LOG_WARNING, "SQL Get Data error: %s: %s!\n[%s]\n\n", state, diagnostic, sql);
2294 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2295 ast_odbc_release_obj(obj);
2296 goto yuck;
2298 if (strcasecmp(coltitle, "msgnum") && strcasecmp(coltitle, "dir") && f)
2299 fprintf(f, "%s=%s\n", coltitle, rowdata);
2302 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2303 ast_odbc_release_obj(obj);
2304 } else
2305 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2306 yuck:
2307 if (f)
2308 fclose(f);
2309 if (fd > -1)
2310 close(fd);
2311 return x - 1;
2314 static int last_message_index(struct ast_vm_user *vmu, char *dir)
2316 int x = 0;
2317 int res;
2318 SQLHSTMT stmt;
2319 char sql[PATH_MAX];
2320 char rowdata[20];
2321 char *argv[] = { dir };
2322 struct generic_prepare_struct gps = { .sql = sql, .argc = 1, .argv = argv };
2324 struct odbc_obj *obj;
2325 obj = ast_odbc_request_obj(odbc_database, 0);
2326 if (obj) {
2327 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=?",odbc_table);
2328 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2329 if (!stmt) {
2330 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2331 ast_odbc_release_obj(obj);
2332 goto yuck;
2334 res = SQLFetch(stmt);
2335 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2336 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2337 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2338 ast_odbc_release_obj(obj);
2339 goto yuck;
2341 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2342 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2343 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2344 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2345 ast_odbc_release_obj(obj);
2346 goto yuck;
2348 if (sscanf(rowdata, "%d", &x) != 1)
2349 ast_log(LOG_WARNING, "Failed to read message count!\n");
2350 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2351 ast_odbc_release_obj(obj);
2352 } else
2353 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2354 yuck:
2355 return x - 1;
2358 static int message_exists(char *dir, int msgnum)
2360 int x = 0;
2361 int res;
2362 SQLHSTMT stmt;
2363 char sql[PATH_MAX];
2364 char rowdata[20];
2365 char msgnums[20];
2366 char *argv[] = { dir, msgnums };
2367 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
2369 struct odbc_obj *obj;
2370 obj = ast_odbc_request_obj(odbc_database, 0);
2371 if (obj) {
2372 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
2373 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir=? AND msgnum=?",odbc_table);
2374 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2375 if (!stmt) {
2376 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2377 ast_odbc_release_obj(obj);
2378 goto yuck;
2380 res = SQLFetch(stmt);
2381 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2382 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
2383 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2384 ast_odbc_release_obj(obj);
2385 goto yuck;
2387 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
2388 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2389 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
2390 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2391 ast_odbc_release_obj(obj);
2392 goto yuck;
2394 if (sscanf(rowdata, "%d", &x) != 1)
2395 ast_log(LOG_WARNING, "Failed to read message count!\n");
2396 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2397 ast_odbc_release_obj(obj);
2398 } else
2399 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2400 yuck:
2401 return x;
2404 static int count_messages(struct ast_vm_user *vmu, char *dir)
2406 return last_message_index(vmu, dir) + 1;
2409 static void delete_file(char *sdir, int smsg)
2411 SQLHSTMT stmt;
2412 char sql[PATH_MAX];
2413 char msgnums[20];
2414 char *argv[] = { sdir, msgnums };
2415 struct generic_prepare_struct gps = { .sql = sql, .argc = 2, .argv = argv };
2417 struct odbc_obj *obj;
2418 obj = ast_odbc_request_obj(odbc_database, 0);
2419 if (obj) {
2420 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
2421 snprintf(sql, sizeof(sql), "DELETE FROM %s WHERE dir=? AND msgnum=?",odbc_table);
2422 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2423 if (!stmt)
2424 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2425 else
2426 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
2427 ast_odbc_release_obj(obj);
2428 } else
2429 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2430 return;
2433 static void copy_file(char *sdir, int smsg, char *ddir, int dmsg, char *dmailboxuser, char *dmailboxcontext)
2435 SQLHSTMT stmt;
2436 char sql[512];
2437 char msgnums[20];
2438 char msgnumd[20];
2439 struct odbc_obj *obj;
2440 char *argv[] = { ddir, msgnumd, dmailboxuser, dmailboxcontext, sdir, msgnums };
2441 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
2443 delete_file(ddir, dmsg);
2444 obj = ast_odbc_request_obj(odbc_database, 0);
2445 if (obj) {
2446 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
2447 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
2448 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);
2449 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2450 if (!stmt)
2451 ast_log(LOG_WARNING, "SQL Execute error!\n[%s] (You probably don't have MySQL 4.1 or later installed)\n\n", sql);
2452 else
2453 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2454 ast_odbc_release_obj(obj);
2455 } else
2456 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2457 return;
2460 struct insert_cb_struct {
2461 char *dir;
2462 char *msgnum;
2463 void *recording;
2464 size_t recordinglen;
2465 SQLLEN indlen;
2466 const char *context;
2467 const char *macrocontext;
2468 const char *callerid;
2469 const char *origtime;
2470 const char *duration;
2471 char *mailboxuser;
2472 char *mailboxcontext;
2473 const char *category;
2474 char *sql;
2477 static SQLHSTMT insert_cb(struct odbc_obj *obj, void *vd)
2479 struct insert_cb_struct *d = vd;
2480 int res;
2481 SQLHSTMT stmt;
2483 res = SQLAllocHandle(SQL_HANDLE_STMT, obj->con, &stmt);
2484 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2485 ast_log(LOG_WARNING, "SQL Alloc Handle failed!\n");
2486 return NULL;
2489 res = SQLPrepare(stmt, (unsigned char *)d->sql, SQL_NTS);
2490 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
2491 ast_log(LOG_WARNING, "SQL Prepare failed![%s]\n", d->sql);
2492 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2493 return NULL;
2496 SQLBindParameter(stmt, 1, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->dir), 0, (void *)d->dir, 0, NULL);
2497 SQLBindParameter(stmt, 2, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->msgnum), 0, (void *)d->msgnum, 0, NULL);
2498 SQLBindParameter(stmt, 3, SQL_PARAM_INPUT, SQL_C_BINARY, SQL_LONGVARBINARY, d->recordinglen, 0, (void *)d->recording, 0, &d->indlen);
2499 SQLBindParameter(stmt, 4, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->context), 0, (void *)d->context, 0, NULL);
2500 SQLBindParameter(stmt, 5, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->macrocontext), 0, (void *)d->macrocontext, 0, NULL);
2501 SQLBindParameter(stmt, 6, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->callerid), 0, (void *)d->callerid, 0, NULL);
2502 SQLBindParameter(stmt, 7, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->origtime), 0, (void *)d->origtime, 0, NULL);
2503 SQLBindParameter(stmt, 8, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->duration), 0, (void *)d->duration, 0, NULL);
2504 SQLBindParameter(stmt, 9, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->mailboxuser), 0, (void *)d->mailboxuser, 0, NULL);
2505 SQLBindParameter(stmt, 10, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->mailboxcontext), 0, (void *)d->mailboxcontext, 0, NULL);
2506 if (!ast_strlen_zero(d->category)) {
2507 SQLBindParameter(stmt, 11, SQL_PARAM_INPUT, SQL_C_CHAR, SQL_CHAR, strlen(d->category), 0, (void *)d->category, 0, NULL);
2510 return stmt;
2513 static int store_file(char *dir, char *mailboxuser, char *mailboxcontext, int msgnum)
2515 int x = 0;
2516 int fd = -1;
2517 void *fdm = MAP_FAILED;
2518 size_t fdlen = -1;
2519 SQLHSTMT stmt;
2520 char sql[PATH_MAX];
2521 char msgnums[20];
2522 char fn[PATH_MAX];
2523 char full_fn[PATH_MAX];
2524 char fmt[80]="";
2525 char *c;
2526 struct insert_cb_struct d = {
2527 .dir = dir,
2528 .msgnum = msgnums,
2529 .context = "",
2530 .macrocontext = "",
2531 .callerid = "",
2532 .origtime = "",
2533 .duration = "",
2534 .mailboxuser = mailboxuser,
2535 .mailboxcontext = mailboxcontext,
2536 .category = "",
2537 .sql = sql
2539 struct ast_config *cfg=NULL;
2540 struct odbc_obj *obj;
2542 delete_file(dir, msgnum);
2543 obj = ast_odbc_request_obj(odbc_database, 0);
2544 if (obj) {
2545 ast_copy_string(fmt, vmfmts, sizeof(fmt));
2546 c = strchr(fmt, '|');
2547 if (c)
2548 *c = '\0';
2549 if (!strcasecmp(fmt, "wav49"))
2550 strcpy(fmt, "WAV");
2551 snprintf(msgnums, sizeof(msgnums),"%d", msgnum);
2552 if (msgnum > -1)
2553 make_file(fn, sizeof(fn), dir, msgnum);
2554 else
2555 ast_copy_string(fn, dir, sizeof(fn));
2556 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
2557 cfg = ast_config_load(full_fn);
2558 snprintf(full_fn, sizeof(full_fn), "%s.%s", fn, fmt);
2559 fd = open(full_fn, O_RDWR);
2560 if (fd < 0) {
2561 ast_log(LOG_WARNING, "Open of sound file '%s' failed: %s\n", full_fn, strerror(errno));
2562 ast_odbc_release_obj(obj);
2563 goto yuck;
2565 if (cfg) {
2566 d.context = ast_variable_retrieve(cfg, "message", "context");
2567 if (!d.context) d.context = "";
2568 d.macrocontext = ast_variable_retrieve(cfg, "message", "macrocontext");
2569 if (!d.macrocontext) d.macrocontext = "";
2570 d.callerid = ast_variable_retrieve(cfg, "message", "callerid");
2571 if (!d.callerid) d.callerid = "";
2572 d.origtime = ast_variable_retrieve(cfg, "message", "origtime");
2573 if (!d.origtime) d.origtime = "";
2574 d.duration = ast_variable_retrieve(cfg, "message", "duration");
2575 if (!d.duration) d.duration = "";
2576 d.category = ast_variable_retrieve(cfg, "message", "category");
2577 if (!d.category) d.category = "";
2579 fdlen = lseek(fd, 0, SEEK_END);
2580 lseek(fd, 0, SEEK_SET);
2581 printf("Length is %zd\n", fdlen);
2582 fdm = mmap(NULL, fdlen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
2583 if (fdm == MAP_FAILED) {
2584 ast_log(LOG_WARNING, "Memory map failed!\n");
2585 ast_odbc_release_obj(obj);
2586 goto yuck;
2588 d.recording = fdm;
2589 d.recordinglen = d.indlen = fdlen; /* SQL_LEN_DATA_AT_EXEC(fdlen); */
2590 if (!ast_strlen_zero(d.category))
2591 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext,category) VALUES (?,?,?,?,?,?,?,?,?,?,?)",odbc_table);
2592 else
2593 snprintf(sql, sizeof(sql), "INSERT INTO %s (dir,msgnum,recording,context,macrocontext,callerid,origtime,duration,mailboxuser,mailboxcontext) VALUES (?,?, ? , ?,?,?,?,?,?,?)",odbc_table);
2594 stmt = ast_odbc_prepare_and_execute(obj, insert_cb, &d);
2595 if (stmt) {
2596 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2598 ast_odbc_release_obj(obj);
2599 } else
2600 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2601 yuck:
2602 if (cfg)
2603 ast_config_destroy(cfg);
2604 if (fdm != MAP_FAILED)
2605 munmap(fdm, fdlen);
2606 if (fd > -1)
2607 close(fd);
2608 return x;
2611 static void rename_file(char *sdir, int smsg, char *mailboxuser, char *mailboxcontext, char *ddir, int dmsg)
2613 SQLHSTMT stmt;
2614 char sql[PATH_MAX];
2615 char msgnums[20];
2616 char msgnumd[20];
2617 struct odbc_obj *obj;
2618 char *argv[] = { ddir, msgnumd, mailboxuser, mailboxcontext, sdir, msgnums };
2619 struct generic_prepare_struct gps = { .sql = sql, .argc = 6, .argv = argv };
2621 delete_file(ddir, dmsg);
2622 obj = ast_odbc_request_obj(odbc_database, 0);
2623 if (obj) {
2624 snprintf(msgnums, sizeof(msgnums), "%d", smsg);
2625 snprintf(msgnumd, sizeof(msgnumd), "%d", dmsg);
2626 snprintf(sql, sizeof(sql), "UPDATE %s SET dir=?, msgnum=?, mailboxuser=?, mailboxcontext=? WHERE dir=? AND msgnum=?",odbc_table);
2627 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
2628 if (!stmt)
2629 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
2630 else
2631 SQLFreeHandle(SQL_HANDLE_STMT, stmt);
2632 ast_odbc_release_obj(obj);
2633 } else
2634 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
2635 return;
2638 #else
2639 #ifndef IMAP_STORAGE
2640 static int count_messages(struct ast_vm_user *vmu, char *dir)
2642 /* Find all .txt files - even if they are not in sequence from 0000 */
2644 int vmcount = 0;
2645 DIR *vmdir = NULL;
2646 struct dirent *vment = NULL;
2648 if (vm_lock_path(dir))
2649 return ERROR_LOCK_PATH;
2651 if ((vmdir = opendir(dir))) {
2652 while ((vment = readdir(vmdir))) {
2653 if (strlen(vment->d_name) > 7 && !strncmp(vment->d_name + 7, ".txt", 4))
2654 vmcount++;
2656 closedir(vmdir);
2658 ast_unlock_path(dir);
2660 return vmcount;
2663 static void rename_file(char *sfn, char *dfn)
2665 char stxt[PATH_MAX];
2666 char dtxt[PATH_MAX];
2667 ast_filerename(sfn,dfn,NULL);
2668 snprintf(stxt, sizeof(stxt), "%s.txt", sfn);
2669 snprintf(dtxt, sizeof(dtxt), "%s.txt", dfn);
2670 rename(stxt, dtxt);
2672 #endif
2675 * A negative return value indicates an error.
2677 #if (!defined(IMAP_STORAGE) && !defined(ODBC_STORAGE))
2678 static int last_message_index(struct ast_vm_user *vmu, char *dir)
2680 int x;
2681 char fn[PATH_MAX];
2683 if (vm_lock_path(dir))
2684 return ERROR_LOCK_PATH;
2686 for (x = 0; x < vmu->maxmsg; x++) {
2687 make_file(fn, sizeof(fn), dir, x);
2688 if (ast_fileexists(fn, NULL, NULL) < 1)
2689 break;
2691 ast_unlock_path(dir);
2693 return x - 1;
2695 #endif
2696 #endif
2697 #if (defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
2698 static int remove_file(char *dir, int msgnum)
2700 char fn[PATH_MAX];
2701 char full_fn[PATH_MAX];
2702 char msgnums[80];
2704 if (msgnum > -1) {
2705 snprintf(msgnums, sizeof(msgnums), "%d", msgnum);
2706 make_file(fn, sizeof(fn), dir, msgnum);
2707 } else
2708 ast_copy_string(fn, dir, sizeof(fn));
2709 ast_filedelete(fn, NULL);
2710 snprintf(full_fn, sizeof(full_fn), "%s.txt", fn);
2711 unlink(full_fn);
2712 return 0;
2714 #endif
2716 static int copy(char *infile, char *outfile)
2718 int ifd;
2719 int ofd;
2720 int res;
2721 int len;
2722 char buf[4096];
2724 #ifdef HARDLINK_WHEN_POSSIBLE
2725 /* Hard link if possible; saves disk space & is faster */
2726 if (link(infile, outfile)) {
2727 #endif
2728 if ((ifd = open(infile, O_RDONLY)) < 0) {
2729 ast_log(LOG_WARNING, "Unable to open %s in read-only mode: %s\n", infile, strerror(errno));
2730 return -1;
2732 if ((ofd = open(outfile, O_WRONLY | O_TRUNC | O_CREAT, VOICEMAIL_FILE_MODE)) < 0) {
2733 ast_log(LOG_WARNING, "Unable to open %s in write-only mode: %s\n", outfile, strerror(errno));
2734 close(ifd);
2735 return -1;
2737 do {
2738 len = read(ifd, buf, sizeof(buf));
2739 if (len < 0) {
2740 ast_log(LOG_WARNING, "Read failed on %s: %s\n", infile, strerror(errno));
2741 close(ifd);
2742 close(ofd);
2743 unlink(outfile);
2745 if (len) {
2746 res = write(ofd, buf, len);
2747 if (errno == ENOMEM || errno == ENOSPC || res != len) {
2748 ast_log(LOG_WARNING, "Write failed on %s (%d of %d): %s\n", outfile, res, len, strerror(errno));
2749 close(ifd);
2750 close(ofd);
2751 unlink(outfile);
2754 } while (len);
2755 close(ifd);
2756 close(ofd);
2757 return 0;
2758 #ifdef HARDLINK_WHEN_POSSIBLE
2759 } else {
2760 /* Hard link succeeded */
2761 return 0;
2763 #endif
2766 static void copy_plain_file(char *frompath, char *topath)
2768 char frompath2[PATH_MAX], topath2[PATH_MAX];
2769 ast_filecopy(frompath, topath, NULL);
2770 snprintf(frompath2, sizeof(frompath2), "%s.txt", frompath);
2771 snprintf(topath2, sizeof(topath2), "%s.txt", topath);
2772 copy(frompath2, topath2);
2775 static int vm_delete(char *file)
2777 char *txt;
2778 int txtsize = 0;
2780 txtsize = (strlen(file) + 5)*sizeof(char);
2781 txt = alloca(txtsize);
2782 /* Sprintf here would safe because we alloca'd exactly the right length,
2783 * but trying to eliminate all sprintf's anyhow
2785 snprintf(txt, txtsize, "%s.txt", file);
2786 unlink(txt);
2787 return ast_filedelete(file, NULL);
2790 static int inbuf(struct baseio *bio, FILE *fi)
2792 int l;
2794 if (bio->ateof)
2795 return 0;
2797 if ((l = fread(bio->iobuf,1,BASEMAXINLINE,fi)) <= 0) {
2798 if (ferror(fi))
2799 return -1;
2801 bio->ateof = 1;
2802 return 0;
2805 bio->iolen= l;
2806 bio->iocp= 0;
2808 return 1;
2811 static int inchar(struct baseio *bio, FILE *fi)
2813 if (bio->iocp>=bio->iolen) {
2814 if (!inbuf(bio, fi))
2815 return EOF;
2818 return bio->iobuf[bio->iocp++];
2821 static int ochar(struct baseio *bio, int c, FILE *so)
2823 if (bio->linelength>=BASELINELEN) {
2824 if (fputs(eol,so)==EOF)
2825 return -1;
2827 bio->linelength= 0;
2830 if (putc(((unsigned char)c),so)==EOF)
2831 return -1;
2833 bio->linelength++;
2835 return 1;
2838 static int base_encode(char *filename, FILE *so)
2840 unsigned char dtable[BASEMAXINLINE];
2841 int i,hiteof= 0;
2842 FILE *fi;
2843 struct baseio bio;
2845 memset(&bio, 0, sizeof(bio));
2846 bio.iocp = BASEMAXINLINE;
2848 if (!(fi = fopen(filename, "rb"))) {
2849 ast_log(LOG_WARNING, "Failed to open file: %s: %s\n", filename, strerror(errno));
2850 return -1;
2853 for (i= 0;i<9;i++) {
2854 dtable[i]= 'A'+i;
2855 dtable[i+9]= 'J'+i;
2856 dtable[26+i]= 'a'+i;
2857 dtable[26+i+9]= 'j'+i;
2859 for (i= 0;i<8;i++) {
2860 dtable[i+18]= 'S'+i;
2861 dtable[26+i+18]= 's'+i;
2863 for (i= 0;i<10;i++) {
2864 dtable[52+i]= '0'+i;
2866 dtable[62]= '+';
2867 dtable[63]= '/';
2869 while (!hiteof){
2870 unsigned char igroup[3],ogroup[4];
2871 int c,n;
2873 igroup[0]= igroup[1]= igroup[2]= 0;
2875 for (n= 0;n<3;n++) {
2876 if ((c = inchar(&bio, fi)) == EOF) {
2877 hiteof= 1;
2878 break;
2881 igroup[n]= (unsigned char)c;
2884 if (n> 0) {
2885 ogroup[0]= dtable[igroup[0]>>2];
2886 ogroup[1]= dtable[((igroup[0]&3)<<4)|(igroup[1]>>4)];
2887 ogroup[2]= dtable[((igroup[1]&0xF)<<2)|(igroup[2]>>6)];
2888 ogroup[3]= dtable[igroup[2]&0x3F];
2890 if (n<3) {
2891 ogroup[3]= '=';
2893 if (n<2)
2894 ogroup[2]= '=';
2897 for (i= 0;i<4;i++)
2898 ochar(&bio, ogroup[i], so);
2902 fclose(fi);
2904 if (fputs(eol,so)==EOF)
2905 return 0;
2907 return 1;
2910 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)
2912 char callerid[256];
2913 /* Prepare variables for substition in email body and subject */
2914 pbx_builtin_setvar_helper(ast, "VM_NAME", vmu->fullname);
2915 pbx_builtin_setvar_helper(ast, "VM_DUR", dur);
2916 snprintf(passdata, passdatasize, "%d", msgnum);
2917 pbx_builtin_setvar_helper(ast, "VM_MSGNUM", passdata);
2918 pbx_builtin_setvar_helper(ast, "VM_CONTEXT", context);
2919 pbx_builtin_setvar_helper(ast, "VM_MAILBOX", mailbox);
2920 pbx_builtin_setvar_helper(ast, "VM_CALLERID", ast_callerid_merge(callerid, sizeof(callerid), cidname, cidnum, "Unknown Caller"));
2921 pbx_builtin_setvar_helper(ast, "VM_CIDNAME", (cidname ? cidname : "an unknown caller"));
2922 pbx_builtin_setvar_helper(ast, "VM_CIDNUM", (cidnum ? cidnum : "an unknown caller"));
2923 pbx_builtin_setvar_helper(ast, "VM_DATE", date);
2924 pbx_builtin_setvar_helper(ast, "VM_CATEGORY", category ? ast_strdupa(category) : "no category");
2927 static char *quote(const char *from, char *to, size_t len)
2929 char *ptr = to;
2930 *ptr++ = '"';
2931 for (; ptr < to + len - 1; from++) {
2932 if (*from == '"')
2933 *ptr++ = '\\';
2934 else if (*from == '\0')
2935 break;
2936 *ptr++ = *from;
2938 if (ptr < to + len - 1)
2939 *ptr++ = '"';
2940 *ptr = '\0';
2941 return to;
2944 * fill in *tm for current time according to the proper timezone, if any.
2945 * Return tm so it can be used as a function argument.
2947 static const struct tm *vmu_tm(const struct ast_vm_user *vmu, struct tm *tm)
2949 const struct vm_zone *z = NULL;
2950 time_t t = time(NULL);
2952 /* Does this user have a timezone specified? */
2953 if (!ast_strlen_zero(vmu->zonetag)) {
2954 /* Find the zone in the list */
2955 AST_LIST_LOCK(&zones);
2956 AST_LIST_TRAVERSE(&zones, z, list) {
2957 if (!strcmp(z->name, vmu->zonetag))
2958 break;
2960 AST_LIST_UNLOCK(&zones);
2962 ast_localtime(&t, tm, z ? z->timezone : NULL);
2963 return tm;
2966 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)
2968 char date[256];
2969 char host[MAXHOSTNAMELEN] = "";
2970 char who[256];
2971 char bound[256];
2972 char fname[256];
2973 char dur[256];
2974 char tmpcmd[256];
2975 char enc_cidnum[256] = "", enc_cidname[256] = "";
2976 struct tm tm;
2977 char *passdata2;
2978 size_t len_passdata;
2979 #ifdef IMAP_STORAGE
2980 #define ENDL "\r\n"
2981 #else
2982 #define ENDL "\n"
2983 #endif
2985 if (cidnum) {
2986 strip_control(cidnum, enc_cidnum, sizeof(enc_cidnum));
2988 if (cidname) {
2989 strip_control(cidname, enc_cidname, sizeof(enc_cidname));
2991 gethostname(host, sizeof(host) - 1);
2992 if (strchr(srcemail, '@'))
2993 ast_copy_string(who, srcemail, sizeof(who));
2994 else {
2995 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
2997 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
2998 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
2999 fprintf(p, "Date: %s" ENDL, date);
3001 /* Set date format for voicemail mail */
3002 strftime(date, sizeof(date), emaildateformat, &tm);
3004 if (*fromstring) {
3005 struct ast_channel *ast;
3006 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
3007 char *passdata;
3008 int vmlen = strlen(fromstring)*3 + 200;
3009 if ((passdata = alloca(vmlen))) {
3010 memset(passdata, 0, vmlen);
3011 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, enc_cidnum, enc_cidname, dur, date, passdata, vmlen, category);
3012 pbx_substitute_variables_helper(ast, fromstring, passdata, vmlen);
3013 len_passdata = strlen(passdata) * 2 + 3;
3014 passdata2 = alloca(len_passdata);
3015 fprintf(p, "From: %s <%s>" ENDL, quote(passdata, passdata2, len_passdata), who);
3016 } else
3017 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
3018 ast_channel_free(ast);
3019 } else
3020 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
3021 } else
3022 fprintf(p, "From: Asterisk PBX <%s>" ENDL, who);
3023 len_passdata = strlen(vmu->fullname) * 2 + 3;
3024 passdata2 = alloca(len_passdata);
3025 fprintf(p, "To: %s <%s>" ENDL, quote(vmu->fullname, passdata2, len_passdata), vmu->email);
3026 if (emailsubject) {
3027 struct ast_channel *ast;
3028 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
3029 char *passdata;
3030 int vmlen = strlen(emailsubject)*3 + 200;
3031 if ((passdata = alloca(vmlen))) {
3032 memset(passdata, 0, vmlen);
3033 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
3034 pbx_substitute_variables_helper(ast, emailsubject, passdata, vmlen);
3035 fprintf(p, "Subject: %s" ENDL, passdata);
3036 } else {
3037 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
3039 ast_channel_free(ast);
3040 } else {
3041 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
3043 } else if (*emailtitle) {
3044 fprintf(p, emailtitle, msgnum + 1, mailbox) ;
3045 fprintf(p, ENDL) ;
3046 } else if (ast_test_flag((&globalflags), VM_PBXSKIP)) {
3047 fprintf(p, "Subject: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
3048 } else {
3049 fprintf(p, "Subject: [PBX]: New message %d in mailbox %s" ENDL, msgnum + 1, mailbox);
3051 fprintf(p, "Message-ID: <Asterisk-%d-%d-%s-%d@%s>" ENDL, msgnum + 1, (unsigned int)ast_random(), mailbox, (int)getpid(), host);
3052 if (imap) {
3053 /* additional information needed for IMAP searching */
3054 fprintf(p, "X-Asterisk-VM-Message-Num: %d" ENDL, msgnum + 1);
3055 /* fprintf(p, "X-Asterisk-VM-Orig-Mailbox: %s" ENDL, ext); */
3056 fprintf(p, "X-Asterisk-VM-Server-Name: %s" ENDL, fromstring);
3057 fprintf(p, "X-Asterisk-VM-Context: %s" ENDL, context);
3058 fprintf(p, "X-Asterisk-VM-Extension: %s" ENDL, mailbox);
3059 fprintf(p, "X-Asterisk-VM-Priority: %d" ENDL, chan->priority);
3060 fprintf(p, "X-Asterisk-VM-Caller-channel: %s" ENDL, chan->name);
3061 fprintf(p, "X-Asterisk-VM-Caller-ID-Num: %s" ENDL, enc_cidnum);
3062 fprintf(p, "X-Asterisk-VM-Caller-ID-Name: %s" ENDL, enc_cidname);
3063 fprintf(p, "X-Asterisk-VM-Duration: %d" ENDL, duration);
3064 if (!ast_strlen_zero(category)) {
3065 fprintf(p, "X-Asterisk-VM-Category: %s" ENDL, category);
3067 fprintf(p, "X-Asterisk-VM-Orig-date: %s" ENDL, date);
3068 fprintf(p, "X-Asterisk-VM-Orig-time: %ld" ENDL, (long)time(NULL));
3070 if (!ast_strlen_zero(cidnum)) {
3071 fprintf(p, "X-Asterisk-CallerID: %s" ENDL, enc_cidnum);
3073 if (!ast_strlen_zero(cidname)) {
3074 fprintf(p, "X-Asterisk-CallerIDName: %s" ENDL, enc_cidname);
3076 fprintf(p, "MIME-Version: 1.0" ENDL);
3077 if (attach_user_voicemail) {
3078 /* Something unique. */
3079 snprintf(bound, sizeof(bound), "----voicemail_%d%s%d%d", msgnum + 1, mailbox, (int)getpid(), (unsigned int)ast_random());
3081 fprintf(p, "Content-Type: multipart/mixed; boundary=\"%s\"" ENDL, bound);
3082 fprintf(p, ENDL ENDL "This is a multi-part message in MIME format." ENDL ENDL);
3083 fprintf(p, "--%s" ENDL, bound);
3085 fprintf(p, "Content-Type: text/plain; charset=%s" ENDL "Content-Transfer-Encoding: 8bit" ENDL ENDL, charset);
3086 if (emailbody) {
3087 struct ast_channel *ast;
3088 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
3089 char *passdata;
3090 int vmlen = strlen(emailbody)*3 + 200;
3091 if ((passdata = alloca(vmlen))) {
3092 memset(passdata, 0, vmlen);
3093 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
3094 pbx_substitute_variables_helper(ast, emailbody, passdata, vmlen);
3095 fprintf(p, "%s" ENDL, passdata);
3096 } else
3097 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
3098 ast_channel_free(ast);
3099 } else
3100 ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
3101 } else {
3102 fprintf(p, "Dear %s:" ENDL ENDL "\tJust wanted to let you know you were just left a %s long message (number %d)" ENDL
3104 "in mailbox %s from %s, on %s so you might" ENDL
3105 "want to check it when you get a chance. Thanks!" ENDL ENDL "\t\t\t\t--Asterisk" ENDL ENDL, vmu->fullname,
3106 dur, msgnum + 1, mailbox, (cidname ? cidname : (cidnum ? cidnum : "an unknown caller")), date);
3108 if (attach_user_voicemail) {
3109 /* Eww. We want formats to tell us their own MIME type */
3110 char *ctype = (!strcasecmp(format, "ogg")) ? "application/" : "audio/x-";
3111 char tmpdir[256], newtmp[256];
3112 int tmpfd = -1;
3114 if (vmu->volgain < -.001 || vmu->volgain > .001) {
3115 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, vmu->mailbox, "tmp");
3116 snprintf(newtmp, sizeof(newtmp), "%s/XXXXXX", tmpdir);
3117 tmpfd = mkstemp(newtmp);
3118 chmod(newtmp, VOICEMAIL_FILE_MODE & ~my_umask);
3119 if (option_debug > 2)
3120 ast_log(LOG_DEBUG, "newtmp: %s\n", newtmp);
3121 if (tmpfd > -1) {
3122 int soxstatus;
3123 snprintf(tmpcmd, sizeof(tmpcmd), "sox -v %.4f %s.%s %s.%s", vmu->volgain, attach, format, newtmp, format);
3124 if ((soxstatus = ast_safe_system(tmpcmd)) == 0) {
3125 attach = newtmp;
3126 if (option_debug > 2) {
3127 ast_log(LOG_DEBUG, "VOLGAIN: Stored at: %s.%s - Level: %.4f - Mailbox: %s\n", attach, format, vmu->volgain, mailbox);
3129 } else {
3130 ast_log(LOG_WARNING, "Sox failed to reencode %s.%s: %s (have you installed support for all sox file formats?)\n", attach, format,
3131 soxstatus == 1 ? "Problem with command line options" : "An error occurred during file processing");
3132 ast_log(LOG_WARNING, "Voicemail attachment will have no volume gain.\n");
3136 fprintf(p, "--%s" ENDL, bound);
3137 fprintf(p, "Content-Type: %s%s; name=\"msg%04d.%s\"" ENDL, ctype, format, msgnum + 1, format);
3138 fprintf(p, "Content-Transfer-Encoding: base64" ENDL);
3139 fprintf(p, "Content-Description: Voicemail sound attachment." ENDL);
3140 fprintf(p, "Content-Disposition: attachment; filename=\"msg%04d.%s\"" ENDL ENDL, msgnum + 1, format);
3141 snprintf(fname, sizeof(fname), "%s.%s", attach, format);
3142 base_encode(fname, p);
3143 fprintf(p, ENDL "--%s--" ENDL "." ENDL, bound);
3144 if (tmpfd > -1) {
3145 unlink(fname);
3146 close(tmpfd);
3147 unlink(newtmp);
3150 #undef ENDL
3152 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)
3154 FILE *p=NULL;
3155 char tmp[80] = "/tmp/astmail-XXXXXX";
3156 char tmp2[256];
3158 if (vmu && ast_strlen_zero(vmu->email)) {
3159 ast_log(LOG_WARNING, "E-mail address missing for mailbox [%s]. E-mail will not be sent.\n", vmu->mailbox);
3160 return(0);
3162 if (!strcmp(format, "wav49"))
3163 format = "WAV";
3164 if (option_debug > 2)
3165 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));
3166 /* Make a temporary file instead of piping directly to sendmail, in case the mail
3167 command hangs */
3168 if ((p = vm_mkftemp(tmp)) == NULL) {
3169 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
3170 return -1;
3171 } else {
3172 make_email_file(p, srcemail, vmu, msgnum, context, mailbox, cidnum, cidname, attach, format, duration, attach_user_voicemail, chan, category, 0);
3173 fclose(p);
3174 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
3175 ast_safe_system(tmp2);
3176 if (option_debug > 2)
3177 ast_log(LOG_DEBUG, "Sent mail to %s with command '%s'\n", vmu->email, mailcmd);
3179 return 0;
3182 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)
3184 char date[256];
3185 char host[MAXHOSTNAMELEN] = "";
3186 char who[256];
3187 char dur[PATH_MAX];
3188 char tmp[80] = "/tmp/astmail-XXXXXX";
3189 char tmp2[PATH_MAX];
3190 struct tm tm;
3191 FILE *p;
3193 if ((p = vm_mkftemp(tmp)) == NULL) {
3194 ast_log(LOG_WARNING, "Unable to launch '%s' (can't create temporary file)\n", mailcmd);
3195 return -1;
3196 } else {
3197 gethostname(host, sizeof(host)-1);
3198 if (strchr(srcemail, '@'))
3199 ast_copy_string(who, srcemail, sizeof(who));
3200 else {
3201 snprintf(who, sizeof(who), "%s@%s", srcemail, host);
3203 snprintf(dur, sizeof(dur), "%d:%02d", duration / 60, duration % 60);
3204 strftime(date, sizeof(date), "%a, %d %b %Y %H:%M:%S %z", vmu_tm(vmu, &tm));
3205 fprintf(p, "Date: %s\n", date);
3207 if (*pagerfromstring) {
3208 struct ast_channel *ast;
3209 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
3210 char *passdata;
3211 int vmlen = strlen(fromstring)*3 + 200;
3212 if ((passdata = alloca(vmlen))) {
3213 memset(passdata, 0, vmlen);
3214 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
3215 pbx_substitute_variables_helper(ast, pagerfromstring, passdata, vmlen);
3216 fprintf(p, "From: %s <%s>\n", passdata, who);
3217 } else
3218 ast_log(LOG_WARNING, "Cannot allocate workspace for variable substitution\n");
3219 ast_channel_free(ast);
3220 } else ast_log(LOG_WARNING, "Cannot allocate the channel for variables substitution\n");
3221 } else
3222 fprintf(p, "From: Asterisk PBX <%s>\n", who);
3223 fprintf(p, "To: %s\n", pager);
3224 if (pagersubject) {
3225 struct ast_channel *ast;
3226 if ((ast = ast_channel_alloc(0, AST_STATE_DOWN, 0, 0, "", "", "", 0, 0))) {
3227 char *passdata;
3228 int vmlen = strlen(pagersubject) * 3 + 200;
3229 if ((passdata = alloca(vmlen))) {
3230 memset(passdata, 0, vmlen);
3231 prep_email_sub_vars(ast, vmu, msgnum + 1, context, mailbox, cidnum, cidname, dur, date, passdata, vmlen, category);
3232 pbx_substitute_variables_helper(ast, pagersubject, passdata, vmlen);
3233 fprintf(p, "Subject: %s\n\n", passdata);
3234 } else 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, "Subject: New VM\n\n");
3239 strftime(date, sizeof(date), "%A, %B %d, %Y at %r", &tm);
3240 if (pagerbody) {
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(pagerbody)*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, pagerbody, passdata, vmlen);
3249 fprintf(p, "%s\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, "New %s long msg in box %s\n"
3255 "from %s, on %s", dur, mailbox, (cidname ? cidname : (cidnum ? cidnum : "unknown")), date);
3257 fclose(p);
3258 snprintf(tmp2, sizeof(tmp2), "( %s < %s ; rm -f %s ) &", mailcmd, tmp, tmp);
3259 ast_safe_system(tmp2);
3260 if (option_debug > 2)
3261 ast_log(LOG_DEBUG, "Sent page to %s with command '%s'\n", pager, mailcmd);
3263 return 0;
3266 static int get_date(char *s, int len)
3268 struct tm tm;
3269 time_t t;
3271 time(&t);
3273 ast_localtime(&t, &tm, NULL);
3275 return strftime(s, len, "%a %b %e %r %Z %Y", &tm);
3278 static int play_greeting(struct ast_channel *chan, struct ast_vm_user *vmu, char *filename, char *ecodes)
3280 int res = -2;
3282 #ifdef ODBC_STORAGE
3283 int success =
3284 #endif
3285 RETRIEVE(filename, -1, vmu);
3286 if (ast_fileexists(filename, NULL, NULL) > 0) {
3287 res = ast_streamfile(chan, filename, chan->language);
3288 if (res > -1)
3289 res = ast_waitstream(chan, ecodes);
3290 #ifdef ODBC_STORAGE
3291 if (success == -1) {
3292 /* We couldn't retrieve the file from the database, but we found it on the file system. Let's put it in the database. */
3293 if (option_debug)
3294 ast_log(LOG_DEBUG, "Greeting not retrieved from database, but found in file storage. Inserting into database\n");
3295 store_file(filename, vmu->mailbox, vmu->context, -1);
3297 #endif
3299 DISPOSE(filename, -1);
3301 return res;
3304 static int invent_message(struct ast_channel *chan, struct ast_vm_user *vmu, char *ext, int busy, char *ecodes)
3306 int res;
3307 char fn[PATH_MAX];
3308 char dest[PATH_MAX];
3310 snprintf(fn, sizeof(fn), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, ext);
3312 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "greet"))) {
3313 ast_log(LOG_WARNING, "Failed to make directory(%s)\n", fn);
3314 return -1;
3317 res = play_greeting(chan, vmu, fn, ecodes);
3318 if (res == -2) {
3319 /* File did not exist */
3320 res = ast_stream_and_wait(chan, "vm-theperson", chan->language, ecodes);
3321 if (res)
3322 return res;
3323 res = ast_say_digit_str(chan, ext, ecodes, chan->language);
3326 if (res)
3327 return res;
3329 res = ast_stream_and_wait(chan, busy ? "vm-isonphone" : "vm-isunavail", chan->language, ecodes);
3330 return res;
3333 static void free_zone(struct vm_zone *z)
3335 free(z);
3338 #ifdef ODBC_STORAGE
3339 /*! XXX \todo Fix this function to support multiple mailboxes in the intput string */
3340 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
3342 int x = -1;
3343 int res;
3344 SQLHSTMT stmt;
3345 char sql[PATH_MAX];
3346 char rowdata[20];
3347 char tmp[PATH_MAX] = "";
3348 struct odbc_obj *obj;
3349 char *context;
3350 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
3352 if (newmsgs)
3353 *newmsgs = 0;
3354 if (oldmsgs)
3355 *oldmsgs = 0;
3357 /* If no mailbox, return immediately */
3358 if (ast_strlen_zero(mailbox))
3359 return 0;
3361 ast_copy_string(tmp, mailbox, sizeof(tmp));
3363 context = strchr(tmp, '@');
3364 if (context) {
3365 *context = '\0';
3366 context++;
3367 } else
3368 context = "default";
3370 obj = ast_odbc_request_obj(odbc_database, 0);
3371 if (obj) {
3372 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "INBOX");
3373 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3374 if (!stmt) {
3375 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3376 ast_odbc_release_obj(obj);
3377 goto yuck;
3379 res = SQLFetch(stmt);
3380 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3381 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3382 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3383 ast_odbc_release_obj(obj);
3384 goto yuck;
3386 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3387 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3388 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3389 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3390 ast_odbc_release_obj(obj);
3391 goto yuck;
3393 *newmsgs = atoi(rowdata);
3394 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3396 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, tmp, "Old");
3397 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3398 if (!stmt) {
3399 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3400 ast_odbc_release_obj(obj);
3401 goto yuck;
3403 res = SQLFetch(stmt);
3404 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3405 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3406 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3407 ast_odbc_release_obj(obj);
3408 goto yuck;
3410 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3411 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3412 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3413 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3414 ast_odbc_release_obj(obj);
3415 goto yuck;
3417 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3418 ast_odbc_release_obj(obj);
3419 *oldmsgs = atoi(rowdata);
3420 x = 0;
3421 } else
3422 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3424 yuck:
3425 return x;
3428 static int messagecount(const char *context, const char *mailbox, const char *folder)
3430 struct odbc_obj *obj = NULL;
3431 int nummsgs = 0;
3432 int res;
3433 SQLHSTMT stmt = NULL;
3434 char sql[PATH_MAX];
3435 char rowdata[20];
3436 struct generic_prepare_struct gps = { .sql = sql, .argc = 0 };
3437 if (!folder)
3438 folder = "INBOX";
3439 /* If no mailbox, return immediately */
3440 if (ast_strlen_zero(mailbox))
3441 return 0;
3443 obj = ast_odbc_request_obj(odbc_database, 0);
3444 if (obj) {
3445 snprintf(sql, sizeof(sql), "SELECT COUNT(*) FROM %s WHERE dir = '%s%s/%s/%s'", odbc_table, VM_SPOOL_DIR, context, mailbox, folder);
3446 stmt = ast_odbc_prepare_and_execute(obj, generic_prepare, &gps);
3447 if (!stmt) {
3448 ast_log(LOG_WARNING, "SQL Execute error!\n[%s]\n\n", sql);
3449 goto yuck;
3451 res = SQLFetch(stmt);
3452 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3453 ast_log(LOG_WARNING, "SQL Fetch error!\n[%s]\n\n", sql);
3454 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3455 goto yuck;
3457 res = SQLGetData(stmt, 1, SQL_CHAR, rowdata, sizeof(rowdata), NULL);
3458 if ((res != SQL_SUCCESS) && (res != SQL_SUCCESS_WITH_INFO)) {
3459 ast_log(LOG_WARNING, "SQL Get Data error!\n[%s]\n\n", sql);
3460 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3461 goto yuck;
3463 nummsgs = atoi(rowdata);
3464 SQLFreeHandle (SQL_HANDLE_STMT, stmt);
3465 } else
3466 ast_log(LOG_WARNING, "Failed to obtain database object for '%s'!\n", odbc_database);
3468 yuck:
3469 if (obj)
3470 ast_odbc_release_obj(obj);
3471 return nummsgs;
3474 static int has_voicemail(const char *mailbox, const char *folder)
3476 char tmp[256], *tmp2 = tmp, *mbox, *context;
3477 ast_copy_string(tmp, mailbox, sizeof(tmp));
3478 while ((context = mbox = strsep(&tmp2, ","))) {
3479 strsep(&context, "@");
3480 if (ast_strlen_zero(context))
3481 context = "default";
3482 if (messagecount(context, mbox, folder))
3483 return 1;
3485 return 0;
3487 #endif
3488 #ifndef IMAP_STORAGE
3489 /* copy message only used by file storage */
3490 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)
3492 char fromdir[PATH_MAX], todir[PATH_MAX], frompath[PATH_MAX], topath[PATH_MAX];
3493 const char *frombox = mbox(imbox);
3494 int recipmsgnum;
3496 ast_log(LOG_NOTICE, "Copying message from %s@%s to %s@%s\n", vmu->mailbox, vmu->context, recip->mailbox, recip->context);
3498 create_dirpath(todir, sizeof(todir), recip->context, recip->mailbox, "INBOX");
3500 if (!dir)
3501 make_dir(fromdir, sizeof(fromdir), vmu->context, vmu->mailbox, frombox);
3502 else
3503 ast_copy_string(fromdir, dir, sizeof(fromdir));
3505 make_file(frompath, sizeof(frompath), fromdir, msgnum);
3507 if (vm_lock_path(todir))
3508 return ERROR_LOCK_PATH;
3510 recipmsgnum = 0;
3511 do {
3512 make_file(topath, sizeof(topath), todir, recipmsgnum);
3513 if (!EXISTS(todir, recipmsgnum, topath, chan->language))
3514 break;
3515 recipmsgnum++;
3516 } while (recipmsgnum < recip->maxmsg);
3517 if (recipmsgnum < recip->maxmsg) {
3518 if (EXISTS(fromdir, msgnum, frompath, chan->language)) {
3519 COPY(fromdir, msgnum, todir, recipmsgnum, recip->mailbox, recip->context, frompath, topath);
3520 } else {
3521 /* For ODBC storage, if the file we want to copy isn't yet in the database, then the SQL
3522 * copy will fail. Instead, we need to create a local copy, store it, and delete the local
3523 * copy. We don't have to #ifdef this because if file storage reaches this point, there's a
3524 * much worse problem happening and IMAP storage doesn't call this function
3526 copy_plain_file(frompath, topath);
3527 STORE(todir, recip->mailbox, recip->context, recipmsgnum, chan, recip, fmt, duration, NULL);
3528 vm_delete(topath);
3530 } else {
3531 ast_log(LOG_ERROR, "Recipient mailbox %s@%s is full\n", recip->mailbox, recip->context);
3533 ast_unlock_path(todir);
3534 notify_new_message(chan, recip, recipmsgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
3536 return 0;
3538 #endif
3539 #if !(defined(IMAP_STORAGE) || defined(ODBC_STORAGE))
3540 static int messagecount(const char *context, const char *mailbox, const char *folder)
3542 return __has_voicemail(context, mailbox, folder, 0);
3546 static int __has_voicemail(const char *context, const char *mailbox, const char *folder, int shortcircuit)
3548 DIR *dir;
3549 struct dirent *de;
3550 char fn[256];
3551 int ret = 0;
3552 if (!folder)
3553 folder = "INBOX";
3554 /* If no mailbox, return immediately */
3555 if (ast_strlen_zero(mailbox))
3556 return 0;
3557 if (!context)
3558 context = "default";
3559 snprintf(fn, sizeof(fn), "%s%s/%s/%s", VM_SPOOL_DIR, context, mailbox, folder);
3560 dir = opendir(fn);
3561 if (!dir)
3562 return 0;
3563 while ((de = readdir(dir))) {
3564 if (!strncasecmp(de->d_name, "msg", 3)) {
3565 if (shortcircuit) {
3566 ret = 1;
3567 break;
3568 } else if (!strncasecmp(de->d_name + 8, "txt", 3))
3569 ret++;
3572 closedir(dir);
3573 return ret;
3577 static int has_voicemail(const char *mailbox, const char *folder)
3579 char tmp[256], *tmp2 = tmp, *mbox, *context;
3580 ast_copy_string(tmp, mailbox, sizeof(tmp));
3581 while ((mbox = strsep(&tmp2, ","))) {
3582 if ((context = strchr(mbox, '@')))
3583 *context++ = '\0';
3584 else
3585 context = "default";
3586 if (__has_voicemail(context, mbox, folder, 1))
3587 return 1;
3589 return 0;
3593 static int inboxcount(const char *mailbox, int *newmsgs, int *oldmsgs)
3595 char tmp[256];
3596 char *context;
3598 if (newmsgs)
3599 *newmsgs = 0;
3600 if (oldmsgs)
3601 *oldmsgs = 0;
3602 /* If no mailbox, return immediately */
3603 if (ast_strlen_zero(mailbox))
3604 return 0;
3605 if (strchr(mailbox, ',')) {
3606 int tmpnew, tmpold;
3607 char *mb, *cur;
3609 ast_copy_string(tmp, mailbox, sizeof(tmp));
3610 mb = tmp;
3611 while ((cur = strsep(&mb, ", "))) {
3612 if (!ast_strlen_zero(cur)) {
3613 if (inboxcount(cur, newmsgs ? &tmpnew : NULL, oldmsgs ? &tmpold : NULL))
3614 return -1;
3615 else {
3616 if (newmsgs)
3617 *newmsgs += tmpnew;
3618 if (oldmsgs)
3619 *oldmsgs += tmpold;
3623 return 0;
3625 ast_copy_string(tmp, mailbox, sizeof(tmp));
3626 context = strchr(tmp, '@');
3627 if (context) {
3628 *context = '\0';
3629 context++;
3630 } else
3631 context = "default";
3632 if (newmsgs)
3633 *newmsgs = __has_voicemail(context, tmp, "INBOX", 0);
3634 if (oldmsgs)
3635 *oldmsgs = __has_voicemail(context, tmp, "Old", 0);
3636 return 0;
3639 #endif
3641 static void run_externnotify(char *context, char *extension)
3643 char arguments[255];
3644 char ext_context[256] = "";
3645 int newvoicemails = 0, oldvoicemails = 0;
3646 struct ast_smdi_mwi_message *mwi_msg;
3648 if (!ast_strlen_zero(context))
3649 snprintf(ext_context, sizeof(ext_context), "%s@%s", extension, context);
3650 else
3651 ast_copy_string(ext_context, extension, sizeof(ext_context));
3653 if (!strcasecmp(externnotify, "smdi")) {
3654 if (ast_app_has_voicemail(ext_context, NULL))
3655 ast_smdi_mwi_set(smdi_iface, extension);
3656 else
3657 ast_smdi_mwi_unset(smdi_iface, extension);
3659 if ((mwi_msg = ast_smdi_mwi_message_wait_station(smdi_iface, SMDI_MWI_WAIT_TIMEOUT, extension))) {
3660 ast_log(LOG_ERROR, "Error executing SMDI MWI change for %s\n", extension);
3661 if (!strncmp(mwi_msg->cause, "INV", 3))
3662 ast_log(LOG_ERROR, "Invalid MWI extension: %s\n", mwi_msg->fwd_st);
3663 else if (!strncmp(mwi_msg->cause, "BLK", 3))
3664 ast_log(LOG_WARNING, "MWI light was already on or off for %s\n", mwi_msg->fwd_st);
3665 ast_log(LOG_WARNING, "The switch reported '%s'\n", mwi_msg->cause);
3666 ASTOBJ_UNREF(mwi_msg, ast_smdi_mwi_message_destroy);
3667 } else {
3668 if (option_debug)
3669 ast_log(LOG_DEBUG, "Successfully executed SMDI MWI change for %s\n", extension);
3671 } else if (!ast_strlen_zero(externnotify)) {
3672 if (inboxcount(ext_context, &newvoicemails, &oldvoicemails)) {
3673 ast_log(LOG_ERROR, "Problem in calculating number of voicemail messages available for extension %s\n", extension);
3674 } else {
3675 snprintf(arguments, sizeof(arguments), "%s %s %s %d&", externnotify, context, extension, newvoicemails);
3676 if (option_debug)
3677 ast_log(LOG_DEBUG, "Executing %s\n", arguments);
3678 ast_safe_system(arguments);
3683 struct leave_vm_options {
3684 unsigned int flags;
3685 signed char record_gain;
3688 static int leave_voicemail(struct ast_channel *chan, char *ext, struct leave_vm_options *options)
3690 #ifdef IMAP_STORAGE
3691 int newmsgs, oldmsgs;
3692 #endif
3693 struct vm_state *vms = NULL;
3694 char txtfile[PATH_MAX], tmptxtfile[PATH_MAX];
3695 char callerid[256];
3696 FILE *txt;
3697 char date[256];
3698 int txtdes;
3699 int res = 0;
3700 int msgnum;
3701 int duration = 0;
3702 int ausemacro = 0;
3703 int ousemacro = 0;
3704 int ouseexten = 0;
3705 char dir[PATH_MAX], tmpdir[PATH_MAX];
3706 char dest[PATH_MAX];
3707 char fn[PATH_MAX];
3708 char prefile[PATH_MAX] = "";
3709 char tempfile[PATH_MAX] = "";
3710 char ext_context[256] = "";
3711 char fmt[80];
3712 char *context;
3713 char ecodes[16] = "#";
3714 char tmp[1024] = "", *tmpptr;
3715 struct ast_vm_user *vmu;
3716 struct ast_vm_user svm;
3717 const char *category = NULL;
3719 ast_copy_string(tmp, ext, sizeof(tmp));
3720 ext = tmp;
3721 context = strchr(tmp, '@');
3722 if (context) {
3723 *context++ = '\0';
3724 tmpptr = strchr(context, '&');
3725 } else {
3726 tmpptr = strchr(ext, '&');
3729 if (tmpptr)
3730 *tmpptr++ = '\0';
3732 category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
3734 if (option_debug > 2)
3735 ast_log(LOG_DEBUG, "Before find_user\n");
3736 if (!(vmu = find_user(&svm, context, ext))) {
3737 ast_log(LOG_WARNING, "No entry in voicemail config file for '%s'\n", ext);
3738 if (ast_test_flag(options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
3739 ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101);
3740 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3741 return res;
3743 /* Setup pre-file if appropriate */
3744 if (strcmp(vmu->context, "default"))
3745 snprintf(ext_context, sizeof(ext_context), "%s@%s", ext, vmu->context);
3746 else
3747 ast_copy_string(ext_context, vmu->mailbox, sizeof(ext_context));
3748 if (ast_test_flag(options, OPT_BUSY_GREETING)) {
3749 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "busy");
3750 snprintf(prefile, sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, ext);
3751 } else if (ast_test_flag(options, OPT_UNAVAIL_GREETING)) {
3752 res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "unavail");
3753 snprintf(prefile, sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, ext);
3755 snprintf(tempfile, sizeof(tempfile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, ext);
3756 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, ext, "temp"))) {
3757 ast_log(LOG_WARNING, "Failed to make directory (%s)\n", tempfile);
3758 return -1;
3760 RETRIEVE(tempfile, -1, vmu);
3761 if (ast_fileexists(tempfile, NULL, NULL) > 0)
3762 ast_copy_string(prefile, tempfile, sizeof(prefile));
3763 DISPOSE(tempfile, -1);
3764 /* It's easier just to try to make it than to check for its existence */
3765 create_dirpath(dir, sizeof(dir), vmu->context, ext, "INBOX");
3766 create_dirpath(tmpdir, sizeof(tmpdir), vmu->context, ext, "tmp");
3768 /* Check current or macro-calling context for special extensions */
3769 if (ast_test_flag(vmu, VM_OPERATOR)) {
3770 if (!ast_strlen_zero(vmu->exit)) {
3771 if (ast_exists_extension(chan, vmu->exit, "o", 1, chan->cid.cid_num)) {
3772 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3773 ouseexten = 1;
3775 } else if (ast_exists_extension(chan, chan->context, "o", 1, chan->cid.cid_num)) {
3776 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3777 ouseexten = 1;
3779 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "o", 1, chan->cid.cid_num)) {
3780 strncat(ecodes, "0", sizeof(ecodes) - strlen(ecodes) - 1);
3781 ousemacro = 1;
3785 if (!ast_strlen_zero(vmu->exit)) {
3786 if (ast_exists_extension(chan, vmu->exit, "a", 1, chan->cid.cid_num))
3787 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3788 } else if (ast_exists_extension(chan, chan->context, "a", 1, chan->cid.cid_num))
3789 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3790 else if (!ast_strlen_zero(chan->macrocontext) && ast_exists_extension(chan, chan->macrocontext, "a", 1, chan->cid.cid_num)) {
3791 strncat(ecodes, "*", sizeof(ecodes) - strlen(ecodes) - 1);
3792 ausemacro = 1;
3795 /* Play the beginning intro if desired */
3796 if (!ast_strlen_zero(prefile)) {
3797 res = play_greeting(chan, vmu, prefile, ecodes);
3798 if (res == -2) {
3799 /* The file did not exist */
3800 if (option_debug)
3801 ast_log(LOG_DEBUG, "%s doesn't exist, doing what we can\n", prefile);
3802 res = invent_message(chan, vmu, ext, ast_test_flag(options, OPT_BUSY_GREETING), ecodes);
3804 if (res < 0) {
3805 if (option_debug)
3806 ast_log(LOG_DEBUG, "Hang up during prefile playback\n");
3807 free_user(vmu);
3808 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3809 return -1;
3812 if (res == '#') {
3813 /* On a '#' we skip the instructions */
3814 ast_set_flag(options, OPT_SILENT);
3815 res = 0;
3817 if (!res && !ast_test_flag(options, OPT_SILENT)) {
3818 res = ast_stream_and_wait(chan, INTRO, chan->language, ecodes);
3819 if (res == '#') {
3820 ast_set_flag(options, OPT_SILENT);
3821 res = 0;
3824 if (res > 0)
3825 ast_stopstream(chan);
3826 /* Check for a '*' here in case the caller wants to escape from voicemail to something
3827 other than the operator -- an automated attendant or mailbox login for example */
3828 if (res == '*') {
3829 chan->exten[0] = 'a';
3830 chan->exten[1] = '\0';
3831 if (!ast_strlen_zero(vmu->exit)) {
3832 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3833 } else if (ausemacro && !ast_strlen_zero(chan->macrocontext)) {
3834 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3836 chan->priority = 0;
3837 free_user(vmu);
3838 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3839 return 0;
3842 /* Check for a '0' here */
3843 if (res == '0') {
3844 transfer:
3845 if (ouseexten || ousemacro) {
3846 chan->exten[0] = 'o';
3847 chan->exten[1] = '\0';
3848 if (!ast_strlen_zero(vmu->exit)) {
3849 ast_copy_string(chan->context, vmu->exit, sizeof(chan->context));
3850 } else if (ousemacro && !ast_strlen_zero(chan->macrocontext)) {
3851 ast_copy_string(chan->context, chan->macrocontext, sizeof(chan->context));
3853 ast_play_and_wait(chan, "transfer");
3854 chan->priority = 0;
3855 free_user(vmu);
3856 pbx_builtin_setvar_helper(chan, "VMSTATUS", "USEREXIT");
3858 return 0;
3860 if (res < 0) {
3861 free_user(vmu);
3862 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3863 return -1;
3865 /* The meat of recording the message... All the announcements and beeps have been played*/
3866 ast_copy_string(fmt, vmfmts, sizeof(fmt));
3867 if (!ast_strlen_zero(fmt)) {
3868 msgnum = 0;
3870 #ifdef IMAP_STORAGE
3871 /* Is ext a mailbox? */
3872 /* must open stream for this user to get info! */
3873 res = inboxcount(ext_context, &newmsgs, &oldmsgs);
3874 if (res < 0) {
3875 ast_log(LOG_NOTICE,"Can not leave voicemail, unable to count messages\n");
3876 return -1;
3878 if (!(vms = get_vm_state_by_mailbox(ext, context, 0))) {
3879 /*It is possible under certain circumstances that inboxcount did not create a vm_state when it was needed. This is a catchall which will
3880 * rarely be used*/
3881 if (!(vms = create_vm_state_from_user(vmu))) {
3882 ast_log(LOG_ERROR, "Couldn't allocate necessary space\n");
3883 return -1;
3886 vms->newmessages++;
3887 /* here is a big difference! We add one to it later */
3888 msgnum = newmsgs + oldmsgs;
3889 if (option_debug > 2)
3890 ast_log(LOG_DEBUG, "Messagecount set to %d\n",msgnum);
3891 snprintf(fn, sizeof(fn), "%s/imap/msg%s%04d", VM_SPOOL_DIR, vmu->mailbox, msgnum);
3892 /* set variable for compatability */
3893 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
3895 /* Check if mailbox is full */
3896 check_quota(vms, imapfolder);
3897 if (vms->quota_limit && vms->quota_usage >= vms->quota_limit) {
3898 if (option_debug)
3899 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!! %u >= %u\n", vms->quota_usage, vms->quota_limit);
3900 ast_play_and_wait(chan, "vm-mailboxfull");
3901 return -1;
3903 if (option_debug > 2)
3904 ast_log(LOG_DEBUG, "Checking message number quota - mailbox has %d messages, maximum is set to %d\n",msgnum,vmu->maxmsg);
3905 if (msgnum >= vmu->maxmsg) {
3906 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3907 if (!res)
3908 res = ast_waitstream(chan, "");
3909 ast_log(LOG_WARNING, "No more messages possible\n");
3910 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3911 goto leave_vm_out;
3914 /* Check if we have exceeded maxmsg */
3915 if (msgnum >= vmu->maxmsg) {
3916 ast_log(LOG_WARNING, "Unable to leave message since we will exceed the maximum number of messages allowed (%u > %u)\n", msgnum, vmu->maxmsg);
3917 ast_play_and_wait(chan, "vm-mailboxfull");
3918 return -1;
3920 #else
3921 if (count_messages(vmu, dir) >= 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 #endif
3931 snprintf(tmptxtfile, sizeof(tmptxtfile), "%s/XXXXXX", tmpdir);
3932 txtdes = mkstemp(tmptxtfile);
3933 chmod(tmptxtfile, VOICEMAIL_FILE_MODE & ~my_umask);
3934 if (txtdes < 0) {
3935 res = ast_streamfile(chan, "vm-mailboxfull", chan->language);
3936 if (!res)
3937 res = ast_waitstream(chan, "");
3938 ast_log(LOG_ERROR, "Unable to create message file: %s\n", strerror(errno));
3939 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
3940 goto leave_vm_out;
3943 /* Now play the beep once we have the message number for our next message. */
3944 if (res >= 0) {
3945 /* Unless we're *really* silent, try to send the beep */
3946 res = ast_stream_and_wait(chan, "beep", chan->language, "");
3949 /* Store information */
3950 txt = fdopen(txtdes, "w+");
3951 if (txt) {
3952 get_date(date, sizeof(date));
3953 fprintf(txt,
3954 ";\n"
3955 "; Message Information file\n"
3956 ";\n"
3957 "[message]\n"
3958 "origmailbox=%s\n"
3959 "context=%s\n"
3960 "macrocontext=%s\n"
3961 "exten=%s\n"
3962 "priority=%d\n"
3963 "callerchan=%s\n"
3964 "callerid=%s\n"
3965 "origdate=%s\n"
3966 "origtime=%ld\n"
3967 "category=%s\n",
3968 ext,
3969 chan->context,
3970 chan->macrocontext,
3971 chan->exten,
3972 chan->priority,
3973 chan->name,
3974 ast_callerid_merge(callerid, sizeof(callerid), S_OR(chan->cid.cid_name, NULL), S_OR(chan->cid.cid_num, NULL), "Unknown"),
3975 date, (long)time(NULL),
3976 category ? category : "");
3977 } else
3978 ast_log(LOG_WARNING, "Error opening text file for output\n");
3979 res = play_record_review(chan, NULL, tmptxtfile, vmmaxmessage, fmt, 1, vmu, &duration, NULL, options->record_gain, vms);
3981 if (txt) {
3982 if (duration < vmminmessage) {
3983 fclose(txt);
3984 if (option_verbose > 2)
3985 ast_verbose( VERBOSE_PREFIX_3 "Recording was %d seconds long but needs to be at least %d - abandoning\n", duration, vmminmessage);
3986 ast_filedelete(tmptxtfile, NULL);
3987 unlink(tmptxtfile);
3988 } else {
3989 fprintf(txt, "duration=%d\n", duration);
3990 fclose(txt);
3991 if (vm_lock_path(dir)) {
3992 ast_log(LOG_ERROR, "Couldn't lock directory %s. Voicemail will be lost.\n", dir);
3993 /* Delete files */
3994 ast_filedelete(tmptxtfile, NULL);
3995 unlink(tmptxtfile);
3996 } else if (ast_fileexists(tmptxtfile, NULL, NULL) <= 0) {
3997 if (option_debug)
3998 ast_log(LOG_DEBUG, "The recorded media file is gone, so we should remove the .txt file too!\n");
3999 unlink(tmptxtfile);
4000 ast_unlock_path(dir);
4001 } else {
4002 for (;;) {
4003 make_file(fn, sizeof(fn), dir, msgnum);
4004 if (!EXISTS(dir, msgnum, fn, NULL))
4005 break;
4006 msgnum++;
4009 /* assign a variable with the name of the voicemail file */
4010 #ifndef IMAP_STORAGE
4011 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", fn);
4012 #else
4013 pbx_builtin_setvar_helper(chan, "VM_MESSAGEFILE", "IMAP_STORAGE");
4014 #endif
4016 snprintf(txtfile, sizeof(txtfile), "%s.txt", fn);
4017 ast_filerename(tmptxtfile, fn, NULL);
4018 rename(tmptxtfile, txtfile);
4020 ast_unlock_path(dir);
4021 /* We must store the file first, before copying the message, because
4022 * ODBC storage does the entire copy with SQL.
4024 if (ast_fileexists(fn, NULL, NULL) > 0) {
4025 STORE(dir, vmu->mailbox, vmu->context, msgnum, chan, vmu, fmt, duration, vms);
4028 /* Are there to be more recipients of this message? */
4029 while (tmpptr) {
4030 struct ast_vm_user recipu, *recip;
4031 char *exten, *context;
4033 exten = strsep(&tmpptr, "&");
4034 context = strchr(exten, '@');
4035 if (context) {
4036 *context = '\0';
4037 context++;
4039 if ((recip = find_user(&recipu, context, exten))) {
4040 copy_message(chan, vmu, 0, msgnum, duration, recip, fmt, dir);
4041 free_user(recip);
4044 /* Notification and disposal needs to happen after the copy, though. */
4045 if (ast_fileexists(fn, NULL, NULL)) {
4046 notify_new_message(chan, vmu, msgnum, duration, fmt, S_OR(chan->cid.cid_num, NULL), S_OR(chan->cid.cid_name, NULL));
4047 DISPOSE(dir, msgnum);
4052 if (res == '0') {
4053 goto transfer;
4054 } else if (res > 0)
4055 res = 0;
4057 if (duration < vmminmessage)
4058 /* XXX We should really give a prompt too short/option start again, with leave_vm_out called only after a timeout XXX */
4059 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
4060 else
4061 pbx_builtin_setvar_helper(chan, "VMSTATUS", "SUCCESS");
4062 } else
4063 ast_log(LOG_WARNING, "No format for saving voicemail?\n");
4064 leave_vm_out:
4065 free_user(vmu);
4067 return res;
4070 #ifndef IMAP_STORAGE
4071 static int resequence_mailbox(struct ast_vm_user *vmu, char *dir)
4073 /* we know max messages, so stop process when number is hit */
4075 int x,dest;
4076 char sfn[PATH_MAX];
4077 char dfn[PATH_MAX];
4079 if (vm_lock_path(dir))
4080 return ERROR_LOCK_PATH;
4082 for (x = 0, dest = 0; x < vmu->maxmsg; x++) {
4083 make_file(sfn, sizeof(sfn), dir, x);
4084 if (EXISTS(dir, x, sfn, NULL)) {
4086 if (x != dest) {
4087 make_file(dfn, sizeof(dfn), dir, dest);
4088 RENAME(dir, x, vmu->mailbox, vmu->context, dir, dest, sfn, dfn);
4091 dest++;
4094 ast_unlock_path(dir);
4096 return 0;
4098 #endif
4100 static int say_and_wait(struct ast_channel *chan, int num, const char *language)
4102 int d;
4103 d = ast_say_number(chan, num, AST_DIGIT_ANY, language, (char *) NULL);
4104 return d;
4107 static int save_to_folder(struct ast_vm_user *vmu, struct vm_state *vms, int msg, int box)
4109 #ifdef IMAP_STORAGE
4110 /* we must use mbox(x) folder names, and copy the message there */
4111 /* simple. huh? */
4112 char sequence[10];
4113 /* get the real IMAP message number for this message */
4114 snprintf(sequence, sizeof(sequence), "%ld", vms->msgArray[msg]);
4115 if (option_debug > 2)
4116 ast_log(LOG_DEBUG, "Copying sequence %s to mailbox %s\n",sequence,(char *) mbox(box));
4117 if (box == 1) {
4118 mail_setflag(vms->mailstream, sequence, "\\Seen");
4119 } else if (box == 0) {
4120 mail_clearflag(vms->mailstream, sequence, "\\Seen");
4122 if (!strcasecmp(mbox(0), vms->curbox) && (box == 0 || box == 1))
4123 return 0;
4124 else
4125 return !mail_copy(vms->mailstream,sequence,(char *) mbox(box));
4126 #else
4127 char *dir = vms->curdir;
4128 char *username = vms->username;
4129 char *context = vmu->context;
4130 char sfn[PATH_MAX];
4131 char dfn[PATH_MAX];
4132 char ddir[PATH_MAX];
4133 const char *dbox = mbox(box);
4134 int x;
4135 make_file(sfn, sizeof(sfn), dir, msg);
4136 create_dirpath(ddir, sizeof(ddir), context, username, dbox);
4138 if (vm_lock_path(ddir))
4139 return ERROR_LOCK_PATH;
4141 for (x = 0; x < vmu->maxmsg; x++) {
4142 make_file(dfn, sizeof(dfn), ddir, x);
4143 if (!EXISTS(ddir, x, dfn, NULL))
4144 break;
4146 if (x >= vmu->maxmsg) {
4147 ast_unlock_path(ddir);
4148 return ERROR_MAILBOX_FULL;
4150 if (strcmp(sfn, dfn)) {
4151 COPY(dir, msg, ddir, x, username, context, sfn, dfn);
4153 ast_unlock_path(ddir);
4154 #endif
4155 return 0;
4158 static int adsi_logo(unsigned char *buf)
4160 int bytes = 0;
4161 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, "Comedian Mail", "");
4162 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, "(C)2002-2006 Digium, Inc.", "");
4163 return bytes;
4166 static int adsi_load_vmail(struct ast_channel *chan, int *useadsi)
4168 unsigned char buf[256];
4169 int bytes=0;
4170 int x;
4171 char num[5];
4173 *useadsi = 0;
4174 bytes += ast_adsi_data_mode(buf + bytes);
4175 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4177 bytes = 0;
4178 bytes += adsi_logo(buf);
4179 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
4180 #ifdef DISPLAY
4181 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " .", "");
4182 #endif
4183 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4184 bytes += ast_adsi_data_mode(buf + bytes);
4185 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4187 if (ast_adsi_begin_download(chan, addesc, adsifdn, adsisec, adsiver)) {
4188 bytes = 0;
4189 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Cancelled.", "");
4190 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
4191 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4192 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4193 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4194 return 0;
4197 #ifdef DISPLAY
4198 /* Add a dot */
4199 bytes = 0;
4200 bytes += ast_adsi_logo(buf);
4201 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Downloading Scripts", "");
4202 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ..", "");
4203 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4204 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4205 #endif
4206 bytes = 0;
4207 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 0, "Listen", "Listen", "1", 1);
4208 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 1, "Folder", "Folder", "2", 1);
4209 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 2, "Advanced", "Advnced", "3", 1);
4210 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Options", "Options", "0", 1);
4211 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 4, "Help", "Help", "*", 1);
4212 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 5, "Exit", "Exit", "#", 1);
4213 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
4215 #ifdef DISPLAY
4216 /* Add another dot */
4217 bytes = 0;
4218 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ...", "");
4219 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4221 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4222 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4223 #endif
4225 bytes = 0;
4226 /* These buttons we load but don't use yet */
4227 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 6, "Previous", "Prev", "4", 1);
4228 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 8, "Repeat", "Repeat", "5", 1);
4229 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 7, "Delete", "Delete", "7", 1);
4230 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 9, "Next", "Next", "6", 1);
4231 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 10, "Save", "Save", "9", 1);
4232 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 11, "Undelete", "Restore", "7", 1);
4233 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
4235 #ifdef DISPLAY
4236 /* Add another dot */
4237 bytes = 0;
4238 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, " ....", "");
4239 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4240 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4241 #endif
4243 bytes = 0;
4244 for (x=0;x<5;x++) {
4245 snprintf(num, sizeof(num), "%d", x);
4246 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + x, mbox(x), mbox(x), num, 1);
4248 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 12 + 5, "Cancel", "Cancel", "#", 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 if (ast_adsi_end_download(chan)) {
4260 bytes = 0;
4261 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Download Unsuccessful.", "");
4262 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "ADSI Unavailable", "");
4263 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4264 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4265 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4266 return 0;
4268 bytes = 0;
4269 bytes += ast_adsi_download_disconnect(buf + bytes);
4270 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4271 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DOWNLOAD);
4273 if (option_debug)
4274 ast_log(LOG_DEBUG, "Done downloading scripts...\n");
4276 #ifdef DISPLAY
4277 /* Add last dot */
4278 bytes = 0;
4279 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ......", "");
4280 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4281 #endif
4282 if (option_debug)
4283 ast_log(LOG_DEBUG, "Restarting session...\n");
4285 bytes = 0;
4286 /* Load the session now */
4287 if (ast_adsi_load_session(chan, adsifdn, adsiver, 1) == 1) {
4288 *useadsi = 1;
4289 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Scripts Loaded!", "");
4290 } else
4291 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Load Failed!", "");
4293 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4294 return 0;
4297 static void adsi_begin(struct ast_channel *chan, int *useadsi)
4299 int x;
4300 if (!ast_adsi_available(chan))
4301 return;
4302 x = ast_adsi_load_session(chan, adsifdn, adsiver, 1);
4303 if (x < 0)
4304 return;
4305 if (!x) {
4306 if (adsi_load_vmail(chan, useadsi)) {
4307 ast_log(LOG_WARNING, "Unable to upload voicemail scripts\n");
4308 return;
4310 } else
4311 *useadsi = 1;
4314 static void adsi_login(struct ast_channel *chan)
4316 unsigned char buf[256];
4317 int bytes=0;
4318 unsigned char keys[8];
4319 int x;
4320 if (!ast_adsi_available(chan))
4321 return;
4323 for (x=0;x<8;x++)
4324 keys[x] = 0;
4325 /* Set one key for next */
4326 keys[3] = ADSI_KEY_APPS + 3;
4328 bytes += adsi_logo(buf + bytes);
4329 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, " ", "");
4330 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, " ", "");
4331 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4332 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Mailbox: ******", "");
4333 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 1, 1, ADSI_JUST_LEFT);
4334 bytes += ast_adsi_load_soft_key(buf + bytes, ADSI_KEY_APPS + 3, "Enter", "Enter", "#", 1);
4335 bytes += ast_adsi_set_keys(buf + bytes, keys);
4336 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4337 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4340 static void adsi_password(struct ast_channel *chan)
4342 unsigned char buf[256];
4343 int bytes=0;
4344 unsigned char keys[8];
4345 int x;
4346 if (!ast_adsi_available(chan))
4347 return;
4349 for (x=0;x<8;x++)
4350 keys[x] = 0;
4351 /* Set one key for next */
4352 keys[3] = ADSI_KEY_APPS + 3;
4354 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4355 bytes += ast_adsi_input_format(buf + bytes, 1, ADSI_DIR_FROM_LEFT, 0, "Password: ******", "");
4356 bytes += ast_adsi_input_control(buf + bytes, ADSI_COMM_PAGE, 4, 0, 1, ADSI_JUST_LEFT);
4357 bytes += ast_adsi_set_keys(buf + bytes, keys);
4358 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4359 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4362 static void adsi_folders(struct ast_channel *chan, int start, char *label)
4364 unsigned char buf[256];
4365 int bytes=0;
4366 unsigned char keys[8];
4367 int x,y;
4369 if (!ast_adsi_available(chan))
4370 return;
4372 for (x=0;x<5;x++) {
4373 y = ADSI_KEY_APPS + 12 + start + x;
4374 if (y > ADSI_KEY_APPS + 12 + 4)
4375 y = 0;
4376 keys[x] = ADSI_KEY_SKT | y;
4378 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 17);
4379 keys[6] = 0;
4380 keys[7] = 0;
4382 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_CENT, 0, label, "");
4383 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_CENT, 0, " ", "");
4384 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4385 bytes += ast_adsi_set_keys(buf + bytes, keys);
4386 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4388 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4391 static void adsi_message(struct ast_channel *chan, struct vm_state *vms)
4393 int bytes=0;
4394 unsigned char buf[256];
4395 char buf1[256], buf2[256];
4396 char fn2[PATH_MAX];
4398 char cid[256]="";
4399 char *val;
4400 char *name, *num;
4401 char datetime[21]="";
4402 FILE *f;
4404 unsigned char keys[8];
4406 int x;
4408 if (!ast_adsi_available(chan))
4409 return;
4411 /* Retrieve important info */
4412 snprintf(fn2, sizeof(fn2), "%s.txt", vms->fn);
4413 f = fopen(fn2, "r");
4414 if (f) {
4415 while (!feof(f)) {
4416 fgets((char *)buf, sizeof(buf), f);
4417 if (!feof(f)) {
4418 char *stringp=NULL;
4419 stringp = (char *)buf;
4420 strsep(&stringp, "=");
4421 val = strsep(&stringp, "=");
4422 if (!ast_strlen_zero(val)) {
4423 if (!strcmp((char *)buf, "callerid"))
4424 ast_copy_string(cid, val, sizeof(cid));
4425 if (!strcmp((char *)buf, "origdate"))
4426 ast_copy_string(datetime, val, sizeof(datetime));
4430 fclose(f);
4432 /* New meaning for keys */
4433 for (x=0;x<5;x++)
4434 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
4435 keys[6] = 0x0;
4436 keys[7] = 0x0;
4438 if (!vms->curmsg) {
4439 /* No prev key, provide "Folder" instead */
4440 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
4442 if (vms->curmsg >= vms->lastmsg) {
4443 /* If last message ... */
4444 if (vms->curmsg) {
4445 /* but not only message, provide "Folder" instead */
4446 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
4447 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4449 } else {
4450 /* Otherwise if only message, leave blank */
4451 keys[3] = 1;
4455 if (!ast_strlen_zero(cid)) {
4456 ast_callerid_parse(cid, &name, &num);
4457 if (!name)
4458 name = num;
4459 } else
4460 name = "Unknown Caller";
4462 /* If deleted, show "undeleted" */
4464 if (vms->deleted[vms->curmsg])
4465 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
4467 /* Except "Exit" */
4468 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
4469 snprintf(buf1, sizeof(buf1), "%s%s", vms->curbox,
4470 strcasecmp(vms->curbox, "INBOX") ? " Messages" : "");
4471 snprintf(buf2, sizeof(buf2), "Message %d of %d", vms->curmsg + 1, vms->lastmsg + 1);
4473 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
4474 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
4475 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, name, "");
4476 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_LEFT, 0, datetime, "");
4477 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4478 bytes += ast_adsi_set_keys(buf + bytes, keys);
4479 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4481 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4484 static void adsi_delete(struct ast_channel *chan, struct vm_state *vms)
4486 int bytes=0;
4487 unsigned char buf[256];
4488 unsigned char keys[8];
4490 int x;
4492 if (!ast_adsi_available(chan))
4493 return;
4495 /* New meaning for keys */
4496 for (x=0;x<5;x++)
4497 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 6 + x);
4499 keys[6] = 0x0;
4500 keys[7] = 0x0;
4502 if (!vms->curmsg) {
4503 /* No prev key, provide "Folder" instead */
4504 keys[0] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
4506 if (vms->curmsg >= vms->lastmsg) {
4507 /* If last message ... */
4508 if (vms->curmsg) {
4509 /* but not only message, provide "Folder" instead */
4510 keys[3] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 1);
4511 } else {
4512 /* Otherwise if only message, leave blank */
4513 keys[3] = 1;
4517 /* If deleted, show "undeleted" */
4518 if (vms->deleted[vms->curmsg])
4519 keys[1] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 11);
4521 /* Except "Exit" */
4522 keys[5] = ADSI_KEY_SKT | (ADSI_KEY_APPS + 5);
4523 bytes += ast_adsi_set_keys(buf + bytes, keys);
4524 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4526 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4529 static void adsi_status(struct ast_channel *chan, struct vm_state *vms)
4531 unsigned char buf[256] = "";
4532 char buf1[256] = "", buf2[256] = "";
4533 int bytes=0;
4534 unsigned char keys[8];
4535 int x;
4537 char *newm = (vms->newmessages == 1) ? "message" : "messages";
4538 char *oldm = (vms->oldmessages == 1) ? "message" : "messages";
4539 if (!ast_adsi_available(chan))
4540 return;
4541 if (vms->newmessages) {
4542 snprintf(buf1, sizeof(buf1), "You have %d new", vms->newmessages);
4543 if (vms->oldmessages) {
4544 strncat(buf1, " and", sizeof(buf1) - strlen(buf1) - 1);
4545 snprintf(buf2, sizeof(buf2), "%d old %s.", vms->oldmessages, oldm);
4546 } else {
4547 snprintf(buf2, sizeof(buf2), "%s.", newm);
4549 } else if (vms->oldmessages) {
4550 snprintf(buf1, sizeof(buf1), "You have %d old", vms->oldmessages);
4551 snprintf(buf2, sizeof(buf2), "%s.", oldm);
4552 } else {
4553 strcpy(buf1, "You have no messages.");
4554 buf2[0] = ' ';
4555 buf2[1] = '\0';
4557 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
4558 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
4559 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4561 for (x=0;x<6;x++)
4562 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
4563 keys[6] = 0;
4564 keys[7] = 0;
4566 /* Don't let them listen if there are none */
4567 if (vms->lastmsg < 0)
4568 keys[0] = 1;
4569 bytes += ast_adsi_set_keys(buf + bytes, keys);
4571 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4573 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4576 static void adsi_status2(struct ast_channel *chan, struct vm_state *vms)
4578 unsigned char buf[256] = "";
4579 char buf1[256] = "", buf2[256] = "";
4580 int bytes=0;
4581 unsigned char keys[8];
4582 int x;
4584 char *mess = (vms->lastmsg == 0) ? "message" : "messages";
4586 if (!ast_adsi_available(chan))
4587 return;
4589 /* Original command keys */
4590 for (x=0;x<6;x++)
4591 keys[x] = ADSI_KEY_SKT | (ADSI_KEY_APPS + x);
4593 keys[6] = 0;
4594 keys[7] = 0;
4596 if ((vms->lastmsg + 1) < 1)
4597 keys[0] = 0;
4599 snprintf(buf1, sizeof(buf1), "%s%s has", vms->curbox,
4600 strcasecmp(vms->curbox, "INBOX") ? " folder" : "");
4602 if (vms->lastmsg + 1)
4603 snprintf(buf2, sizeof(buf2), "%d %s.", vms->lastmsg + 1, mess);
4604 else
4605 strcpy(buf2, "no messages.");
4606 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 1, ADSI_JUST_LEFT, 0, buf1, "");
4607 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 2, ADSI_JUST_LEFT, 0, buf2, "");
4608 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, "", "");
4609 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4610 bytes += ast_adsi_set_keys(buf + bytes, keys);
4612 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4614 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4619 static void adsi_clear(struct ast_channel *chan)
4621 char buf[256];
4622 int bytes=0;
4623 if (!ast_adsi_available(chan))
4624 return;
4625 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4626 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4628 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4632 static void adsi_goodbye(struct ast_channel *chan)
4634 unsigned char buf[256];
4635 int bytes=0;
4637 if (!ast_adsi_available(chan))
4638 return;
4639 bytes += adsi_logo(buf + bytes);
4640 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_LEFT, 0, " ", "");
4641 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Goodbye", "");
4642 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
4643 bytes += ast_adsi_voice_mode(buf + bytes, 0);
4645 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
4648 /*--- get_folder: Folder menu ---*/
4649 /* Plays "press 1 for INBOX messages" etc
4650 Should possibly be internationalized
4652 static int get_folder(struct ast_channel *chan, int start)
4654 int x;
4655 int d;
4656 char fn[PATH_MAX];
4657 d = ast_play_and_wait(chan, "vm-press"); /* "Press" */
4658 if (d)
4659 return d;
4660 for (x = start; x< 5; x++) { /* For all folders */
4661 if ((d = ast_say_number(chan, x, AST_DIGIT_ANY, chan->language, (char *) NULL)))
4662 return d;
4663 d = ast_play_and_wait(chan, "vm-for"); /* "for" */
4664 if (d)
4665 return d;
4666 snprintf(fn, sizeof(fn), "vm-%s", mbox(x)); /* Folder name */
4667 d = vm_play_folder_name(chan, fn);
4668 if (d)
4669 return d;
4670 d = ast_waitfordigit(chan, 500);
4671 if (d)
4672 return d;
4674 d = ast_play_and_wait(chan, "vm-tocancel"); /* "or pound to cancel" */
4675 if (d)
4676 return d;
4677 d = ast_waitfordigit(chan, 4000);
4678 return d;
4681 static int get_folder2(struct ast_channel *chan, char *fn, int start)
4683 int res = 0;
4684 res = ast_play_and_wait(chan, fn); /* Folder name */
4685 while (((res < '0') || (res > '9')) &&
4686 (res != '#') && (res >= 0)) {
4687 res = get_folder(chan, 0);
4689 return res;
4692 static int vm_forwardoptions(struct ast_channel *chan, struct ast_vm_user *vmu, char *curdir, int curmsg, char *vmfmts,
4693 char *context, signed char record_gain, long *duration, struct vm_state *vms)
4695 int cmd = 0;
4696 int retries = 0, prepend_duration = 0, already_recorded = 0;
4697 signed char zero_gain = 0;
4698 struct ast_config *msg_cfg;
4699 const char *duration_str;
4700 char msgfile[PATH_MAX], backup[PATH_MAX];
4701 char textfile[PATH_MAX];
4703 /* Must always populate duration correctly */
4704 make_file(msgfile, sizeof(msgfile), curdir, curmsg);
4705 strcpy(textfile, msgfile);
4706 strcpy(backup, msgfile);
4707 strncat(textfile, ".txt", sizeof(textfile) - strlen(textfile) - 1);
4708 strncat(backup, "-bak", sizeof(backup) - strlen(backup) - 1);
4710 if (!(msg_cfg = ast_config_load(textfile))) {
4711 return -1;
4714 *duration = 0;
4715 if ((duration_str = ast_variable_retrieve(msg_cfg, "message", "duration")))
4716 *duration = atoi(duration_str);
4718 while ((cmd >= 0) && (cmd != 't') && (cmd != '*')) {
4719 if (cmd)
4720 retries = 0;
4721 switch (cmd) {
4722 case '1':
4723 /* prepend a message to the current message, update the metadata and return */
4725 prepend_duration = 0;
4727 /* if we can't read the message metadata, stop now */
4728 if (!msg_cfg) {
4729 cmd = 0;
4730 break;
4733 /* Back up the original file, so we can retry the prepend */
4734 if (already_recorded)
4735 ast_filecopy(backup, msgfile, NULL);
4736 else
4737 ast_filecopy(msgfile, backup, NULL);
4738 already_recorded = 1;
4740 if (record_gain)
4741 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
4743 cmd = ast_play_and_prepend(chan, NULL, msgfile, 0, vmfmts, &prepend_duration, 1, silencethreshold, maxsilence);
4744 if (record_gain)
4745 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
4747 if (prepend_duration) {
4748 struct ast_category *msg_cat;
4749 /* need enough space for a maximum-length message duration */
4750 char duration_str[12];
4752 prepend_duration += *duration;
4753 msg_cat = ast_category_get(msg_cfg, "message");
4754 snprintf(duration_str, 11, "%d", prepend_duration);
4755 if (!ast_variable_update(msg_cat, "duration", duration_str, NULL, 0)) {
4756 config_text_file_save(textfile, msg_cfg, "app_voicemail");
4760 break;
4762 case '2':
4763 cmd = 't';
4764 break;
4765 case '*':
4766 cmd = '*';
4767 break;
4768 default:
4769 cmd = ast_play_and_wait(chan,"vm-forwardoptions");
4770 /* "Press 1 to prepend a message or 2 to forward the message without prepending" */
4771 if (!cmd)
4772 cmd = ast_play_and_wait(chan,"vm-starmain");
4773 /* "press star to return to the main menu" */
4774 if (!cmd)
4775 cmd = ast_waitfordigit(chan,6000);
4776 if (!cmd)
4777 retries++;
4778 if (retries > 3)
4779 cmd = 't';
4783 ast_config_destroy(msg_cfg);
4784 if (already_recorded)
4785 ast_filedelete(backup, NULL);
4786 if (prepend_duration)
4787 *duration = prepend_duration;
4789 if (cmd == 't' || cmd == 'S')
4790 cmd = 0;
4791 return cmd;
4794 static int notify_new_message(struct ast_channel *chan, struct ast_vm_user *vmu, int msgnum, long duration, char *fmt, char *cidnum, char *cidname)
4796 char todir[PATH_MAX], fn[PATH_MAX], ext_context[PATH_MAX], *stringp;
4797 int newmsgs = 0, oldmsgs = 0;
4798 const char *category = pbx_builtin_getvar_helper(chan, "VM_CATEGORY");
4800 make_dir(todir, sizeof(todir), vmu->context, vmu->mailbox, "INBOX");
4801 make_file(fn, sizeof(fn), todir, msgnum);
4802 snprintf(ext_context, sizeof(ext_context), "%s@%s", vmu->mailbox, vmu->context);
4804 if (!ast_strlen_zero(vmu->attachfmt)) {
4805 if (strstr(fmt, vmu->attachfmt)) {
4806 fmt = vmu->attachfmt;
4807 } else {
4808 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);
4812 /* Attach only the first format */
4813 fmt = ast_strdupa(fmt);
4814 stringp = fmt;
4815 strsep(&stringp, "|");
4817 if (!ast_strlen_zero(vmu->email)) {
4818 int attach_user_voicemail = ast_test_flag((&globalflags), VM_ATTACH);
4819 char *myserveremail = serveremail;
4820 attach_user_voicemail = ast_test_flag(vmu, VM_ATTACH);
4821 if (!ast_strlen_zero(vmu->serveremail))
4822 myserveremail = vmu->serveremail;
4824 if (attach_user_voicemail)
4825 RETRIEVE(todir, msgnum, vmu);
4827 /*XXX possible imap issue, should category be NULL XXX*/
4828 sendmail(myserveremail, vmu, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, fn, fmt, duration, attach_user_voicemail, chan, category);
4830 if (attach_user_voicemail)
4831 DISPOSE(todir, msgnum);
4834 if (!ast_strlen_zero(vmu->pager)) {
4835 char *myserveremail = serveremail;
4836 if (!ast_strlen_zero(vmu->serveremail))
4837 myserveremail = vmu->serveremail;
4838 sendpage(myserveremail, vmu->pager, msgnum, vmu->context, vmu->mailbox, cidnum, cidname, duration, vmu, category);
4841 if (ast_test_flag(vmu, VM_DELETE)) {
4842 DELETE(todir, msgnum, fn, vmu);
4845 /* Leave voicemail for someone */
4846 if (ast_app_has_voicemail(ext_context, NULL)) {
4847 ast_app_inboxcount(ext_context, &newmsgs, &oldmsgs);
4849 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);
4850 run_externnotify(vmu->context, vmu->mailbox);
4851 return 0;
4854 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)
4856 #ifdef IMAP_STORAGE
4857 int todircount=0;
4858 struct vm_state *dstvms;
4859 #endif
4860 char username[70]="";
4861 int res = 0, cmd = 0;
4862 struct ast_vm_user *receiver = NULL, *vmtmp;
4863 AST_LIST_HEAD_NOLOCK_STATIC(extensions, ast_vm_user);
4864 char *stringp;
4865 const char *s;
4866 int saved_messages = 0, found = 0;
4867 int valid_extensions = 0;
4868 char *dir;
4869 int curmsg;
4871 if (vms == NULL) return -1;
4872 dir = vms->curdir;
4873 curmsg = vms->curmsg;
4875 while (!res && !valid_extensions) {
4876 int use_directory = 0;
4877 if (ast_test_flag((&globalflags), VM_DIRECFORWARD)) {
4878 int done = 0;
4879 int retries = 0;
4880 cmd=0;
4881 while ((cmd >= 0) && !done ){
4882 if (cmd)
4883 retries = 0;
4884 switch (cmd) {
4885 case '1':
4886 use_directory = 0;
4887 done = 1;
4888 break;
4889 case '2':
4890 use_directory = 1;
4891 done=1;
4892 break;
4893 case '*':
4894 cmd = 't';
4895 done = 1;
4896 break;
4897 default:
4898 /* Press 1 to enter an extension press 2 to use the directory */
4899 cmd = ast_play_and_wait(chan,"vm-forward");
4900 if (!cmd)
4901 cmd = ast_waitfordigit(chan,3000);
4902 if (!cmd)
4903 retries++;
4904 if (retries > 3)
4906 cmd = 't';
4907 done = 1;
4912 if (cmd < 0 || cmd == 't')
4913 break;
4916 if (use_directory) {
4917 /* use app_directory */
4919 char old_context[sizeof(chan->context)];
4920 char old_exten[sizeof(chan->exten)];
4921 int old_priority;
4922 struct ast_app* app;
4925 app = pbx_findapp("Directory");
4926 if (app) {
4927 char vmcontext[256];
4928 /* make backup copies */
4929 memcpy(old_context, chan->context, sizeof(chan->context));
4930 memcpy(old_exten, chan->exten, sizeof(chan->exten));
4931 old_priority = chan->priority;
4933 /* call the the Directory, changes the channel */
4934 snprintf(vmcontext, sizeof(vmcontext), "%s||v", context ? context : "default");
4935 res = pbx_exec(chan, app, vmcontext);
4937 ast_copy_string(username, chan->exten, sizeof(username));
4939 /* restore the old context, exten, and priority */
4940 memcpy(chan->context, old_context, sizeof(chan->context));
4941 memcpy(chan->exten, old_exten, sizeof(chan->exten));
4942 chan->priority = old_priority;
4944 } else {
4945 ast_log(LOG_WARNING, "Could not find the Directory application, disabling directory_forward\n");
4946 ast_clear_flag((&globalflags), VM_DIRECFORWARD);
4948 } else {
4949 /* Ask for an extension */
4950 res = ast_streamfile(chan, "vm-extension", chan->language); /* "extension" */
4951 if (res)
4952 break;
4953 if ((res = ast_readstring(chan, username, sizeof(username) - 1, 2000, 10000, "#") < 0))
4954 break;
4957 /* start all over if no username */
4958 if (ast_strlen_zero(username))
4959 continue;
4960 stringp = username;
4961 s = strsep(&stringp, "*");
4962 /* start optimistic */
4963 valid_extensions = 1;
4964 while (s) {
4965 /* 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 */
4966 if ((flag == 1 || strcmp(s,sender->mailbox)) && (receiver = find_user(NULL, context, s))) {
4967 AST_LIST_INSERT_HEAD(&extensions, receiver, list);
4968 found++;
4969 } else {
4970 valid_extensions = 0;
4971 break;
4973 s = strsep(&stringp, "*");
4975 /* break from the loop of reading the extensions */
4976 if (valid_extensions)
4977 break;
4978 /* "I am sorry, that's not a valid extension. Please try again." */
4979 res = ast_play_and_wait(chan, "pbx-invalid");
4981 /* check if we're clear to proceed */
4982 if (AST_LIST_EMPTY(&extensions) || !valid_extensions)
4983 return res;
4984 if (flag==1) {
4985 struct leave_vm_options leave_options;
4986 char mailbox[AST_MAX_EXTENSION * 2 + 2];
4987 /* Make sure that context doesn't get set as a literal "(null)" (or else find_user won't find it) */
4988 if (context)
4989 snprintf(mailbox, sizeof(mailbox), "%s@%s", username, context);
4990 else
4991 ast_copy_string(mailbox, username, sizeof(mailbox));
4993 /* Send VoiceMail */
4994 memset(&leave_options, 0, sizeof(leave_options));
4995 leave_options.record_gain = record_gain;
4996 cmd = leave_voicemail(chan, mailbox, &leave_options);
4997 } else {
4998 /* Forward VoiceMail */
4999 long duration = 0;
5000 char origmsgfile[PATH_MAX], msgfile[PATH_MAX];
5001 struct vm_state vmstmp;
5003 memcpy(&vmstmp, vms, sizeof(vmstmp));
5005 make_file(origmsgfile, sizeof(origmsgfile), dir, curmsg);
5006 create_dirpath(vmstmp.curdir, sizeof(vmstmp.curdir), sender->context, vmstmp.username, "tmp");
5007 make_file(msgfile, sizeof(msgfile), vmstmp.curdir, curmsg);
5009 RETRIEVE(dir, curmsg, sender);
5011 /* Alter a surrogate file, only */
5012 copy_plain_file(origmsgfile, msgfile);
5014 cmd = vm_forwardoptions(chan, sender, vmstmp.curdir, curmsg, vmfmts, S_OR(context, "default"), record_gain, &duration, &vmstmp);
5015 if (!cmd) {
5016 AST_LIST_TRAVERSE_SAFE_BEGIN(&extensions, vmtmp, list) {
5017 #ifdef IMAP_STORAGE
5018 char *myserveremail;
5019 int attach_user_voicemail;
5020 /* get destination mailbox */
5021 dstvms = get_vm_state_by_mailbox(vmtmp->mailbox, vmtmp->context, 0);
5022 if (!dstvms) {
5023 dstvms = create_vm_state_from_user(vmtmp);
5025 if (dstvms) {
5026 init_mailstream(dstvms, 0);
5027 if (!dstvms->mailstream) {
5028 ast_log (LOG_ERROR,"IMAP mailstream for %s is NULL\n",vmtmp->mailbox);
5029 } else {
5030 STORE(vmstmp.curdir, vmtmp->mailbox, vmtmp->context, dstvms->curmsg, chan, vmtmp, fmt, duration, dstvms);
5031 run_externnotify(vmtmp->context, vmtmp->mailbox);
5033 } else {
5034 ast_log (LOG_ERROR,"Could not find state information for mailbox %s\n",vmtmp->mailbox);
5036 myserveremail = serveremail;
5037 if (!ast_strlen_zero(vmtmp->serveremail))
5038 myserveremail = vmtmp->serveremail;
5039 attach_user_voicemail = ast_test_flag(vmtmp, VM_ATTACH);
5040 /* NULL category for IMAP storage */
5041 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);
5042 #else
5043 copy_message(chan, sender, -1, curmsg, duration, vmtmp, fmt, vmstmp.curdir);
5044 #endif
5045 saved_messages++;
5046 AST_LIST_REMOVE_CURRENT(&extensions, list);
5047 free_user(vmtmp);
5048 if (res)
5049 break;
5051 AST_LIST_TRAVERSE_SAFE_END;
5052 if (saved_messages > 0) {
5053 /* give confirmation that the message was saved */
5054 /* commented out since we can't forward batches yet
5055 if (saved_messages == 1)
5056 res = ast_play_and_wait(chan, "vm-message");
5057 else
5058 res = ast_play_and_wait(chan, "vm-messages");
5059 if (!res)
5060 res = ast_play_and_wait(chan, "vm-saved"); */
5061 res = ast_play_and_wait(chan, "vm-msgsaved");
5065 /* Remove surrogate file */
5066 vm_delete(msgfile);
5067 DISPOSE(dir, curmsg);
5070 /* If anything failed above, we still have this list to free */
5071 while ((vmtmp = AST_LIST_REMOVE_HEAD(&extensions, list)))
5072 free_user(vmtmp);
5073 return res ? res : cmd;
5076 static int wait_file2(struct ast_channel *chan, struct vm_state *vms, char *file)
5078 int res;
5079 if ((res = ast_stream_and_wait(chan, file, chan->language, AST_DIGIT_ANY)) < 0)
5080 ast_log(LOG_WARNING, "Unable to play message %s\n", file);
5081 return res;
5084 static int wait_file(struct ast_channel *chan, struct vm_state *vms, char *file)
5086 return ast_control_streamfile(chan, file, "#", "*", "1456789", "0", "2", skipms);
5089 static int play_message_category(struct ast_channel *chan, const char *category)
5091 int res = 0;
5093 if (!ast_strlen_zero(category))
5094 res = ast_play_and_wait(chan, category);
5096 if (res) {
5097 ast_log(LOG_WARNING, "No sound file for category '%s' was found.\n", category);
5098 res = 0;
5101 return res;
5104 static int play_message_datetime(struct ast_channel *chan, struct ast_vm_user *vmu, const char *origtime, const char *filename)
5106 int res = 0;
5107 struct vm_zone *the_zone = NULL;
5108 time_t t;
5110 if (ast_get_time_t(origtime, &t, 0, NULL)) {
5111 ast_log(LOG_WARNING, "Couldn't find origtime in %s\n", filename);
5112 return 0;
5115 /* Does this user have a timezone specified? */
5116 if (!ast_strlen_zero(vmu->zonetag)) {
5117 /* Find the zone in the list */
5118 struct vm_zone *z;
5119 AST_LIST_LOCK(&zones);
5120 AST_LIST_TRAVERSE(&zones, z, list) {
5121 if (!strcmp(z->name, vmu->zonetag)) {
5122 the_zone = z;
5123 break;
5126 AST_LIST_UNLOCK(&zones);
5129 /* No internal variable parsing for now, so we'll comment it out for the time being */
5130 #if 0
5131 /* Set the DIFF_* variables */
5132 ast_localtime(&t, &time_now, NULL);
5133 tv_now = ast_tvnow();
5134 tnow = tv_now.tv_sec;
5135 ast_localtime(&tnow, &time_then, NULL);
5137 /* Day difference */
5138 if (time_now.tm_year == time_then.tm_year)
5139 snprintf(temp,sizeof(temp),"%d",time_now.tm_yday);
5140 else
5141 snprintf(temp,sizeof(temp),"%d",(time_now.tm_year - time_then.tm_year) * 365 + (time_now.tm_yday - time_then.tm_yday));
5142 pbx_builtin_setvar_helper(chan, "DIFF_DAY", temp);
5144 /* Can't think of how other diffs might be helpful, but I'm sure somebody will think of something. */
5145 #endif
5146 if (the_zone)
5147 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, the_zone->msg_format, the_zone->timezone);
5148 else if (!strcasecmp(chan->language,"pl")) /* POLISH syntax */
5149 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q HM", NULL);
5150 else if (!strcasecmp(chan->language,"se")) /* SWEDISH syntax */
5151 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' dB 'digits/at' k 'and' M", NULL);
5152 else if (!strcasecmp(chan->language,"no")) /* NORWEGIAN syntax */
5153 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
5154 else if (!strcasecmp(chan->language,"de")) /* GERMAN syntax */
5155 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' Q 'digits/at' HM", NULL);
5156 else if (!strcasecmp(chan->language,"nl")) /* DUTCH syntax */
5157 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/nl-om' HM", NULL);
5158 else if (!strcasecmp(chan->language,"it")) /* ITALIAN syntax */
5159 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);
5160 else if (!strcasecmp(chan->language,"gr"))
5161 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q H 'digits/kai' M ", NULL);
5162 else if (!strcasecmp(chan->language,"pt_BR"))
5163 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);
5164 else
5165 res = ast_say_date_with_format(chan, t, AST_DIGIT_ANY, chan->language, "'vm-received' q 'digits/at' IMp", NULL);
5166 #if 0
5167 pbx_builtin_setvar_helper(chan, "DIFF_DAY", NULL);
5168 #endif
5169 return res;
5174 static int play_message_callerid(struct ast_channel *chan, struct vm_state *vms, char *cid, const char *context, int callback)
5176 int res = 0;
5177 int i;
5178 char *callerid, *name;
5179 char prefile[PATH_MAX] = "";
5182 /* If voicemail cid is not enabled, or we didn't get cid or context from the attribute file, leave now. */
5183 /* 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 */
5184 if ((cid == NULL)||(context == NULL))
5185 return res;
5187 /* Strip off caller ID number from name */
5188 if (option_debug > 2)
5189 ast_log(LOG_DEBUG, "VM-CID: composite caller ID received: %s, context: %s\n", cid, context);
5190 ast_callerid_parse(cid, &name, &callerid);
5191 if ((!ast_strlen_zero(callerid)) && strcmp(callerid, "Unknown")) {
5192 /* Check for internal contexts and only */
5193 /* say extension when the call didn't come from an internal context in the list */
5194 for (i = 0 ; i < MAX_NUM_CID_CONTEXTS ; i++){
5195 if (option_debug > 2)
5196 ast_log(LOG_DEBUG, "VM-CID: comparing internalcontext: %s\n", cidinternalcontexts[i]);
5197 if ((strcmp(cidinternalcontexts[i], context) == 0))
5198 break;
5200 if (i != MAX_NUM_CID_CONTEXTS){ /* internal context? */
5201 if (!res) {
5202 snprintf(prefile, sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, context, callerid);
5203 if (!ast_strlen_zero(prefile)) {
5204 /* See if we can find a recorded name for this person instead of their extension number */
5205 if (ast_fileexists(prefile, NULL, NULL) > 0) {
5206 if (option_verbose > 2)
5207 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: CID number '%s' matches mailbox number, playing recorded name\n", callerid);
5208 if (!callback)
5209 res = wait_file2(chan, vms, "vm-from");
5210 res = ast_stream_and_wait(chan, prefile, chan->language, "");
5211 } else {
5212 if (option_verbose > 2)
5213 ast_verbose(VERBOSE_PREFIX_3 "Playing envelope info: message from '%s'\n", callerid);
5214 /* BB: Say "from extension" as one saying to sound smoother */
5215 if (!callback)
5216 res = wait_file2(chan, vms, "vm-from-extension");
5217 res = ast_say_digit_str(chan, callerid, "", chan->language);
5223 else if (!res){
5224 if (option_debug > 2)
5225 ast_log(LOG_DEBUG, "VM-CID: Numeric caller id: (%s)\n",callerid);
5226 /* BB: Since this is all nicely figured out, why not say "from phone number" in this case" */
5227 if (!callback)
5228 res = wait_file2(chan, vms, "vm-from-phonenumber");
5229 res = ast_say_digit_str(chan, callerid, AST_DIGIT_ANY, chan->language);
5231 } else {
5232 /* Number unknown */
5233 if (option_debug)
5234 ast_log(LOG_DEBUG, "VM-CID: From an unknown number\n");
5235 /* Say "from an unknown caller" as one phrase - it is already recorded by "the voice" anyhow */
5236 res = wait_file2(chan, vms, "vm-unknown-caller");
5238 return res;
5241 static int play_message_duration(struct ast_channel *chan, struct vm_state *vms, const char *duration, int minduration)
5243 int res = 0;
5244 int durationm;
5245 int durations;
5246 /* Verify that we have a duration for the message */
5247 if (duration == NULL)
5248 return res;
5250 /* Convert from seconds to minutes */
5251 durations=atoi(duration);
5252 durationm=(durations / 60);
5254 if (option_debug > 2)
5255 ast_log(LOG_DEBUG, "VM-Duration: duration is: %d seconds converted to: %d minutes\n", durations, durationm);
5257 if ((!res) && (durationm >= minduration)) {
5258 res = wait_file2(chan, vms, "vm-duration");
5260 /* POLISH syntax */
5261 if (!strcasecmp(chan->language, "pl")) {
5262 div_t num = div(durationm, 10);
5264 if (durationm == 1) {
5265 res = ast_play_and_wait(chan, "digits/1z");
5266 res = res ? res : ast_play_and_wait(chan, "vm-minute-ta");
5267 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5268 if (num.rem == 2) {
5269 if (!num.quot) {
5270 res = ast_play_and_wait(chan, "digits/2-ie");
5271 } else {
5272 res = say_and_wait(chan, durationm - 2 , chan->language);
5273 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5275 } else {
5276 res = say_and_wait(chan, durationm, chan->language);
5278 res = res ? res : ast_play_and_wait(chan, "vm-minute-ty");
5279 } else {
5280 res = say_and_wait(chan, durationm, chan->language);
5281 res = res ? res : ast_play_and_wait(chan, "vm-minute-t");
5283 /* DEFAULT syntax */
5284 } else {
5285 res = ast_say_number(chan, durationm, AST_DIGIT_ANY, chan->language, NULL);
5286 res = wait_file2(chan, vms, "vm-minutes");
5289 return res;
5292 static int play_message(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
5294 int res = 0;
5295 char filename[256], *cid;
5296 const char *origtime, *context, *category, *duration;
5297 struct ast_config *msg_cfg;
5299 vms->starting = 0;
5300 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
5301 adsi_message(chan, vms);
5302 if (!vms->curmsg)
5303 res = wait_file2(chan, vms, "vm-first"); /* "First" */
5304 else if (vms->curmsg == vms->lastmsg)
5305 res = wait_file2(chan, vms, "vm-last"); /* "last" */
5306 if (!res) {
5307 /* POLISH syntax */
5308 if (!strcasecmp(chan->language, "pl")) {
5309 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
5310 int ten, one;
5311 char nextmsg[256];
5312 ten = (vms->curmsg + 1) / 10;
5313 one = (vms->curmsg + 1) % 10;
5315 if (vms->curmsg < 20) {
5316 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", vms->curmsg + 1);
5317 res = wait_file2(chan, vms, nextmsg);
5318 } else {
5319 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", ten * 10);
5320 res = wait_file2(chan, vms, nextmsg);
5321 if (one > 0) {
5322 if (!res) {
5323 snprintf(nextmsg, sizeof(nextmsg), "digits/n-%d", one);
5324 res = wait_file2(chan, vms, nextmsg);
5329 if (!res)
5330 res = wait_file2(chan, vms, "vm-message");
5331 } else {
5332 if (!strcasecmp(chan->language, "se")) /* SWEDISH syntax */
5333 res = wait_file2(chan, vms, "vm-meddelandet"); /* "message" */
5334 else /* DEFAULT syntax */
5335 res = wait_file2(chan, vms, "vm-message");
5336 if (vms->curmsg && (vms->curmsg != vms->lastmsg)) {
5337 if (!res)
5338 res = ast_say_number(chan, vms->curmsg + 1, AST_DIGIT_ANY, chan->language, NULL);
5343 /* Retrieve info from VM attribute file */
5344 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
5345 snprintf(filename, sizeof(filename), "%s.txt", vms->fn2);
5346 RETRIEVE(vms->curdir, vms->curmsg, vmu);
5347 msg_cfg = ast_config_load(filename);
5348 if (!msg_cfg) {
5349 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
5350 return 0;
5353 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
5354 ast_log(LOG_WARNING, "No origtime?!\n");
5355 DISPOSE(vms->curdir, vms->curmsg);
5356 ast_config_destroy(msg_cfg);
5357 return 0;
5360 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
5361 duration = ast_variable_retrieve(msg_cfg, "message", "duration");
5362 category = ast_variable_retrieve(msg_cfg, "message", "category");
5364 context = ast_variable_retrieve(msg_cfg, "message", "context");
5365 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
5366 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
5367 if (!res)
5368 res = play_message_category(chan, category);
5369 if ((!res) && (ast_test_flag(vmu, VM_ENVELOPE)))
5370 res = play_message_datetime(chan, vmu, origtime, filename);
5371 if ((!res) && (ast_test_flag(vmu, VM_SAYCID)))
5372 res = play_message_callerid(chan, vms, cid, context, 0);
5373 if ((!res) && (ast_test_flag(vmu, VM_SAYDURATION)))
5374 res = play_message_duration(chan, vms, duration, vmu->saydurationm);
5375 /* Allow pressing '1' to skip envelope / callerid */
5376 if (res == '1')
5377 res = 0;
5378 ast_config_destroy(msg_cfg);
5380 if (!res) {
5381 make_file(vms->fn, sizeof(vms->fn), vms->curdir, vms->curmsg);
5382 vms->heard[vms->curmsg] = 1;
5383 if ((res = wait_file(chan, vms, vms->fn)) < 0) {
5384 ast_log(LOG_WARNING, "Playback of message %s failed\n", vms->fn);
5385 res = 0;
5388 DISPOSE(vms->curdir, vms->curmsg);
5389 return res;
5392 #ifndef IMAP_STORAGE
5393 static int open_mailbox(struct vm_state *vms, struct ast_vm_user *vmu,int box)
5395 int res = 0;
5396 int count_msg, last_msg;
5398 ast_copy_string(vms->curbox, mbox(box), sizeof(vms->curbox));
5400 /* Rename the member vmbox HERE so that we don't try to return before
5401 * we know what's going on.
5403 snprintf(vms->vmbox, sizeof(vms->vmbox), "vm-%s", vms->curbox);
5405 /* Faster to make the directory than to check if it exists. */
5406 create_dirpath(vms->curdir, sizeof(vms->curdir), vmu->context, vms->username, vms->curbox);
5408 count_msg = count_messages(vmu, vms->curdir);
5409 if (count_msg < 0)
5410 return count_msg;
5411 else
5412 vms->lastmsg = count_msg - 1;
5415 The following test is needed in case sequencing gets messed up.
5416 There appears to be more than one way to mess up sequence, so
5417 we will not try to find all of the root causes--just fix it when
5418 detected.
5421 last_msg = last_message_index(vmu, vms->curdir);
5422 if (last_msg < 0)
5423 return last_msg;
5424 else if (vms->lastmsg != last_msg)
5426 ast_log(LOG_NOTICE, "Resequencing Mailbox: %s\n", vms->curdir);
5427 res = resequence_mailbox(vmu, vms->curdir);
5428 if (res)
5429 return res;
5432 return 0;
5434 #endif
5436 static int close_mailbox(struct vm_state *vms, struct ast_vm_user *vmu)
5438 int x = 0;
5439 #ifndef IMAP_STORAGE
5440 int res = 0, nummsg;
5441 #endif
5443 if (vms->lastmsg <= -1)
5444 goto done;
5446 vms->curmsg = -1;
5447 #ifndef IMAP_STORAGE
5448 /* Get the deleted messages fixed */
5449 if (vm_lock_path(vms->curdir))
5450 return ERROR_LOCK_PATH;
5452 for (x = 0; x < vmu->maxmsg; x++) {
5453 if (!vms->deleted[x] && (strcasecmp(vms->curbox, "INBOX") || !vms->heard[x])) {
5454 /* Save this message. It's not in INBOX or hasn't been heard */
5455 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
5456 if (!EXISTS(vms->curdir, x, vms->fn, NULL))
5457 break;
5458 vms->curmsg++;
5459 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
5460 if (strcmp(vms->fn, vms->fn2)) {
5461 RENAME(vms->curdir, x, vmu->mailbox,vmu->context, vms->curdir, vms->curmsg, vms->fn, vms->fn2);
5463 } else if (!strcasecmp(vms->curbox, "INBOX") && vms->heard[x] && !vms->deleted[x]) {
5464 /* Move to old folder before deleting */
5465 res = save_to_folder(vmu, vms, x, 1);
5466 if (res == ERROR_LOCK_PATH || res == ERROR_MAILBOX_FULL) {
5467 /* If save failed do not delete the message */
5468 ast_log(LOG_WARNING, "Save failed. Not moving message: %s.\n", res == ERROR_LOCK_PATH ? "unable to lock path" : "destination folder full");
5469 vms->deleted[x] = 0;
5470 vms->heard[x] = 0;
5471 --x;
5476 /* Delete ALL remaining messages */
5477 nummsg = x - 1;
5478 for (x = vms->curmsg + 1; x <= nummsg; x++) {
5479 make_file(vms->fn, sizeof(vms->fn), vms->curdir, x);
5480 if (EXISTS(vms->curdir, x, vms->fn, NULL))
5481 DELETE(vms->curdir, x, vms->fn, vmu);
5483 ast_unlock_path(vms->curdir);
5484 #else
5485 if (vms->deleted) {
5486 for (x=0;x < vmu->maxmsg;x++) {
5487 if (vms->deleted[x]) {
5488 if (option_debug > 2)
5489 ast_log(LOG_DEBUG,"IMAP delete of %d\n",x);
5490 DELETE(vms->curdir, x, vms->fn, vmu);
5494 #endif
5496 done:
5497 if (vms->deleted)
5498 memset(vms->deleted, 0, vmu->maxmsg * sizeof(int));
5499 if (vms->heard)
5500 memset(vms->heard, 0, vmu->maxmsg * sizeof(int));
5502 return 0;
5505 /* In Greek even though we CAN use a syntax like "friends messages"
5506 * ("filika mynhmata") it is not elegant. This also goes for "work/family messages"
5507 * ("ergasiaka/oikogeniaka mynhmata"). Therefore it is better to use a reversed
5508 * syntax for the above three categories which is more elegant.
5511 static int vm_play_folder_name_gr(struct ast_channel *chan, char *mbox)
5513 int cmd;
5514 char *buf;
5516 buf = alloca(strlen(mbox)+2);
5517 strcpy(buf, mbox);
5518 strcat(buf,"s");
5520 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")){
5521 cmd = ast_play_and_wait(chan, buf); /* "NEA / PALIA" */
5522 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
5523 } else {
5524 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages" -> "MYNHMATA" */
5525 return cmd ? cmd : ast_play_and_wait(chan, mbox); /* friends/family/work... -> "FILWN"/"OIKOGENIAS"/"DOULEIAS"*/
5529 static int vm_play_folder_name_pl(struct ast_channel *chan, char *mbox)
5531 int cmd;
5533 if (!strcasecmp(mbox, "vm-INBOX") || !strcasecmp(mbox, "vm-Old")) {
5534 if (!strcasecmp(mbox, "vm-INBOX"))
5535 cmd = ast_play_and_wait(chan, "vm-new-e");
5536 else
5537 cmd = ast_play_and_wait(chan, "vm-old-e");
5538 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
5539 } else {
5540 cmd = ast_play_and_wait(chan, "vm-messages");
5541 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5545 static int vm_play_folder_name_ua(struct ast_channel *chan, char *mbox)
5547 int cmd;
5549 if (!strcasecmp(mbox, "vm-Family") || !strcasecmp(mbox, "vm-Friends") || !strcasecmp(mbox, "vm-Work")){
5550 cmd = ast_play_and_wait(chan, "vm-messages");
5551 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5552 } else {
5553 cmd = ast_play_and_wait(chan, mbox);
5554 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages");
5558 static int vm_play_folder_name(struct ast_channel *chan, char *mbox)
5560 int cmd;
5562 if (!strcasecmp(chan->language, "it") || !strcasecmp(chan->language, "es") || !strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* Italian, Spanish, French or Portuguese syntax */
5563 cmd = ast_play_and_wait(chan, "vm-messages"); /* "messages */
5564 return cmd ? cmd : ast_play_and_wait(chan, mbox);
5565 } else if (!strcasecmp(chan->language, "gr")){
5566 return vm_play_folder_name_gr(chan, mbox);
5567 } else if (!strcasecmp(chan->language, "pl")){
5568 return vm_play_folder_name_pl(chan, mbox);
5569 } else if (!strcasecmp(chan->language, "ua")){ /* Ukrainian syntax */
5570 return vm_play_folder_name_ua(chan, mbox);
5571 } else { /* Default English */
5572 cmd = ast_play_and_wait(chan, mbox);
5573 return cmd ? cmd : ast_play_and_wait(chan, "vm-messages"); /* "messages */
5577 /* GREEK SYNTAX
5578 In greek the plural for old/new is
5579 different so we need the following files
5580 We also need vm-denExeteMynhmata because
5581 this syntax is different.
5583 -> vm-Olds.wav : "Palia"
5584 -> vm-INBOXs.wav : "Nea"
5585 -> vm-denExeteMynhmata : "den exete mynhmata"
5589 static int vm_intro_gr(struct ast_channel *chan, struct vm_state *vms)
5591 int res = 0;
5593 if (vms->newmessages) {
5594 res = ast_play_and_wait(chan, "vm-youhave");
5595 if (!res)
5596 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, NULL);
5597 if (!res) {
5598 if ((vms->newmessages == 1)) {
5599 res = ast_play_and_wait(chan, "vm-INBOX");
5600 if (!res)
5601 res = ast_play_and_wait(chan, "vm-message");
5602 } else {
5603 res = ast_play_and_wait(chan, "vm-INBOXs");
5604 if (!res)
5605 res = ast_play_and_wait(chan, "vm-messages");
5608 } else if (vms->oldmessages){
5609 res = ast_play_and_wait(chan, "vm-youhave");
5610 if (!res)
5611 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, NULL);
5612 if ((vms->oldmessages == 1)){
5613 res = ast_play_and_wait(chan, "vm-Old");
5614 if (!res)
5615 res = ast_play_and_wait(chan, "vm-message");
5616 } else {
5617 res = ast_play_and_wait(chan, "vm-Olds");
5618 if (!res)
5619 res = ast_play_and_wait(chan, "vm-messages");
5621 } else if (!vms->oldmessages && !vms->newmessages)
5622 res = ast_play_and_wait(chan, "vm-denExeteMynhmata");
5623 return res;
5626 /* Default English syntax */
5627 static int vm_intro_en(struct ast_channel *chan, struct vm_state *vms)
5629 int res;
5631 /* Introduce messages they have */
5632 res = ast_play_and_wait(chan, "vm-youhave");
5633 if (!res) {
5634 if (vms->newmessages) {
5635 res = say_and_wait(chan, vms->newmessages, chan->language);
5636 if (!res)
5637 res = ast_play_and_wait(chan, "vm-INBOX");
5638 if (vms->oldmessages && !res)
5639 res = ast_play_and_wait(chan, "vm-and");
5640 else if (!res) {
5641 if ((vms->newmessages == 1))
5642 res = ast_play_and_wait(chan, "vm-message");
5643 else
5644 res = ast_play_and_wait(chan, "vm-messages");
5648 if (!res && vms->oldmessages) {
5649 res = say_and_wait(chan, vms->oldmessages, chan->language);
5650 if (!res)
5651 res = ast_play_and_wait(chan, "vm-Old");
5652 if (!res) {
5653 if (vms->oldmessages == 1)
5654 res = ast_play_and_wait(chan, "vm-message");
5655 else
5656 res = ast_play_and_wait(chan, "vm-messages");
5659 if (!res) {
5660 if (!vms->oldmessages && !vms->newmessages) {
5661 res = ast_play_and_wait(chan, "vm-no");
5662 if (!res)
5663 res = ast_play_and_wait(chan, "vm-messages");
5667 return res;
5670 /* ITALIAN syntax */
5671 static int vm_intro_it(struct ast_channel *chan, struct vm_state *vms)
5673 /* Introduce messages they have */
5674 int res;
5675 if (!vms->oldmessages && !vms->newmessages)
5676 res = ast_play_and_wait(chan, "vm-no") ||
5677 ast_play_and_wait(chan, "vm-message");
5678 else
5679 res = ast_play_and_wait(chan, "vm-youhave");
5680 if (!res && vms->newmessages) {
5681 res = (vms->newmessages == 1) ?
5682 ast_play_and_wait(chan, "digits/un") ||
5683 ast_play_and_wait(chan, "vm-nuovo") ||
5684 ast_play_and_wait(chan, "vm-message") :
5685 /* 2 or more new messages */
5686 say_and_wait(chan, vms->newmessages, chan->language) ||
5687 ast_play_and_wait(chan, "vm-nuovi") ||
5688 ast_play_and_wait(chan, "vm-messages");
5689 if (!res && vms->oldmessages)
5690 res = ast_play_and_wait(chan, "vm-and");
5692 if (!res && vms->oldmessages) {
5693 res = (vms->oldmessages == 1) ?
5694 ast_play_and_wait(chan, "digits/un") ||
5695 ast_play_and_wait(chan, "vm-vecchio") ||
5696 ast_play_and_wait(chan, "vm-message") :
5697 /* 2 or more old messages */
5698 say_and_wait(chan, vms->oldmessages, chan->language) ||
5699 ast_play_and_wait(chan, "vm-vecchi") ||
5700 ast_play_and_wait(chan, "vm-messages");
5702 return res ? -1 : 0;
5705 /* POLISH syntax */
5706 static int vm_intro_pl(struct ast_channel *chan, struct vm_state *vms)
5708 /* Introduce messages they have */
5709 int res;
5710 div_t num;
5712 if (!vms->oldmessages && !vms->newmessages) {
5713 res = ast_play_and_wait(chan, "vm-no");
5714 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5715 return res;
5716 } else {
5717 res = ast_play_and_wait(chan, "vm-youhave");
5720 if (vms->newmessages) {
5721 num = div(vms->newmessages, 10);
5722 if (vms->newmessages == 1) {
5723 res = ast_play_and_wait(chan, "digits/1-a");
5724 res = res ? res : ast_play_and_wait(chan, "vm-new-a");
5725 res = res ? res : ast_play_and_wait(chan, "vm-message");
5726 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5727 if (num.rem == 2) {
5728 if (!num.quot) {
5729 res = ast_play_and_wait(chan, "digits/2-ie");
5730 } else {
5731 res = say_and_wait(chan, vms->newmessages - 2 , chan->language);
5732 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5734 } else {
5735 res = say_and_wait(chan, vms->newmessages, chan->language);
5737 res = res ? res : ast_play_and_wait(chan, "vm-new-e");
5738 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5739 } else {
5740 res = say_and_wait(chan, vms->newmessages, chan->language);
5741 res = res ? res : ast_play_and_wait(chan, "vm-new-ych");
5742 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5744 if (!res && vms->oldmessages)
5745 res = ast_play_and_wait(chan, "vm-and");
5747 if (!res && vms->oldmessages) {
5748 num = div(vms->oldmessages, 10);
5749 if (vms->oldmessages == 1) {
5750 res = ast_play_and_wait(chan, "digits/1-a");
5751 res = res ? res : ast_play_and_wait(chan, "vm-old-a");
5752 res = res ? res : ast_play_and_wait(chan, "vm-message");
5753 } else if (num.rem > 1 && num.rem < 5 && num.quot != 1) {
5754 if (num.rem == 2) {
5755 if (!num.quot) {
5756 res = ast_play_and_wait(chan, "digits/2-ie");
5757 } else {
5758 res = say_and_wait(chan, vms->oldmessages - 2 , chan->language);
5759 res = res ? res : ast_play_and_wait(chan, "digits/2-ie");
5761 } else {
5762 res = say_and_wait(chan, vms->oldmessages, chan->language);
5764 res = res ? res : ast_play_and_wait(chan, "vm-old-e");
5765 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5766 } else {
5767 res = say_and_wait(chan, vms->oldmessages, chan->language);
5768 res = res ? res : ast_play_and_wait(chan, "vm-old-ych");
5769 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5773 return res;
5776 /* SWEDISH syntax */
5777 static int vm_intro_se(struct ast_channel *chan, struct vm_state *vms)
5779 /* Introduce messages they have */
5780 int res;
5782 res = ast_play_and_wait(chan, "vm-youhave");
5783 if (res)
5784 return res;
5786 if (!vms->oldmessages && !vms->newmessages) {
5787 res = ast_play_and_wait(chan, "vm-no");
5788 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5789 return res;
5792 if (vms->newmessages) {
5793 if ((vms->newmessages == 1)) {
5794 res = ast_play_and_wait(chan, "digits/ett");
5795 res = res ? res : ast_play_and_wait(chan, "vm-nytt");
5796 res = res ? res : ast_play_and_wait(chan, "vm-message");
5797 } else {
5798 res = say_and_wait(chan, vms->newmessages, chan->language);
5799 res = res ? res : ast_play_and_wait(chan, "vm-nya");
5800 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5802 if (!res && vms->oldmessages)
5803 res = ast_play_and_wait(chan, "vm-and");
5805 if (!res && vms->oldmessages) {
5806 if (vms->oldmessages == 1) {
5807 res = ast_play_and_wait(chan, "digits/ett");
5808 res = res ? res : ast_play_and_wait(chan, "vm-gammalt");
5809 res = res ? res : ast_play_and_wait(chan, "vm-message");
5810 } else {
5811 res = say_and_wait(chan, vms->oldmessages, chan->language);
5812 res = res ? res : ast_play_and_wait(chan, "vm-gamla");
5813 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5817 return res;
5820 /* NORWEGIAN syntax */
5821 static int vm_intro_no(struct ast_channel *chan,struct vm_state *vms)
5823 /* Introduce messages they have */
5824 int res;
5826 res = ast_play_and_wait(chan, "vm-youhave");
5827 if (res)
5828 return res;
5830 if (!vms->oldmessages && !vms->newmessages) {
5831 res = ast_play_and_wait(chan, "vm-no");
5832 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5833 return res;
5836 if (vms->newmessages) {
5837 if ((vms->newmessages == 1)) {
5838 res = ast_play_and_wait(chan, "digits/1");
5839 res = res ? res : ast_play_and_wait(chan, "vm-ny");
5840 res = res ? res : ast_play_and_wait(chan, "vm-message");
5841 } else {
5842 res = say_and_wait(chan, vms->newmessages, chan->language);
5843 res = res ? res : ast_play_and_wait(chan, "vm-nye");
5844 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5846 if (!res && vms->oldmessages)
5847 res = ast_play_and_wait(chan, "vm-and");
5849 if (!res && vms->oldmessages) {
5850 if (vms->oldmessages == 1) {
5851 res = ast_play_and_wait(chan, "digits/1");
5852 res = res ? res : ast_play_and_wait(chan, "vm-gamel");
5853 res = res ? res : ast_play_and_wait(chan, "vm-message");
5854 } else {
5855 res = say_and_wait(chan, vms->oldmessages, chan->language);
5856 res = res ? res : ast_play_and_wait(chan, "vm-gamle");
5857 res = res ? res : ast_play_and_wait(chan, "vm-messages");
5861 return res;
5864 /* GERMAN syntax */
5865 static int vm_intro_de(struct ast_channel *chan,struct vm_state *vms)
5867 /* Introduce messages they have */
5868 int res;
5869 res = ast_play_and_wait(chan, "vm-youhave");
5870 if (!res) {
5871 if (vms->newmessages) {
5872 if ((vms->newmessages == 1))
5873 res = ast_play_and_wait(chan, "digits/1F");
5874 else
5875 res = say_and_wait(chan, vms->newmessages, chan->language);
5876 if (!res)
5877 res = ast_play_and_wait(chan, "vm-INBOX");
5878 if (vms->oldmessages && !res)
5879 res = ast_play_and_wait(chan, "vm-and");
5880 else if (!res) {
5881 if ((vms->newmessages == 1))
5882 res = ast_play_and_wait(chan, "vm-message");
5883 else
5884 res = ast_play_and_wait(chan, "vm-messages");
5888 if (!res && vms->oldmessages) {
5889 if (vms->oldmessages == 1)
5890 res = ast_play_and_wait(chan, "digits/1F");
5891 else
5892 res = say_and_wait(chan, vms->oldmessages, chan->language);
5893 if (!res)
5894 res = ast_play_and_wait(chan, "vm-Old");
5895 if (!res) {
5896 if (vms->oldmessages == 1)
5897 res = ast_play_and_wait(chan, "vm-message");
5898 else
5899 res = ast_play_and_wait(chan, "vm-messages");
5902 if (!res) {
5903 if (!vms->oldmessages && !vms->newmessages) {
5904 res = ast_play_and_wait(chan, "vm-no");
5905 if (!res)
5906 res = ast_play_and_wait(chan, "vm-messages");
5910 return res;
5913 /* SPANISH syntax */
5914 static int vm_intro_es(struct ast_channel *chan,struct vm_state *vms)
5916 /* Introduce messages they have */
5917 int res;
5918 if (!vms->oldmessages && !vms->newmessages) {
5919 res = ast_play_and_wait(chan, "vm-youhaveno");
5920 if (!res)
5921 res = ast_play_and_wait(chan, "vm-messages");
5922 } else {
5923 res = ast_play_and_wait(chan, "vm-youhave");
5925 if (!res) {
5926 if (vms->newmessages) {
5927 if (!res) {
5928 if ((vms->newmessages == 1)) {
5929 res = ast_play_and_wait(chan, "digits/1M");
5930 if (!res)
5931 res = ast_play_and_wait(chan, "vm-message");
5932 if (!res)
5933 res = ast_play_and_wait(chan, "vm-INBOXs");
5934 } else {
5935 res = say_and_wait(chan, vms->newmessages, chan->language);
5936 if (!res)
5937 res = ast_play_and_wait(chan, "vm-messages");
5938 if (!res)
5939 res = ast_play_and_wait(chan, "vm-INBOX");
5942 if (vms->oldmessages && !res)
5943 res = ast_play_and_wait(chan, "vm-and");
5945 if (vms->oldmessages) {
5946 if (!res) {
5947 if (vms->oldmessages == 1) {
5948 res = ast_play_and_wait(chan, "digits/1M");
5949 if (!res)
5950 res = ast_play_and_wait(chan, "vm-message");
5951 if (!res)
5952 res = ast_play_and_wait(chan, "vm-Olds");
5953 } else {
5954 res = say_and_wait(chan, vms->oldmessages, chan->language);
5955 if (!res)
5956 res = ast_play_and_wait(chan, "vm-messages");
5957 if (!res)
5958 res = ast_play_and_wait(chan, "vm-Old");
5963 return res;
5966 /* BRAZILIAN PORTUGUESE syntax */
5967 static int vm_intro_pt_BR(struct ast_channel *chan,struct vm_state *vms) {
5968 /* Introduce messages they have */
5969 int res;
5970 if (!vms->oldmessages && !vms->newmessages) {
5971 res = ast_play_and_wait(chan, "vm-nomessages");
5972 return res;
5974 else {
5975 res = ast_play_and_wait(chan, "vm-youhave");
5977 if (vms->newmessages) {
5978 if (!res)
5979 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
5980 if ((vms->newmessages == 1)) {
5981 if (!res)
5982 res = ast_play_and_wait(chan, "vm-message");
5983 if (!res)
5984 res = ast_play_and_wait(chan, "vm-INBOXs");
5986 else {
5987 if (!res)
5988 res = ast_play_and_wait(chan, "vm-messages");
5989 if (!res)
5990 res = ast_play_and_wait(chan, "vm-INBOX");
5992 if (vms->oldmessages && !res)
5993 res = ast_play_and_wait(chan, "vm-and");
5995 if (vms->oldmessages) {
5996 if (!res)
5997 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
5998 if (vms->oldmessages == 1) {
5999 if (!res)
6000 res = ast_play_and_wait(chan, "vm-message");
6001 if (!res)
6002 res = ast_play_and_wait(chan, "vm-Olds");
6004 else {
6005 if (!res)
6006 res = ast_play_and_wait(chan, "vm-messages");
6007 if (!res)
6008 res = ast_play_and_wait(chan, "vm-Old");
6011 return res;
6014 /* FRENCH syntax */
6015 static int vm_intro_fr(struct ast_channel *chan,struct vm_state *vms)
6017 /* Introduce messages they have */
6018 int res;
6019 res = ast_play_and_wait(chan, "vm-youhave");
6020 if (!res) {
6021 if (vms->newmessages) {
6022 res = say_and_wait(chan, vms->newmessages, chan->language);
6023 if (!res)
6024 res = ast_play_and_wait(chan, "vm-INBOX");
6025 if (vms->oldmessages && !res)
6026 res = ast_play_and_wait(chan, "vm-and");
6027 else if (!res) {
6028 if ((vms->newmessages == 1))
6029 res = ast_play_and_wait(chan, "vm-message");
6030 else
6031 res = ast_play_and_wait(chan, "vm-messages");
6035 if (!res && vms->oldmessages) {
6036 res = say_and_wait(chan, vms->oldmessages, chan->language);
6037 if (!res)
6038 res = ast_play_and_wait(chan, "vm-Old");
6039 if (!res) {
6040 if (vms->oldmessages == 1)
6041 res = ast_play_and_wait(chan, "vm-message");
6042 else
6043 res = ast_play_and_wait(chan, "vm-messages");
6046 if (!res) {
6047 if (!vms->oldmessages && !vms->newmessages) {
6048 res = ast_play_and_wait(chan, "vm-no");
6049 if (!res)
6050 res = ast_play_and_wait(chan, "vm-messages");
6054 return res;
6057 /* DUTCH syntax */
6058 static int vm_intro_nl(struct ast_channel *chan,struct vm_state *vms)
6060 /* Introduce messages they have */
6061 int res;
6062 res = ast_play_and_wait(chan, "vm-youhave");
6063 if (!res) {
6064 if (vms->newmessages) {
6065 res = say_and_wait(chan, vms->newmessages, chan->language);
6066 if (!res) {
6067 if (vms->newmessages == 1)
6068 res = ast_play_and_wait(chan, "vm-INBOXs");
6069 else
6070 res = ast_play_and_wait(chan, "vm-INBOX");
6072 if (vms->oldmessages && !res)
6073 res = ast_play_and_wait(chan, "vm-and");
6074 else if (!res) {
6075 if ((vms->newmessages == 1))
6076 res = ast_play_and_wait(chan, "vm-message");
6077 else
6078 res = ast_play_and_wait(chan, "vm-messages");
6082 if (!res && vms->oldmessages) {
6083 res = say_and_wait(chan, vms->oldmessages, chan->language);
6084 if (!res) {
6085 if (vms->oldmessages == 1)
6086 res = ast_play_and_wait(chan, "vm-Olds");
6087 else
6088 res = ast_play_and_wait(chan, "vm-Old");
6090 if (!res) {
6091 if (vms->oldmessages == 1)
6092 res = ast_play_and_wait(chan, "vm-message");
6093 else
6094 res = ast_play_and_wait(chan, "vm-messages");
6097 if (!res) {
6098 if (!vms->oldmessages && !vms->newmessages) {
6099 res = ast_play_and_wait(chan, "vm-no");
6100 if (!res)
6101 res = ast_play_and_wait(chan, "vm-messages");
6105 return res;
6108 /* PORTUGUESE syntax */
6109 static int vm_intro_pt(struct ast_channel *chan,struct vm_state *vms)
6111 /* Introduce messages they have */
6112 int res;
6113 res = ast_play_and_wait(chan, "vm-youhave");
6114 if (!res) {
6115 if (vms->newmessages) {
6116 res = ast_say_number(chan, vms->newmessages, AST_DIGIT_ANY, chan->language, "f");
6117 if (!res) {
6118 if ((vms->newmessages == 1)) {
6119 res = ast_play_and_wait(chan, "vm-message");
6120 if (!res)
6121 res = ast_play_and_wait(chan, "vm-INBOXs");
6122 } else {
6123 res = ast_play_and_wait(chan, "vm-messages");
6124 if (!res)
6125 res = ast_play_and_wait(chan, "vm-INBOX");
6128 if (vms->oldmessages && !res)
6129 res = ast_play_and_wait(chan, "vm-and");
6131 if (!res && vms->oldmessages) {
6132 res = ast_say_number(chan, vms->oldmessages, AST_DIGIT_ANY, chan->language, "f");
6133 if (!res) {
6134 if (vms->oldmessages == 1) {
6135 res = ast_play_and_wait(chan, "vm-message");
6136 if (!res)
6137 res = ast_play_and_wait(chan, "vm-Olds");
6138 } else {
6139 res = ast_play_and_wait(chan, "vm-messages");
6140 if (!res)
6141 res = ast_play_and_wait(chan, "vm-Old");
6145 if (!res) {
6146 if (!vms->oldmessages && !vms->newmessages) {
6147 res = ast_play_and_wait(chan, "vm-no");
6148 if (!res)
6149 res = ast_play_and_wait(chan, "vm-messages");
6153 return res;
6157 /* CZECH syntax */
6158 /* in czech there must be declension of word new and message
6159 * czech : english : czech : english
6160 * --------------------------------------------------------
6161 * vm-youhave : you have
6162 * vm-novou : one new : vm-zpravu : message
6163 * vm-nove : 2-4 new : vm-zpravy : messages
6164 * vm-novych : 5-infinite new : vm-zprav : messages
6165 * vm-starou : one old
6166 * vm-stare : 2-4 old
6167 * vm-starych : 5-infinite old
6168 * jednu : one - falling 4.
6169 * vm-no : no ( no messages )
6172 static int vm_intro_cz(struct ast_channel *chan,struct vm_state *vms)
6174 int res;
6175 res = ast_play_and_wait(chan, "vm-youhave");
6176 if (!res) {
6177 if (vms->newmessages) {
6178 if (vms->newmessages == 1) {
6179 res = ast_play_and_wait(chan, "digits/jednu");
6180 } else {
6181 res = say_and_wait(chan, vms->newmessages, chan->language);
6183 if (!res) {
6184 if ((vms->newmessages == 1))
6185 res = ast_play_and_wait(chan, "vm-novou");
6186 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
6187 res = ast_play_and_wait(chan, "vm-nove");
6188 if (vms->newmessages > 4)
6189 res = ast_play_and_wait(chan, "vm-novych");
6191 if (vms->oldmessages && !res)
6192 res = ast_play_and_wait(chan, "vm-and");
6193 else if (!res) {
6194 if ((vms->newmessages == 1))
6195 res = ast_play_and_wait(chan, "vm-zpravu");
6196 if ((vms->newmessages) > 1 && (vms->newmessages < 5))
6197 res = ast_play_and_wait(chan, "vm-zpravy");
6198 if (vms->newmessages > 4)
6199 res = ast_play_and_wait(chan, "vm-zprav");
6202 if (!res && vms->oldmessages) {
6203 res = say_and_wait(chan, vms->oldmessages, chan->language);
6204 if (!res) {
6205 if ((vms->oldmessages == 1))
6206 res = ast_play_and_wait(chan, "vm-starou");
6207 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
6208 res = ast_play_and_wait(chan, "vm-stare");
6209 if (vms->oldmessages > 4)
6210 res = ast_play_and_wait(chan, "vm-starych");
6212 if (!res) {
6213 if ((vms->oldmessages == 1))
6214 res = ast_play_and_wait(chan, "vm-zpravu");
6215 if ((vms->oldmessages) > 1 && (vms->oldmessages < 5))
6216 res = ast_play_and_wait(chan, "vm-zpravy");
6217 if (vms->oldmessages > 4)
6218 res = ast_play_and_wait(chan, "vm-zprav");
6221 if (!res) {
6222 if (!vms->oldmessages && !vms->newmessages) {
6223 res = ast_play_and_wait(chan, "vm-no");
6224 if (!res)
6225 res = ast_play_and_wait(chan, "vm-zpravy");
6229 return res;
6232 static int get_lastdigits(int num)
6234 num %= 100;
6235 return (num < 20) ? num : num % 10;
6238 static int vm_intro_ru(struct ast_channel *chan,struct vm_state *vms)
6240 int res;
6241 int lastnum = 0;
6242 int dcnum;
6244 res = ast_play_and_wait(chan, "vm-youhave");
6245 if (!res && vms->newmessages) {
6246 lastnum = get_lastdigits(vms->newmessages);
6247 dcnum = vms->newmessages - lastnum;
6248 if (dcnum)
6249 res = say_and_wait(chan, dcnum, chan->language);
6250 if (!res && lastnum) {
6251 if (lastnum == 1)
6252 res = ast_play_and_wait(chan, "digits/odno");
6253 else
6254 res = say_and_wait(chan, lastnum, chan->language);
6257 if (!res)
6258 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-novoe" : "vm-novyh");
6260 if (!res && vms->oldmessages)
6261 res = ast_play_and_wait(chan, "vm-and");
6264 if (!res && vms->oldmessages) {
6265 lastnum = get_lastdigits(vms->oldmessages);
6266 dcnum = vms->oldmessages - lastnum;
6267 if (dcnum)
6268 res = say_and_wait(chan, dcnum, chan->language);
6269 if (!res && lastnum) {
6270 if (lastnum == 1)
6271 res = ast_play_and_wait(chan, "digits/odno");
6272 else
6273 res = say_and_wait(chan, lastnum, chan->language);
6276 if (!res)
6277 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-staroe" : "vm-staryh");
6280 if (!res && !vms->newmessages && !vms->oldmessages) {
6281 lastnum = 0;
6282 res = ast_play_and_wait(chan, "vm-no");
6285 if (!res) {
6286 switch (lastnum) {
6287 case 1:
6288 res = ast_play_and_wait(chan, "vm-soobshenie");
6289 break;
6290 case 2:
6291 case 3:
6292 case 4:
6293 res = ast_play_and_wait(chan, "vm-soobsheniya");
6294 break;
6295 default:
6296 res = ast_play_and_wait(chan, "vm-soobsheniy");
6297 break;
6301 return res;
6304 /* UKRAINIAN syntax */
6305 /* in ukrainian the syntax is different so we need the following files
6306 * --------------------------------------------------------
6307 * /digits/ua/1e 'odne'
6308 * vm-nove 'nove'
6309 * vm-stare 'stare'
6312 static int vm_intro_ua(struct ast_channel *chan,struct vm_state *vms)
6314 int res;
6315 int lastnum = 0;
6316 int dcnum;
6318 res = ast_play_and_wait(chan, "vm-youhave");
6319 if (!res && vms->newmessages) {
6320 lastnum = get_lastdigits(vms->newmessages);
6321 dcnum = vms->newmessages - lastnum;
6322 if (dcnum)
6323 res = say_and_wait(chan, dcnum, chan->language);
6324 if (!res && lastnum) {
6325 if (lastnum == 1)
6326 res = ast_play_and_wait(chan, "digits/ua/1e");
6327 else
6328 res = say_and_wait(chan, lastnum, chan->language);
6331 if (!res)
6332 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-nove" : "vm-INBOX");
6334 if (!res && vms->oldmessages)
6335 res = ast_play_and_wait(chan, "vm-and");
6338 if (!res && vms->oldmessages) {
6339 lastnum = get_lastdigits(vms->oldmessages);
6340 dcnum = vms->oldmessages - lastnum;
6341 if (dcnum)
6342 res = say_and_wait(chan, dcnum, chan->language);
6343 if (!res && lastnum) {
6344 if (lastnum == 1)
6345 res = ast_play_and_wait(chan, "digits/ua/1e");
6346 else
6347 res = say_and_wait(chan, lastnum, chan->language);
6350 if (!res)
6351 res = ast_play_and_wait(chan, (lastnum == 1) ? "vm-stare" : "vm-Old");
6354 if (!res && !vms->newmessages && !vms->oldmessages) {
6355 lastnum = 0;
6356 res = ast_play_and_wait(chan, "vm-no");
6359 if (!res) {
6360 switch (lastnum) {
6361 case 1:
6362 case 2:
6363 case 3:
6364 case 4:
6365 res = ast_play_and_wait(chan, "vm-message");
6366 break;
6367 default:
6368 res = ast_play_and_wait(chan, "vm-messages");
6369 break;
6373 return res;
6377 static int vm_intro(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms)
6379 char prefile[256];
6381 /* Notify the user that the temp greeting is set and give them the option to remove it */
6382 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
6383 if (ast_test_flag(vmu, VM_TEMPGREETWARN)) {
6384 if (ast_fileexists(prefile, NULL, NULL) > 0)
6385 ast_play_and_wait(chan, "vm-tempgreetactive");
6388 /* Play voicemail intro - syntax is different for different languages */
6389 if (!strcasecmp(chan->language, "de")) { /* GERMAN syntax */
6390 return vm_intro_de(chan, vms);
6391 } else if (!strcasecmp(chan->language, "es")) { /* SPANISH syntax */
6392 return vm_intro_es(chan, vms);
6393 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN syntax */
6394 return vm_intro_it(chan, vms);
6395 } else if (!strcasecmp(chan->language, "fr")) { /* FRENCH syntax */
6396 return vm_intro_fr(chan, vms);
6397 } else if (!strcasecmp(chan->language, "nl")) { /* DUTCH syntax */
6398 return vm_intro_nl(chan, vms);
6399 } else if (!strcasecmp(chan->language, "pt")) { /* PORTUGUESE syntax */
6400 return vm_intro_pt(chan, vms);
6401 } else if (!strcasecmp(chan->language, "pt_BR")) { /* BRAZILIAN PORTUGUESE syntax */
6402 return vm_intro_pt_BR(chan, vms);
6403 } else if (!strcasecmp(chan->language, "cz")) { /* CZECH syntax */
6404 return vm_intro_cz(chan, vms);
6405 } else if (!strcasecmp(chan->language, "gr")) { /* GREEK syntax */
6406 return vm_intro_gr(chan, vms);
6407 } else if (!strcasecmp(chan->language, "pl")) { /* POLISH syntax */
6408 return vm_intro_pl(chan, vms);
6409 } else if (!strcasecmp(chan->language, "se")) { /* SWEDISH syntax */
6410 return vm_intro_se(chan, vms);
6411 } else if (!strcasecmp(chan->language, "no")) { /* NORWEGIAN syntax */
6412 return vm_intro_no(chan, vms);
6413 } else if (!strcasecmp(chan->language, "ru")) { /* RUSSIAN syntax */
6414 return vm_intro_ru(chan, vms);
6415 } else if (!strcasecmp(chan->language, "ua")) { /* UKRAINIAN syntax */
6416 return vm_intro_ua(chan, vms);
6417 } else { /* Default to ENGLISH */
6418 return vm_intro_en(chan, vms);
6422 static int vm_instructions(struct ast_channel *chan, struct vm_state *vms, int skipadvanced)
6424 int res = 0;
6425 /* Play instructions and wait for new command */
6426 while (!res) {
6427 if (vms->starting) {
6428 if (vms->lastmsg > -1) {
6429 res = ast_play_and_wait(chan, "vm-onefor");
6430 if (!res)
6431 res = vm_play_folder_name(chan, vms->vmbox);
6433 if (!res)
6434 res = ast_play_and_wait(chan, "vm-opts");
6435 } else {
6436 if (vms->curmsg)
6437 res = ast_play_and_wait(chan, "vm-prev");
6438 if (!res && !skipadvanced)
6439 res = ast_play_and_wait(chan, "vm-advopts");
6440 if (!res)
6441 res = ast_play_and_wait(chan, "vm-repeat");
6442 if (!res && (vms->curmsg != vms->lastmsg))
6443 res = ast_play_and_wait(chan, "vm-next");
6444 if (!res) {
6445 if (!vms->deleted[vms->curmsg])
6446 res = ast_play_and_wait(chan, "vm-delete");
6447 else
6448 res = ast_play_and_wait(chan, "vm-undelete");
6449 if (!res)
6450 res = ast_play_and_wait(chan, "vm-toforward");
6451 if (!res)
6452 res = ast_play_and_wait(chan, "vm-savemessage");
6455 if (!res)
6456 res = ast_play_and_wait(chan, "vm-helpexit");
6457 if (!res)
6458 res = ast_waitfordigit(chan, 6000);
6459 if (!res) {
6460 vms->repeats++;
6461 if (vms->repeats > 2) {
6462 res = 't';
6466 return res;
6469 static int vm_newuser(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
6471 int cmd = 0;
6472 int duration = 0;
6473 int tries = 0;
6474 char newpassword[80] = "";
6475 char newpassword2[80] = "";
6476 char prefile[PATH_MAX] = "";
6477 unsigned char buf[256];
6478 int bytes=0;
6480 if (ast_adsi_available(chan)) {
6481 bytes += adsi_logo(buf + bytes);
6482 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "New User Setup", "");
6483 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
6484 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6485 bytes += ast_adsi_voice_mode(buf + bytes, 0);
6486 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6489 /* First, have the user change their password
6490 so they won't get here again */
6491 for (;;) {
6492 newpassword[1] = '\0';
6493 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
6494 if (cmd == '#')
6495 newpassword[0] = '\0';
6496 if (cmd < 0 || cmd == 't' || cmd == '#')
6497 return cmd;
6498 cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#");
6499 if (cmd < 0 || cmd == 't' || cmd == '#')
6500 return cmd;
6501 newpassword2[1] = '\0';
6502 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
6503 if (cmd == '#')
6504 newpassword2[0] = '\0';
6505 if (cmd < 0 || cmd == 't' || cmd == '#')
6506 return cmd;
6507 cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#");
6508 if (cmd < 0 || cmd == 't' || cmd == '#')
6509 return cmd;
6510 if (!strcmp(newpassword, newpassword2))
6511 break;
6512 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
6513 cmd = ast_play_and_wait(chan, "vm-mismatch");
6514 if (++tries == 3)
6515 return -1;
6517 if (ast_strlen_zero(ext_pass_cmd))
6518 vm_change_password(vmu,newpassword);
6519 else
6520 vm_change_password_shell(vmu,newpassword);
6521 if (option_debug > 2)
6522 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
6523 cmd = ast_play_and_wait(chan,"vm-passchanged");
6525 /* If forcename is set, have the user record their name */
6526 if (ast_test_flag(vmu, VM_FORCENAME)) {
6527 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
6528 if (ast_fileexists(prefile, NULL, NULL) < 1) {
6529 cmd = play_record_review(chan, "vm-rec-name", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6530 if (cmd < 0 || cmd == 't' || cmd == '#')
6531 return cmd;
6535 /* If forcegreetings is set, have the user record their greetings */
6536 if (ast_test_flag(vmu, VM_FORCEGREET)) {
6537 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
6538 if (ast_fileexists(prefile, NULL, NULL) < 1) {
6539 cmd = play_record_review(chan, "vm-rec-unv", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6540 if (cmd < 0 || cmd == 't' || cmd == '#')
6541 return cmd;
6544 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
6545 if (ast_fileexists(prefile, NULL, NULL) < 1) {
6546 cmd = play_record_review(chan, "vm-rec-busy", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6547 if (cmd < 0 || cmd == 't' || cmd == '#')
6548 return cmd;
6552 return cmd;
6555 static int vm_options(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
6557 int cmd = 0;
6558 int retries = 0;
6559 int duration = 0;
6560 char newpassword[80] = "";
6561 char newpassword2[80] = "";
6562 char prefile[PATH_MAX] = "";
6563 unsigned char buf[256];
6564 int bytes=0;
6566 if (ast_adsi_available(chan))
6568 bytes += adsi_logo(buf + bytes);
6569 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Options Menu", "");
6570 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
6571 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6572 bytes += ast_adsi_voice_mode(buf + bytes, 0);
6573 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6575 while ((cmd >= 0) && (cmd != 't')) {
6576 if (cmd)
6577 retries = 0;
6578 switch (cmd) {
6579 case '1':
6580 snprintf(prefile,sizeof(prefile), "%s%s/%s/unavail", VM_SPOOL_DIR, vmu->context, vms->username);
6581 cmd = play_record_review(chan,"vm-rec-unv",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6582 break;
6583 case '2':
6584 snprintf(prefile,sizeof(prefile), "%s%s/%s/busy", VM_SPOOL_DIR, vmu->context, vms->username);
6585 cmd = play_record_review(chan,"vm-rec-busy",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6586 break;
6587 case '3':
6588 snprintf(prefile,sizeof(prefile), "%s%s/%s/greet", VM_SPOOL_DIR, vmu->context, vms->username);
6589 cmd = play_record_review(chan,"vm-rec-name",prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6590 break;
6591 case '4':
6592 cmd = vm_tempgreeting(chan, vmu, vms, fmtc, record_gain);
6593 break;
6594 case '5':
6595 if (vmu->password[0] == '-') {
6596 cmd = ast_play_and_wait(chan, "vm-no");
6597 break;
6599 newpassword[1] = '\0';
6600 newpassword[0] = cmd = ast_play_and_wait(chan,"vm-newpassword");
6601 if (cmd == '#')
6602 newpassword[0] = '\0';
6603 else {
6604 if (cmd < 0)
6605 break;
6606 if ((cmd = ast_readstring(chan,newpassword + strlen(newpassword),sizeof(newpassword)-1,2000,10000,"#")) < 0) {
6607 break;
6610 newpassword2[1] = '\0';
6611 newpassword2[0] = cmd = ast_play_and_wait(chan,"vm-reenterpassword");
6612 if (cmd == '#')
6613 newpassword2[0] = '\0';
6614 else {
6615 if (cmd < 0)
6616 break;
6618 if ((cmd = ast_readstring(chan,newpassword2 + strlen(newpassword2),sizeof(newpassword2)-1,2000,10000,"#"))) {
6619 break;
6622 if (strcmp(newpassword, newpassword2)) {
6623 ast_log(LOG_NOTICE,"Password mismatch for user %s (%s != %s)\n", vms->username, newpassword, newpassword2);
6624 cmd = ast_play_and_wait(chan, "vm-mismatch");
6625 break;
6627 if (ast_strlen_zero(ext_pass_cmd))
6628 vm_change_password(vmu,newpassword);
6629 else
6630 vm_change_password_shell(vmu,newpassword);
6631 if (option_debug > 2)
6632 ast_log(LOG_DEBUG,"User %s set password to %s of length %d\n",vms->username,newpassword,(int)strlen(newpassword));
6633 cmd = ast_play_and_wait(chan,"vm-passchanged");
6634 break;
6635 case '*':
6636 cmd = 't';
6637 break;
6638 default:
6639 cmd = 0;
6640 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
6641 if (ast_fileexists(prefile, NULL, NULL))
6642 cmd = ast_play_and_wait(chan, "vm-tmpexists");
6643 if (!cmd)
6644 cmd = ast_play_and_wait(chan, "vm-options");
6645 if (!cmd)
6646 cmd = ast_waitfordigit(chan,6000);
6647 if (!cmd)
6648 retries++;
6649 if (retries > 3)
6650 cmd = 't';
6653 if (cmd == 't')
6654 cmd = 0;
6655 return cmd;
6658 static int vm_tempgreeting(struct ast_channel *chan, struct ast_vm_user *vmu, struct vm_state *vms, char *fmtc, signed char record_gain)
6660 int res;
6661 int cmd = 0;
6662 int retries = 0;
6663 int duration = 0;
6664 char prefile[PATH_MAX] = "";
6665 unsigned char buf[256];
6666 char dest[PATH_MAX];
6667 int bytes = 0;
6669 if (ast_adsi_available(chan)) {
6670 bytes += adsi_logo(buf + bytes);
6671 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 3, ADSI_JUST_CENT, 0, "Temp Greeting Menu", "");
6672 bytes += ast_adsi_display(buf + bytes, ADSI_COMM_PAGE, 4, ADSI_JUST_CENT, 0, "Not Done", "");
6673 bytes += ast_adsi_set_line(buf + bytes, ADSI_COMM_PAGE, 1);
6674 bytes += ast_adsi_voice_mode(buf + bytes, 0);
6675 ast_adsi_transmit_message(chan, buf, bytes, ADSI_MSG_DISPLAY);
6678 snprintf(prefile, sizeof(prefile), "%s%s/%s/temp", VM_SPOOL_DIR, vmu->context, vms->username);
6679 if ((res = create_dirpath(dest, sizeof(dest), vmu->context, vms->username, "temp"))) {
6680 ast_log(LOG_WARNING, "Failed to create directory (%s).\n", prefile);
6681 return -1;
6683 while ((cmd >= 0) && (cmd != 't')) {
6684 if (cmd)
6685 retries = 0;
6686 RETRIEVE(prefile, -1, vmu);
6687 if (ast_fileexists(prefile, NULL, NULL) <= 0) {
6688 play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6689 cmd = 't';
6690 } else {
6691 switch (cmd) {
6692 case '1':
6693 cmd = play_record_review(chan, "vm-rec-temp", prefile, maxgreet, fmtc, 0, vmu, &duration, NULL, record_gain, vms);
6694 break;
6695 case '2':
6696 DELETE(prefile, -1, prefile, vmu);
6697 ast_play_and_wait(chan, "vm-tempremoved");
6698 cmd = 't';
6699 break;
6700 case '*':
6701 cmd = 't';
6702 break;
6703 default:
6704 cmd = ast_play_and_wait(chan,
6705 ast_fileexists(prefile, NULL, NULL) > 0 ? /* XXX always true ? */
6706 "vm-tempgreeting2" : "vm-tempgreeting");
6707 if (!cmd)
6708 cmd = ast_waitfordigit(chan,6000);
6709 if (!cmd)
6710 retries++;
6711 if (retries > 3)
6712 cmd = 't';
6715 DISPOSE(prefile, -1);
6717 if (cmd == 't')
6718 cmd = 0;
6719 return cmd;
6722 /* GREEK SYNTAX */
6724 static int vm_browse_messages_gr(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6726 int cmd=0;
6728 if (vms->lastmsg > -1) {
6729 cmd = play_message(chan, vmu, vms);
6730 } else {
6731 cmd = ast_play_and_wait(chan, "vm-youhaveno");
6732 if (!strcasecmp(vms->vmbox, "vm-INBOX") ||!strcasecmp(vms->vmbox, "vm-Old")){
6733 if (!cmd) {
6734 snprintf(vms->fn, sizeof(vms->fn), "vm-%ss", vms->curbox);
6735 cmd = ast_play_and_wait(chan, vms->fn);
6737 if (!cmd)
6738 cmd = ast_play_and_wait(chan, "vm-messages");
6739 } else {
6740 if (!cmd)
6741 cmd = ast_play_and_wait(chan, "vm-messages");
6742 if (!cmd) {
6743 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6744 cmd = ast_play_and_wait(chan, vms->fn);
6748 return cmd;
6751 /* Default English syntax */
6752 static int vm_browse_messages_en(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6754 int cmd=0;
6756 if (vms->lastmsg > -1) {
6757 cmd = play_message(chan, vmu, vms);
6758 } else {
6759 cmd = ast_play_and_wait(chan, "vm-youhave");
6760 if (!cmd)
6761 cmd = ast_play_and_wait(chan, "vm-no");
6762 if (!cmd) {
6763 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6764 cmd = ast_play_and_wait(chan, vms->fn);
6766 if (!cmd)
6767 cmd = ast_play_and_wait(chan, "vm-messages");
6769 return cmd;
6772 /* ITALIAN syntax */
6773 static int vm_browse_messages_it(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6775 int cmd=0;
6777 if (vms->lastmsg > -1) {
6778 cmd = play_message(chan, vmu, vms);
6779 } else {
6780 cmd = ast_play_and_wait(chan, "vm-no");
6781 if (!cmd)
6782 cmd = ast_play_and_wait(chan, "vm-message");
6783 if (!cmd) {
6784 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6785 cmd = ast_play_and_wait(chan, vms->fn);
6788 return cmd;
6791 /* SPANISH syntax */
6792 static int vm_browse_messages_es(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6794 int cmd=0;
6796 if (vms->lastmsg > -1) {
6797 cmd = play_message(chan, vmu, vms);
6798 } else {
6799 cmd = ast_play_and_wait(chan, "vm-youhaveno");
6800 if (!cmd)
6801 cmd = ast_play_and_wait(chan, "vm-messages");
6802 if (!cmd) {
6803 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6804 cmd = ast_play_and_wait(chan, vms->fn);
6807 return cmd;
6810 /* PORTUGUESE syntax */
6811 static int vm_browse_messages_pt(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6813 int cmd=0;
6815 if (vms->lastmsg > -1) {
6816 cmd = play_message(chan, vmu, vms);
6817 } else {
6818 cmd = ast_play_and_wait(chan, "vm-no");
6819 if (!cmd) {
6820 snprintf(vms->fn, sizeof(vms->fn), "vm-%s", vms->curbox);
6821 cmd = ast_play_and_wait(chan, vms->fn);
6823 if (!cmd)
6824 cmd = ast_play_and_wait(chan, "vm-messages");
6826 return cmd;
6829 static int vm_browse_messages(struct ast_channel *chan, struct vm_state *vms, struct ast_vm_user *vmu)
6831 if (!strcasecmp(chan->language, "es")) { /* SPANISH */
6832 return vm_browse_messages_es(chan, vms, vmu);
6833 } else if (!strcasecmp(chan->language, "it")) { /* ITALIAN */
6834 return vm_browse_messages_it(chan, vms, vmu);
6835 } else if (!strcasecmp(chan->language, "pt") || !strcasecmp(chan->language, "pt_BR")) { /* PORTUGUESE */
6836 return vm_browse_messages_pt(chan, vms, vmu);
6837 } else if (!strcasecmp(chan->language, "gr")){
6838 return vm_browse_messages_gr(chan, vms, vmu); /* GREEK */
6839 } else { /* Default to English syntax */
6840 return vm_browse_messages_en(chan, vms, vmu);
6844 static int vm_authenticate(struct ast_channel *chan, char *mailbox, int mailbox_size,
6845 struct ast_vm_user *res_vmu, const char *context, const char *prefix,
6846 int skipuser, int maxlogins, int silent)
6848 int useadsi=0, valid=0, logretries=0;
6849 char password[AST_MAX_EXTENSION]="", *passptr;
6850 struct ast_vm_user vmus, *vmu = NULL;
6852 /* If ADSI is supported, setup login screen */
6853 adsi_begin(chan, &useadsi);
6854 if (!skipuser && useadsi)
6855 adsi_login(chan);
6856 if (!silent && !skipuser && ast_streamfile(chan, "vm-login", chan->language)) {
6857 ast_log(LOG_WARNING, "Couldn't stream login file\n");
6858 return -1;
6861 /* Authenticate them and get their mailbox/password */
6863 while (!valid && (logretries < maxlogins)) {
6864 /* Prompt for, and read in the username */
6865 if (!skipuser && ast_readstring(chan, mailbox, mailbox_size - 1, 2000, 10000, "#") < 0) {
6866 ast_log(LOG_WARNING, "Couldn't read username\n");
6867 return -1;
6869 if (ast_strlen_zero(mailbox)) {
6870 if (chan->cid.cid_num) {
6871 ast_copy_string(mailbox, chan->cid.cid_num, mailbox_size);
6872 } else {
6873 if (option_verbose > 2)
6874 ast_verbose(VERBOSE_PREFIX_3 "Username not entered\n");
6875 return -1;
6878 if (useadsi)
6879 adsi_password(chan);
6881 if (!ast_strlen_zero(prefix)) {
6882 char fullusername[80] = "";
6883 ast_copy_string(fullusername, prefix, sizeof(fullusername));
6884 strncat(fullusername, mailbox, sizeof(fullusername) - 1 - strlen(fullusername));
6885 ast_copy_string(mailbox, fullusername, mailbox_size);
6888 if (option_debug)
6889 ast_log(LOG_DEBUG, "Before find user for mailbox %s\n",mailbox);
6890 vmu = find_user(&vmus, context, mailbox);
6891 if (vmu && (vmu->password[0] == '\0' || (vmu->password[0] == '-' && vmu->password[1] == '\0'))) {
6892 /* saved password is blank, so don't bother asking */
6893 password[0] = '\0';
6894 } else {
6895 if (ast_streamfile(chan, "vm-password", chan->language)) {
6896 ast_log(LOG_WARNING, "Unable to stream password file\n");
6897 return -1;
6899 if (ast_readstring(chan, password, sizeof(password) - 1, 2000, 10000, "#") < 0) {
6900 ast_log(LOG_WARNING, "Unable to read password\n");
6901 return -1;
6905 if (vmu) {
6906 passptr = vmu->password;
6907 if (passptr[0] == '-') passptr++;
6909 if (vmu && !strcmp(passptr, password))
6910 valid++;
6911 else {
6912 if (option_verbose > 2)
6913 ast_verbose( VERBOSE_PREFIX_3 "Incorrect password '%s' for user '%s' (context = %s)\n", password, mailbox, context ? context : "default");
6914 if (!ast_strlen_zero(prefix))
6915 mailbox[0] = '\0';
6917 logretries++;
6918 if (!valid) {
6919 if (skipuser || logretries >= maxlogins) {
6920 if (ast_streamfile(chan, "vm-incorrect", chan->language)) {
6921 ast_log(LOG_WARNING, "Unable to stream incorrect message\n");
6922 return -1;
6924 } else {
6925 if (useadsi)
6926 adsi_login(chan);
6927 if (ast_streamfile(chan, "vm-incorrect-mailbox", chan->language)) {
6928 ast_log(LOG_WARNING, "Unable to stream incorrect mailbox message\n");
6929 return -1;
6932 if (ast_waitstream(chan, "")) /* Channel is hung up */
6933 return -1;
6936 if (!valid && (logretries >= maxlogins)) {
6937 ast_stopstream(chan);
6938 ast_play_and_wait(chan, "vm-goodbye");
6939 return -1;
6941 if (vmu && !skipuser) {
6942 memcpy(res_vmu, vmu, sizeof(struct ast_vm_user));
6944 return 0;
6947 static int vm_execmain(struct ast_channel *chan, void *data)
6949 /* XXX This is, admittedly, some pretty horrendus code. For some
6950 reason it just seemed a lot easier to do with GOTO's. I feel
6951 like I'm back in my GWBASIC days. XXX */
6952 int res=-1;
6953 int cmd=0;
6954 int valid = 0;
6955 struct ast_module_user *u;
6956 char prefixstr[80] ="";
6957 char ext_context[256]="";
6958 int box;
6959 int useadsi = 0;
6960 int skipuser = 0;
6961 struct vm_state vms;
6962 struct ast_vm_user *vmu = NULL, vmus;
6963 char *context=NULL;
6964 int silentexit = 0;
6965 struct ast_flags flags = { 0 };
6966 signed char record_gain = 0;
6967 int play_auto = 0;
6968 int play_folder = 0;
6969 #ifdef IMAP_STORAGE
6970 int deleted = 0;
6971 #endif
6972 u = ast_module_user_add(chan);
6974 /* Add the vm_state to the active list and keep it active */
6975 memset(&vms, 0, sizeof(vms));
6976 vms.lastmsg = -1;
6978 memset(&vmus, 0, sizeof(vmus));
6980 if (chan->_state != AST_STATE_UP) {
6981 if (option_debug)
6982 ast_log(LOG_DEBUG, "Before ast_answer\n");
6983 ast_answer(chan);
6986 if (!ast_strlen_zero(data)) {
6987 char *opts[OPT_ARG_ARRAY_SIZE];
6988 char *parse;
6989 AST_DECLARE_APP_ARGS(args,
6990 AST_APP_ARG(argv0);
6991 AST_APP_ARG(argv1);
6994 parse = ast_strdupa(data);
6996 AST_STANDARD_APP_ARGS(args, parse);
6998 if (args.argc == 2) {
6999 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
7000 ast_module_user_remove(u);
7001 return -1;
7003 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
7004 int gain;
7005 if (!ast_strlen_zero(opts[OPT_ARG_RECORDGAIN])) {
7006 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
7007 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
7008 ast_module_user_remove(u);
7009 return -1;
7010 } else {
7011 record_gain = (signed char) gain;
7013 } else {
7014 ast_log(LOG_WARNING, "Invalid Gain level set with option g\n");
7017 if (ast_test_flag(&flags, OPT_AUTOPLAY) ) {
7018 play_auto = 1;
7019 if (opts[OPT_ARG_PLAYFOLDER]) {
7020 if (sscanf(opts[OPT_ARG_PLAYFOLDER], "%d", &play_folder) != 1) {
7021 ast_log(LOG_WARNING, "Invalid value '%s' provided for folder autoplay option\n", opts[OPT_ARG_PLAYFOLDER]);
7023 } else {
7024 ast_log(LOG_WARNING, "Invalid folder set with option a\n");
7026 if ( play_folder > 9 || play_folder < 0) {
7027 ast_log(LOG_WARNING, "Invalid value '%d' provided for folder autoplay option\n", play_folder);
7028 play_folder = 0;
7031 } else {
7032 /* old style options parsing */
7033 while (*(args.argv0)) {
7034 if (*(args.argv0) == 's')
7035 ast_set_flag(&flags, OPT_SILENT);
7036 else if (*(args.argv0) == 'p')
7037 ast_set_flag(&flags, OPT_PREPEND_MAILBOX);
7038 else
7039 break;
7040 (args.argv0)++;
7045 valid = ast_test_flag(&flags, OPT_SILENT);
7047 if ((context = strchr(args.argv0, '@')))
7048 *context++ = '\0';
7050 if (ast_test_flag(&flags, OPT_PREPEND_MAILBOX))
7051 ast_copy_string(prefixstr, args.argv0, sizeof(prefixstr));
7052 else
7053 ast_copy_string(vms.username, args.argv0, sizeof(vms.username));
7055 if (!ast_strlen_zero(vms.username) && (vmu = find_user(&vmus, context ,vms.username)))
7056 skipuser++;
7057 else
7058 valid = 0;
7061 if (!valid)
7062 res = vm_authenticate(chan, vms.username, sizeof(vms.username), &vmus, context, prefixstr, skipuser, maxlogins, 0);
7064 if (option_debug)
7065 ast_log(LOG_DEBUG, "After vm_authenticate\n");
7066 if (!res) {
7067 valid = 1;
7068 if (!skipuser)
7069 vmu = &vmus;
7070 } else {
7071 res = 0;
7074 /* If ADSI is supported, setup login screen */
7075 adsi_begin(chan, &useadsi);
7077 #ifdef IMAP_STORAGE
7078 vms.interactive = 1;
7079 vms.updated = 1;
7080 ast_copy_string(vms.context, vmu->context, sizeof(vms.context));
7081 vmstate_insert(&vms);
7082 init_vm_state(&vms);
7083 #endif
7084 if (!valid)
7085 goto out;
7087 if (!(vms.deleted = ast_calloc(vmu->maxmsg, sizeof(int)))) {
7088 /* TODO: Handle memory allocation failure */
7090 if (!(vms.heard = ast_calloc(vmu->maxmsg, sizeof(int)))) {
7091 /* TODO: Handle memory allocation failure */
7094 /* Set language from config to override channel language */
7095 if (!ast_strlen_zero(vmu->language))
7096 ast_string_field_set(chan, language, vmu->language);
7097 create_dirpath(vms.curdir, sizeof(vms.curdir), vmu->context, vms.username, "");
7098 /* Retrieve old and new message counts */
7099 if (option_debug)
7100 ast_log(LOG_DEBUG, "Before open_mailbox\n");
7101 res = open_mailbox(&vms, vmu, 1);
7102 if (res == ERROR_LOCK_PATH)
7103 goto out;
7104 vms.oldmessages = vms.lastmsg + 1;
7105 if (option_debug > 2)
7106 ast_log(LOG_DEBUG, "Number of old messages: %d\n",vms.oldmessages);
7107 /* Start in INBOX */
7108 res = open_mailbox(&vms, vmu, 0);
7109 if (res == ERROR_LOCK_PATH)
7110 goto out;
7111 vms.newmessages = vms.lastmsg + 1;
7112 if (option_debug > 2)
7113 ast_log(LOG_DEBUG, "Number of new messages: %d\n",vms.newmessages);
7115 /* Select proper mailbox FIRST!! */
7116 if (play_auto) {
7117 res = open_mailbox(&vms, vmu, play_folder);
7118 if (res == ERROR_LOCK_PATH)
7119 goto out;
7121 /* If there are no new messages, inform the user and hangup */
7122 if (vms.lastmsg == -1) {
7123 cmd = vm_browse_messages(chan, &vms, vmu);
7124 res = 0;
7125 goto out;
7127 } else {
7128 if (!vms.newmessages && vms.oldmessages) {
7129 /* If we only have old messages start here */
7130 res = open_mailbox(&vms, vmu, 1);
7131 play_folder = 1;
7132 if (res == ERROR_LOCK_PATH)
7133 goto out;
7137 if (useadsi)
7138 adsi_status(chan, &vms);
7139 res = 0;
7141 /* Check to see if this is a new user */
7142 if (!strcasecmp(vmu->mailbox, vmu->password) &&
7143 (ast_test_flag(vmu, VM_FORCENAME | VM_FORCEGREET))) {
7144 if (ast_play_and_wait(chan, "vm-newuser") == -1)
7145 ast_log(LOG_WARNING, "Couldn't stream new user file\n");
7146 cmd = vm_newuser(chan, vmu, &vms, vmfmts, record_gain);
7147 if ((cmd == 't') || (cmd == '#')) {
7148 /* Timeout */
7149 res = 0;
7150 goto out;
7151 } else if (cmd < 0) {
7152 /* Hangup */
7153 res = -1;
7154 goto out;
7157 #ifdef IMAP_STORAGE
7158 if (option_debug > 2)
7159 ast_log(LOG_DEBUG, "Checking quotas: comparing %u to %u\n",vms.quota_usage,vms.quota_limit);
7160 if (vms.quota_limit && vms.quota_usage >= vms.quota_limit) {
7161 if (option_debug)
7162 ast_log(LOG_DEBUG, "*** QUOTA EXCEEDED!!\n");
7163 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
7165 if (option_debug > 2)
7166 ast_log(LOG_DEBUG, "Checking quotas: User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
7167 if ((vms.newmessages + vms.oldmessages) >= vmu->maxmsg) {
7168 ast_log(LOG_WARNING, "No more messages possible. User has %d messages and limit is %d.\n",(vms.newmessages + vms.oldmessages),vmu->maxmsg);
7169 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
7171 #endif
7172 if (play_auto) {
7173 cmd = '1';
7174 } else {
7175 cmd = vm_intro(chan, vmu, &vms);
7178 vms.repeats = 0;
7179 vms.starting = 1;
7180 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
7181 /* Run main menu */
7182 switch (cmd) {
7183 case '1':
7184 vms.curmsg = 0;
7185 /* Fall through */
7186 case '5':
7187 cmd = vm_browse_messages(chan, &vms, vmu);
7188 break;
7189 case '2': /* Change folders */
7190 if (useadsi)
7191 adsi_folders(chan, 0, "Change to folder...");
7192 cmd = get_folder2(chan, "vm-changeto", 0);
7193 if (cmd == '#') {
7194 cmd = 0;
7195 } else if (cmd > 0) {
7196 cmd = cmd - '0';
7197 res = close_mailbox(&vms, vmu);
7198 if (res == ERROR_LOCK_PATH)
7199 goto out;
7200 res = open_mailbox(&vms, vmu, cmd);
7201 if (res == ERROR_LOCK_PATH)
7202 goto out;
7203 play_folder = cmd;
7204 cmd = 0;
7206 if (useadsi)
7207 adsi_status2(chan, &vms);
7209 if (!cmd)
7210 cmd = vm_play_folder_name(chan, vms.vmbox);
7212 vms.starting = 1;
7213 break;
7214 case '3': /* Advanced options */
7215 cmd = 0;
7216 vms.repeats = 0;
7217 while ((cmd > -1) && (cmd != 't') && (cmd != '#')) {
7218 switch (cmd) {
7219 case '1': /* Reply */
7220 if (vms.lastmsg > -1 && !vms.starting) {
7221 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 1, record_gain);
7222 if (cmd == ERROR_LOCK_PATH) {
7223 res = cmd;
7224 goto out;
7226 } else
7227 cmd = ast_play_and_wait(chan, "vm-sorry");
7228 cmd = 't';
7229 break;
7230 case '2': /* Callback */
7231 if (option_verbose > 2 && !vms.starting)
7232 ast_verbose( VERBOSE_PREFIX_3 "Callback Requested\n");
7233 if (!ast_strlen_zero(vmu->callback) && vms.lastmsg > -1 && !vms.starting) {
7234 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 2, record_gain);
7235 if (cmd == 9) {
7236 silentexit = 1;
7237 goto out;
7238 } else if (cmd == ERROR_LOCK_PATH) {
7239 res = cmd;
7240 goto out;
7243 else
7244 cmd = ast_play_and_wait(chan, "vm-sorry");
7245 cmd = 't';
7246 break;
7247 case '3': /* Envelope */
7248 if (vms.lastmsg > -1 && !vms.starting) {
7249 cmd = advanced_options(chan, vmu, &vms, vms.curmsg, 3, record_gain);
7250 if (cmd == ERROR_LOCK_PATH) {
7251 res = cmd;
7252 goto out;
7254 } else
7255 cmd = ast_play_and_wait(chan, "vm-sorry");
7256 cmd = 't';
7257 break;
7258 case '4': /* Dialout */
7259 if (!ast_strlen_zero(vmu->dialout)) {
7260 cmd = dialout(chan, vmu, NULL, vmu->dialout);
7261 if (cmd == 9) {
7262 silentexit = 1;
7263 goto out;
7266 else
7267 cmd = ast_play_and_wait(chan, "vm-sorry");
7268 cmd = 't';
7269 break;
7271 case '5': /* Leave VoiceMail */
7272 if (ast_test_flag(vmu, VM_SVMAIL)) {
7273 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 1, record_gain);
7274 if (cmd == ERROR_LOCK_PATH) {
7275 res = cmd;
7276 ast_log(LOG_WARNING, "forward_message failed to lock path.\n");
7277 goto out;
7279 } else
7280 cmd = ast_play_and_wait(chan,"vm-sorry");
7281 cmd='t';
7282 break;
7284 case '*': /* Return to main menu */
7285 cmd = 't';
7286 break;
7288 default:
7289 cmd = 0;
7290 if (!vms.starting) {
7291 cmd = ast_play_and_wait(chan, "vm-toreply");
7293 if (!ast_strlen_zero(vmu->callback) && !vms.starting && !cmd) {
7294 cmd = ast_play_and_wait(chan, "vm-tocallback");
7296 if (!cmd && !vms.starting) {
7297 cmd = ast_play_and_wait(chan, "vm-tohearenv");
7299 if (!ast_strlen_zero(vmu->dialout) && !cmd) {
7300 cmd = ast_play_and_wait(chan, "vm-tomakecall");
7302 if (ast_test_flag(vmu, VM_SVMAIL) && !cmd)
7303 cmd=ast_play_and_wait(chan, "vm-leavemsg");
7304 if (!cmd)
7305 cmd = ast_play_and_wait(chan, "vm-starmain");
7306 if (!cmd)
7307 cmd = ast_waitfordigit(chan,6000);
7308 if (!cmd)
7309 vms.repeats++;
7310 if (vms.repeats > 3)
7311 cmd = 't';
7314 if (cmd == 't') {
7315 cmd = 0;
7316 vms.repeats = 0;
7318 break;
7319 case '4':
7320 if (vms.curmsg > 0) {
7321 vms.curmsg--;
7322 cmd = play_message(chan, vmu, &vms);
7323 } else {
7324 cmd = ast_play_and_wait(chan, "vm-nomore");
7326 break;
7327 case '6':
7328 if (vms.curmsg < vms.lastmsg) {
7329 vms.curmsg++;
7330 cmd = play_message(chan, vmu, &vms);
7331 } else {
7332 cmd = ast_play_and_wait(chan, "vm-nomore");
7334 break;
7335 case '7':
7336 if (vms.curmsg >= 0 && vms.curmsg <= vms.lastmsg) {
7337 vms.deleted[vms.curmsg] = !vms.deleted[vms.curmsg];
7338 if (useadsi)
7339 adsi_delete(chan, &vms);
7340 if (vms.deleted[vms.curmsg]) {
7341 if (play_folder == 0)
7342 vms.newmessages--;
7343 else if (play_folder == 1)
7344 vms.oldmessages--;
7345 cmd = ast_play_and_wait(chan, "vm-deleted");
7347 else {
7348 if (play_folder == 0)
7349 vms.newmessages++;
7350 else if (play_folder == 1)
7351 vms.oldmessages++;
7352 cmd = ast_play_and_wait(chan, "vm-undeleted");
7354 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
7355 if (vms.curmsg < vms.lastmsg) {
7356 vms.curmsg++;
7357 cmd = play_message(chan, vmu, &vms);
7358 } else {
7359 cmd = ast_play_and_wait(chan, "vm-nomore");
7362 } else /* Delete not valid if we haven't selected a message */
7363 cmd = 0;
7364 #ifdef IMAP_STORAGE
7365 deleted = 1;
7366 #endif
7367 break;
7369 case '8':
7370 if (vms.lastmsg > -1) {
7371 cmd = forward_message(chan, context, &vms, vmu, vmfmts, 0, record_gain);
7372 if (cmd == ERROR_LOCK_PATH) {
7373 res = cmd;
7374 goto out;
7376 } else
7377 cmd = ast_play_and_wait(chan, "vm-nomore");
7378 break;
7379 case '9':
7380 if (vms.curmsg < 0 || vms.curmsg > vms.lastmsg) {
7381 /* No message selected */
7382 cmd = 0;
7383 break;
7385 if (useadsi)
7386 adsi_folders(chan, 1, "Save to folder...");
7387 cmd = get_folder2(chan, "vm-savefolder", 1);
7388 box = 0; /* Shut up compiler */
7389 if (cmd == '#') {
7390 cmd = 0;
7391 break;
7392 } else if (cmd > 0) {
7393 box = cmd = cmd - '0';
7394 cmd = save_to_folder(vmu, &vms, vms.curmsg, cmd);
7395 if (cmd == ERROR_LOCK_PATH) {
7396 res = cmd;
7397 goto out;
7398 #ifndef IMAP_STORAGE
7399 } else if (!cmd) {
7400 vms.deleted[vms.curmsg] = 1;
7401 #endif
7402 } else {
7403 vms.deleted[vms.curmsg] = 0;
7404 vms.heard[vms.curmsg] = 0;
7407 make_file(vms.fn, sizeof(vms.fn), vms.curdir, vms.curmsg);
7408 if (useadsi)
7409 adsi_message(chan, &vms);
7410 snprintf(vms.fn, sizeof(vms.fn), "vm-%s", mbox(box));
7411 if (!cmd) {
7412 cmd = ast_play_and_wait(chan, "vm-message");
7413 if (!cmd)
7414 cmd = say_and_wait(chan, vms.curmsg + 1, chan->language);
7415 if (!cmd)
7416 cmd = ast_play_and_wait(chan, "vm-savedto");
7417 if (!cmd)
7418 cmd = vm_play_folder_name(chan, vms.fn);
7419 } else {
7420 cmd = ast_play_and_wait(chan, "vm-mailboxfull");
7422 if (ast_test_flag((&globalflags), VM_SKIPAFTERCMD)) {
7423 if (vms.curmsg < vms.lastmsg) {
7424 vms.curmsg++;
7425 cmd = play_message(chan, vmu, &vms);
7426 } else {
7427 cmd = ast_play_and_wait(chan, "vm-nomore");
7430 break;
7431 case '*':
7432 if (!vms.starting) {
7433 cmd = ast_play_and_wait(chan, "vm-onefor");
7434 if (!cmd)
7435 cmd = vm_play_folder_name(chan, vms.vmbox);
7436 if (!cmd)
7437 cmd = ast_play_and_wait(chan, "vm-opts");
7438 if (!cmd)
7439 cmd = vm_instructions(chan, &vms, 1);
7440 } else
7441 cmd = 0;
7442 break;
7443 case '0':
7444 cmd = vm_options(chan, vmu, &vms, vmfmts, record_gain);
7445 if (useadsi)
7446 adsi_status(chan, &vms);
7447 break;
7448 default: /* Nothing */
7449 cmd = vm_instructions(chan, &vms, 0);
7450 break;
7453 if ((cmd == 't') || (cmd == '#')) {
7454 /* Timeout */
7455 res = 0;
7456 } else {
7457 /* Hangup */
7458 res = -1;
7461 out:
7462 if (res > -1) {
7463 ast_stopstream(chan);
7464 adsi_goodbye(chan);
7465 if (valid) {
7466 if (silentexit)
7467 res = ast_play_and_wait(chan, "vm-dialout");
7468 else
7469 res = ast_play_and_wait(chan, "vm-goodbye");
7470 if (res > 0)
7471 res = 0;
7473 if (useadsi)
7474 ast_adsi_unload_session(chan);
7476 if (vmu)
7477 close_mailbox(&vms, vmu);
7478 if (valid) {
7479 snprintf(ext_context, sizeof(ext_context), "%s@%s", vms.username, vmu->context);
7480 manager_event(EVENT_FLAG_CALL, "MessageWaiting", "Mailbox: %s\r\nWaiting: %d\r\n", ext_context, has_voicemail(ext_context, NULL));
7481 run_externnotify(vmu->context, vmu->mailbox);
7483 #ifdef IMAP_STORAGE
7484 /* expunge message - use UID Expunge if supported on IMAP server*/
7485 if (option_debug > 2)
7486 ast_log(LOG_DEBUG, "*** Checking if we can expunge, deleted set to %d, expungeonhangup set to %d\n",deleted,expungeonhangup);
7487 if (vmu && deleted == 1 && expungeonhangup == 1) {
7488 #ifdef HAVE_IMAP_TK2006
7489 if (LEVELUIDPLUS (vms.mailstream)) {
7490 mail_expunge_full(vms.mailstream,NIL,EX_UID);
7491 } else
7492 #endif
7493 mail_expunge(vms.mailstream);
7495 /* before we delete the state, we should copy pertinent info
7496 * back to the persistent model */
7497 vmstate_delete(&vms);
7498 #endif
7499 if (vmu)
7500 free_user(vmu);
7501 if (vms.deleted)
7502 free(vms.deleted);
7503 if (vms.heard)
7504 free(vms.heard);
7505 ast_module_user_remove(u);
7507 return res;
7510 static int vm_exec(struct ast_channel *chan, void *data)
7512 int res = 0;
7513 struct ast_module_user *u;
7514 char *tmp;
7515 struct leave_vm_options leave_options;
7516 struct ast_flags flags = { 0 };
7517 static int deprecate_warning = 0;
7518 char *opts[OPT_ARG_ARRAY_SIZE];
7519 AST_DECLARE_APP_ARGS(args,
7520 AST_APP_ARG(argv0);
7521 AST_APP_ARG(argv1);
7524 u = ast_module_user_add(chan);
7526 memset(&leave_options, 0, sizeof(leave_options));
7528 if (chan->_state != AST_STATE_UP)
7529 ast_answer(chan);
7531 if (!ast_strlen_zero(data)) {
7532 tmp = ast_strdupa(data);
7533 AST_STANDARD_APP_ARGS(args, tmp);
7534 if (args.argc == 2) {
7535 if (ast_app_parse_options(vm_app_options, &flags, opts, args.argv1)) {
7536 ast_module_user_remove(u);
7537 return -1;
7539 ast_copy_flags(&leave_options, &flags, OPT_SILENT | OPT_BUSY_GREETING | OPT_UNAVAIL_GREETING | OPT_PRIORITY_JUMP);
7540 if (ast_test_flag(&flags, OPT_RECORDGAIN)) {
7541 int gain;
7543 if (sscanf(opts[OPT_ARG_RECORDGAIN], "%d", &gain) != 1) {
7544 ast_log(LOG_WARNING, "Invalid value '%s' provided for record gain option\n", opts[OPT_ARG_RECORDGAIN]);
7545 ast_module_user_remove(u);
7546 return -1;
7547 } else {
7548 leave_options.record_gain = (signed char) gain;
7551 } else {
7552 /* old style options parsing */
7553 int old = 0;
7554 char *orig_argv0 = args.argv0;
7555 while (*(args.argv0)) {
7556 if (*(args.argv0) == 's') {
7557 old = 1;
7558 ast_set_flag(&leave_options, OPT_SILENT);
7559 } else if (*(args.argv0) == 'b') {
7560 old = 1;
7561 ast_set_flag(&leave_options, OPT_BUSY_GREETING);
7562 } else if (*(args.argv0) == 'u') {
7563 old = 1;
7564 ast_set_flag(&leave_options, OPT_UNAVAIL_GREETING);
7565 } else if (*(args.argv0) == 'j') {
7566 old = 1;
7567 ast_set_flag(&leave_options, OPT_PRIORITY_JUMP);
7568 } else
7569 break;
7570 (args.argv0)++;
7572 if (!deprecate_warning && old) {
7573 deprecate_warning = 1;
7574 ast_log(LOG_WARNING, "Prefixing the mailbox with an option is deprecated ('%s').\n", orig_argv0);
7575 ast_log(LOG_WARNING, "Please move all leading options to the second argument.\n");
7578 } else {
7579 char tmp[256];
7580 res = ast_app_getdata(chan, "vm-whichbox", tmp, sizeof(tmp) - 1, 0);
7581 if (res < 0) {
7582 ast_module_user_remove(u);
7583 return res;
7585 if (ast_strlen_zero(tmp)) {
7586 ast_module_user_remove(u);
7587 return 0;
7589 args.argv0 = ast_strdupa(tmp);
7592 res = leave_voicemail(chan, args.argv0, &leave_options);
7594 if (res == ERROR_LOCK_PATH) {
7595 ast_log(LOG_ERROR, "Could not leave voicemail. The path is already locked.\n");
7596 /*Send the call to n+101 priority, where n is the current priority*/
7597 if (ast_test_flag(&leave_options, OPT_PRIORITY_JUMP) || ast_opt_priority_jumping)
7598 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
7599 ast_log(LOG_WARNING, "Extension %s, priority %d doesn't exist.\n", chan->exten, chan->priority + 101);
7600 pbx_builtin_setvar_helper(chan, "VMSTATUS", "FAILED");
7601 res = 0;
7604 ast_module_user_remove(u);
7606 return res;
7609 static struct ast_vm_user *find_or_create(char *context, char *mbox)
7611 struct ast_vm_user *vmu;
7612 AST_LIST_TRAVERSE(&users, vmu, list) {
7613 if (ast_test_flag((&globalflags), VM_SEARCH) && !strcasecmp(mbox, vmu->mailbox))
7614 break;
7615 if (context && (!strcasecmp(context, vmu->context)) && (!strcasecmp(mbox, vmu->mailbox)))
7616 break;
7619 if (!vmu) {
7620 if ((vmu = ast_calloc(1, sizeof(*vmu)))) {
7621 ast_copy_string(vmu->context, context, sizeof(vmu->context));
7622 ast_copy_string(vmu->mailbox, mbox, sizeof(vmu->mailbox));
7623 AST_LIST_INSERT_TAIL(&users, vmu, list);
7626 return vmu;
7629 static int append_mailbox(char *context, char *mbox, char *data)
7631 /* Assumes lock is already held */
7632 char *tmp;
7633 char *stringp;
7634 char *s;
7635 struct ast_vm_user *vmu;
7637 tmp = ast_strdupa(data);
7639 if ((vmu = find_or_create(context, mbox))) {
7640 populate_defaults(vmu);
7642 stringp = tmp;
7643 if ((s = strsep(&stringp, ",")))
7644 ast_copy_string(vmu->password, s, sizeof(vmu->password));
7645 if (stringp && (s = strsep(&stringp, ",")))
7646 ast_copy_string(vmu->fullname, s, sizeof(vmu->fullname));
7647 if (stringp && (s = strsep(&stringp, ",")))
7648 ast_copy_string(vmu->email, s, sizeof(vmu->email));
7649 if (stringp && (s = strsep(&stringp, ",")))
7650 ast_copy_string(vmu->pager, s, sizeof(vmu->pager));
7651 if (stringp && (s = strsep(&stringp, ",")))
7652 apply_options(vmu, s);
7654 return 0;
7657 static int vm_box_exists(struct ast_channel *chan, void *data)
7659 struct ast_module_user *u;
7660 struct ast_vm_user svm;
7661 char *context, *box;
7662 int priority_jump = 0;
7663 AST_DECLARE_APP_ARGS(args,
7664 AST_APP_ARG(mbox);
7665 AST_APP_ARG(options);
7668 if (ast_strlen_zero(data)) {
7669 ast_log(LOG_ERROR, "MailboxExists requires an argument: (vmbox[@context][|options])\n");
7670 return -1;
7673 u = ast_module_user_add(chan);
7675 box = ast_strdupa(data);
7677 AST_STANDARD_APP_ARGS(args, box);
7679 if (args.options) {
7680 if (strchr(args.options, 'j'))
7681 priority_jump = 1;
7684 if ((context = strchr(args.mbox, '@'))) {
7685 *context = '\0';
7686 context++;
7689 if (find_user(&svm, context, args.mbox)) {
7690 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "SUCCESS");
7691 if (priority_jump || ast_opt_priority_jumping)
7692 if (ast_goto_if_exists(chan, chan->context, chan->exten, chan->priority + 101))
7693 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);
7694 } else
7695 pbx_builtin_setvar_helper(chan, "VMBOXEXISTSSTATUS", "FAILED");
7696 ast_module_user_remove(u);
7697 return 0;
7700 static int vmauthenticate(struct ast_channel *chan, void *data)
7702 struct ast_module_user *u;
7703 char *s = data, *user=NULL, *context=NULL, mailbox[AST_MAX_EXTENSION] = "";
7704 struct ast_vm_user vmus;
7705 char *options = NULL;
7706 int silent = 0, skipuser = 0;
7707 int res = -1;
7709 u = ast_module_user_add(chan);
7711 if (s) {
7712 s = ast_strdupa(s);
7713 user = strsep(&s, "|");
7714 options = strsep(&s, "|");
7715 if (user) {
7716 s = user;
7717 user = strsep(&s, "@");
7718 context = strsep(&s, "");
7719 if (!ast_strlen_zero(user))
7720 skipuser++;
7721 ast_copy_string(mailbox, user, sizeof(mailbox));
7725 if (options) {
7726 silent = (strchr(options, 's')) != NULL;
7729 if (!vm_authenticate(chan, mailbox, sizeof(mailbox), &vmus, context, NULL, skipuser, 3, silent)) {
7730 pbx_builtin_setvar_helper(chan, "AUTH_MAILBOX", mailbox);
7731 pbx_builtin_setvar_helper(chan, "AUTH_CONTEXT", vmus.context);
7732 ast_play_and_wait(chan, "auth-thankyou");
7733 res = 0;
7736 ast_module_user_remove(u);
7737 return res;
7740 static char voicemail_show_users_help[] =
7741 "Usage: voicemail show users [for <context>]\n"
7742 " Lists all mailboxes currently set up\n";
7744 static char voicemail_show_zones_help[] =
7745 "Usage: voicemail show zones\n"
7746 " Lists zone message formats\n";
7748 static int handle_voicemail_show_users(int fd, int argc, char *argv[])
7750 struct ast_vm_user *vmu;
7751 char *output_format = "%-10s %-5s %-25s %-10s %6s\n";
7753 if ((argc < 3) || (argc > 5) || (argc == 4)) return RESULT_SHOWUSAGE;
7754 else if ((argc == 5) && strcmp(argv[3],"for")) return RESULT_SHOWUSAGE;
7756 AST_LIST_LOCK(&users);
7757 if (!AST_LIST_EMPTY(&users)) {
7758 if (argc == 3)
7759 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
7760 else {
7761 int count = 0;
7762 AST_LIST_TRAVERSE(&users, vmu, list) {
7763 if (!strcmp(argv[4],vmu->context))
7764 count++;
7766 if (count) {
7767 ast_cli(fd, output_format, "Context", "Mbox", "User", "Zone", "NewMsg");
7768 } else {
7769 ast_cli(fd, "No such voicemail context \"%s\"\n", argv[4]);
7770 AST_LIST_UNLOCK(&users);
7771 return RESULT_FAILURE;
7774 AST_LIST_TRAVERSE(&users, vmu, list) {
7775 int newmsgs = 0, oldmsgs = 0;
7776 char count[12], tmp[256] = "";
7778 if ((argc == 3) || ((argc == 5) && !strcmp(argv[4],vmu->context))) {
7779 snprintf(tmp, sizeof(tmp), "%s@%s", vmu->mailbox, ast_strlen_zero(vmu->context) ? "default" : vmu->context);
7780 inboxcount(tmp, &newmsgs, &oldmsgs);
7781 snprintf(count,sizeof(count),"%d",newmsgs);
7782 ast_cli(fd, output_format, vmu->context, vmu->mailbox, vmu->fullname, vmu->zonetag, count);
7785 } else {
7786 ast_cli(fd, "There are no voicemail users currently defined\n");
7787 AST_LIST_UNLOCK(&users);
7788 return RESULT_FAILURE;
7790 AST_LIST_UNLOCK(&users);
7791 return RESULT_SUCCESS;
7794 static int handle_voicemail_show_zones(int fd, int argc, char *argv[])
7796 struct vm_zone *zone;
7797 char *output_format = "%-15s %-20s %-45s\n";
7798 int res = RESULT_SUCCESS;
7800 if (argc != 3)
7801 return RESULT_SHOWUSAGE;
7803 AST_LIST_LOCK(&zones);
7804 if (!AST_LIST_EMPTY(&zones)) {
7805 ast_cli(fd, output_format, "Zone", "Timezone", "Message Format");
7806 AST_LIST_TRAVERSE(&zones, zone, list) {
7807 ast_cli(fd, output_format, zone->name, zone->timezone, zone->msg_format);
7809 } else {
7810 ast_cli(fd, "There are no voicemail zones currently defined\n");
7811 res = RESULT_FAILURE;
7813 AST_LIST_UNLOCK(&zones);
7815 return res;
7818 static char *complete_voicemail_show_users(const char *line, const char *word, int pos, int state)
7820 int which = 0;
7821 int wordlen;
7822 struct ast_vm_user *vmu;
7823 const char *context = "";
7825 /* 0 - show; 1 - voicemail; 2 - users; 3 - for; 4 - <context> */
7826 if (pos > 4)
7827 return NULL;
7828 if (pos == 3)
7829 return (state == 0) ? ast_strdup("for") : NULL;
7830 wordlen = strlen(word);
7831 AST_LIST_TRAVERSE(&users, vmu, list) {
7832 if (!strncasecmp(word, vmu->context, wordlen)) {
7833 if (context && strcmp(context, vmu->context) && ++which > state)
7834 return ast_strdup(vmu->context);
7835 /* ignore repeated contexts ? */
7836 context = vmu->context;
7839 return NULL;
7842 static struct ast_cli_entry cli_show_voicemail_users_deprecated = {
7843 { "show", "voicemail", "users", NULL },
7844 handle_voicemail_show_users, NULL,
7845 NULL, complete_voicemail_show_users };
7847 static struct ast_cli_entry cli_show_voicemail_zones_deprecated = {
7848 { "show", "voicemail", "zones", NULL },
7849 handle_voicemail_show_zones, NULL,
7850 NULL, NULL };
7852 static struct ast_cli_entry cli_voicemail[] = {
7853 { { "voicemail", "show", "users", NULL },
7854 handle_voicemail_show_users, "List defined voicemail boxes",
7855 voicemail_show_users_help, complete_voicemail_show_users, &cli_show_voicemail_users_deprecated },
7857 { { "voicemail", "show", "zones", NULL },
7858 handle_voicemail_show_zones, "List zone message formats",
7859 voicemail_show_zones_help, NULL, &cli_show_voicemail_zones_deprecated },
7862 static void free_vm_users(void)
7864 struct ast_vm_user *cur;
7865 struct vm_zone *zcur;
7867 AST_LIST_LOCK(&users);
7868 while ((cur = AST_LIST_REMOVE_HEAD(&users, list))) {
7869 ast_set_flag(cur, VM_ALLOCED);
7870 free_user(cur);
7872 AST_LIST_UNLOCK(&users);
7874 AST_LIST_LOCK(&zones);
7875 while ((zcur = AST_LIST_REMOVE_HEAD(&zones, list))) {
7876 free_zone(zcur);
7878 AST_LIST_UNLOCK(&zones);
7881 static int load_config(void)
7883 struct ast_vm_user *cur;
7884 struct ast_config *cfg, *ucfg;
7885 char *cat;
7886 struct ast_variable *var;
7887 const char *notifystr = NULL;
7888 const char *smdistr = NULL;
7889 const char *astattach;
7890 const char *astsearch;
7891 const char *astsaycid;
7892 const char *send_voicemail;
7893 #ifdef IMAP_STORAGE
7894 const char *imap_server;
7895 const char *imap_port;
7896 const char *imap_flags;
7897 const char *imap_folder;
7898 const char *auth_user;
7899 const char *auth_password;
7900 const char *expunge_on_hangup;
7901 const char *imap_timeout;
7902 #endif
7903 const char *astcallop;
7904 const char *astreview;
7905 const char *asttempgreetwarn;
7906 const char *astskipcmd;
7907 const char *asthearenv;
7908 const char *astsaydurationinfo;
7909 const char *astsaydurationminfo;
7910 const char *silencestr;
7911 const char *maxmsgstr;
7912 const char *astdirfwd;
7913 const char *thresholdstr;
7914 const char *fmt;
7915 const char *astemail;
7916 const char *ucontext;
7917 const char *astmailcmd = SENDMAIL;
7918 const char *astforcename;
7919 const char *astforcegreet;
7920 const char *s;
7921 char *q,*stringp;
7922 const char *dialoutcxt = NULL;
7923 const char *callbackcxt = NULL;
7924 const char *exitcxt = NULL;
7925 const char *extpc;
7926 const char *emaildateformatstr;
7927 const char *volgainstr;
7928 int x;
7929 int tmpadsi[4];
7931 cfg = ast_config_load(VOICEMAIL_CONFIG);
7933 free_vm_users();
7935 AST_LIST_LOCK(&users);
7937 memset(ext_pass_cmd, 0, sizeof(ext_pass_cmd));
7939 if (cfg) {
7940 /* General settings */
7942 if (!(ucontext = ast_variable_retrieve(cfg, "general", "userscontext")))
7943 ucontext = "default";
7944 ast_copy_string(userscontext, ucontext, sizeof(userscontext));
7945 /* Attach voice message to mail message ? */
7946 if (!(astattach = ast_variable_retrieve(cfg, "general", "attach")))
7947 astattach = "yes";
7948 ast_set2_flag((&globalflags), ast_true(astattach), VM_ATTACH);
7950 if (!(astsearch = ast_variable_retrieve(cfg, "general", "searchcontexts")))
7951 astsearch = "no";
7952 ast_set2_flag((&globalflags), ast_true(astsearch), VM_SEARCH);
7954 volgain = 0.0;
7955 if ((volgainstr = ast_variable_retrieve(cfg, "general", "volgain")))
7956 sscanf(volgainstr, "%lf", &volgain);
7958 #ifdef ODBC_STORAGE
7959 strcpy(odbc_database, "asterisk");
7960 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbcstorage"))) {
7961 ast_copy_string(odbc_database, thresholdstr, sizeof(odbc_database));
7963 strcpy(odbc_table, "voicemessages");
7964 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "odbctable"))) {
7965 ast_copy_string(odbc_table, thresholdstr, sizeof(odbc_table));
7967 #endif
7968 /* Mail command */
7969 strcpy(mailcmd, SENDMAIL);
7970 if ((astmailcmd = ast_variable_retrieve(cfg, "general", "mailcmd")))
7971 ast_copy_string(mailcmd, astmailcmd, sizeof(mailcmd)); /* User setting */
7973 maxsilence = 0;
7974 if ((silencestr = ast_variable_retrieve(cfg, "general", "maxsilence"))) {
7975 maxsilence = atoi(silencestr);
7976 if (maxsilence > 0)
7977 maxsilence *= 1000;
7980 if (!(maxmsgstr = ast_variable_retrieve(cfg, "general", "maxmsg"))) {
7981 maxmsg = MAXMSG;
7982 } else {
7983 maxmsg = atoi(maxmsgstr);
7984 if (maxmsg <= 0) {
7985 ast_log(LOG_WARNING, "Invalid number of messages per folder '%s'. Using default value %i\n", maxmsgstr, MAXMSG);
7986 maxmsg = MAXMSG;
7987 } else if (maxmsg > MAXMSGLIMIT) {
7988 ast_log(LOG_WARNING, "Maximum number of messages per folder is %i. Cannot accept value '%s'\n", MAXMSGLIMIT, maxmsgstr);
7989 maxmsg = MAXMSGLIMIT;
7993 /* Load date format config for voicemail mail */
7994 if ((emaildateformatstr = ast_variable_retrieve(cfg, "general", "emaildateformat"))) {
7995 ast_copy_string(emaildateformat, emaildateformatstr, sizeof(emaildateformat));
7998 /* External password changing command */
7999 if ((extpc = ast_variable_retrieve(cfg, "general", "externpass"))) {
8000 ast_copy_string(ext_pass_cmd,extpc,sizeof(ext_pass_cmd));
8002 #ifdef IMAP_STORAGE
8003 /* IMAP server address */
8004 if ((imap_server = ast_variable_retrieve(cfg, "general", "imapserver"))) {
8005 ast_copy_string(imapserver, imap_server, sizeof(imapserver));
8006 } else {
8007 ast_copy_string(imapserver,"localhost", sizeof(imapserver));
8009 /* IMAP server port */
8010 if ((imap_port = ast_variable_retrieve(cfg, "general", "imapport"))) {
8011 ast_copy_string(imapport, imap_port, sizeof(imapport));
8012 } else {
8013 ast_copy_string(imapport,"143", sizeof(imapport));
8015 /* IMAP server flags */
8016 if ((imap_flags = ast_variable_retrieve(cfg, "general", "imapflags"))) {
8017 ast_copy_string(imapflags, imap_flags, sizeof(imapflags));
8019 /* IMAP server master username */
8020 if ((auth_user = ast_variable_retrieve(cfg, "general", "authuser"))) {
8021 ast_copy_string(authuser, auth_user, sizeof(authuser));
8023 /* IMAP server master password */
8024 if ((auth_password = ast_variable_retrieve(cfg, "general", "authpassword"))) {
8025 ast_copy_string(authpassword, auth_password, sizeof(authpassword));
8027 /* Expunge on exit */
8028 if ((expunge_on_hangup = ast_variable_retrieve(cfg, "general", "expungeonhangup"))) {
8029 if (ast_false(expunge_on_hangup))
8030 expungeonhangup = 0;
8031 else
8032 expungeonhangup = 1;
8033 } else {
8034 expungeonhangup = 1;
8036 /* IMAP voicemail folder */
8037 if ((imap_folder = ast_variable_retrieve(cfg, "general", "imapfolder"))) {
8038 ast_copy_string(imapfolder, imap_folder, sizeof(imapfolder));
8039 } else {
8040 ast_copy_string(imapfolder,"INBOX", sizeof(imapfolder));
8043 /* There is some very unorthodox casting done here. This is due
8044 * to the way c-client handles the argument passed in. It expects a
8045 * void pointer and casts the pointer directly to a long without
8046 * first dereferencing it. */
8048 if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapreadtimeout"))) {
8049 mail_parameters(NIL, SET_READTIMEOUT, (void *) (atol(imap_timeout)));
8050 } else {
8051 mail_parameters(NIL, SET_READTIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
8054 if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapwritetimeout"))) {
8055 mail_parameters(NIL, SET_WRITETIMEOUT, (void *) (atol(imap_timeout)));
8056 } else {
8057 mail_parameters(NIL, SET_WRITETIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
8060 if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapopentimeout"))) {
8061 mail_parameters(NIL, SET_OPENTIMEOUT, (void *) (atol(imap_timeout)));
8062 } else {
8063 mail_parameters(NIL, SET_OPENTIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
8066 if ((imap_timeout = ast_variable_retrieve(cfg, "general", "imapclosetimeout"))) {
8067 mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) (atol(imap_timeout)));
8068 } else {
8069 mail_parameters(NIL, SET_CLOSETIMEOUT, (void *) DEFAULT_IMAP_TCP_TIMEOUT);
8072 #endif
8073 /* External voicemail notify application */
8075 if ((notifystr = ast_variable_retrieve(cfg, "general", "externnotify"))) {
8076 ast_copy_string(externnotify, notifystr, sizeof(externnotify));
8077 if (option_debug > 2)
8078 ast_log(LOG_DEBUG, "found externnotify: %s\n", externnotify);
8079 if (!strcasecmp(externnotify, "smdi")) {
8080 if (option_debug)
8081 ast_log(LOG_DEBUG, "Using SMDI for external voicemail notification\n");
8082 if ((smdistr = ast_variable_retrieve(cfg, "general", "smdiport"))) {
8083 smdi_iface = ast_smdi_interface_find(smdistr);
8084 } else {
8085 if (option_debug)
8086 ast_log(LOG_DEBUG, "No SMDI interface set, trying default (/dev/ttyS0)\n");
8087 smdi_iface = ast_smdi_interface_find("/dev/ttyS0");
8090 if (!smdi_iface) {
8091 ast_log(LOG_ERROR, "No valid SMDI interface specfied, disabling external voicemail notification\n");
8092 externnotify[0] = '\0';
8095 } else {
8096 externnotify[0] = '\0';
8099 /* Silence treshold */
8100 silencethreshold = 256;
8101 if ((thresholdstr = ast_variable_retrieve(cfg, "general", "silencethreshold")))
8102 silencethreshold = atoi(thresholdstr);
8104 if (!(astemail = ast_variable_retrieve(cfg, "general", "serveremail")))
8105 astemail = ASTERISK_USERNAME;
8106 ast_copy_string(serveremail, astemail, sizeof(serveremail));
8108 vmmaxmessage = 0;
8109 if ((s = ast_variable_retrieve(cfg, "general", "maxmessage"))) {
8110 if (sscanf(s, "%d", &x) == 1) {
8111 vmmaxmessage = x;
8112 } else {
8113 ast_log(LOG_WARNING, "Invalid max message time length\n");
8117 vmminmessage = 0;
8118 if ((s = ast_variable_retrieve(cfg, "general", "minmessage"))) {
8119 if (sscanf(s, "%d", &x) == 1) {
8120 vmminmessage = x;
8121 if (maxsilence <= vmminmessage)
8122 ast_log(LOG_WARNING, "maxsilence should be less than minmessage or you may get empty messages\n");
8123 } else {
8124 ast_log(LOG_WARNING, "Invalid min message time length\n");
8127 fmt = ast_variable_retrieve(cfg, "general", "format");
8128 if (!fmt)
8129 fmt = "wav";
8130 ast_copy_string(vmfmts, fmt, sizeof(vmfmts));
8132 skipms = 3000;
8133 if ((s = ast_variable_retrieve(cfg, "general", "maxgreet"))) {
8134 if (sscanf(s, "%d", &x) == 1) {
8135 maxgreet = x;
8136 } else {
8137 ast_log(LOG_WARNING, "Invalid max message greeting length\n");
8141 if ((s = ast_variable_retrieve(cfg, "general", "skipms"))) {
8142 if (sscanf(s, "%d", &x) == 1) {
8143 skipms = x;
8144 } else {
8145 ast_log(LOG_WARNING, "Invalid skipms value\n");
8149 maxlogins = 3;
8150 if ((s = ast_variable_retrieve(cfg, "general", "maxlogins"))) {
8151 if (sscanf(s, "%d", &x) == 1) {
8152 maxlogins = x;
8153 } else {
8154 ast_log(LOG_WARNING, "Invalid max failed login attempts\n");
8158 /* Force new user to record name ? */
8159 if (!(astforcename = ast_variable_retrieve(cfg, "general", "forcename")))
8160 astforcename = "no";
8161 ast_set2_flag((&globalflags), ast_true(astforcename), VM_FORCENAME);
8163 /* Force new user to record greetings ? */
8164 if (!(astforcegreet = ast_variable_retrieve(cfg, "general", "forcegreetings")))
8165 astforcegreet = "no";
8166 ast_set2_flag((&globalflags), ast_true(astforcegreet), VM_FORCEGREET);
8168 if ((s = ast_variable_retrieve(cfg, "general", "cidinternalcontexts"))){
8169 if (option_debug > 2)
8170 ast_log(LOG_DEBUG,"VM_CID Internal context string: %s\n",s);
8171 stringp = ast_strdupa(s);
8172 for (x = 0 ; x < MAX_NUM_CID_CONTEXTS ; x++){
8173 if (!ast_strlen_zero(stringp)) {
8174 q = strsep(&stringp,",");
8175 while ((*q == ' ')||(*q == '\t')) /* Eat white space between contexts */
8176 q++;
8177 ast_copy_string(cidinternalcontexts[x], q, sizeof(cidinternalcontexts[x]));
8178 if (option_debug > 2)
8179 ast_log(LOG_DEBUG,"VM_CID Internal context %d: %s\n", x, cidinternalcontexts[x]);
8180 } else {
8181 cidinternalcontexts[x][0] = '\0';
8185 if (!(astreview = ast_variable_retrieve(cfg, "general", "review"))){
8186 if (option_debug)
8187 ast_log(LOG_DEBUG,"VM Review Option disabled globally\n");
8188 astreview = "no";
8190 ast_set2_flag((&globalflags), ast_true(astreview), VM_REVIEW);
8192 /*Temperary greeting reminder */
8193 if (!(asttempgreetwarn = ast_variable_retrieve(cfg, "general", "tempgreetwarn"))) {
8194 if (option_debug)
8195 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option disabled globally\n");
8196 asttempgreetwarn = "no";
8197 } else {
8198 if (option_debug)
8199 ast_log(LOG_DEBUG, "VM Temperary Greeting Reminder Option enabled globally\n");
8201 ast_set2_flag((&globalflags), ast_true(asttempgreetwarn), VM_TEMPGREETWARN);
8203 if (!(astcallop = ast_variable_retrieve(cfg, "general", "operator"))){
8204 if (option_debug)
8205 ast_log(LOG_DEBUG,"VM Operator break disabled globally\n");
8206 astcallop = "no";
8208 ast_set2_flag((&globalflags), ast_true(astcallop), VM_OPERATOR);
8210 if (!(astsaycid = ast_variable_retrieve(cfg, "general", "saycid"))) {
8211 if (option_debug)
8212 ast_log(LOG_DEBUG,"VM CID Info before msg disabled globally\n");
8213 astsaycid = "no";
8215 ast_set2_flag((&globalflags), ast_true(astsaycid), VM_SAYCID);
8217 if (!(send_voicemail = ast_variable_retrieve(cfg,"general", "sendvoicemail"))){
8218 if (option_debug)
8219 ast_log(LOG_DEBUG,"Send Voicemail msg disabled globally\n");
8220 send_voicemail = "no";
8222 ast_set2_flag((&globalflags), ast_true(send_voicemail), VM_SVMAIL);
8224 if (!(asthearenv = ast_variable_retrieve(cfg, "general", "envelope"))) {
8225 if (option_debug)
8226 ast_log(LOG_DEBUG,"ENVELOPE before msg enabled globally\n");
8227 asthearenv = "yes";
8229 ast_set2_flag((&globalflags), ast_true(asthearenv), VM_ENVELOPE);
8231 if (!(astsaydurationinfo = ast_variable_retrieve(cfg, "general", "sayduration"))) {
8232 if (option_debug)
8233 ast_log(LOG_DEBUG,"Duration info before msg enabled globally\n");
8234 astsaydurationinfo = "yes";
8236 ast_set2_flag((&globalflags), ast_true(astsaydurationinfo), VM_SAYDURATION);
8238 saydurationminfo = 2;
8239 if ((astsaydurationminfo = ast_variable_retrieve(cfg, "general", "saydurationm"))) {
8240 if (sscanf(astsaydurationminfo, "%d", &x) == 1) {
8241 saydurationminfo = x;
8242 } else {
8243 ast_log(LOG_WARNING, "Invalid min duration for say duration\n");
8247 if (!(astskipcmd = ast_variable_retrieve(cfg, "general", "nextaftercmd"))) {
8248 if (option_debug)
8249 ast_log(LOG_DEBUG,"We are not going to skip to the next msg after save/delete\n");
8250 astskipcmd = "no";
8252 ast_set2_flag((&globalflags), ast_true(astskipcmd), VM_SKIPAFTERCMD);
8254 if ((dialoutcxt = ast_variable_retrieve(cfg, "general", "dialout"))) {
8255 ast_copy_string(dialcontext, dialoutcxt, sizeof(dialcontext));
8256 if (option_debug)
8257 ast_log(LOG_DEBUG, "found dialout context: %s\n", dialcontext);
8258 } else {
8259 dialcontext[0] = '\0';
8262 if ((callbackcxt = ast_variable_retrieve(cfg, "general", "callback"))) {
8263 ast_copy_string(callcontext, callbackcxt, sizeof(callcontext));
8264 if (option_debug)
8265 ast_log(LOG_DEBUG, "found callback context: %s\n", callcontext);
8266 } else {
8267 callcontext[0] = '\0';
8270 if ((exitcxt = ast_variable_retrieve(cfg, "general", "exitcontext"))) {
8271 ast_copy_string(exitcontext, exitcxt, sizeof(exitcontext));
8272 if (option_debug)
8273 ast_log(LOG_DEBUG, "found operator context: %s\n", exitcontext);
8274 } else {
8275 exitcontext[0] = '\0';
8278 if (!(astdirfwd = ast_variable_retrieve(cfg, "general", "usedirectory")))
8279 astdirfwd = "no";
8280 ast_set2_flag((&globalflags), ast_true(astdirfwd), VM_DIRECFORWARD);
8281 if ((ucfg = ast_config_load("users.conf"))) {
8282 for (cat = ast_category_browse(ucfg, NULL); cat ; cat = ast_category_browse(ucfg, cat)) {
8283 if (!ast_true(ast_config_option(ucfg, cat, "hasvoicemail")))
8284 continue;
8285 if ((cur = find_or_create(userscontext, cat))) {
8286 populate_defaults(cur);
8287 apply_options_full(cur, ast_variable_browse(ucfg, cat));
8288 ast_copy_string(cur->context, userscontext, sizeof(cur->context));
8291 ast_config_destroy(ucfg);
8293 cat = ast_category_browse(cfg, NULL);
8294 while (cat) {
8295 if (strcasecmp(cat, "general")) {
8296 var = ast_variable_browse(cfg, cat);
8297 if (strcasecmp(cat, "zonemessages")) {
8298 /* Process mailboxes in this context */
8299 while (var) {
8300 append_mailbox(cat, var->name, var->value);
8301 var = var->next;
8303 } else {
8304 /* Timezones in this context */
8305 while (var) {
8306 struct vm_zone *z;
8307 if ((z = ast_malloc(sizeof(*z)))) {
8308 char *msg_format, *timezone;
8309 msg_format = ast_strdupa(var->value);
8310 timezone = strsep(&msg_format, "|");
8311 if (msg_format) {
8312 ast_copy_string(z->name, var->name, sizeof(z->name));
8313 ast_copy_string(z->timezone, timezone, sizeof(z->timezone));
8314 ast_copy_string(z->msg_format, msg_format, sizeof(z->msg_format));
8315 AST_LIST_LOCK(&zones);
8316 AST_LIST_INSERT_HEAD(&zones, z, list);
8317 AST_LIST_UNLOCK(&zones);
8318 } else {
8319 ast_log(LOG_WARNING, "Invalid timezone definition at line %d\n", var->lineno);
8320 free(z);
8322 } else {
8323 free(z);
8324 AST_LIST_UNLOCK(&users);
8325 ast_config_destroy(cfg);
8326 return -1;
8328 var = var->next;
8332 cat = ast_category_browse(cfg, cat);
8334 memset(fromstring,0,sizeof(fromstring));
8335 memset(pagerfromstring,0,sizeof(pagerfromstring));
8336 memset(emailtitle,0,sizeof(emailtitle));
8337 strcpy(charset, "ISO-8859-1");
8338 if (emailbody) {
8339 free(emailbody);
8340 emailbody = NULL;
8342 if (emailsubject) {
8343 free(emailsubject);
8344 emailsubject = NULL;
8346 if (pagerbody) {
8347 free(pagerbody);
8348 pagerbody = NULL;
8350 if (pagersubject) {
8351 free(pagersubject);
8352 pagersubject = NULL;
8354 if ((s = ast_variable_retrieve(cfg, "general", "pbxskip")))
8355 ast_set2_flag((&globalflags), ast_true(s), VM_PBXSKIP);
8356 if ((s = ast_variable_retrieve(cfg, "general", "fromstring")))
8357 ast_copy_string(fromstring,s,sizeof(fromstring));
8358 if ((s = ast_variable_retrieve(cfg, "general", "pagerfromstring")))
8359 ast_copy_string(pagerfromstring,s,sizeof(pagerfromstring));
8360 if ((s = ast_variable_retrieve(cfg, "general", "charset")))
8361 ast_copy_string(charset,s,sizeof(charset));
8362 if ((s = ast_variable_retrieve(cfg, "general", "adsifdn"))) {
8363 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
8364 for (x = 0; x < 4; x++) {
8365 memcpy(&adsifdn[x], &tmpadsi[x], 1);
8368 if ((s = ast_variable_retrieve(cfg, "general", "adsisec"))) {
8369 sscanf(s, "%2x%2x%2x%2x", &tmpadsi[0], &tmpadsi[1], &tmpadsi[2], &tmpadsi[3]);
8370 for (x = 0; x < 4; x++) {
8371 memcpy(&adsisec[x], &tmpadsi[x], 1);
8374 if ((s = ast_variable_retrieve(cfg, "general", "adsiver")))
8375 if (atoi(s)) {
8376 adsiver = atoi(s);
8378 if ((s = ast_variable_retrieve(cfg, "general", "emailtitle"))) {
8379 ast_log(LOG_NOTICE, "Keyword 'emailtitle' is DEPRECATED, please use 'emailsubject' instead.\n");
8380 ast_copy_string(emailtitle,s,sizeof(emailtitle));
8382 if ((s = ast_variable_retrieve(cfg, "general", "emailsubject")))
8383 emailsubject = ast_strdup(s);
8384 if ((s = ast_variable_retrieve(cfg, "general", "emailbody"))) {
8385 char *tmpread, *tmpwrite;
8386 emailbody = ast_strdup(s);
8388 /* substitute strings \t and \n into the appropriate characters */
8389 tmpread = tmpwrite = emailbody;
8390 while ((tmpwrite = strchr(tmpread,'\\'))) {
8391 switch (tmpwrite[1]) {
8392 case 'r':
8393 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
8394 *tmpwrite = '\r';
8395 break;
8396 case 'n':
8397 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
8398 *tmpwrite = '\n';
8399 break;
8400 case 't':
8401 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
8402 *tmpwrite = '\t';
8403 break;
8404 default:
8405 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
8407 tmpread = tmpwrite + 1;
8410 if ((s = ast_variable_retrieve(cfg, "general", "pagersubject")))
8411 pagersubject = ast_strdup(s);
8412 if ((s = ast_variable_retrieve(cfg, "general", "pagerbody"))) {
8413 char *tmpread, *tmpwrite;
8414 pagerbody = ast_strdup(s);
8416 /* substitute strings \t and \n into the appropriate characters */
8417 tmpread = tmpwrite = pagerbody;
8418 while ((tmpwrite = strchr(tmpread, '\\'))) {
8419 switch (tmpwrite[1]) {
8420 case 'r':
8421 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
8422 *tmpwrite = '\r';
8423 break;
8424 case 'n':
8425 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
8426 *tmpwrite = '\n';
8427 break;
8428 case 't':
8429 memmove(tmpwrite + 1, tmpwrite + 2, strlen(tmpwrite + 2) + 1);
8430 *tmpwrite = '\t';
8431 break;
8432 default:
8433 ast_log(LOG_NOTICE, "Substitution routine does not support this character: %c\n", tmpwrite[1]);
8435 tmpread = tmpwrite + 1;
8438 AST_LIST_UNLOCK(&users);
8439 ast_config_destroy(cfg);
8440 return 0;
8441 } else {
8442 AST_LIST_UNLOCK(&users);
8443 ast_log(LOG_WARNING, "Failed to load configuration file.\n");
8444 return 0;
8448 static int reload(void)
8450 return(load_config());
8453 static int unload_module(void)
8455 int res;
8457 res = ast_unregister_application(app);
8458 res |= ast_unregister_application(app2);
8459 res |= ast_unregister_application(app3);
8460 res |= ast_unregister_application(app4);
8461 ast_cli_unregister_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
8462 ast_uninstall_vm_functions();
8464 ast_module_user_hangup_all();
8466 return res;
8469 static int load_module(void)
8471 int res;
8472 char *adsi_loaded = ast_module_helper("", "res_adsi.so", 0, 0, 0, 0);
8473 free(adsi_loaded);
8474 if (!adsi_loaded) {
8475 /* If embedded, res_adsi may be known as "res_adsi" not "res_adsi.so" */
8476 adsi_loaded = ast_module_helper("", "res_adsi", 0, 0, 0, 0);
8477 ast_free(adsi_loaded);
8478 if (!adsi_loaded) {
8479 ast_log(LOG_ERROR, "app_voicemail.so depends upon res_adsi.so\n");
8480 return AST_MODULE_LOAD_DECLINE;
8484 my_umask = umask(0);
8485 umask(my_umask);
8486 res = ast_register_application(app, vm_exec, synopsis_vm, descrip_vm);
8487 res |= ast_register_application(app2, vm_execmain, synopsis_vmain, descrip_vmain);
8488 res |= ast_register_application(app3, vm_box_exists, synopsis_vm_box_exists, descrip_vm_box_exists);
8489 res |= ast_register_application(app4, vmauthenticate, synopsis_vmauthenticate, descrip_vmauthenticate);
8490 if (res)
8491 return(res);
8493 if ((res=load_config())) {
8494 return(res);
8497 ast_cli_register_multiple(cli_voicemail, sizeof(cli_voicemail) / sizeof(struct ast_cli_entry));
8499 /* compute the location of the voicemail spool directory */
8500 snprintf(VM_SPOOL_DIR, sizeof(VM_SPOOL_DIR), "%s/voicemail/", ast_config_AST_SPOOL_DIR);
8502 ast_install_vm_functions(has_voicemail, inboxcount, messagecount);
8504 return res;
8507 static int dialout(struct ast_channel *chan, struct ast_vm_user *vmu, char *num, char *outgoing_context)
8509 int cmd = 0;
8510 char destination[80] = "";
8511 int retries = 0;
8513 if (!num) {
8514 if (option_verbose > 2)
8515 ast_verbose( VERBOSE_PREFIX_3 "Destination number will be entered manually\n");
8516 while (retries < 3 && cmd != 't') {
8517 destination[1] = '\0';
8518 destination[0] = cmd = ast_play_and_wait(chan,"vm-enter-num-to-call");
8519 if (!cmd)
8520 destination[0] = cmd = ast_play_and_wait(chan, "vm-then-pound");
8521 if (!cmd)
8522 destination[0] = cmd = ast_play_and_wait(chan, "vm-star-cancel");
8523 if (!cmd) {
8524 cmd = ast_waitfordigit(chan, 6000);
8525 if (cmd)
8526 destination[0] = cmd;
8528 if (!cmd) {
8529 retries++;
8530 } else {
8532 if (cmd < 0)
8533 return 0;
8534 if (cmd == '*') {
8535 if (option_verbose > 2)
8536 ast_verbose( VERBOSE_PREFIX_3 "User hit '*' to cancel outgoing call\n");
8537 return 0;
8539 if ((cmd = ast_readstring(chan,destination + strlen(destination),sizeof(destination)-1,6000,10000,"#")) < 0)
8540 retries++;
8541 else
8542 cmd = 't';
8545 if (retries >= 3) {
8546 return 0;
8549 } else {
8550 if (option_verbose > 2)
8551 ast_verbose( VERBOSE_PREFIX_3 "Destination number is CID number '%s'\n", num);
8552 ast_copy_string(destination, num, sizeof(destination));
8555 if (!ast_strlen_zero(destination)) {
8556 if (destination[strlen(destination) -1 ] == '*')
8557 return 0;
8558 if (option_verbose > 2)
8559 ast_verbose( VERBOSE_PREFIX_3 "Placing outgoing call to extension '%s' in context '%s' from context '%s'\n", destination, outgoing_context, chan->context);
8560 ast_copy_string(chan->exten, destination, sizeof(chan->exten));
8561 ast_copy_string(chan->context, outgoing_context, sizeof(chan->context));
8562 chan->priority = 0;
8563 return 9;
8565 return 0;
8568 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)
8570 int res = 0;
8571 char filename[PATH_MAX];
8572 struct ast_config *msg_cfg = NULL;
8573 const char *origtime, *context;
8574 char *cid, *name, *num;
8575 int retries = 0;
8577 vms->starting = 0;
8578 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
8580 /* Retrieve info from VM attribute file */
8581 make_file(vms->fn2, sizeof(vms->fn2), vms->curdir, vms->curmsg);
8582 snprintf(filename,sizeof(filename), "%s.txt", vms->fn2);
8583 RETRIEVE(vms->curdir, vms->curmsg, vmu);
8584 msg_cfg = ast_config_load(filename);
8585 DISPOSE(vms->curdir, vms->curmsg);
8586 if (!msg_cfg) {
8587 ast_log(LOG_WARNING, "No message attribute file?!! (%s)\n", filename);
8588 return 0;
8591 if (!(origtime = ast_variable_retrieve(msg_cfg, "message", "origtime"))) {
8592 ast_config_destroy(msg_cfg);
8593 return 0;
8596 cid = ast_strdupa(ast_variable_retrieve(msg_cfg, "message", "callerid"));
8598 context = ast_variable_retrieve(msg_cfg, "message", "context");
8599 if (!strncasecmp("macro",context,5)) /* Macro names in contexts are useless for our needs */
8600 context = ast_variable_retrieve(msg_cfg, "message","macrocontext");
8601 switch (option) {
8602 case 3:
8603 if (!res)
8604 res = play_message_datetime(chan, vmu, origtime, filename);
8605 if (!res)
8606 res = play_message_callerid(chan, vms, cid, context, 0);
8608 res = 't';
8609 break;
8611 case 2: /* Call back */
8613 if (ast_strlen_zero(cid))
8614 break;
8616 ast_callerid_parse(cid, &name, &num);
8617 while ((res > -1) && (res != 't')) {
8618 switch (res) {
8619 case '1':
8620 if (num) {
8621 /* Dial the CID number */
8622 res = dialout(chan, vmu, num, vmu->callback);
8623 if (res) {
8624 ast_config_destroy(msg_cfg);
8625 return 9;
8627 } else {
8628 res = '2';
8630 break;
8632 case '2':
8633 /* Want to enter a different number, can only do this if there's a dialout context for this user */
8634 if (!ast_strlen_zero(vmu->dialout)) {
8635 res = dialout(chan, vmu, NULL, vmu->dialout);
8636 if (res) {
8637 ast_config_destroy(msg_cfg);
8638 return 9;
8640 } else {
8641 if (option_verbose > 2)
8642 ast_verbose( VERBOSE_PREFIX_3 "Caller can not specify callback number - no dialout context available\n");
8643 res = ast_play_and_wait(chan, "vm-sorry");
8645 ast_config_destroy(msg_cfg);
8646 return res;
8647 case '*':
8648 res = 't';
8649 break;
8650 case '3':
8651 case '4':
8652 case '5':
8653 case '6':
8654 case '7':
8655 case '8':
8656 case '9':
8657 case '0':
8659 res = ast_play_and_wait(chan, "vm-sorry");
8660 retries++;
8661 break;
8662 default:
8663 if (num) {
8664 if (option_verbose > 2)
8665 ast_verbose( VERBOSE_PREFIX_3 "Confirm CID number '%s' is number to use for callback\n", num);
8666 res = ast_play_and_wait(chan, "vm-num-i-have");
8667 if (!res)
8668 res = play_message_callerid(chan, vms, num, vmu->context, 1);
8669 if (!res)
8670 res = ast_play_and_wait(chan, "vm-tocallnum");
8671 /* Only prompt for a caller-specified number if there is a dialout context specified */
8672 if (!ast_strlen_zero(vmu->dialout)) {
8673 if (!res)
8674 res = ast_play_and_wait(chan, "vm-calldiffnum");
8676 } else {
8677 res = ast_play_and_wait(chan, "vm-nonumber");
8678 if (!ast_strlen_zero(vmu->dialout)) {
8679 if (!res)
8680 res = ast_play_and_wait(chan, "vm-toenternumber");
8683 if (!res)
8684 res = ast_play_and_wait(chan, "vm-star-cancel");
8685 if (!res)
8686 res = ast_waitfordigit(chan, 6000);
8687 if (!res) {
8688 retries++;
8689 if (retries > 3)
8690 res = 't';
8692 break;
8695 if (res == 't')
8696 res = 0;
8697 else if (res == '*')
8698 res = -1;
8700 break;
8702 case 1: /* Reply */
8703 /* Send reply directly to sender */
8704 if (ast_strlen_zero(cid))
8705 break;
8707 ast_callerid_parse(cid, &name, &num);
8708 if (!num) {
8709 if (option_verbose > 2)
8710 ast_verbose(VERBOSE_PREFIX_3 "No CID number available, no reply sent\n");
8711 if (!res)
8712 res = ast_play_and_wait(chan, "vm-nonumber");
8713 ast_config_destroy(msg_cfg);
8714 return res;
8715 } else {
8716 struct ast_vm_user vmu2;
8717 if (find_user(&vmu2, vmu->context, num)) {
8718 struct leave_vm_options leave_options;
8719 char mailbox[AST_MAX_EXTENSION * 2 + 2];
8720 snprintf(mailbox, sizeof(mailbox), "%s@%s", num, vmu->context);
8722 if (option_verbose > 2)
8723 ast_verbose(VERBOSE_PREFIX_3 "Leaving voicemail for '%s' in context '%s'\n", num, vmu->context);
8725 memset(&leave_options, 0, sizeof(leave_options));
8726 leave_options.record_gain = record_gain;
8727 res = leave_voicemail(chan, mailbox, &leave_options);
8728 if (!res)
8729 res = 't';
8730 ast_config_destroy(msg_cfg);
8731 return res;
8732 } else {
8733 /* Sender has no mailbox, can't reply */
8734 if (option_verbose > 2)
8735 ast_verbose( VERBOSE_PREFIX_3 "No mailbox number '%s' in context '%s', no reply sent\n", num, vmu->context);
8736 ast_play_and_wait(chan, "vm-nobox");
8737 res = 't';
8738 ast_config_destroy(msg_cfg);
8739 return res;
8742 res = 0;
8744 break;
8747 #ifndef IMAP_STORAGE
8748 ast_config_destroy(msg_cfg);
8750 if (!res) {
8751 make_file(vms->fn, sizeof(vms->fn), vms->curdir, msg);
8752 vms->heard[msg] = 1;
8753 res = wait_file(chan, vms, vms->fn);
8755 #endif
8756 return res;
8759 static int play_record_review(struct ast_channel *chan, char *playfile, char *recordfile, int maxtime, char *fmt,
8760 int outsidecaller, struct ast_vm_user *vmu, int *duration, const char *unlockdir,
8761 signed char record_gain, struct vm_state *vms)
8763 /* Record message & let caller review or re-record it, or set options if applicable */
8764 int res = 0;
8765 int cmd = 0;
8766 int max_attempts = 3;
8767 int attempts = 0;
8768 int recorded = 0;
8769 int message_exists = 0;
8770 signed char zero_gain = 0;
8771 char tempfile[PATH_MAX];
8772 char *acceptdtmf = "#";
8773 char *canceldtmf = "";
8775 /* Note that urgent and private are for flagging messages as such in the future */
8777 /* barf if no pointer passed to store duration in */
8778 if (duration == NULL) {
8779 ast_log(LOG_WARNING, "Error play_record_review called without duration pointer\n");
8780 return -1;
8783 if (!outsidecaller)
8784 snprintf(tempfile, sizeof(tempfile), "%s.tmp", recordfile);
8785 else
8786 ast_copy_string(tempfile, recordfile, sizeof(tempfile));
8788 cmd = '3'; /* Want to start by recording */
8790 while ((cmd >= 0) && (cmd != 't')) {
8791 switch (cmd) {
8792 case '1':
8793 if (!message_exists) {
8794 /* In this case, 1 is to record a message */
8795 cmd = '3';
8796 break;
8797 } else {
8798 /* Otherwise 1 is to save the existing message */
8799 if (option_verbose > 2)
8800 ast_verbose(VERBOSE_PREFIX_3 "Saving message as is\n");
8801 if (!outsidecaller)
8802 ast_filerename(tempfile, recordfile, NULL);
8803 ast_stream_and_wait(chan, "vm-msgsaved", chan->language, "");
8804 if (!outsidecaller) {
8805 STORE(recordfile, vmu->mailbox, vmu->context, -1, chan, vmu, fmt, *duration, vms);
8806 DISPOSE(recordfile, -1);
8808 cmd = 't';
8809 return res;
8811 case '2':
8812 /* Review */
8813 if (option_verbose > 2)
8814 ast_verbose(VERBOSE_PREFIX_3 "Reviewing the message\n");
8815 cmd = ast_stream_and_wait(chan, tempfile, chan->language, AST_DIGIT_ANY);
8816 break;
8817 case '3':
8818 message_exists = 0;
8819 /* Record */
8820 if (recorded == 1) {
8821 if (option_verbose > 2)
8822 ast_verbose(VERBOSE_PREFIX_3 "Re-recording the message\n");
8823 } else {
8824 if (option_verbose > 2)
8825 ast_verbose(VERBOSE_PREFIX_3 "Recording the message\n");
8827 if (recorded && outsidecaller) {
8828 cmd = ast_play_and_wait(chan, INTRO);
8829 cmd = ast_play_and_wait(chan, "beep");
8831 recorded = 1;
8832 /* 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 */
8833 if (record_gain)
8834 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &record_gain, sizeof(record_gain), 0);
8835 if (ast_test_flag(vmu, VM_OPERATOR))
8836 canceldtmf = "0";
8837 cmd = ast_play_and_record_full(chan, playfile, tempfile, maxtime, fmt, duration, silencethreshold, maxsilence, unlockdir, acceptdtmf, canceldtmf);
8838 if (record_gain)
8839 ast_channel_setoption(chan, AST_OPTION_RXGAIN, &zero_gain, sizeof(zero_gain), 0);
8840 if (cmd == -1) {
8841 /* User has hung up, no options to give */
8842 if (!outsidecaller) {
8843 /* user was recording a greeting and they hung up, so let's delete the recording. */
8844 ast_filedelete(tempfile, NULL);
8846 return cmd;
8848 if (cmd == '0') {
8849 break;
8850 } else if (cmd == '*') {
8851 break;
8853 #if 0
8854 else if (vmu->review && (*duration < 5)) {
8855 /* Message is too short */
8856 if (option_verbose > 2)
8857 ast_verbose(VERBOSE_PREFIX_3 "Message too short\n");
8858 cmd = ast_play_and_wait(chan, "vm-tooshort");
8859 cmd = ast_filedelete(tempfile, NULL);
8860 break;
8862 else if (vmu->review && (cmd == 2 && *duration < (maxsilence + 3))) {
8863 /* Message is all silence */
8864 if (option_verbose > 2)
8865 ast_verbose(VERBOSE_PREFIX_3 "Nothing recorded\n");
8866 cmd = ast_filedelete(tempfile, NULL);
8867 cmd = ast_play_and_wait(chan, "vm-nothingrecorded");
8868 if (!cmd)
8869 cmd = ast_play_and_wait(chan, "vm-speakup");
8870 break;
8872 #endif
8873 else {
8874 /* If all is well, a message exists */
8875 message_exists = 1;
8876 cmd = 0;
8878 break;
8879 case '4':
8880 case '5':
8881 case '6':
8882 case '7':
8883 case '8':
8884 case '9':
8885 case '*':
8886 case '#':
8887 cmd = ast_play_and_wait(chan, "vm-sorry");
8888 break;
8889 #if 0
8890 /* XXX Commented out for the moment because of the dangers of deleting
8891 a message while recording (can put the message numbers out of sync) */
8892 case '*':
8893 /* Cancel recording, delete message, offer to take another message*/
8894 cmd = ast_play_and_wait(chan, "vm-deleted");
8895 cmd = ast_filedelete(tempfile, NULL);
8896 if (outsidecaller) {
8897 res = vm_exec(chan, NULL);
8898 return res;
8900 else
8901 return 1;
8902 #endif
8903 case '0':
8904 if (!ast_test_flag(vmu, VM_OPERATOR)) {
8905 cmd = ast_play_and_wait(chan, "vm-sorry");
8906 break;
8908 if (message_exists || recorded) {
8909 cmd = ast_play_and_wait(chan, "vm-saveoper");
8910 if (!cmd)
8911 cmd = ast_waitfordigit(chan, 3000);
8912 if (cmd == '1') {
8913 ast_play_and_wait(chan, "vm-msgsaved");
8914 cmd = '0';
8915 } else {
8916 ast_play_and_wait(chan, "vm-deleted");
8917 DELETE(recordfile, -1, recordfile, vmu);
8918 cmd = '0';
8921 return cmd;
8922 default:
8923 /* If the caller is an ouside caller, and the review option is enabled,
8924 allow them to review the message, but let the owner of the box review
8925 their OGM's */
8926 if (outsidecaller && !ast_test_flag(vmu, VM_REVIEW))
8927 return cmd;
8928 if (message_exists) {
8929 cmd = ast_play_and_wait(chan, "vm-review");
8931 else {
8932 cmd = ast_play_and_wait(chan, "vm-torerecord");
8933 if (!cmd)
8934 cmd = ast_waitfordigit(chan, 600);
8937 if (!cmd && outsidecaller && ast_test_flag(vmu, VM_OPERATOR)) {
8938 cmd = ast_play_and_wait(chan, "vm-reachoper");
8939 if (!cmd)
8940 cmd = ast_waitfordigit(chan, 600);
8942 #if 0
8943 if (!cmd)
8944 cmd = ast_play_and_wait(chan, "vm-tocancelmsg");
8945 #endif
8946 if (!cmd)
8947 cmd = ast_waitfordigit(chan, 6000);
8948 if (!cmd) {
8949 attempts++;
8951 if (attempts > max_attempts) {
8952 cmd = 't';
8956 if (outsidecaller)
8957 ast_play_and_wait(chan, "vm-goodbye");
8958 if (cmd == 't')
8959 cmd = 0;
8960 return cmd;
8963 /* This is a workaround so that menuselect displays a proper description
8964 * AST_MODULE_INFO(, , "Comedian Mail (Voicemail System)"
8967 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, tdesc,
8968 .load = load_module,
8969 .unload = unload_module,
8970 .reload = reload,